Skip to content

feat: extend container reusables (7 build inputs + tolerate-pull-failure)#143

Merged
CybotTM merged 3 commits into
mainfrom
feat/container-reusables-extensions
May 22, 2026
Merged

feat: extend container reusables (7 build inputs + tolerate-pull-failure)#143
CybotTM merged 3 commits into
mainfrom
feat/container-reusables-extensions

Conversation

@CybotTM
Copy link
Copy Markdown
Member

@CybotTM CybotTM commented May 21, 2026

Summary

Two atomic commits, both unblock the Phase 2 caller migration in netresearch/snipe-it-docker-compose-stack#11 (build.yml + security.yml currently cannot delegate to these reusables because of input gaps).

Commit 1 — build-container.yml: +7 inputs

Input Type Purpose
target string Multi-stage build target (snipe-it: runtime)
build-args string (multi-line) Newline-separated KEY=VALUE for Dockerfile ARGs
cache-scope string GHA cache scope name — parallel matrix cells stay separate
provenance string Buildx provenance mode (snipe-it: mode=max)
sbom boolean In-image SBOM (default false, backward compat)
metadata-tags string (multi-line) Override metadata-action's tags: for bespoke fan-out
metadata-labels string (multi-line) OCI labels appended to auto-generated set

Backward compatible: every new input has a safe default that preserves current behavior.

The metadata-action's default 5 tag patterns are now computed in a shell step gated on metadata-tags == '', then passed as a step output. Keeps the YAML expression-syntax simple without losing flexibility.

Commit 2 — security-container.yml: +tolerate-pull-failure

Boolean input. When true, the Trivy step gets continue-on-error: true. Use case: caller fans a matrix over tags where some images may not exist (rolling-variants blocked by composer audit). Job-level continue-on-error: on a reusable-caller is forbidden by GitHub, so only step-level (inside the reusable) works.

Off by default → no behavioral change for existing callers.

Caller migration sketch (snipe-it Phase 2 follow-up)

jobs:
  build:
    uses: netresearch/.github/.github/workflows/build-container.yml@main
    permissions:
      contents: read
      packages: write
      security-events: write
      id-token: write
      attestations: write
    with:
      image-name: snipe-it-php-fpm
      ref: ${{ steps.ref.outputs.ref }}
      target: runtime
      build-args: |
        SNIPE_IT_VERSION=${{ steps.ref.outputs.ref }}
        BUILD_DATE=${{ steps.ref.outputs.build_date }}
        VCS_REF=${{ github.sha }}
        ROLLING_DEPS=${{ matrix.composer == 'rolling' }}
      provenance: mode=max
      sbom: true
      cache-scope: ${{ matrix.track.name }}-${{ matrix.composer }}
      metadata-tags: ${{ steps.tags.outputs.tags }}
      attest: true
      sign: true

  security:
    uses: netresearch/.github/.github/workflows/security-container.yml@main
    permissions:
      contents: read
      security-events: write
      packages: read
    with:
      image-ref: ghcr.io/.../snipe-it-php-fpm:${{ matrix.tag }}
      tolerate-pull-failure: ${{ matrix.composer == 'rolling' }}

Verification

  • actionlint clean on both modified files
  • All new inputs default to backward-compatible values
  • Signed commits + DCO + conventional-commit prefix

After this lands

  • snipe-it-docker-compose-stack#11 can absorb the build.yml + security.yml migration as a follow-up commit. Realistic line-count for build.yml: ~301 → ~150 (matrix + ref-resolution + per-cell tag computation stay in the caller).

CybotTM added 2 commits May 21, 2026 23:35
The Phase 2 sub-agent migrating netresearch/snipe-it-docker-compose-stack
to this reusable identified six gaps that prevented build.yml from
delegating its build/push/sign/attest core. This commit adds them:

  target          — multi-stage target stage (snipe-it: "runtime")
  build-args      — newline-separated KEY=VALUE for Dockerfile ARGs
                    (snipe-it: SNIPE_IT_VERSION, BUILD_DATE, VCS_REF,
                    ROLLING_DEPS)
  cache-scope     — GHA cache scope name. When set, both cache-from
                    and cache-to suffix the scope so parallel matrix
                    calls (snipe-it: track × composer) don't share a
                    cache. Empty = single global cache (current
                    backward-compatible default).
  provenance      — buildx provenance mode passed to build-push-action
                    (snipe-it: "mode=max"). Distinct from the existing
                    `attest:` input which uses actions/attest-build-
                    provenance for SLSA OUTside the image.
  sbom            — boolean, in-image SBOM via build-push-action
                    (off by default for backward compat).
  metadata-tags   — caller-provided override for docker/metadata-
                    action's tags: input. When set, REPLACES the
                    reusable's default 5 patterns (ref-branch /
                    ref-pr / semver-{version,M.m,M}). Snipe-it's
                    bespoke tag fan-out (`:8.5.0` / `:8.5` / `:8`
                    plus `-rolling` variants) goes here.
  metadata-labels — caller-provided OCI labels (newline-separated
                    KEY=VALUE), APPENDED to the auto-generated set
                    (created/revision/source/etc.). For image.title,
                    image.description, image.licenses overrides.

