From 9821b1cf4e337b02be2fc44fa2def195d14df5b6 Mon Sep 17 00:00:00 2001 From: Julien Goux Date: Tue, 30 Jun 2026 15:35:04 +0200 Subject: [PATCH] chore(cli): define hotfix release workflow --- .github/workflows/deploy-check.yml | 4 +- .github/workflows/sync-main-to-develop.yml | 56 ++++++++++++++++++++++ apps/cli/docs/release-process.md | 30 +++++++++++- 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/sync-main-to-develop.yml diff --git a/.github/workflows/deploy-check.yml b/.github/workflows/deploy-check.yml index 8db23a6e55..534aebd7d9 100644 --- a/.github/workflows/deploy-check.yml +++ b/.github/workflows/deploy-check.yml @@ -15,9 +15,9 @@ permissions: jobs: check: - if: github.head_ref != 'develop' + if: github.head_ref != 'develop' && !startsWith(github.head_ref, 'hotfix/') runs-on: ubuntu-latest steps: - run: | - echo "Pull requests to main branch are only allowed from develop branch." + echo "Pull requests to main branch are only allowed from develop or hotfix/* branches." exit 1 diff --git a/.github/workflows/sync-main-to-develop.yml b/.github/workflows/sync-main-to-develop.yml new file mode 100644 index 0000000000..c93f2135ed --- /dev/null +++ b/.github/workflows/sync-main-to-develop.yml @@ -0,0 +1,56 @@ +name: Sync main to develop + +# Keeps stable hotfix commits reachable from the integration branch after the +# production source of truth publishes successfully. +on: + workflow_run: + workflows: + - Release + types: + - completed + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: sync-main-to-develop + cancel-in-progress: false + +jobs: + sync: + name: Merge main into develop + if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + steps: + - id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.GH_APP_CLIENT_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + permission-contents: write + + - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 + with: + ref: develop + fetch-depth: 0 + persist-credentials: true + token: ${{ steps.app-token.outputs.token }} + + - name: Merge main into develop + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git fetch origin main develop + if git merge-base --is-ancestor origin/main HEAD; then + echo "develop already contains main." + else + git merge origin/main --no-edit + git push origin develop + fi diff --git a/apps/cli/docs/release-process.md b/apps/cli/docs/release-process.md index c8196ef732..05ab1cb582 100644 --- a/apps/cli/docs/release-process.md +++ b/apps/cli/docs/release-process.md @@ -247,7 +247,7 @@ flowchart TD ### Trigger -Most releases are automatic — merge a PR into `develop` (beta) or approve the weekly Prod-Deploy PR into `main` (stable). For an `alpha` cut or a one-off override, dispatch manually: +Most releases are automatic — merge a PR into `develop` (beta) or approve the weekly Prod-Deploy PR into `main` (stable). Hotfixes use the same production gate: a reviewed `hotfix/*` PR targets `main`, and the resulting `main` push triggers the stable release path. For an `alpha` cut or a one-off override, dispatch manually: ```sh # Manual alpha cut (v3 / next shell): @@ -265,6 +265,34 @@ gh workflow run release.yml \ Auto-trigger paths leave `version` empty: semantic-release computes it from commits since the last tag. +### Hotfix release flow + +Use a hotfix when an urgent stable fix must ship before the next scheduled `develop` -> `main` promotion. The hotfix path deliberately reuses the production PR gate instead of adding a second approval mechanism: + +1. Branch from the current `main` tip: + + ```sh + git fetch origin main + git switch -c hotfix/ origin/main + ``` + +2. Make the smallest safe fix and open a PR from `hotfix/` into `main`. +3. Before merging, run a release dry run against the hotfix branch and the next unique stable version: + + ```sh + gh workflow run release.yml \ + --ref hotfix/ \ + --field channel=stable \ + --field version= \ + --field dry_run=true + ``` + +4. After review and green checks, merge the hotfix PR into `main`. The `push: main` trigger runs the normal stable release pipeline and publishes the next semantic-release version. +5. Watch the stable release workflow through publish, Homebrew/Scoop updates, and verification. +6. Confirm that `Sync main to develop` succeeds. That workflow merges `main` back into `develop` after a successful `Release` run on `main`, keeping the hotfix reachable from the next beta and the next scheduled production deploy. If the sync conflicts, resolve it with a follow-up PR into `develop` before the next production promotion. + +Do not use `workflow_dispatch dry_run=false` as the normal hotfix path. Manual stable dispatch is reserved for re-cutting a unique version after an interrupted or stale-bytes release. Hotfixes should land through a PR to `main` so the production source of truth and release tag history stay aligned. + ### What each job does `**build` (ubuntu-latest):\*\*