@mnci/cli 1.8.0 → 1.9.1
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 +140 -23
- package/dist/cli.js +174 -35
- package/dist/cli.js.map +1 -1
- package/package.json +7 -4
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
|
```
|
|
@@ -162,15 +167,24 @@ no programmatic API yet; the two aliases are what make it work.)
|
|
|
162
167
|
|
|
163
168
|
## Layout convention = release scoping
|
|
164
169
|
|
|
165
|
-
| Directory | Contents
|
|
166
|
-
| ------------------ |
|
|
167
|
-
| `apps/` | React / Node / Python apps (plain or
|
|
168
|
-
| `packages/` | Publishable npm libraries
|
|
169
|
-
| `python-packages/` | Publishable Python packages (hatchling wheels)
|
|
170
|
-
| `libs/` | Internal libraries (TS or
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
| Directory | Contents | Released? |
|
|
171
|
+
| ------------------ | -------------------------------------------------------------- | -------------------------------------- |
|
|
172
|
+
| `apps/` | React / Node / Python / Go / Flutter apps (plain or Functions) | Never (packed into the drop) |
|
|
173
|
+
| `packages/` | Publishable npm libraries, plus Go and Dart packages | Yes — `nx release`, per-package tags |
|
|
174
|
+
| `python-packages/` | Publishable Python packages (hatchling wheels) | Yes — `twine upload` (Azure Artifacts) |
|
|
175
|
+
| `libs/` | Internal libraries (TS, Python, Go or Dart), never published | Never |
|
|
176
|
+
|
|
177
|
+
The directory is very nearly the whole model — one exception, and it is a bug
|
|
178
|
+
fix rather than a nicety. `go-lib` also lives in `packages/`, but a Go package
|
|
179
|
+
has **no per-project manifest** (mnci puts every Go project in one root
|
|
180
|
+
`go.mod`), so Nx's default `versionActions` looks for a `package.json` that is
|
|
181
|
+
not there and aborts _while building the release graph_ — killing `nx release`
|
|
182
|
+
for the whole workspace, not just the Go project. It is therefore excluded with
|
|
183
|
+
`!tag:type:go-lib`, which is also the semantically correct call: one module
|
|
184
|
+
means its packages have no independent versions to bump. A publishable **Dart**
|
|
185
|
+
package in `packages/` needs no such exclusion — `pubspec.yaml` has a real
|
|
186
|
+
`version:` field, and `@mnci/nx-flutter` stamps a `versionActions` override that
|
|
187
|
+
reads it. Publishable Python packages get their own
|
|
174
188
|
`python-packages/` dir so the npm `nx release` (`packages/*`) is never entangled
|
|
175
189
|
with Python publishing.
|
|
176
190
|
|
|
@@ -291,7 +305,16 @@ lint,test,build`. Pushes to `main` then:
|
|
|
291
305
|
the raw token twine needs for the Python publish. Skipped cleanly when there
|
|
292
306
|
is nothing to release. A guarded step installs the fixed Python toolchain
|
|
293
307
|
(`ruff`/`pytest`/`build`/`twine`/`pip-audit`) before any Python target runs,
|
|
294
|
-
skipped cleanly on a workspace with no Python projects.
|
|
308
|
+
skipped cleanly on a workspace with no Python projects. On a `--ci=github`
|
|
309
|
+
workspace this same step also creates a **GitHub Release per project**, with
|
|
310
|
+
a changelog Nx generates from conventional commits — `nx release` pushes the
|
|
311
|
+
tag itself here (needs `GITHUB_TOKEN`, which GitHub Actions provides for
|
|
312
|
+
free under the workflow's own `contents: write` permission), so there's no
|
|
313
|
+
separate explicit `git push origin --tags` step on this provider. `--ci=azure`
|
|
314
|
+
and `--ci=both` keep today's behaviour (no GitHub Release, explicit tag
|
|
315
|
+
push) — GitHub Release creation only turns on when GitHub Actions is the
|
|
316
|
+
_only_ configured provider, since that's the one case a `GITHUB_TOKEN` is
|
|
317
|
+
guaranteed to exist.
|
|
295
318
|
|
|
296
319
|
**npm auth** is the base64 `PAT`, read the same way on both providers but from
|
|
297
320
|
a different place: on Azure Pipelines, a **variable group**
|
|
@@ -370,17 +393,31 @@ fix-ci`) — `mnci` does not automate that step.
|
|
|
370
393
|
Being upfront about what mnci leans on, so it's a conscious trade-off rather
|
|
371
394
|
than a surprise:
|
|
372
395
|
|
|
373
|
-
- **
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
396
|
+
- **Two Nx plugins this project builds and maintains** carry the most weight,
|
|
397
|
+
both for the same reason — the ecosystem has no maintained, Nx-23-compatible
|
|
398
|
+
option:
|
|
399
|
+
- **`@mnci/nx-python-pip`** (`packages/nx-python-pip`): no maintained plugin
|
|
400
|
+
supports pip. The obvious candidate, `@nxlv/python`, requires `uv`, which
|
|
401
|
+
the company standardizing on this tool does not use.
|
|
402
|
+
- **`@mnci/nx-flutter`** (`packages/nx-flutter`): `@nxrocks/nx-flutter`
|
|
403
|
+
cannot even load on Nx 23 — it imports
|
|
404
|
+
`@nx/workspace/src/utilities/fileutils`, removed in 23 — and there is no
|
|
405
|
+
alternative. Its exposure is smaller than the Python plugin's, because
|
|
406
|
+
scaffolding is delegated to the official `flutter create` rather than
|
|
407
|
+
hand-maintained templates; what this project owns is the pub-workspace
|
|
408
|
+
wiring, the targets and the release integration.
|
|
409
|
+
|
|
410
|
+
Both trade third-party risk for a different, real one: **this project owns
|
|
411
|
+
two extra packages' maintenance surface** (generators, executors, their own
|
|
412
|
+
release cycles). Unlike official `@nx/*` plugins, if either needs fixes, this
|
|
413
|
+
project owns them directly. That is the cost of the gaps in the ecosystem.
|
|
414
|
+
|
|
415
|
+
- **`@nx-go/nx-go` is a third-party plugin on a declared-incompatible range.**
|
|
416
|
+
It declares `@nx/devkit ">= 20 < 23"` while this workspace runs Nx 23. That
|
|
417
|
+
range is a plain dependency rather than a peer, so npm nests its own devkit
|
|
418
|
+
copy and everything works — validated empirically against a real Nx 23.1.0
|
|
419
|
+
workspace (generators, build, test, lint). It is still a version trap worth
|
|
420
|
+
re-checking on Nx upgrades.
|
|
384
421
|
- **The TS7 dual-compiler aliases pin a very new, fast-moving dependency.**
|
|
385
422
|
TypeScript 7's native compiler is recent; `TS_COMPILER_DEPENDENCIES` pins
|
|
386
423
|
`npm:typescript@^7.0.2` / `npm:@typescript/typescript6@^6.0.2` specifically
|
|
@@ -392,6 +429,15 @@ than a surprise:
|
|
|
392
429
|
## Known gaps (accepted for the experiment)
|
|
393
430
|
|
|
394
431
|
- No `doctor`/`resurrect`/`spell` — out of scope until the model is proven.
|
|
432
|
+
- **Flutter apps build for web only.** Android would require the Android SDK and
|
|
433
|
+
NDK on every build agent; iOS is impossible on a Linux agent regardless. Add
|
|
434
|
+
other platforms per-app with `flutter create --platforms=...` — the generated
|
|
435
|
+
`build` target only knows about web.
|
|
436
|
+
- **Go has no e2e coverage.** The `go-*` kinds have real unit tests and real CI
|
|
437
|
+
wiring, but the e2e suite never drives them, because it needs Go on the
|
|
438
|
+
machine. Flutter took the opposite approach — its e2e section runs when the
|
|
439
|
+
SDK is present and reports **SKIPPED** (loudly) when it is not — so the same
|
|
440
|
+
pattern could be applied to Go, but has not been.
|
|
395
441
|
- Azure Functions Core Tools is only needed for **local** `func start` — never
|
|
396
442
|
for `mnci add node-function-app`/`python-function-app` generation, since
|
|
397
443
|
neither shells out to the `func` CLI.
|
|
@@ -400,8 +446,13 @@ than a surprise:
|
|
|
400
446
|
inside the published `drop` artifact is the deploy input. Deploying it means
|
|
401
447
|
Azure's Oryx build installing real dependencies (`npm install`/`pip install`)
|
|
402
448
|
from the zipped manifest — no `node_modules`/venv is bundled.
|
|
403
|
-
- Changelog files are off (unpushable under the tag-only
|
|
404
|
-
|
|
449
|
+
- Changelog **files** are off everywhere (unpushable under the tag-only
|
|
450
|
+
model — `git.commit` stays `false`, so a written `CHANGELOG.md` would just
|
|
451
|
+
be discarded at the end of every CI run). On a `--ci=github` workspace (and
|
|
452
|
+
only that one — see below) each release still gets a real changelog: Nx
|
|
453
|
+
generates it from conventional commits and posts it straight to a GitHub
|
|
454
|
+
Release, with no file ever touched. `--ci=azure` and `--ci=both` fall back
|
|
455
|
+
to the git tag history as the changelog, same as before.
|
|
405
456
|
- **No lock file for Python** — plain pip has none, matching the company
|
|
406
457
|
standard this migration was for. A published wheel's `Requires-Dist` mirrors
|
|
407
458
|
whatever specifier the `pyproject.toml` declares (e.g. `tomli>=2.0.0`)
|
|
@@ -667,3 +718,69 @@ build` links statically, so the binary in the drop already contains
|
|
|
667
718
|
then added to `PATH` for later steps. All three skip cleanly when the
|
|
668
719
|
workspace has no root `go.mod`, and the linter install also skips when the
|
|
669
720
|
agent already provides it.
|
|
721
|
+
|
|
722
|
+
## Flutter (`@mnci/nx-flutter` — one root `pubspec.yaml` pub workspace)
|
|
723
|
+
|
|
724
|
+
Requires the **Flutter SDK** (3.27+, for Dart 3.6+ pub workspaces) on the
|
|
725
|
+
machine; `mnci add flutter-*` fails fast with an install link when `flutter` is
|
|
726
|
+
not on the `PATH`. Unlike Python and Go, the SDK is **not** present on hosted
|
|
727
|
+
build agents, so the generated pipeline installs it itself (see below).
|
|
728
|
+
|
|
729
|
+
| Kind | Location | Build / deploy |
|
|
730
|
+
| ---------------------- | ----------------- | ----------------------------------------------------------------------------------------------------- |
|
|
731
|
+
| `flutter-app` | `apps/<name>` | `flutter build web` bundle into `dist/apps/<name>/`, zipped into `dist/drop/flutter-app-<name>.zip` |
|
|
732
|
+
| `flutter-lib` | `packages/<name>` | publishable **by git tag** — see below; analyze + test targets only |
|
|
733
|
+
| `flutter-internal-lib` | `libs/<name>` | private shared code, analyze + test only — a Dart package is compiled into whatever app depends on it |
|
|
734
|
+
|
|
735
|
+
- **Dependencies are central, via a Dart pub workspace.** One root
|
|
736
|
+
`pubspec.yaml` lists every project under `workspace:`, and each project
|
|
737
|
+
carries `resolution: workspace`. A single `flutter pub get` at the root then
|
|
738
|
+
resolves the whole graph into **one** `pubspec.lock` and **one**
|
|
739
|
+
`.dart_tool/package_config.json` — pub actively deletes any per-package
|
|
740
|
+
copies. This is the Flutter half of the same root-manifest model as the root
|
|
741
|
+
`package.json` for TS, `requirements-dev.txt` for Python and `go.mod` for Go.
|
|
742
|
+
- **An internal library is consumed with a plain version constraint — no
|
|
743
|
+
`path:`.** `dependencies: { core: ^0.0.1 }` resolves to the local package
|
|
744
|
+
because it is a workspace member. That is also why Flutter needs **no
|
|
745
|
+
vendoring step**: contrast `mnci add python-vendor`, which exists only
|
|
746
|
+
because pip cannot bundle an unpublished sibling into a wheel. Flutter is in
|
|
747
|
+
the Go camp here — nothing to weave in at build time.
|
|
748
|
+
- **Lint configuration is central too.** The workspace root owns one
|
|
749
|
+
`analysis_options.yaml` (including `package:flutter_lints/flutter.yaml`), and
|
|
750
|
+
each project's own file is a one-line relative `include:` of it, so a rule
|
|
751
|
+
change lands in one place.
|
|
752
|
+
- **`flutter analyze --fatal-infos`, pinned explicitly.** `flutter analyze`
|
|
753
|
+
already defaults `--fatal-infos` on — verified against 3.44.8, where
|
|
754
|
+
`--no-fatal-infos` turns a failing lint run green. It is passed anyway
|
|
755
|
+
because that default is the only thing making this a real gate: nearly every
|
|
756
|
+
`flutter_lints` rule reports at _info_ severity. Worth knowing that plain
|
|
757
|
+
`dart analyze` defaults the opposite way (it fails on errors and warnings but
|
|
758
|
+
not infos), so swapping the command without carrying the flag across would
|
|
759
|
+
silently stop enforcing anything.
|
|
760
|
+
- **Publishing a `flutter-lib` is a git tag, not a registry upload.** Azure
|
|
761
|
+
Artifacts has no pub/Dart feed type, so a private pub registry is not
|
|
762
|
+
available on this stack, and these packages are deliberately not pushed to
|
|
763
|
+
pub.dev. `nx release` versions and tags them; no `nx-release-publish` target
|
|
764
|
+
is written.
|
|
765
|
+
- **The publishable lib carries a `versionActions` override, and it is
|
|
766
|
+
load-bearing.** Nx's default reads a `package.json`, which a Dart package
|
|
767
|
+
does not have. Without the override `nx release` aborts while building the
|
|
768
|
+
release graph — taking down the release of **every** project in the
|
|
769
|
+
workspace, not just the Dart one. The plugin's `library` generator stamps
|
|
770
|
+
`@mnci/nx-flutter/release/version-actions` on, which reads and writes
|
|
771
|
+
`pubspec.yaml`'s `version:`.
|
|
772
|
+
- **Apps build for web only.** Web needs nothing beyond the Flutter SDK,
|
|
773
|
+
whereas an Android build would drag the whole Android SDK and NDK onto every
|
|
774
|
+
build agent. Other platforms can be added per-app later with
|
|
775
|
+
`flutter create --platforms=...`.
|
|
776
|
+
- **CI** installs the SDK by shallow `git clone` at a pinned tag — Flutter's own
|
|
777
|
+
documented install method, and the only one uniform across agents (the
|
|
778
|
+
release archives differ by platform). It is cloned **outside** the workspace,
|
|
779
|
+
under the agent's home directory: the SDK ships dozens of its own
|
|
780
|
+
`pubspec.yaml` files, which inside the tree would pollute pub's resolution and
|
|
781
|
+
give Nx thousands of extra files to glob. The SDK version is **pinned**
|
|
782
|
+
(unlike `golangci-lint`'s `@latest`) because it determines the Dart version,
|
|
783
|
+
and pub workspaces need Dart 3.6+. Three guarded steps precede the build —
|
|
784
|
+
install, add to `PATH`, and one root `flutter pub get` — and all three skip
|
|
785
|
+
cleanly when the workspace has no root `pubspec.yaml`; the install also skips
|
|
786
|
+
when an SDK is already available.
|
package/dist/cli.js
CHANGED
|
@@ -164,10 +164,6 @@ function assertValidProjectName(name, label) {
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
// src/commands/add/go.ts
|
|
168
|
-
var import_node_fs2 = require("fs");
|
|
169
|
-
var import_node_path3 = require("path");
|
|
170
|
-
|
|
171
167
|
// src/commands/add/shared.ts
|
|
172
168
|
var import_node_path2 = require("path");
|
|
173
169
|
function hasPlugin(workspaceRoot, packageName) {
|
|
@@ -203,7 +199,48 @@ function addNxTargets(manifestPath, newTargets) {
|
|
|
203
199
|
);
|
|
204
200
|
}
|
|
205
201
|
|
|
202
|
+
// src/commands/add/flutter.ts
|
|
203
|
+
function ensureFlutter(workspaceRoot) {
|
|
204
|
+
if (runShell("flutter", ["--version"], workspaceRoot) !== 0) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
"Flutter not found. Install the Flutter SDK first (3.27+, for Dart 3.6+ pub workspaces): https://docs.flutter.dev/get-started/install"
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function nxFlutterPluginSpec() {
|
|
211
|
+
return process.env.MNCI_NX_FLUTTER_SPEC ?? "@mnci/nx-flutter";
|
|
212
|
+
}
|
|
213
|
+
function ensureNxFlutterPlugin(workspaceRoot) {
|
|
214
|
+
if (hasPlugin(workspaceRoot, "@mnci/nx-flutter")) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const spec = nxFlutterPluginSpec();
|
|
218
|
+
logger.step(`Installing the Flutter toolchain plugin (${spec})`);
|
|
219
|
+
if (runShell("npm", ["install", "--save-dev", spec, "--no-audit", "--no-fund"], workspaceRoot) !== 0) {
|
|
220
|
+
throw new Error("npm install of @mnci/nx-flutter failed");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function prepareFlutter(workspaceRoot) {
|
|
224
|
+
ensureFlutter(workspaceRoot);
|
|
225
|
+
ensureNxFlutterPlugin(workspaceRoot);
|
|
226
|
+
}
|
|
227
|
+
function addFlutterApp(workspaceRoot, name) {
|
|
228
|
+
prepareFlutter(workspaceRoot);
|
|
229
|
+
ensureAdmZip(workspaceRoot);
|
|
230
|
+
runNx(["g", "@mnci/nx-flutter:application", name, "--no-interactive"], workspaceRoot);
|
|
231
|
+
}
|
|
232
|
+
function addFlutterLib(workspaceRoot, name) {
|
|
233
|
+
prepareFlutter(workspaceRoot);
|
|
234
|
+
runNx(["g", "@mnci/nx-flutter:library", name, "--no-interactive"], workspaceRoot);
|
|
235
|
+
}
|
|
236
|
+
function addFlutterInternalLib(workspaceRoot, name) {
|
|
237
|
+
prepareFlutter(workspaceRoot);
|
|
238
|
+
runNx(["g", "@mnci/nx-flutter:internal-library", name, "--no-interactive"], workspaceRoot);
|
|
239
|
+
}
|
|
240
|
+
|
|
206
241
|
// src/commands/add/go.ts
|
|
242
|
+
var import_node_fs2 = require("fs");
|
|
243
|
+
var import_node_path3 = require("path");
|
|
207
244
|
function ensureGo(workspaceRoot) {
|
|
208
245
|
if (runShell("go", ["version"], workspaceRoot) !== 0) {
|
|
209
246
|
throw new Error("Go not found. Install Go 1.21+ first: https://go.dev/dl/");
|
|
@@ -539,6 +576,17 @@ export default [
|
|
|
539
576
|
ignoredFiles: [
|
|
540
577
|
'{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}',
|
|
541
578
|
'{projectRoot}/rollup.config.{js,ts,mjs,mts,cjs,cts}',
|
|
579
|
+
// Same reasoning as the two config files above, extended to the test
|
|
580
|
+
// toolchain: rollup bundles from the entry point only, so neither the
|
|
581
|
+
// Vitest config nor the spec files reach the published package \u2014 their
|
|
582
|
+
// imports must not drive the published manifest.
|
|
583
|
+
//
|
|
584
|
+
// Without this, a vitest-stack workspace fails \`npm run lint\` on a
|
|
585
|
+
// freshly generated npm-lib: Nx's own spec imports \`vitest\`, and
|
|
586
|
+
// @nx/dependency-checks then demands it be declared as a runtime
|
|
587
|
+
// dependency of a package that never uses it at runtime.
|
|
588
|
+
'{projectRoot}/vitest.config.{js,ts,mjs,mts,cjs,cts}',
|
|
589
|
+
'{projectRoot}/**/*.spec.{js,ts,jsx,tsx}',
|
|
542
590
|
],
|
|
543
591
|
},
|
|
544
592
|
],
|
|
@@ -873,7 +921,10 @@ var PROJECT_KINDS = [
|
|
|
873
921
|
"go-app",
|
|
874
922
|
"go-function-app",
|
|
875
923
|
"go-lib",
|
|
876
|
-
"go-internal-lib"
|
|
924
|
+
"go-internal-lib",
|
|
925
|
+
"flutter-app",
|
|
926
|
+
"flutter-lib",
|
|
927
|
+
"flutter-internal-lib"
|
|
877
928
|
];
|
|
878
929
|
async function runAdd(kind, name, options) {
|
|
879
930
|
const workspaceRoot = process.cwd();
|
|
@@ -953,6 +1004,18 @@ async function runAdd(kind, name, options) {
|
|
|
953
1004
|
addGoInternalLib(workspaceRoot, resolvedName);
|
|
954
1005
|
break;
|
|
955
1006
|
}
|
|
1007
|
+
case "flutter-app": {
|
|
1008
|
+
addFlutterApp(workspaceRoot, resolvedName);
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
case "flutter-lib": {
|
|
1012
|
+
addFlutterLib(workspaceRoot, resolvedName);
|
|
1013
|
+
break;
|
|
1014
|
+
}
|
|
1015
|
+
case "flutter-internal-lib": {
|
|
1016
|
+
addFlutterInternalLib(workspaceRoot, resolvedName);
|
|
1017
|
+
break;
|
|
1018
|
+
}
|
|
956
1019
|
case "python-vendor": {
|
|
957
1020
|
await addPythonVendor(workspaceRoot, resolvedName, options, kindProvided);
|
|
958
1021
|
syncProjectReferences(workspaceRoot);
|
|
@@ -1034,27 +1097,33 @@ function npmrcContent(registry, scope) {
|
|
|
1034
1097
|
lines.push("");
|
|
1035
1098
|
return lines.join("\n");
|
|
1036
1099
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
}
|
|
1100
|
+
function releaseConfig(ci) {
|
|
1101
|
+
const githubReleases = ci === "github";
|
|
1102
|
+
return {
|
|
1103
|
+
projectsRelationship: "independent",
|
|
1104
|
+
projects: ["packages/*", "python-packages/*", "!tag:type:go-lib"],
|
|
1105
|
+
releaseTag: { pattern: "{projectName}@{version}" },
|
|
1106
|
+
git: { commit: false, tag: true, push: githubReleases },
|
|
1107
|
+
version: {
|
|
1108
|
+
conventionalCommits: true,
|
|
1109
|
+
fallbackCurrentVersionResolver: "disk",
|
|
1110
|
+
// Build only what is being released. Without this, @nx/js:lib's generator
|
|
1111
|
+
// defaults the pre-version command to building EVERY project, so a broken
|
|
1112
|
+
// (or merely slow) app build would block releasing unrelated packages.
|
|
1113
|
+
// Set here at `new` time it wins: the generator only fills this in when
|
|
1114
|
+
// absent (it spreads the existing release.version over its default). Both
|
|
1115
|
+
// globs are listed; `nx run-many` no-ops cleanly when one matches nothing.
|
|
1116
|
+
preVersionCommand: "npx nx run-many -t build --projects=packages/*,python-packages/*"
|
|
1117
|
+
},
|
|
1118
|
+
changelog: githubReleases ? {
|
|
1119
|
+
workspaceChangelog: false,
|
|
1120
|
+
projectChangelogs: { createRelease: "github", file: false }
|
|
1121
|
+
} : { workspaceChangelog: false }
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1055
1124
|
var SYNC_CONFIG = { applyChanges: true };
|
|
1056
|
-
function withReleaseConfig(nxJson) {
|
|
1057
|
-
return { ...nxJson, defaultBase: "main", release:
|
|
1125
|
+
function withReleaseConfig(nxJson, ci) {
|
|
1126
|
+
return { ...nxJson, defaultBase: "main", release: releaseConfig(ci) };
|
|
1058
1127
|
}
|
|
1059
1128
|
var COMMITLINT_CONFIG = `export default { extends: ['@commitlint/config-conventional'] }
|
|
1060
1129
|
`;
|
|
@@ -1187,6 +1256,13 @@ var GOLANGCI_LINT_INSTALL_GUARD = `node -e "const fs=require('node:fs'),cp=requi
|
|
|
1187
1256
|
var GO_TOOL_PATH_PRELUDE = `const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}const r=cp.spawnSync('go',['env','GOPATH'],{encoding:'utf8'});if(r.status!==0){console.log('Could not resolve GOPATH - skipping.');process.exit(0)}const bin=require('node:path').join(r.stdout.trim(),'bin');`;
|
|
1188
1257
|
var GO_TOOL_PATH_AZURE = `node -e "${GO_TOOL_PATH_PRELUDE}console.log('##vso[task.prependpath]'+bin)"`;
|
|
1189
1258
|
var GO_TOOL_PATH_GITHUB = `node -e "${GO_TOOL_PATH_PRELUDE}if(!process.env.GITHUB_PATH){console.log('Not running in GitHub Actions - skipping.');process.exit(0)}fs.appendFileSync(process.env.GITHUB_PATH,bin+${String.raw`'\n'`})"`;
|
|
1259
|
+
var FLUTTER_SDK_VERSION = "3.44.8";
|
|
1260
|
+
var FLUTTER_SDK_DIRECTORY_EXPRESSION = `require('node:path').join(require('node:os').homedir(),'.mnci-flutter-${FLUTTER_SDK_VERSION}')`;
|
|
1261
|
+
var FLUTTER_SDK_INSTALL_GUARD = `node -e "const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('pubspec.yaml')){console.log('No Flutter projects - skipping.');process.exit(0)}if(cp.spawnSync('flutter',['--version'],{stdio:'ignore',shell:process.platform==='win32'}).status===0){console.log('Flutter SDK already on PATH - skipping.');process.exit(0)}const sdk=${FLUTTER_SDK_DIRECTORY_EXPRESSION};if(fs.existsSync(sdk)){console.log('Flutter SDK already installed at '+sdk+' - skipping.');process.exit(0)}process.exit(cp.spawnSync('git',['clone','--depth','1','--branch','${FLUTTER_SDK_VERSION}','https://github.com/flutter/flutter.git',sdk],{stdio:'inherit'}).status ?? 1)"`;
|
|
1262
|
+
var FLUTTER_TOOL_PATH_PRELUDE = `const fs=require('node:fs'),path=require('node:path');if(!fs.existsSync('pubspec.yaml')){console.log('No Flutter projects - skipping.');process.exit(0)}const sdk=${FLUTTER_SDK_DIRECTORY_EXPRESSION};if(!fs.existsSync(sdk)){console.log('Flutter SDK was not installed by mnci (already on PATH) - skipping.');process.exit(0)}const bin=path.join(sdk,'bin');`;
|
|
1263
|
+
var FLUTTER_TOOL_PATH_AZURE = `node -e "${FLUTTER_TOOL_PATH_PRELUDE}console.log('##vso[task.prependpath]'+bin)"`;
|
|
1264
|
+
var FLUTTER_TOOL_PATH_GITHUB = `node -e "${FLUTTER_TOOL_PATH_PRELUDE}if(!process.env.GITHUB_PATH){console.log('Not running in GitHub Actions - skipping.');process.exit(0)}fs.appendFileSync(process.env.GITHUB_PATH,bin+${String.raw`'\n'`})"`;
|
|
1265
|
+
var FLUTTER_PUB_GET_GUARD = `node -e "const fs=require('node:fs');if(!fs.existsSync('pubspec.yaml')){console.log('No Flutter projects - skipping.');process.exit(0)}process.exit(require('node:child_process').spawnSync('flutter',['pub','get'],{stdio:'inherit',shell:process.platform==='win32'}).status ?? 1)"`;
|
|
1190
1266
|
var NPM_AUDIT_STEP = "npm audit --audit-level=high || echo npm audit found vulnerabilities, see log above - non-blocking, run npm audit locally to inspect";
|
|
1191
1267
|
var PACK_APPS_GUARD = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});if(fs.globSync('apps/*/project.json').length===0){console.log('No apps to pack - skipping.');process.exit(0)}process.exit(require('node:child_process').spawnSync('npx nx run-many -t package',{stdio:'inherit',shell:true}).status ?? 1)"`;
|
|
1192
1268
|
function releaseGuard(pythonPublishEnv) {
|
|
@@ -1302,6 +1378,23 @@ steps:
|
|
|
1302
1378
|
- script: ${GO_TOOL_PATH_AZURE}
|
|
1303
1379
|
displayName: Add Go tool bin to PATH
|
|
1304
1380
|
|
|
1381
|
+
# Flutter, if the workspace has any. Unlike Python and Go, hosted agents do
|
|
1382
|
+
# NOT ship the Flutter SDK, so this installs it (shallow git clone at a
|
|
1383
|
+
# pinned tag) and puts it on PATH. All three guards skip cleanly on a
|
|
1384
|
+
# workspace with no root pubspec.yaml, and the install also skips when an
|
|
1385
|
+
# SDK is already on PATH.
|
|
1386
|
+
- script: ${FLUTTER_SDK_INSTALL_GUARD}
|
|
1387
|
+
displayName: Install the Flutter SDK (${FLUTTER_SDK_VERSION})
|
|
1388
|
+
|
|
1389
|
+
- script: ${FLUTTER_TOOL_PATH_AZURE}
|
|
1390
|
+
displayName: Add the Flutter SDK to PATH
|
|
1391
|
+
|
|
1392
|
+
# One command resolves EVERY Dart dependency, internal and external, for the
|
|
1393
|
+
# whole workspace: the projects are pub workspace members, so this writes a
|
|
1394
|
+
# single root pubspec.lock they all share.
|
|
1395
|
+
- script: ${FLUTTER_PUB_GET_GUARD}
|
|
1396
|
+
displayName: Resolve Dart dependencies (one pub get for the whole workspace)
|
|
1397
|
+
|
|
1305
1398
|
# Fails fast, with an unambiguous message, when a stale TypeScript project
|
|
1306
1399
|
# reference (or another sync generator's drift) was never synced+committed
|
|
1307
1400
|
# locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
|
|
@@ -1363,12 +1456,13 @@ steps:
|
|
|
1363
1456
|
condition: ${onMain}
|
|
1364
1457
|
`;
|
|
1365
1458
|
}
|
|
1366
|
-
function githubActionsYaml(agent, pythonPublishUrl2, registryKind = "azure-artifacts") {
|
|
1459
|
+
function githubActionsYaml(agent, pythonPublishUrl2, registryKind = "azure-artifacts", ci = "github") {
|
|
1367
1460
|
const onMain = `github.event_name != 'pull_request' && github.ref_name == 'main'`;
|
|
1368
1461
|
const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(
|
|
1369
1462
|
registryKind,
|
|
1370
1463
|
(name) => `\${{ secrets.${name} }}`
|
|
1371
1464
|
);
|
|
1465
|
+
const githubReleases = ci === "github";
|
|
1372
1466
|
return `name: CI
|
|
1373
1467
|
|
|
1374
1468
|
# Generated by MoNecromanCI. Deliberately thin: Nx builds, 'nx release'
|
|
@@ -1384,7 +1478,8 @@ on:
|
|
|
1384
1478
|
branches: [main]
|
|
1385
1479
|
|
|
1386
1480
|
permissions:
|
|
1387
|
-
# Lets the release step push the version tag nx release creates back to main
|
|
1481
|
+
# Lets the release step push the version tag nx release creates back to main,
|
|
1482
|
+
# and (github-only provider) create the GitHub Release itself.
|
|
1388
1483
|
contents: write
|
|
1389
1484
|
|
|
1390
1485
|
jobs:
|
|
@@ -1454,6 +1549,23 @@ jobs:
|
|
|
1454
1549
|
- run: ${GO_TOOL_PATH_GITHUB}
|
|
1455
1550
|
name: Add Go tool bin to PATH
|
|
1456
1551
|
|
|
1552
|
+
# Flutter, if the workspace has any. Unlike Python and Go, hosted runners
|
|
1553
|
+
# do NOT ship the Flutter SDK, so this installs it (shallow git clone at
|
|
1554
|
+
# a pinned tag) and puts it on PATH. All three guards skip cleanly on a
|
|
1555
|
+
# workspace with no root pubspec.yaml, and the install also skips when an
|
|
1556
|
+
# SDK is already on PATH.
|
|
1557
|
+
- run: ${FLUTTER_SDK_INSTALL_GUARD}
|
|
1558
|
+
name: Install the Flutter SDK (${FLUTTER_SDK_VERSION})
|
|
1559
|
+
|
|
1560
|
+
- run: ${FLUTTER_TOOL_PATH_GITHUB}
|
|
1561
|
+
name: Add the Flutter SDK to PATH
|
|
1562
|
+
|
|
1563
|
+
# One command resolves EVERY Dart dependency, internal and external, for
|
|
1564
|
+
# the whole workspace: the projects are pub workspace members, so this
|
|
1565
|
+
# writes a single root pubspec.lock they all share.
|
|
1566
|
+
- run: ${FLUTTER_PUB_GET_GUARD}
|
|
1567
|
+
name: Resolve Dart dependencies (one pub get for the whole workspace)
|
|
1568
|
+
|
|
1457
1569
|
# Fails fast, with an unambiguous message, when a stale TypeScript project
|
|
1458
1570
|
# reference (or another sync generator's drift) was never synced+committed
|
|
1459
1571
|
# locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
|
|
@@ -1491,13 +1603,19 @@ jobs:
|
|
|
1491
1603
|
# (python-packages/*) \u2014 conventional commits, tag-only push. Portable guard:
|
|
1492
1604
|
# nx release errors on an empty scope, so skip cleanly when there is nothing
|
|
1493
1605
|
# to release. When there are Python packages and an Azure feed, twine
|
|
1494
|
-
# publish credentials are exported (raw PAT, decoded from the base64 secret)
|
|
1606
|
+
# publish credentials are exported (raw PAT, decoded from the base64 secret).${githubReleases ? `
|
|
1607
|
+
# This provider also creates a per-project GitHub Release (changelog
|
|
1608
|
+
# generated from conventional commits since that project's last tag),
|
|
1609
|
+
# which is why 'nx release' pushes the tag itself here \u2014 see
|
|
1610
|
+
# releaseConfig's remarks for why that is safe on this Nx version and
|
|
1611
|
+
# why every other provider combination keeps the explicit push step below.` : ""}
|
|
1495
1612
|
- run: ${releaseGuard(pythonPublishEnvFragment(pythonPublishUrl2))}
|
|
1496
|
-
name: Release \u2014 version, tag and publish (npm + Python)
|
|
1613
|
+
name: Release \u2014 version, tag${githubReleases ? ", publish and GitHub Release" : " and publish"} (npm + Python)
|
|
1497
1614
|
if: \${{ ${onMain} }}
|
|
1498
1615
|
env:
|
|
1499
|
-
${npmAuthName}: ${npmAuthValue}
|
|
1500
|
-
|
|
1616
|
+
${npmAuthName}: ${npmAuthValue}${githubReleases ? `
|
|
1617
|
+
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}` : ""}
|
|
1618
|
+
${githubReleases ? "" : `
|
|
1501
1619
|
# nx release's own git push (release.git.push) is deliberately left off: it
|
|
1502
1620
|
# only runs when a remote GitHub/GitLab Release is configured, which this
|
|
1503
1621
|
# workflow never does, so it would never push the tag the step above just
|
|
@@ -1506,7 +1624,7 @@ jobs:
|
|
|
1506
1624
|
- run: git push origin --tags
|
|
1507
1625
|
name: Push release tags (nx release's own push never runs without a remote Release configured)
|
|
1508
1626
|
if: \${{ ${onMain} }}
|
|
1509
|
-
`;
|
|
1627
|
+
`}`;
|
|
1510
1628
|
}
|
|
1511
1629
|
var DEPENDABOT_CONFIG = `# Generated by MoNecromanCI. Weekly dependency-update PRs so vulnerable or
|
|
1512
1630
|
# stale dependencies surface as a reviewable PR instead of only a push-time
|
|
@@ -1533,6 +1651,18 @@ updates:
|
|
|
1533
1651
|
- "/libs/*"
|
|
1534
1652
|
schedule:
|
|
1535
1653
|
interval: weekly
|
|
1654
|
+
|
|
1655
|
+
# Same glob reasoning as pip above. Dart projects are pub workspace members,
|
|
1656
|
+
# so each one declares its own dependencies even though they all resolve
|
|
1657
|
+
# through the single root pubspec.lock \u2014 hence the per-project directories
|
|
1658
|
+
# rather than just "/".
|
|
1659
|
+
- package-ecosystem: pub
|
|
1660
|
+
directories:
|
|
1661
|
+
- "/apps/*"
|
|
1662
|
+
- "/packages/*"
|
|
1663
|
+
- "/libs/*"
|
|
1664
|
+
schedule:
|
|
1665
|
+
interval: weekly
|
|
1536
1666
|
`;
|
|
1537
1667
|
function applyOverlay(workspaceRoot, options) {
|
|
1538
1668
|
const nxJsonPath = (0, import_node_path9.join)(workspaceRoot, "nx.json");
|
|
@@ -1543,7 +1673,10 @@ function applyOverlay(workspaceRoot, options) {
|
|
|
1543
1673
|
};
|
|
1544
1674
|
const sync = { ...nxJson.sync, ...SYNC_CONFIG };
|
|
1545
1675
|
const mnci = { ...nxJson.mnci, ...mnciConfig(options) };
|
|
1546
|
-
writeFileEnsured(
|
|
1676
|
+
writeFileEnsured(
|
|
1677
|
+
nxJsonPath,
|
|
1678
|
+
toJson({ ...withReleaseConfig(nxJson, options.ci), generators, sync, mnci })
|
|
1679
|
+
);
|
|
1547
1680
|
const manifestPath = (0, import_node_path9.join)(workspaceRoot, "package.json");
|
|
1548
1681
|
const manifest = readJson(manifestPath);
|
|
1549
1682
|
const scripts = {
|
|
@@ -1580,7 +1713,7 @@ function applyOverlay(workspaceRoot, options) {
|
|
|
1580
1713
|
if (options.ci === "github" || options.ci === "both") {
|
|
1581
1714
|
writeFileEnsured(
|
|
1582
1715
|
(0, import_node_path9.join)(workspaceRoot, ".github/workflows/ci.yml"),
|
|
1583
|
-
githubActionsYaml(options.agent, publishUrl, options.registry.kind)
|
|
1716
|
+
githubActionsYaml(options.agent, publishUrl, options.registry.kind, options.ci)
|
|
1584
1717
|
);
|
|
1585
1718
|
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".github/dependabot.yml"), DEPENDABOT_CONFIG);
|
|
1586
1719
|
}
|
|
@@ -1672,7 +1805,13 @@ async function runNew(name, options) {
|
|
|
1672
1805
|
" mnci add react-app web # or: node-app, node-function-app, npm-lib, internal-lib,"
|
|
1673
1806
|
);
|
|
1674
1807
|
logger.info(
|
|
1675
|
-
" # python-app, python-function-app, python-lib, python-internal-lib"
|
|
1808
|
+
" # python-app, python-function-app, python-lib, python-internal-lib,"
|
|
1809
|
+
);
|
|
1810
|
+
logger.info(
|
|
1811
|
+
" # go-app, go-function-app, go-lib, go-internal-lib,"
|
|
1812
|
+
);
|
|
1813
|
+
logger.info(
|
|
1814
|
+
" # flutter-app, flutter-lib, flutter-internal-lib"
|
|
1676
1815
|
);
|
|
1677
1816
|
logger.info(' git add -A && git commit -m "feat: initial workspace"');
|
|
1678
1817
|
}
|