@mnci/cli 4.15.0 → 4.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.
package/README.md CHANGED
@@ -65,7 +65,7 @@ named here because the next reader deserves to know before opening them:
65
65
 
66
66
  | Path | Rule waived | Why, and removal condition |
67
67
  |---|---|---|
68
- | `workspace-overlay/overlay.use-case.ts` | one responsibility per file | ~3.5k lines covering CI YAML for two providers, `.npmrc`, `nuget.config`, the VS Code workspace, release config and the CI guard scripts. Splitting it is a decomposition, not a move, so it was deliberately kept out of the change that created these slices. **Temporary** — removed when that decomposition lands. |
68
+ | `workspace-overlay/overlay.use-case.ts` | one responsibility per file | ~5k lines covering CI YAML for two providers (including the native-app job), `.npmrc`, `nuget.config`, the VS Code workspace, release config and the CI guard scripts. Splitting it is a decomposition, not a move, so it was deliberately kept out of the change that created these slices. **Temporary** — removed when that decomposition lands. |
69
69
  | `rollup-library/repair-rollup-config.use-case.spec.ts` | a test takes its subject's basename | It also holds the `withUpgradedDeclarationSpecifierPlugin` describe, whose subject is `rollup-config.algorithm.ts`. That transform shares three fixtures with the repairs that apply it (`OLD_DTS_PLUGIN_CONFIG`, `EXTENSION_ONLY_DTS_PLUGIN_CONFIG`, `loadWriteBundle`), and duplicating them across two spec files is the worse trade. **Permanent** unless those fixtures stop being shared. |
70
70
 
71
71
  `rollup-library/` exists because of the own-the-concept rule. Its contents used
@@ -75,7 +75,7 @@ helpers — they have no interest in adding a project. The concept now sits in i
75
75
  own slice that depends on nothing above it (`file-system` alone), and all three
76
76
  consumers point at it.
77
77
 
78
- ## Commands (deliberately just six)
78
+ ## Commands (deliberately just seven)
79
79
 
80
80
  ```sh
81
81
  mnci new my-repo # create a monorepo (prompts scope + registry)
@@ -104,6 +104,7 @@ mnci add python-vendor shared --lib core # wire core's module into shared's bui
104
104
  mnci add go-app api # executable -> apps/ (binary, zipped into the drop)
105
105
  mnci add go-app cli --release # ...released: tag + per-platform zips on the GitHub Release
106
106
  mnci add go-app tray --cgo # needs a C toolchain: built on a runner of each OS
107
+ mnci add go-app site --web web # embeds and serves the React app apps/web in one binary
107
108
  mnci add go-function-app fn # serverless handler -> apps/
108
109
  mnci add go-lib core # publishable (by git tag) -> packages/
109
110
  mnci add go-internal-lib util # private shared package -> libs/
@@ -123,6 +124,8 @@ mnci upgrade --agent windows-latest # ...with an explicit override
123
124
 
124
125
  mnci doctor # check this workspace's invariants (read-only)
125
126
 
127
+ mnci ci verify # the pipeline's verify phase, run here: see `mnci ci` below
128
+
126
129
  mnci sync # converge dependency ranges + nx sync (TS project refs)
127
130
  mnci sync --check # ...report and exit non-zero, writing nothing
128
131
 
@@ -320,6 +323,35 @@ TTY, so a piped or CI run reports instead of hanging on a prompt), `-y/--yes`
320
323
  (take everything), `--ecosystem`, and `--no-install` (edit the manifests but skip
321
324
  the reinstall).
322
325
 
326
+ ## `mnci ci`: the pipeline's phases, as a command
327
+
328
+ `mnci ci verify` runs the pipeline's verify phase on your machine: `nx sync:check`, then
329
+ every project (or, when a pull request's target branch is set, only the projects affected
330
+ since the merge-base with it) through `lint`, `typecheck`, `test` and `build`. It exits with
331
+ the failing command's own status, so it can stand in for the pipeline's step.
332
+
333
+ It is not a shortcut for those Nx targets, and the CLI still has no wrapper for `nx test`.
334
+ It is the pipeline's own logic, which until now lived as a `node -e` one-liner inside the
335
+ generated YAML, ported to tested code, so a laptop and a pipeline run the same thing. The
336
+ design (one `npx mnci ci` call in the pipeline, with room for your own steps around it) is
337
+ tracked in #269, and this is its first phase: **the generated pipelines do not call it yet**
338
+ and are unchanged, so nothing about an existing workspace changes. Phases follow as their
339
+ guards are ported.
340
+
341
+ - **Same scoping as the guard.** No pull-request target means every project, so a push to
342
+ main verifies in full. A pull request verifies what is affected since the merge-base with
343
+ `origin/<target>`, fetching the target once if that ref is missing, and falls back to every
344
+ project when no merge-base can be found, since a run that verifies too little still reports
345
+ green. The target comes from `GITHUB_BASE_REF` or `SYSTEM_PULLREQUEST_TARGETBRANCH`, so to
346
+ reproduce a pull request run locally, set one: `GITHUB_BASE_REF=main mnci ci verify`.
347
+ - **Native (cgo) apps are left out**, as in the pipeline, since one agent cannot build them.
348
+ - **Log groups.** Under GitHub Actions and Azure Pipelines each part is a collapsible group
349
+ (`::group::`, `##[group]`); locally it is a plain heading.
350
+ - **Checked against the guard it replaces.** An integration spec runs the inline guard and
351
+ this command against the same real git repository, with a recording stand-in for `npx`,
352
+ across a push, a pull request, Azure's `refs/heads/` form, a missing `origin/<target>` and
353
+ an unresolvable one, and requires the same Nx commands in each.
354
+
323
355
  ## `mnci doctor`: checking the invariants actually hold
