Versions and upgrades
This page explains how mfeflow numbers its releases and how to move a project to a new one with the upgrade command.
How are versions numbered?
mfeflow follows semantic versioning. Every public package moves together: mfeflow, create-mfeflow and each @mfeflow/* package share one version number. A package with no changes still gets the new number.
Releases are listed in each package's CHANGELOG.md. The mfeflow package changelog is the best one to read.
What can break before 1.0?
While the version starts with 0, the rules are shifted by one place.
From 1.0, a major release is breaking, a minor adds features, and a patch fixes bugs.
What counts as public API?
A change to any of these is a breaking change:
- CLI commands, flags, exit codes and JSON output shapes
- Error codes and what they mean
- The schemas of the project file, the app file and the registry index
- The feature file format and the transform operations
- Each package's exports
- The layout of a generated project
Generated file contents that mfeflow does not migrate are not public API. They can change in a patch.
How do I upgrade a project?
This tutorial moves a project made with an older mfeflow to the version you run. The command never overwrites a file you changed unless you say so. It does not ask npm for a newer version. It upgrades to the version that runs, so pnpm dlx mfeflow@latest upgrade is the whole command.
You need a project with an mfeflow.json and a clean working tree, so you can review the result.
-
Read the changelog for every version between yours and the new one. A minor release before 1.0 can break things.
-
See what would change. A dry run prints a summary and every diff, and writes nothing:
You should see the summary first:
The diffs follow. The last group is the one to read.
-
Decide about each file you changed. Take the new version with
--accept, or keep yours with--keep. Both take a path, a comma list, orall:In a terminal it asks once before it writes. You should see:
-
Run it again to check. A kept file stays quiet until the new version of that file changes:
You should see:
-
Run the checks to find drift between the apps:
You should see
0 error(s)at the end.
What happens when I skip step 3?
In CI or a script there is nobody to ask. A file you changed and did not decide on stops the run before anything is written:
You should see an error, and the exit code is 8:
Add --accept or --keep and run it again. --yes does not skip this: it only stops the questions. A --dry-run never fails this way, and exits 0 even when files wait for a decision. The error page has the details.
What does the upgrade touch?
- A generated file you did not edit is replaced. A new file is created.
package.json,pnpm-workspace.yamlandturbo.jsononly get small edits. The@mfeflow/*ranges move, and new scripts, dependencies and tasks are added.- Files the new version no longer makes, and files you deleted, are listed. They are never deleted and never brought back.
- A file that
add env-keyadded keys to is replaced only if every key in it is still in the new version. Otherwise it is listed with your keys. Accepting it drops them, so add them back withmfeflow add env-key. - If you edited such a file, a key whose value differs from the new version counts as lost too.
mfeflow add env-keyrecords each key it adds inenvKeysinmfeflow.json. When the new version changes the value of a recorded key, the file is listed as changed by the CLI and needs a decision:--accepttakes the new value and--keepkeeps yours. If the project has no record of the key (it was made beforeenvKeysexisted), an untouched file still takes the new value and the plan prints a warning naming the keys, because mfeflow cannot tell a value you set withadd env-keyfrom one the template changed. When an env file is held back for a missing key, the changed values are listed with it. - A symlink is reported and never written over, even with
--accept all. It needs no decision and never blocks a run. - A file inside a symlinked folder counts the same way. If
apps/shell/srcpoints somewhere else, nothing under it is written, because the write would land outside the place the plan shows. The check runs again when the files are written, for every file includingpackage.json, so a link added after the plan was made is caught and the run stops with E105 before anything is written. - A file you edited is offered only when the new version generates something different from the old one.
- A feature from a registry is applied only after you confirm, or with
--yeswhen there is no terminal. If the registry now serves other bytes for the same version, the run stops with E406 until you pass--accept-changed-content. - A registry feature with no hash in
mfeflow.jsoncannot be checked. The plan lists it as first seen, and the hash the registry serves now is recorded, so the next run can check it. - Files are written all or nothing, and the install runs in the same step. If it fails, every file is put back.
mfeflow.json records a hash for each copied file, and a kept entry for each file you kept. The flags are in the CLI reference.
What if a registry feature changed?
If the registry serves other bytes for a feature at the version you installed, the upgrade stops with E406. Look at what changed before you go on. Then take the new content for the feature you trust:
You should see the upgrade run, and mfeflow.json now holds the new hash. Without a name the flag takes every changed feature, so name the ones you checked. A feature you did not name still stops the run. The new hash is saved even when the files themselves did not change, so the next run does not stop again.
How are releases made?
This part is for contributors. Every change to a published package needs a changeset, a small file that records the bump and the note for people upgrading:
When changes reach main, a bot opens or updates a "version packages" pull request. Merging it is the release decision. Publishing uses npm trusted publishing with provenance and no stored token. The full flow is in docs/versioning.md in the repository.
Pre-releases use the next npm tag, for example npm i mfeflow@next.