diff --git a/.github/actions/apply-oci-labels/action.yml b/.github/actions/apply-oci-labels/action.yml index af29915..ee16669 100644 --- a/.github/actions/apply-oci-labels/action.yml +++ b/.github/actions/apply-oci-labels/action.yml @@ -26,15 +26,15 @@ inputs: required: false default: "" dockerfile: - description: "Path to the Dockerfile. The last FROM line drives base.name / base.digest resolution unless base-name/base-digest overrides are set." + description: "Deprecated no-op. Base image labels are intentionally not emitted by this action." required: false default: "./Dockerfile" base-name: - description: "Override for org.opencontainers.image.base.name. Set this for build systems where the Dockerfile is not parseable (Family B-E)." + description: "Deprecated no-op. Base image labels are intentionally not emitted by this action." required: false default: "" base-digest: - description: "Override for org.opencontainers.image.base.digest." + description: "Deprecated no-op. Base image labels are intentionally not emitted by this action." required: false default: "" documentation-url: @@ -42,12 +42,7 @@ inputs: required: false default: "https://documentation.suse.com/cloudnative/suse-observability/latest/en/classic.html" registry-credentials: - description: | - Optional JSON array of registry credentials. Each entry is an object with - keys `registry`, `username`, `password`. The action runs `docker login` - for every entry before resolving base image metadata, so private-registry - bases can be inspected. Example: - [{"registry": "dp.apps.rancher.io", "username": "...", "password": "..."}] + description: "Deprecated no-op. Kept for caller compatibility." required: false default: "[]" @@ -59,11 +54,6 @@ outputs: runs: using: composite steps: - # Precondition: docker buildx must be set up by the caller. push-single-arch - # already does this via docker/setup-buildx-action. The action relies on - # `docker buildx imagetools inspect` to resolve the base image digest without - # pulling the image. - - name: Validate required inputs shell: bash env: @@ -81,74 +71,6 @@ runs: exit 1 fi - # `docker buildx imagetools inspect` (used to resolve base.digest below) - # hits the registry API directly and needs auth for non-public bases. We - # loop over the JSON array with --password-stdin so credentials never - # appear on the command line. Matches scan-image's bash login pattern. - - name: Login to source registries - if: ${{ inputs.registry-credentials != '[]' && inputs.registry-credentials != '' }} - shell: bash - env: - REGISTRY_CREDENTIALS: ${{ inputs.registry-credentials }} - run: | - set -euo pipefail - printf '%s' "${REGISTRY_CREDENTIALS}" | jq -c '.[]' | while IFS= read -r entry; do - registry=$(printf '%s' "${entry}" | jq -r '.registry // ""') - username=$(printf '%s' "${entry}" | jq -r '.username // ""') - password=$(printf '%s' "${entry}" | jq -r '.password // ""') - if [ -z "${registry}" ] || [ -z "${username}" ] || [ -z "${password}" ]; then - echo "::error::registry-credentials entry missing one of registry/username/password" >&2 - exit 1 - fi - printf '%s' "${password}" | docker login "${registry}" --username "${username}" --password-stdin - done - - - name: Resolve base image - id: base - shell: bash - env: - DOCKERFILE: ${{ inputs.dockerfile }} - BASE_NAME_IN: ${{ inputs.base-name }} - BASE_DIGEST_IN: ${{ inputs.base-digest }} - run: | - set -euo pipefail - - if [ -n "${BASE_NAME_IN}" ]; then - base_name="${BASE_NAME_IN}" - else - if [ ! -f "${DOCKERFILE}" ]; then - echo "::error::dockerfile not found and no base-name override given: ${DOCKERFILE}" >&2 - exit 1 - fi - - # Export ARG defaults from the Dockerfile so envsubst can expand ${...} in FROM. - # Only ARGs with a default value (ARG NAME=VALUE) are considered. - while IFS= read -r line; do - if [[ "${line}" =~ ^[[:space:]]*ARG[[:space:]]+([A-Za-z_][A-Za-z0-9_]*)=([^[:space:]]+) ]]; then - export "${BASH_REMATCH[1]}=${BASH_REMATCH[2]}" - fi - done < "${DOCKERFILE}" - - # Last FROM wins. Strip an optional --platform=... flag and a trailing `AS `. - raw=$(grep -E '^[[:space:]]*FROM[[:space:]]' "${DOCKERFILE}" | tail -n1 \ - | sed -E 's/^[[:space:]]*FROM[[:space:]]+(--platform=[^[:space:]]+[[:space:]]+)?([^[:space:]]+).*/\2/') - base_name=$(envsubst <<< "${raw}") - - if [ -z "${base_name}" ]; then - echo "::error::failed to resolve base image from ${DOCKERFILE}" >&2 - exit 1 - fi - fi - - if [ -n "${BASE_DIGEST_IN}" ]; then - base_digest="${BASE_DIGEST_IN}" - else - base_digest=$(docker buildx imagetools inspect "${base_name}" --format '{{json .Manifest}}' | jq -er '.digest') - fi - - echo "name=${base_name}" >> "$GITHUB_OUTPUT" - echo "digest=${base_digest}" >> "$GITHUB_OUTPUT" - - name: Compose label set id: compose shell: bash @@ -160,8 +82,6 @@ runs: COMPONENT: ${{ inputs.component }} PRODUCT: ${{ inputs.product }} UPSTREAM: ${{ inputs.upstream-name }} - BASE_NAME: ${{ steps.base.outputs.name }} - BASE_DIGEST: ${{ steps.base.outputs.digest }} DOC_URL: ${{ inputs.documentation-url }} SERVER_URL: ${{ github.server_url }} REPO: ${{ github.repository }} @@ -182,9 +102,9 @@ runs: run_url="${source_url}/actions/runs/${RUN_ID}" created=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - # Canonical SUSE Observability OCI label set (docker-image-oci-labels.md §4). - # Rows 15-18 (opensuse.reference, artifacthub.{logo,readme}-url, - # openbuildservice.disturl) are deferred per §8.6 and NOT emitted here. + # Canonical SUSE Observability label set. Base image labels are omitted + # deliberately: resolving them requires Dockerfile parsing and registry + # lookups that are brittle enough to block image publication. { echo 'labels<<__LABELS_EOF__' echo "org.opencontainers.image.title=${TITLE}" @@ -194,8 +114,6 @@ runs: echo "org.opencontainers.image.source=${source_url}" echo "org.opencontainers.image.revision=${SHA}" echo "org.opencontainers.image.created=${created}" - echo "org.opencontainers.image.base.name=${BASE_NAME}" - echo "org.opencontainers.image.base.digest=${BASE_DIGEST}" echo "org.opencontainers.image.vendor=SUSE LLC" echo "org.opencontainers.image.authors=suse-observability-ops@suse.com" echo "org.opencontainers.image.url=${DOC_URL}" diff --git a/.github/actions/apply-oci-labels/testdata/Dockerfile b/.github/actions/apply-oci-labels/testdata/Dockerfile deleted file mode 100644 index 8e5342f..0000000 --- a/.github/actions/apply-oci-labels/testdata/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -# syntax=docker/dockerfile:1.7 -# -# Minimal Dockerfile consumed by .github/workflows/apply-oci-labels-ci.yml to -# exercise the action's Dockerfile-parsing path. The image is never built; the -# action only reads the last FROM line and substitutes the ARG default to -# resolve org.opencontainers.image.base.name. - -ARG BCI_VERSION=15.7-56.15 -FROM registry.suse.com/bci/bci-micro:${BCI_VERSION} diff --git a/.github/workflows/apply-oci-labels-ci.yml b/.github/workflows/apply-oci-labels-ci.yml index b639949..441212c 100644 --- a/.github/workflows/apply-oci-labels-ci.yml +++ b/.github/workflows/apply-oci-labels-ci.yml @@ -21,49 +21,27 @@ concurrency: jobs: smoke: - name: smoke (${{ matrix.case.name }} / ${{ matrix.arch }}) - runs-on: ${{ matrix.runner }} + name: smoke (${{ matrix.case.name }}) + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: - arch: [amd64, arm64] case: - # Exercises Dockerfile parsing + ARG substitution + imagetools digest - # resolution against a real released BCI tag. base.digest is checked - # by regex so the test stays green if SUSE re-pushes the tag. - - name: from-dockerfile - base-name: "" - base-digest: "" + - name: default-product-with-upstream product: "" upstream-name: TestUpstream - expect-base-name: "registry.suse.com/bci/bci-micro:15.7-56.15" expect-product: "suse-observability" expect-upstream: "true" - # Both overrides set: action skips Dockerfile parsing AND the network - # call. Asserts overrides win and the absent upstream-name input - # suppresses the upstream.name label entirely. - - name: with-overrides - base-name: "docker.io/library/alpine:3.20" - base-digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000" + - name: explicit-product-without-upstream product: suse-observability-agent upstream-name: "" - expect-base-name: "docker.io/library/alpine:3.20" expect-product: "suse-observability-agent" expect-upstream: "false" - include: - - arch: amd64 - runner: ubuntu-24.04 - - arch: arm64 - runner: ubuntu-24.04-arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - # Action precondition (docker buildx imagetools inspect on the base image). - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - name: Apply OCI labels id: apply uses: ./.github/actions/apply-oci-labels @@ -75,14 +53,10 @@ jobs: component: smoke product: ${{ matrix.case.product }} upstream-name: ${{ matrix.case.upstream-name }} - dockerfile: .github/actions/apply-oci-labels/testdata/Dockerfile - base-name: ${{ matrix.case.base-name }} - base-digest: ${{ matrix.case.base-digest }} - name: Assert canonical labels env: LABELS: ${{ steps.apply.outputs.labels }} - EXPECT_BASE_NAME: ${{ matrix.case.expect-base-name }} EXPECT_PRODUCT: ${{ matrix.case.expect-product }} EXPECT_UPSTREAM: ${{ matrix.case.expect-upstream }} run: | @@ -96,13 +70,15 @@ jobs: grep -Fx "org.opencontainers.image.vendor=SUSE LLC" <<<"${LABELS}" grep -Fx "org.opencontainers.image.authors=suse-observability-ops@suse.com" <<<"${LABELS}" grep -Fx "com.suse.observability.component=smoke" <<<"${LABELS}" - - # Per-case variable lines. - grep -Fx "org.opencontainers.image.base.name=${EXPECT_BASE_NAME}" <<<"${LABELS}" grep -Fx "com.suse.observability.product=${EXPECT_PRODUCT}" <<<"${LABELS}" - # base.digest must look like sha256:<64 hex>. - grep -Eq '^org\.opencontainers\.image\.base\.digest=sha256:[a-f0-9]{64}$' <<<"${LABELS}" + # Base image labels are intentionally not emitted. Keeping this + # assertion protects the action from reintroducing Dockerfile parsing + # and registry lookups in the publish path. + if grep -Eq '^org\.opencontainers\.image\.base\.(name|digest)=' <<<"${LABELS}"; then + echo "::error::base image labels should not be emitted by apply-oci-labels" >&2 + exit 1 + fi # upstream.name is emitted only when the upstream-name input is non-empty. if [ "${EXPECT_UPSTREAM}" = "true" ]; then