@ferrflow/doc 7.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/docs-en/ci/github-actions.md +120 -0
  2. package/docs-en/ci/gitlab-ci.md +90 -0
  3. package/docs-en/ci/hosted-bot.md +82 -0
  4. package/docs-en/ci/pipeline-triggers.md +287 -0
  5. package/docs-en/configuration/config-file.md +1259 -0
  6. package/docs-en/configuration/formats.md +220 -0
  7. package/docs-en/configuration/monorepo.md +390 -0
  8. package/docs-en/installation.md +56 -0
  9. package/docs-en/introduction.md +56 -0
  10. package/docs-en/quickstart.md +66 -0
  11. package/docs-en/reference/api.md +106 -0
  12. package/docs-en/reference/cli.md +483 -0
  13. package/docs-en/reference/conventional-commits.md +103 -0
  14. package/docs-en/reference/errors.md +508 -0
  15. package/docs-en/verifying-releases.md +97 -0
  16. package/docs-fr/ci/github-actions.md +109 -0
  17. package/docs-fr/ci/gitlab-ci.md +77 -0
  18. package/docs-fr/ci/hosted-bot.md +82 -0
  19. package/docs-fr/ci/pipeline-triggers.md +238 -0
  20. package/docs-fr/configuration/config-file.md +839 -0
  21. package/docs-fr/configuration/formats.md +163 -0
  22. package/docs-fr/configuration/monorepo.md +357 -0
  23. package/docs-fr/installation.md +56 -0
  24. package/docs-fr/introduction.md +54 -0
  25. package/docs-fr/quickstart.md +63 -0
  26. package/docs-fr/reference/api.md +106 -0
  27. package/docs-fr/reference/cli.md +407 -0
  28. package/docs-fr/reference/conventional-commits.md +103 -0
  29. package/docs-fr/reference/errors.md +378 -0
  30. package/docs-fr/verifying-releases.md +97 -0
  31. package/docs-fr-v4/ci/github-actions.md +106 -0
  32. package/docs-fr-v4/ci/gitlab-ci.md +77 -0
  33. package/docs-fr-v4/ci/pipeline-triggers.md +214 -0
  34. package/docs-fr-v4/configuration/config-file.md +769 -0
  35. package/docs-fr-v4/configuration/formats.md +128 -0
  36. package/docs-fr-v4/configuration/monorepo.md +324 -0
  37. package/docs-fr-v4/installation.md +48 -0
  38. package/docs-fr-v4/introduction.md +54 -0
  39. package/docs-fr-v4/legal/telemetry.md +65 -0
  40. package/docs-fr-v4/quickstart.md +63 -0
  41. package/docs-fr-v4/reference/cli.md +130 -0
  42. package/docs-fr-v4/reference/conventional-commits.md +67 -0
  43. package/docs-fr-v4/reference/errors.md +372 -0
  44. package/docs-fr-v5/ci/github-actions.md +109 -0
  45. package/docs-fr-v5/ci/gitlab-ci.md +77 -0
  46. package/docs-fr-v5/ci/hosted-bot.md +82 -0
  47. package/docs-fr-v5/ci/pipeline-triggers.md +238 -0
  48. package/docs-fr-v5/configuration/config-file.md +812 -0
  49. package/docs-fr-v5/configuration/formats.md +150 -0
  50. package/docs-fr-v5/configuration/monorepo.md +357 -0
  51. package/docs-fr-v5/installation.md +56 -0
  52. package/docs-fr-v5/introduction.md +54 -0
  53. package/docs-fr-v5/legal/telemetry.md +26 -0
  54. package/docs-fr-v5/quickstart.md +63 -0
  55. package/docs-fr-v5/reference/api.md +106 -0
  56. package/docs-fr-v5/reference/cli.md +356 -0
  57. package/docs-fr-v5/reference/conventional-commits.md +88 -0
  58. package/docs-fr-v5/reference/errors.md +378 -0
  59. package/docs-fr-v5/verifying-releases.md +97 -0
  60. package/docs-fr-v6/ci/github-actions.md +109 -0
  61. package/docs-fr-v6/ci/gitlab-ci.md +77 -0
  62. package/docs-fr-v6/ci/hosted-bot.md +82 -0
  63. package/docs-fr-v6/ci/pipeline-triggers.md +238 -0
  64. package/docs-fr-v6/configuration/config-file.md +813 -0
  65. package/docs-fr-v6/configuration/formats.md +150 -0
  66. package/docs-fr-v6/configuration/monorepo.md +357 -0
  67. package/docs-fr-v6/installation.md +56 -0
  68. package/docs-fr-v6/introduction.md +54 -0
  69. package/docs-fr-v6/quickstart.md +63 -0
  70. package/docs-fr-v6/reference/api.md +106 -0
  71. package/docs-fr-v6/reference/cli.md +356 -0
  72. package/docs-fr-v6/reference/conventional-commits.md +88 -0
  73. package/docs-fr-v6/reference/errors.md +378 -0
  74. package/docs-fr-v6/verifying-releases.md +97 -0
  75. package/docs-v0/ci/github-actions.md +77 -0
  76. package/docs-v0/ci/gitlab-ci.md +59 -0
  77. package/docs-v0/configuration/config-file.md +97 -0
  78. package/docs-v0/configuration/formats.md +86 -0
  79. package/docs-v0/configuration/monorepo.md +59 -0
  80. package/docs-v0/installation.md +48 -0
  81. package/docs-v0/introduction.md +34 -0
  82. package/docs-v0/legal/telemetry.md +63 -0
  83. package/docs-v0/quickstart.md +58 -0
  84. package/docs-v0/reference/cli.md +95 -0
  85. package/docs-v0/reference/conventional-commits.md +68 -0
  86. package/docs-v1/ci/github-actions.md +76 -0
  87. package/docs-v1/ci/gitlab-ci.md +58 -0
  88. package/docs-v1/configuration/config-file.md +515 -0
  89. package/docs-v1/configuration/formats.md +115 -0
  90. package/docs-v1/configuration/monorepo.md +246 -0
  91. package/docs-v1/installation.md +48 -0
  92. package/docs-v1/introduction.md +39 -0
  93. package/docs-v1/legal/telemetry.md +63 -0
  94. package/docs-v1/quickstart.md +62 -0
  95. package/docs-v1/reference/cli.md +128 -0
  96. package/docs-v1/reference/conventional-commits.md +67 -0
  97. package/docs-v2/ci/github-actions.md +117 -0
  98. package/docs-v2/ci/gitlab-ci.md +90 -0
  99. package/docs-v2/ci/pipeline-triggers.md +263 -0
  100. package/docs-v2/configuration/config-file.md +806 -0
  101. package/docs-v2/configuration/formats.md +98 -0
  102. package/docs-v2/configuration/monorepo.md +324 -0
  103. package/docs-v2/installation.md +48 -0
  104. package/docs-v2/introduction.md +40 -0
  105. package/docs-v2/legal/telemetry.md +66 -0
  106. package/docs-v2/quickstart.md +63 -0
  107. package/docs-v2/reference/cli.md +130 -0
  108. package/docs-v2/reference/conventional-commits.md +67 -0
  109. package/docs-v2/reference/errors.md +500 -0
  110. package/docs-v2/self-hosting.md +101 -0
  111. package/docs-v3/ci/github-actions.md +117 -0
  112. package/docs-v3/ci/gitlab-ci.md +90 -0
  113. package/docs-v3/ci/pipeline-triggers.md +263 -0
  114. package/docs-v3/configuration/config-file.md +806 -0
  115. package/docs-v3/configuration/formats.md +99 -0
  116. package/docs-v3/configuration/monorepo.md +324 -0
  117. package/docs-v3/installation.md +48 -0
  118. package/docs-v3/introduction.md +40 -0
  119. package/docs-v3/legal/telemetry.md +66 -0
  120. package/docs-v3/quickstart.md +66 -0
  121. package/docs-v3/reference/cli.md +161 -0
  122. package/docs-v3/reference/conventional-commits.md +67 -0
  123. package/docs-v3/reference/errors.md +502 -0
  124. package/docs-v3/self-hosting.md +137 -0
  125. package/docs-v4/ci/github-actions.md +117 -0
  126. package/docs-v4/ci/gitlab-ci.md +90 -0
  127. package/docs-v4/ci/pipeline-triggers.md +263 -0
  128. package/docs-v4/configuration/config-file.md +850 -0
  129. package/docs-v4/configuration/formats.md +182 -0
  130. package/docs-v4/configuration/monorepo.md +324 -0
  131. package/docs-v4/installation.md +48 -0
  132. package/docs-v4/introduction.md +56 -0
  133. package/docs-v4/legal/telemetry.md +65 -0
  134. package/docs-v4/quickstart.md +66 -0
  135. package/docs-v4/reference/cli.md +161 -0
  136. package/docs-v4/reference/conventional-commits.md +67 -0
  137. package/docs-v4/reference/errors.md +502 -0
  138. package/docs-v4/self-hosting.md +137 -0
  139. package/docs-v5/ci/github-actions.md +120 -0
  140. package/docs-v5/ci/gitlab-ci.md +90 -0
  141. package/docs-v5/ci/hosted-bot.md +82 -0
  142. package/docs-v5/ci/pipeline-triggers.md +287 -0
  143. package/docs-v5/configuration/config-file.md +1133 -0
  144. package/docs-v5/configuration/formats.md +206 -0
  145. package/docs-v5/configuration/monorepo.md +390 -0
  146. package/docs-v5/installation.md +56 -0
  147. package/docs-v5/introduction.md +56 -0
  148. package/docs-v5/legal/telemetry.md +26 -0
  149. package/docs-v5/quickstart.md +66 -0
  150. package/docs-v5/reference/api.md +106 -0
  151. package/docs-v5/reference/cli.md +431 -0
  152. package/docs-v5/reference/conventional-commits.md +88 -0
  153. package/docs-v5/reference/errors.md +508 -0
  154. package/docs-v5/verifying-releases.md +97 -0
  155. package/docs-v6/ci/github-actions.md +120 -0
  156. package/docs-v6/ci/gitlab-ci.md +90 -0
  157. package/docs-v6/ci/hosted-bot.md +82 -0
  158. package/docs-v6/ci/pipeline-triggers.md +287 -0
  159. package/docs-v6/configuration/config-file.md +1134 -0
  160. package/docs-v6/configuration/formats.md +206 -0
  161. package/docs-v6/configuration/monorepo.md +390 -0
  162. package/docs-v6/installation.md +56 -0
  163. package/docs-v6/introduction.md +56 -0
  164. package/docs-v6/quickstart.md +66 -0
  165. package/docs-v6/reference/api.md +106 -0
  166. package/docs-v6/reference/cli.md +431 -0
  167. package/docs-v6/reference/conventional-commits.md +88 -0
  168. package/docs-v6/reference/errors.md +508 -0
  169. package/docs-v6/verifying-releases.md +97 -0
  170. package/package.json +17 -0
