@williamthorsen/nmr 0.51.0 → 0.53.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/.meta/changelog.json +97 -0
- package/.readyup/kits/default.js +29 -27
- package/.readyup/manifest.json +31 -23
- package/CHANGELOG.md +32 -0
- package/README.md +13 -6
- package/bin/nmr-actionlint.js +11 -0
- package/dist/esm/check-cache-gate.d.ts +1 -1
- package/dist/esm/check-cache-gate.js +16 -4
- package/dist/esm/check-cache.js +1 -0
- package/dist/esm/cli-actionlint.d.ts +1 -0
- package/dist/esm/cli-actionlint.js +12 -0
- package/dist/esm/commands/actionlint.d.ts +23 -0
- package/dist/esm/commands/actionlint.js +31 -0
- package/dist/esm/commands/build-output.d.ts +13 -0
- package/dist/esm/commands/build-output.js +30 -1
- package/dist/esm/commands/build.js +12 -3
- package/dist/esm/commands/clean.d.ts +1 -1
- package/dist/esm/commands/clean.js +2 -1
- package/dist/esm/commands/fmt.d.ts +2 -1
- package/dist/esm/commands/fmt.js +7 -2
- package/dist/esm/commands/run-vitest.js +2 -0
- package/dist/esm/commands/workflow-tools.d.ts +39 -0
- package/dist/esm/commands/workflow-tools.js +140 -0
- package/dist/esm/default-scripts.js +5 -3
- package/dist/esm/delegate-host.js +3 -3
- package/dist/esm/glyphs.d.ts +1 -1
- package/dist/esm/glyphs.js +1 -0
- package/dist/esm/remote-host-session.js +60 -0
- package/dist/esm/remote-run.js +5 -5
- package/dist/esm/remote-snapshot.js +2 -2
- package/dist/esm/runner.d.ts +7 -0
- package/dist/esm/runner.js +37 -3
- package/dist/esm/stated-outcome.d.ts +37 -0
- package/dist/esm/stated-outcome.js +60 -0
- package/dist/esm/taze.d.ts +9 -7
- package/dist/esm/taze.js +0 -1
- package/dist/esm/verdict.d.ts +2 -0
- package/dist/esm/verdict.js +15 -3
- package/docs/check-cache.md +1 -1
- package/docs/delegate.md +4 -4
- package/docs/reporting.md +5 -1
- package/docs/scripts.md +9 -1
- package/docs/upgrades.md +6 -4
- package/docs/utilities.md +14 -3
- package/package.json +8 -7
package/.meta/changelog.json
CHANGED
|
@@ -1,4 +1,101 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.53.0",
|
|
4
|
+
"date": "2026-10-27",
|
|
5
|
+
"sections": [
|
|
6
|
+
{
|
|
7
|
+
"title": "🎉 Features",
|
|
8
|
+
"audience": "all",
|
|
9
|
+
"items": [
|
|
10
|
+
{
|
|
11
|
+
"description": "Adds the time saved to the line that `nmr-compile` prints when it skips an unchanged package, as in `Skipping build, saved ~2.1s.`, when the skipped emit took at least a second. (#1112)",
|
|
12
|
+
"hash": "ead0549c50d886ef371cb03d979883e027b051c2",
|
|
13
|
+
"entry": 1
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"description": "Adds `build skipped as unchanged` to the verdict line of a compile that hits the build cache, followed by the saved time when the recorded emit took at least a second, which nmr prints in a quiet run as well. (#1113)",
|
|
17
|
+
"hash": "02cbf9688061958d23230901120fc374e56ddfe7",
|
|
18
|
+
"entry": 1
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"description": "Adds a `stated` field to the `--json` record of a passing command that stated its own outcome, such as `{\"kind\":\"build-unchanged\",\"savedMs\":2100}`, which records the saving whatever its size. (#1113)",
|
|
22
|
+
"hash": "02cbf9688061958d23230901120fc374e56ddfe7",
|
|
23
|
+
"entry": 2
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"description": "Removes the 7-day `maturityPeriod` from the shared policy that `defineConfig` in `@williamthorsen/nmr/taze` supplies, so that `nmr upgrade` waits for the `minimumReleaseAge` set in `pnpm-workspace.yaml` (or pnpm's default) and no longer withholds releases that pnpm already resolves for other packages. (#1129)",
|
|
27
|
+
"hash": "e471e998babeb45ef0ca4db64a02fd0fd490702d",
|
|
28
|
+
"entry": 1
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"title": "🐛 Bug fixes",
|
|
34
|
+
"audience": "all",
|
|
35
|
+
"items": [
|
|
36
|
+
{
|
|
37
|
+
"description": "Fixes the issue that nmr reported a short or negative duration for a command's run, a delegated run, or a remote lock wait when the system clock was adjusted during that span. (#1112)",
|
|
38
|
+
"hash": "ead0549c50d886ef371cb03d979883e027b051c2",
|
|
39
|
+
"entry": 2
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"description": "Fixes the issue that `nmr-fmt` never formatted a file whose name starts with `-` and rejected `--` as an unknown option, by making `nmr-fmt` accept `--` before its pathspecs and pass `--` to Prettier before the file list. (#1130)",
|
|
43
|
+
"hash": "d7cd93b2309c0668b7bcb82bf52bad5fc0a5b742",
|
|
44
|
+
"entry": 1
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"title": "♻️ Refactoring",
|
|
50
|
+
"audience": "dev",
|
|
51
|
+
"items": [
|
|
52
|
+
{
|
|
53
|
+
"description": "Apply the naming conventions to nmr-core's identifiers (#1125)",
|
|
54
|
+
"body": "Renames the identifiers in nmr-core to follow the naming conventions, including the exported option keys of `updateManagedFile` and `writeFileWithCheck`, which change from `dryRun` and `overwrite` to `isDryRun` and `shouldOverwrite`, and the constant `GIT_OUTPUT_LIMIT`, which becomes `GIT_OUTPUT_LIMIT_BYTES`. Updates the call sites in nmr, release-kit, and v11y-check to the new names.",
|
|
55
|
+
"hash": "2cb60f628512ed6ec2273a219a7b6c71aa35b1bf"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"version": "0.52.0",
|
|
63
|
+
"date": "2026-10-24",
|
|
64
|
+
"sections": [
|
|
65
|
+
{
|
|
66
|
+
"title": "🎉 Features",
|
|
67
|
+
"audience": "all",
|
|
68
|
+
"items": [
|
|
69
|
+
{
|
|
70
|
+
"description": "Adds a 📡 glyph before the host name in the rich-output `nmr` verdicts that name a host, for a passed, failed, or recalled run, which leaves plain output and verdicts reading `on this tree` unchanged. (#1105)",
|
|
71
|
+
"hash": "15c0d637325fff429ceae5aa5ec6e0ee19741daa",
|
|
72
|
+
"entry": 1
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"description": "Adds the root command `lint:workflows` and its `nmr-actionlint` bin, which lint `.github/workflows/` with actionlint 1.7.12, including its shellcheck pass over each `run:` block with shellcheck 0.11.0, using binaries that nmr downloads on first use, checks against pinned SHA-256 checksums, and caches under `node_modules/.cache/nmr-tools/`. (#1107)",
|
|
76
|
+
"hash": "b4ddac2c4954a882d6be05962f9709e323ac3ece",
|
|
77
|
+
"entry": 1
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"description": "Adds `lint:workflows` to the root `check`, `check:strict`, and `root:check` composites, which makes those composites and `nmr ci` fail on any actionlint finding in a repo that has workflows, need access to `github.com` on their first run, and fail on any platform other than macOS or Linux on x64 or arm64. (#1107)",
|
|
81
|
+
"hash": "b4ddac2c4954a882d6be05962f9709e323ac3ece",
|
|
82
|
+
"entry": 2
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"title": "🐛 Bug fixes",
|
|
88
|
+
"audience": "all",
|
|
89
|
+
"items": [
|
|
90
|
+
{
|
|
91
|
+
"description": "Fixes the issue that a delegated `nmr` command could not find a bin that the branch added to a workspace package, because the host's `pnpm install --frozen-lockfile` did not link new bins when the lockfile was unchanged. (#1108)",
|
|
92
|
+
"hash": "5b694689a51679ddfbb04f65a88d0757ca310f3f",
|
|
93
|
+
"entry": 1
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
},
|
|
2
99
|
{
|
|
3
100
|
"version": "0.51.0",
|
|
4
101
|
"date": "2026-10-24",
|
package/.readyup/kits/default.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @noformat -- @generated. Do not edit. Compiled by rdy. */
|
|
2
2
|
/* eslint-disable */
|
|
3
|
-
export const __readyupVersion = "0.
|
|
3
|
+
export const __readyupVersion = "0.44.0";
|
|
4
4
|
import { createRequire as __rdyCreateRequire } from 'node:module';
|
|
5
5
|
const require = __rdyCreateRequire(import.meta.url);
|
|
6
6
|
|
|
@@ -7382,12 +7382,12 @@ import { isRecord, listTrackedFiles } from "readyup/check-utils";
|
|
|
7382
7382
|
import { globSync } from "node:fs";
|
|
7383
7383
|
import { basename, join, sep } from "node:path";
|
|
7384
7384
|
|
|
7385
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.errors@0.
|
|
7385
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.errors@0.9.0/node_modules/@williamthorsen/toolbelt.errors/dist/esm/4-release/isError.js
|
|
7386
7386
|
function isError(error) {
|
|
7387
7387
|
return error instanceof Error || Object.prototype.toString.call(error) === "[object Error]";
|
|
7388
7388
|
}
|
|
7389
7389
|
|
|
7390
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.errors@0.
|
|
7390
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.errors@0.9.0/node_modules/@williamthorsen/toolbelt.errors/dist/esm/4-release/describeError.js
|
|
7391
7391
|
function describeError(error) {
|
|
7392
7392
|
try {
|
|
7393
7393
|
if (isError(error) && typeof error.message === "string" && error.message !== "") {
|
|
@@ -7646,6 +7646,7 @@ var TYPECHECK_STEP = { run: "typecheck", shouldDeclineArguments: true };
|
|
|
7646
7646
|
var ROOT_TYPECHECK_STEP = { run: "root:typecheck", shouldDeclineArguments: true };
|
|
7647
7647
|
var LINT_CHECK_STEP = { run: "lint:check", shouldRunInBackground: true };
|
|
7648
7648
|
var LINT_STRICT_STEP = { run: "lint:strict", shouldRunInBackground: true };
|
|
7649
|
+
var LINT_WORKFLOWS_STEP = { run: "lint:workflows", shouldDeclineArguments: true };
|
|
7649
7650
|
var workspaceScripts = {
|
|
7650
7651
|
build: ["compile"],
|
|
7651
7652
|
check: [LINT_CHECK_STEP, TYPECHECK_STEP, "fmt:check", "test"],
|
|
@@ -7678,8 +7679,8 @@ function buildRootScripts(workspaceGlobs) {
|
|
|
7678
7679
|
"audit:dev": "pnpm exec v11y --dev",
|
|
7679
7680
|
"audit:prod": "pnpm exec v11y --prod",
|
|
7680
7681
|
build: ["-R build"],
|
|
7681
|
-
check: [LINT_CHECK_STEP, TYPECHECK_STEP, "fmt:check", "test"],
|
|
7682
|
-
"check:strict": [LINT_STRICT_STEP, TYPECHECK_STEP, "fmt:check", "test"],
|
|
7682
|
+
check: [LINT_CHECK_STEP, LINT_WORKFLOWS_STEP, TYPECHECK_STEP, "fmt:check", "test"],
|
|
7683
|
+
"check:strict": [LINT_STRICT_STEP, LINT_WORKFLOWS_STEP, TYPECHECK_STEP, "fmt:check", "test"],
|
|
7683
7684
|
// Excludes the audit, which in CI has a workflow of its own. The narrowed check runs against the build, so
|
|
7684
7685
|
// the build declines the arguments rather than being narrowed by them.
|
|
7685
7686
|
ci: [{ run: "build", shouldDeclineArguments: true }, "check:strict"],
|
|
@@ -7691,11 +7692,12 @@ function buildRootScripts(workspaceGlobs) {
|
|
|
7691
7692
|
lint: "eslint --fix .",
|
|
7692
7693
|
"lint:check": "eslint .",
|
|
7693
7694
|
"lint:strict": "strict-lint",
|
|
7695
|
+
"lint:workflows": "nmr-actionlint",
|
|
7694
7696
|
// The audit takes seconds and `ci` takes minutes, so the cheap gate fails first. The audit reads the
|
|
7695
7697
|
// dependency tree, and an argument narrowing the code under test does not say anything about that tree.
|
|
7696
7698
|
prepush: [{ run: "audit", shouldDeclineArguments: true }, "ci"],
|
|
7697
7699
|
"report-overrides": "nmr-report-overrides",
|
|
7698
|
-
"root:check": [ROOT_TYPECHECK_STEP, "fmt:check", "root:lint:check", "root:test"],
|
|
7700
|
+
"root:check": [ROOT_TYPECHECK_STEP, "fmt:check", "root:lint:check", LINT_WORKFLOWS_STEP, "root:test"],
|
|
7699
7701
|
"root:lint": `eslint --fix ${ignorePatterns}.`,
|
|
7700
7702
|
"root:lint:check": `eslint ${ignorePatterns}.`,
|
|
7701
7703
|
"root:lint:strict": `strict-lint ${ignorePatterns}.`,
|
|
@@ -7730,12 +7732,12 @@ var import_yaml = __toESM(require_dist(), 1);
|
|
|
7730
7732
|
// ../nmr-core/dist/esm/helpers/workspace-patterns.js
|
|
7731
7733
|
import { globSync as globSync2 } from "node:fs";
|
|
7732
7734
|
import path from "node:path";
|
|
7733
|
-
var
|
|
7734
|
-
var
|
|
7735
|
+
var MANIFEST_FILENAME = "package.json";
|
|
7736
|
+
var ALWAYS_EXCLUDED_PATTERNS = ["**/node_modules/**"];
|
|
7735
7737
|
function matchPackageDirs(monorepoRoot, includedPatterns, excludedPatterns) {
|
|
7736
7738
|
const options = {
|
|
7737
7739
|
cwd: monorepoRoot,
|
|
7738
|
-
exclude: [...excludedPatterns, ...
|
|
7740
|
+
exclude: [...excludedPatterns, ...ALWAYS_EXCLUDED_PATTERNS],
|
|
7739
7741
|
followSymlinks: true
|
|
7740
7742
|
};
|
|
7741
7743
|
const matches = globSync2([...includedPatterns], options);
|
|
@@ -7756,34 +7758,34 @@ function splitWorkspacePatterns(patterns) {
|
|
|
7756
7758
|
if (pattern.trim() === "")
|
|
7757
7759
|
continue;
|
|
7758
7760
|
const isNegated = pattern.startsWith("!");
|
|
7759
|
-
const
|
|
7760
|
-
|
|
7761
|
+
const targetPatterns = isNegated ? excludedPatterns : includedPatterns;
|
|
7762
|
+
targetPatterns.push(buildManifestPattern(isNegated ? pattern.slice(1) : pattern));
|
|
7761
7763
|
}
|
|
7762
7764
|
return { excludedPatterns, includedPatterns };
|
|
7763
7765
|
}
|
|
7764
7766
|
function buildManifestPattern(pattern) {
|
|
7765
|
-
return `${pattern.replace(/\/?$/, "")}/${
|
|
7767
|
+
return `${pattern.replace(/\/?$/, "")}/${MANIFEST_FILENAME}`;
|
|
7766
7768
|
}
|
|
7767
7769
|
|
|
7768
7770
|
// ../nmr-core/dist/esm/workspace.js
|
|
7769
|
-
var
|
|
7771
|
+
var WORKSPACE_MANIFEST_FILENAME = "pnpm-workspace.yaml";
|
|
7770
7772
|
function isMonorepoRoot(dir) {
|
|
7771
|
-
return existsSync(path2.join(dir,
|
|
7773
|
+
return existsSync(path2.join(dir, WORKSPACE_MANIFEST_FILENAME));
|
|
7772
7774
|
}
|
|
7773
7775
|
function resolveWorkspace(monorepoRoot) {
|
|
7774
|
-
const
|
|
7775
|
-
if (
|
|
7776
|
+
const manifestReadResult = readWorkspaceManifest(monorepoRoot);
|
|
7777
|
+
if (manifestReadResult.kind === "absent") {
|
|
7776
7778
|
return { kind: "not-a-workspace" };
|
|
7777
7779
|
}
|
|
7778
|
-
if (
|
|
7780
|
+
if (manifestReadResult.kind === "unreadable") {
|
|
7779
7781
|
return { cause: "unreadable-manifest", kind: "empty", patterns: [] };
|
|
7780
7782
|
}
|
|
7781
|
-
const
|
|
7782
|
-
if (
|
|
7783
|
-
const cause =
|
|
7783
|
+
const packagesReadResult = readDeclaredPackages(manifestReadResult.value);
|
|
7784
|
+
if (packagesReadResult.kind !== "patterns") {
|
|
7785
|
+
const cause = packagesReadResult.kind === "absent" ? "no-packages-list" : "unreadable-packages";
|
|
7784
7786
|
return { cause, kind: "empty", patterns: [] };
|
|
7785
7787
|
}
|
|
7786
|
-
const { patterns } =
|
|
7788
|
+
const { patterns } = packagesReadResult;
|
|
7787
7789
|
const packageDirs = resolvePackageDirs(monorepoRoot, patterns);
|
|
7788
7790
|
if (packageDirs.length > 0) {
|
|
7789
7791
|
return { kind: "packages", packageDirs, patterns };
|
|
@@ -7816,12 +7818,12 @@ function isObject(value) {
|
|
|
7816
7818
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7817
7819
|
}
|
|
7818
7820
|
function readWorkspaceManifest(monorepoRoot) {
|
|
7819
|
-
const
|
|
7820
|
-
if (!existsSync(
|
|
7821
|
+
const workspaceManifestPath = path2.join(monorepoRoot, WORKSPACE_MANIFEST_FILENAME);
|
|
7822
|
+
if (!existsSync(workspaceManifestPath)) {
|
|
7821
7823
|
return { kind: "absent" };
|
|
7822
7824
|
}
|
|
7823
7825
|
try {
|
|
7824
|
-
return { kind: "parsed", value: (0, import_yaml.parse)(readFileSync(
|
|
7826
|
+
return { kind: "parsed", value: (0, import_yaml.parse)(readFileSync(workspaceManifestPath, "utf8")) };
|
|
7825
7827
|
} catch {
|
|
7826
7828
|
return { kind: "unreadable" };
|
|
7827
7829
|
}
|
|
@@ -7836,11 +7838,11 @@ var UserError = class extends Error {
|
|
|
7836
7838
|
};
|
|
7837
7839
|
|
|
7838
7840
|
// src/workspace.ts
|
|
7839
|
-
var
|
|
7841
|
+
var WORKSPACE_MANIFEST = "pnpm-workspace.yaml";
|
|
7840
7842
|
function getWorkspacePackageDirs(monorepoRoot) {
|
|
7841
7843
|
const resolution = resolveWorkspace(monorepoRoot);
|
|
7842
7844
|
if (resolution.kind === "not-a-workspace") {
|
|
7843
|
-
throw new UserError(`Not a monorepo root: no ${
|
|
7845
|
+
throw new UserError(`Not a monorepo root: no ${WORKSPACE_MANIFEST} in ${monorepoRoot}`);
|
|
7844
7846
|
}
|
|
7845
7847
|
return resolution.kind === "packages" ? resolution.packageDirs : [];
|
|
7846
7848
|
}
|
|
@@ -9188,7 +9190,7 @@ function hasSupportedStrictLintVersion() {
|
|
|
9188
9190
|
});
|
|
9189
9191
|
}
|
|
9190
9192
|
function getMinVersion() {
|
|
9191
|
-
const pickedFields = { "version": "0.
|
|
9193
|
+
const pickedFields = { "version": "0.53.0" };
|
|
9192
9194
|
if (typeof pickedFields["version"] !== "string") {
|
|
9193
9195
|
throw new TypeError("nmr/package.json: 'version' must be a string");
|
|
9194
9196
|
}
|
package/.readyup/manifest.json
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
"kits": [
|
|
4
4
|
{
|
|
5
5
|
"bundledDependencies": {
|
|
6
|
-
"@williamthorsen/toolbelt.
|
|
7
|
-
"@williamthorsen/toolbelt.
|
|
8
|
-
"@williamthorsen/toolbelt.
|
|
9
|
-
"@williamthorsen/toolbelt.
|
|
6
|
+
"@williamthorsen/toolbelt.datetime": "4.1.0",
|
|
7
|
+
"@williamthorsen/toolbelt.errors": "0.9.0",
|
|
8
|
+
"@williamthorsen/toolbelt.filesystem": "0.10.0",
|
|
9
|
+
"@williamthorsen/toolbelt.numbers": "7.3.0",
|
|
10
|
+
"@williamthorsen/toolbelt.objects": "4.4.0",
|
|
11
|
+
"@williamthorsen/toolbelt.packaging": "0.7.0",
|
|
12
|
+
"@williamthorsen/toolbelt.terminal": "0.3.0",
|
|
10
13
|
"ansi-regex": "6.4.0",
|
|
11
14
|
"ansi-styles": "6.2.3",
|
|
12
15
|
"cli-truncate": "6.1.1",
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
"checklist": "nmr",
|
|
25
28
|
"checks": [
|
|
26
29
|
"@williamthorsen/nmr in devDependencies",
|
|
27
|
-
"@williamthorsen/nmr >= 0.
|
|
30
|
+
"@williamthorsen/nmr >= 0.53.0",
|
|
28
31
|
"pnpm-workspace.yaml exists",
|
|
29
32
|
"catalogued dependencies use catalog:",
|
|
30
33
|
"package.json has packageManager field",
|
|
@@ -71,22 +74,22 @@
|
|
|
71
74
|
"esbuildVersion": "0.28.2",
|
|
72
75
|
"inputs": [
|
|
73
76
|
{
|
|
74
|
-
"hash": "
|
|
77
|
+
"hash": "bf29a9a3",
|
|
75
78
|
"kind": "module",
|
|
76
79
|
"path": "../../nmr-core/dist/esm/cache-store.js"
|
|
77
80
|
},
|
|
78
81
|
{
|
|
79
|
-
"hash": "
|
|
82
|
+
"hash": "bc0baa4a",
|
|
80
83
|
"kind": "module",
|
|
81
84
|
"path": "../../nmr-core/dist/esm/collectChangedPaths.js"
|
|
82
85
|
},
|
|
83
86
|
{
|
|
84
|
-
"hash": "
|
|
87
|
+
"hash": "70d519e7",
|
|
85
88
|
"kind": "module",
|
|
86
89
|
"path": "../../nmr-core/dist/esm/gitObjects.js"
|
|
87
90
|
},
|
|
88
91
|
{
|
|
89
|
-
"hash": "
|
|
92
|
+
"hash": "14216f5f",
|
|
90
93
|
"kind": "module",
|
|
91
94
|
"path": "../../nmr-core/dist/esm/gitOutputLimit.js"
|
|
92
95
|
},
|
|
@@ -96,47 +99,52 @@
|
|
|
96
99
|
"path": "../../nmr-core/dist/esm/hasErrnoCode.js"
|
|
97
100
|
},
|
|
98
101
|
{
|
|
99
|
-
"hash": "
|
|
102
|
+
"hash": "0d7b3f7c",
|
|
100
103
|
"kind": "module",
|
|
101
104
|
"path": "../../nmr-core/dist/esm/hashWorkingTree.js"
|
|
102
105
|
},
|
|
103
106
|
{
|
|
104
|
-
"hash": "
|
|
107
|
+
"hash": "c1eae24e",
|
|
105
108
|
"kind": "module",
|
|
106
109
|
"path": "../../nmr-core/dist/esm/helpers/workspace-patterns.js"
|
|
107
110
|
},
|
|
108
111
|
{
|
|
109
|
-
"hash": "
|
|
112
|
+
"hash": "dcd24627",
|
|
110
113
|
"kind": "module",
|
|
111
114
|
"path": "../../nmr-core/dist/esm/index.js"
|
|
112
115
|
},
|
|
113
116
|
{
|
|
114
|
-
"hash": "
|
|
117
|
+
"hash": "a2053951",
|
|
115
118
|
"kind": "module",
|
|
116
119
|
"path": "../../nmr-core/dist/esm/parseArgs.js"
|
|
117
120
|
},
|
|
118
121
|
{
|
|
119
|
-
"hash": "
|
|
122
|
+
"hash": "9104fb90",
|
|
120
123
|
"kind": "module",
|
|
121
124
|
"path": "../../nmr-core/dist/esm/runGit.js"
|
|
122
125
|
},
|
|
123
126
|
{
|
|
124
|
-
"hash": "
|
|
127
|
+
"hash": "46b44e18",
|
|
125
128
|
"kind": "module",
|
|
126
129
|
"path": "../../nmr-core/dist/esm/terminal.js"
|
|
127
130
|
},
|
|
128
131
|
{
|
|
129
|
-
"hash": "
|
|
132
|
+
"hash": "85afaf3e",
|
|
133
|
+
"kind": "module",
|
|
134
|
+
"path": "../../nmr-core/dist/esm/timer.js"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"hash": "5ae3e980",
|
|
130
138
|
"kind": "module",
|
|
131
139
|
"path": "../../nmr-core/dist/esm/updateManagedFile.js"
|
|
132
140
|
},
|
|
133
141
|
{
|
|
134
|
-
"hash": "
|
|
142
|
+
"hash": "00d00fae",
|
|
135
143
|
"kind": "module",
|
|
136
144
|
"path": "../../nmr-core/dist/esm/workspace.js"
|
|
137
145
|
},
|
|
138
146
|
{
|
|
139
|
-
"hash": "
|
|
147
|
+
"hash": "d377c37c",
|
|
140
148
|
"kind": "module",
|
|
141
149
|
"path": "../../nmr-core/dist/esm/writeFileWithCheck.js"
|
|
142
150
|
},
|
|
@@ -186,7 +194,7 @@
|
|
|
186
194
|
"path": "lib/setup.ts"
|
|
187
195
|
},
|
|
188
196
|
{
|
|
189
|
-
"hash": "
|
|
197
|
+
"hash": "64995a53",
|
|
190
198
|
"kind": "module",
|
|
191
199
|
"path": "lib/shared-configs.ts"
|
|
192
200
|
},
|
|
@@ -201,7 +209,7 @@
|
|
|
201
209
|
"path": "lib/workspace-build.ts"
|
|
202
210
|
},
|
|
203
211
|
{
|
|
204
|
-
"hash": "
|
|
212
|
+
"hash": "aa5cf58e",
|
|
205
213
|
"kind": "inline",
|
|
206
214
|
"path": "../package.json",
|
|
207
215
|
"paths": [
|
|
@@ -224,7 +232,7 @@
|
|
|
224
232
|
"path": "../src/config.ts"
|
|
225
233
|
},
|
|
226
234
|
{
|
|
227
|
-
"hash": "
|
|
235
|
+
"hash": "b8e98c81",
|
|
228
236
|
"kind": "module",
|
|
229
237
|
"path": "../src/default-scripts.ts"
|
|
230
238
|
},
|
|
@@ -316,10 +324,10 @@
|
|
|
316
324
|
],
|
|
317
325
|
"name": "default",
|
|
318
326
|
"path": "kits/default.js",
|
|
319
|
-
"readyupVersion": "0.
|
|
327
|
+
"readyupVersion": "0.44.0",
|
|
320
328
|
"source": "kits/default.ts",
|
|
321
329
|
"sourceHash": "9fd61bf4",
|
|
322
|
-
"targetHash": "
|
|
330
|
+
"targetHash": "ad118293"
|
|
323
331
|
}
|
|
324
332
|
]
|
|
325
333
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.53.0 — 2026-10-27
|
|
6
|
+
|
|
7
|
+
### 🎉 Features
|
|
8
|
+
|
|
9
|
+
- Adds the time saved to the line that `nmr-compile` prints when it skips an unchanged package, as in `Skipping build, saved ~2.1s.`, when the skipped emit took at least a second. (#1112)
|
|
10
|
+
- Adds `build skipped as unchanged` to the verdict line of a compile that hits the build cache, followed by the saved time when the recorded emit took at least a second, which nmr prints in a quiet run as well. (#1113)
|
|
11
|
+
- Adds a `stated` field to the `--json` record of a passing command that stated its own outcome, such as `{"kind":"build-unchanged","savedMs":2100}`, which records the saving whatever its size. (#1113)
|
|
12
|
+
- Removes the 7-day `maturityPeriod` from the shared policy that `defineConfig` in `@williamthorsen/nmr/taze` supplies, so that `nmr upgrade` waits for the `minimumReleaseAge` set in `pnpm-workspace.yaml` (or pnpm's default) and no longer withholds releases that pnpm already resolves for other packages. (#1129)
|
|
13
|
+
|
|
14
|
+
### 🐛 Bug fixes
|
|
15
|
+
|
|
16
|
+
- Fixes the issue that nmr reported a short or negative duration for a command's run, a delegated run, or a remote lock wait when the system clock was adjusted during that span. (#1112)
|
|
17
|
+
- Fixes the issue that `nmr-fmt` never formatted a file whose name starts with `-` and rejected `--` as an unknown option, by making `nmr-fmt` accept `--` before its pathspecs and pass `--` to Prettier before the file list. (#1130)
|
|
18
|
+
|
|
19
|
+
### ♻️ Refactoring
|
|
20
|
+
|
|
21
|
+
- Apply the naming conventions to nmr-core's identifiers (#1125)
|
|
22
|
+
|
|
23
|
+
Renames the identifiers in nmr-core to follow the naming conventions, including the exported option keys of `updateManagedFile` and `writeFileWithCheck`, which change from `dryRun` and `overwrite` to `isDryRun` and `shouldOverwrite`, and the constant `GIT_OUTPUT_LIMIT`, which becomes `GIT_OUTPUT_LIMIT_BYTES`. Updates the call sites in nmr, release-kit, and v11y-check to the new names.
|
|
24
|
+
|
|
25
|
+
## 0.52.0 — 2026-10-24
|
|
26
|
+
|
|
27
|
+
### 🎉 Features
|
|
28
|
+
|
|
29
|
+
- Adds a 📡 glyph before the host name in the rich-output `nmr` verdicts that name a host, for a passed, failed, or recalled run, which leaves plain output and verdicts reading `on this tree` unchanged. (#1105)
|
|
30
|
+
- Adds the root command `lint:workflows` and its `nmr-actionlint` bin, which lint `.github/workflows/` with actionlint 1.7.12, including its shellcheck pass over each `run:` block with shellcheck 0.11.0, using binaries that nmr downloads on first use, checks against pinned SHA-256 checksums, and caches under `node_modules/.cache/nmr-tools/`. (#1107)
|
|
31
|
+
- Adds `lint:workflows` to the root `check`, `check:strict`, and `root:check` composites, which makes those composites and `nmr ci` fail on any actionlint finding in a repo that has workflows, need access to `github.com` on their first run, and fail on any platform other than macOS or Linux on x64 or arm64. (#1107)
|
|
32
|
+
|
|
33
|
+
### 🐛 Bug fixes
|
|
34
|
+
|
|
35
|
+
- Fixes the issue that a delegated `nmr` command could not find a bin that the branch added to a workspace package, because the host's `pnpm install --frozen-lockfile` did not link new bins when the lockfile was unchanged. (#1108)
|
|
36
|
+
|
|
5
37
|
## 0.51.0 — 2026-10-24
|
|
6
38
|
|
|
7
39
|
### 🎉 Features
|
package/README.md
CHANGED
|
@@ -5,12 +5,19 @@
|
|
|
5
5
|
Context-aware script runner for pnpm monorepos. Includes an `nmr` (node-monorepo run) binary that provides centralized, consistent script execution across workspace packages and the monorepo root.
|
|
6
6
|
|
|
7
7
|
<!-- section:release-notes -->
|
|
8
|
-
## Release notes — v0.
|
|
8
|
+
## Release notes — v0.53.0 (2026-10-27)
|
|
9
9
|
|
|
10
10
|
### 🎉 Features
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
- Adds
|
|
12
|
+
- Adds the time saved to the line that `nmr-compile` prints when it skips an unchanged package, as in `Skipping build, saved ~2.1s.`, when the skipped emit took at least a second. (#1112)
|
|
13
|
+
- Adds `build skipped as unchanged` to the verdict line of a compile that hits the build cache, followed by the saved time when the recorded emit took at least a second, which nmr prints in a quiet run as well. (#1113)
|
|
14
|
+
- Adds a `stated` field to the `--json` record of a passing command that stated its own outcome, such as `{"kind":"build-unchanged","savedMs":2100}`, which records the saving whatever its size. (#1113)
|
|
15
|
+
- Removes the 7-day `maturityPeriod` from the shared policy that `defineConfig` in `@williamthorsen/nmr/taze` supplies, so that `nmr upgrade` waits for the `minimumReleaseAge` set in `pnpm-workspace.yaml` (or pnpm's default) and no longer withholds releases that pnpm already resolves for other packages. (#1129)
|
|
16
|
+
|
|
17
|
+
### 🐛 Bug fixes
|
|
18
|
+
|
|
19
|
+
- Fixes the issue that nmr reported a short or negative duration for a command's run, a delegated run, or a remote lock wait when the system clock was adjusted during that span. (#1112)
|
|
20
|
+
- Fixes the issue that `nmr-fmt` never formatted a file whose name starts with `-` and rejected `--` as an unknown option, by making `nmr-fmt` accept `--` before its pathspecs and pass `--` to Prettier before the file list. (#1130)
|
|
14
21
|
<!-- /section:release-notes -->
|
|
15
22
|
|
|
16
23
|
## Installation
|
|
@@ -57,7 +64,7 @@ From the monorepo root:
|
|
|
57
64
|
```bash
|
|
58
65
|
nmr test # Run root tests + recursive workspace tests
|
|
59
66
|
nmr build # Build all packages
|
|
60
|
-
nmr ci # Build, then run the strict checks
|
|
67
|
+
nmr ci # Build, then run the strict checks, workflow lint included
|
|
61
68
|
nmr prepush # Run everything the remote runs, before a push
|
|
62
69
|
```
|
|
63
70
|
|
|
@@ -206,7 +213,7 @@ export default {
|
|
|
206
213
|
|
|
207
214
|
```console
|
|
208
215
|
$ nmr --delegate-to imac2 ci
|
|
209
|
-
✅ my-repo: ci: passed on imac2 in 1m 48s
|
|
216
|
+
✅ my-repo: ci: passed on 📡 imac2 in 1m 48s
|
|
210
217
|
```
|
|
211
218
|
|
|
212
219
|
A delegation consults the [check-result cache](#check-result-cache) first, so a tree that already passed is not sent to the host; a pass that a host earned does not satisfy a run here. A repo whose checks must run on the machine that has the tree declares `delegate: false` in its config. [Delegating to another machine](docs/delegate.md) covers the user config, `--delegate`, `NMR_DELEGATE`, the rules that decide where a command runs, which commands can be delegated, how nmr chooses among several hosts, how the cache is consulted, what the host runs, how failures are reported, and setting up the host.
|
|
@@ -317,7 +324,7 @@ rdy list --from npm:@williamthorsen/nmr # what nmr publishes
|
|
|
317
324
|
- [Machine lock](docs/machine-lock.md): Which commands wait for one another, where the lock lives, and bypassing it
|
|
318
325
|
- [Delegating to another machine](docs/delegate.md): The user config, `--delegate`, `--delegate-to`, `NMR_DELEGATE`, where a command runs, choosing among several hosts, what the host runs, and setting it up
|
|
319
326
|
- [Dependency upgrades](docs/upgrades.md): `nmr upgrade`, the upgrade policy, `report-catalog`, `report-overrides`, and `nmr-taze`
|
|
320
|
-
- [Standalone utilities](docs/utilities.md): `nmr-clean`, `nmr-compile`, `nmr-fmt`, `nmr-vitest`, and `ensure-prepublish-hooks`
|
|
327
|
+
- [Standalone utilities](docs/utilities.md): `nmr-actionlint`, `nmr-clean`, `nmr-compile`, `nmr-fmt`, `nmr-vitest`, and `ensure-prepublish-hooks`
|
|
321
328
|
- [Shared Vitest config](docs/vitest.md): Test tiers, test selections, the conventions check, and customization
|
|
322
329
|
- [Shared Prettier config](docs/prettier.md): What it formats and how to customize it
|
|
323
330
|
- [Workspace introspection](docs/workspace.md): The two workspace lookups
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
try {
|
|
3
|
+
await import('../dist/esm/cli-actionlint.js');
|
|
4
|
+
} catch (error) {
|
|
5
|
+
if (error.code === 'ERR_MODULE_NOT_FOUND') {
|
|
6
|
+
process.stderr.write('nmr-actionlint: build output not found; run `pnpm run bootstrap` first\n');
|
|
7
|
+
} else {
|
|
8
|
+
process.stderr.write(`nmr-actionlint: failed to load: ${error.message}\n`);
|
|
9
|
+
}
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
|
-
import type
|
|
2
|
+
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
3
3
|
import { type TreeSnapshot } from './check-cache.js';
|
|
4
4
|
import { type RunPlan } from './invocation.js';
|
|
5
5
|
import type { NmrConfig } from './types.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { startTimer } from '@williamthorsen/nmr-core';
|
|
1
2
|
import { formatMisplacedNoCacheWarning, isCacheableCommand, resolveTreeSnapshot, writeDebugNote, } from "./check-cache.js";
|
|
2
3
|
import { findStaleBuildOutput, readBuildOutputState } from "./check-cache-build-output.js";
|
|
3
4
|
import { recordDelegatedPass } from "./check-cache-delegation.js";
|
|
@@ -10,6 +11,7 @@ import { VERSION } from "./invocation.js";
|
|
|
10
11
|
import { resolveChannel } from "./output-channel.js";
|
|
11
12
|
import { writePassManifestEntry } from "./pass-manifest.js";
|
|
12
13
|
import { runSteps } from "./runner.js";
|
|
14
|
+
import { splitSegments } from "./shell-syntax.js";
|
|
13
15
|
export function openGate(options) {
|
|
14
16
|
const { command, config, env, monorepoRoot, passthrough, stderr, style } = options;
|
|
15
17
|
if (!isCacheableCommand(config.checkCache, command)) {
|
|
@@ -83,9 +85,9 @@ export async function runGated(plan) {
|
|
|
83
85
|
stdout.write(plan.overrideNotice);
|
|
84
86
|
}
|
|
85
87
|
const runOptions = { ...plan.runOptions, env: addLockEnv(plan.runOptions.env ?? env, lock) };
|
|
86
|
-
const
|
|
87
|
-
const { exitCode, retainedOutput } = await runSteps(plan.steps, anchorDir, runOptions);
|
|
88
|
-
const durationMs =
|
|
88
|
+
const readElapsedMs = startTimer();
|
|
89
|
+
const { exitCode, retainedOutput, statedOutcomes } = await runSteps(plan.steps, anchorDir, runOptions);
|
|
90
|
+
const durationMs = readElapsedMs();
|
|
89
91
|
if (exitCode === 0 && gate !== undefined) {
|
|
90
92
|
await recordPass({
|
|
91
93
|
anchorDir,
|
|
@@ -105,9 +107,12 @@ export async function runGated(plan) {
|
|
|
105
107
|
stderr,
|
|
106
108
|
});
|
|
107
109
|
}
|
|
110
|
+
const stated = readOwnStatement(plan.ownSteps, statedOutcomes);
|
|
108
111
|
return {
|
|
109
112
|
exitCode,
|
|
110
|
-
outcome: exitCode === 0
|
|
113
|
+
outcome: exitCode === 0
|
|
114
|
+
? { outcome: 'passed', durationMs, ...(stated !== undefined && { stated }) }
|
|
115
|
+
: { outcome: 'failed', durationMs, exitCode },
|
|
111
116
|
};
|
|
112
117
|
}
|
|
113
118
|
finally {
|
|
@@ -168,6 +173,13 @@ async function readGate(plan) {
|
|
|
168
173
|
snapshot,
|
|
169
174
|
};
|
|
170
175
|
}
|
|
176
|
+
function readOwnStatement(ownSteps, statedOutcomes) {
|
|
177
|
+
const [ownStep] = ownSteps;
|
|
178
|
+
if (ownSteps.length !== 1 || ownStep?.kind !== 'opaque' || splitSegments(ownStep.command).length !== 1) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
return statedOutcomes?.get(ownStep);
|
|
182
|
+
}
|
|
171
183
|
async function recallPass(plan, gate) {
|
|
172
184
|
const { anchorDir, command, context, env, stderr } = plan.invocation;
|
|
173
185
|
const { monorepoRoot } = context;
|
package/dist/esm/check-cache.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { reportError } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
|
+
import { runActionlint } from "./commands/actionlint.js";
|
|
5
|
+
import { findMonorepoRoot } from "./workspace.js";
|
|
6
|
+
try {
|
|
7
|
+
process.exitCode = await runActionlint(findMonorepoRoot(), process.argv.slice(2));
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
reportError(describeError(error));
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type WorkflowTool } from './workflow-tools.js';
|
|
2
|
+
/** How a spawned actionlint ended. */
|
|
3
|
+
export interface ActionlintOutcome {
|
|
4
|
+
error?: Error | undefined;
|
|
5
|
+
signal: NodeJS.Signals | null;
|
|
6
|
+
status: number | null;
|
|
7
|
+
}
|
|
8
|
+
export interface RunActionlintOptions {
|
|
9
|
+
/** Directory under which the pinned tools are cached; defaults to `node_modules/.cache/nmr-tools` at the root. */
|
|
10
|
+
cacheRoot?: string;
|
|
11
|
+
ensure?: (tool: WorkflowTool, cacheRoot: string) => Promise<string>;
|
|
12
|
+
spawn?: (file: string, args: string[], cwd: string) => ActionlintOutcome;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Lints the monorepo's GitHub Actions workflows with the pinned actionlint, running its shellcheck pass with the
|
|
16
|
+
* pinned shellcheck, and returns actionlint's exit status. A repo without a workflows directory passes.
|
|
17
|
+
*/
|
|
18
|
+
export declare function runActionlint(monorepoRoot: string, args: readonly string[], options?: RunActionlintOptions): Promise<number>;
|
|
19
|
+
/**
|
|
20
|
+
* Builds actionlint's arguments. The pyflakes integration is disabled so that the result does not depend on whether
|
|
21
|
+
* the machine happens to have pyflakes on `PATH`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildActionlintArgs(shellcheckPath: string, args: readonly string[]): string[];
|