Publish Docker images to both Docker Hub (docker.io/wikid82/charon) and GitHub Container Registry (ghcr.io/wikid82/charon) for maximum reach. Add Docker Hub login with secret existence check for graceful fallback Update docker/metadata-action to generate tags for both registries Add Cosign keyless signing for both GHCR and Docker Hub images Attach SBOM to Docker Hub via cosign attach sbom Add Docker Hub signature verification to supply-chain-verify workflow Update README with Docker Hub badges and dual registry examples Update getting-started.md with both registry options Supply chain security maintained: identical tags, signatures, and SBOMs on both registries. PR images remain GHCR-only.
324 lines
11 KiB
YAML
324 lines
11 KiB
YAML
name: Nightly Build & Package
|
|
on:
|
|
schedule:
|
|
# Daily at 09:00 UTC (4am EST / 5am EDT)
|
|
- cron: '0 9 * * *'
|
|
workflow_dispatch:
|
|
inputs:
|
|
reason:
|
|
description: "Why are you running this manually?"
|
|
required: true
|
|
default: "manual trigger"
|
|
skip_tests:
|
|
description: "Skip test-nightly-image job?"
|
|
required: false
|
|
default: "false"
|
|
|
|
env:
|
|
GHCR_REGISTRY: ghcr.io
|
|
DOCKERHUB_REGISTRY: docker.io
|
|
IMAGE_NAME: wikid82/charon
|
|
|
|
jobs:
|
|
sync-development-to-nightly:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
outputs:
|
|
has_changes: ${{ steps.sync.outputs.has_changes }}
|
|
|
|
steps:
|
|
- name: Checkout nightly branch
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: nightly
|
|
fetch-depth: 0
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Configure Git
|
|
run: |
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
|
|
- name: Sync development to nightly
|
|
id: sync
|
|
run: |
|
|
# Fetch development branch
|
|
git fetch origin development
|
|
|
|
# Check if there are differences
|
|
if git diff --quiet nightly origin/development; then
|
|
echo "No changes to sync from development to nightly"
|
|
echo "has_changes=false" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "Syncing changes from development to nightly"
|
|
# Fast-forward merge development into nightly
|
|
git merge origin/development --ff-only -m "chore: sync from development branch [skip ci]" || {
|
|
# If fast-forward fails, force reset to development
|
|
echo "Fast-forward not possible, resetting nightly to development"
|
|
git reset --hard origin/development
|
|
}
|
|
git push origin nightly
|
|
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
build-and-push-nightly:
|
|
needs: sync-development-to-nightly
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
id-token: write
|
|
outputs:
|
|
version: ${{ steps.meta.outputs.version }}
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
digest: ${{ steps.build.outputs.digest }}
|
|
|
|
steps:
|
|
- name: Checkout nightly branch
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: nightly
|
|
fetch-depth: 0
|
|
|
|
- name: Set lowercase image name
|
|
run: echo "IMAGE_NAME_LC=${IMAGE_NAME,,}" >> $GITHUB_ENV
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
|
with:
|
|
registry: ${{ env.GHCR_REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Log in to Docker Hub
|
|
if: secrets.DOCKERHUB_TOKEN != ''
|
|
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
|
with:
|
|
registry: docker.io
|
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Extract metadata
|
|
id: meta
|
|
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
|
with:
|
|
images: |
|
|
${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
tags: |
|
|
type=raw,value=nightly
|
|
type=raw,value=nightly-{{date 'YYYY-MM-DD'}}
|
|
type=sha,prefix=nightly-,format=short
|
|
labels: |
|
|
org.opencontainers.image.title=Charon Nightly
|
|
org.opencontainers.image.description=Nightly build of Charon
|
|
|
|
- name: Build and push Docker image
|
|
id: build
|
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
|
with:
|
|
context: .
|
|
platforms: linux/amd64,linux/arm64
|
|
push: true
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
build-args: |
|
|
VERSION=nightly-${{ github.sha }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
provenance: true
|
|
sbom: true
|
|
|
|
- name: Generate SBOM
|
|
uses: anchore/sbom-action@62ad5284b8ced813296287a0b63906cb364b73ee # v0.22.0
|
|
with:
|
|
image: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:nightly
|
|
format: cyclonedx-json
|
|
output-file: sbom-nightly.json
|
|
|
|
- name: Upload SBOM artifact
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
|
with:
|
|
name: sbom-nightly
|
|
path: sbom-nightly.json
|
|
retention-days: 30
|
|
|
|
# Install Cosign for keyless signing
|
|
- name: Install Cosign
|
|
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
|
|
|
|
# Sign GHCR image with keyless signing (Sigstore/Fulcio)
|
|
- name: Sign GHCR Image
|
|
run: |
|
|
echo "Signing GHCR nightly image with keyless signing..."
|
|
cosign sign --yes ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
|
|
echo "✅ GHCR nightly image signed successfully"
|
|
|
|
# Sign Docker Hub image with keyless signing (Sigstore/Fulcio)
|
|
- name: Sign Docker Hub Image
|
|
if: secrets.DOCKERHUB_TOKEN != ''
|
|
run: |
|
|
echo "Signing Docker Hub nightly image with keyless signing..."
|
|
cosign sign --yes ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
|
|
echo "✅ Docker Hub nightly image signed successfully"
|
|
|
|
# Attach SBOM to Docker Hub image
|
|
- name: Attach SBOM to Docker Hub
|
|
if: secrets.DOCKERHUB_TOKEN != ''
|
|
run: |
|
|
echo "Attaching SBOM to Docker Hub nightly image..."
|
|
cosign attach sbom --sbom sbom-nightly.json ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
|
|
echo "✅ SBOM attached to Docker Hub nightly image"
|
|
|
|
test-nightly-image:
|
|
needs: build-and-push-nightly
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: read
|
|
|
|
steps:
|
|
- name: Checkout nightly branch
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: nightly
|
|
|
|
- name: Set lowercase image name
|
|
run: echo "IMAGE_NAME_LC=${IMAGE_NAME,,}" >> $GITHUB_ENV
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
|
with:
|
|
registry: ${{ env.GHCR_REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Pull nightly image
|
|
run: docker pull ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:nightly
|
|
|
|
- name: Run container smoke test
|
|
run: |
|
|
docker run --name charon-nightly -d \
|
|
-p 8080:8080 \
|
|
${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:nightly
|
|
|
|
# Wait for container to start
|
|
sleep 10
|
|
|
|
# Check container is running
|
|
docker ps | grep charon-nightly
|
|
|
|
# Basic health check
|
|
curl -f http://localhost:8080/health || exit 1
|
|
|
|
# Cleanup
|
|
docker stop charon-nightly
|
|
docker rm charon-nightly
|
|
|
|
build-nightly-release:
|
|
needs: test-nightly-image
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
|
|
steps:
|
|
- name: Checkout nightly branch
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: nightly
|
|
fetch-depth: 0
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
|
with:
|
|
go-version: '1.25.6'
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
with:
|
|
node-version: '24.13.0'
|
|
|
|
- name: Set up Zig (for cross-compilation)
|
|
uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2.2.1
|
|
with:
|
|
version: 0.11.0
|
|
|
|
- name: Build frontend
|
|
working-directory: ./frontend
|
|
run: |
|
|
npm ci
|
|
npm run build
|
|
|
|
- name: Run GoReleaser (snapshot mode)
|
|
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
|
with:
|
|
distribution: goreleaser
|
|
version: '~> v2'
|
|
args: release --snapshot --skip=publish --clean
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Upload nightly binaries
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
|
with:
|
|
name: nightly-binaries
|
|
path: dist/*
|
|
retention-days: 30
|
|
|
|
verify-nightly-supply-chain:
|
|
needs: build-and-push-nightly
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: read
|
|
security-events: write
|
|
|
|
steps:
|
|
- name: Checkout nightly branch
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: nightly
|
|
|
|
- name: Set lowercase image name
|
|
run: echo "IMAGE_NAME_LC=${IMAGE_NAME,,}" >> $GITHUB_ENV
|
|
|
|
- name: Download SBOM
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
|
with:
|
|
name: sbom-nightly
|
|
|
|
- name: Scan with Grype
|
|
uses: anchore/scan-action@0d444ed77d83ee2ba7f5ced0d90d640a1281d762 # v7.3.0
|
|
with:
|
|
sbom: sbom-nightly.json
|
|
fail-build: false
|
|
severity-cutoff: high
|
|
|
|
- name: Scan with Trivy
|
|
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
|
|
with:
|
|
image-ref: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:nightly
|
|
format: 'sarif'
|
|
output: 'trivy-nightly.sarif'
|
|
|
|
- name: Upload Trivy results
|
|
uses: github/codeql-action/upload-sarif@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4.31.11
|
|
with:
|
|
sarif_file: 'trivy-nightly.sarif'
|
|
category: 'trivy-nightly'
|
|
|
|
- name: Check for critical CVEs
|
|
run: |
|
|
if grep -q "CRITICAL" trivy-nightly.sarif; then
|
|
echo "❌ Critical vulnerabilities found in nightly build"
|
|
exit 1
|
|
fi
|
|
echo "✅ No critical vulnerabilities found"
|