fg-devkit 1.8.1 → 1.8.4
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/lefthook.yml +0 -2
- package/package.json +2 -2
- package/scaffold/vue/package.json +1 -1
- package/scripts/cz/.cz-config.cjs +0 -3
- package/scripts/fg-devkit.mjs +23 -25
- package/scripts/release.sh +70 -14
- package/scripts/resolve-candidate-version.mjs +80 -0
- package/scripts/sync-consumer-lefthook.mjs +0 -2
- package/scripts/lefthook-audit-if-enabled.mjs +0 -23
package/configs/lefthook.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fg-devkit",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.4",
|
|
4
4
|
"description": "Shared configs, scripts, and DX tooling for Vue / TypeScript frontend repos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"./scripts/poeditor-upload.mjs": "./scripts/poeditor-upload.mjs",
|
|
46
46
|
"./scripts/scaffold-vue.mjs": "./scripts/scaffold-vue.mjs",
|
|
47
47
|
"./scripts/fg-devkit.mjs": "./scripts/fg-devkit.mjs",
|
|
48
|
-
"./scripts/
|
|
48
|
+
"./scripts/resolve-candidate-version.mjs": "./scripts/resolve-candidate-version.mjs",
|
|
49
49
|
"./scripts/release.sh": "./scripts/release.sh",
|
|
50
50
|
"./scripts/cz/.cz-config.cjs": "./scripts/cz/.cz-config.cjs",
|
|
51
51
|
"./cursor-rules/core-coding-standards.mdc": "./.cursor/rules/core-coding-standards.mdc"
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* cz-git config used by fg-devkit commit flows.
|
|
3
3
|
* Keep this in czrc-compatible shape for `czg --config`.
|
|
4
|
-
*
|
|
5
|
-
* Note: interactive `fg-devkit commit` asks each dep/vuln commit whether Lefthook should run
|
|
6
|
-
* `pnpm audit` for that commit only (via env, not persisted).
|
|
7
4
|
*/
|
|
8
5
|
module.exports = {
|
|
9
6
|
types: [
|
package/scripts/fg-devkit.mjs
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* pnpm exec fg-devkit setup-lefthook
|
|
21
21
|
* pnpm exec fg-devkit prune-consumer-deps [--dry-run]
|
|
22
22
|
* pnpm exec fg-devkit full-consumer-setup
|
|
23
|
-
* pnpm exec fg-devkit create-release [patch|minor|major]
|
|
23
|
+
* pnpm exec fg-devkit create-release [release|candidate] [patch|minor|major]
|
|
24
24
|
* pnpm exec fg-devkit commit [cz-args...] (czg/cz-git with fg-devkit bundled config)
|
|
25
25
|
* pnpm exec fg-devkit commit-vuln (staged package.json version bumps → vuln() commit)
|
|
26
26
|
* pnpm exec fg-devkit lint [eslint-args...] (run ESLint with fg-devkit bundled packages)
|
|
@@ -72,7 +72,7 @@ function runNode(scriptRelative, args = [], options = {}) {
|
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* Run release.sh (pnpm version bump, git push, gh release). Uses consumer cwd.
|
|
75
|
-
*
|
|
75
|
+
* Args: [release|candidate] [patch|minor|major] or [patch|minor|major] [release|candidate].
|
|
76
76
|
*/
|
|
77
77
|
function runReleaseScript(args = []) {
|
|
78
78
|
const shPath = path.join(scriptDir, 'release.sh')
|
|
@@ -273,18 +273,6 @@ async function runCzg(args = [], extraEnv = {}) {
|
|
|
273
273
|
})
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
let runLefthookAuditThisCommit = true
|
|
277
|
-
if (isDepOrVuln) {
|
|
278
|
-
runLefthookAuditThisCommit = await selectPrompt({
|
|
279
|
-
message: 'Run pnpm audit on commit (Lefthook pre-commit)?',
|
|
280
|
-
choices: [
|
|
281
|
-
{ value: true, name: 'Yes' },
|
|
282
|
-
{ value: false, name: 'No' },
|
|
283
|
-
],
|
|
284
|
-
default: true,
|
|
285
|
-
})
|
|
286
|
-
}
|
|
287
|
-
|
|
288
276
|
if (!isDepOrVuln) {
|
|
289
277
|
shortcutId = await ask('Shortcut ID digits only (optional, e.g. 1234):')
|
|
290
278
|
subject = await ask('Commit subject (imperative, no trailing period):', {
|
|
@@ -305,16 +293,10 @@ async function runCzg(args = [], extraEnv = {}) {
|
|
|
305
293
|
const finalSubject = selectedType === 'dep' ? depSubject : selectedType === 'vuln' ? vulnSubject : subject
|
|
306
294
|
const commitMessage = `${selectedType}${scopePart}: ${finalSubject}${shortcutPart}${bodyPart}`
|
|
307
295
|
|
|
308
|
-
const gitEnv = { ...process.env, ...extraEnv }
|
|
309
|
-
delete gitEnv.FG_DEVKIT_LEFTHOOK_AUDIT
|
|
310
|
-
if (isDepOrVuln) {
|
|
311
|
-
gitEnv.FG_DEVKIT_LEFTHOOK_AUDIT = runLefthookAuditThisCommit ? '1' : '0'
|
|
312
|
-
}
|
|
313
|
-
|
|
314
296
|
const r = spawnSync('git', ['commit', ...args, '-m', commitMessage], {
|
|
315
297
|
stdio: 'inherit',
|
|
316
298
|
cwd: process.cwd(),
|
|
317
|
-
env:
|
|
299
|
+
env: { ...process.env, ...extraEnv },
|
|
318
300
|
})
|
|
319
301
|
if (r.error) {
|
|
320
302
|
console.error('[fg-devkit]', r.error.message)
|
|
@@ -511,7 +493,8 @@ Commands:
|
|
|
511
493
|
setup-lefthook Setup lefthook and migrate from husky/lint-staged
|
|
512
494
|
prune-consumer-deps [--dry-run] Remove consumer tooling deps bundled by fg-devkit
|
|
513
495
|
full-consumer-setup Run: step 7 -> scripts -> lefthook -> step 3 -> step 2 -> pnpm install
|
|
514
|
-
create-release [
|
|
496
|
+
create-release [release|candidate] [patch|minor|major]
|
|
497
|
+
Stable release or candidate (X.Y.Z-rc.N); omit = interactive menu
|
|
515
498
|
(alias: createRelease)
|
|
516
499
|
commit [args...] czg/cz-git with fg-devkit bundled config
|
|
517
500
|
commit-vuln Commit staged dependency bumps as vuln() (alias: vuln-commit)
|
|
@@ -653,8 +636,20 @@ async function runInteractiveMenu() {
|
|
|
653
636
|
}
|
|
654
637
|
|
|
655
638
|
if (group === 'release') {
|
|
656
|
-
const
|
|
639
|
+
const releaseKind = await selectPrompt({
|
|
657
640
|
message: 'Create release',
|
|
641
|
+
choices: [
|
|
642
|
+
{ name: 'Release', value: 'release' },
|
|
643
|
+
{ name: 'Candidate', value: 'candidate' },
|
|
644
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
645
|
+
],
|
|
646
|
+
default: 'release',
|
|
647
|
+
loop: false,
|
|
648
|
+
})
|
|
649
|
+
if (releaseKind === 'cancel') continue
|
|
650
|
+
|
|
651
|
+
const bumpChoice = await selectPrompt({
|
|
652
|
+
message: releaseKind === 'candidate' ? 'Candidate' : 'Release',
|
|
658
653
|
choices: [
|
|
659
654
|
{ name: 'Patch', value: 'patch' },
|
|
660
655
|
{ name: 'Minor', value: 'minor' },
|
|
@@ -664,8 +659,11 @@ async function runInteractiveMenu() {
|
|
|
664
659
|
default: 'patch',
|
|
665
660
|
loop: false,
|
|
666
661
|
})
|
|
667
|
-
if (
|
|
668
|
-
|
|
662
|
+
if (bumpChoice === 'cancel') continue
|
|
663
|
+
|
|
664
|
+
const releaseArgs =
|
|
665
|
+
releaseKind === 'candidate' ? [bumpChoice, 'candidate'] : [bumpChoice]
|
|
666
|
+
return runSelectedAction(() => runReleaseScript(releaseArgs))
|
|
669
667
|
}
|
|
670
668
|
|
|
671
669
|
if (group === 'lint-typecheck') {
|
package/scripts/release.sh
CHANGED
|
@@ -3,12 +3,29 @@
|
|
|
3
3
|
# Interactive or scripted release for this package: bumps version with pnpm, pushes commits/tags,
|
|
4
4
|
# and creates a GitHub Release with generated notes (requires gh CLI, authenticated).
|
|
5
5
|
#
|
|
6
|
-
# Usage:
|
|
7
|
-
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# ./scripts/release.sh [patch|minor|major] # stable release (default mode)
|
|
8
|
+
# ./scripts/release.sh [patch|minor|major] candidate # prerelease tag X.Y.Z-rc.N
|
|
9
|
+
# ./scripts/release.sh candidate [patch|minor|major]
|
|
10
|
+
#
|
|
11
|
+
# Candidate tags follow SemVer prerelease format: <version>-rc.<number> (e.g. 2.8.0-rc.1).
|
|
8
12
|
|
|
9
13
|
set -euo pipefail
|
|
10
14
|
|
|
11
|
-
bump_type="
|
|
15
|
+
bump_type=""
|
|
16
|
+
release_mode="release"
|
|
17
|
+
|
|
18
|
+
for arg in "$@"; do
|
|
19
|
+
case "$arg" in
|
|
20
|
+
patch | minor | major) bump_type="$arg" ;;
|
|
21
|
+
release | candidate) release_mode="$arg" ;;
|
|
22
|
+
*)
|
|
23
|
+
echo "Unknown argument: $arg"
|
|
24
|
+
echo "Usage: ./scripts/release.sh [patch|minor|major] [release|candidate]"
|
|
25
|
+
exit 1
|
|
26
|
+
;;
|
|
27
|
+
esac
|
|
28
|
+
done
|
|
12
29
|
|
|
13
30
|
if [[ -z "$bump_type" ]]; then
|
|
14
31
|
echo "Select release type:"
|
|
@@ -29,7 +46,7 @@ if [[ -z "$bump_type" ]]; then
|
|
|
29
46
|
fi
|
|
30
47
|
|
|
31
48
|
if [[ "$bump_type" != "major" && "$bump_type" != "minor" && "$bump_type" != "patch" ]]; then
|
|
32
|
-
echo "Usage: ./scripts/release.sh [
|
|
49
|
+
echo "Usage: ./scripts/release.sh [patch|minor|major] [release|candidate]"
|
|
33
50
|
exit 1
|
|
34
51
|
fi
|
|
35
52
|
|
|
@@ -50,22 +67,61 @@ fi
|
|
|
50
67
|
|
|
51
68
|
previous_tag="$(git describe --tags --abbrev=0 2>/dev/null || true)"
|
|
52
69
|
|
|
53
|
-
pnpm version "$bump_type" --no-git-tag-version
|
|
54
|
-
|
|
55
|
-
new_version="$(node -p "require('./package.json').version")"
|
|
56
|
-
new_tag="v${new_version}"
|
|
57
|
-
|
|
58
70
|
is_fg_devkit_repo="$(
|
|
59
71
|
node -e "const fs=require('fs'); try { const p=JSON.parse(fs.readFileSync('./package.json','utf8')); process.stdout.write(p.name==='fg-devkit' ? 'yes' : 'no'); } catch { process.stdout.write('no'); }"
|
|
60
72
|
)"
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
sync_fg_devkit_package_files() {
|
|
75
|
+
if [[ "$is_fg_devkit_repo" == "yes" && -f "./scripts/sync-scaffold-vue-deps.mjs" && -f "./scaffold/vue/package.json" ]]; then
|
|
76
|
+
node ./scripts/sync-scaffold-vue-deps.mjs
|
|
77
|
+
git add package.json scaffold/vue/package.json
|
|
78
|
+
else
|
|
79
|
+
git add package.json
|
|
80
|
+
fi
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if [[ "$release_mode" == "candidate" ]]; then
|
|
84
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
85
|
+
new_version="$(node "${script_dir}/resolve-candidate-version.mjs" "$bump_type")"
|
|
86
|
+
|
|
87
|
+
node -e "
|
|
88
|
+
const fs = require('fs');
|
|
89
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
90
|
+
pkg.version = process.argv[1];
|
|
91
|
+
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
92
|
+
" "$new_version"
|
|
93
|
+
|
|
94
|
+
sync_fg_devkit_package_files
|
|
95
|
+
|
|
96
|
+
new_tag="v${new_version}"
|
|
97
|
+
git commit -m "${new_tag}"
|
|
98
|
+
git tag "${new_tag}"
|
|
99
|
+
|
|
100
|
+
git push
|
|
101
|
+
git push --tags
|
|
102
|
+
|
|
103
|
+
if [[ -n "$previous_tag" ]]; then
|
|
104
|
+
gh release create "$new_tag" \
|
|
105
|
+
--prerelease \
|
|
106
|
+
--generate-notes \
|
|
107
|
+
--notes-start-tag "$previous_tag"
|
|
108
|
+
else
|
|
109
|
+
gh release create "$new_tag" \
|
|
110
|
+
--prerelease \
|
|
111
|
+
--generate-notes
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
echo "Candidate release created: ${new_tag}"
|
|
115
|
+
exit 0
|
|
67
116
|
fi
|
|
68
117
|
|
|
118
|
+
pnpm version "$bump_type" --no-git-tag-version
|
|
119
|
+
|
|
120
|
+
new_version="$(node -p "require('./package.json').version")"
|
|
121
|
+
new_tag="v${new_version}"
|
|
122
|
+
|
|
123
|
+
sync_fg_devkit_package_files
|
|
124
|
+
|
|
69
125
|
git commit -m "${new_tag}"
|
|
70
126
|
git tag "${new_tag}"
|
|
71
127
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Resolves next candidate version: <semver>-rc.<n> (e.g. 2.8.0-rc.2).
|
|
4
|
+
* Usage: node resolve-candidate-version.mjs <patch|minor|major>
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs'
|
|
7
|
+
import { execSync } from 'node:child_process'
|
|
8
|
+
|
|
9
|
+
const bump = process.argv[2]
|
|
10
|
+
if (bump !== 'patch' && bump !== 'minor' && bump !== 'major') {
|
|
11
|
+
console.error('[resolve-candidate-version] Usage: node resolve-candidate-version.mjs <patch|minor|major>')
|
|
12
|
+
process.exit(1)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
|
|
16
|
+
const v = pkg.version
|
|
17
|
+
|
|
18
|
+
/** @param {string} ver @param {'patch'|'minor'|'major'} type */
|
|
19
|
+
function bumpSemver(ver, type) {
|
|
20
|
+
const m = ver.match(/^(\d+)\.(\d+)\.(\d+)$/)
|
|
21
|
+
if (!m) throw new Error(`Invalid semver base: ${ver}`)
|
|
22
|
+
let maj = Number(m[1])
|
|
23
|
+
let min = Number(m[2])
|
|
24
|
+
let pat = Number(m[3])
|
|
25
|
+
if (type === 'major') {
|
|
26
|
+
maj += 1
|
|
27
|
+
min = 0
|
|
28
|
+
pat = 0
|
|
29
|
+
} else if (type === 'minor') {
|
|
30
|
+
min += 1
|
|
31
|
+
pat = 0
|
|
32
|
+
} else if (type === 'patch') {
|
|
33
|
+
pat += 1
|
|
34
|
+
}
|
|
35
|
+
return `${maj}.${min}.${pat}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const plainMatch = v.match(/^(\d+\.\d+\.\d+)/)
|
|
39
|
+
if (!plainMatch) {
|
|
40
|
+
console.error(`[resolve-candidate-version] Could not parse version: ${v}`)
|
|
41
|
+
process.exit(1)
|
|
42
|
+
}
|
|
43
|
+
const plain = plainMatch[1]
|
|
44
|
+
|
|
45
|
+
/** @type {string} */
|
|
46
|
+
let targetBase
|
|
47
|
+
if (/^\d+\.\d+\.\d+-rc\.\d+$/.test(v)) {
|
|
48
|
+
targetBase = plain
|
|
49
|
+
} else {
|
|
50
|
+
targetBase = bumpSemver(plain, bump)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** @param {string} base @param {string[]} tags */
|
|
54
|
+
function maxRcForBase(base, tags) {
|
|
55
|
+
const prefixes = [`${base}-rc.`, `v${base}-rc.`]
|
|
56
|
+
let max = 0
|
|
57
|
+
for (const tag of tags) {
|
|
58
|
+
for (const prefix of prefixes) {
|
|
59
|
+
if (tag.startsWith(prefix)) {
|
|
60
|
+
const n = Number(tag.slice(prefix.length))
|
|
61
|
+
if (!Number.isNaN(n)) max = Math.max(max, n)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return max
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** @type {string[]} */
|
|
69
|
+
let tags = []
|
|
70
|
+
try {
|
|
71
|
+
tags = execSync('git tag -l', { encoding: 'utf8' })
|
|
72
|
+
.split('\n')
|
|
73
|
+
.map((t) => t.trim())
|
|
74
|
+
.filter(Boolean)
|
|
75
|
+
} catch {
|
|
76
|
+
tags = []
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const nextRc = maxRcForBase(targetBase, tags) + 1
|
|
80
|
+
process.stdout.write(`${targetBase}-rc.${nextRc}`)
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
* - removes legacy lint-staged config block from package.json
|
|
7
7
|
* - removes legacy prepare hook scripts for husky/lefthook bootstrap
|
|
8
8
|
*
|
|
9
|
-
* Lefthook `audit` skips only when `FG_DEVKIT_LEFTHOOK_AUDIT=0` for that commit (dep/vuln flow).
|
|
10
|
-
*
|
|
11
9
|
* Usage:
|
|
12
10
|
* node node_modules/fg-devkit/scripts/sync-consumer-lefthook.mjs
|
|
13
11
|
* pnpm exec fg-devkit setup-lefthook
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Lefthook pre-commit helper: runs `pnpm audit` unless this commit was started with
|
|
4
|
-
* `FG_DEVKIT_LEFTHOOK_AUDIT=0` (set only for the current `git commit` by `fg-devkit commit`
|
|
5
|
-
* when the user chooses "No" on dep/vuln). Unset or `1` runs audit.
|
|
6
|
-
*/
|
|
7
|
-
import { spawnSync } from 'node:child_process'
|
|
8
|
-
|
|
9
|
-
function shouldRunAudit() {
|
|
10
|
-
const v = process.env.FG_DEVKIT_LEFTHOOK_AUDIT
|
|
11
|
-
return v !== '0' && v !== 'false'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (!shouldRunAudit()) {
|
|
15
|
-
console.log('[fg-devkit] Skipping pnpm audit (FG_DEVKIT_LEFTHOOK_AUDIT=0 for this commit).')
|
|
16
|
-
process.exit(0)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const r = spawnSync('pnpm', ['audit'], {
|
|
20
|
-
stdio: 'inherit',
|
|
21
|
-
cwd: process.cwd(),
|
|
22
|
-
})
|
|
23
|
-
process.exit(r.status ?? 1)
|