mfeflow 0.5.0 → 0.6.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 (68) hide show
  1. package/README.md +8 -3
  2. package/assets/features/registry-server/feature.json +128 -0
  3. package/assets/features/registry-server/files/deploy-server.yml +69 -0
  4. package/assets/features/registry-server/files/registry-server.md.eta +52 -0
  5. package/assets/features/registry-server/files/registry-sources.ts +12 -0
  6. package/assets/features/registry-static/feature.json +61 -0
  7. package/assets/features/registry-static/files/deploy.yml +61 -0
  8. package/assets/features/registry-static/files/registry.md.eta +49 -0
  9. package/assets/features/release/feature.json +43 -0
  10. package/assets/features/release/files/changeset-readme.md +7 -0
  11. package/assets/features/release/files/config.json +10 -0
  12. package/assets/features/release/files/releasing.md +21 -0
  13. package/assets/templates/monorepo/feature.json +7 -6
  14. package/assets/templates/monorepo/files/remote/{mfe.json.eta → mfeflow.app.json.eta} +1 -0
  15. package/assets/templates/monorepo/files/root/README.md.eta +1 -1
  16. package/assets/templates/monorepo/files/shell/{mfe.json.eta → mfeflow.app.json.eta} +1 -0
  17. package/assets/templates/monorepo/files/shell/rsbuild.config.ts.eta +42 -6
  18. package/assets/templates/monorepo/migrations/0.4.0-app-file.mjs +58 -0
  19. package/assets/templates/polyrepo/feature.json +305 -0
  20. package/assets/templates/polyrepo/files/AGENTS.md.eta +15 -0
  21. package/assets/templates/polyrepo/files/changeset/README.md +5 -0
  22. package/assets/templates/polyrepo/files/changeset/config.json +9 -0
  23. package/assets/templates/polyrepo/files/packages/contracts/package.json.eta +23 -0
  24. package/assets/templates/polyrepo/files/packages/contracts/src/index.ts +11 -0
  25. package/assets/templates/polyrepo/files/packages/contracts/tsconfig.json.eta +5 -0
  26. package/assets/templates/polyrepo/files/packages/tokens/package.json.eta +23 -0
  27. package/assets/templates/polyrepo/files/packages/tokens/src/index.ts +13 -0
  28. package/assets/templates/polyrepo/files/packages/tokens/tsconfig.json.eta +5 -0
  29. package/assets/templates/polyrepo/files/platform/README.md.eta +43 -0
  30. package/assets/templates/polyrepo/files/platform/default.json.eta +27 -0
  31. package/assets/templates/polyrepo/files/remote/README.md.eta +23 -0
  32. package/assets/templates/polyrepo/files/renovate.json.eta +4 -0
  33. package/assets/templates/polyrepo/files/shell/README.md.eta +32 -0
  34. package/assets/templates/polyrepo/files/tsconfig.base.json.eta +13 -0
  35. package/assets/templates/polyrepo/files/workflows/platform-ci.yml +29 -0
  36. package/assets/templates/polyrepo/files/workflows/release.yml +40 -0
  37. package/assets/templates/polyrepo/files/workflows/reusable-ci.yml.eta +50 -0
  38. package/assets/templates/polyrepo/files/workflows/team-ci.yml.eta +12 -0
  39. package/dist/against.d.ts +16 -0
  40. package/dist/catalog.d.ts +4 -0
  41. package/dist/checks/compat.d.ts +12 -0
  42. package/dist/checks/index.d.ts +0 -1
  43. package/dist/commands/add.d.ts +0 -2
  44. package/dist/commands/adopt.d.ts +54 -0
  45. package/dist/commands/deploy.d.ts +35 -0
  46. package/dist/commands/doctor.d.ts +5 -0
  47. package/dist/commands/init.d.ts +9 -1
  48. package/dist/commands/server-mode.d.ts +210 -0
  49. package/dist/commands/status.d.ts +5 -0
  50. package/dist/commands/team-repo.d.ts +9 -0
  51. package/dist/commands/upgrade.d.ts +66 -0
  52. package/dist/devremotes.d.ts +15 -0
  53. package/dist/exit.d.ts +1 -0
  54. package/dist/glue.d.ts +6 -0
  55. package/dist/index.d.ts +1 -1
  56. package/dist/index.js +4546 -1880
  57. package/dist/inspect.d.ts +14 -2
  58. package/dist/output.d.ts +138 -3
  59. package/dist/pipeline.d.ts +5 -2
  60. package/dist/polyrepo.d.ts +69 -0
  61. package/dist/publish.d.ts +10 -1
  62. package/dist/runtime.d.ts +2 -0
  63. package/dist/server-client.d.ts +61 -0
  64. package/dist/system.d.ts +2 -0
  65. package/dist/testing.d.ts +17 -1
  66. package/dist/upgrade.d.ts +25 -0
  67. package/dist/write.d.ts +8 -0
  68. package/package.json +5 -5