@@ -0,0 +1,120 @@
1
+ ---
2
+ title: GitHub Actions
3
+ description: Run FerrFlow releases automatically in GitHub Actions.
4
+ ---
5
+
6
+ ## Using the official action
7
+
8
+ The easiest way to use FerrFlow in GitHub Actions is the `FerrLabs/ferrflow@v5` action. It installs the binary and runs `ferrflow release` automatically.
9
+
10
+ ```yaml title=".github/workflows/release.yml"
11
+ name: Release
12
+
13
+ on:
14
+ push:
15
+ branches: [main]
16
+
17
+ jobs:
18
+ release:
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ contents: write # required to push tags and create releases
22
+ steps:
23
+ - uses: actions/checkout@v6
24
+ with:
25
+ fetch-depth: 0 # full history needed for commit scanning
26
+ token: ${{ secrets.GITHUB_TOKEN }}
27
+
28
+ - uses: FerrLabs/ferrflow@v5
29
+ env:
30
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31
+ ```
32
+
33
+ <aside class="ferr-aside ferr-aside--warning"><div class="ferr-aside__body"><p><code>fetch-depth: 0</code> is required. Without it, FerrFlow cannot find previous tags and will treat every commit as new.</p>
34
+ </div></aside>
35
+
36
+ ## Permissions
37
+
38
+ FerrFlow needs `contents: write` to:
39
+
40
+ - Push version bump commits
41
+ - Create and push git tags
42
+ - Create GitHub Releases
43
+
44
+ If your repository has branch protection rules, create a dedicated token with the necessary permissions and pass it as `FERRFLOW_TOKEN` or configure the action's `token` input.
45
+
46
+ <aside class="ferr-aside ferr-aside--tip"><div class="ferr-aside__body"><p>Prefer not to manage a token? Set <code>bot: true</code> to author releases as <code>ferrflow[bot]</code> with zero secrets: see the <a href="/docs/ci/hosted-bot">Hosted bot</a> guide.</p>
47
+ </div></aside>
48
+
49
+ ## Accessing the release output
50
+
51
+ The action exposes the new version as an output you can use in downstream steps:
52
+
53
+ ```yaml title=".github/workflows/release.yml"
54
+ - uses: FerrLabs/ferrflow@v5
55
+ id: ferrflow
56
+ env:
57
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58
+
59
+ - name: Build Docker image
60
+ if: steps.ferrflow.outputs.version != ''
61
+ run: |
62
+ docker build -t myimage:${{ steps.ferrflow.outputs.version }} .
63
+ docker push myimage:${{ steps.ferrflow.outputs.version }}
64
+ ```
65
+
66
+ ## Skip CI on release commits
67
+
68
+ FerrFlow commits version bumps with `[skip ci]` in the message by default to prevent infinite loops. No extra configuration needed.
69
+
70
+ ## PR preview comments
71
+
72
+ FerrFlow can post a comment on every pull request showing what versions will be bumped when the PR is merged. The comment is automatically updated on each push.
73
+
74
+ ```yaml title=".github/workflows/preview.yml"
75
+ name: FerrFlow Preview
76
+
77
+ on:
78
+ pull_request:
79
+
80
+ permissions:
81
+ contents: read
82
+ pull-requests: write
83
+
84
+ jobs:
85
+ preview:
86
+ runs-on: ubuntu-latest
87
+ steps:
88
+ - uses: actions/checkout@v6
89
+ with:
90
+ fetch-depth: 0
91
+ - uses: FerrLabs/ferrflow@v5
92
+ with:
93
+ mode: preview
94
+ env:
95
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
96
+ ```
97
+
98
+ The comment looks like:
99
+
100
+ > **FerrFlow Release Preview**
101
+ >
102
+ > | Package | Current | Next | Bump |
103
+ > | ------- | ------- | ------- | ----- |
104
+ > | api | `1.5.0` | `1.6.0` | minor |
105
+ > | site | `1.8.0` | `1.8.1` | patch |
106
+ >
107
+ > Based on 3 commit(s).
108
+
109
+ If no releasable changes are detected, the comment says so.
110
+
111
+ ## Monorepo example
112
+
113
+ In a monorepo, FerrFlow releases each changed package in a single run:
114
+
115
+ ```yaml title=".github/workflows/release.yml"
116
+ - uses: FerrLabs/ferrflow@v5
117
+ env:
118
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
119
+ # Creates api@v1.3.0 and site@v0.5.1 in one step if both changed
120
+ ```
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: GitLab CI
3
+ description: Run FerrFlow releases automatically in GitLab CI.
4
+ ---
5
+
6
+ ## Using the Docker image
7
+
8
+ The official FerrFlow Docker image ships the binary and can be used directly as a GitLab CI job image.
9
+
10
+ ```yaml title=".gitlab-ci.yml"
11
+ release:
12
+ image: ghcr.io/ferrlabs/ferrflow:latest
13
+ stage: release
14
+ script:
15
+ - ferrflow release
16
+ variables:
17
+ GITLAB_TOKEN: $CI_JOB_TOKEN
18
+ rules:
19
+ - if: $CI_COMMIT_BRANCH == "main"
20
+ when: on_success
21
+ ```
22
+
23
+ <aside class="ferr-aside ferr-aside--warning"><div class="ferr-aside__body"><p>Make sure your CI runner clones with full history. Add <code>GIT_DEPTH: 0</code> to the job variables to disable shallow cloning.</p>
24
+ </div></aside>
25
+
26
+ ## Full history
27
+
28
+ ```yaml title=".gitlab-ci.yml"
29
+ release:
30
+ image: ghcr.io/ferrlabs/ferrflow:latest
31
+ variables:
32
+ GIT_DEPTH: 0 # full history: required for tag scanning
33
+ GITLAB_TOKEN: $CI_JOB_TOKEN
34
+ script:
35
+ - ferrflow release
36
+ rules:
37
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
38
+ ```
39
+
40
+ ## Using a deploy token
41
+
42
+ If `CI_JOB_TOKEN` doesn't have permission to push tags, create a project deploy token with `write_repository` access and store it as a CI variable:
43
+
44
+ ```yaml title=".gitlab-ci.yml"
45
+ release:
46
+ image: ghcr.io/ferrlabs/ferrflow:latest
47
+ variables:
48
+ GIT_DEPTH: 0
49
+ GITLAB_TOKEN: $FERRFLOW_DEPLOY_TOKEN # CI variable with write_repository access
50
+ script:
51
+ - ferrflow release
52
+ rules:
53
+ - if: $CI_COMMIT_BRANCH == "main"
54
+ ```
55
+
56
+ ## MR preview comments
57
+
58
+ FerrFlow can post a comment on every merge request showing what versions will be bumped when the MR is merged. The comment is automatically updated on each push.
59
+
60
+ ```yaml title=".gitlab-ci.yml"
61
+ ferrflow-preview:
62
+ image: ghcr.io/ferrlabs/ferrflow:latest
63
+ stage: test
64
+ variables:
65
+ GIT_DEPTH: 0
66
+ GITLAB_TOKEN: $CI_JOB_TOKEN
67
+ script:
68
+ - ferrflow check --comment
69
+ rules:
70
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
71
+ ```
72
+
73
+ The comment looks like:
74
+
75
+ > **FerrFlow Release Preview**
76
+ >
77
+ > | Package | Current | Next | Bump |
78
+ > | ------- | ------- | ------- | ----- |
79
+ > | api | `1.5.0` | `1.6.0` | minor |
80
+ >
81
+ > Based on 2 commit(s).
82
+
83
+ If no releasable changes are detected, the comment says so.
84
+
85
+ <aside class="ferr-aside ferr-aside--note"><div class="ferr-aside__body"><p><code>CI_JOB_TOKEN</code> has permission to post MR notes by default. If your project restricts this, use a project access token with <code>api</code> scope stored as a CI variable.</p>
86
+ </div></aside>
87
+
88
+ ## GitLab Releases
89
+
90
+ When `GITLAB_TOKEN` is set, FerrFlow creates a GitLab Release with the generated changelog as release notes, matching the behaviour of the GitHub integration.
@@ -0,0 +1,82 @@
1
+ ---
2
+ title: Hosted bot (ferrflow[bot])
3
+ description: Author releases as ferrflow[bot] with zero secrets, using the hosted FerrFlow GitHub App and OIDC token exchange.
4
+ ---
5
+
6
+ By default, the releases and tags FerrFlow pushes are authored by whoever owns the token in your workflow: usually a personal access token, so releases show up under _your_ account. The hosted bot lets releases be authored by **`ferrflow[bot]`** instead, with a clean, consistent identity across every repo.
7
+
8
+ It works like Renovate or Dependabot:
9
+
10
+ - **Zero secrets** in your workflow. No PAT to create, store, or rotate.
11
+ - Releases authored by **`ferrflow[bot]`**.
12
+ - **Short-lived, scoped tokens**: each run gets a fresh token that expires in an hour and is scoped to the single repository.
13
+
14
+ ## 1. Install the app
15
+
16
+ Go to **[github.com/apps/ferrflow](https://github.com/apps/ferrflow)** and click **Install**, then choose the organization and repositories you want FerrFlow to release. That's it. There are no secrets to create.
17
+
18
+ The app asks only for **Contents** (read & write, to push tags and create releases) and **Metadata** (read). You can review or uninstall it at any time from your organization's settings.
19
+
20
+ ## 2. Enable it in your workflow
21
+
22
+ Add `bot: true` to the action and grant the workflow permission to mint an OIDC token:
23
+
24
+ ```yaml title=".github/workflows/release.yml"
25
+ name: Release
26
+
27
+ on:
28
+ push:
29
+ branches: [main]
30
+
31
+ jobs:
32
+ release:
33
+ runs-on: ubuntu-latest
34
+ permissions:
35
+ id-token: write # lets the runner prove the repo's identity to FerrFlow
36
+ contents: read # for checkout; the release push uses the bot token
37
+ steps:
38
+ - uses: actions/checkout@v6
39
+ with:
40
+ fetch-depth: 0 # full history is needed for commit scanning
41
+
42
+ - uses: FerrLabs/FerrFlow@v5
43
+ with:
44
+ bot: true
45
+ ```
46
+
47
+ `permissions.id-token: write` is required. It's what lets the runner request the OIDC token that proves which repository is calling. Without it, FerrFlow stops with a clear error rather than falling back silently.
48
+
49
+ ## How it works
50
+
51
+ No secret ever leaves your repository. Each run exchanges a proof of identity for a token:
52
+
53
+ 1. The GitHub Actions runner issues a short-lived **OIDC token** describing your repository (audience `ferrflow.ferrlabs.com`).
54
+ 2. FerrFlow sends that token to **`api.ferrflow.com`**, which verifies it against GitHub's public keys.
55
+ 3. The service signs a **scoped installation token** for the FerrFlow app on your repo (using a private key that never leaves FerrLabs' KMS) and returns it. The token lives for one hour.
56
+ 4. FerrFlow uses that token to push tags, commits, and releases, so they're authored by `ferrflow[bot]`.
57
+
58
+ ## Security model
59
+
60
+ - **The app's private key never leaves FerrLabs' servers**: it lives in a KMS and is only ever used to sign installation tokens server-side.
61
+ - **Your identity is proven by OIDC, not a shared secret**: nothing sensitive is stored in your repo or transmitted from it.
62
+ - **Tokens are minimal and short-lived**: scoped to one repository, expiring after an hour.
63
+ - **You stay in control**: uninstall the app at any time to revoke all access immediately.
64
+
65
+ ## Troubleshooting
66
+
67
+ FerrFlow never falls back silently: if bot mode can't get a token, it fails with a message naming the exact cause.
68
+
69
+ | Message | Cause and fix |
70
+ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
71
+ | `bot mode requires permissions: id-token: write in your workflow` | The job is missing `permissions.id-token: write`. Add it (see above). |
72
+ | `FerrFlow App not installed on this repository's owner` | Install the app at [github.com/apps/ferrflow](https://github.com/apps/ferrflow) for this org / repo. |
73
+ | `FerrFlow hosted bot rate limit hit (429)` | Token requests are rate-limited per repository. Retry shortly, or use a PAT via `token:` for that run. |
74
+ | `FerrFlow hosted bot service unavailable` | A transient service issue. Check [status.ferrlabs.com](https://status.ferrlabs.com) or retry. |
75
+
76
+ ## Alternatives
77
+
78
+ The hosted bot is the recommended path, but it isn't the only one:
79
+
80
+ - **`token:` with a PAT**: supply your own [personal access token](/docs/ci/github-actions). Releases are authored by that token's owner. Works anywhere, including non-GitHub-Actions CI.
81
+ - **`token:` with your own GitHub App**: if you'd rather run your own bot identity, pass a token minted from your own app.
82
+ - **Default `GITHUB_TOKEN`**: the simplest option, but note that pushes made with `GITHUB_TOKEN` **do not trigger downstream workflows** (so a tag push won't kick off a separate publish job). The hosted bot and PATs don't have this limitation.
@@ -0,0 +1,287 @@
1
+ ---
2
+ title: Pipeline Triggers
3
+ description: Choose the right CI trigger strategy for your FerrFlow releases.
4
+ ---
5
+
6
+ FerrFlow works with any CI trigger strategy. This page covers the most common patterns, when to use each one, and how they interact with `releaseCommitMode`.
7
+
8
+ ## Push to main
9
+
10
+ The simplest setup: run `ferrflow release` on every push to the default branch. FerrFlow decides whether a release is needed based on the commits since the last tag.
11
+
12
+ ```yaml title=".github/workflows/release.yml"
13
+ name: Release
14
+
15
+ on:
16
+ push:
17
+ branches: [main]
18
+
19
+ jobs:
20
+ release:
21
+ runs-on: ubuntu-latest
22
+ # Skip release commits to avoid infinite loops
23
+ if: "!startsWith(github.event.head_commit.message, 'chore(release):')"
24
+ permissions:
25
+ contents: write
26
+ steps:
27
+ - uses: actions/checkout@v6
28
+ with:
29
+ fetch-depth: 0
30
+ token: ${{ secrets.FERRFLOW_TOKEN }}
31
+
32
+ - uses: FerrLabs/ferrflow@v5
33
+ env:
34
+ GITHUB_TOKEN: ${{ secrets.FERRFLOW_TOKEN }}
35
+ ```
36
+
37
+ **When to use:** Most projects. Simple, predictable, fully automated.
38
+
39
+ **Trade-offs:** Every push to main triggers a workflow run, even if no release is needed. FerrFlow exits early when there are no releasable commits, so the cost is minimal.
40
+
41
+ **Works with:** `releaseCommitMode: commit` (default) or `none`.
42
+
43
+ ## Tag-triggered
44
+
45
+ Run your build/deploy pipeline when FerrFlow creates a new tag. This separates the release step (tagging) from the downstream steps (building, publishing, deploying).
46
+
47
+ ```yaml title=".github/workflows/build.yml"
48
+ name: Build & Publish
49
+
50
+ on:
51
+ push:
52
+ tags:
53
+ - 'v*' # single-repo: v1.2.0
54
+ - '*@v*' # monorepo: api@v1.2.0, site@v0.5.1
55
+
56
+ jobs:
57
+ build:
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - uses: actions/checkout@v6
61
+
62
+ - name: Extract version from tag
63
+ id: version
64
+ run: |
65
+ TAG="${GITHUB_REF_NAME}"
66
+ VERSION="${TAG##*v}"
67
+ echo "tag=$TAG" >> "$GITHUB_OUTPUT"
68
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
69
+
70
+ - name: Build
71
+ run: echo "Building version ${{ steps.version.outputs.version }}"
72
+ ```
73
+
74
+ **When to use:** When you want to decouple versioning from build/deploy. Common for Docker image builds, npm publishing, or binary releases.
75
+
76
+ **Trade-offs:** Requires two workflows: one for the release (push-to-main) and one for the downstream build (tag-triggered). Adds a few seconds of latency between the tag push and the build start.
77
+
78
+ ### Monorepo: per-package builds
79
+
80
+ In a monorepo, use tag patterns to build only the package that was released:
81
+
82
+ ```yaml title=".github/workflows/build.yml"
83
+ name: Build Package
84
+
85
+ on:
86
+ push:
87
+ tags:
88
+ - 'api@v*'
89
+ - 'site@v*'
90
+
91
+ jobs:
92
+ build-api:
93
+ if: startsWith(github.ref_name, 'api@v')
94
+ runs-on: ubuntu-latest
95
+ steps:
96
+ - uses: actions/checkout@v6
97
+ - run: echo "Building API ${{ github.ref_name }}"
98
+
99
+ build-site:
100
+ if: startsWith(github.ref_name, 'site@v')
101
+ runs-on: ubuntu-latest
102
+ steps:
103
+ - uses: actions/checkout@v6
104
+ - run: echo "Building site ${{ github.ref_name }}"
105
+ ```
106
+
107
+ <aside class="ferr-aside ferr-aside--note"><div class="ferr-aside__body"><p>If FerrFlow releases multiple packages in a single run (e.g. <code>api@v1.3.0</code> and <code>site@v0.5.1</code>), each tag triggers its own workflow run. The builds happen in parallel automatically.</p>
108
+ </div></aside>
109
+
110
+ ## Release-triggered
111
+
112
+ Run a pipeline when a GitHub Release is published. This works well with FerrFlow's `--draft` flag: FerrFlow creates a draft release, you review it, then publishing triggers the build.
113
+
114
+ ```yaml title=".github/workflows/deploy.yml"
115
+ name: Deploy
116
+
117
+ on:
118
+ release:
119
+ types: [published]
120
+
121
+ jobs:
122
+ deploy:
123
+ runs-on: ubuntu-latest
124
+ steps:
125
+ - uses: actions/checkout@v6
126
+ with:
127
+ ref: ${{ github.event.release.tag_name }}
128
+
129
+ - name: Deploy
130
+ run: echo "Deploying ${{ github.event.release.tag_name }}"
131
+ ```
132
+
133
+ **When to use:** When you want a manual review gate before deploying. Create draft releases with `ferrflow release --draft`, review the changelog, then publish.
134
+
135
+ **Trade-offs:** Adds a manual step. The draft release must be published before the deploy runs.
136
+
137
+ **Works with:** All `releaseCommitMode` values.
138
+
139
+ ## Manual (workflow_dispatch)
140
+
141
+ Trigger a release on demand with an optional dry-run flag. Useful for controlled release cadences or when you don't want every merge to potentially release.
142
+
143
+ ```yaml title=".github/workflows/release.yml"
144
+ name: Release
145
+
146
+ on:
147
+ workflow_dispatch:
148
+ inputs:
149
+ dry_run:
150
+ description: 'Dry run (no tags, no commits, no releases)'
151
+ type: boolean
152
+ default: false
153
+
154
+ jobs:
155
+ release:
156
+ runs-on: ubuntu-latest
157
+ permissions:
158
+ contents: write
159
+ steps:
160
+ - uses: actions/checkout@v6
161
+ with:
162
+ fetch-depth: 0
163
+ token: ${{ secrets.FERRFLOW_TOKEN }}
164
+
165
+ - uses: FerrLabs/ferrflow@v5
166
+ with:
167
+ args: ${{ inputs.dry_run == true && '--dry-run' || '' }}
168
+ env:
169
+ GITHUB_TOKEN: ${{ secrets.FERRFLOW_TOKEN }}
170
+ ```
171
+
172
+ **When to use:** Teams that prefer explicit release decisions over automatic releases. Also useful as a secondary workflow alongside push-to-main for ad-hoc releases.
173
+
174
+ **Trade-offs:** Requires someone to click "Run workflow" in the Actions tab. Commits can pile up between releases, producing larger changelogs.
175
+
176
+ ## PR-based
177
+
178
+ Use `releaseCommitMode: pr` to have FerrFlow open a pull request with the version bump instead of committing directly. The release completes when the PR is merged.
179
+
180
+ ```yaml title="ferrflow.json"
181
+ { 'workspace': { 'releaseCommitMode': 'pr' } }
182
+ ```
183
+
184
+ ```yaml title=".github/workflows/release.yml"
185
+ name: Release
186
+
187
+ on:
188
+ push:
189
+ branches: [main]
190
+
191
+ jobs:
192
+ release:
193
+ runs-on: ubuntu-latest
194
+ if: "!startsWith(github.event.head_commit.message, 'chore(release):')"
195
+ permissions:
196
+ contents: write
197
+ pull-requests: write
198
+ steps:
199
+ - uses: actions/checkout@v6
200
+ with:
201
+ fetch-depth: 0
202
+ token: ${{ secrets.FERRFLOW_TOKEN }}
203
+
204
+ - uses: FerrLabs/ferrflow@v5
205
+ env:
206
+ GITHUB_TOKEN: ${{ secrets.FERRFLOW_TOKEN }}
207
+ ```
208
+
209
+ **When to use:** When you want to review version bumps before they land, or when branch protection prevents direct pushes to main.
210
+
211
+ **Trade-offs:** Adds an extra merge step. The PR must be merged before tags are created.
212
+
213
+ **Works with:** `releaseCommitMode: pr` only. Requires `pull-requests: write` permission.
214
+
215
+ ## Combining strategies
216
+
217
+ A common production setup combines push-to-main for versioning with tag-triggered builds:
218
+
219
+ ```yaml title=".github/workflows/release.yml"
220
+ # Workflow 1: Version and tag on every push to main
221
+ name: Release
222
+ on:
223
+ push:
224
+ branches: [main]
225
+ jobs:
226
+ release:
227
+ if: "!startsWith(github.event.head_commit.message, 'chore(release):')"
228
+ runs-on: ubuntu-latest
229
+ permissions:
230
+ contents: write
231
+ steps:
232
+ - uses: actions/checkout@v6
233
+ with:
234
+ fetch-depth: 0
235
+ token: ${{ secrets.FERRFLOW_TOKEN }}
236
+ - uses: FerrLabs/ferrflow@v5
237
+ env:
238
+ GITHUB_TOKEN: ${{ secrets.FERRFLOW_TOKEN }}
239
+ ```
240
+
241
+ ```yaml title=".github/workflows/build.yml"
242
+ # Workflow 2: Build and deploy when a tag is pushed
243
+ name: Build
244
+ on:
245
+ push:
246
+ tags: ['v*', '*@v*']
247
+ jobs:
248
+ build:
249
+ runs-on: ubuntu-latest
250
+ steps:
251
+ - uses: actions/checkout@v6
252
+ - run: echo "Building ${{ github.ref_name }}"
253
+ ```
254
+
255
+ ## Concurrency safety
256
+
257
+ Since v5.2, `ferrflow release` acquires `.git/ferrflow.lock` atomically (`O_CREAT|O_EXCL`) at the start of every mutating run. A second concurrent invocation on the same repo fails fast with a clear error rather than racing on git refs: the classic failure mode is a manually-triggered release firing at the same time as a cron-driven `auto-release`, producing half-pushed tag sets, non-fast-forward rejects, or duplicate draft releases.
258
+
259
+ You don't need to wire anything up. The lock is automatic on every `release` invocation. Read-only commands (`check`, `status`, `version`, `tag`) skip it.
260
+
261
+ If a previous run crashed without releasing the lock, the next invocation takes it over automatically after 30 minutes (the host + PID stamped inside the lockfile lets FerrFlow detect stale locks). To take it over sooner, delete `.git/ferrflow.lock` manually.
262
+
263
+ <aside class="ferr-aside ferr-aside--note"><div class="ferr-aside__body"><p>The lock is per-repo, scoped to <code>.git/</code>. It does not protect across separate clones of the same repo: if you run releases concurrently from two different runners against two different checkouts of the same remote, the lock won&#39;t see the other side. Use a single release runner, or serialize at the CI level (<code>concurrency:</code> in GitHub Actions, <code>interruptible: false</code> in GitLab).</p>
264
+ </div></aside>
265
+
266
+ ## Crash-resume
267
+
268
+ A release does several side-effecting steps in sequence: write versioned files → commit → tag → push commit → push tags → create GitHub Releases → run `post_publish` hooks. If the process dies between any of those (network blip, runner termination, SIGKILL), the repo is in a half-released state.
269
+
270
+ Since v5.3, FerrFlow writes a `.git/ferrflow.checkpoint.json` after each phase succeeds. The next `ferrflow release` invocation on the same commit resumes from the recorded phase and skips the work that already happened: useful at CI scale where transient failures are routine. The checkpoint is deleted automatically when a release finishes cleanly.
271
+
272
+ Two safety rails:
273
+
274
+ - HEAD-pinning: the checkpoint records the commit SHA the release was operating on. If you advance HEAD between runs, FerrFlow refuses to resume and tells you to either reset HEAD back to the recorded commit or delete the checkpoint manually. This avoids replaying old tags onto a different commit graph.
275
+ - Idempotency: the underlying git and forge operations already no-op when their target exists (a tag that's already pushed is a no-op, a release that already exists is reused), so a mid-phase crash still recovers cleanly on the next run.
276
+
277
+ To start fresh after a crash you don't want to resume from, delete `.git/ferrflow.checkpoint.json` manually.
278
+
279
+ ## Summary
280
+
281
+ | Trigger | Automatic | Review gate | Best for |
282
+ | ----------------- | --------- | ----------- | -------------------------- |
283
+ | Push to main | Yes | No | Most projects |
284
+ | Tag-triggered | Yes | No | Decoupled build/deploy |
285
+ | Release-triggered | No | Yes | Draft → review → publish |
286
+ | Manual | No | Yes | Controlled release cadence |
287
+ | PR-based | Partial | Yes | Branch protection / review |