fg-devkit 1.12.5 → 1.12.7
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/configs/dependency-policy.vue-workspace.json +40 -34
- package/package.json +25 -9
- package/scaffold/vue/package.json +15 -12
- package/scripts/bump-node-toolchain.mjs +260 -0
- package/scripts/bump-pnpm-toolchain.mjs +65 -28
- package/scripts/check-dependency-policy.mjs +8 -0
- package/scripts/dependency-policy-toolchain.mjs +187 -7
- package/scripts/fg-devkit.mjs +214 -3
- package/scripts/fleet-git-commit-push-scoped.mjs +97 -0
- package/scripts/fleet-git-commit-push.mjs +75 -0
- package/scripts/fleet-git-discard-scoped.mjs +93 -0
- package/scripts/fleet-git-discard.mjs +66 -0
- package/scripts/fleet-git-pull-scoped.mjs +92 -0
- package/scripts/fleet-git-pull.mjs +65 -0
- package/scripts/fleet-project-repos.mjs +173 -0
- package/scripts/fleet-repo-picker.mjs +190 -0
- package/scripts/fleet-vue-repos.mjs +3 -1
- package/scripts/fleet-vulns.mjs +2 -1
- package/scripts/git-commit-push-fleet-common.mjs +147 -0
- package/scripts/git-discard-fleet-common.mjs +100 -0
- package/scripts/git-fleet-common.mjs +120 -0
- package/scripts/git-pull-fleet-common.mjs +90 -0
- package/scripts/node-toolchain-sync.mjs +347 -0
- package/scripts/pnpm-toolchain-sync.mjs +231 -0
- package/scripts/scaffold-vue.mjs +2 -2
- package/scripts/sync-dependency-policy.mjs +47 -15
- package/scripts/sync-node-toolchain.mjs +64 -0
- package/scripts/sync-pnpm-toolchain.mjs +59 -0
- package/scripts/sync-scaffold-vue-deps.mjs +12 -5
- package/scaffold/vue/Dockerfile.local +0 -16
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
collectToolchainPolicyViolations,
|
|
23
23
|
resolveExpectedPackageManager,
|
|
24
24
|
} from './dependency-policy-toolchain.mjs'
|
|
25
|
+
import { findPnpmVersionMismatchesInRepo } from './pnpm-toolchain-sync.mjs'
|
|
26
|
+
import { collectNodeToolchainViolations } from './node-toolchain-sync.mjs'
|
|
25
27
|
import { resolveDependencyPolicyPath } from './resolve-dependency-policy-path.mjs'
|
|
26
28
|
|
|
27
29
|
const cwd = process.cwd()
|
|
@@ -98,6 +100,12 @@ if (expectedPackageManager !== undefined) {
|
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
|
|
103
|
+
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
104
|
+
mismatches.push(...collectNodeToolchainViolations(cwd, policy, fgDevkitRoot))
|
|
105
|
+
if (expectedPackageManager !== undefined) {
|
|
106
|
+
mismatches.push(...findPnpmVersionMismatchesInRepo(cwd, expectedPackageManager))
|
|
107
|
+
}
|
|
108
|
+
|
|
101
109
|
// Intersection: only packages that exist in both policy and consumer are compared.
|
|
102
110
|
for (const [section, dependencies] of Object.entries(policy.managedDependencies ?? {})) {
|
|
103
111
|
const current = pkg[section] ?? {}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared helpers for fleet-wide pnpm / Corepack toolchain policy.
|
|
2
|
+
* Shared helpers for fleet-wide pnpm / Corepack / Node toolchain policy.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import fs from 'node:fs'
|
|
6
6
|
import path from 'node:path'
|
|
7
7
|
|
|
8
8
|
const PNPM_PACKAGE_MANAGER_PATTERN = /^pnpm@\d+\.\d+\.\d+(-[\w.]+)?$/i
|
|
9
|
+
const NODE_NVMRC_PATTERN = /^\d+(\.\d+(\.\d+)?)?$/
|
|
9
10
|
|
|
10
11
|
/** @param {unknown} value */
|
|
11
12
|
export function isValidPnpmPackageManager(value) {
|
|
@@ -67,16 +68,195 @@ export function resolveExpectedPackageManager(policy, fgDevkitRoot) {
|
|
|
67
68
|
* @returns {string[]}
|
|
68
69
|
*/
|
|
69
70
|
export function collectToolchainPolicyViolations(policy, labelPrefix = '[policy] toolchain.') {
|
|
71
|
+
const violations = []
|
|
70
72
|
const packageManager = policy?.toolchain?.packageManager
|
|
71
|
-
if (packageManager
|
|
73
|
+
if (packageManager !== undefined && !isValidPnpmPackageManager(packageManager)) {
|
|
74
|
+
violations.push(
|
|
75
|
+
`${labelPrefix}packageManager: must be pnpm@x.y.z, got ${JSON.stringify(packageManager)}`,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
violations.push(...collectNodeToolchainPolicyViolations(policy, labelPrefix))
|
|
79
|
+
return violations
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {unknown} value
|
|
84
|
+
*/
|
|
85
|
+
export function isValidNodeNvmrc(value) {
|
|
86
|
+
return typeof value === 'string' && NODE_NVMRC_PATTERN.test(value.trim().replace(/^v/, ''))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {string} value
|
|
91
|
+
* @returns {string}
|
|
92
|
+
*/
|
|
93
|
+
export function normalizeNodeVersionLine(value) {
|
|
94
|
+
return value.trim().replace(/^v/, '')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {string} nvmrc
|
|
99
|
+
* @returns {string}
|
|
100
|
+
*/
|
|
101
|
+
export function deriveJenkinsImageTag(nvmrc) {
|
|
102
|
+
return `node${normalizeNodeVersionLine(nvmrc).split('.')[0]}`
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {string} version Bare major or semver (e.g. 22 or 22.13.0).
|
|
107
|
+
* @returns {string}
|
|
108
|
+
*/
|
|
109
|
+
export function formatNodeNvmrc(version) {
|
|
110
|
+
const bare = normalizeNodeVersionLine(version)
|
|
111
|
+
if (!NODE_NVMRC_PATTERN.test(bare)) {
|
|
112
|
+
throw new Error(`Invalid Node nvmrc version: ${JSON.stringify(version)}`)
|
|
113
|
+
}
|
|
114
|
+
return bare
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @typedef {{ nvmrc: string, engines?: string, jenkinsImage?: string, dockerTag?: string }} NodeToolchainPolicy
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Docker image tag after `node:` (e.g. 22-alpine). Bookworm and other variants normalize to policy/default alpine.
|
|
123
|
+
*
|
|
124
|
+
* @param {string} nvmrc
|
|
125
|
+
* @param {string} [dockerTag]
|
|
126
|
+
* @returns {string}
|
|
127
|
+
*/
|
|
128
|
+
export function resolveDockerNodeTag(nvmrc, dockerTag) {
|
|
129
|
+
if (typeof dockerTag === 'string' && dockerTag.trim()) {
|
|
130
|
+
return dockerTag.trim()
|
|
131
|
+
}
|
|
132
|
+
return `${formatNodeNvmrc(nvmrc).split('.')[0]}-alpine`
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @param {string} fgDevkitRoot
|
|
137
|
+
* @returns {NodeToolchainPolicy | undefined}
|
|
138
|
+
*/
|
|
139
|
+
export function readFgDevkitNodePolicy(fgDevkitRoot) {
|
|
140
|
+
const nvmrcPath = path.join(fgDevkitRoot, '.nvmrc')
|
|
141
|
+
if (!fs.existsSync(nvmrcPath)) {
|
|
142
|
+
return undefined
|
|
143
|
+
}
|
|
144
|
+
const nvmrc = formatNodeNvmrc(fs.readFileSync(nvmrcPath, 'utf8'))
|
|
145
|
+
|
|
146
|
+
const pkgPath = path.join(fgDevkitRoot, 'package.json')
|
|
147
|
+
let engines
|
|
148
|
+
if (fs.existsSync(pkgPath)) {
|
|
149
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
150
|
+
if (typeof pkg.engines?.node === 'string') {
|
|
151
|
+
engines = pkg.engines.node
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
nvmrc,
|
|
157
|
+
engines,
|
|
158
|
+
jenkinsImage: deriveJenkinsImageTag(nvmrc),
|
|
159
|
+
dockerTag: resolveDockerNodeTag(nvmrc),
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {Record<string, unknown>} policy
|
|
165
|
+
* @param {string} [fgDevkitRoot]
|
|
166
|
+
* @returns {NodeToolchainPolicy | undefined}
|
|
167
|
+
*/
|
|
168
|
+
export function resolveExpectedNodePolicy(policy, fgDevkitRoot) {
|
|
169
|
+
const fromPolicy = policy?.toolchain?.node
|
|
170
|
+
if (fromPolicy && typeof fromPolicy === 'object' && typeof fromPolicy.nvmrc === 'string') {
|
|
171
|
+
const nvmrc = formatNodeNvmrc(fromPolicy.nvmrc)
|
|
172
|
+
const engines =
|
|
173
|
+
fromPolicy.engines === null || fromPolicy.engines === undefined
|
|
174
|
+
? undefined
|
|
175
|
+
: String(fromPolicy.engines)
|
|
176
|
+
const jenkinsImage =
|
|
177
|
+
typeof fromPolicy.jenkinsImage === 'string'
|
|
178
|
+
? fromPolicy.jenkinsImage
|
|
179
|
+
: deriveJenkinsImageTag(nvmrc)
|
|
180
|
+
const dockerTag =
|
|
181
|
+
typeof fromPolicy.dockerTag === 'string'
|
|
182
|
+
? fromPolicy.dockerTag
|
|
183
|
+
: resolveDockerNodeTag(nvmrc)
|
|
184
|
+
return { nvmrc, engines, jenkinsImage, dockerTag }
|
|
185
|
+
}
|
|
186
|
+
if (fgDevkitRoot) {
|
|
187
|
+
return readFgDevkitNodePolicy(fgDevkitRoot)
|
|
188
|
+
}
|
|
189
|
+
return undefined
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @param {Record<string, unknown>} policy
|
|
194
|
+
* @param {string} fgDevkitRoot
|
|
195
|
+
* @returns {{ policy: Record<string, unknown>; changed: boolean }}
|
|
196
|
+
*/
|
|
197
|
+
export function syncPolicyNodeFromFgDevkit(policy, fgDevkitRoot) {
|
|
198
|
+
const fromFgDevkit = readFgDevkitNodePolicy(fgDevkitRoot)
|
|
199
|
+
if (!fromFgDevkit) {
|
|
200
|
+
return { policy, changed: false }
|
|
201
|
+
}
|
|
202
|
+
const next = {
|
|
203
|
+
...policy,
|
|
204
|
+
toolchain: {
|
|
205
|
+
...(policy.toolchain ?? {}),
|
|
206
|
+
node: {
|
|
207
|
+
nvmrc: fromFgDevkit.nvmrc,
|
|
208
|
+
...(fromFgDevkit.engines ? { engines: fromFgDevkit.engines } : {}),
|
|
209
|
+
jenkinsImage: fromFgDevkit.jenkinsImage,
|
|
210
|
+
dockerTag: fromFgDevkit.dockerTag,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
const current = policy.toolchain?.node
|
|
215
|
+
const changed =
|
|
216
|
+
!current ||
|
|
217
|
+
current.nvmrc !== next.toolchain.node.nvmrc ||
|
|
218
|
+
current.engines !== next.toolchain.node.engines ||
|
|
219
|
+
current.jenkinsImage !== next.toolchain.node.jenkinsImage ||
|
|
220
|
+
current.dockerTag !== next.toolchain.node.dockerTag
|
|
221
|
+
return { policy: changed ? next : policy, changed }
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* @param {Record<string, unknown>} policy
|
|
226
|
+
* @param {string} [labelPrefix]
|
|
227
|
+
* @returns {string[]}
|
|
228
|
+
*/
|
|
229
|
+
export function collectNodeToolchainPolicyViolations(policy, labelPrefix = '[policy] toolchain.') {
|
|
230
|
+
const node = policy?.toolchain?.node
|
|
231
|
+
if (node === undefined || node === null) {
|
|
72
232
|
return []
|
|
73
233
|
}
|
|
74
|
-
if (
|
|
75
|
-
return [
|
|
76
|
-
|
|
77
|
-
|
|
234
|
+
if (typeof node !== 'object') {
|
|
235
|
+
return [`${labelPrefix}node: must be an object`]
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const violations = []
|
|
239
|
+
if (!isValidNodeNvmrc(node.nvmrc)) {
|
|
240
|
+
violations.push(
|
|
241
|
+
`${labelPrefix}node.nvmrc: must be a version like "22" or "22.13.0", got ${JSON.stringify(node.nvmrc)}`,
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
if (
|
|
245
|
+
node.engines !== undefined &&
|
|
246
|
+
node.engines !== null &&
|
|
247
|
+
(typeof node.engines !== 'string' || node.engines.trim() === '')
|
|
248
|
+
) {
|
|
249
|
+
violations.push(`${labelPrefix}node.engines: must be a non-empty string or null`)
|
|
250
|
+
}
|
|
251
|
+
if (
|
|
252
|
+
node.jenkinsImage !== undefined &&
|
|
253
|
+
(typeof node.jenkinsImage !== 'string' || !/^node\d+$/.test(node.jenkinsImage))
|
|
254
|
+
) {
|
|
255
|
+
violations.push(
|
|
256
|
+
`${labelPrefix}node.jenkinsImage: must be like "node22", got ${JSON.stringify(node.jenkinsImage)}`,
|
|
257
|
+
)
|
|
78
258
|
}
|
|
79
|
-
return
|
|
259
|
+
return violations
|
|
80
260
|
}
|
|
81
261
|
|
|
82
262
|
/**
|
package/scripts/fg-devkit.mjs
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* pnpm exec fg-devkit fleet-vulns [projects-root] [--summary-only] [--delay-ms=<ms>]
|
|
12
12
|
* pnpm exec fg-devkit fleet-bump-fg-devkit [projects-root] [--dry-run]
|
|
13
13
|
* pnpm exec fg-devkit bump-pnpm-toolchain [version] [projects-root] [--dry-run] [--skip-fleet] [--skip-install]
|
|
14
|
+
* pnpm exec fg-devkit sync-node-toolchain [policy-file] [--dry-run]
|
|
15
|
+
* pnpm exec fg-devkit bump-node-toolchain [nvmrc-version] [projects-root] [--dry-run] [--skip-fleet] [--no-engines]
|
|
14
16
|
* pnpm exec fg-devkit policy-upgrade [policy-file] [ncu-args...]
|
|
15
17
|
* pnpm exec fg-devkit policy-upgrade-commit [ncu-args...]
|
|
16
18
|
* pnpm exec fg-devkit audit-override-commit [pnpm-audit-args...]
|
|
@@ -462,6 +464,15 @@ const DIRECT_COMMANDS = {
|
|
|
462
464
|
'fleet-vulns': 'fleet-vulns.mjs',
|
|
463
465
|
'fleet-bump-fg-devkit': 'fleet-bump-fg-devkit.mjs',
|
|
464
466
|
'bump-pnpm-toolchain': 'bump-pnpm-toolchain.mjs',
|
|
467
|
+
'sync-node-toolchain': 'sync-node-toolchain.mjs',
|
|
468
|
+
'sync-pnpm-toolchain': 'sync-pnpm-toolchain.mjs',
|
|
469
|
+
'bump-node-toolchain': 'bump-node-toolchain.mjs',
|
|
470
|
+
'fleet-git-pull': 'fleet-git-pull.mjs',
|
|
471
|
+
'fleet-git-pull-scoped': 'fleet-git-pull-scoped.mjs',
|
|
472
|
+
'fleet-git-commit-push': 'fleet-git-commit-push.mjs',
|
|
473
|
+
'fleet-git-commit-push-scoped': 'fleet-git-commit-push-scoped.mjs',
|
|
474
|
+
'fleet-git-discard': 'fleet-git-discard.mjs',
|
|
475
|
+
'fleet-git-discard-scoped': 'fleet-git-discard-scoped.mjs',
|
|
465
476
|
'policy-upgrade': 'policy-upgrade.mjs',
|
|
466
477
|
'policy-upgrade-commit': 'policy-upgrade-commit.mjs',
|
|
467
478
|
'audit-override-commit': 'audit-override-commit.mjs',
|
|
@@ -486,7 +497,7 @@ function printHelp() {
|
|
|
486
497
|
|
|
487
498
|
Commands:
|
|
488
499
|
check-deps [policy-file] Validate package.json + pins + toolchain against policy
|
|
489
|
-
sync-deps [policy-file] Align declared versions and
|
|
500
|
+
sync-deps [policy-file] Align declared versions, packageManager, and Node toolchain to policy
|
|
490
501
|
policy-outdated [policy-file] Show outdated policy packages via pnpm outdated
|
|
491
502
|
policy-vulns [policy-file] Check all policy packages together (including transitives) via pnpm audit
|
|
492
503
|
repo-vulns Check all repo dependencies for vulnerabilities via pnpm audit
|
|
@@ -496,7 +507,30 @@ Commands:
|
|
|
496
507
|
Bump fg-devkit, run sync-deps, then pnpm install across Vue repos
|
|
497
508
|
bump-pnpm-toolchain [version] [projects-root] [options]
|
|
498
509
|
Maintainer: bump pnpm to latest (or version), sync fg-devkit + fleet
|
|
499
|
-
|
|
510
|
+
Fleet: all pnpm repos (Vue+fg-devkit get full sync-deps/check-deps)
|
|
511
|
+
Options: --dry-run, --skip-fleet, --skip-install, --pick, --all, --only=a,b
|
|
512
|
+
sync-node-toolchain [policy-file] [--dry-run]
|
|
513
|
+
Align .nvmrc, engines, Dockerfiles, and Jenkinsfile to policy.node
|
|
514
|
+
sync-pnpm-toolchain [policy-file] [--dry-run]
|
|
515
|
+
Align packageManager and all pnpm@ pins to policy.packageManager
|
|
516
|
+
bump-node-toolchain [version] [projects-root] [options]
|
|
517
|
+
Maintainer: set policy.node and sync Node toolchain across fleet
|
|
518
|
+
Options: --dry-run, --skip-fleet, --no-engines, --engines=RANGE, --pick, --all, --only=a,b
|
|
519
|
+
fleet-git-pull [projects-root] [--dry-run]
|
|
520
|
+
Maintainer: git pull (current branch) for all git repos under projects root
|
|
521
|
+
fleet-git-pull-scoped [projects-root] [options]
|
|
522
|
+
Maintainer: git pull (current branch) for chosen repositories
|
|
523
|
+
Options: --dry-run, --fg-devkit-only, --pick, --all, --only=a,b
|
|
524
|
+
fleet-git-commit-push --message="..." [projects-root] [--dry-run]
|
|
525
|
+
Maintainer: stage, commit, and push all git repos under projects root
|
|
526
|
+
fleet-git-commit-push-scoped --message="..." [projects-root] [options]
|
|
527
|
+
Maintainer: stage, commit, and push chosen repositories
|
|
528
|
+
Options: --dry-run, --fg-devkit-only, --pick, --all, --only=a,b
|
|
529
|
+
fleet-git-discard [projects-root] [--dry-run]
|
|
530
|
+
Maintainer: discard tracked git changes in all git repos under projects root
|
|
531
|
+
fleet-git-discard-scoped [projects-root] [options]
|
|
532
|
+
Maintainer: discard tracked git changes in chosen repositories
|
|
533
|
+
Options: --dry-run, --fg-devkit-only, --pick, --all, --only=a,b
|
|
500
534
|
policy-upgrade [policy-file] [ncu-args...]
|
|
501
535
|
Upgrade policy packages via npm-check-updates (default interactive)
|
|
502
536
|
policy-upgrade-commit [ncu-args...]
|
|
@@ -825,6 +859,13 @@ async function runInteractiveMenu() {
|
|
|
825
859
|
{ name: 'Check vulnerabilities across all Vue repos', value: 'm6' },
|
|
826
860
|
{ name: 'Bump fg-devkit in all Vue repos, run sync-deps, and pnpm install', value: 'm7' },
|
|
827
861
|
{ name: 'Bump pnpm toolchain (latest)', value: 'm8' },
|
|
862
|
+
{ name: 'Bump Node toolchain (fleet sync)', value: 'm9' },
|
|
863
|
+
{ name: 'Git pull all repositories', value: 'm10' },
|
|
864
|
+
{ name: 'Git pull (choose repositories)', value: 'm11' },
|
|
865
|
+
{ name: 'Stage, commit and push all repositories', value: 'm12' },
|
|
866
|
+
{ name: 'Stage, commit and push (choose repositories)', value: 'm13' },
|
|
867
|
+
{ name: 'Discard tracked changes in all repositories', value: 'm14' },
|
|
868
|
+
{ name: 'Discard tracked changes (choose repositories)', value: 'm15' },
|
|
828
869
|
{ name: 'Cancel', value: 'cancel' },
|
|
829
870
|
],
|
|
830
871
|
default: 'm1',
|
|
@@ -845,7 +886,8 @@ async function runInteractiveMenu() {
|
|
|
845
886
|
message: 'Bump pnpm toolchain',
|
|
846
887
|
choices: [
|
|
847
888
|
{ name: 'fg-devkit only (policy, scaffold, install)', value: 'fg-devkit' },
|
|
848
|
-
{ name: 'fg-devkit +
|
|
889
|
+
{ name: 'fg-devkit + pick pnpm repos under projects root', value: 'fleet' },
|
|
890
|
+
{ name: 'Dry run + pick pnpm repos', value: 'dry-run-fleet' },
|
|
849
891
|
{ name: 'Cancel', value: 'cancel' },
|
|
850
892
|
],
|
|
851
893
|
default: 'fg-devkit',
|
|
@@ -856,8 +898,159 @@ async function runInteractiveMenu() {
|
|
|
856
898
|
if (scope === 'fg-devkit') {
|
|
857
899
|
bumpArgs.push('--skip-fleet')
|
|
858
900
|
}
|
|
901
|
+
if (scope === 'fleet') {
|
|
902
|
+
bumpArgs.push('--pick')
|
|
903
|
+
}
|
|
904
|
+
if (scope === 'dry-run-fleet') {
|
|
905
|
+
bumpArgs.push('--dry-run', '--pick')
|
|
906
|
+
}
|
|
859
907
|
return runSelectedAction(() => runNode('bump-pnpm-toolchain.mjs', bumpArgs))
|
|
860
908
|
}
|
|
909
|
+
if (choice === 'm9') {
|
|
910
|
+
const scope = await selectPrompt({
|
|
911
|
+
message: 'Bump Node toolchain',
|
|
912
|
+
choices: [
|
|
913
|
+
{ name: 'fg-devkit only (policy + local files)', value: 'fg-devkit' },
|
|
914
|
+
{ name: 'fg-devkit + pick projects under projects root', value: 'fleet' },
|
|
915
|
+
{ name: 'Dry run + pick projects', value: 'dry-run-fleet' },
|
|
916
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
917
|
+
],
|
|
918
|
+
default: 'fleet',
|
|
919
|
+
loop: false,
|
|
920
|
+
})
|
|
921
|
+
if (scope === 'cancel') continue
|
|
922
|
+
const bumpArgs = []
|
|
923
|
+
if (scope === 'fg-devkit') {
|
|
924
|
+
bumpArgs.push('--skip-fleet')
|
|
925
|
+
}
|
|
926
|
+
if (scope === 'fleet') {
|
|
927
|
+
bumpArgs.push('--pick')
|
|
928
|
+
}
|
|
929
|
+
if (scope === 'dry-run-fleet') {
|
|
930
|
+
bumpArgs.push('--dry-run', '--pick')
|
|
931
|
+
}
|
|
932
|
+
return runSelectedAction(() => runNode('bump-node-toolchain.mjs', bumpArgs))
|
|
933
|
+
}
|
|
934
|
+
if (choice === 'm10') {
|
|
935
|
+
const scope = await selectPrompt({
|
|
936
|
+
message: 'Git pull all repositories',
|
|
937
|
+
choices: [
|
|
938
|
+
{ name: 'Pull all git repos under projects root', value: 'pull' },
|
|
939
|
+
{ name: 'Dry run (all repos)', value: 'dry-run' },
|
|
940
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
941
|
+
],
|
|
942
|
+
default: 'pull',
|
|
943
|
+
loop: false,
|
|
944
|
+
})
|
|
945
|
+
if (scope === 'cancel') continue
|
|
946
|
+
const pullArgs = scope === 'dry-run' ? ['--dry-run'] : []
|
|
947
|
+
return runSelectedAction(() => runNode('fleet-git-pull.mjs', pullArgs))
|
|
948
|
+
}
|
|
949
|
+
if (choice === 'm11') {
|
|
950
|
+
const scope = await selectPrompt({
|
|
951
|
+
message: 'Git pull (choose repositories)',
|
|
952
|
+
choices: [
|
|
953
|
+
{ name: 'fg-devkit only', value: 'fg-devkit' },
|
|
954
|
+
{ name: 'Choose repositories under projects root', value: 'pick' },
|
|
955
|
+
{ name: 'Dry run + choose repositories', value: 'dry-run-pick' },
|
|
956
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
957
|
+
],
|
|
958
|
+
default: 'pick',
|
|
959
|
+
loop: false,
|
|
960
|
+
})
|
|
961
|
+
if (scope === 'cancel') continue
|
|
962
|
+
const pullArgs = []
|
|
963
|
+
if (scope === 'fg-devkit') {
|
|
964
|
+
pullArgs.push('--fg-devkit-only')
|
|
965
|
+
}
|
|
966
|
+
if (scope === 'pick') {
|
|
967
|
+
pullArgs.push('--pick')
|
|
968
|
+
}
|
|
969
|
+
if (scope === 'dry-run-pick') {
|
|
970
|
+
pullArgs.push('--dry-run', '--pick')
|
|
971
|
+
}
|
|
972
|
+
return runSelectedAction(() => runNode('fleet-git-pull-scoped.mjs', pullArgs))
|
|
973
|
+
}
|
|
974
|
+
if (choice === 'm12') {
|
|
975
|
+
const scope = await selectPrompt({
|
|
976
|
+
message: 'Stage, commit and push all repositories',
|
|
977
|
+
choices: [
|
|
978
|
+
{ name: 'Stage, commit and push all repos', value: 'run' },
|
|
979
|
+
{ name: 'Dry run (all repos)', value: 'dry-run' },
|
|
980
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
981
|
+
],
|
|
982
|
+
default: 'run',
|
|
983
|
+
loop: false,
|
|
984
|
+
})
|
|
985
|
+
if (scope === 'cancel') continue
|
|
986
|
+
const commitArgs = scope === 'dry-run' ? ['--dry-run'] : []
|
|
987
|
+
return runSelectedAction(() => runNode('fleet-git-commit-push.mjs', commitArgs))
|
|
988
|
+
}
|
|
989
|
+
if (choice === 'm13') {
|
|
990
|
+
const scope = await selectPrompt({
|
|
991
|
+
message: 'Stage, commit and push (choose repositories)',
|
|
992
|
+
choices: [
|
|
993
|
+
{ name: 'fg-devkit only', value: 'fg-devkit' },
|
|
994
|
+
{ name: 'Choose repositories under projects root', value: 'pick' },
|
|
995
|
+
{ name: 'Dry run + choose repositories', value: 'dry-run-pick' },
|
|
996
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
997
|
+
],
|
|
998
|
+
default: 'pick',
|
|
999
|
+
loop: false,
|
|
1000
|
+
})
|
|
1001
|
+
if (scope === 'cancel') continue
|
|
1002
|
+
const commitArgs = []
|
|
1003
|
+
if (scope === 'fg-devkit') {
|
|
1004
|
+
commitArgs.push('--fg-devkit-only')
|
|
1005
|
+
}
|
|
1006
|
+
if (scope === 'pick') {
|
|
1007
|
+
commitArgs.push('--pick')
|
|
1008
|
+
}
|
|
1009
|
+
if (scope === 'dry-run-pick') {
|
|
1010
|
+
commitArgs.push('--dry-run', '--pick')
|
|
1011
|
+
}
|
|
1012
|
+
return runSelectedAction(() => runNode('fleet-git-commit-push-scoped.mjs', commitArgs))
|
|
1013
|
+
}
|
|
1014
|
+
if (choice === 'm14') {
|
|
1015
|
+
const scope = await selectPrompt({
|
|
1016
|
+
message: 'Discard tracked changes in all repositories',
|
|
1017
|
+
choices: [
|
|
1018
|
+
{ name: 'Discard tracked changes in all repos', value: 'run' },
|
|
1019
|
+
{ name: 'Dry run (all repos)', value: 'dry-run' },
|
|
1020
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
1021
|
+
],
|
|
1022
|
+
default: 'run',
|
|
1023
|
+
loop: false,
|
|
1024
|
+
})
|
|
1025
|
+
if (scope === 'cancel') continue
|
|
1026
|
+
const discardArgs = scope === 'dry-run' ? ['--dry-run'] : []
|
|
1027
|
+
return runSelectedAction(() => runNode('fleet-git-discard.mjs', discardArgs))
|
|
1028
|
+
}
|
|
1029
|
+
if (choice === 'm15') {
|
|
1030
|
+
const scope = await selectPrompt({
|
|
1031
|
+
message: 'Discard tracked changes (choose repositories)',
|
|
1032
|
+
choices: [
|
|
1033
|
+
{ name: 'fg-devkit only', value: 'fg-devkit' },
|
|
1034
|
+
{ name: 'Choose repositories under projects root', value: 'pick' },
|
|
1035
|
+
{ name: 'Dry run + choose repositories', value: 'dry-run-pick' },
|
|
1036
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
1037
|
+
],
|
|
1038
|
+
default: 'pick',
|
|
1039
|
+
loop: false,
|
|
1040
|
+
})
|
|
1041
|
+
if (scope === 'cancel') continue
|
|
1042
|
+
const discardArgs = []
|
|
1043
|
+
if (scope === 'fg-devkit') {
|
|
1044
|
+
discardArgs.push('--fg-devkit-only')
|
|
1045
|
+
}
|
|
1046
|
+
if (scope === 'pick') {
|
|
1047
|
+
discardArgs.push('--pick')
|
|
1048
|
+
}
|
|
1049
|
+
if (scope === 'dry-run-pick') {
|
|
1050
|
+
discardArgs.push('--dry-run', '--pick')
|
|
1051
|
+
}
|
|
1052
|
+
return runSelectedAction(() => runNode('fleet-git-discard-scoped.mjs', discardArgs))
|
|
1053
|
+
}
|
|
861
1054
|
return 1
|
|
862
1055
|
}
|
|
863
1056
|
}
|
|
@@ -942,6 +1135,24 @@ async function main() {
|
|
|
942
1135
|
process.exitCode = 1
|
|
943
1136
|
return
|
|
944
1137
|
}
|
|
1138
|
+
if (sub === 'bump-node-toolchain' && !isFgDevkitMaintainerContext()) {
|
|
1139
|
+
console.error('[fg-devkit] bump-node-toolchain is only available in the fg-devkit repository root.')
|
|
1140
|
+
process.exitCode = 1
|
|
1141
|
+
return
|
|
1142
|
+
}
|
|
1143
|
+
if (
|
|
1144
|
+
(sub === 'fleet-git-pull' ||
|
|
1145
|
+
sub === 'fleet-git-pull-scoped' ||
|
|
1146
|
+
sub === 'fleet-git-commit-push' ||
|
|
1147
|
+
sub === 'fleet-git-commit-push-scoped' ||
|
|
1148
|
+
sub === 'fleet-git-discard' ||
|
|
1149
|
+
sub === 'fleet-git-discard-scoped') &&
|
|
1150
|
+
!isFgDevkitMaintainerContext()
|
|
1151
|
+
) {
|
|
1152
|
+
console.error(`[fg-devkit] ${sub} is only available in the fg-devkit repository root.`)
|
|
1153
|
+
process.exitCode = 1
|
|
1154
|
+
return
|
|
1155
|
+
}
|
|
945
1156
|
process.exitCode = runNode(script, rest)
|
|
946
1157
|
return
|
|
947
1158
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Maintainer: stage all changes, commit, and push on the current branch for chosen repositories.
|
|
4
|
+
*
|
|
5
|
+
* Usage (from fg-devkit repository root):
|
|
6
|
+
* pnpm exec fg-devkit fleet-git-commit-push-scoped --message="chore: sync toolchain" [options]
|
|
7
|
+
*
|
|
8
|
+
* Options:
|
|
9
|
+
* --dry-run Print planned actions without staging, committing, or pushing
|
|
10
|
+
* --fg-devkit-only Only process the fg-devkit repository
|
|
11
|
+
* --pick Interactive checkbox to choose repositories (default when TTY)
|
|
12
|
+
* --all Run on all git repositories under projects root without prompting
|
|
13
|
+
* --only=a,b Run only on matching repos (basename or path under projects root)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from 'node:fs'
|
|
17
|
+
import path from 'node:path'
|
|
18
|
+
import { fileURLToPath } from 'node:url'
|
|
19
|
+
|
|
20
|
+
import { collectFleetGitRepos } from './fleet-project-repos.mjs'
|
|
21
|
+
import { parseFleetSelectionFlags, resolveFleetRepos } from './fleet-repo-picker.mjs'
|
|
22
|
+
import { filterCommitPushPositionalArgs, resolveCommitMessage } from './git-fleet-common.mjs'
|
|
23
|
+
import { runFleetGitCommitPush } from './git-commit-push-fleet-common.mjs'
|
|
24
|
+
|
|
25
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
26
|
+
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
27
|
+
const fgDevkitPkgPath = path.join(fgDevkitRoot, 'package.json')
|
|
28
|
+
const LOG_PREFIX = '[fleet-git-commit-push-scoped]'
|
|
29
|
+
|
|
30
|
+
const cliArgs = process.argv.slice(2)
|
|
31
|
+
const dryRun = cliArgs.includes('--dry-run')
|
|
32
|
+
const fgDevkitOnly = cliArgs.includes('--fg-devkit-only')
|
|
33
|
+
const fleetArgs = filterCommitPushPositionalArgs(cliArgs)
|
|
34
|
+
const { pick, all, only, filteredArgs: positionalArgs } = parseFleetSelectionFlags(fleetArgs)
|
|
35
|
+
const projectsRootArg = positionalArgs[0]
|
|
36
|
+
let projectsRoot = projectsRootArg
|
|
37
|
+
? path.resolve(process.cwd(), projectsRootArg)
|
|
38
|
+
: path.resolve(fgDevkitRoot, '..')
|
|
39
|
+
|
|
40
|
+
function assertFgDevkitMaintainerContext() {
|
|
41
|
+
if (path.resolve(process.cwd()) !== fgDevkitRoot) {
|
|
42
|
+
console.error(`${LOG_PREFIX} Run from the fg-devkit repository root.`)
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const pkg = JSON.parse(fs.readFileSync(fgDevkitPkgPath, 'utf8'))
|
|
47
|
+
if (pkg?.name !== 'fg-devkit') {
|
|
48
|
+
console.error(`${LOG_PREFIX} package.json name must be fg-devkit.`)
|
|
49
|
+
process.exit(1)
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
console.error(`${LOG_PREFIX} Could not read fg-devkit package.json.`)
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function main() {
|
|
58
|
+
assertFgDevkitMaintainerContext()
|
|
59
|
+
|
|
60
|
+
if (!fgDevkitOnly && !fs.existsSync(projectsRoot)) {
|
|
61
|
+
console.error(`${LOG_PREFIX} projects root not found: ${projectsRoot}`)
|
|
62
|
+
process.exit(1)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const commitMessage = await resolveCommitMessage(cliArgs, LOG_PREFIX)
|
|
66
|
+
|
|
67
|
+
let repos
|
|
68
|
+
if (fgDevkitOnly) {
|
|
69
|
+
repos = [fgDevkitRoot]
|
|
70
|
+
projectsRoot = path.dirname(fgDevkitRoot)
|
|
71
|
+
} else {
|
|
72
|
+
const allRepos = collectFleetGitRepos(projectsRoot)
|
|
73
|
+
repos = await resolveFleetRepos(allRepos, projectsRoot, {
|
|
74
|
+
pick,
|
|
75
|
+
all,
|
|
76
|
+
only,
|
|
77
|
+
message: 'Select repositories to stage, commit, and push',
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(`${LOG_PREFIX} projectsRoot=${projectsRoot}`)
|
|
82
|
+
console.log(`${LOG_PREFIX} repositories=${repos.length}`)
|
|
83
|
+
|
|
84
|
+
const exitCode = runFleetGitCommitPush({
|
|
85
|
+
repos,
|
|
86
|
+
projectsRoot,
|
|
87
|
+
commitMessage,
|
|
88
|
+
dryRun,
|
|
89
|
+
logPrefix: LOG_PREFIX,
|
|
90
|
+
})
|
|
91
|
+
process.exit(exitCode)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
main().catch((err) => {
|
|
95
|
+
console.error(LOG_PREFIX, err)
|
|
96
|
+
process.exit(1)
|
|
97
|
+
})
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Maintainer: stage all changes, commit, and push on the current branch for every git repo under projects root.
|
|
4
|
+
*
|
|
5
|
+
* Usage (from fg-devkit repository root):
|
|
6
|
+
* pnpm exec fg-devkit fleet-git-commit-push --message="chore: sync toolchain" [projects-root] [--dry-run]
|
|
7
|
+
* pnpm exec fg-devkit fleet-git-commit-push -m "chore: sync toolchain"
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
import path from 'node:path'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
13
|
+
|
|
14
|
+
import { collectFleetGitRepos } from './fleet-project-repos.mjs'
|
|
15
|
+
import { filterCommitPushPositionalArgs, resolveCommitMessage } from './git-fleet-common.mjs'
|
|
16
|
+
import { runFleetGitCommitPush } from './git-commit-push-fleet-common.mjs'
|
|
17
|
+
|
|
18
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
19
|
+
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
20
|
+
const fgDevkitPkgPath = path.join(fgDevkitRoot, 'package.json')
|
|
21
|
+
const LOG_PREFIX = '[fleet-git-commit-push]'
|
|
22
|
+
|
|
23
|
+
const cliArgs = process.argv.slice(2)
|
|
24
|
+
const dryRun = cliArgs.includes('--dry-run')
|
|
25
|
+
const positionalArgs = filterCommitPushPositionalArgs(cliArgs)
|
|
26
|
+
const projectsRootArg = positionalArgs.find((a) => !a.startsWith('-'))
|
|
27
|
+
const projectsRoot = projectsRootArg
|
|
28
|
+
? path.resolve(process.cwd(), projectsRootArg)
|
|
29
|
+
: path.resolve(fgDevkitRoot, '..')
|
|
30
|
+
|
|
31
|
+
function assertFgDevkitMaintainerContext() {
|
|
32
|
+
if (path.resolve(process.cwd()) !== fgDevkitRoot) {
|
|
33
|
+
console.error(`${LOG_PREFIX} Run from the fg-devkit repository root.`)
|
|
34
|
+
process.exit(1)
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const pkg = JSON.parse(fs.readFileSync(fgDevkitPkgPath, 'utf8'))
|
|
38
|
+
if (pkg?.name !== 'fg-devkit') {
|
|
39
|
+
console.error(`${LOG_PREFIX} package.json name must be fg-devkit.`)
|
|
40
|
+
process.exit(1)
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
console.error(`${LOG_PREFIX} Could not read fg-devkit package.json.`)
|
|
44
|
+
process.exit(1)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function main() {
|
|
49
|
+
assertFgDevkitMaintainerContext()
|
|
50
|
+
|
|
51
|
+
if (!fs.existsSync(projectsRoot)) {
|
|
52
|
+
console.error(`${LOG_PREFIX} projects root not found: ${projectsRoot}`)
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const commitMessage = await resolveCommitMessage(cliArgs, LOG_PREFIX)
|
|
57
|
+
const repos = collectFleetGitRepos(projectsRoot)
|
|
58
|
+
|
|
59
|
+
console.log(`${LOG_PREFIX} projectsRoot=${projectsRoot}`)
|
|
60
|
+
console.log(`${LOG_PREFIX} repositories=${repos.length}`)
|
|
61
|
+
|
|
62
|
+
const exitCode = runFleetGitCommitPush({
|
|
63
|
+
repos,
|
|
64
|
+
projectsRoot,
|
|
65
|
+
commitMessage,
|
|
66
|
+
dryRun,
|
|
67
|
+
logPrefix: LOG_PREFIX,
|
|
68
|
+
})
|
|
69
|
+
process.exit(exitCode)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
main().catch((err) => {
|
|
73
|
+
console.error(LOG_PREFIX, err)
|
|
74
|
+
process.exit(1)
|
|
75
|
+
})
|