Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/deploy-artifact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ jobs:
ARTIFACT_NAME: ${{ inputs.artifact-name }}
ARTIFACT_VERSION: ${{ inputs.artifact-version }}
RENDER_HASH: ${{ steps.render.outputs.render-hash }}
GITHUB_TOKEN: ${{ github.token }}
GITHUB_ACTOR: ${{ github.actor }}
run: bash .github-workflows/actions/deploy-artifact/publish.sh

- name: Attest build provenance
Expand Down
40 changes: 26 additions & 14 deletions actions/deploy-artifact/publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ main() {
local artifact_ref
artifact_ref="ghcr.io/jorisjonkers-dev/${artifact_name}:${artifact_version}"

# (0) Authenticate to GHCR so oras push and cosign sign can write packages.
# GITHUB_TOKEN is always injected by GitHub Actions (packages: write on the
# deploy-artifact.yml caller grants write access to the service's packages).
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
printf '%s' "$GITHUB_TOKEN" | oras login ghcr.io \
--username "${GITHUB_ACTOR:-github-actions}" \
--password-stdin
printf '%s' "$GITHUB_TOKEN" | docker login ghcr.io \
--username "${GITHUB_ACTOR:-github-actions}" \
--password-stdin
fi

# (1) Build deterministic tar (SC-9: sort, mtime=0, uid=0, gid=0, numeric)
tar \
--sort=name \
Expand All @@ -60,14 +72,18 @@ main() {

local artifact_digest

# (2) Check if tag already exists (idempotent publish)
if oras manifest fetch "$artifact_ref" > /dev/null 2>&1; then
# Tag exists: verify render-hash matches; fail on mismatch (content collision)
# (2) Check if a deploy artifact tag already exists (idempotent publish).
# Use --media-type to distinguish deploy artifacts from container images that
# share the same tag (container images have a different manifest media type
# and will not match, preventing false-positive idempotency hits).
if oras manifest fetch "$artifact_ref" \
--media-type "application/vnd.jorisjonkers.deployment.artifact.v1+tar" \
> /dev/null 2>&1; then
# Deploy artifact tag exists: verify render-hash; fail on mismatch.
local existing_digest
existing_digest=$(oras resolve "$artifact_ref" 2>/dev/null)

local existing_hash=""
# Attempt to extract artifact-contract from the stored artifact blob
local existing_contract
if existing_contract=$(oras blob fetch \
"ghcr.io/jorisjonkers-dev/${artifact_name}@${existing_digest}" \
Expand All @@ -82,7 +98,6 @@ main() {
fail "E_PUBLISH_RACE_TAG_MOVED: tag=${artifact_ref} existing-hash=${existing_hash} new-hash=${render_hash} (different content published under same version tag)"
fi

# Hashes match (or could not extract existing hash): idempotent re-use of existing artifact
artifact_digest="$existing_digest"
warn "artifact already exists with matching render-hash; skipping push (idempotent)"
else
Expand All @@ -107,19 +122,16 @@ main() {
artifact_digest="$pushed_digest"
fi

# (5) SBOM via syft → attach
syft "ghcr.io/jorisjonkers-dev/${artifact_name}@${artifact_digest}" \
-o spdx-json=sbom.spdx.json

oras attach "${artifact_ref}@${artifact_digest}" \
--artifact-type "application/vnd.syft.sbom.spdx+json" \
sbom.spdx.json
# NOTE: SBOM via syft is intentionally omitted here. This artifact is a
# YAML/tar package (deployment configuration), not a container image; there
# are no software dependencies to enumerate. Container-image SBOM is handled
# separately by the container-publish.yml workflow.

# (6) Keyless cosign sign (SC-10)
# (5) Keyless cosign sign (SC-10)
cosign sign --yes \
"ghcr.io/jorisjonkers-dev/${artifact_name}@${artifact_digest}"

# (7) SLSA build provenance attestation
# (6) SLSA build provenance attestation
# Uses the GitHub Actions attest action injected via the workflow permissions
printf '%s\n' "artifact-ref=${artifact_ref}@${artifact_digest}" >> "${GITHUB_OUTPUT:-/dev/null}"
printf '%s\n' "artifact-digest=${artifact_digest}" >> "${GITHUB_OUTPUT:-/dev/null}"
Expand Down
Loading