mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-19 23:06:49 +00:00
3b12f2678f
Pin the push_to_registry job to a GitHub `production` environment so the DOCKERHUB_USERNAME and DOCKERHUB_TOKEN credentials can live there instead of as repo-wide secrets. Any future workflow that tries to push to Docker Hub without declaring this environment will fail to resolve the credentials, which is exactly the blast-radius reduction we want. As a side benefit, adding a required reviewer to the `production` environment in repo settings now turns every release into a manual approval gate with zero workflow changes. The secrets were already moved into the environment ahead of this commit, so the next release run will pick them up seamlessly.
106 lines
4.1 KiB
YAML
106 lines
4.1 KiB
YAML
name: Build and Publish Docker Image
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: docker-publish
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
push_to_registry:
|
|
name: Push Docker image to Docker Hub
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
# DOCKERHUB_USERNAME and DOCKERHUB_TOKEN live in the `production` environment,
|
|
# not repo-wide secrets. Any future workflow that tries to push to Docker Hub
|
|
# without declaring this environment will fail to resolve the credentials,
|
|
# which is exactly the blast-radius reduction we want. Adding a required
|
|
# reviewer to the environment in repo settings also turns every release into
|
|
# a manual-approval gate without any workflow change.
|
|
environment: production
|
|
permissions:
|
|
contents: read
|
|
# Required for cosign keyless signing via GitHub OIDC.
|
|
id-token: write
|
|
steps:
|
|
- name: Check out the repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v4
|
|
with:
|
|
platforms: arm64
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v4
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v4
|
|
with:
|
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Install cosign
|
|
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
|
|
|
|
- name: Extract metadata (tags, labels) for Docker
|
|
id: meta
|
|
uses: docker/metadata-action@v6
|
|
with:
|
|
images: saelix/sencho
|
|
# On a v-tag push we publish:
|
|
# latest always points at the newest release
|
|
# X.Y.Z the immutable semver tag
|
|
# X.Y moving minor tag for users who want latest patch
|
|
# The pre-1.0 constraint hides the {{major}}-only tag until we ship 1.0,
|
|
# since on 0.x every minor is potentially breaking.
|
|
tags: |
|
|
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
|
|
- name: Build and push Docker image
|
|
id: build
|
|
uses: docker/build-push-action@v7
|
|
with:
|
|
context: .
|
|
push: true
|
|
platforms: linux/amd64,linux/arm64
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
cache-from: type=registry,ref=saelix/sencho:buildcache
|
|
cache-to: type=registry,ref=saelix/sencho:buildcache,mode=max
|
|
# SBOM + provenance attestations are embedded as OCI referrers on the
|
|
# published image. Inspect with: docker buildx imagetools inspect <img>
|
|
sbom: true
|
|
provenance: mode=max
|
|
|
|
- name: Sign published image with cosign (keyless)
|
|
# Signs every tag produced by metadata-action using the ambient GitHub
|
|
# OIDC token. No private keys, no secrets. Users verify with:
|
|
# cosign verify saelix/sencho:<tag> \
|
|
# --certificate-identity-regexp "https://github.com/AnsoCode/Sencho/.*" \
|
|
# --certificate-oidc-issuer https://token.actions.githubusercontent.com
|
|
env:
|
|
TAGS: ${{ steps.meta.outputs.tags }}
|
|
DIGEST: ${{ steps.build.outputs.digest }}
|
|
run: |
|
|
# Build a single cosign invocation with every tag pinned to the digest.
|
|
# All tags resolve to the same manifest, so one call signs them all and
|
|
# saves N-1 Rekor/Fulcio round trips. On workflow_dispatch $TAGS is empty
|
|
# and the refs array stays empty, so we no-op cleanly.
|
|
refs=()
|
|
while IFS= read -r tag; do
|
|
[ -n "$tag" ] || continue
|
|
refs+=("${tag}@${DIGEST}")
|
|
done <<< "$TAGS"
|
|
if [ ${#refs[@]} -gt 0 ]; then
|
|
cosign sign --yes "${refs[@]}"
|
|
else
|
|
echo "No tags to sign (likely a workflow_dispatch run on a non-tag ref)."
|
|
fi
|