324
356
 
325
357
  Read-only — it never edits the workspace. Every failing finding names the command
@@ -689,6 +721,64 @@ the generated tree, so it runs afterwards, but still before the first write:
689
721
  a refusal leaves the target byte-identical to how it was found, and the staging
690
722
  copy is discarded.
691
723
 
724
+ ### Adopting a flat Go module
725
+
726
+ A repository with its own `go.mod`, a root `main.go` and `internal/` packages is the
727
+ other common starting point, and `--into` takes it as it is. Measured on a clone of a
728
+ real one (twelve `internal/` packages, its own `release.yml`):
729
+
730
+ - **`go.mod` and `go.sum` are never touched.** `add go-*` skips the module bootstrap
731
+ when a `go.mod` exists, so the module path, the `require` lines and the Go version
732
+ survive byte for byte, and no `go.work` is created.
733
+ - **The Go plugin is registered anyway.** The bootstrap that registers it in `nx.json`
734
+ is the one that is skipped, which used to leave the plugin installed and unlisted:
735
+ every target worked, and Nx had **no Go project graph**, so `nx affected` skipped an
736
+ app that imports a changed library, silently. `add go-*` now registers it, `mnci
737
+ upgrade` repairs a workspace that adopted before, and `mnci doctor` fails while Go
738
+ projects exist and it is missing.
739
+ - **Your own workflow coexists.** A release workflow that fires on `v*` tags and the
740
+ generated `ci.yml`, which fires on pushes and pull requests to `main`, do not overlap.
741
+ mnci tags a released app `<name>@<version>`, so once `--release` and its GitHub
742
+ Release assets do what yours did, delete the old workflow.
743
+ - **The format step can fail on a file you already had**, and says so without failing
744
+ the run: a UTF-16 `.json` at the root made `eslint .` report a parse error. The root
745
+ lint covers root-level files, so a repository's existing JSON, Markdown and YAML are
746
+ now inside it; fix or delete what it names.
747
+
748
+ Landing the code is mechanical, so it is a recipe, not a command (`git mv` and one
749
+ import rewrite are the whole job, and a command would have to guess your layout). With
750
+ `youtube-downloader` as the module path, `mvd-cli` as the app and `mvd-core` as the
751
+ library:
752
+
753
+ ```sh
754
+ mnci add go-app mvd-cli
755
+ mnci add go-internal-lib mvd-core
756
+
757
+ # The generated starters are placeholders: drop them.
758
+ rm apps/mvd-cli/main.go apps/mvd-cli/main_test.go
759
+ rm -r libs/mvd-core/mvdcore
760
+
761
+ # git mv, not mv, so `git log --follow` reaches the history before the move.
762
+ git mv main.go apps/mvd-cli/main.go
763
+ for slice in internal/*/; do git mv "$slice" "libs/mvd-core/$(basename "$slice")"; done
764
+
765
+ # Rewrite the import paths (macOS: sed -i '').
766
+ grep -rl 'youtube-downloader/internal/' --include=*.go apps libs \
767
+ | xargs sed -i 's#youtube-downloader/internal/#youtube-downloader/libs/mvd-core/#g'
768
+
769
+ npx nx run-many -t test,build --projects=mvd-cli,mvd-core
770
+ ```
771
+
772
+ Use `nx` and not a bare `go test ./...` from the root: `./...` also walks
773
+ `node_modules`, where npm packages that contain Go code sit (one did here). On the
774
+ repository measured, this left `go build` clean, every test passing, 62 renames
775
+ detected by git, and the app depending on the library in the project graph. The e2e
776
+ (`go adoption`) runs this recipe on a fixture and asserts each of those.
777
+
778
+ Not measured here: whether `golangci-lint`, which is what each Go project's `lint`
779
+ runs, passes on a codebase that has never been linted. Run `nx run <lib>:lint` before
780
+ relying on it, and expect to fix or silence what it reports.
781
+
692
782
  ## Layout convention = release scoping