package/README.md CHANGED
@@ -26,7 +26,7 @@ pnpm dev # shell on :4300, remotes on :4311,
26
26
  | `mfeflow registry build <dir>` | Check a folder of features and write `index.json` and the files to host |
27
27
  | `mfeflow dev` | Run the shell and every remote, one prefixed log each; follows `add remote` |
28
28
  | `mfeflow doctor` | Singletons, compatibility, config health, drift; `--fix` repairs the safe ones |
29
- | `mfeflow validate` | `mfeflow.json`, every `mfe.json` and every registry entry against their schemas |
29
+ | `mfeflow validate` | `mfeflow.json`, every `mfeflow.app.json` and every registry entry against schemas |
30
30
  | `mfeflow check-compat` | Each remote's shared ranges against the shells it runs in |
31
31
  | `mfeflow budget` | Built first-load JS of the shell and each remote against the cost-model budget |
32
32
  | `mfeflow deploy <remote> --env <env>` | Upload a remote's build to a static registry host and point the env's index at it |
@@ -48,13 +48,17 @@ runs part of the project; `--remote name=url` uses a server you run yourself (th
48
48
  localhost unless `--allow-origin <origin>` names it); Ctrl+C stops every app, process trees included, on Windows too.
49
49
  It exits 130 after Ctrl+C.
50
50
 
51
+ Each app keeps its owner, routes and budgets in `mfeflow.app.json`. An app that still has the old `mfe.json` works:
52
+ the CLI reads it and prints one notice per command. An app with both files is an error (E122).
53
+
51
54
  `doctor` runs these checks, each with a code and a docs page (`https://mfeflow.github.io/mfeflow/errors/<code>`). It exits with 0
52
55
  when there are no errors, else with the lowest exit code among the errors' ranges (project 3 before build 5).
53
56
 
54
57
  | Check | Code | Fixed by `--fix` |
55
58
  | ------------------------------------- | -------------- | ----------------------------- |
56
59
  | every app in mfeflow.json is on disk | E112 | |
57
- | valid `mfe.json`, name matches | E113, E116 | |
60
+ | valid `mfeflow.app.json`, name match | E113, E116 | |
61
+ | no app has both app file names | E122 | |
58
62
  | no shared port | E115 | |
59
63
  | no route claimed twice | E116 | |
60
64
  | registry entry per remote and env | E114 | writes the missing ones |
@@ -70,7 +74,7 @@ when there are no errors, else with the lowest exit code among the errors' range
70
74
  | server keys listed in `turbo.json` | E121 (warning) | |
71
75
 
72
76
  `validate` runs the schema and registry checks, `check-compat` the compatibility check, `budget` reads the build output
73
- (`dist/`): each remote's entry against `budget.remoteKb` (10), a route against `mfe.json` `budgets.initialJsKb`, and the
77
+ (`dist/`): each remote's entry against `budget.remoteKb` (10), a route against `mfeflow.app.json` `budgets.initialJsKb`, and the
74
78
  shell against `budget.baselineKb + budget.shellKb` (32) when a baseline is set. All four take `--json` and exit non-zero on
75
79
  errors. LCP is measured by the perf job, not by `budget`.
76
80
 
