Skip to content

ci: bump actions/github-script from 7 to 9 (#108) #88

ci: bump actions/github-script from 7 to 9 (#108)

ci: bump actions/github-script from 7 to 9 (#108) #88

Workflow file for this run

name: Build & Deploy
on:
push:
branches: [main]
paths-ignore: ['**.md', 'docs/**']
workflow_dispatch:
inputs:
deploy_ack:
description: "Type production to approve this production deploy."
required: true
type: string
reason:
description: "Production deploy reason (min 12 chars)."
required: true
type: string
concurrency:
group: coforma-production-kustomization
cancel-in-progress: false
jobs:
build-and-deploy:
name: Build, Push & Update Manifests
runs-on: ${{ vars.ARC_BOOTSTRAP_COMPLETE == 'true' && 'madfam-runners-blue' || 'ubuntu-latest' }}
permissions:
contents: write
packages: write
id-token: write
steps:
- uses: actions/checkout@v7
- name: Require manual production deploy acknowledgement
if: github.event_name == 'workflow_dispatch'
env:
DEPLOY_ACK: ${{ inputs.deploy_ack }}
DEPLOY_REASON: ${{ inputs.reason }}
run: |
if [ "${DEPLOY_ACK}" != "production" ]; then
echo "deploy_ack must be production" >&2
exit 1
fi
if [ ${#DEPLOY_REASON} -lt 12 ]; then
echo "reason must be at least 12 characters" >&2
exit 1
fi
- name: Log in to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.MADFAM_BOT_PAT }}
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to Docker Hub
uses: docker/login-action@v4
continue-on-error: true
with:
registry: docker.io
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and push web
uses: docker/build-push-action@v7
id: build-web
with:
context: .
file: Dockerfile
push: true
tags: |
ghcr.io/madfam-org/coforma-studio/web:${{ github.sha }}
cache-from: type=gha,scope=web
cache-to: type=gha,mode=max,scope=web
# provenance/sbom off to avoid GHCR 403s and attestation-manifest
# digest ambiguity (https://github.com/docker/build-push-action/issues/981)
provenance: false
sbom: false
- name: Preflight sigstore connectivity
# Canonical pattern from tulana/.github/workflows/deploy-api.yml:
# probe every sigstore endpoint first so a runner-egress problem
# names itself instead of surfacing as cosign's opaque JSON parse
# error ("invalid character 'u' looking for beginning of value").
run: |
set -euo pipefail
FAILED=0
for HOST in oauth2.sigstore.dev fulcio.sigstore.dev rekor.sigstore.dev tuf-repo-cdn.sigstore.dev; do
if curl -sS --max-time 10 -o /dev/null "https://${HOST}"; then
echo "ok: ${HOST}"
else
echo "UNREACHABLE: ${HOST}" >&2
FAILED=1
fi
done
if [ "${FAILED}" -ne 0 ]; then
echo "::error::sigstore endpoints unreachable from this runner — fix runner egress before retrying."
exit 1
fi
- name: Mint sigstore OIDC identity token
# Mint the token explicitly instead of relying on cosign's ambient
# fetch: a failure surfaces the Actions service's real error message
# and the audience is pinned to `sigstore` (tulana deploy-api.yml
# canonical pattern).
id: sigstore-token
uses: actions/github-script@v9
with:
script: |
const token = await core.getIDToken('sigstore')
core.setSecret(token)
core.setOutput('token', token)
- name: Sign image with cosign
env:
SIGSTORE_ID_TOKEN: ${{ steps.sigstore-token.outputs.token }}
run: |
cosign sign --yes --identity-token "${SIGSTORE_ID_TOKEN}" \
ghcr.io/madfam-org/coforma-studio/web@${{ steps.build-web.outputs.digest }}
- name: Resolve manifest digest
id: digest
run: |
set -euo pipefail
# `docker buildx imagetools inspect` returns the manifest-list
# digest for multi-arch images; build-push-action's output digest
# is only safe for single-arch builds (see enclii#136 / hcm-api).
DIGEST=$(docker buildx imagetools inspect \
"ghcr.io/madfam-org/coforma-studio/web:${{ github.sha }}" \
| awk '/^Digest:/{print $2; exit}')
if [ -z "$DIGEST" ]; then
echo "Failed to resolve digest" >&2
exit 1
fi
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved digest: ${DIGEST}"
- name: Install kustomize
uses: imranismail/setup-kustomize@v3
- name: Commit image digest to GitOps manifest
# Atomic commit-with-retry (canonical tulana pattern): fetch latest
# main, set digest, push. Handles races against concurrent sessions
# committing to the same kustomization file.
run: |
set -euo pipefail
DIGEST="${{ steps.digest.outputs.digest }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
for ATTEMPT in 1 2 3; do
git fetch origin main
git reset --hard origin/main
cd infra/k8s/production
kustomize edit set image \
ghcr.io/madfam-org/coforma-studio/web=ghcr.io/madfam-org/coforma-studio/web@${DIGEST}
cd ../../..
git add infra/k8s/production/kustomization.yaml
if git diff --staged --quiet; then
echo "No digest change — manifest already pinned to ${DIGEST}"
exit 0
fi
git commit -m "deploy(web): pin digest ${DIGEST:7:19} [skip ci]" -m "build-source-sha: ${GITHUB_SHA}"
if git push origin main; then
echo "Pushed digest update on attempt $ATTEMPT"
exit 0
fi
echo "Push failed on attempt $ATTEMPT, retrying after backoff..."
sleep $((ATTEMPT * 10))
done
echo "Failed to commit digest after 3 attempts" >&2
exit 1