The default 5 tag patterns are now computed in a shell step gated on
`metadata-tags == ''`, then passed to metadata-action as a step
output. This keeps the action expression syntax simple while
preserving exact backward compatibility for callers that don't
override `metadata-tags`.

cache-scope handling uses GitHub Actions expression `format()` with
conditional inclusion — empty input produces `type=gha` and
`type=gha,mode=max` (current behaviour); set input produces
`type=gha,scope=<name>` etc.

Verified: actionlint clean on the modified file. The seventh input
(SBOM) was added because sub-agent reported "provenance: mode=max /
sbom: true" as a single gap — they're separate build-push-action
inputs that snipe-it uses together.

Companion: security-container.yml gets its own `tolerate-pull-failure`
input in the next commit on this branch.

Refs: netresearch/snipe-it-docker-compose-stack#11 (Phase 2 caller PR
that surfaced these gaps).

Signed-off-by: Sebastian Mendel <sebastian.mendel@netresearch.de>
Companion to the build-container.yml input expansion in e5cdafb.
Surfaces a `tolerate-pull-failure` boolean input that gives the
Trivy step `continue-on-error: true` when set. Off by default.

Motivation: callers that fan a matrix over may-not-exist image tags
(snipe-it's rolling-variant images, where composer audit can block
the build for a specific matrix cell and leave no image to scan) need
this semantic. Job-level `continue-on-error:` on the reusable-caller
side is forbidden by GitHub — only step-level continue-on-error
works, and only inside the reusable itself.

When tolerate-pull-failure is true:
  - Trivy's pull failure (manifest unknown / 401 / image not found)
    is treated as a non-error and the job continues.
  - SARIF upload is gated on hashFiles('trivy-results.sarif') != ''
    (already in place), so the upload step skips cleanly when no
    file was produced.

When false (default), the job correctly fails on a legitimate pull
failure — backward compatible.

Refs: netresearch/snipe-it-docker-compose-stack#11 (Phase 2 caller PR
that surfaced this gap).

Signed-off-by: Sebastian Mendel <sebastian.mendel@netresearch.de>
Copilot AI review requested due to automatic review settings May 21, 2026 21:36
@gemini-code-assist
Copy link
Copy Markdown

Note

Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the repository’s reusable container workflows to support more advanced caller use cases (multi-stage targets, build args, cache scoping, custom metadata tags/labels) and to optionally tolerate Trivy pull failures in post-build security scans—unblocking downstream workflow migrations that currently can’t delegate due to input gaps.

Changes:

  • Added 7 new inputs to build-container.yml and wired them through to docker/metadata-action and docker/build-push-action (including overrideable metadata tags and cache scoping).
  • Added tolerate-pull-failure to security-container.yml to optionally set continue-on-error on the Trivy step.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
.github/workflows/build-container.yml Adds new reusable inputs for build customization (target, build args, cache scope, provenance/SBOM, and metadata overrides) and computes default metadata tag patterns when not overridden.
.github/workflows/security-container.yml Adds an input to optionally tolerate Trivy step failures by enabling step-level continue-on-error.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/security-container.yml
Copilot caught a real gap in fa8c444: putting `continue-on-error: true`
directly on the Trivy step suppressed ALL Trivy failures, not just
pull/manifest failures. A caller that set `exit-code: '1'` to make
CVE findings fatal would silently get those findings swallowed by the
same flag that was supposed to only tolerate "image doesn't exist".

Refactored to a pre-flight pattern:

  1. New "Pre-flight — image manifest exists" step (id: manifest_check)
     - Only runs when inputs.tolerate-pull-failure is true.
     - `docker manifest inspect <image-ref>` is a cheap HEAD-style probe
       (no full layer pull). Succeeds → manifest available; fails →
       image not present in registry.
     - `continue-on-error: true` here is scoped to this single probe;
       it doesn't affect any other step.

  2. Run Trivy step now gated on `steps.manifest_check.outcome != 'failure'`.
     - When tolerate-pull-failure is false (default), manifest_check is
       skipped — `outcome` is empty string, !='failure' → Trivy runs.
       (Backward compatible: no behavioral change for existing callers.)
     - When tolerate-pull-failure is true AND manifest exists:
       manifest_check succeeds → Trivy runs with NORMAL exit semantics.
       CVE findings + caller's `exit-code: '1'` still produce real
       failures.
     - When tolerate-pull-failure is true AND manifest missing:
       manifest_check fails (but the step's continue-on-error swallows
       it for job purposes); Trivy is skipped. Job exits green.

The existing SARIF-upload gate `hashFiles('trivy-results.sarif') != ''`
already handles the case where Trivy didn't run, so nothing else needed
to change.

Verified: actionlint clean.

Refs: PR 143 comment from copilot-pull-request-reviewer
(#143 (comment)...).

Signed-off-by: Sebastian Mendel <sebastian.mendel@netresearch.de>
@sonarqubecloud
Copy link
Copy Markdown

@CybotTM CybotTM merged commit d1112fd into main May 22, 2026
9 checks passed
@CybotTM CybotTM deleted the feat/container-reusables-extensions branch May 22, 2026 05:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants