@produck/agent-toolkit 0.9.2 → 0.11.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/bin/agent-toolkit.mjs +16 -0
- package/bin/build-publish-assets.mjs +28 -6
- package/bin/command/enforce-node-baseline/index.mjs +11 -0
- package/bin/command/main/help.txt +4 -2
- package/bin/command/sync-coverage/help.txt +6 -21
- package/bin/command/sync-coverage/index.mjs +31 -200
- package/bin/command/sync-coverage/required-c8-config.json +10 -5
- package/bin/command/sync-format/index.mjs +30 -3
- package/bin/command/sync-git/index.mjs +31 -2
- package/bin/command/sync-lint/index.mjs +29 -7
- package/bin/command/sync-publish/help.txt +1 -1
- package/bin/command/sync-publish/index.mjs +38 -15
- package/bin/command/sync-typescript/help.txt +14 -0
- package/bin/command/sync-typescript/index.mjs +154 -0
- package/bin/command/sync-workspace/help.txt +22 -0
- package/bin/command/sync-workspace/index.mjs +379 -0
- package/package.json +21 -21
- package/publish-assets/gitignore +3 -0
- package/publish-assets/instructions/produck/00-produck-base.instructions.md +3 -3
- package/publish-assets/instructions/produck/10-produck-node.instructions.md +151 -118
- package/publish-assets/instructions/produck/12-produck-test.instructions.md +92 -0
- package/publish-assets/instructions/produck/15-produck-workspace.instructions.md +73 -39
- package/publish-assets/instructions/produck/20-produck-commit.instructions.md +12 -0
- package/publish-assets/instructions/produck/tooling-version-baseline.json +53 -48
- package/publish-assets/lerna.json +3 -3
- package/publish-assets/prettierrc +9 -1
package/bin/agent-toolkit.mjs
CHANGED
|
@@ -42,6 +42,14 @@ import {
|
|
|
42
42
|
printSyncPublishHelp,
|
|
43
43
|
runSyncPublish,
|
|
44
44
|
} from './command/sync-publish/index.mjs';
|
|
45
|
+
import {
|
|
46
|
+
printSyncTypescriptHelp,
|
|
47
|
+
runSyncTypescript,
|
|
48
|
+
} from './command/sync-typescript/index.mjs';
|
|
49
|
+
import {
|
|
50
|
+
printSyncWorkspaceHelp,
|
|
51
|
+
runSyncWorkspace,
|
|
52
|
+
} from './command/sync-workspace/index.mjs';
|
|
45
53
|
import { hasFlag, parseCommonArgs } from './command/shared/args.mjs';
|
|
46
54
|
import {
|
|
47
55
|
printValidateCommitMsgHelp,
|
|
@@ -101,6 +109,14 @@ const COMMANDS = {
|
|
|
101
109
|
printHelp: printSyncPublishHelp,
|
|
102
110
|
run: runSyncPublish,
|
|
103
111
|
},
|
|
112
|
+
'sync-typescript': {
|
|
113
|
+
printHelp: printSyncTypescriptHelp,
|
|
114
|
+
run: runSyncTypescript,
|
|
115
|
+
},
|
|
116
|
+
'sync-workspace': {
|
|
117
|
+
printHelp: printSyncWorkspaceHelp,
|
|
118
|
+
run: runSyncWorkspace,
|
|
119
|
+
},
|
|
104
120
|
};
|
|
105
121
|
|
|
106
122
|
const DEFAULT_COMMAND = 'enforce-node-baseline';
|
|
@@ -49,6 +49,7 @@ const OUTPUT_LERNA_PATH = path.resolve(
|
|
|
49
49
|
PACKAGE_ROOT,
|
|
50
50
|
'publish-assets/lerna.json',
|
|
51
51
|
);
|
|
52
|
+
const ROOT_PACKAGE_JSON_PATH = path.resolve(REPO_ROOT, 'package.json');
|
|
52
53
|
const LEGACY_OUTPUT_PATH = path.resolve(
|
|
53
54
|
PACKAGE_ROOT,
|
|
54
55
|
'publish-assets/instructions/org.instructions.md',
|
|
@@ -80,6 +81,10 @@ function validateSourceFile(fileName, text) {
|
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
function resolveSemverExact(text) {
|
|
85
|
+
return text.replace(/^[\^~>=<]+\s*/, '').trim();
|
|
86
|
+
}
|
|
87
|
+
|
|
83
88
|
function readAndValidateToolingBaseline() {
|
|
84
89
|
if (!fs.existsSync(SOURCE_TOOLING_BASELINE_PATH)) {
|
|
85
90
|
throw new Error(
|
|
@@ -98,25 +103,40 @@ function readAndValidateToolingBaseline() {
|
|
|
98
103
|
);
|
|
99
104
|
}
|
|
100
105
|
|
|
101
|
-
const c8Version = baseline?.tools?.c8?.version;
|
|
102
|
-
const lernaVersion = baseline?.tools?.lerna?.version;
|
|
103
|
-
const coverageScriptTemplate = baseline?.coverage?.scriptTemplate;
|
|
104
|
-
|
|
105
106
|
if (typeof baseline.schemaVersion !== 'number') {
|
|
106
107
|
throw new Error(
|
|
107
108
|
`Invalid tooling baseline schemaVersion in: ${SOURCE_TOOLING_BASELINE_PATH}`,
|
|
108
109
|
);
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
// Auto-resolve tool versions: only override version when set to "auto"
|
|
113
|
+
const rootPkg = JSON.parse(fs.readFileSync(ROOT_PACKAGE_JSON_PATH, 'utf8'));
|
|
114
|
+
const devDeps = rootPkg.devDependencies || {};
|
|
115
|
+
|
|
116
|
+
for (const [toolName, entry] of Object.entries(baseline.tools)) {
|
|
117
|
+
if (entry.version === 'auto') {
|
|
118
|
+
const depVersion = devDeps[toolName];
|
|
119
|
+
if (typeof depVersion === 'string' && depVersion.trim()) {
|
|
120
|
+
entry.version = resolveSemverExact(depVersion);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const c8Version = baseline?.tools?.c8?.version;
|
|
126
|
+
const lernaVersion = baseline?.tools?.lerna?.version;
|
|
127
|
+
const coverageScriptTemplate = baseline?.coverage?.scriptTemplate;
|
|
128
|
+
|
|
111
129
|
if (typeof c8Version !== 'string' || c8Version.trim() === '') {
|
|
112
130
|
throw new Error(
|
|
113
|
-
`Invalid tools.c8.version in: ${SOURCE_TOOLING_BASELINE_PATH}
|
|
131
|
+
`Invalid tools.c8.version in: ${SOURCE_TOOLING_BASELINE_PATH} ` +
|
|
132
|
+
'(c8 must be listed in root package.json devDependencies)',
|
|
114
133
|
);
|
|
115
134
|
}
|
|
116
135
|
|
|
117
136
|
if (typeof lernaVersion !== 'string' || lernaVersion.trim() === '') {
|
|
118
137
|
throw new Error(
|
|
119
|
-
`Invalid tools.lerna.version in: ${SOURCE_TOOLING_BASELINE_PATH}
|
|
138
|
+
`Invalid tools.lerna.version in: ${SOURCE_TOOLING_BASELINE_PATH} ` +
|
|
139
|
+
'(lerna must be listed in root package.json devDependencies)',
|
|
120
140
|
);
|
|
121
141
|
}
|
|
122
142
|
|
|
@@ -129,6 +149,7 @@ function readAndValidateToolingBaseline() {
|
|
|
129
149
|
);
|
|
130
150
|
}
|
|
131
151
|
|
|
152
|
+
// Dynamically inject @produck/eslint-rules version from its own package.json
|
|
132
153
|
const eslintRulesPkgPath = path.resolve(
|
|
133
154
|
PACKAGE_ROOT,
|
|
134
155
|
'../eslint-rules/package.json',
|
|
@@ -146,6 +167,7 @@ function readAndValidateToolingBaseline() {
|
|
|
146
167
|
};
|
|
147
168
|
}
|
|
148
169
|
}
|
|
170
|
+
|
|
149
171
|
return `${JSON.stringify(baseline, null, 2)}\n`;
|
|
150
172
|
}
|
|
151
173
|
|
|
@@ -168,6 +168,16 @@ export function runEnforceNodeBaseline(options) {
|
|
|
168
168
|
syncEslintConfigArgs.push('--dry-run');
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
const syncWorkspaceArgs = ['sync-workspace', '--cwd', cwd];
|
|
172
|
+
for (const workspacePath of workspaces) {
|
|
173
|
+
syncWorkspaceArgs.push('--workspace', workspacePath);
|
|
174
|
+
}
|
|
175
|
+
if (check) {
|
|
176
|
+
syncWorkspaceArgs.push('--check');
|
|
177
|
+
} else if (dryRun) {
|
|
178
|
+
syncWorkspaceArgs.push('--dry-run');
|
|
179
|
+
}
|
|
180
|
+
|
|
171
181
|
const plan = [
|
|
172
182
|
{ name: 'preflight', args: preflightArgs },
|
|
173
183
|
{ name: 'sync-instructions', args: syncInstructionsArgs },
|
|
@@ -177,6 +187,7 @@ export function runEnforceNodeBaseline(options) {
|
|
|
177
187
|
{ name: 'sync-install', args: syncInstallArgs },
|
|
178
188
|
{ name: 'sync-git', args: syncGitArgs },
|
|
179
189
|
{ name: 'sync-coverage', args: syncCoverageArgs },
|
|
190
|
+
{ name: 'sync-workspace', args: syncWorkspaceArgs },
|
|
180
191
|
{ name: 'sync-publish', args: syncPublishArgs },
|
|
181
192
|
];
|
|
182
193
|
|
|
@@ -5,13 +5,15 @@ agent-toolkit commands:
|
|
|
5
5
|
summarize-log
|
|
6
6
|
sync-coverage
|
|
7
7
|
sync-editorconfig
|
|
8
|
-
sync-git
|
|
9
8
|
sync-format
|
|
9
|
+
sync-git
|
|
10
10
|
sync-install
|
|
11
|
+
sync-instructions
|
|
11
12
|
sync-lint
|
|
12
13
|
sync-publish
|
|
14
|
+
sync-typescript
|
|
15
|
+
sync-workspace
|
|
13
16
|
validate-commit-msg
|
|
14
|
-
sync-instructions
|
|
15
17
|
|
|
16
18
|
Default:
|
|
17
19
|
agent-toolkit
|
|
@@ -1,30 +1,15 @@
|
|
|
1
1
|
Usage:
|
|
2
2
|
agent-toolkit sync-coverage [--cwd <dir>]
|
|
3
|
-
[--
|
|
3
|
+
[--check] [--dry-run] [--json <file>]
|
|
4
4
|
|
|
5
5
|
Behavior:
|
|
6
|
-
- Adds or updates root scripts.produck:coverage, root .c8rc.json,
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
node -e "console.log('No tests configured')"
|
|
11
|
-
- The produck:coverage script in subpackages is for local and AI development use only.
|
|
12
|
-
- Subpackage produck:coverage is NOT enforced by organization CI or .c8rc.json.
|
|
13
|
-
- Only the root workspace (monorepo root) is subject to org-level coverage enforcement and .c8rc.json.
|
|
6
|
+
- Adds or updates root scripts.produck:coverage, root .c8rc.json,
|
|
7
|
+
and root devDependencies.c8.
|
|
8
|
+
- Only the root workspace (monorepo root) is subject to org-level
|
|
9
|
+
coverage enforcement and .c8rc.json.
|
|
14
10
|
- Organization-reserved script key is scripts.produck:coverage
|
|
15
|
-
-
|
|
16
|
-
(lookup order):
|
|
17
|
-
1) .github/distribution/produck/tooling-version-baseline.json
|
|
18
|
-
2) publish-assets/instructions/produck/tooling-version-baseline.json
|
|
19
|
-
- Baseline template:
|
|
20
|
-
c8 --reporter=lcov --reporter=html --reporter=text-summary npm test
|
|
11
|
+
- c8 version is resolved from organization tooling baseline file
|
|
21
12
|
|
|
22
13
|
Rules:
|
|
23
|
-
- When --workspace is omitted, root package.json workspaces are used
|
|
24
|
-
- Root workspaces must be explicit paths (no glob tokens)
|
|
25
|
-
- Workspace package.json files must include scripts.test
|
|
26
|
-
- Workspace package.json files must pin devDependencies.c8 to baseline version
|
|
27
|
-
- Subpackage produck:coverage is not a CI or org gate; it is for local/dev use only
|
|
28
|
-
- Only the root workspace is enforced by org CI and .c8rc.json
|
|
29
14
|
- --check validates without writing and exits non-zero on mismatch
|
|
30
15
|
- --dry-run prints planned changes without writing
|
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { getSingle, hasFlag } from '../shared/args.mjs';
|
|
6
6
|
import { printTextResource } from '../shared/text-resource.mjs';
|
|
7
7
|
|
|
8
8
|
const COMMAND_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -19,7 +19,6 @@ const TOOLING_BASELINE_CANDIDATE_PATHS = [
|
|
|
19
19
|
'publish-assets/instructions/produck/tooling-version-baseline.json',
|
|
20
20
|
),
|
|
21
21
|
];
|
|
22
|
-
const GLOB_TOKEN_PATTERN = /[*?{}[\]]/;
|
|
23
22
|
const REQUIRED_ROOT_COVERAGE_SCRIPT_KEY = 'produck:coverage';
|
|
24
23
|
const REQUIRED_ROOT_COVERAGE_SCRIPT_VALUE = [
|
|
25
24
|
'c8',
|
|
@@ -28,10 +27,6 @@ const REQUIRED_ROOT_COVERAGE_SCRIPT_VALUE = [
|
|
|
28
27
|
'--workspaces',
|
|
29
28
|
'--if-present',
|
|
30
29
|
].join(' ');
|
|
31
|
-
const REQUIRED_COVERAGE_SCRIPT_KEY = 'produck:coverage';
|
|
32
|
-
const REQUIRED_TEST_SCRIPT_KEY = 'test';
|
|
33
|
-
const DEFAULT_TEST_SCRIPT_VALUE =
|
|
34
|
-
'node -e "console.log(\'No tests configured\')"';
|
|
35
30
|
const REQUIRED_C8_CONFIG_FILE = '.c8rc.json';
|
|
36
31
|
const REQUIRED_C8_CONFIG_TEMPLATE_FILE = path.resolve(
|
|
37
32
|
COMMAND_DIR,
|
|
@@ -51,6 +46,35 @@ function parseJsonFile(filePath, label) {
|
|
|
51
46
|
}
|
|
52
47
|
}
|
|
53
48
|
|
|
49
|
+
function resolveSemverExact(text) {
|
|
50
|
+
return text.replace(/^[\^~>=<]+\s*/, '').trim();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveToolVersionFromDevDeps(baseline, toolName) {
|
|
54
|
+
// If baseline has a concrete version (not "auto"), use it directly.
|
|
55
|
+
// This is the case when reading the published publish-assets baseline.
|
|
56
|
+
const baselineVersion = String(
|
|
57
|
+
baseline?.tools?.[toolName]?.version || '',
|
|
58
|
+
).trim();
|
|
59
|
+
if (baselineVersion && baselineVersion !== 'auto') {
|
|
60
|
+
return baselineVersion;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Fall back to resolving from local root package.json devDependencies.
|
|
64
|
+
// This covers source baseline with version="auto" during local dev.
|
|
65
|
+
const repoRoot = path.resolve(PACKAGE_ROOT, '../..');
|
|
66
|
+
const pkgJsonPath = path.resolve(repoRoot, 'package.json');
|
|
67
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
68
|
+
const pkg = parseJsonFile(pkgJsonPath, 'root package.json');
|
|
69
|
+
const dep = pkg?.devDependencies?.[toolName];
|
|
70
|
+
if (typeof dep === 'string' && dep.trim()) {
|
|
71
|
+
return resolveSemverExact(dep);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return '';
|
|
76
|
+
}
|
|
77
|
+
|
|
54
78
|
function loadToolingBaseline() {
|
|
55
79
|
const toolingBaselinePath = TOOLING_BASELINE_CANDIDATE_PATHS.find(
|
|
56
80
|
(candidatePath) => {
|
|
@@ -76,7 +100,7 @@ function loadToolingBaseline() {
|
|
|
76
100
|
process.exit(2);
|
|
77
101
|
}
|
|
78
102
|
|
|
79
|
-
const c8Version = baseline
|
|
103
|
+
const c8Version = resolveToolVersionFromDevDeps(baseline, 'c8');
|
|
80
104
|
if (typeof c8Version !== 'string' || c8Version.trim() === '') {
|
|
81
105
|
console.error(
|
|
82
106
|
`Tooling baseline tools.c8.version must be a non-empty string: ${toolingBaselinePath}`,
|
|
@@ -98,12 +122,6 @@ function loadToolingBaseline() {
|
|
|
98
122
|
};
|
|
99
123
|
}
|
|
100
124
|
|
|
101
|
-
function buildRequiredCoverageScript(baseline) {
|
|
102
|
-
const c8Version = String(baseline.tools.c8.version);
|
|
103
|
-
const coverageTemplate = String(baseline.coverage.scriptTemplate);
|
|
104
|
-
return coverageTemplate.replace(/\{c8\.version\}/g, c8Version);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
125
|
function buildRequiredC8DevDependency(baseline) {
|
|
108
126
|
return String(baseline.tools.c8.version);
|
|
109
127
|
}
|
|
@@ -131,37 +149,6 @@ function loadRequiredC8ConfigContent() {
|
|
|
131
149
|
return `${JSON.stringify(template, null, 2)}\n`;
|
|
132
150
|
}
|
|
133
151
|
|
|
134
|
-
function resolveWorkspacePaths(cwd, options) {
|
|
135
|
-
const manual = getMulti(options, '--workspace');
|
|
136
|
-
if (manual.length > 0) {
|
|
137
|
-
return manual;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const rootPackageJsonPath = path.resolve(cwd, 'package.json');
|
|
141
|
-
const rootPackageJson = parseJsonFile(
|
|
142
|
-
rootPackageJsonPath,
|
|
143
|
-
'Root package.json',
|
|
144
|
-
);
|
|
145
|
-
if (!Array.isArray(rootPackageJson.workspaces)) {
|
|
146
|
-
return [];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const workspaces = rootPackageJson.workspaces.map((entry) => String(entry));
|
|
150
|
-
if (workspaces.length === 0) {
|
|
151
|
-
return [];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const hasGlob = workspaces.some((entry) => GLOB_TOKEN_PATTERN.test(entry));
|
|
155
|
-
if (hasGlob) {
|
|
156
|
-
console.error(
|
|
157
|
-
'Root package.json `workspaces` must use explicit paths without glob tokens',
|
|
158
|
-
);
|
|
159
|
-
process.exit(2);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return workspaces;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
152
|
function syncRootCoverage(
|
|
166
153
|
cwd,
|
|
167
154
|
mode,
|
|
@@ -245,130 +232,6 @@ function syncRootCoverage(
|
|
|
245
232
|
};
|
|
246
233
|
}
|
|
247
234
|
|
|
248
|
-
function reconcileCoverageScript(
|
|
249
|
-
cwd,
|
|
250
|
-
workspacePath,
|
|
251
|
-
mode,
|
|
252
|
-
requiredCoverageScript,
|
|
253
|
-
requiredC8Version,
|
|
254
|
-
) {
|
|
255
|
-
const packageDir = path.resolve(cwd, workspacePath);
|
|
256
|
-
const packageJsonPath = path.resolve(packageDir, 'package.json');
|
|
257
|
-
|
|
258
|
-
const result = {
|
|
259
|
-
workspacePath,
|
|
260
|
-
packageDir,
|
|
261
|
-
packageJsonPath,
|
|
262
|
-
exists: false,
|
|
263
|
-
validJson: false,
|
|
264
|
-
previousCoverage: null,
|
|
265
|
-
coverageScript: null,
|
|
266
|
-
previousTestScript: null,
|
|
267
|
-
testScript: null,
|
|
268
|
-
previousC8DevDependency: null,
|
|
269
|
-
c8DevDependency: null,
|
|
270
|
-
matchesRequiredCoverageBefore: false,
|
|
271
|
-
matchesRequiredCoverageAfter: false,
|
|
272
|
-
hasRequiredTestScriptBefore: false,
|
|
273
|
-
hasRequiredTestScriptAfter: false,
|
|
274
|
-
matchesRequiredC8DevDependencyBefore: false,
|
|
275
|
-
matchesRequiredC8DevDependencyAfter: false,
|
|
276
|
-
updated: false,
|
|
277
|
-
error: '',
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
281
|
-
result.error = `Workspace package.json does not exist: ${workspacePath}`;
|
|
282
|
-
return result;
|
|
283
|
-
}
|
|
284
|
-
result.exists = true;
|
|
285
|
-
|
|
286
|
-
let pkg;
|
|
287
|
-
try {
|
|
288
|
-
pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
289
|
-
result.validJson = true;
|
|
290
|
-
} catch {
|
|
291
|
-
result.error = `Workspace package.json is not valid JSON: ${workspacePath}`;
|
|
292
|
-
return result;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const scripts =
|
|
296
|
-
pkg.scripts &&
|
|
297
|
-
typeof pkg.scripts === 'object' &&
|
|
298
|
-
!Array.isArray(pkg.scripts)
|
|
299
|
-
? { ...pkg.scripts }
|
|
300
|
-
: {};
|
|
301
|
-
const devDependencies =
|
|
302
|
-
pkg.devDependencies &&
|
|
303
|
-
typeof pkg.devDependencies === 'object' &&
|
|
304
|
-
!Array.isArray(pkg.devDependencies)
|
|
305
|
-
? { ...pkg.devDependencies }
|
|
306
|
-
: {};
|
|
307
|
-
|
|
308
|
-
const previousCoverage =
|
|
309
|
-
typeof scripts[REQUIRED_COVERAGE_SCRIPT_KEY] === 'string'
|
|
310
|
-
? scripts[REQUIRED_COVERAGE_SCRIPT_KEY]
|
|
311
|
-
: null;
|
|
312
|
-
const previousTestScript =
|
|
313
|
-
typeof scripts[REQUIRED_TEST_SCRIPT_KEY] === 'string' &&
|
|
314
|
-
scripts[REQUIRED_TEST_SCRIPT_KEY].trim() !== ''
|
|
315
|
-
? scripts[REQUIRED_TEST_SCRIPT_KEY]
|
|
316
|
-
: null;
|
|
317
|
-
const previousC8DevDependency =
|
|
318
|
-
typeof devDependencies.c8 === 'string' ? devDependencies.c8 : null;
|
|
319
|
-
result.previousCoverage = previousCoverage;
|
|
320
|
-
result.previousTestScript = previousTestScript;
|
|
321
|
-
result.previousC8DevDependency = previousC8DevDependency;
|
|
322
|
-
result.matchesRequiredCoverageBefore =
|
|
323
|
-
previousCoverage === requiredCoverageScript;
|
|
324
|
-
result.hasRequiredTestScriptBefore = previousTestScript !== null;
|
|
325
|
-
result.matchesRequiredC8DevDependencyBefore =
|
|
326
|
-
previousC8DevDependency === requiredC8Version;
|
|
327
|
-
|
|
328
|
-
if (
|
|
329
|
-
(!result.matchesRequiredCoverageBefore ||
|
|
330
|
-
!result.hasRequiredTestScriptBefore ||
|
|
331
|
-
!result.matchesRequiredC8DevDependencyBefore) &&
|
|
332
|
-
mode === 'sync'
|
|
333
|
-
) {
|
|
334
|
-
scripts[REQUIRED_COVERAGE_SCRIPT_KEY] = requiredCoverageScript;
|
|
335
|
-
if (!result.hasRequiredTestScriptBefore) {
|
|
336
|
-
scripts[REQUIRED_TEST_SCRIPT_KEY] = DEFAULT_TEST_SCRIPT_VALUE;
|
|
337
|
-
}
|
|
338
|
-
devDependencies.c8 = requiredC8Version;
|
|
339
|
-
pkg.scripts = scripts;
|
|
340
|
-
pkg.devDependencies = devDependencies;
|
|
341
|
-
fs.writeFileSync(
|
|
342
|
-
packageJsonPath,
|
|
343
|
-
`${JSON.stringify(pkg, null, 2)}\n`,
|
|
344
|
-
'utf8',
|
|
345
|
-
);
|
|
346
|
-
result.updated = true;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
result.coverageScript =
|
|
350
|
-
mode === 'sync' && !result.matchesRequiredCoverageBefore
|
|
351
|
-
? requiredCoverageScript
|
|
352
|
-
: previousCoverage;
|
|
353
|
-
result.testScript =
|
|
354
|
-
mode === 'sync' && !result.hasRequiredTestScriptBefore
|
|
355
|
-
? DEFAULT_TEST_SCRIPT_VALUE
|
|
356
|
-
: previousTestScript;
|
|
357
|
-
result.c8DevDependency =
|
|
358
|
-
mode === 'sync' && !result.matchesRequiredC8DevDependencyBefore
|
|
359
|
-
? requiredC8Version
|
|
360
|
-
: previousC8DevDependency;
|
|
361
|
-
|
|
362
|
-
result.matchesRequiredCoverageAfter =
|
|
363
|
-
result.updated || result.matchesRequiredCoverageBefore;
|
|
364
|
-
result.hasRequiredTestScriptAfter =
|
|
365
|
-
(mode === 'sync' && !result.hasRequiredTestScriptBefore) ||
|
|
366
|
-
result.hasRequiredTestScriptBefore;
|
|
367
|
-
result.matchesRequiredC8DevDependencyAfter =
|
|
368
|
-
result.updated || result.matchesRequiredC8DevDependencyBefore;
|
|
369
|
-
return result;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
235
|
export function runSyncCoverage(options) {
|
|
373
236
|
const cwd = path.resolve(getSingle(options, '--cwd', process.cwd()));
|
|
374
237
|
const check = hasFlag(options, '--check');
|
|
@@ -376,7 +239,6 @@ export function runSyncCoverage(options) {
|
|
|
376
239
|
const jsonFile = getSingle(options, '--json', '');
|
|
377
240
|
const { baseline: toolingBaseline, toolingBaselinePath } =
|
|
378
241
|
loadToolingBaseline();
|
|
379
|
-
const requiredCoverageScript = buildRequiredCoverageScript(toolingBaseline);
|
|
380
242
|
const requiredC8Version = buildRequiredC8DevDependency(toolingBaseline);
|
|
381
243
|
const requiredC8ConfigContent = loadRequiredC8ConfigContent();
|
|
382
244
|
|
|
@@ -392,7 +254,6 @@ export function runSyncCoverage(options) {
|
|
|
392
254
|
requiredC8Version,
|
|
393
255
|
requiredC8ConfigContent,
|
|
394
256
|
);
|
|
395
|
-
const workspacePaths = resolveWorkspacePaths(cwd, options);
|
|
396
257
|
|
|
397
258
|
const report = {
|
|
398
259
|
cwd,
|
|
@@ -402,12 +263,8 @@ export function runSyncCoverage(options) {
|
|
|
402
263
|
schemaVersion: toolingBaseline.schemaVersion,
|
|
403
264
|
c8Version: toolingBaseline.tools.c8.version,
|
|
404
265
|
},
|
|
405
|
-
requiredCoverageScript,
|
|
406
|
-
requiredTestScript: DEFAULT_TEST_SCRIPT_VALUE,
|
|
407
266
|
requiredC8DevDependency: requiredC8Version,
|
|
408
267
|
root,
|
|
409
|
-
workspaces: workspacePaths,
|
|
410
|
-
results: [],
|
|
411
268
|
ok: true,
|
|
412
269
|
};
|
|
413
270
|
|
|
@@ -420,32 +277,6 @@ export function runSyncCoverage(options) {
|
|
|
420
277
|
report.ok = false;
|
|
421
278
|
}
|
|
422
279
|
|
|
423
|
-
for (const workspacePath of workspacePaths) {
|
|
424
|
-
const effectiveMode = mode === 'sync' ? 'sync' : 'check';
|
|
425
|
-
const item = reconcileCoverageScript(
|
|
426
|
-
cwd,
|
|
427
|
-
workspacePath,
|
|
428
|
-
effectiveMode,
|
|
429
|
-
requiredCoverageScript,
|
|
430
|
-
requiredC8Version,
|
|
431
|
-
);
|
|
432
|
-
report.results.push(item);
|
|
433
|
-
|
|
434
|
-
if (item.error) {
|
|
435
|
-
report.ok = false;
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (
|
|
440
|
-
mode === 'check' &&
|
|
441
|
-
(!item.matchesRequiredCoverageAfter ||
|
|
442
|
-
!item.hasRequiredTestScriptAfter ||
|
|
443
|
-
!item.matchesRequiredC8DevDependencyAfter)
|
|
444
|
-
) {
|
|
445
|
-
report.ok = false;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
280
|
if (jsonFile) {
|
|
450
281
|
const outPath = path.resolve(cwd, jsonFile);
|
|
451
282
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
+
"branches": 99.5,
|
|
2
3
|
"check-coverage": true,
|
|
3
4
|
"exclude": [
|
|
4
5
|
"**/node_modules/**",
|
|
5
6
|
"**/coverage/**",
|
|
6
7
|
"**/dist/**",
|
|
7
8
|
"**/build/**",
|
|
8
|
-
"**/out/**"
|
|
9
|
+
"**/out/**",
|
|
10
|
+
"**/test/**"
|
|
9
11
|
],
|
|
10
|
-
"reporter": ["lcov", "html", "text-summary"],
|
|
11
|
-
"branches": 99.5,
|
|
12
12
|
"functions": 99.5,
|
|
13
|
-
"
|
|
14
|
-
"
|
|
13
|
+
"lines": 99.5,
|
|
14
|
+
"reporter": [
|
|
15
|
+
"lcov",
|
|
16
|
+
"html",
|
|
17
|
+
"text-summary"
|
|
18
|
+
],
|
|
19
|
+
"statements": 99.5
|
|
15
20
|
}
|
|
@@ -99,6 +99,35 @@ function parseJsonFile(filePath, label) {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function resolveSemverExact(text) {
|
|
103
|
+
return text.replace(/^[\^~>=<]+\s*/, '').trim();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function resolveToolVersionFromDevDeps(baseline, toolName) {
|
|
107
|
+
// If baseline has a concrete version (not "auto"), use it directly.
|
|
108
|
+
// This is the case when reading the published publish-assets baseline.
|
|
109
|
+
const baselineVersion = String(
|
|
110
|
+
baseline?.tools?.[toolName]?.version || '',
|
|
111
|
+
).trim();
|
|
112
|
+
if (baselineVersion && baselineVersion !== 'auto') {
|
|
113
|
+
return baselineVersion;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Fall back to resolving from local root package.json devDependencies.
|
|
117
|
+
// This covers source baseline with version="auto" during local dev.
|
|
118
|
+
const repoRoot = path.resolve(PACKAGE_ROOT, '../..');
|
|
119
|
+
const pkgJsonPath = path.resolve(repoRoot, 'package.json');
|
|
120
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
121
|
+
const pkg = parseJsonFile(pkgJsonPath, 'root package.json');
|
|
122
|
+
const dep = pkg?.devDependencies?.[toolName];
|
|
123
|
+
if (typeof dep === 'string' && dep.trim()) {
|
|
124
|
+
return resolveSemverExact(dep);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
|
|
102
131
|
function loadToolingBaseline() {
|
|
103
132
|
const toolingBaselinePath = TOOLING_BASELINE_CANDIDATE_PATHS.find(
|
|
104
133
|
(candidatePath) => {
|
|
@@ -117,9 +146,7 @@ function loadToolingBaseline() {
|
|
|
117
146
|
}
|
|
118
147
|
|
|
119
148
|
const baseline = parseJsonFile(toolingBaselinePath, 'Tooling baseline file');
|
|
120
|
-
const prettierVersion =
|
|
121
|
-
baseline?.tools?.prettier?.version || '',
|
|
122
|
-
).trim();
|
|
149
|
+
const prettierVersion = resolveToolVersionFromDevDeps(baseline, 'prettier');
|
|
123
150
|
|
|
124
151
|
if (!prettierVersion) {
|
|
125
152
|
console.error(
|
|
@@ -116,6 +116,35 @@ function getRequiredToolkitDevDependency() {
|
|
|
116
116
|
return version;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
function resolveSemverExact(text) {
|
|
120
|
+
return text.replace(/^[\^~>=<]+\s*/, '').trim();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resolveToolVersionFromDevDeps(baseline, toolName) {
|
|
124
|
+
// If baseline has a concrete version (not "auto"), use it directly.
|
|
125
|
+
// This is the case when reading the published publish-assets baseline.
|
|
126
|
+
const baselineVersion = String(
|
|
127
|
+
baseline?.tools?.[toolName]?.version || '',
|
|
128
|
+
).trim();
|
|
129
|
+
if (baselineVersion && baselineVersion !== 'auto') {
|
|
130
|
+
return baselineVersion;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Fall back to resolving from local root package.json devDependencies.
|
|
134
|
+
// This covers source baseline with version="auto" during local dev.
|
|
135
|
+
const repoRoot = path.resolve(PACKAGE_ROOT, '../..');
|
|
136
|
+
const pkgJsonPath = path.resolve(repoRoot, 'package.json');
|
|
137
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
138
|
+
const pkg = parseJsonFile(pkgJsonPath, 'root package.json');
|
|
139
|
+
const dep = pkg?.devDependencies?.[toolName];
|
|
140
|
+
if (typeof dep === 'string' && dep.trim()) {
|
|
141
|
+
return resolveSemverExact(dep);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return '';
|
|
146
|
+
}
|
|
147
|
+
|
|
119
148
|
function loadToolingBaseline() {
|
|
120
149
|
const toolingBaselinePath = TOOLING_BASELINE_CANDIDATE_PATHS.find(
|
|
121
150
|
(candidatePath) => {
|
|
@@ -134,8 +163,8 @@ function loadToolingBaseline() {
|
|
|
134
163
|
}
|
|
135
164
|
|
|
136
165
|
const baseline = parseJsonFile(toolingBaselinePath, 'Tooling baseline file');
|
|
137
|
-
const huskyVersion =
|
|
138
|
-
const lernaVersion =
|
|
166
|
+
const huskyVersion = resolveToolVersionFromDevDeps(baseline, 'husky');
|
|
167
|
+
const lernaVersion = resolveToolVersionFromDevDeps(baseline, 'lerna');
|
|
139
168
|
|
|
140
169
|
if (!huskyVersion || !lernaVersion) {
|
|
141
170
|
console.error(
|
|
@@ -128,16 +128,38 @@ function getRequiredEslintDevDependencies() {
|
|
|
128
128
|
eslintRulesVersion = v;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
function resolveSemverExact(text) {
|
|
132
|
+
return text.replace(/^[\^~>=<]+\s*/, '').trim();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function resolveToolVersionFromDevDeps(name) {
|
|
136
|
+
// If baseline has a concrete version (not "auto"), use it directly.
|
|
137
|
+
// This is the case when reading the published publish-assets baseline.
|
|
138
|
+
const entry = baseline?.tools?.[name];
|
|
139
|
+
const v = typeof entry?.version === 'string' ? entry.version.trim() : '';
|
|
140
|
+
if (v && v !== 'auto') {
|
|
141
|
+
return v;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Fall back to resolving from local root package.json devDependencies.
|
|
145
|
+
// This covers source baseline with version="auto" during local dev.
|
|
146
|
+
const repoRoot = path.resolve(PACKAGE_ROOT, '../..');
|
|
147
|
+
const pkgJsonPath = path.resolve(repoRoot, 'package.json');
|
|
148
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
149
|
+
const pkg = parseJsonFile(pkgJsonPath, 'root package.json');
|
|
150
|
+
const dep = pkg?.devDependencies?.[name];
|
|
151
|
+
if (typeof dep === 'string' && dep.trim()) {
|
|
152
|
+
return resolveSemverExact(dep);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return '';
|
|
157
|
+
}
|
|
158
|
+
|
|
131
159
|
const deps = { [ESLINT_RULES_PACKAGE_NAME]: eslintRulesVersion };
|
|
132
160
|
|
|
133
161
|
for (const name of ESLINT_TOOLING_PACKAGE_NAMES) {
|
|
134
|
-
const
|
|
135
|
-
const v =
|
|
136
|
-
typeof entry?.version === 'string'
|
|
137
|
-
? entry.version.trim()
|
|
138
|
-
: /* c8 ignore next */
|
|
139
|
-
'';
|
|
140
|
-
/* c8 ignore next 6 */
|
|
162
|
+
const v = resolveToolVersionFromDevDeps(name);
|
|
141
163
|
if (!v) {
|
|
142
164
|
console.error(
|
|
143
165
|
`Tooling baseline tools["${name}"].version must be a non-empty string: ${toolingBaselinePath}`,
|
|
@@ -10,7 +10,7 @@ Behavior:
|
|
|
10
10
|
- Sync mode applies organization-required root publish scripts:
|
|
11
11
|
- scripts.produck:publish:check = npm run produck:install && npm run produck:format && npm run produck:lint && npm run produck:coverage
|
|
12
12
|
- scripts.produck:publish = npm run produck:publish:check && npm run publish --
|
|
13
|
-
|
|
13
|
+
- If scripts.publish is missing, defaults to "lerna publish"
|
|
14
14
|
- Enforces lerna.json command.version.commitHooks = false so publish/version commits skip git commit hooks
|
|
15
15
|
|
|
16
16
|
Rules:
|