@mnci/cli 1.9.0 → 1.10.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.
package/README.md CHANGED
@@ -52,6 +52,11 @@ mnci add go-function-app fn # serverless handler -> apps/
52
52
  mnci add go-lib core # publishable (by git tag) -> packages/
53
53
  mnci add go-internal-lib util # private shared package -> libs/
54
54
 
55
+ # Flutter (@mnci/nx-flutter — one root pubspec.yaml pub workspace, analyze + test)
56
+ mnci add flutter-app hello # Flutter web app -> apps/ (bundle, zipped into the drop)
57
+ mnci add flutter-lib shared # publishable (by git tag) -> packages/
58
+ mnci add flutter-internal-lib core # private shared package -> libs/
59
+
55
60
  mnci upgrade # re-apply the latest overlay (see below)
56
61
  mnci upgrade --agent windows-latest # ...with an explicit override
57
62
  ```
@@ -70,6 +75,43 @@ each a single cross-platform command:
70
75
  | `npm run python:install` | fixed Python toolchain (ruff/pytest/build/twine) + editable-install every Python project — the same two guards CI runs, for local dev |
71
76
  | `prepare` | `husky` (commit-msg lint hook) |
72
77
 
78
+ ## Every `add` also wires local-dev commands
79
+
80
+ Every `mnci add` (and the inline `internal-lib` case) finishes by calling
81
+ `registerProjectCommands` (`commands/add/shared.ts`), which writes up to three
82
+ root `package.json` scripts for the project just added:
83
+
84
+ | Script | Runs | When it's added |
85
+ | -------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
86
+ | `<name>:build` | `nx run <name>:build` | the kind has a `build` target (not every kind does — a private lib with nothing to publish, or a Python function app deployed as source, has none) |
87
+ | `<name>:qa` | `nx run <name>:lint && nx run <name>:test` | always — every kind has both |
88
+ | `<name>:start` | the kind's real local-dev command | only kinds with a genuine dev-server story — never a library |
89
+
90
+ The same three (when present) are appended as VS Code Tasks into the
91
+ workspace's `<workspace-name>.code-workspace` file, so they also show up
92
+ under **Terminal → Run Task** / the Command Palette — `build`/`qa` grouped
93
+ accordingly, `start` marked `isBackground` since it runs a process that
94
+ doesn't exit on its own. Re-running `add` for the same project name
95
+ overwrites its own scripts/tasks rather than duplicating them.
96
+
97
+ `:start` resolves differently per kind — an existing generator target where
98
+ one already exists, a small `nx:run-commands` target mnci writes where none
99
+ did:
100
+
101
+ | Kind(s) | `:start` runs |
102
+ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
103
+ | `react-app`, `node-app` | `nx run <name>:serve` — the generator's own inferred dev-server target |
104
+ | `node-function-app`, `python-function-app` | `nx run <name>:start` → `func start` (Azure Functions Core Tools, install separately — never a prerequisite for `add` itself) |
105
+ | `python-app` | `nx run <name>:start` → `python3 main.py` — mnci writes a runnable `main.py`, since the plugin's own sample module has no entry point |
106
+ | `go-app` | `nx run <name>:start` → `go run .` |
107
+ | `flutter-app` | `nx run <name>:start` → `flutter run -d chrome` (web is the only platform this plugin builds for) |
108
+ | every library, `go-function-app` | no `:start` at all — see below |
109
+
110
+ **`go-function-app` is a known gap, not an oversight**: unlike the Node and
111
+ Python function-app kinds, it writes no `host.json`/custom-handler config, so
112
+ there is nothing for `func start` to attach to. Shipping a `:start` script
113
+ that would just fail felt worse than being upfront that it doesn't exist yet.
114
+
73
115
  ## What `new` actually does
74
116
 
75
117
  1. `npx create-nx-workspace@latest <name> --preset=ts` — npm workspaces +
@@ -85,9 +127,10 @@ each a single cross-platform command:
85
127
  (`azure-pipelines.yml` and/or `.github/workflows/ci.yml`, `--ci`, default
86
128
  `azure`; `github`/`both` also gets `.github/dependabot.yml` — weekly
87
129
  dependency-update PRs), a `<workspace-name>.code-workspace` file (VS Code
88
- workspace configuration with folder structure, ESLint/Prettier settings, and
89
- recommended extensions — open it in VS Code via `File > Open Workspace from
90
- File`), and the curated root scripts.
130
+ workspace configuration with folder structure, ESLint/Prettier settings,
131
+ recommended extensions, and an empty `tasks` array that `mnci add` fills in
132
+ per project — see below — open it in VS Code via `File > Open Workspace
133
+ from File`), and the curated root scripts.
91
134
  4. Installs the chosen **stack** (see below), `husky` + `@commitlint/*` for
92
135
  real, so versions resolve at generation time.
93
136
 
@@ -162,15 +205,24 @@ no programmatic API yet; the two aliases are what make it work.)
162
205
 
163
206
  ## Layout convention = release scoping
164
207
 
165
- | Directory | Contents | Released? |
166
- | ------------------ | ----------------------------------------------------- | -------------------------------------- |
167
- | `apps/` | React / Node / Python apps (plain or Azure Functions) | Never (packed into the drop) |
168
- | `packages/` | Publishable npm libraries (rollup-bundled) | Yes — `nx release`, per-package tags |
169
- | `python-packages/` | Publishable Python packages (hatchling wheels) | Yes — `twine upload` (Azure Artifacts) |
170
- | `libs/` | Internal libraries (TS or Python), never published | Never |
171
-
172
- No custom tags, no stamp file — the directory (and, for npm, the `private` flag)
173
- are the whole model. Publishable Python packages get their own
208
+ | Directory | Contents | Released? |
209
+ | ------------------ | -------------------------------------------------------------- | -------------------------------------- |
210
+ | `apps/` | React / Node / Python / Go / Flutter apps (plain or Functions) | Never (packed into the drop) |
211
+ | `packages/` | Publishable npm libraries, plus Go and Dart packages | Yes — `nx release`, per-package tags |
212
+ | `python-packages/` | Publishable Python packages (hatchling wheels) | Yes — `twine upload` (Azure Artifacts) |
213
+ | `libs/` | Internal libraries (TS, Python, Go or Dart), never published | Never |
214
+
215
+ The directory is very nearly the whole model — one exception, and it is a bug
216
+ fix rather than a nicety. `go-lib` also lives in `packages/`, but a Go package
217
+ has **no per-project manifest** (mnci puts every Go project in one root
218
+ `go.mod`), so Nx's default `versionActions` looks for a `package.json` that is
219
+ not there and aborts _while building the release graph_ — killing `nx release`
220
+ for the whole workspace, not just the Go project. It is therefore excluded with
221
+ `!tag:type:go-lib`, which is also the semantically correct call: one module
222
+ means its packages have no independent versions to bump. A publishable **Dart**
223
+ package in `packages/` needs no such exclusion — `pubspec.yaml` has a real
224
+ `version:` field, and `@mnci/nx-flutter` stamps a `versionActions` override that
225
+ reads it. Publishable Python packages get their own
174
226
  `python-packages/` dir so the npm `nx release` (`packages/*`) is never entangled
175
227
  with Python publishing.
176
228
 
@@ -299,7 +351,7 @@ lint,test,build`. Pushes to `main` then:
299
351
  separate explicit `git push origin --tags` step on this provider. `--ci=azure`
300
352
  and `--ci=both` keep today's behaviour (no GitHub Release, explicit tag
301
353
  push) — GitHub Release creation only turns on when GitHub Actions is the
302
- *only* configured provider, since that's the one case a `GITHUB_TOKEN` is
354
+ _only_ configured provider, since that's the one case a `GITHUB_TOKEN` is
303
355
  guaranteed to exist.
304
356
 
305
357
  **npm auth** is the base64 `PAT`, read the same way on both providers but from
@@ -379,17 +431,31 @@ fix-ci`) — `mnci` does not automate that step.
379
431
  Being upfront about what mnci leans on, so it's a conscious trade-off rather
380
432
  than a surprise:
381
433
 
382
- - **One Nx plugin this project builds and maintains** carries the most weight:
383
- **`@mnci/nx-python-pip`** — a real Nx plugin built into this monorepo itself
384
- (`packages/nx-python-pip`), created because no maintained, Nx-23-compatible
385
- Python plugin supports pip (the previous Python plugin, `@nxlv/python`,
386
- requires `uv`, which the company standardizing on this tool does not use, and
387
- no maintained alternative supports pip). This trades third-party risk for a
388
- different, real one: **this project now owns a second package's maintenance
389
- surface** (generators, executors, its own release cycle) — worth being
390
- explicit about, since it did not exist before. Unlike official `@nx/*`
391
- plugins, if this one needs fixes or updates, this project owns those directly.
392
- That's the cost of having no maintained pip-native Nx plugin in the ecosystem.
434
+ - **Two Nx plugins this project builds and maintains** carry the most weight,
435
+ both for the same reason — the ecosystem has no maintained, Nx-23-compatible
436
+ option:
437
+ - **`@mnci/nx-python-pip`** (`packages/nx-python-pip`): no maintained plugin
438
+ supports pip. The obvious candidate, `@nxlv/python`, requires `uv`, which
439
+ the company standardizing on this tool does not use.
440
+ - **`@mnci/nx-flutter`** (`packages/nx-flutter`): `@nxrocks/nx-flutter`
441
+ cannot even load on Nx 23 — it imports
442
+ `@nx/workspace/src/utilities/fileutils`, removed in 23 — and there is no
443
+ alternative. Its exposure is smaller than the Python plugin's, because
444
+ scaffolding is delegated to the official `flutter create` rather than
445
+ hand-maintained templates; what this project owns is the pub-workspace
446
+ wiring, the targets and the release integration.
447
+
448
+ Both trade third-party risk for a different, real one: **this project owns
449
+ two extra packages' maintenance surface** (generators, executors, their own
450
+ release cycles). Unlike official `@nx/*` plugins, if either needs fixes, this
451
+ project owns them directly. That is the cost of the gaps in the ecosystem.
452
+
453
+ - **`@nx-go/nx-go` is a third-party plugin on a declared-incompatible range.**
454
+ It declares `@nx/devkit ">= 20 < 23"` while this workspace runs Nx 23. That
455
+ range is a plain dependency rather than a peer, so npm nests its own devkit
456
+ copy and everything works — validated empirically against a real Nx 23.1.0
457
+ workspace (generators, build, test, lint). It is still a version trap worth
458
+ re-checking on Nx upgrades.
393
459
  - **The TS7 dual-compiler aliases pin a very new, fast-moving dependency.**
394
460
  TypeScript 7's native compiler is recent; `TS_COMPILER_DEPENDENCIES` pins
395
461
  `npm:typescript@^7.0.2` / `npm:@typescript/typescript6@^6.0.2` specifically
@@ -401,6 +467,15 @@ than a surprise:
401
467
  ## Known gaps (accepted for the experiment)
402
468
 
403
469
  - No `doctor`/`resurrect`/`spell` — out of scope until the model is proven.
470
+ - **Flutter apps build for web only.** Android would require the Android SDK and
471
+ NDK on every build agent; iOS is impossible on a Linux agent regardless. Add
472
+ other platforms per-app with `flutter create --platforms=...` — the generated
473
+ `build` target only knows about web.
474
+ - **Go has no e2e coverage.** The `go-*` kinds have real unit tests and real CI
475
+ wiring, but the e2e suite never drives them, because it needs Go on the
476
+ machine. Flutter took the opposite approach — its e2e section runs when the
477
+ SDK is present and reports **SKIPPED** (loudly) when it is not — so the same
478
+ pattern could be applied to Go, but has not been.
404
479
  - Azure Functions Core Tools is only needed for **local** `func start` — never
405
480
  for `mnci add node-function-app`/`python-function-app` generation, since
406
481
  neither shells out to the `func` CLI.
@@ -681,3 +756,69 @@ build` links statically, so the binary in the drop already contains
681
756
  then added to `PATH` for later steps. All three skip cleanly when the
682
757
  workspace has no root `go.mod`, and the linter install also skips when the
683
758
  agent already provides it.
759
+
760
+ ## Flutter (`@mnci/nx-flutter` — one root `pubspec.yaml` pub workspace)
761
+
762
+ Requires the **Flutter SDK** (3.27+, for Dart 3.6+ pub workspaces) on the
763
+ machine; `mnci add flutter-*` fails fast with an install link when `flutter` is
764
+ not on the `PATH`. Unlike Python and Go, the SDK is **not** present on hosted
765
+ build agents, so the generated pipeline installs it itself (see below).
766
+
767
+ | Kind | Location | Build / deploy |
768
+ | ---------------------- | ----------------- | ----------------------------------------------------------------------------------------------------- |
769
+ | `flutter-app` | `apps/<name>` | `flutter build web` bundle into `dist/apps/<name>/`, zipped into `dist/drop/flutter-app-<name>.zip` |
770
+ | `flutter-lib` | `packages/<name>` | publishable **by git tag** — see below; analyze + test targets only |
771
+ | `flutter-internal-lib` | `libs/<name>` | private shared code, analyze + test only — a Dart package is compiled into whatever app depends on it |
772
+
773
+ - **Dependencies are central, via a Dart pub workspace.** One root
774
+ `pubspec.yaml` lists every project under `workspace:`, and each project
775
+ carries `resolution: workspace`. A single `flutter pub get` at the root then
776
+ resolves the whole graph into **one** `pubspec.lock` and **one**
777
+ `.dart_tool/package_config.json` — pub actively deletes any per-package
778
+ copies. This is the Flutter half of the same root-manifest model as the root
779
+ `package.json` for TS, `requirements-dev.txt` for Python and `go.mod` for Go.
780
+ - **An internal library is consumed with a plain version constraint — no
781
+ `path:`.** `dependencies: { core: ^0.0.1 }` resolves to the local package
782
+ because it is a workspace member. That is also why Flutter needs **no
783
+ vendoring step**: contrast `mnci add python-vendor`, which exists only
784
+ because pip cannot bundle an unpublished sibling into a wheel. Flutter is in
785
+ the Go camp here — nothing to weave in at build time.
786
+ - **Lint configuration is central too.** The workspace root owns one
787
+ `analysis_options.yaml` (including `package:flutter_lints/flutter.yaml`), and
788
+ each project's own file is a one-line relative `include:` of it, so a rule
789
+ change lands in one place.
790
+ - **`flutter analyze --fatal-infos`, pinned explicitly.** `flutter analyze`
791
+ already defaults `--fatal-infos` on — verified against 3.44.8, where
792
+ `--no-fatal-infos` turns a failing lint run green. It is passed anyway
793
+ because that default is the only thing making this a real gate: nearly every
794
+ `flutter_lints` rule reports at _info_ severity. Worth knowing that plain
795
+ `dart analyze` defaults the opposite way (it fails on errors and warnings but
796
+ not infos), so swapping the command without carrying the flag across would
797
+ silently stop enforcing anything.
798
+ - **Publishing a `flutter-lib` is a git tag, not a registry upload.** Azure
799
+ Artifacts has no pub/Dart feed type, so a private pub registry is not
800
+ available on this stack, and these packages are deliberately not pushed to
801
+ pub.dev. `nx release` versions and tags them; no `nx-release-publish` target
802
+ is written.
803
+ - **The publishable lib carries a `versionActions` override, and it is
804
+ load-bearing.** Nx's default reads a `package.json`, which a Dart package
805
+ does not have. Without the override `nx release` aborts while building the
806
+ release graph — taking down the release of **every** project in the
807
+ workspace, not just the Dart one. The plugin's `library` generator stamps
808
+ `@mnci/nx-flutter/release/version-actions` on, which reads and writes
809
+ `pubspec.yaml`'s `version:`.
810
+ - **Apps build for web only.** Web needs nothing beyond the Flutter SDK,
811
+ whereas an Android build would drag the whole Android SDK and NDK onto every
812
+ build agent. Other platforms can be added per-app later with
813
+ `flutter create --platforms=...`.
814
+ - **CI** installs the SDK by shallow `git clone` at a pinned tag — Flutter's own
815
+ documented install method, and the only one uniform across agents (the
816
+ release archives differ by platform). It is cloned **outside** the workspace,
817
+ under the agent's home directory: the SDK ships dozens of its own
818
+ `pubspec.yaml` files, which inside the tree would pollute pub's resolution and
819
+ give Nx thousands of extra files to glob. The SDK version is **pinned**
820
+ (unlike `golangci-lint`'s `@latest`) because it determines the Dart version,
821
+ and pub workspaces need Dart 3.6+. Three guarded steps precede the build —
822
+ install, add to `PATH`, and one root `flutter pub get` — and all three skip
823
+ cleanly when the workspace has no root `pubspec.yaml`; the install also skips
824
+ when an SDK is already available.