fg-devkit 1.9.1 → 1.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/configs/cliff.toml +54 -0
- package/configs/cliff.user.toml +64 -0
- package/configs/dependency-policy.vue-workspace.json +9 -13
- package/package.json +20 -6
- package/release-notes/format.ts +82 -0
- package/release-notes/parseChangelog.ts +85 -0
- package/release-notes/serializeUserChangelog.ts +22 -0
- package/release-notes/sync-user-changelog.ts +84 -0
- package/release-notes/types.ts +10 -0
- package/scaffold/vue/Dockerfile.local +1 -1
- package/scaffold/vue/package.json +5 -5
- package/scripts/audit-override-commit.mjs +166 -0
- package/scripts/bump-pnpm-toolchain.mjs +281 -0
- package/scripts/check-dependency-policy.mjs +16 -0
- package/scripts/dependency-policy-toolchain.mjs +101 -0
- package/scripts/fg-devkit.mjs +71 -4
- package/scripts/fleet-bump-fg-devkit.mjs +3 -52
- package/scripts/fleet-vue-repos.mjs +54 -0
- package/scripts/generate-release-notes.mjs +84 -0
- package/scripts/refresh-dependency-policy-from-repos.mjs +14 -0
- package/scripts/release-notes-common.mjs +114 -0
- package/scripts/release.sh +152 -26
- package/scripts/sync-consumer-scripts.mjs +1 -0
- package/scripts/sync-dependency-policy.mjs +12 -1
- package/scripts/sync-release-notes-config.mjs +104 -0
- package/scripts/sync-scaffold-vue-deps.mjs +35 -2
- package/scripts/verify-release-notes.mjs +158 -0
package/scripts/fg-devkit.mjs
CHANGED
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
* pnpm exec fg-devkit repo-vulns
|
|
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
|
+
* pnpm exec fg-devkit bump-pnpm-toolchain [version] [projects-root] [--dry-run] [--skip-fleet] [--skip-install]
|
|
13
14
|
* pnpm exec fg-devkit policy-upgrade [policy-file] [ncu-args...]
|
|
14
15
|
* pnpm exec fg-devkit policy-upgrade-commit [ncu-args...]
|
|
16
|
+
* pnpm exec fg-devkit audit-override-commit [pnpm-audit-args...]
|
|
15
17
|
* pnpm exec fg-devkit sync-project-configs (VS Code + tooling sync; overwrites config files)
|
|
16
18
|
* pnpm exec fg-devkit sync-tooling-configs (tooling sync only; overwrites config files)
|
|
17
19
|
* pnpm exec fg-devkit sync-vscode-settings
|
|
@@ -20,7 +22,10 @@
|
|
|
20
22
|
* pnpm exec fg-devkit setup-lefthook
|
|
21
23
|
* pnpm exec fg-devkit prune-consumer-deps [--dry-run]
|
|
22
24
|
* pnpm exec fg-devkit full-consumer-setup
|
|
23
|
-
* pnpm exec fg-devkit create-release [release|candidate] [patch|minor|major]
|
|
25
|
+
* pnpm exec fg-devkit create-release [release|candidate] [patch|minor|major] [--skip-release-notes]
|
|
26
|
+
* pnpm exec fg-devkit generate-release-notes [--tag=vX.Y.Z]
|
|
27
|
+
* pnpm exec fg-devkit verify-release-notes
|
|
28
|
+
* pnpm exec fg-devkit sync-release-notes-config
|
|
24
29
|
* pnpm exec fg-devkit commit [cz-args...] (czg/cz-git with fg-devkit bundled config)
|
|
25
30
|
* pnpm exec fg-devkit commit-vuln (staged package.json version bumps → vuln() commit)
|
|
26
31
|
* pnpm exec fg-devkit lint [eslint-args...] (run ESLint with fg-devkit bundled packages)
|
|
@@ -153,6 +158,9 @@ function runFullConsumerSetup() {
|
|
|
153
158
|
const scriptsStep = runNode('sync-consumer-scripts.mjs')
|
|
154
159
|
if (scriptsStep !== 0) return scriptsStep
|
|
155
160
|
|
|
161
|
+
const releaseNotesStep = runNode('sync-release-notes-config.mjs')
|
|
162
|
+
if (releaseNotesStep !== 0) return releaseNotesStep
|
|
163
|
+
|
|
156
164
|
const hooksStep = runSetupLefthook()
|
|
157
165
|
if (hooksStep !== 0) return hooksStep
|
|
158
166
|
|
|
@@ -453,13 +461,18 @@ const DIRECT_COMMANDS = {
|
|
|
453
461
|
'repo-vulns': 'repo-vulns.mjs',
|
|
454
462
|
'fleet-vulns': 'fleet-vulns.mjs',
|
|
455
463
|
'fleet-bump-fg-devkit': 'fleet-bump-fg-devkit.mjs',
|
|
464
|
+
'bump-pnpm-toolchain': 'bump-pnpm-toolchain.mjs',
|
|
456
465
|
'policy-upgrade': 'policy-upgrade.mjs',
|
|
457
466
|
'policy-upgrade-commit': 'policy-upgrade-commit.mjs',
|
|
467
|
+
'audit-override-commit': 'audit-override-commit.mjs',
|
|
458
468
|
'sync-project-configs': 'sync-project-configs.mjs',
|
|
459
469
|
'sync-tooling-configs': 'sync-tooling-configs.mjs',
|
|
460
470
|
'sync-vscode-settings': 'sync-vscode-settings.mjs',
|
|
461
471
|
'sync-core-coding-standards': 'sync-core-coding-standards.mjs',
|
|
462
472
|
'sync-consumer-scripts': 'sync-consumer-scripts.mjs',
|
|
473
|
+
'sync-release-notes-config': 'sync-release-notes-config.mjs',
|
|
474
|
+
'generate-release-notes': 'generate-release-notes.mjs',
|
|
475
|
+
'verify-release-notes': 'verify-release-notes.mjs',
|
|
463
476
|
'setup-lefthook': 'setup-lefthook',
|
|
464
477
|
'prune-consumer-deps': 'prune-consumer-deps.mjs',
|
|
465
478
|
'sync-scaffold-vue-deps': 'sync-scaffold-vue-deps.mjs',
|
|
@@ -472,8 +485,8 @@ function printHelp() {
|
|
|
472
485
|
pnpm exec fg-devkit [command] [args...]
|
|
473
486
|
|
|
474
487
|
Commands:
|
|
475
|
-
check-deps [policy-file] Validate package.json + pins against policy
|
|
476
|
-
sync-deps [policy-file] Align declared versions to policy
|
|
488
|
+
check-deps [policy-file] Validate package.json + pins + toolchain against policy
|
|
489
|
+
sync-deps [policy-file] Align declared versions and packageManager to policy
|
|
477
490
|
policy-outdated [policy-file] Show outdated policy packages via pnpm outdated
|
|
478
491
|
policy-vulns [policy-file] Check all policy packages together (including transitives) via pnpm audit
|
|
479
492
|
repo-vulns Check all repo dependencies for vulnerabilities via pnpm audit
|
|
@@ -481,10 +494,15 @@ Commands:
|
|
|
481
494
|
Run pnpm audit across Vue repos (full output + combined summary)
|
|
482
495
|
fleet-bump-fg-devkit [projects-root] [--dry-run]
|
|
483
496
|
Bump fg-devkit, run sync-deps, then pnpm install across Vue repos
|
|
497
|
+
bump-pnpm-toolchain [version] [projects-root] [options]
|
|
498
|
+
Maintainer: bump pnpm to latest (or version), sync fg-devkit + fleet
|
|
499
|
+
Options: --dry-run, --skip-fleet, --skip-install
|
|
484
500
|
policy-upgrade [policy-file] [ncu-args...]
|
|
485
501
|
Upgrade policy packages via npm-check-updates (default interactive)
|
|
486
502
|
policy-upgrade-commit [ncu-args...]
|
|
487
503
|
Upgrade one consumer package.json package and auto-commit
|
|
504
|
+
audit-override-commit [pnpm-audit-args...]
|
|
505
|
+
Fix one vulnerability override via pnpm audit and auto-commit
|
|
488
506
|
sync-project-configs VS Code + tooling sync (overwrites config files)
|
|
489
507
|
sync-tooling-configs Tooling sync only (overwrites config files)
|
|
490
508
|
sync-vscode-settings Copy shared VS Code settings into .vscode/settings.json
|
|
@@ -493,9 +511,13 @@ Commands:
|
|
|
493
511
|
setup-lefthook Setup lefthook and migrate from husky/lint-staged
|
|
494
512
|
prune-consumer-deps [--dry-run] Remove consumer tooling deps bundled by fg-devkit
|
|
495
513
|
full-consumer-setup Run: step 7 -> scripts -> lefthook -> step 3 -> step 2 -> pnpm install
|
|
496
|
-
create-release [release|candidate] [patch|minor|major]
|
|
514
|
+
create-release [release|candidate] [patch|minor|major] [--skip-release-notes]
|
|
497
515
|
Stable release or candidate (X.Y.Z-rc.N); omit = interactive menu
|
|
498
516
|
(alias: createRelease)
|
|
517
|
+
generate-release-notes [--tag=vX.Y.Z]
|
|
518
|
+
Generate CHANGELOG.md and user changelog via git-cliff
|
|
519
|
+
verify-release-notes Interactive review step before committing release notes
|
|
520
|
+
sync-release-notes-config Copy cliff configs and generate:release-notes script when missing
|
|
499
521
|
commit [args...] czg/cz-git with fg-devkit bundled config
|
|
500
522
|
commit-vuln Commit staged dependency bumps as vuln() (alias: vuln-commit)
|
|
501
523
|
lint [eslint-args...] Run ESLint using fg-devkit bundled ESLint stack
|
|
@@ -648,6 +670,18 @@ async function runInteractiveMenu() {
|
|
|
648
670
|
})
|
|
649
671
|
if (releaseKind === 'cancel') continue
|
|
650
672
|
|
|
673
|
+
const releaseMode = await selectPrompt({
|
|
674
|
+
message: 'Create release',
|
|
675
|
+
choices: [
|
|
676
|
+
{ name: 'Release + release notes', value: 'with-notes' },
|
|
677
|
+
{ name: 'Release', value: 'without-notes' },
|
|
678
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
679
|
+
],
|
|
680
|
+
default: 'with-notes',
|
|
681
|
+
loop: false,
|
|
682
|
+
})
|
|
683
|
+
if (releaseMode === 'cancel') continue
|
|
684
|
+
|
|
651
685
|
const bumpChoice = await selectPrompt({
|
|
652
686
|
message: releaseKind === 'candidate' ? 'Candidate' : 'Release',
|
|
653
687
|
choices: [
|
|
@@ -663,6 +697,11 @@ async function runInteractiveMenu() {
|
|
|
663
697
|
|
|
664
698
|
const releaseArgs =
|
|
665
699
|
releaseKind === 'candidate' ? [bumpChoice, 'candidate'] : [bumpChoice]
|
|
700
|
+
|
|
701
|
+
if (releaseMode === 'without-notes') {
|
|
702
|
+
releaseArgs.push('--skip-release-notes')
|
|
703
|
+
}
|
|
704
|
+
|
|
666
705
|
return runSelectedAction(() => runReleaseScript(releaseArgs))
|
|
667
706
|
}
|
|
668
707
|
|
|
@@ -710,6 +749,7 @@ async function runInteractiveMenu() {
|
|
|
710
749
|
{ name: 'Check', value: 'check' },
|
|
711
750
|
{ name: 'Sync', value: 'sync' },
|
|
712
751
|
{ name: 'Upgrade one package.json package and auto-commit', value: 'upgrade-commit' },
|
|
752
|
+
{ name: 'Fix one vulnerability override and auto-commit', value: 'audit-override-commit' },
|
|
713
753
|
{ name: 'Cancel', value: 'cancel' },
|
|
714
754
|
],
|
|
715
755
|
default: 'check',
|
|
@@ -718,6 +758,9 @@ async function runInteractiveMenu() {
|
|
|
718
758
|
if (choice === 'cancel') continue
|
|
719
759
|
if (choice === 'check') return runSelectedAction(() => runNode('check-dependency-policy.mjs'))
|
|
720
760
|
if (choice === 'sync') return runSelectedAction(() => runNode('sync-dependency-policy.mjs'))
|
|
761
|
+
if (choice === 'audit-override-commit') {
|
|
762
|
+
return runSelectedAction(() => runNode('audit-override-commit.mjs'))
|
|
763
|
+
}
|
|
721
764
|
return runSelectedAction(() => runNode('policy-upgrade-commit.mjs'))
|
|
722
765
|
}
|
|
723
766
|
|
|
@@ -781,6 +824,7 @@ async function runInteractiveMenu() {
|
|
|
781
824
|
{ name: 'Check fg-devkit repo vulnerabilities', value: 'm5' },
|
|
782
825
|
{ name: 'Check vulnerabilities across all Vue repos', value: 'm6' },
|
|
783
826
|
{ name: 'Bump fg-devkit in all Vue repos, run sync-deps, and pnpm install', value: 'm7' },
|
|
827
|
+
{ name: 'Bump pnpm toolchain (latest)', value: 'm8' },
|
|
784
828
|
{ name: 'Cancel', value: 'cancel' },
|
|
785
829
|
],
|
|
786
830
|
default: 'm1',
|
|
@@ -796,6 +840,24 @@ async function runInteractiveMenu() {
|
|
|
796
840
|
if (choice === 'm5') return runSelectedAction(() => runMaintainerAuditAll())
|
|
797
841
|
if (choice === 'm6') return runSelectedAction(() => runNode('fleet-vulns.mjs'))
|
|
798
842
|
if (choice === 'm7') return runSelectedAction(() => runNode('fleet-bump-fg-devkit.mjs'))
|
|
843
|
+
if (choice === 'm8') {
|
|
844
|
+
const scope = await selectPrompt({
|
|
845
|
+
message: 'Bump pnpm toolchain',
|
|
846
|
+
choices: [
|
|
847
|
+
{ name: 'fg-devkit only (policy, scaffold, install)', value: 'fg-devkit' },
|
|
848
|
+
{ name: 'fg-devkit + all Vue repos under projects root', value: 'fleet' },
|
|
849
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
850
|
+
],
|
|
851
|
+
default: 'fg-devkit',
|
|
852
|
+
loop: false,
|
|
853
|
+
})
|
|
854
|
+
if (scope === 'cancel') continue
|
|
855
|
+
const bumpArgs = []
|
|
856
|
+
if (scope === 'fg-devkit') {
|
|
857
|
+
bumpArgs.push('--skip-fleet')
|
|
858
|
+
}
|
|
859
|
+
return runSelectedAction(() => runNode('bump-pnpm-toolchain.mjs', bumpArgs))
|
|
860
|
+
}
|
|
799
861
|
return 1
|
|
800
862
|
}
|
|
801
863
|
}
|
|
@@ -875,6 +937,11 @@ async function main() {
|
|
|
875
937
|
process.exitCode = 1
|
|
876
938
|
return
|
|
877
939
|
}
|
|
940
|
+
if (sub === 'bump-pnpm-toolchain' && !isFgDevkitMaintainerContext()) {
|
|
941
|
+
console.error('[fg-devkit] bump-pnpm-toolchain is only available in the fg-devkit repository root.')
|
|
942
|
+
process.exitCode = 1
|
|
943
|
+
return
|
|
944
|
+
}
|
|
878
945
|
process.exitCode = runNode(script, rest)
|
|
879
946
|
return
|
|
880
947
|
}
|
|
@@ -6,68 +6,19 @@
|
|
|
6
6
|
* pnpm exec fg-devkit fleet-bump-fg-devkit [projects-root] [--dry-run]
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import fs from 'node:fs'
|
|
10
9
|
import path from 'node:path'
|
|
11
10
|
import { spawnSync } from 'node:child_process'
|
|
12
11
|
import { fileURLToPath } from 'node:url'
|
|
13
12
|
|
|
13
|
+
import { collectFleetVueRepos } from './fleet-vue-repos.mjs'
|
|
14
|
+
|
|
14
15
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
15
16
|
const cliArgs = process.argv.slice(2)
|
|
16
17
|
const dryRun = cliArgs.includes('--dry-run')
|
|
17
18
|
const projectsRootArg = cliArgs.find((a) => !a.startsWith('-'))
|
|
18
19
|
const projectsRoot = projectsRootArg ? path.resolve(process.cwd(), projectsRootArg) : path.resolve(scriptDir, '../..')
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
const normalized = relDir.split(path.sep).join('/')
|
|
22
|
-
if (normalized === 'scaffold/vue' || normalized.endsWith('/scaffold/vue')) {
|
|
23
|
-
return true
|
|
24
|
-
}
|
|
25
|
-
return false
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function walk(dir, acc = []) {
|
|
29
|
-
for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
30
|
-
if (item.name === 'node_modules' || item.name === '.git') continue
|
|
31
|
-
const p = path.join(dir, item.name)
|
|
32
|
-
if (item.isDirectory()) walk(p, acc)
|
|
33
|
-
else if (item.name === 'package.json') acc.push(p)
|
|
34
|
-
}
|
|
35
|
-
return acc
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function readJson(p) {
|
|
39
|
-
return JSON.parse(fs.readFileSync(p, 'utf8'))
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function isVueProject(pkg) {
|
|
43
|
-
return Boolean(pkg.dependencies?.vue || pkg.devDependencies?.vue)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function hasFgDevkit(pkg) {
|
|
47
|
-
return Boolean(pkg.dependencies?.['fg-devkit'] || pkg.devDependencies?.['fg-devkit'])
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function isFgDevkitRepo(pkg) {
|
|
51
|
-
return pkg?.name === 'fg-devkit'
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const packageJsonFiles = walk(projectsRoot)
|
|
55
|
-
const repos = []
|
|
56
|
-
for (const packageJsonPath of packageJsonFiles) {
|
|
57
|
-
try {
|
|
58
|
-
const relDir = path.relative(projectsRoot, path.dirname(packageJsonPath))
|
|
59
|
-
if (shouldSkipPackageJsonDir(relDir)) continue
|
|
60
|
-
const pkg = readJson(packageJsonPath)
|
|
61
|
-
if (!isVueProject(pkg)) continue
|
|
62
|
-
if (isFgDevkitRepo(pkg)) continue
|
|
63
|
-
if (!hasFgDevkit(pkg)) continue
|
|
64
|
-
repos.push(path.dirname(packageJsonPath))
|
|
65
|
-
} catch {
|
|
66
|
-
// Ignore invalid package.json files.
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const uniqueRepos = [...new Set(repos)].sort((a, b) => a.localeCompare(b))
|
|
21
|
+
const uniqueRepos = collectFleetVueRepos(projectsRoot)
|
|
71
22
|
if (uniqueRepos.length === 0) {
|
|
72
23
|
console.log(`[fleet-bump-fg-devkit] No Vue repositories with fg-devkit found under ${projectsRoot}`)
|
|
73
24
|
process.exit(0)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discover Vue repositories under a projects root that use fg-devkit.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs'
|
|
6
|
+
import path from 'node:path'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} projectsRoot
|
|
10
|
+
* @param {{ skipScaffoldVue?: boolean }} [options]
|
|
11
|
+
* @returns {string[]} Absolute repo directory paths, sorted.
|
|
12
|
+
*/
|
|
13
|
+
export function collectFleetVueRepos(projectsRoot, options = {}) {
|
|
14
|
+
const { skipScaffoldVue = true } = options
|
|
15
|
+
|
|
16
|
+
function shouldSkipPackageJsonDir(relDir) {
|
|
17
|
+
if (!skipScaffoldVue) {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
const normalized = relDir.split(path.sep).join('/')
|
|
21
|
+
return normalized === 'scaffold/vue' || normalized.endsWith('/scaffold/vue')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function walk(dir, acc = []) {
|
|
25
|
+
for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
26
|
+
if (item.name === 'node_modules' || item.name === '.git') continue
|
|
27
|
+
const p = path.join(dir, item.name)
|
|
28
|
+
if (item.isDirectory()) walk(p, acc)
|
|
29
|
+
else if (item.name === 'package.json') acc.push(p)
|
|
30
|
+
}
|
|
31
|
+
return acc
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readJson(p) {
|
|
35
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const repos = []
|
|
39
|
+
for (const packageJsonPath of walk(projectsRoot)) {
|
|
40
|
+
try {
|
|
41
|
+
const relDir = path.relative(projectsRoot, path.dirname(packageJsonPath))
|
|
42
|
+
if (shouldSkipPackageJsonDir(relDir)) continue
|
|
43
|
+
const pkg = readJson(packageJsonPath)
|
|
44
|
+
if (pkg?.name === 'fg-devkit') continue
|
|
45
|
+
if (!pkg.dependencies?.vue && !pkg.devDependencies?.vue) continue
|
|
46
|
+
if (!pkg.dependencies?.['fg-devkit'] && !pkg.devDependencies?.['fg-devkit']) continue
|
|
47
|
+
repos.push(path.dirname(packageJsonPath))
|
|
48
|
+
} catch {
|
|
49
|
+
// Ignore invalid package.json files.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return [...new Set(repos)].sort((a, b) => a.localeCompare(b))
|
|
54
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate technical and user-facing changelogs via git-cliff, then sync user changelog.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node node_modules/fg-devkit/scripts/generate-release-notes.mjs [--tag=vX.Y.Z]
|
|
7
|
+
* pnpm exec fg-devkit generate-release-notes [--tag=vX.Y.Z]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawnSync } from 'node:child_process'
|
|
11
|
+
import path from 'node:path'
|
|
12
|
+
import {
|
|
13
|
+
hasReleaseNotesConfig,
|
|
14
|
+
resolveCliffConfig,
|
|
15
|
+
resolveGitCliffBin,
|
|
16
|
+
runSyncUserChangelog,
|
|
17
|
+
} from './release-notes-common.mjs'
|
|
18
|
+
const cwd = process.cwd()
|
|
19
|
+
const tagArg = process.argv.find((arg) => arg.startsWith('--tag='))?.slice('--tag='.length)
|
|
20
|
+
|
|
21
|
+
function runGitCliff(configPath, outputPath) {
|
|
22
|
+
const gitCliffBin = resolveGitCliffBin()
|
|
23
|
+
if (!gitCliffBin) {
|
|
24
|
+
console.error('[fg-devkit] Could not resolve git-cliff. Reinstall fg-devkit and retry.')
|
|
25
|
+
return 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const args = ['-c', configPath, '-o', outputPath]
|
|
29
|
+
if (tagArg) {
|
|
30
|
+
args.push('--tag', tagArg)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const r = spawnSync(gitCliffBin, args, {
|
|
34
|
+
stdio: 'inherit',
|
|
35
|
+
cwd,
|
|
36
|
+
env: process.env,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
if (r.error) {
|
|
40
|
+
console.error('[fg-devkit]', r.error.message)
|
|
41
|
+
return 1
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return r.status ?? 1
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function main() {
|
|
48
|
+
if (!hasReleaseNotesConfig(cwd)) {
|
|
49
|
+
console.error(
|
|
50
|
+
'[fg-devkit] No release-notes config found. Run "pnpm exec fgd sync-release-notes-config" first.',
|
|
51
|
+
)
|
|
52
|
+
return 1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const technicalConfig = resolveCliffConfig('cliff.toml', cwd)
|
|
56
|
+
if (!technicalConfig) {
|
|
57
|
+
console.error('[fg-devkit] Missing cliff.toml in repo and fg-devkit configs.')
|
|
58
|
+
return 1
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const userConfig = resolveCliffConfig('cliff.user.toml', cwd)
|
|
62
|
+
|
|
63
|
+
const technicalCode = runGitCliff(technicalConfig, path.join(cwd, 'CHANGELOG.md'))
|
|
64
|
+
if (technicalCode !== 0) {
|
|
65
|
+
return technicalCode
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (userConfig) {
|
|
69
|
+
const userCode = runGitCliff(userConfig, path.join(cwd, 'CHANGELOG.user.draft.md'))
|
|
70
|
+
if (userCode !== 0) {
|
|
71
|
+
return userCode
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const syncCode = runSyncUserChangelog(cwd)
|
|
75
|
+
if (syncCode !== 0) {
|
|
76
|
+
return syncCode
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log('[fg-devkit] Release notes generated.')
|
|
81
|
+
return 0
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
process.exitCode = main()
|
|
@@ -29,6 +29,7 @@ import path from 'node:path'
|
|
|
29
29
|
import { fileURLToPath } from 'node:url'
|
|
30
30
|
|
|
31
31
|
import { collectPinViolations } from './dependency-policy-pins.mjs'
|
|
32
|
+
import { collectToolchainPolicyViolations } from './dependency-policy-toolchain.mjs'
|
|
32
33
|
import { syncScaffoldVueDepsFromPolicy } from './sync-scaffold-vue-deps.mjs'
|
|
33
34
|
|
|
34
35
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
@@ -188,6 +189,18 @@ for (const [name, u] of usage) {
|
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
const policy = {
|
|
192
|
+
toolchain:
|
|
193
|
+
existingPolicy.toolchain ??
|
|
194
|
+
(() => {
|
|
195
|
+
try {
|
|
196
|
+
const fgDevkitPkg = JSON.parse(fs.readFileSync(fgDevkitPackageJsonPath, 'utf8'))
|
|
197
|
+
return fgDevkitPkg.packageManager
|
|
198
|
+
? { packageManager: fgDevkitPkg.packageManager }
|
|
199
|
+
: undefined
|
|
200
|
+
} catch {
|
|
201
|
+
return undefined
|
|
202
|
+
}
|
|
203
|
+
})(),
|
|
191
204
|
managedDependencies: {
|
|
192
205
|
dependencies: sortRecord(outDeps),
|
|
193
206
|
devDependencies: sortRecord(outDev),
|
|
@@ -201,6 +214,7 @@ const policyErrors = [
|
|
|
201
214
|
collectPinViolations(`managedDependencies.${section}.`, deps),
|
|
202
215
|
),
|
|
203
216
|
...collectPinViolations('managedAnywhere.', policy.managedAnywhere),
|
|
217
|
+
...collectToolchainPolicyViolations(policy),
|
|
204
218
|
]
|
|
205
219
|
|
|
206
220
|
if (policyErrors.length > 0) {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Shared helpers for release-notes scripts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawnSync } from 'node:child_process'
|
|
7
|
+
import fs from 'node:fs'
|
|
8
|
+
import path from 'node:path'
|
|
9
|
+
import { createRequire } from 'node:module'
|
|
10
|
+
import { fileURLToPath } from 'node:url'
|
|
11
|
+
|
|
12
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
13
|
+
const require = createRequire(import.meta.url)
|
|
14
|
+
|
|
15
|
+
export const fgRoot = path.resolve(scriptDir, '..')
|
|
16
|
+
|
|
17
|
+
/** True when the consumer repo has opted into git-cliff release notes. */
|
|
18
|
+
export function hasReleaseNotesConfig(cwd = process.cwd()) {
|
|
19
|
+
return (
|
|
20
|
+
fs.existsSync(path.join(cwd, 'cliff.toml')) ||
|
|
21
|
+
fs.existsSync(path.join(cwd, 'cliff.user.toml')) ||
|
|
22
|
+
fs.existsSync(path.join(cwd, 'CHANGELOG.md')) ||
|
|
23
|
+
fs.existsSync(path.join(cwd, 'CHANGELOG.user.md'))
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function resolveCliffConfig(name, cwd = process.cwd()) {
|
|
28
|
+
const local = path.join(cwd, name)
|
|
29
|
+
if (fs.existsSync(local)) {
|
|
30
|
+
return local
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const bundled = path.join(fgRoot, 'configs', name)
|
|
34
|
+
if (fs.existsSync(bundled)) {
|
|
35
|
+
return bundled
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function resolveGitCliffBin() {
|
|
42
|
+
const candidates = [
|
|
43
|
+
path.join(fgRoot, 'node_modules', 'git-cliff', 'lib', 'cli', 'cli.js'),
|
|
44
|
+
path.join(process.cwd(), 'node_modules', 'git-cliff', 'lib', 'cli', 'cli.js'),
|
|
45
|
+
path.join(process.cwd(), 'node_modules', 'fg-devkit', 'node_modules', 'git-cliff', 'lib', 'cli', 'cli.js'),
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
for (const candidate of candidates) {
|
|
49
|
+
if (fs.existsSync(candidate)) {
|
|
50
|
+
return candidate
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function resolveTsxBin() {
|
|
58
|
+
const candidates = [
|
|
59
|
+
path.join(fgRoot, 'node_modules', 'tsx', 'dist', 'cli.mjs'),
|
|
60
|
+
path.join(fgRoot, 'node_modules', 'tsx', 'cli.mjs'),
|
|
61
|
+
path.join(process.cwd(), 'node_modules', 'tsx', 'dist', 'cli.mjs'),
|
|
62
|
+
path.join(process.cwd(), 'node_modules', 'fg-devkit', 'node_modules', 'tsx', 'dist', 'cli.mjs'),
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
if (fs.existsSync(candidate)) {
|
|
67
|
+
return candidate
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
return require.resolve('tsx/cli.mjs', { paths: [fgRoot, process.cwd()] })
|
|
73
|
+
} catch {
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function runSyncUserChangelog(cwd = process.cwd()) {
|
|
79
|
+
const tsxBin = resolveTsxBin()
|
|
80
|
+
const syncScript = path.join(fgRoot, 'release-notes', 'sync-user-changelog.ts')
|
|
81
|
+
|
|
82
|
+
if (!tsxBin) {
|
|
83
|
+
console.error('[fg-devkit] Could not resolve tsx. Reinstall fg-devkit and retry.')
|
|
84
|
+
return 1
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const r = spawnSync(process.execPath, [tsxBin, syncScript], {
|
|
88
|
+
stdio: 'inherit',
|
|
89
|
+
cwd,
|
|
90
|
+
env: { ...process.env, FG_DEVKIT_CWD: cwd },
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
if (r.error) {
|
|
94
|
+
console.error('[fg-devkit]', r.error.message)
|
|
95
|
+
return 1
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return r.status ?? 1
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function readPreviewFiles(cwd = process.cwd()) {
|
|
102
|
+
const files = ['CHANGELOG.user.draft.md', 'CHANGELOG.user.md', 'CHANGELOG.md']
|
|
103
|
+
const previews = []
|
|
104
|
+
|
|
105
|
+
for (const file of files) {
|
|
106
|
+
const filePath = path.join(cwd, file)
|
|
107
|
+
if (!fs.existsSync(filePath)) {
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
previews.push({ file, content: fs.readFileSync(filePath, 'utf8') })
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return previews
|
|
114
|
+
}
|