name: Build Docker image on: workflow_dispatch: inputs: repo: description: 仓库地址 required: true type: string ref: description: 分支名称 required: true type: string image_name: description: 镜像名称 required: true type: string image_tag: description: 镜像标签 required: false default: latest type: string build_context: description: 构建上下文目录 required: false type: string default: . dockerfile: description: Dockerfile 在仓库中的路径 required: false type: string default: Dockerfile architectures: description: 构建架构 required: false type: string default: "linux/amd64, linux/arm64, linux/arm/v7" build_args: description: 以空格分隔的列表,格式为`arg_name=value`,用于定义Dockerfile中指定的构建参数 required: false type: string build_target: description: 构建目标 required: false type: string env: IMAGE_DESCRIPTION: Built by GitHub Actions, the associated workflow is https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} permissions: contents: read packages: write id-token: write attestations: write jobs: publish-docker: name: Build and publish runs-on: ubuntu-latest steps: - name: Confirm current parameters env: REPO_URL: ${{ github.event.inputs.repo }} TARGET_REF: ${{ github.event.inputs.ref }} IMAGE_NAME: ${{ github.event.inputs.image_name }} IMAGE_TAG: ${{ github.event.inputs.image_tag }} BUILD_CONTEXT: ${{ github.event.inputs.build_context }} DOCKERFILE: ${{ github.event.inputs.dockerfile }} ARCHITECTURES: ${{ github.event.inputs.architectures }} BUILD_ARGS: ${{ github.event.inputs.build_args }} BUILD_TARGET: ${{ github.event.inputs.build_target }} run: | echo "REPO_URL=$REPO_URL" echo "TARGET_REF=$TARGET_REF" echo "IMAGE_NAME=$IMAGE_NAME" echo "IMAGE_TAG=$IMAGE_TAG" echo "BUILD_CONTEXT=$BUILD_CONTEXT" echo "DOCKERFILE=$DOCKERFILE" echo "ARCHITECTURES=$ARCHITECTURES" echo "BUILD_ARGS=$BUILD_ARGS" echo "BUILD_TARGET=$BUILD_TARGET" - name: Set env env: IMAGE_NAME: ${{ github.event.inputs.image_name }} IMAGE_TAG: ${{ github.event.inputs.image_tag }} run: | owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]') echo "OWNER=$owner" >> "$GITHUB_ENV" echo "IMAGE_FULL_NAME=ghcr.io/$owner/$IMAGE_NAME" >> $GITHUB_ENV echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV echo "FIRST_TAG=$(echo $IMAGE_TAG | cut -d',' -f1)" >> $GITHUB_ENV - name: Set multiple tags id: set-tags run: | TAGS_ARRAY=($(echo "${{ env.IMAGE_TAG }}" | tr ',' ' ')) TAGS_MULTILINE="" for tag in "${TAGS_ARRAY[@]}"; do TAGS_MULTILINE+="type=raw,value=${tag}\n" done if [[ "${{ env.IMAGE_TAG }}" != *"latest"* ]]; then TAGS_MULTILINE+="type=raw,value=latest\n" fi echo "tags<> $GITHUB_OUTPUT echo -e "$TAGS_MULTILINE" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - name: Checkout repository env: REPO_URL: ${{ github.event.inputs.repo }} TARGET_REF: ${{ github.event.inputs.ref }} run: | git clone $REPO_URL . git fetch --tags git checkout $TARGET_REF - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: '4.0.1' - name: Get license run: | gem install licensee license_result=$(licensee detect --json | jq -r '.licenses[0].spdx_id' 2>&1) status=$? if [ $status -eq 0 ]; then if [ "$license_result" = "null" ] || [ -z "$license_result" ]; then echo "LICENSES=none" >> "$GITHUB_ENV" else echo "LICENSES=$license_result" >> "$GITHUB_ENV" fi else echo "LICENSES=none" >> "$GITHUB_ENV" fi - name: Confirm current version run: | git describe --tags --always - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ env.OWNER }} password: ${{ github.token }} - name: Extract metadata (tags, labels) for image registry id: meta uses: docker/metadata-action@v5 with: images: ${{ env.IMAGE_FULL_NAME }} tags: ${{ steps.set-tags.outputs.tags }} annotations: | org.opencontainers.image.description=${{ env.IMAGE_DESCRIPTION }} org.opencontainers.image.url=${{ github.event.inputs.repo }} org.opencontainers.image.source=${{ github.event.inputs.repo }} org.opencontainers.image.title=${{ env.IMAGE_NAME }} org.opencontainers.image.version=${{ env.FIRST_TAG }} ${{ env.LICENSES != 'none' && format('org.opencontainers.image.licenses={0}', env.LICENSES) || '' }} labels: | org.opencontainers.image.description=${{ env.IMAGE_DESCRIPTION }} org.opencontainers.image.url=${{ github.event.inputs.repo }} org.opencontainers.image.source=${{ github.event.inputs.repo }} org.opencontainers.image.title=${{ env.IMAGE_NAME }} org.opencontainers.image.version=${{ env.FIRST_TAG }} ${{ env.LICENSES != 'none' && format('org.opencontainers.image.licenses={0}', env.LICENSES) || '' }} env: DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push id: push uses: docker/build-push-action@v6 with: build-args: | ${{ github.event.inputs.build_args }} context: ${{ github.event.inputs.build_context}} target: ${{ github.event.inputs.build_target }} push: true file: ${{ github.event.inputs.dockerfile }} tags: ${{ steps.meta.outputs.tags }} annotations: ${{ steps.meta.outputs.annotations }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max platforms: ${{ github.event.inputs.architectures }} - name: Calculate image size run: | docker manifest inspect ${{ env.IMAGE_FULL_NAME }}:${{ env.FIRST_TAG }} -v > manifest.json curl -o ./get_image_size.sh -s https://raw.githubusercontent.com/${{ github.repository }}/refs/heads/script/get_image_size.sh chmod +x get_image_size.sh - name: Get the package id run: | gh auth login --with-token <<< "${{ github.token }}" response=$(gh api \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ /users/${{ env.OWNER }}/packages/container/${{ env.IMAGE_NAME }}/versions) version_id=$(echo "$response" | jq -r '.[] | select(.metadata.container.tags[]? == "${{ env.FIRST_TAG }}") | .id') echo "VERSION_ID=$version_id" >> $GITHUB_ENV - name: Add a summary for the job run: | arch_array=($(echo ${{ github.event.inputs.architectures }} | tr -d '"' | tr ',' '\n' | tr -d ' ')) echo "## Build Report" >> $GITHUB_STEP_SUMMARY echo "### Get the image" >> $GITHUB_STEP_SUMMARY echo '
' >> $GITHUB_STEP_SUMMARY
          echo "docker pull ${{ env.IMAGE_FULL_NAME }}:${{ env.FIRST_TAG }}" >> $GITHUB_STEP_SUMMARY
          echo "
" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Image Url: https://github.com/users/${{ env.OWNER }}/packages/container/${{ env.IMAGE_NAME }}/${{ env.VERSION_ID }}?tag=${{ env.FIRST_TAG }}" >> $GITHUB_STEP_SUMMARY echo "### Image info" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Repository | Reference / Branch / Tag | Push Time |" >> $GITHUB_STEP_SUMMARY echo "|:-:|:-:|:-:|" >> $GITHUB_STEP_SUMMARY echo "| ${{ github.event.inputs.repo }} | [$(git log --pretty=format:"%h" -n 1)](${{ github.event.inputs.repo }}/tree/$(git log --pretty=format:"%H" -n 1)) |$(git log --pretty=format:"%ci" -n 1)|" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Architecture | Image Size |" >> $GITHUB_STEP_SUMMARY echo "|:-:|:-:|" >> $GITHUB_STEP_SUMMARY for arch in "${arch_array[@]}"; do echo "| $arch | $(./get_image_size.sh --arch $arch) |" >> $GITHUB_STEP_SUMMARY done echo "### Verify the image" >> $GITHUB_STEP_SUMMARY echo "You can use github cli to verify the image, run the command:" >> $GITHUB_STEP_SUMMARY echo '
' >> $GITHUB_STEP_SUMMARY
          echo "gh attestation verify oci://${{ env.IMAGE_FULL_NAME }}:${{ env.FIRST_TAG }} -R ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY
          echo "
" >> $GITHUB_STEP_SUMMARY - name: Generate artifact attestation uses: actions/attest@v4 with: subject-name: ${{ env.IMAGE_FULL_NAME }} subject-digest: ${{ steps.push.outputs.digest }} push-to-registry: true