diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml new file mode 100644 index 0000000..00cc5b2 --- /dev/null +++ b/.github/workflows/container.yml @@ -0,0 +1,200 @@ +# Builds and publishes the funannotate container image to GitHub Container +# Registry (GHCR), signs it with Cosign (Sigstore), attaches SLSA provenance +# attestations, and verifies the signature. +# +# - Every push of a v* tag: builds the image (./Dockerfile) and publishes it +# as ghcr.io//funannotate:vX.Y.Z and :X.Y.Z, plus :latest for stable +# (non-prerelease) releases. workflow_dispatch allows a manual rebuild. + +name: Build and Sign Container Image + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + outputs: + tags: ${{ steps.meta.outputs.tags }} + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Compute image tags + id: meta + env: + REF_NAME: ${{ github.ref_name }} + REF_TYPE: ${{ github.ref_type }} + REGISTRY: ${{ env.REGISTRY }} + IMAGE_NAME: ${{ env.IMAGE_NAME }} + run: | + ref="${REF_NAME}" + version="${ref#v}" + prerelease="false" + case "$version" in *-*) prerelease="true" ;; esac + { + echo "tags<<_EOF_" + echo "${REGISTRY}/${IMAGE_NAME}:${ref}" + if [ "${REF_TYPE}" = "tag" ]; then + echo "${REGISTRY}/${IMAGE_NAME}:${version}" + if [ "$prerelease" = "false" ]; then + echo "${REGISTRY}/${IMAGE_NAME}:latest" + fi + fi + echo "_EOF_" + echo "version=${version}" + } >> "$GITHUB_OUTPUT" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + cache-from: type=gha + cache-to: type=gha,mode=max + + sign: + needs: build + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + packages: write + + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Cosign + uses: sigstore/cosign-installer@v3 + + - name: Sign images + env: + TAGS: ${{ needs.build.outputs.tags }} + run: | + printf '%s\n' "$TAGS" | while IFS= read -r tag; do + [ -n "${tag}" ] || continue + cosign sign --yes --recursive "${tag}" + done + + provenance: + needs: [build, sign] + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + packages: write + + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Cosign + uses: sigstore/cosign-installer@v3 + + - name: Generate SLSA provenance predicate + run: | + started_on="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + predicate_file="${RUNNER_TEMP}/slsa-provenance.json" + cat > "${predicate_file}" <