diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e7d0e5b..d75d49f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,7 +4,13 @@ updates: directory: "/" schedule: interval: "daily" + cooldown: + default-days: 2 open-pull-requests-limit: 10 + groups: + crazy-max-dot-github: + patterns: + - "crazy-max/.github/*" labels: - "area/dependencies" - "bot" diff --git a/.github/workflows/.pr-assign-author.yml b/.github/workflows/.pr-assign-author.yml index a806bd9..f433362 100644 --- a/.github/workflows/.pr-assign-author.yml +++ b/.github/workflows/.pr-assign-author.yml @@ -1,6 +1,5 @@ name: .pr-assign-author -# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions permissions: contents: read @@ -11,41 +10,8 @@ on: - reopened jobs: - assign-author: - runs-on: ubuntu-24.04 + run: + uses: crazy-max/.github/.github/workflows/pr-assign-author.yml@bb328ea508cd6a89d0865555ddbeb148e5724aed # v1.3.0 permissions: contents: read pull-requests: write - steps: - - - name: Assigning author to PR - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - try { - const dt = context.payload?.pull_request; - if (!dt) { - throw new Error(`No pull request payload found, skipping.`); - } - - const { assignees, number, user: { login: author, type } } = dt; - if (assignees.length > 0) { - throw new Error(`Pull request is already assigned to someone, skipping.`); - } else if (type !== 'User') { - throw new Error(`Not a user, skipping.`); - } - - const respAdd = await github.rest.issues.addAssignees({ - ...context.repo, - issue_number: number, - assignees: [author] - }); - core.debug(`addAssignees resp: ${JSON.stringify(respAdd, null, 2)}`); - if (respAdd.status !== 201) { - throw new Error(`Failed to assign @${author} to the pull request #${number}.`); - } - - core.info(`@${author} has been assigned to the pull request #${number}`); - } catch (e) { - core.warning(e.message); - } diff --git a/.github/workflows/.test-bake.yml b/.github/workflows/.test-bake.yml index 2ae83e0..d16b0bd 100644 --- a/.github/workflows/.test-bake.yml +++ b/.github/workflows/.test-bake.yml @@ -1,6 +1,5 @@ name: .test-bake -# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions permissions: contents: read @@ -596,3 +595,29 @@ jobs: sbom: true sign: ${{ github.event_name != 'pull_request' }} target: go-cross-with-contexts + + bake-ghcr-index-annotations: + uses: ./.github/workflows/bake.yml + permissions: + contents: read + id-token: write + packages: write + with: + context: test + output: image + push: ${{ github.event_name != 'pull_request' }} + sbom: true + set: | + *.args.VERSION={{meta.version}} + target: hello-cross + set-meta-annotations: true + meta-images: ghcr.io/docker/github-builder-test + meta-tags: | + type=raw,value=bake-index-annotations-${{ github.run_id }} + meta-annotations: | + io.github.docker.github-builder.test-index-annotation=bake-${{ github.run_id }} + secrets: + registry-auths: | + - registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/.test-build.yml b/.github/workflows/.test-build.yml index c8c81e5..c8de7d2 100644 --- a/.github/workflows/.test-build.yml +++ b/.github/workflows/.test-build.yml @@ -1,6 +1,5 @@ name: .test-build -# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions permissions: contents: read @@ -615,3 +614,29 @@ jobs: - registry: registry-1-stage.docker.io username: ${{ vars.DOCKERHUB_STAGE_USERNAME }} password: ${{ secrets.DOCKERHUB_STAGE_TOKEN }} + + build-ghcr-index-annotations: + uses: ./.github/workflows/build.yml + permissions: + contents: read + id-token: write + packages: write + with: + annotations: | + io.github.docker.github-builder.test-index-annotation=build-${{ github.run_id }} + build-args: | + VERSION={{meta.version}} + file: test/hello.Dockerfile + output: image + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + sbom: true + set-meta-annotations: true + meta-images: ghcr.io/docker/github-builder-test + meta-tags: | + type=raw,value=build-index-annotations-${{ github.run_id }} + secrets: + registry-auths: | + - registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/.zizmor.yml b/.github/workflows/.zizmor.yml index 876517f..62cf85b 100644 --- a/.github/workflows/.zizmor.yml +++ b/.github/workflows/.zizmor.yml @@ -1,6 +1,5 @@ name: .zizmor -# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions permissions: contents: read @@ -16,50 +15,13 @@ on: - 'releases/v*' pull_request: -env: - ZIZMOR_VERSION: 1.22.0 # https://github.com/zizmorcore/zizmor - jobs: zizmor: - runs-on: ubuntu-24.04 + uses: crazy-max/.github/.github/workflows/zizmor.yml@bb328ea508cd6a89d0865555ddbeb148e5724aed # v1.3.0 permissions: contents: read security-events: write - env: - TMPDIR: /tmp/zizmor - steps: - - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Setup uv - uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0 - with: - enable-cache: false - - - name: Install zizmor - run: | - set -ex - uv tool install zizmor@${ZIZMOR_VERSION} - - - name: Run zizmor - id: zizmor - run: | - mkdir -p ${TMPDIR} - set -ex - zizmor --min-severity=medium --min-confidence=medium --persona=pedantic --no-online-audits --format=sarif . > ${TMPDIR}/zizmor.sarif - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Zizmor crash report - if: ${{ failure() && steps.zizmor.conclusion == 'failure' }} - run: | - cat ${TMPDIR}/report-*.toml - - - name: Upload SARIF report - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 - with: - sarif_file: ${{ env.TMPDIR }}/zizmor.sarif - category: zizmor + with: + min-severity: medium + min-confidence: medium + persona: pedantic diff --git a/.github/workflows/bake.yml b/.github/workflows/bake.yml index ba2ca7e..c2a3c13 100644 --- a/.github/workflows/bake.yml +++ b/.github/workflows/bake.yml @@ -13,6 +13,11 @@ on: description: "Whether to distribute the build across multiple runners (one platform per runner)" required: false default: true + fail-fast: + type: boolean + description: "Whether to cancel all in-progress and queued jobs in the matrix if any job fails" + required: false + default: false setup-qemu: type: boolean description: "Runs the setup-qemu-action step to install QEMU static binaries" @@ -150,14 +155,15 @@ on: env: BUILDX_VERSION: "v0.32.1" - BUILDKIT_IMAGE: "moby/buildkit:v0.28.0" + BUILDKIT_IMAGE: "moby/buildkit:v0.28.1" SBOM_IMAGE: "docker/buildkit-syft-scanner:1.10.0" BINFMT_IMAGE: "tonistiigi/binfmt:qemu-v10.2.1-65" - DOCKER_ACTIONS_TOOLKIT_MODULE: "@docker/actions-toolkit@0.83.0" + DOCKER_ACTIONS_TOOLKIT_MODULE: "@docker/actions-toolkit@0.85.0" HANDLEBARS_MODULE: "handlebars@4.7.8" COSIGN_VERSION: "v3.0.2" LOCAL_EXPORT_DIR: "/tmp/buildx-output" MATRIX_SIZE_LIMIT: "20" + BUILDX_SEND_GIT_QUERY_AS_INPUT: "true" jobs: prepare: @@ -226,6 +232,14 @@ jobs: } }); } + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + with: + version: ${{ env.BUILDX_VERSION }} + cache-binary: false + driver-opts: | + image=${{ env.BUILDKIT_IMAGE }} - name: Expose GitHub Runtime uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487 # v4.0.0 @@ -254,6 +268,7 @@ jobs: script: | const os = require('os'); const { Bake } = require('@docker/actions-toolkit/lib/buildx/bake'); + const { Build } = require('@docker/actions-toolkit/lib/buildx/build'); const { GitHub } = require('@docker/actions-toolkit/lib/github/github'); const { Util } = require('@docker/actions-toolkit/lib/util'); @@ -300,7 +315,7 @@ jobs: return; } - const bakeSource = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}.git#${process.env.GITHUB_REF}:${inpContext}`; + const bakeSource = await new Build().gitContext({subdir: inpContext}); await core.group(`Set bake source`, async () => { core.info(bakeSource); }); @@ -431,7 +446,7 @@ jobs: needs: - prepare strategy: - fail-fast: false + fail-fast: ${{ inputs.fail-fast }} matrix: include: ${{ fromJson(needs.prepare.outputs.includes) }} outputs: @@ -668,7 +683,7 @@ jobs: }; const renderTemplate = value => Handlebars.compile(value, {noEscape: true})({meta}); - const bakeSource = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}.git#${process.env.GITHUB_REF}:${inpContext}`; + const bakeSource = await new Build().gitContext({subdir: inpContext}); await core.group(`Set source output`, async () => { core.info(bakeSource); core.setOutput('source', bakeSource); @@ -781,7 +796,7 @@ jobs: targets: ${{ steps.prepare.outputs.target }} sbom: ${{ steps.prepare.outputs.sbom }} set: ${{ steps.prepare.outputs.overrides }} - env: ${{ fromJson(steps.prepare.outputs.envs) }} + env: ${{ fromJson(steps.prepare.outputs.envs || '{}') }} - name: Get image digest id: get-image-digest @@ -976,6 +991,8 @@ jobs: INPUT_IMAGE-NAMES: ${{ inputs.meta-images }} INPUT_TAG-NAMES: ${{ steps.meta.outputs.tag-names }} INPUT_BUILD-OUTPUTS: ${{ toJSON(needs.build.outputs) }} + INPUT_SET-META-ANNOTATIONS: ${{ inputs.set-meta-annotations }} + INPUT_META-ANNOTATIONS: ${{ steps.meta.outputs.annotations }} with: script: | const { ImageTools } = require('@docker/actions-toolkit/lib/buildx/imagetools'); @@ -984,6 +1001,28 @@ jobs: const inpImageNames = core.getMultilineInput('image-names'); const inpTagNames = core.getMultilineInput('tag-names'); const inpBuildOutputs = JSON.parse(core.getInput('build-outputs')); + const inpSetMetaAnnotations = core.getBooleanInput('set-meta-annotations'); + const inpMetaAnnotations = core.getMultilineInput('meta-annotations'); + + const toIndexAnnotation = annotation => { + const keyEnd = annotation.indexOf('='); + const rawKey = keyEnd === -1 ? annotation : annotation.substring(0, keyEnd); + const rawValue = keyEnd === -1 ? '' : annotation.substring(keyEnd); + const typeSeparator = rawKey.indexOf(':'); + if (typeSeparator !== -1) { + const typeExpr = rawKey.substring(0, typeSeparator); + const key = rawKey.substring(typeSeparator + 1); + const hasKnownType = typeExpr.split(',').map(type => type.replace(/\[.*\]$/, '')).some(type => ['manifest', 'index', 'manifest-descriptor', 'index-descriptor'].includes(type)); + if (hasKnownType) { + return `index:${key}${rawValue}`; + } + } + return `index:${annotation}`; + }; + const indexAnnotations = []; + if (inpSetMetaAnnotations && inpMetaAnnotations.length > 0) { + indexAnnotations.push(...inpMetaAnnotations.filter(annotation => annotation.length > 0).map(toIndexAnnotation)); + } const digests = []; for (const key of Object.keys(inpBuildOutputs)) { @@ -1006,6 +1045,7 @@ jobs: const result = await new ImageTools().create({ sources: digests, tags: tags, + annotations: indexAnnotations, skipExec: !inpPush }); if (inpPush) { diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d4ff39..e00e9a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,6 +13,11 @@ on: description: "Whether to distribute the build across multiple runners (one platform per runner)" required: false default: true + fail-fast: + type: boolean + description: "Whether to cancel all in-progress and queued jobs in the matrix if any job fails" + required: false + default: false setup-qemu: type: boolean description: "Runs the setup-qemu-action step to install QEMU static binaries" @@ -153,14 +158,15 @@ on: env: BUILDX_VERSION: "v0.32.1" - BUILDKIT_IMAGE: "moby/buildkit:v0.28.0" + BUILDKIT_IMAGE: "moby/buildkit:v0.28.1" SBOM_IMAGE: "docker/buildkit-syft-scanner:1.10.0" BINFMT_IMAGE: "tonistiigi/binfmt:qemu-v10.2.1-65" - DOCKER_ACTIONS_TOOLKIT_MODULE: "@docker/actions-toolkit@0.83.0" + DOCKER_ACTIONS_TOOLKIT_MODULE: "@docker/actions-toolkit@0.85.0" HANDLEBARS_MODULE: "handlebars@4.7.8" COSIGN_VERSION: "v3.0.2" LOCAL_EXPORT_DIR: "/tmp/buildx-output" MATRIX_SIZE_LIMIT: "20" + BUILDX_SEND_GIT_QUERY_AS_INPUT: "true" jobs: prepare: @@ -333,7 +339,7 @@ jobs: needs: - prepare strategy: - fail-fast: false + fail-fast: ${{ inputs.fail-fast }} matrix: include: ${{ fromJson(needs.prepare.outputs.includes) }} outputs: @@ -570,7 +576,7 @@ jobs: const renderTemplate = value => Handlebars.compile(value, {noEscape: true})({meta}); const toMultilineInput = value => value.split(/\r?\n/).map(line => line.trim()).filter(Boolean); - const buildContext = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}.git#${process.env.GITHUB_REF}:${inpContext}`; + const buildContext = await new Build().gitContext({subdir: inpContext}); core.setOutput('context', buildContext); switch (inpOutput) { @@ -839,6 +845,9 @@ jobs: INPUT_IMAGE-NAMES: ${{ inputs.meta-images }} INPUT_TAG-NAMES: ${{ steps.meta.outputs.tag-names }} INPUT_BUILD-OUTPUTS: ${{ toJSON(needs.build.outputs) }} + INPUT_ANNOTATIONS: ${{ inputs.annotations }} + INPUT_SET-META-ANNOTATIONS: ${{ inputs.set-meta-annotations }} + INPUT_META-ANNOTATIONS: ${{ steps.meta.outputs.annotations }} with: script: | const { ImageTools } = require('@docker/actions-toolkit/lib/buildx/imagetools'); @@ -847,6 +856,29 @@ jobs: const inpImageNames = core.getMultilineInput('image-names'); const inpTagNames = core.getMultilineInput('tag-names'); const inpBuildOutputs = JSON.parse(core.getInput('build-outputs')); + const inpAnnotations = core.getMultilineInput('annotations'); + const inpSetMetaAnnotations = core.getBooleanInput('set-meta-annotations'); + const inpMetaAnnotations = core.getMultilineInput('meta-annotations'); + + const toIndexAnnotation = annotation => { + const keyEnd = annotation.indexOf('='); + const rawKey = keyEnd === -1 ? annotation : annotation.substring(0, keyEnd); + const rawValue = keyEnd === -1 ? '' : annotation.substring(keyEnd); + const typeSeparator = rawKey.indexOf(':'); + if (typeSeparator !== -1) { + const typeExpr = rawKey.substring(0, typeSeparator); + const key = rawKey.substring(typeSeparator + 1); + const hasKnownType = typeExpr.split(',').map(type => type.replace(/\[.*\]$/, '')).some(type => ['manifest', 'index', 'manifest-descriptor', 'index-descriptor'].includes(type)); + if (hasKnownType) { + return `index:${key}${rawValue}`; + } + } + return `index:${annotation}`; + }; + if (inpSetMetaAnnotations && inpMetaAnnotations.length > 0) { + inpAnnotations.push(...inpMetaAnnotations); + } + const indexAnnotations = inpAnnotations.filter(annotation => annotation.length > 0).map(toIndexAnnotation); const digests = []; for (const key of Object.keys(inpBuildOutputs)) { @@ -869,6 +901,7 @@ jobs: const result = await new ImageTools().create({ sources: digests, tags: tags, + annotations: indexAnnotations, skipExec: !inpPush }); if (inpPush) { diff --git a/.github/zizmor.yml b/.github/zizmor.yml index efe8dec..5c40748 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -7,3 +7,7 @@ rules: - bake.yml - build.yml - verify.yml + + # FIXME: remove this rule when zizmor 1.24.0 is released, fixing the right persona attached to this rule: https://github.com/zizmorcore/zizmor/pull/1783 + secrets-outside-env: + disable: true diff --git a/README.md b/README.md index 742320d..25eb553 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ on: - 'v*' pull_request: +jobs: build: uses: docker/github-builder/.github/workflows/build.yml@v1 permissions: @@ -166,6 +167,7 @@ on: - 'v*' pull_request: +jobs: build: uses: docker/github-builder/.github/workflows/build.yml@v1 permissions: @@ -222,6 +224,7 @@ on: |------------------------|----------|--------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `runner` | String | `auto` | [Ubuntu GitHub Hosted Runner](https://github.com/actions/runner-images?tab=readme-ov-file#available-images) to build on (one of `auto`, `amd64`, `arm64`). The `auto` runner selects the best-matching runner based on target `platforms`. You can set it to `amd64` if your build doesn't require emulation (e.g. cross-compilation) | | `distribute` | Bool | `true` | Whether to distribute the build across multiple runners (one platform per runner) | +| `fail-fast` | Bool | `false` | Whether to cancel all in-progress and queued jobs in the matrix if any job fails | | `setup-qemu` | Bool | `false` | Runs the `setup-qemu-action` step to install QEMU static binaries | | `artifact-name` | String | `docker-github-builder-assets` | Name of the uploaded GitHub artifact (for `local` output) | | `artifact-upload` | Bool | `false` | Upload build output GitHub artifact (for `local` output) | @@ -317,6 +320,7 @@ on: - 'v*' pull_request: +jobs: bake: uses: docker/github-builder/.github/workflows/bake.yml@v1 permissions: @@ -369,6 +373,7 @@ on: |------------------------|--------|--------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `runner` | String | `auto` | [Ubuntu GitHub Hosted Runner](https://github.com/actions/runner-images?tab=readme-ov-file#available-images) to build on (one of `auto`, `amd64`, `arm64`). The `auto` runner selects the best-matching runner based on target `platforms`. You can set it to `amd64` if your build doesn't require emulation (e.g. cross-compilation) | | `distribute` | Bool | `true` | Whether to distribute the build across multiple runners (one platform per runner) | +| `fail-fast` | Bool | `false` | Whether to cancel all in-progress and queued jobs in the matrix if any job fails | | `setup-qemu` | Bool | `false` | Runs the `setup-qemu-action` step to install QEMU static binaries | | `artifact-name` | String | `docker-github-builder-assets` | Name of the uploaded GitHub artifact (for `local` output) | | `artifact-upload` | Bool | `false` | Upload build output GitHub artifact (for `local` output) |