Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"changelog-path": ".github/CHANGELOG.md",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": true,
"draft": true,
"changelog-sections": [
{ "type": "feat", "section": "Features" },
{ "type": "fix", "section": "Bug Fixes" },
Expand Down
71 changes: 71 additions & 0 deletions .github/workflows/finalize-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Finalize Release

# Publish draft releases once both CLI and Docker workflows succeed.
# Release Please creates draft releases (immutable once published),
# then CLI (GoReleaser) and Docker (GHCR) attach assets to the draft.
# This workflow fires on each completion and publishes when both are done.

on:
workflow_run:
workflows: [Docker, CLI]
types: [completed]

permissions: {}

jobs:
publish:
name: Publish Draft Release
# Only process tag-triggered runs (release builds)
if: startsWith(github.event.workflow_run.head_branch, 'v')
runs-on: ubuntu-latest
permissions:
contents: write
Comment thread
coderabbitai[bot] marked this conversation as resolved.
steps:
- name: Check both workflows and publish draft
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.event.workflow_run.head_branch }}
run: |
set -euo pipefail

echo "Triggered by: ${{ github.event.workflow.name }}"
echo "Tag: $TAG"
echo "Triggering run conclusion: ${{ github.event.workflow_run.conclusion }}"

# If the triggering workflow failed, no point checking the other
if [ "${{ github.event.workflow_run.conclusion }}" != "success" ]; then
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
echo "Triggering workflow failed — skipping."
exit 0
fi
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

# Check status of both workflows for this tag.
# gh run list --branch works for both branches and tags.
CLI_CONCLUSION=$(gh run list --repo "$GITHUB_REPOSITORY" \
--workflow cli.yml --branch "$TAG" --limit 1 \
--json conclusion --jq '.[0].conclusion // "pending"')

DOCKER_CONCLUSION=$(gh run list --repo "$GITHUB_REPOSITORY" \
--workflow docker.yml --branch "$TAG" --limit 1 \
--json conclusion --jq '.[0].conclusion // "pending"')
Comment thread
greptile-apps[bot] marked this conversation as resolved.

echo "CLI: $CLI_CONCLUSION"
echo "Docker: $DOCKER_CONCLUSION"

if [ "$CLI_CONCLUSION" != "success" ] || [ "$DOCKER_CONCLUSION" != "success" ]; then
echo "Not all workflows succeeded yet (CLI=$CLI_CONCLUSION, Docker=$DOCKER_CONCLUSION)."
echo "The other workflow's completion will trigger another attempt."
exit 0
fi

# Both succeeded — check if release is still a draft
IS_DRAFT=$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" \
--json isDraft --jq '.isDraft')

if [ "$IS_DRAFT" != "true" ]; then
echo "Release $TAG is not a draft (isDraft=$IS_DRAFT). Already published or not found."
exit 0
fi

# Publish the draft release (makes it immutable)
gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --draft=false
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated
echo "Release $TAG published successfully."
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ site/ # Astro landing page (synthorg.io)
- **DAST**: `.github/workflows/dast.yml` — ZAP API scan against the backend OpenAPI spec on push to main + weekly schedule. Builds backend image locally, starts container, runs ZAP. Results available as workflow artifacts (no SARIF — action v0.10.0 lacks native SARIF output). Not on PRs (too slow).
- **Socket.dev**: GitHub App — supply chain attack detection on PRs (typosquatting, malware, suspicious ownership changes, obfuscated code). No config file needed, auto-comments on PRs.
- **CLA**: `.github/workflows/cla.yml` — Contributor License Agreement signature check on PRs via `contributor-assistant/github-action`. Triggers on `pull_request_target` and `issue_comment`. Skips Dependabot. Signatures stored in `.github/cla-signatures.json` on the `cla-signatures` branch (unprotected, so the action can commit directly).
- **Release**: `.github/workflows/release.yml` — Release Please (Google) auto-creates a release PR on every push to main. Merging the release PR creates a git tag (`vX.Y.Z`) + GitHub Release with changelog. Tag push triggers the Docker workflow to build version-tagged images. Uses `RELEASE_PLEASE_TOKEN` secret (PAT/GitHub App token) so tag creation triggers downstream workflows (GITHUB_TOKEN cannot). Config in `.github/release-please-config.json` and `.github/.release-please-manifest.json`. After creating/updating a release PR, auto-updates the BSL Change Date in LICENSE to 3 years ahead.
- **Release**: `.github/workflows/release.yml` — Release Please (Google) auto-creates a release PR on every push to main. Merging the release PR creates a git tag (`vX.Y.Z`) + **draft** GitHub Release with changelog. Tag push triggers Docker and CLI workflows to attach assets to the draft. Uses `RELEASE_PLEASE_TOKEN` secret (PAT/GitHub App token) so tag creation triggers downstream workflows (GITHUB_TOKEN cannot). Config in `.github/release-please-config.json` (`"draft": true`) and `.github/.release-please-manifest.json`. After creating/updating a release PR, auto-updates the BSL Change Date in LICENSE to 3 years ahead.
- **Finalize Release**: `.github/workflows/finalize-release.yml` — publishes draft releases after both Docker and CLI workflows succeed. Triggers on `workflow_run` completion of Docker and CLI. Checks both workflow conclusions for the tag, publishes the draft only when both are successful. Immutable releases are enabled on the repo — once published, release assets and body cannot be modified.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The description of the 'Finalize Release' workflow logic could be clearer. To help developers quickly understand this critical new part of the release process, consider rephrasing it for better flow and clarity. The current wording is a bit repetitive and ambiguous.

Suggested change
- **Finalize Release**: `.github/workflows/finalize-release.yml`publishes draft releases after both Docker and CLI workflows succeed. Triggers on `workflow_run` completion of Docker and CLI. Checks both workflow conclusions for the tag, publishes the draft only when both are successful. Immutable releases are enabled on the repo — once published, release assets and body cannot be modified.
- **Finalize Release**: `.github/workflows/finalize-release.yml`Publishes draft releases created by Release Please. It triggers on `workflow_run` completion of the Docker and CLI workflows. It then verifies that both workflows have succeeded for the associated Git tag before publishing the draft. This two-step process is necessary to comply with the repository's immutable release policy, which prevents modifying already-published releases.


## Dependencies

Expand Down
1 change: 1 addition & 0 deletions docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ Images are **only pushed to GHCR after both scanners pass**.
- **CLI binaries**: SLSA Level 3 provenance attestations (verify via `gh attestation verify`)
- **Git commits**: GPG/SSH signed (enforced by branch protection ruleset)
- **GitHub Actions**: All actions pinned by full SHA commit hash
- **GitHub Releases**: Immutable releases enabled — once published, assets and body cannot be modified (prevents supply chain tampering). Releases are created as drafts by Release Please, finalized after all assets are attached.

---

Expand Down
Loading