Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 6 additions & 1 deletion docs/diagrams/release-algorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ flowchart TB
GRAPH --> TAGS["pre-collect every tag + commit OID<br/>in one tag_foreach scan"]
TAGS --> ANALYSE["per package: find last tag,<br/>read commits since,<br/>classify conventional commits"]
ANALYSE --> PLAN["compute the bump<br/>→ snapshot of every package to change"]
PLAN --> CL["build the changelog section once"]
PLAN --> VC["versionCommand sets the version<br/>(release only, never check or preview)"]
VC --> CL["build the changelog section once"]
CL --> WRITE["write versions into the manifests"]
WRITE --> EXEC["execute_release"]
```

`versionCommand` sits between the decided bump and every write, so the tag, the changelog heading and
the version files all carry the version it prints. Read-only commands skip it and report the version as
command-determined instead of printing a number that may not be the one that ships.

The tag pre-collection matters more than it looks: without it, every package does its own tag walk,
and lookups go from `O(1)` against a prepared map to `O(tags)` callbacks plus an `O(commits)` walk —
per package. On a monorepo with many tags that is the difference between seconds and minutes.
Expand Down
8 changes: 8 additions & 0 deletions docs/site/data/schema/ferrflow-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@
],
"description": "Snake_case form of `buildMetadata` — both spellings are accepted. Overrides `workspace.buildMetadata` for this package. A command replaces the workspace one, `true` inherits it, and `false` releases this package with a plain version while the rest of the workspace stays stamped."
},
"versionCommand": {
"type": "string",
"description": "Overrides `workspace.versionCommand` for this package. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"version_command": {
"type": "string",
"description": "Snake_case form of `versionCommand` — both spellings are accepted. Overrides `workspace.versionCommand` for this package. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"hooks": {
"$ref": "#/$defs/hooks"
},
Expand Down
16 changes: 16 additions & 0 deletions docs/site/data/schema/ferrflow.json
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,14 @@
"build_metadata": {
"type": "string",
"description": "Snake_case form of `buildMetadata` — both spellings are accepted. Command whose stdout is appended to the version as semver build metadata, after a `+`. It is run once per release, never in a dry run, and its output must be dot-separated alphanumerics and hyphens. The tag and the changelog keep the plain version, since build metadata is excluded from version identity and ignored for precedence."
},
"versionCommand": {
"type": "string",
"description": "Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"version_command": {
"type": "string",
"description": "Snake_case form of `versionCommand` — both spellings are accepted. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
}
},
"additionalProperties": false
Expand Down Expand Up @@ -669,6 +677,14 @@
"oneOf": [{ "type": "string" }, { "type": "boolean" }],
"description": "Snake_case form of `buildMetadata` — both spellings are accepted. Overrides `workspace.buildMetadata` for this package. A command replaces the workspace one, `true` inherits it, and `false` releases this package with a plain version while the rest of the workspace stays stamped."
},
"versionCommand": {
"type": "string",
"description": "Overrides `workspace.versionCommand` for this package. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"version_command": {
"type": "string",
"description": "Snake_case form of `versionCommand` — both spellings are accepted. Overrides `workspace.versionCommand` for this package. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"hooks": {
"$ref": "#/$defs/hooks"
},
Expand Down
21 changes: 21 additions & 0 deletions docs/site/docs-en/configuration/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,26 @@ buildMetadata = &quot;sh scripts/protocol-versions.sh&quot;
</div></div>
</div>

### Version command

`versionCommand` names a command whose stdout is the version a release ships. Reach for it when the version itself, not a suffix, is decided outside the commits: a number owned by an upstream project, a date scheme the built-in strategies do not cover. For decoration after a `+`, use `buildMetadata` instead.

The command runs in `release`, `--dry-run` included, after the bump is decided and before any version file, tag or changelog heading is written, so all three carry its output. It receives the usual `FERRFLOW_*` variables; `FERRFLOW_NEW_VERSION` holds the version the strategy would have picked and `FERRFLOW_COMMITS_JSON` the commits. FerrFlow trims the output and drops a leading `v`.

The release aborts if the command fails, prints nothing, prints something the strategy cannot parse, prints a version that does not go past the current one, or prints a version whose tag already exists. Printing the current version releases nothing for that package. In a monorepo the command runs once per package that releases, and a package can set its own.

`check`, `preview`, `status`, `why` and `graph` never run it, because they run in places that must not execute repository code. They report the version as set by the command, and `check --json` prints `null` for `next_version` and `tag`.

A package in a `linked` or `fixed` group cannot set `versionCommand`, and `--force-version` takes precedence over it.

```json
{
"workspace": {
"versionCommand": "sh scripts/next-version.sh"
}
}
```

### Changelog rendering

Omit `changelog` entirely and FerrFlow writes the classic layout: **Breaking Changes**, **Features**, **Bug Fixes** and **Refactoring**, flat bullets, no links. `perf` commits fold into **Bug Fixes** there, and every other type, `docs` and `security` included, is left out.
Expand Down Expand Up @@ -560,6 +580,7 @@ Defines a package to version. You can have one or many.
| `floatingTags` | no | inherited from workspace | Override floating tags for this package |
| `latestTag` | no | inherited from workspace | Override the floating alias tag for this package |
| `buildMetadata` | no | inherited from workspace | Override the build metadata command for this package, or `false` to release it with a plain version |
| `versionCommand` | no | inherited from workspace | Override the version command for this package |
| `versionSource` | no | inherited from workspace | Override the tag-vs-file resolution for this package |

### `versionedFiles`
Expand Down
21 changes: 21 additions & 0 deletions docs/site/docs-fr/configuration/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,8 +420,29 @@ Définit un package à versionner. Vous pouvez en avoir un ou plusieurs.
| `tagTemplate` | non | hérité du workspace | Surcharger le modèle de tag pour ce package |
| `latestTag` | non | hérité du workspace | Surcharger le tag alias flottant pour ce package |
| `buildMetadata` | non | hérité du workspace | Surcharger la commande de métadonnées, ou `false` pour une version nue |
| `versionCommand` | non | hérité du workspace | Surcharger la commande de version pour ce package |
| `versionSource` | non | hérité du workspace | Surcharger la résolution tag / fichier pour ce package |

### Commande de version

`versionCommand` désigne une commande dont la sortie standard est la version publiée par une release. À utiliser quand la version elle-même, et non un suffixe, est décidée hors des commits : un numéro imposé par un projet amont, un schéma de dates que les stratégies intégrées ne couvrent pas. Pour une décoration après un `+`, préférer `buildMetadata`.

La commande est exécutée par `release`, `--dry-run` compris, une fois le bump décidé et avant l'écriture de tout fichier de version, tag ou titre de changelog, qui portent donc tous sa sortie. Elle reçoit les variables `FERRFLOW_*` habituelles ; `FERRFLOW_NEW_VERSION` contient la version que la stratégie aurait choisie et `FERRFLOW_COMMITS_JSON` les commits. FerrFlow élague la sortie et retire un `v` initial.

La release est interrompue si la commande échoue, n'imprime rien, imprime une valeur que la stratégie ne sait pas lire, une version qui ne dépasse pas la version courante, ou une version dont le tag existe déjà. Imprimer la version courante ne publie rien pour ce package. Dans un monorepo la commande s'exécute une fois par package publié, et chaque package peut définir la sienne.

`check`, `preview`, `status`, `why` et `graph` ne l'exécutent jamais, car ils tournent dans des contextes qui ne doivent pas exécuter le code du dépôt. Ils indiquent que la version est fixée par la commande, et `check --json` renvoie `null` pour `next_version` et `tag`.

Un package d'un groupe `linked` ou `fixed` ne peut pas définir `versionCommand`, et `--force-version` l'emporte sur elle.

```json
{
"workspace": {
"versionCommand": "sh scripts/next-version.sh"
}
}
```

### `versionedFiles`

Fichiers dans lesquels le numéro de version doit être mis à jour.
Expand Down
8 changes: 8 additions & 0 deletions schema/ferrflow-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@
],
"description": "Snake_case form of `buildMetadata` — both spellings are accepted. Overrides `workspace.buildMetadata` for this package. A command replaces the workspace one, `true` inherits it, and `false` releases this package with a plain version while the rest of the workspace stays stamped."
},
"versionCommand": {
"type": "string",
"description": "Overrides `workspace.versionCommand` for this package. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"version_command": {
"type": "string",
"description": "Snake_case form of `versionCommand` — both spellings are accepted. Overrides `workspace.versionCommand` for this package. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"hooks": {
"$ref": "#/$defs/hooks"
},
Expand Down
16 changes: 16 additions & 0 deletions schema/ferrflow.json
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,14 @@
"build_metadata": {
"type": "string",
"description": "Snake_case form of `buildMetadata` — both spellings are accepted. Command whose stdout is appended to the version as semver build metadata, after a `+`. It is run once per release, never in a dry run, and its output must be dot-separated alphanumerics and hyphens. The tag and the changelog keep the plain version, since build metadata is excluded from version identity and ignored for precedence."
},
"versionCommand": {
"type": "string",
"description": "Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"version_command": {
"type": "string",
"description": "Snake_case form of `versionCommand` — both spellings are accepted. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
}
},
"additionalProperties": false
Expand Down Expand Up @@ -669,6 +677,14 @@
"oneOf": [{ "type": "string" }, { "type": "boolean" }],
"description": "Snake_case form of `buildMetadata` — both spellings are accepted. Overrides `workspace.buildMetadata` for this package. A command replaces the workspace one, `true` inherits it, and `false` releases this package with a plain version while the rest of the workspace stays stamped."
},
"versionCommand": {
"type": "string",
"description": "Overrides `workspace.versionCommand` for this package. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"version_command": {
"type": "string",
"description": "Snake_case form of `versionCommand` — both spellings are accepted. Overrides `workspace.versionCommand` for this package. Command whose stdout is the version this release ships, replacing the one the versioning strategy computed. It receives the usual `FERRFLOW_*` variables, with `FERRFLOW_NEW_VERSION` holding the computed proposal. It runs only in `release` (dry run included), after the bump is decided and before any version file, tag or changelog heading is written. `check`, `preview`, `status`, `why` and `graph` never run it and report the version as set by the command. The output must be a version that goes past the current one and does not collide with an existing tag. Not allowed on a package in a linked or fixed group."
},
"hooks": {
"$ref": "#/$defs/hooks"
},
Expand Down
Loading
Loading