693
783
 
694
784
  | Directory | Contents | Released? |
@@ -1266,6 +1356,11 @@ get `react-app-<name>-dev` / `-uat` / `-prod`, and the classic release pipeline
1266
1356
  deploys each environment from its own artifact + tag. Need different
1267
1357
  environments? Edit `REACT_ENVIRONMENTS` in the generator.
1268
1358
 
1359
+ **A React app can also ship inside a Go binary**, for an app that serves its own UI
1360
+ (`mnci add go-app <name> --web <react-app>`): the default `build` is what gets
1361
+ embedded, not the per-environment ones, so the app should call its API on the same
1362
+ origin (`/api/...`). See _Serving a React app from a Go app_ in the Go section.
1363
+
1269
1364
  ## Python (`@mnci/nx-python-pip` — pip + Ruff + pytest + PyPA `build`/`twine`, no uv)
1270
1365
 
1271
1366
  Python is the first non-JS language, and follows the same philosophy as every
@@ -1485,6 +1580,41 @@ pipeline installs `golangci-lint` itself (see below).
1485
1580
  OS's zip to the same GitHub Release, so one release collects a zip from every
1486
1581
  runner. Azure Pipelines has no GitHub Release to attach to and stops at the
1487
1582
  artifact.
1583
+ - **Serving a React app from a Go app (`mnci add go-app <name> --web <react-app>`).**
1584
+ For an app that runs a local server and is used in a browser, shipped as one
1585
+ self-contained binary. `//go:embed` cannot reach outside its package directory,
1586
+ and a React app builds into its own `apps/<react-app>/dist`, so the Go app needs
1587
+ wiring mnci writes for you (the React app has to exist first):
1588
+ - **`stage-web`** copies that `dist` into `apps/<name>/web/`, after the React
1589
+ app's `build`, and the directory is git-ignored (an `apps/<name>/.gitignore`).
1590
+ Its output is declared and its inputs are the React build's outputs, so Nx
1591
+ caches it and a change to the React code reaches the binary.
1592
+ - **Every target that compiles Go depends on it**: `build`, `test`, `lint`,
1593
+ `start`, `build-all` and `build-native` (`package*` reach it through those).
1594
+ `//go:embed` fails at compile time without its files, so on a fresh checkout
1595
+ `go vet`, `go test` and `golangci-lint` fail as well as `go build`. A committed
1596
+ placeholder was rejected: staging replaces the directory, so git would show it
1597
+ modified for ever. The cost is that a Go-only change waits for a React build
1598
+ on a cold cache, and your editor shows the embed error until the first
1599
+ `nx run <name>:stage-web`.
1600
+ - **The project graph knows**: `implicitDependencies` makes the React app a
1601
+ dependency, so changing it marks the Go app affected, while changing the Go
1602
+ app does not rebuild the React app.
1603
+ - **The sources**: `main.go` becomes a small server (`ADDR`, default
1604
+ `127.0.0.1:8080`) that logs the stamped `version`, `web.go` embeds the staged
1605
+ files and serves them (an unknown path falls back to `index.html`, so a
1606
+ client-side route survives a reload), and `main_test.go` checks the embed holds
1607
+ a page. They replace the generated hello world.
1608
+ - **`nx run <name>:dev`** starts the Vite dev server and the Go server together.
1609
+ `add` adds `server.proxy: { '/api': 'http://127.0.0.1:8080' }` to the React
1610
+ app's Vite config (and tells you what to add if it cannot find a `server`
1611
+ block), so the browser talks to Vite and `/api` reaches Go.
1612
+ - **Releasing one.** `package-all` stages once and builds six binaries from the
1613
+ same copy. A native (`--cgo`) app is built on a runner per OS, and **each leg
1614
+ rebuilds the React app**: sharing one frontend build between runners is not
1615
+ done yet.
1616
+ - Types shared between Go and TypeScript (OpenAPI, generated types) are out of
1617
+ scope.
1488
1618
  - **Releasing a Go app is an opt-in: `mnci add go-app <name> --release`.** The
1489
1619
  app is tagged `release:go`, which `release.projects` selects by tag (as it
1490
1620
  does for a VS Code extension, so `mnci upgrade` keeps it). Without the flag