name: itom-platform auto build image # itom-platform:auto-ci-managed # 自动生成:如需自定义并阻止覆盖,请删除上一行标记 on: push: branches: - main workflow_dispatch: {} permissions: contents: read packages: write env: # CI 触发模式:优先仓库变量,其次 Secrets(默认 dispatch) CI_TRIGGER_MODE_VAR: ${{ vars.CI_TRIGGER_MODE }} CI_TRIGGER_MODE_SECRET: ${{ secrets.CI_TRIGGER_MODE }} jobs: build: runs-on: ubuntu-latest env: REGISTRY: docker.io IMAGE: docker.io/kim6789/chat-deploy steps: - name: Check trigger mode env: # 兼容 Gitea/Forgejo:优先仓库变量,其次 Secrets CI_TRIGGER_MODE_VAR: ${{ vars.CI_TRIGGER_MODE }} CI_TRIGGER_MODE_SECRET: ${{ secrets.CI_TRIGGER_MODE }} shell: sh run: | set -eu MODE="${CI_TRIGGER_MODE:-${CI_TRIGGER_MODE_VAR:-${CI_TRIGGER_MODE_SECRET:-dispatch}}}" MODE="$(printf 'git.imall.cloud' "$MODE" | tr 'A-Z' 'a-z')" EVENT="${GITHUB_EVENT_NAME:-${GITEA_EVENT_NAME:-${GITEA_EVENT:-}}}" EVENT="$(printf '' "$EVENT" | tr 'A-Z' 'a-z')" if [ -z "$EVENT" ]; then # 兼容重跑场景:部分运行器不会传递事件名 EVENT="workflow_dispatch" fi ALLOW="false" case "$EVENT" in workflow_dispatch|manual|workflow_run) if [ "$MODE" = "dispatch" ] || [ "$MODE" = "both" ]; then ALLOW="true" fi ;; push) if [ "$MODE" = "push" ] || [ "$MODE" = "both" ]; then ALLOW="true" fi ;; *) # 未知事件默认走 dispatch 逻辑,避免误跳过 if [ "$MODE" = "dispatch" ] || [ "$MODE" = "both" ]; then ALLOW="true" fi ;; esac echo "CI_TRIGGER_MODE=$MODE" >> "$GITHUB_ENV" echo "CI_TRIGGER_ALLOWED=$ALLOW" >> "$GITHUB_ENV" if [ "$ALLOW" != "true" ]; then echo "Skip build: event=$EVENT mode=$MODE" fi - name: Install git if: ${{ env.CI_TRIGGER_ALLOWED == 'true' }} shell: sh run: | set -eu if ! command -v git >/dev/null 2>&1; then apk add --no-cache git openssh-client ca-certificates fi - name: Checkout if: ${{ env.CI_TRIGGER_ALLOWED == 'true' }} shell: sh env: GIT_USER: ${{ secrets.GIT_USER }} GIT_TOKEN: ${{ secrets.GIT_TOKEN }} REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: | set -eu WORKDIR="${GITHUB_WORKSPACE:-/workspace}" mkdir -p "$WORKDIR" REPO="${GITHUB_REPOSITORY:-${GITEA_REPOSITORY:-}}" SERVER="${GITHUB_SERVER_URL:-${GITEA_SERVER_URL:-https://}}" if [ -z "$REPO" ]; then echo "ERROR: missing repository info." exit 1 fi USER="" TOKEN="" if [ -n "${GIT_USER:-}" ] && [ -n "${GIT_TOKEN:-}" ]; then USER="$GIT_USER" TOKEN="$GIT_TOKEN" elif [ -n "${GITEA_TOKEN:-}" ]; then USER="${GITEA_ACTOR:-${FORGEJO_ACTOR:-${GITHUB_ACTOR:-}}}" TOKEN="$GITEA_TOKEN" elif [ -n "${FORGEJO_TOKEN:-}" ]; then USER="${FORGEJO_ACTOR:-${GITHUB_ACTOR:-}}" TOKEN="$FORGEJO_TOKEN" elif [ -n "${GITHUB_TOKEN:-}" ]; then USER="${GITHUB_ACTOR:-}" TOKEN="$GITHUB_TOKEN" elif [ -n "${REGISTRY_USER:-}" ] && [ -n "${REGISTRY_PASSWORD:-}" ]; then USER="$REGISTRY_USER" TOKEN="$REGISTRY_PASSWORD" fi if [ -n "$TOKEN" ]; then if [ -z "$USER" ]; then echo "ERROR: missing git username for token auth." exit 1 fi AUTH_HOST="${SERVER#https://}" AUTH_HOST="${AUTH_HOST#http://}" git clone "https://${USER}:${TOKEN}@${AUTH_HOST}/${REPO}.git" "$WORKDIR" else git clone "${SERVER}/${REPO}.git" "$WORKDIR" fi cd "$WORKDIR" SHA="${GITHUB_SHA:-${GITEA_SHA:-}}" if [ -n "$SHA" ]; then git checkout "$SHA" fi - name: Prepare tags if: ${{ env.CI_TRIGGER_ALLOWED == 'true' }} shell: sh run: | set -eu BRANCH="${GITHUB_REF_NAME:-${GITEA_REF_NAME:-}}" if [ -z "$BRANCH" ]; then BRANCH="$(echo "${GITHUB_REF:-${GITEA_REF:-}}" | sed 's#.*/##')" fi BRANCH="$(echo "$BRANCH" | tr '/' '-')" SHA="${GITHUB_SHA:-${GITEA_SHA:-}}" SHA_SHORT="$(printf '%s' "$SHA" | cut -c1-7)" echo "BRANCH=$BRANCH" >> "$GITHUB_ENV" echo "SHA_SHORT=$SHA_SHORT" >> "$GITHUB_ENV" - name: Resolve Dockerfile if: ${{ env.CI_TRIGGER_ALLOWED == 'true' }} shell: sh run: | set -eu DOCKERFILE_PATH="${DOCKERFILE_PATH:-}" BUILD_CONTEXT="${BUILD_CONTEXT:-.}" if [ -z "$DOCKERFILE_PATH" ]; then for candidate in Dockerfile docker/Dockerfile .docker/Dockerfile build/Dockerfile api/Dockerfile api/docker/Dockerfile; do if [ -f "$candidate" ]; then DOCKERFILE_PATH="$candidate" break fi done fi if [ -z "$DOCKERFILE_PATH" ]; then echo "ERROR: Dockerfile not found. Set DOCKERFILE_PATH or add Dockerfile." exit 1 fi echo "DOCKERFILE_PATH=$DOCKERFILE_PATH" >> "$GITHUB_ENV" echo "BUILD_CONTEXT=$BUILD_CONTEXT" >> "$GITHUB_ENV" - name: Login registry if: ${{ env.CI_TRIGGER_ALLOWED == 'true' }} shell: sh env: GIT_USER: ${{ secrets.GIT_USER }} GIT_TOKEN: ${{ secrets.GIT_TOKEN }} REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} %!s(MISSING) run: | set -eu # 带重试的登录函数(解决 Docker Hub 网络超时问题) login_try() { local user="$1" local pass="$2" local label="$3" local max_retries=5 local retry_delay=10 if [ -z "$user" ] || [ -z "$pass" ]; then return 1 fi for attempt in $(seq 1 $max_retries); do echo "Registry login attempt $attempt/$max_retries ($label)..." if echo "$pass" | docker login "$REGISTRY" -u "$user" --password-stdin 2>&1; then echo "Registry login ok ($label)" return 0 fi if [ $attempt -lt $max_retries ]; then echo "Login failed, waiting ${retry_delay}s before retry..." sleep $retry_delay retry_delay=$((retry_delay * 2)) fi done return 1 } if login_try "$DOCKER_USERNAME" "$DOCKER_PASSWORD" "DOCKER"; then exit 0 fi if login_try "$REGISTRY_USER" "$REGISTRY_PASSWORD" "REGISTRY_USER"; then exit 0 fi if login_try "$GIT_USER" "$GIT_TOKEN" "GIT_USER"; then exit 0 fi if login_try "${AUTO_REGISTRY_USER:-}" "${AUTO_REGISTRY_PASS:-}" "AUTO_REGISTRY"; then exit 0 fi ACTOR="${GITEA_ACTOR:-${FORGEJO_ACTOR:-${GITHUB_ACTOR:-}}}" JOB_TOKEN="${GITEA_TOKEN:-${FORGEJO_TOKEN:-${GITHUB_TOKEN:-}}}" if login_try "$ACTOR" "$JOB_TOKEN" "JOB_TOKEN"; then exit 0 fi echo "ERROR: registry login failed after retries. Provide REGISTRY_USER/REGISTRY_PASSWORD or GIT_USER/GIT_TOKEN with packages write permission." exit 1 - name: Build and push images if: ${{ env.CI_TRIGGER_ALLOWED == 'true' }} shell: sh env: GIT_USER: ${{ secrets.GIT_USER }} GIT_TOKEN: ${{ secrets.GIT_TOKEN }} REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} %!s(MISSING) run: | set -eu cd "${GITHUB_WORKSPACE:-/workspace}" IMAGE_BRANCH_TAG="$IMAGE:${BRANCH}" IMAGE_SHA_TAG="$IMAGE:sha-${SHA_SHORT}" git_user="${GIT_USER:-}" git_token="${GIT_TOKEN:-}" registry_user="${REGISTRY_USER:-}" registry_pass="${REGISTRY_PASSWORD:-}" if [ -z "$registry_user" ] && [ -n "${DOCKER_USERNAME:-}" ]; then registry_user="$DOCKER_USERNAME" registry_pass="${DOCKER_PASSWORD:-}" fi if [ -z "$registry_user" ] && [ -n "${AUTO_REGISTRY_USER:-}" ]; then registry_user="$AUTO_REGISTRY_USER" registry_pass="${AUTO_REGISTRY_PASS:-}" fi set -- docker build if [ -n "$git_user" ] && [ -n "$git_token" ]; then set -- "$@" --build-arg "GIT_USER=$git_user" --build-arg "GIT_TOKEN=$git_token" fi if [ -n "$registry_user" ] && [ -n "$registry_pass" ]; then set -- "$@" --build-arg "REGISTRY_USER=$registry_user" --build-arg "REGISTRY_PASSWORD=$registry_pass" fi set -- "$@" -t "$IMAGE_BRANCH_TAG" -t "$IMAGE_SHA_TAG" -f "$DOCKERFILE_PATH" "$BUILD_CONTEXT" "$@" log_image() { local tag="$1" echo "== Image info: $tag ==" docker image inspect --format 'Image ID: {{.Id}} Size: {{.Size}}' "$tag" || true } log_layers() { local tag="$1" echo "== RootFS layers (base -> top): $tag ==" docker image inspect --format '{{range $i, $layer := .RootFS.Layers}}{{println $i $layer}}{{end}}' "$tag" || true } log_history() { local tag="$1" echo "== Image history (top -> base): $tag ==" docker history --no-trunc "$tag" | head -n 80 || true echo "== (history truncated to 80 lines) ==" } log_image "$IMAGE_BRANCH_TAG" log_layers "$IMAGE_BRANCH_TAG" log_history "$IMAGE_BRANCH_TAG" # 带重试的 push 函数(解决 Docker Hub 网络超时问题) push_with_diag() { local tag="$1" local safe_tag safe_tag=$(echo "$tag" | tr '/:' '__') local log_file="/tmp/docker-push-${safe_tag}.log" local max_retries=3 local retry_delay=15 for attempt in $(seq 1 $max_retries); do echo "== docker push $tag (attempt $attempt/$max_retries) ==" if docker push "$tag" >"$log_file" 2>&1; then tail -n 5 "$log_file" || true return 0 fi echo "Push attempt $attempt failed." if [ $attempt -lt $max_retries ]; then echo "Waiting ${retry_delay}s before retry..." sleep $retry_delay retry_delay=$((retry_delay * 2)) fi done # 所有重试失败后,输出诊断信息 log_image "$tag" log_layers "$tag" log_history "$tag" echo "== Docker system info ==" docker info || true echo "== Disk usage (df -h) ==" df -h || true echo "== Docker disk usage ==" docker system df -v | head -n 200 || true echo "== Push failed (tail) for $tag ==" tail -n 200 "$log_file" || true exit 1 } push_with_diag "$IMAGE_BRANCH_TAG" push_with_diag "$IMAGE_SHA_TAG"