@@ -111,6 +115,7 @@ MFEFLOW_E108 The target folder is not empty: /work/app already has notes.txt
111
115
  | 5 | build or runtime problem: `MFEFLOW_E3xx` |
112
116
  | 6 | feature registry problem: `MFEFLOW_E4xx` |
113
117
  | 7 | deploy problem: `MFEFLOW_E5xx` |
118
+ | 8 | upgrade problem, or files that need a decision: `MFEFLOW_E6xx` |
114
119
  | 130 | cancelled at a prompt (nothing was written), or Ctrl+C in `mfeflow dev` |
115
120
 
116
121
  ## Try it from a checkout
@@ -0,0 +1,128 @@
1
+ {
2
+ "name": "registry-server",
3
+ "version": "0.1.0",
4
+ "description": "Wire the project to a self-hosted mfeflow registry server: the shell reads its index from the server (with the browser beacon for percentage rollouts), a CI workflow deploys every remote with a token from the environment, and a guide covers the day-to-day commands. Opt-in; the static registry works without it.",
5
+ "requires": ["core"],
6
+ "prompts": [
7
+ {
8
+ "name": "serverUrl",
9
+ "type": "string",
10
+ "message": "Registry server address (https://registry.example.com), empty to set it later",
11
+ "default": ""
12
+ }
13
+ ],
14
+ "files": [
15
+ {
16
+ "to": "src/registry-sources.ts",
17
+ "target": "shell",
18
+ "from": "files/registry-sources.ts"
19
+ },
20
+ {
21
+ "to": "docs/registry-server.md",
22
+ "target": "root",
23
+ "from": "files/registry-server.md.eta",
24
+ "template": true
25
+ },
26
+ {
27
+ "to": ".github/workflows/deploy-server.yml",
28
+ "target": "root",
29
+ "from": "files/deploy-server.yml",
30
+ "when": {
31
+ "feature:ci-github": true
32
+ }
33
+ }
34
+ ],
35
+ "dependencies": [
36
+ {
37
+ "name": "mfeflow",
38
+ "range": "latest",
39
+ "target": "root",
40
+ "dev": true
41
+ }
42
+ ],
43
+ "scripts": [
44
+ {
45
+ "name": "server:deploy",
46
+ "command": "mfeflow deploy",
47
+ "target": "root"
48
+ },
49
+ {
50
+ "name": "server:rollback",
51
+ "command": "mfeflow rollback",
52
+ "target": "root"
53
+ },
54
+ {
55
+ "name": "server:status",
56
+ "command": "mfeflow status",
57
+ "target": "root"
58
+ }
59
+ ],
60
+ "transforms": [
61
+ {
62
+ "op": "add-env-key",
63
+ "file": "src/config/schema.ts",
64
+ "target": "shell",
65
+ "key": "registryUrls",
66
+ "scope": "runtime",
67
+ "schema": {
68
+ "zod": "z.array(z.string())",
69
+ "valibot": "v.array(v.string())",
70
+ "arktype": "'string[]'"
71
+ },
72
+ "value": [],
73
+ "configFiles": [
74
+ "public/config/runtime-config.dev.json",
75
+ "public/config/runtime-config.staging.json",
76
+ "public/config/runtime-config.prod.json"
77
+ ],
78
+ "when": {
79
+ "feature:env-t3": true
80
+ }
81
+ },
82
+ {
83
+ "op": "add-env-key",
84
+ "file": "src/config/schema.ts",
85
+ "target": "shell",
86
+ "key": "registryBeacon",
87
+ "scope": "runtime",
88
+ "schema": {
89
+ "zod": "z.boolean()",
90
+ "valibot": "v.boolean()",
91
+ "arktype": "'boolean'"
92
+ },
93
+ "value": false,
94
+ "configFiles": [
95
+ "public/config/runtime-config.dev.json",
96
+ "public/config/runtime-config.staging.json",
97
+ "public/config/runtime-config.prod.json"
98
+ ],
99
+ "when": {
100
+ "feature:env-t3": true
101
+ }
102
+ },
103
+ {
104
+ "op": "add-env-key",
105
+ "file": "public/config/runtime-config.json",
106
+ "target": "shell",
107
+ "key": "registryUrls",
108
+ "scope": "runtime",
109
+ "schema": "z.array(z.string())",
110
+ "value": [],
111
+ "when": {
112
+ "feature:env-t3": false
113
+ }
114
+ },
115
+ {
116
+ "op": "add-env-key",
117
+ "file": "public/config/runtime-config.json",
118
+ "target": "shell",
119
+ "key": "registryBeacon",
120
+ "scope": "runtime",
121
+ "schema": "z.boolean()",
122
+ "value": false,
123
+ "when": {
124
+ "feature:env-t3": false
125
+ }
126
+ }
127
+ ]
128
+ }
@@ -0,0 +1,69 @@
1
+ name: deploy-server
2
+
3
+ # Deploys every remote through the registry server when main changes. The builds go to the artifact host first,
4
+ # then the server moves the pointer. Needs the address of the server (MFEFLOW_REGISTRY_URL, a variable), a token
5
+ # (MFEFLOW_REGISTRY_TOKEN, a secret) and an artifact host (see docs/registry-server.md).
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ workflow_dispatch:
10
+ inputs:
11
+ env:
12
+ description: Environment to deploy to
13
+ default: prod
14
+ type: string
15
+
16
+ permissions: {}
17
+
18
+ # Two deploys at once would race on the same pointer; let the first finish.
19
+ concurrency:
20
+ group: deploy-server
21
+ cancel-in-progress: false
22
+
23
+ jobs:
24
+ deploy:
25
+ runs-on: ubuntu-latest
26
+ timeout-minutes: 20
27
+ # A manual run from another branch would otherwise deploy that branch.
28
+ if: github.ref == 'refs/heads/main'
29
+ environment: registry
30
+ permissions:
31
+ contents: read
32
+ steps:
33
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
34
+ with:
35
+ persist-credentials: false
36
+ - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
37
+ - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
38
+ with:
39
+ node-version: lts/*
40
+ cache: pnpm
41
+ - run: pnpm install --frozen-lockfile
42
+ - run: pnpm build
43
+ - name: Deploy remotes
44
+ env:
45
+ DEPLOY_ENV: ${{ inputs.env || 'prod' }}
46
+ MFEFLOW_REGISTRY_URL: ${{ vars.MFEFLOW_REGISTRY_URL }}
47
+ MFEFLOW_REGISTRY_TOKEN: ${{ secrets.MFEFLOW_REGISTRY_TOKEN }}
48
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
49
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
50
+ MFEFLOW_S3_ENDPOINT: ${{ vars.MFEFLOW_S3_ENDPOINT }}
51
+ MFEFLOW_S3_REGION: ${{ vars.MFEFLOW_S3_REGION }}
52
+ run: |
53
+ set -euo pipefail
54
+ # With -e, a mfeflow.json that cannot be read or parsed stops the step here.
55
+ remotes=$(node -p "Object.entries(JSON.parse(require('fs').readFileSync('mfeflow.json', 'utf8')).apps).filter(([, app]) => app.kind === 'remote').map(([name]) => name).join(' ')")
56
+ if [ -z "$remotes" ]; then
57
+ echo "::error::mfeflow.json lists no remotes to deploy"
58
+ exit 1
59
+ fi
60
+ failed=""
61
+ for remote in $remotes; do
62
+ if ! pnpm mfeflow deploy "$remote" --env "$DEPLOY_ENV" --yes; then
63
+ failed="$failed $remote"
64
+ fi
65
+ done
66
+ if [ -n "$failed" ]; then
67
+ echo "::error::deploy failed for:$failed"
68
+ exit 1
69
+ fi
@@ -0,0 +1,52 @@
1
+ # Registry server
2
+
3
+ This project can publish its remotes through a self-hosted mfeflow registry server. The server keeps the pointer
4
+ (which version each environment serves) in SQLite, checks every deploy, and takes tokens with narrow scopes. The
5
+ build files still live on a static host or bucket; the server only moves the pointer.
6
+
7
+ ## Configuration
8
+
9
+ - `serverUrl`: <%= it.options.serverUrl === '' ? 'not set yet' : '`' + it.options.serverUrl + '`' %>
10
+
11
+ This is the address `registry-server` was added with. Commands read it from `MFEFLOW_REGISTRY_URL`, or `--registry-url`.
12
+ The token is read only from `MFEFLOW_REGISTRY_TOKEN`; there is no flag for it, so it never lands in your shell history.
13
+ Plain http works only for localhost.
14
+
15
+ ## Point the shell at the server
16
+
17
+ In `public/config/runtime-config.prod.json`, set the index address and the origins it may be on:
18
+
19
+ ```json
20
+ {
21
+ "registryUrls": ["https://registry.example.com/v1/index/prod", "/registry/index.json"],
22
+ "registryOrigins": ["https://registry.example.com"],
23
+ "registryBeacon": true
24
+ }
25
+ ```
26
+
27
+ The second URL is the static fallback the server republishes, so shells still boot if the server is down. Then use
28
+ `src/registry-sources.ts` in `src/bootstrap.tsx`: replace `registrySources()` with the one from `./registry-sources`.
29
+ `registryBeacon: true` lets browsers report load errors, which is how the server halts a bad percentage rollout.
30
+
31
+ ## Commands
32
+
33
+ ```sh
34
+ export MFEFLOW_REGISTRY_URL=https://registry.example.com
35
+ export MFEFLOW_REGISTRY_TOKEN=... # from `mfeflow-registry token create` on the server
36
+ pnpm server:deploy catalog --env prod # upload the build, ask the server to point prod at it
37
+ pnpm server:status --env prod # what the server serves
38
+ pnpm server:rollback catalog --env prod # back to the previous version
39
+ pnpm mfeflow kill catalog --env prod # out of the index at once
40
+ pnpm mfeflow deploy catalog --env prod --percent 10 --steps 50,100
41
+ pnpm mfeflow rollout status catalog --env prod
42
+ ```
43
+
44
+ `deploy` uploads the build to the artifact host first (`--host`, or `registry.host` in `mfeflow.json`), so the host
45
+ credentials are the same as for the static registry. Then it asks the server, which reads the files back and runs its
46
+ own checks.
47
+
48
+ ## In CI
49
+
50
+ With `ci-github` in the project, `.github/workflows/deploy-server.yml` deploys every remote when `main` changes. Add
51
+ `MFEFLOW_REGISTRY_TOKEN` and the artifact host credentials as secrets of the `registry` environment, and
52
+ `MFEFLOW_REGISTRY_URL` as a variable. Give the token only `read` and `deploy` for the environment it deploys to.
@@ -0,0 +1,12 @@
1
+ import type { RegistrySources } from '@mfeflow/runtime';
2
+
3
+ /**
4
+ * The index URLs for the shell. With `registryBeacon: true` in the runtime config this adds the
5
+ * browser's id to the registry server's URLs (so a percentage rollout reaches the same people every
6
+ * time) and starts the reporter that lets the server halt a bad rollout. Otherwise it is
7
+ * `registrySources()` as it was. Use it in `bootstrap.tsx` in place of `registrySources()`.
8
+ */
9
+ export async function registrySources(): Promise<RegistrySources> {
10
+ const { registrySourcesWithBeacon } = await import('@mfeflow/runtime/beacon');
11
+ return registrySourcesWithBeacon();
12
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "registry-static",
3
+ "version": "0.1.0",
4
+ "description": "Publish remotes to a static registry (a folder, S3, R2 or MinIO) with mfeflow deploy, promote, rollback and status. Adds registry:deploy, registry:rollback and registry:status scripts, a guide, and, when ci-github is present, a workflow that deploys every remote when main changes. Credentials come from CI secrets and the environment only.",
5
+ "requires": ["core"],
6
+ "prompts": [
7
+ {
8
+ "name": "host",
9
+ "type": "string",
10
+ "message": "Registry host (file:<dir> or s3://<bucket>[/prefix]), empty to set it later",
11
+ "default": ""
12
+ },
13
+ {
14
+ "name": "baseUrl",
15
+ "type": "string",
16
+ "message": "URL the host's files are served from (https://cdn.example.com), empty to set it later",
17
+ "default": ""
18
+ }
19
+ ],
20
+ "files": [
21
+ {
22
+ "to": "docs/registry.md",
23
+ "target": "root",
24
+ "from": "files/registry.md.eta",
25
+ "template": true
26
+ },
27
+ {
28
+ "to": ".github/workflows/deploy.yml",
29
+ "target": "root",
30
+ "from": "files/deploy.yml",
31
+ "when": {
32
+ "feature:ci-github": true
33
+ }
34
+ }
35
+ ],
36
+ "dependencies": [
37
+ {
38
+ "name": "mfeflow",
39
+ "range": "latest",
40
+ "target": "root",
41
+ "dev": true
42
+ }
43
+ ],
44
+ "scripts": [
45
+ {
46
+ "name": "registry:deploy",
47
+ "command": "mfeflow deploy",
48
+ "target": "root"
49
+ },
50
+ {
51
+ "name": "registry:rollback",
52
+ "command": "mfeflow rollback",
53
+ "target": "root"
54
+ },
55
+ {
56
+ "name": "registry:status",
57
+ "command": "mfeflow status",
58
+ "target": "root"
59
+ }
60
+ ]
61
+ }
@@ -0,0 +1,61 @@
1
+ name: deploy
2
+
3
+ # Publishes every remote to the static registry when main changes. Needs a host: set the `host` option of
4
+ # the registry-static feature (or `registry.host` in mfeflow.json), and the secrets named below.
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ workflow_dispatch:
9
+ inputs:
10
+ env:
11
+ description: Environment to deploy to
12
+ default: prod
13
+ type: string
14
+
15
+ permissions: {}
16
+
17
+ # Two deploys at once would race on the index; let the first finish.
18
+ concurrency:
19
+ group: deploy
20
+ cancel-in-progress: false
21
+
22
+ jobs:
23
+ deploy:
24
+ runs-on: ubuntu-latest
25
+ timeout-minutes: 20
26
+ # A manual run from another branch would otherwise deploy that branch.
27
+ if: github.ref == 'refs/heads/main'
28
+ environment: registry
29
+ permissions:
30
+ contents: read
31
+ steps:
32
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
33
+ with:
34
+ persist-credentials: false
35
+ - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
36
+ - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
37
+ with:
38
+ node-version: lts/*
39
+ cache: pnpm
40
+ - run: pnpm install --frozen-lockfile
41
+ - run: pnpm build
42
+ - name: Deploy remotes
43
+ env:
44
+ DEPLOY_ENV: ${{ inputs.env || 'prod' }}
45
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
46
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
47
+ MFEFLOW_S3_ENDPOINT: ${{ vars.MFEFLOW_S3_ENDPOINT }}
48
+ MFEFLOW_S3_REGION: ${{ vars.MFEFLOW_S3_REGION }}
49
+ run: |
50
+ set -uo pipefail
51
+ remotes=$(node -p "Object.entries(JSON.parse(require('fs').readFileSync('mfeflow.json', 'utf8')).apps).filter(([, app]) => app.kind === 'remote').map(([name]) => name).join(' ')")
52
+ failed=""
53
+ for remote in $remotes; do
54
+ if ! pnpm mfeflow deploy "$remote" --env "$DEPLOY_ENV" --yes; then
55
+ failed="$failed $remote"
56
+ fi
57
+ done
58
+ if [ -n "$failed" ]; then
59
+ echo "::error::deploy failed for:$failed"
60
+ exit 1
61
+ fi
@@ -0,0 +1,49 @@
1
+ # Registry
2
+
3
+ Remotes are published to a static host: a folder, or an S3-compatible bucket (S3, R2, MinIO). Each version is
4
+ uploaded once and never changes; the environment's `index.json` points at the version people get. A rollback moves
5
+ that pointer back, with no rebuild.
6
+
7
+ ## Configuration
8
+
9
+ - `host`: <%= it.options.host === '' ? 'not set yet' : '`' + it.options.host + '`' %>
10
+ - `baseUrl`: <%= it.options.baseUrl === '' ? 'not set yet' : '`' + it.options.baseUrl + '`' %>
11
+
12
+ These are the options `registry-static` was added with (see `features` in `mfeflow.json`). `registry.host` and
13
+ `registry.baseUrl` in `mfeflow.json` take precedence, and `--host` and `--base-url` on a command beat both.
14
+ `host` is `file:<dir>` or `s3://<bucket>[/prefix]`; `baseUrl` is where the host's files are served from, without a
15
+ trailing slash.
16
+
17
+ ## Commands
18
+
19
+ ```sh
20
+ pnpm registry:deploy catalog --env prod # upload the build, point prod at it
21
+ pnpm registry:status # what each environment serves, and drift
22
+ pnpm registry:rollback catalog --env prod # back to the previous version, no rebuild
23
+ ```
24
+
25
+ `deploy` publishes the version in the remote's `package.json` unless you pass `--version`. Run it with `--dry-run`
26
+ first to see the uploads and the checks.
27
+
28
+ ## Credentials
29
+
30
+ Credentials are read from the environment and never written to a file: `AWS_ACCESS_KEY_ID`,
31
+ `AWS_SECRET_ACCESS_KEY` and, for R2 or MinIO, `MFEFLOW_S3_ENDPOINT` and `MFEFLOW_S3_REGION`. Locally, export them in
32
+ your shell. Do not commit a `.env` file with them.
33
+
34
+ ## In CI
35
+
36
+ With `ci-github` in the project, `.github/workflows/deploy.yml` deploys every remote when `main` changes (and from the
37
+ Actions tab, for another environment). Add `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` as secrets of the
38
+ `registry` environment, and `MFEFLOW_S3_ENDPOINT` and `MFEFLOW_S3_REGION` as its variables. The workflow is only
39
+ generated when `ci-github` is already in the project, so add `ci-github` first.
40
+
41
+ Protect the `registry` environment (Settings, Environments): allow deployments from the `main` branch only, and add
42
+ required reviewers for `prod`. The workflow also refuses to run from any other branch. A remote whose files changed
43
+ without a version bump fails its deploy; the others still deploy, and the run fails at the end with the list.
44
+
45
+ ## How fast a rollback reaches people
46
+
47
+ The index is served with `Cache-Control: max-age=60`, so a browser that already has it keeps it for up to a minute.
48
+ GitHub Pages cannot change its 10 minute cache, so a rollback there takes about 11 minutes. Use a bucket if that is
49
+ too slow.
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "release",
3
+ "version": "0.1.0",
4
+ "description": "Changesets versions each remote on its own. A pull request adds a changeset, `pnpm release:version` bumps the remote's package.json and changelog, and `mfeflow deploy` publishes that same version.",
5
+ "requires": ["core"],
6
+ "files": [
7
+ {
8
+ "to": ".changeset/config.json",
9
+ "target": "root",
10
+ "from": "files/config.json"
11
+ },
12
+ {
13
+ "to": ".changeset/README.md",
14
+ "target": "root",
15
+ "from": "files/changeset-readme.md"
16
+ },
17
+ {
18
+ "to": "docs/releasing.md",
19
+ "target": "root",
20
+ "from": "files/releasing.md"
21
+ }
22
+ ],
23
+ "dependencies": [
24
+ {
25
+ "name": "@changesets/cli",
26
+ "range": "3.0.3",
27
+ "target": "root",
28
+ "dev": true
29
+ }
30
+ ],
31
+ "scripts": [
32
+ {
33
+ "name": "changeset",
34
+ "command": "changeset",
35
+ "target": "root"
36
+ },
37
+ {
38
+ "name": "release:version",
39
+ "command": "changeset version",
40
+ "target": "root"
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,7 @@
1
+ # Changesets
2
+
3
+ A changeset is a short note, in this folder, that says which remote changed and how big the change is. Add one with
4
+ `pnpm changeset` in every pull request that changes a remote. `pnpm release:version` turns the notes into version
5
+ bumps in each remote's `package.json` and into its `CHANGELOG.md`.
6
+
7
+ See `docs/releasing.md`.
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@changesets/config/schema.json",
3
+ "changelog": "@changesets/cli/changelog",
4
+ "commit": false,
5
+ "access": "restricted",
6
+ "baseBranch": "main",
7
+ "updateInternalDependencies": "patch",
8
+ "ignore": [],
9
+ "privatePackages": { "version": true, "tag": false }
10
+ }
@@ -0,0 +1,21 @@
1
+ # Releasing
2
+
3
+ Each remote has its own version, kept by [Changesets](https://github.com/changesets/changesets). `mfeflow deploy`
4
+ publishes the version in the remote's `package.json`, so the version you release and the version you deploy are the
5
+ same number.
6
+
7
+ 1. In a pull request that changes a remote, run `pnpm changeset`. Pick the remote, pick patch, minor or major, and
8
+ write one plain sentence about the change. Commit the file it creates in `.changeset/`.
9
+ 2. When you are ready to release, run `pnpm release:version`. It bumps `package.json` and `CHANGELOG.md` in each remote
10
+ that has a changeset, and deletes the used notes. Commit the result and merge it.
11
+ 3. Deploy: `pnpm mfeflow deploy <remote> --env prod`. With the `registry-static` feature that is
12
+ `pnpm registry:deploy <remote> --env prod`, or the deploy workflow on `main`.
13
+
14
+ The remotes are private packages, so `.changeset/config.json` sets `privatePackages.version` to `true` (they get a
15
+ version) and `tag` to `false` (nothing is published to npm or tagged).
16
+
17
+ The files of a published version never change: deploying the same number with different content is refused. To ship
18
+ a fix, add a changeset and bump.
19
+
20
+ To go back to the previous version, run `mfeflow rollback <remote> --env prod`. It does not touch the version in
21
+ `package.json`, so after a rollback ship the fix as a new patch.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "The shell, the remotes, the registry and the workspace around them.",
5
5
  "files": [
6
6
  {
@@ -67,9 +67,9 @@
67
67
  "template": true
68
68
  },
69
69
  {
70
- "to": "mfe.json",
70
+ "to": "mfeflow.app.json",
71
71
  "target": "shell",
72
- "from": "files/shell/mfe.json.eta",
72
+ "from": "files/shell/mfeflow.app.json.eta",
73
73
  "template": true
74
74
  },
75
75
  {
@@ -121,9 +121,9 @@
121
121
  "template": true
122
122
  },
123
123
  {
124
- "to": "mfe.json",
124
+ "to": "mfeflow.app.json",
125
125
  "target": "remotes",
126
- "from": "files/remote/mfe.json.eta",
126
+ "from": "files/remote/mfeflow.app.json.eta",
127
127
  "template": true
128
128
  },
129
129
  {
@@ -323,5 +323,6 @@
323
323
  "command": "rsbuild build",
324
324
  "target": "remotes"
325
325
  }
326
- ]
326
+ ],
327
+ "migrations": [{ "from": "0.3.0", "to": "0.4.0", "module": "migrations/0.4.0-app-file.mjs" }]
327
328
  }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "$schema": "https://mfeflow.github.io/mfeflow/schema/mfeflow-app.json",
2
3
  "name": "<%= it.app.name %>",
3
4
  "owner": "team-<%= it.app.name %>",
4
5
  "shells": ["shell"],
@@ -36,7 +36,7 @@ pnpm mfeflow add <feature> # see `pnpm mfeflow features`
36
36
 
37
37
  ```sh
38
38
  pnpm doctor # singletons, compatibility, config, drift; `pnpm doctor --fix` repairs the safe ones
39
- pnpm validate # mfeflow.json, every mfe.json and every registry entry
39
+ pnpm validate # mfeflow.json, every mfeflow.app.json and every registry entry
40
40
  pnpm check-compat # each remote's shared ranges against its shell
41
41
  pnpm budget # after `pnpm build`: first-load JS per shell and remote
42
42
  ```
@@ -1,4 +1,5 @@
1
1
  {
2
+ "$schema": "https://mfeflow.github.io/mfeflow/schema/mfeflow-app.json",
2
3
  "name": "<%= it.app.name %>",
3
4
  "owner": "team-<%= it.app.name %>",
4
5
  "shells": [],