name: Build Docker Package on: push: branches: - main tags: - "v*" workflow_dispatch: jobs: docker-package: runs-on: gitea_act # 使用 Docker CLI 镜像,因为 runner 默认任务镜像不一定包含 docker、 # bash 或基于 Node 的 action 依赖。 container: image: docker:28-cli env: REGISTRY: git.hanhan.ltd GITEA_SERVER_URL: https://git.hanhan.ltd NODE_IMAGE: m.daocloud.io/docker.io/library/node:22 NPM_REGISTRY: https://registry.npmmirror.com steps: - name: Install job tools shell: sh run: | apk add --no-cache git docker --version git --version - name: Checkout env: GITEA_ACTOR: ${{ gitea.actor }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} shell: sh run: | command -v git repository="${GITHUB_REPOSITORY:-${{ gitea.repository }}}" # GITHUB_SERVER_URL 可能指向任务容器无法解析的内部容器主机名, # 因此这里固定使用公网 Gitea 地址。 server_url="${GITEA_SERVER_URL}" sha="${GITHUB_SHA:-${{ gitea.sha }}}" git init . git remote add origin "${server_url}/${repository}.git" if [ -n "${GITEA_TOKEN}" ]; then auth="$(printf '%s:%s' "${GITEA_ACTOR}" "${GITEA_TOKEN}" | base64 | tr -d '\n')" git -c "http.extraHeader=Authorization: Basic ${auth}" fetch --depth=1 origin "${sha}" else git fetch --depth=1 origin "${sha}" fi git checkout --force FETCH_HEAD git config --global --add safe.directory "$PWD" - name: Prepare image metadata shell: sh run: | repository="${GITHUB_REPOSITORY:-${{ gitea.repository }}}" image_repository="$(printf '%s' "$repository" | tr '[:upper:]' '[:lower:]')" image="${REGISTRY}/${image_repository}" sha="${GITHUB_SHA:-${{ gitea.sha }}}" ref="${GITHUB_REF:-${{ gitea.ref }}}" source_url="${GITEA_SERVER_URL}/${repository}" : > .image-tags # 分支构建发布滚动的 latest 标签;版本标签如 v1.2.3 # 只发布不可变的 1.2.3 镜像标签。 case "${ref}" in refs/tags/v*) tag="${ref#refs/tags/}" version="${tag#v}" echo "${image}:${version}" >> .image-tags ;; *) echo "${image}:latest" >> .image-tags ;; esac { # OCI 标签可以帮助 Gitea 和其他镜像仓库展示来源仓库; # 即使包仍需手动关联仓库,也能保留来源元数据。 echo "org.opencontainers.image.source=${source_url}" echo "org.opencontainers.image.url=${source_url}" echo "org.opencontainers.image.revision=${sha}" echo "org.opencontainers.image.title=${repository}" } > .image-labels cat .image-tags - name: Login to Gitea container registry env: PACKAGE_USERNAME: ${{ secrets.PACKAGE_USERNAME }} PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }} GITEA_ACTOR: ${{ gitea.actor }} shell: sh run: | if [ -z "${PACKAGE_TOKEN}" ]; then echo "PACKAGE_TOKEN secret is required to publish container packages." >&2 echo "Create a Gitea personal access token with package write permission for PACKAGE_USERNAME." >&2 exit 1 fi username="${PACKAGE_USERNAME:-${GITEA_ACTOR}}" # PACKAGE_USERNAME 为了方便可以不填;但当 token 所属用户和 # 触发流水线的用户不一致时,显式设置它可以避免 unauthorized。 if [ -z "${username}" ]; then echo "PACKAGE_USERNAME secret is required because Gitea actor is empty." >&2 echo "Set it to the Gitea username that owns PACKAGE_TOKEN." >&2 exit 1 fi if [ -z "${PACKAGE_USERNAME}" ]; then echo "PACKAGE_USERNAME is not set; using Gitea actor '${username}' for docker login." echo "If login fails with unauthorized, set PACKAGE_USERNAME to the token owner." fi if ! docker version; then echo "Docker daemon is not reachable from the Gitea runner." >&2 echo "Mount /var/run/docker.sock into the runner or use a dind runner image." >&2 exit 1 fi echo "${PACKAGE_TOKEN}" | docker login "${REGISTRY}" -u "${username}" --password-stdin - name: Build image shell: sh run: | tag_args="" while IFS= read -r tag; do tag_args="${tag_args} --tag ${tag}" done < .image-tags label_args="" while IFS= read -r label; do label_args="${label_args} --label ${label}" done < .image-labels # tag/label 参数都由上面的脚本生成,值里不包含空格; # 这里保留拆词是为了把它们展开成 docker build 参数。 # shellcheck disable=SC2086 docker build $tag_args $label_args \ --build-arg "NODE_IMAGE=${NODE_IMAGE}" \ --build-arg "NPM_REGISTRY=${NPM_REGISTRY}" \ . - name: Push image shell: sh run: | while IFS= read -r tag; do docker push "$tag" done < .image-tags