Files
Charon/.github/workflows/nightly-build.yml
GitHub Actions ba900e20c5 chore(ci): add Docker Hub as secondary container registry
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.
2026-01-25 16:04:42 +00:00

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"