From 0ad9942e3143c831b590f7f7b3dab9d1f927c405 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:46:27 +0000 Subject: [PATCH 1/8] Initial plan From 8b1bb8ad128c9afd1178b58c4ba39a140e4099a9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:48:46 +0000 Subject: [PATCH 2/8] Fix multiline tag handling in container signing workflow Co-authored-by: hyphaltip <48827+hyphaltip@users.noreply.github.com> --- .github/workflows/container.yml | 190 ++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 .github/workflows/container.yml diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml new file mode 100644 index 0000000..7ed681b --- /dev/null +++ b/.github/workflows/container.yml @@ -0,0 +1,190 @@ +# 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 + run: | + ref="${{ github.ref_name }}" + version="${ref#v}" + prerelease="false" + case "$version" in *-*) prerelease="true" ;; esac + { + echo "tags<<_EOF_" + echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${ref}" + if [ "${{ github.ref_type }}" = "tag" ]; then + echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${version}" + if [ "$prerelease" = "false" ]; then + echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" + fi + fi + echo "_EOF_" + echo "version=${version}" >> "$GITHUB_OUTPUT" + } >> "$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 + run: | + while IFS= read -r tag; do + [ -n "${tag}" ] || continue + cosign sign --yes --recursive "${tag}" + done <<< "${{ needs.build.outputs.tags }}" + + 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 + id: slsa-predicate + run: | + started_on="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + predicate_file="${RUNNER_TEMP}/slsa-provenance.json" + cat > "${predicate_file}" <> "${GITHUB_OUTPUT}" + + - name: Attach SLSA provenance + run: | + while IFS= read -r tag; do + [ -n "${tag}" ] || continue + cosign attest --yes \ + --predicate "${{ steps.slsa-predicate.outputs.path }}" \ + --type=slsaprovenance1 \ + "${tag}" + done <<< "${{ needs.build.outputs.tags }}" + + verify: + needs: [sign, provenance] + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + + steps: + - 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: Verify signatures + run: | + while IFS= read -r tag; do + [ -n "${tag}" ] || continue + cosign verify \ + --certificate-identity-regexp="https://github.com/${{ github.repository_owner }}" \ + --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ + "${tag}" + done <<< "${{ needs.build.outputs.tags }}" From 5b747bffadcfbe5563b2a29dbc68672cb40e0a46 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:49:30 +0000 Subject: [PATCH 3/8] Harden container workflow tag output and signature identity checks Co-authored-by: hyphaltip <48827+hyphaltip@users.noreply.github.com> --- .github/workflows/container.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 7ed681b..b848ed7 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -51,7 +51,7 @@ jobs: fi fi echo "_EOF_" - echo "version=${version}" >> "$GITHUB_OUTPUT" + echo "version=${version}" } >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx @@ -184,7 +184,7 @@ jobs: while IFS= read -r tag; do [ -n "${tag}" ] || continue cosign verify \ - --certificate-identity-regexp="https://github.com/${{ github.repository_owner }}" \ + --certificate-identity-regexp="^https://github.com/${{ github.repository }}/.github/workflows/container.yml@.*$" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ "${tag}" done <<< "${{ needs.build.outputs.tags }}" From 6cc55898751ef8bb4f241058050517178442db6d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:50:10 +0000 Subject: [PATCH 4/8] Use safe multiline tag handling in container workflow jobs Co-authored-by: hyphaltip <48827+hyphaltip@users.noreply.github.com> --- .github/workflows/container.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index b848ed7..640e389 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -96,11 +96,13 @@ jobs: uses: sigstore/cosign-installer@v3 - name: Sign images + env: + TAGS: ${{ needs.build.outputs.tags }} run: | - while IFS= read -r tag; do + printf '%s\n' "$TAGS" | while IFS= read -r tag; do [ -n "${tag}" ] || continue cosign sign --yes --recursive "${tag}" - done <<< "${{ needs.build.outputs.tags }}" + done provenance: needs: [build, sign] @@ -152,14 +154,16 @@ jobs: echo "path=${predicate_file}" >> "${GITHUB_OUTPUT}" - name: Attach SLSA provenance + env: + TAGS: ${{ needs.build.outputs.tags }} run: | - while IFS= read -r tag; do + printf '%s\n' "$TAGS" | while IFS= read -r tag; do [ -n "${tag}" ] || continue cosign attest --yes \ --predicate "${{ steps.slsa-predicate.outputs.path }}" \ --type=slsaprovenance1 \ "${tag}" - done <<< "${{ needs.build.outputs.tags }}" + done verify: needs: [sign, provenance] @@ -180,11 +184,13 @@ jobs: uses: sigstore/cosign-installer@v3 - name: Verify signatures + env: + TAGS: ${{ needs.build.outputs.tags }} run: | - while IFS= read -r tag; do + printf '%s\n' "$TAGS" | while IFS= read -r tag; do [ -n "${tag}" ] || continue cosign verify \ - --certificate-identity-regexp="^https://github.com/${{ github.repository }}/.github/workflows/container.yml@.*$" \ + --certificate-identity="https://github.com/${{ github.repository }}/.github/workflows/container.yml@${{ github.ref }}" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ "${tag}" - done <<< "${{ needs.build.outputs.tags }}" + done From 58819be41cfc703f065bd30fb32cf5cd3d79f60b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:50:39 +0000 Subject: [PATCH 5/8] Pass predicate path via env in provenance step Co-authored-by: hyphaltip <48827+hyphaltip@users.noreply.github.com> --- .github/workflows/container.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 640e389..b5e0e49 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -156,11 +156,12 @@ jobs: - name: Attach SLSA provenance env: TAGS: ${{ needs.build.outputs.tags }} + PREDICATE_PATH: ${{ steps.slsa-predicate.outputs.path }} run: | printf '%s\n' "$TAGS" | while IFS= read -r tag; do [ -n "${tag}" ] || continue cosign attest --yes \ - --predicate "${{ steps.slsa-predicate.outputs.path }}" \ + --predicate "${PREDICATE_PATH}" \ --type=slsaprovenance1 \ "${tag}" done From 26646bcefe75801bd386b783251d541a283411e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:51:17 +0000 Subject: [PATCH 6/8] Use env vars for workflow shell interpolation safety Co-authored-by: hyphaltip <48827+hyphaltip@users.noreply.github.com> --- .github/workflows/container.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index b5e0e49..04dc3e7 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -36,18 +36,23 @@ jobs: - 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="${{ github.ref_name }}" + ref="${REF_NAME}" version="${ref#v}" prerelease="false" case "$version" in *-*) prerelease="true" ;; esac { echo "tags<<_EOF_" - echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${ref}" - if [ "${{ github.ref_type }}" = "tag" ]; then - echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${version}" + echo "${REGISTRY}/${IMAGE_NAME}:${ref}" + if [ "${REF_TYPE}" = "tag" ]; then + echo "${REGISTRY}/${IMAGE_NAME}:${version}" if [ "$prerelease" = "false" ]; then - echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" + echo "${REGISTRY}/${IMAGE_NAME}:latest" fi fi echo "_EOF_" @@ -187,11 +192,13 @@ jobs: - name: Verify signatures env: TAGS: ${{ needs.build.outputs.tags }} + REPO: ${{ github.repository }} + GITHUB_REF: ${{ github.ref }} run: | printf '%s\n' "$TAGS" | while IFS= read -r tag; do [ -n "${tag}" ] || continue cosign verify \ - --certificate-identity="https://github.com/${{ github.repository }}/.github/workflows/container.yml@${{ github.ref }}" \ + --certificate-identity="https://github.com/${REPO}/.github/workflows/container.yml@${GITHUB_REF}" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ "${tag}" done From 63d04087a6db709c1067a47e08ee335480074416 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:51:54 +0000 Subject: [PATCH 7/8] Simplify provenance and verify env usage in workflow Co-authored-by: hyphaltip <48827+hyphaltip@users.noreply.github.com> --- .github/workflows/container.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 04dc3e7..46c7b2a 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -132,7 +132,6 @@ jobs: uses: sigstore/cosign-installer@v3 - name: Generate SLSA provenance predicate - id: slsa-predicate run: | started_on="$(date -u +%Y-%m-%dT%H:%M:%SZ)" predicate_file="${RUNNER_TEMP}/slsa-provenance.json" @@ -156,17 +155,15 @@ jobs: } } JSON - echo "path=${predicate_file}" >> "${GITHUB_OUTPUT}" - name: Attach SLSA provenance env: TAGS: ${{ needs.build.outputs.tags }} - PREDICATE_PATH: ${{ steps.slsa-predicate.outputs.path }} run: | printf '%s\n' "$TAGS" | while IFS= read -r tag; do [ -n "${tag}" ] || continue cosign attest --yes \ - --predicate "${PREDICATE_PATH}" \ + --predicate "${RUNNER_TEMP}/slsa-provenance.json" \ --type=slsaprovenance1 \ "${tag}" done @@ -193,7 +190,6 @@ jobs: env: TAGS: ${{ needs.build.outputs.tags }} REPO: ${{ github.repository }} - GITHUB_REF: ${{ github.ref }} run: | printf '%s\n' "$TAGS" | while IFS= read -r tag; do [ -n "${tag}" ] || continue From d52fbdcf19be74da2b1d7d1e54bf742aeac61746 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:52:24 +0000 Subject: [PATCH 8/8] Version SLSA buildType with workflow ref Co-authored-by: hyphaltip <48827+hyphaltip@users.noreply.github.com> --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 46c7b2a..00cc5b2 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -138,7 +138,7 @@ jobs: cat > "${predicate_file}" <