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/release.sh
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
3
|
# Interactive or scripted release for this package: bumps version with pnpm, pushes commits/tags,
|
|
4
|
-
# and creates a GitHub Release
|
|
4
|
+
# and creates a GitHub Release (requires gh CLI, authenticated).
|
|
5
|
+
#
|
|
6
|
+
# Repos with cliff.toml / CHANGELOG files run git-cliff generation and an interactive
|
|
7
|
+
# verification step before commit. Others use gh --generate-notes.
|
|
5
8
|
#
|
|
6
9
|
# Usage:
|
|
7
|
-
# ./scripts/release.sh [patch|minor|major]
|
|
8
|
-
# ./scripts/release.sh [patch|minor|major] candidate
|
|
9
|
-
# ./scripts/release.sh candidate [patch|minor|major]
|
|
10
|
+
# ./scripts/release.sh [patch|minor|major] [--skip-release-notes]
|
|
11
|
+
# ./scripts/release.sh [patch|minor|major] candidate [--skip-release-notes]
|
|
10
12
|
#
|
|
11
13
|
# Candidate tags follow SemVer prerelease format: <version>-rc.<number> (e.g. 2.8.0-rc.1).
|
|
12
14
|
|
|
@@ -14,14 +16,17 @@ set -euo pipefail
|
|
|
14
16
|
|
|
15
17
|
bump_type=""
|
|
16
18
|
release_mode="release"
|
|
19
|
+
skip_release_notes="${FG_DEVKIT_SKIP_RELEASE_NOTES:-0}"
|
|
20
|
+
use_release_notes=0
|
|
17
21
|
|
|
18
22
|
for arg in "$@"; do
|
|
19
23
|
case "$arg" in
|
|
20
24
|
patch | minor | major) bump_type="$arg" ;;
|
|
21
25
|
release | candidate) release_mode="$arg" ;;
|
|
26
|
+
--skip-release-notes) skip_release_notes=1 ;;
|
|
22
27
|
*)
|
|
23
28
|
echo "Unknown argument: $arg"
|
|
24
|
-
echo "Usage: ./scripts/release.sh [patch|minor|major] [release|candidate]"
|
|
29
|
+
echo "Usage: ./scripts/release.sh [patch|minor|major] [release|candidate] [--skip-release-notes]"
|
|
25
30
|
exit 1
|
|
26
31
|
;;
|
|
27
32
|
esac
|
|
@@ -80,8 +85,142 @@ sync_fg_devkit_package_files() {
|
|
|
80
85
|
fi
|
|
81
86
|
}
|
|
82
87
|
|
|
88
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
89
|
+
|
|
90
|
+
has_release_notes_config() {
|
|
91
|
+
[[ -f cliff.toml || -f cliff.user.toml || -f CHANGELOG.md || -f CHANGELOG.user.md ]]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
generate_release_notes() {
|
|
95
|
+
local tag="$1"
|
|
96
|
+
if [[ "$skip_release_notes" == "1" ]]; then
|
|
97
|
+
return 0
|
|
98
|
+
fi
|
|
99
|
+
if ! has_release_notes_config; then
|
|
100
|
+
return 0
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
local args=()
|
|
104
|
+
if [[ -n "$tag" ]]; then
|
|
105
|
+
args+=("--tag=${tag}")
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
node "${script_dir}/generate-release-notes.mjs" "${args[@]}"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
discard_release_notes_changes() {
|
|
112
|
+
for file in CHANGELOG.md CHANGELOG.user.md CHANGELOG.user.draft.md; do
|
|
113
|
+
if [[ ! -f "$file" ]]; then
|
|
114
|
+
continue
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if git ls-files --error-unmatch "$file" >/dev/null 2>&1; then
|
|
118
|
+
git checkout -- "$file"
|
|
119
|
+
else
|
|
120
|
+
rm -f "$file"
|
|
121
|
+
fi
|
|
122
|
+
done
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
verify_release_notes() {
|
|
126
|
+
if [[ "$skip_release_notes" == "1" ]]; then
|
|
127
|
+
return 0
|
|
128
|
+
fi
|
|
129
|
+
if ! has_release_notes_config; then
|
|
130
|
+
return 0
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
set +e
|
|
134
|
+
FG_DEVKIT_RELEASE_TAG="${new_tag:-}" node "${script_dir}/verify-release-notes.mjs"
|
|
135
|
+
local code=$?
|
|
136
|
+
set -e
|
|
137
|
+
|
|
138
|
+
if [[ "$code" -eq 2 ]]; then
|
|
139
|
+
skip_release_notes=1
|
|
140
|
+
use_release_notes=0
|
|
141
|
+
discard_release_notes_changes
|
|
142
|
+
echo "Release notes skipped. Using GitHub-generated notes."
|
|
143
|
+
return 0
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
return "$code"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
stage_release_notes() {
|
|
150
|
+
if [[ "$use_release_notes" != "1" ]]; then
|
|
151
|
+
return 0
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
for file in CHANGELOG.md CHANGELOG.user.md CHANGELOG.user.draft.md; do
|
|
155
|
+
if [[ -f "$file" ]]; then
|
|
156
|
+
git add "$file"
|
|
157
|
+
fi
|
|
158
|
+
done
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
run_release_notes_phase() {
|
|
162
|
+
local tag="$1"
|
|
163
|
+
|
|
164
|
+
if [[ "$skip_release_notes" == "1" ]]; then
|
|
165
|
+
echo "Skipping release notes."
|
|
166
|
+
return 0
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
if ! has_release_notes_config; then
|
|
170
|
+
return 0
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
use_release_notes=1
|
|
174
|
+
generate_release_notes "$tag"
|
|
175
|
+
verify_release_notes
|
|
176
|
+
stage_release_notes
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
create_github_release() {
|
|
180
|
+
local tag="$1"
|
|
181
|
+
local prerelease_flag="${2:-}"
|
|
182
|
+
|
|
183
|
+
if [[ "$use_release_notes" == "1" ]] && [[ -f CHANGELOG.md ]]; then
|
|
184
|
+
if [[ -n "$prerelease_flag" ]]; then
|
|
185
|
+
gh release create "$tag" \
|
|
186
|
+
--prerelease \
|
|
187
|
+
--title "$tag" \
|
|
188
|
+
--notes-file CHANGELOG.md
|
|
189
|
+
else
|
|
190
|
+
gh release create "$tag" \
|
|
191
|
+
--latest \
|
|
192
|
+
--title "$tag" \
|
|
193
|
+
--notes-file CHANGELOG.md
|
|
194
|
+
fi
|
|
195
|
+
return 0
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
if [[ -n "$previous_tag" ]]; then
|
|
199
|
+
if [[ -n "$prerelease_flag" ]]; then
|
|
200
|
+
gh release create "$tag" \
|
|
201
|
+
--prerelease \
|
|
202
|
+
--generate-notes \
|
|
203
|
+
--notes-start-tag "$previous_tag"
|
|
204
|
+
else
|
|
205
|
+
gh release create "$tag" \
|
|
206
|
+
--latest \
|
|
207
|
+
--generate-notes \
|
|
208
|
+
--notes-start-tag "$previous_tag"
|
|
209
|
+
fi
|
|
210
|
+
else
|
|
211
|
+
if [[ -n "$prerelease_flag" ]]; then
|
|
212
|
+
gh release create "$tag" \
|
|
213
|
+
--prerelease \
|
|
214
|
+
--generate-notes
|
|
215
|
+
else
|
|
216
|
+
gh release create "$tag" \
|
|
217
|
+
--latest \
|
|
218
|
+
--generate-notes
|
|
219
|
+
fi
|
|
220
|
+
fi
|
|
221
|
+
}
|
|
222
|
+
|
|
83
223
|
if [[ "$release_mode" == "candidate" ]]; then
|
|
84
|
-
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
85
224
|
new_version="$(node "${script_dir}/resolve-candidate-version.mjs" "$bump_type")"
|
|
86
225
|
|
|
87
226
|
node -e "
|
|
@@ -94,22 +233,16 @@ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
|
94
233
|
sync_fg_devkit_package_files
|
|
95
234
|
|
|
96
235
|
new_tag="v${new_version}"
|
|
236
|
+
|
|
237
|
+
run_release_notes_phase "$new_tag"
|
|
238
|
+
|
|
97
239
|
git commit -m "${new_tag}"
|
|
98
240
|
git tag "${new_tag}"
|
|
99
241
|
|
|
100
242
|
git push
|
|
101
243
|
git push --tags
|
|
102
244
|
|
|
103
|
-
|
|
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
|
|
245
|
+
create_github_release "$new_tag" "--prerelease"
|
|
113
246
|
|
|
114
247
|
echo "Candidate release created: ${new_tag}"
|
|
115
248
|
exit 0
|
|
@@ -122,21 +255,14 @@ new_tag="v${new_version}"
|
|
|
122
255
|
|
|
123
256
|
sync_fg_devkit_package_files
|
|
124
257
|
|
|
258
|
+
run_release_notes_phase "$new_tag"
|
|
259
|
+
|
|
125
260
|
git commit -m "${new_tag}"
|
|
126
261
|
git tag "${new_tag}"
|
|
127
262
|
|
|
128
263
|
git push
|
|
129
264
|
git push --tags
|
|
130
265
|
|
|
131
|
-
|
|
132
|
-
gh release create "$new_tag" \
|
|
133
|
-
--latest \
|
|
134
|
-
--generate-notes \
|
|
135
|
-
--notes-start-tag "$previous_tag"
|
|
136
|
-
else
|
|
137
|
-
gh release create "$new_tag" \
|
|
138
|
-
--latest \
|
|
139
|
-
--generate-notes
|
|
140
|
-
fi
|
|
266
|
+
create_github_release "$new_tag"
|
|
141
267
|
|
|
142
268
|
echo "Release created: ${new_tag}"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Only updates keys that already exist in package.json (does not add new dependencies from the policy).
|
|
6
6
|
* Aligns managedDependencies, managedAnywhere (only where the package is already declared), optional
|
|
7
|
-
* policy overrides into pnpm.overrides, and logs each change.
|
|
7
|
+
* policy overrides into pnpm.overrides, toolchain.packageManager into package.json, and logs each change.
|
|
8
8
|
*
|
|
9
9
|
* Refuses to run if the policy file contains any non-pinned versions.
|
|
10
10
|
*
|
|
@@ -16,6 +16,10 @@ import path from 'node:path'
|
|
|
16
16
|
import { fileURLToPath } from 'node:url'
|
|
17
17
|
|
|
18
18
|
import { collectPinViolations } from './dependency-policy-pins.mjs'
|
|
19
|
+
import {
|
|
20
|
+
collectToolchainPolicyViolations,
|
|
21
|
+
resolveExpectedPackageManager,
|
|
22
|
+
} from './dependency-policy-toolchain.mjs'
|
|
19
23
|
import { resolveDependencyPolicyPath } from './resolve-dependency-policy-path.mjs'
|
|
20
24
|
|
|
21
25
|
const cwd = process.cwd()
|
|
@@ -44,6 +48,7 @@ const policyErrors = [
|
|
|
44
48
|
collectPinViolations(`managedDependencies.${section}.`, deps),
|
|
45
49
|
),
|
|
46
50
|
...collectPinViolations('managedAnywhere.', policy.managedAnywhere ?? {}),
|
|
51
|
+
...collectToolchainPolicyViolations(policy),
|
|
47
52
|
]
|
|
48
53
|
|
|
49
54
|
if (policyErrors.length > 0) {
|
|
@@ -136,6 +141,12 @@ for (const [name, version] of Object.entries(getRelevantPolicyOverrides(policy.o
|
|
|
136
141
|
}
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
const expectedPackageManager = resolveExpectedPackageManager(policy, path.resolve(scriptDir, '..'))
|
|
145
|
+
if (expectedPackageManager !== undefined && pkg.packageManager !== expectedPackageManager) {
|
|
146
|
+
changes.push(`packageManager: ${pkg.packageManager ?? '(missing)'} -> ${expectedPackageManager}`)
|
|
147
|
+
pkg.packageManager = expectedPackageManager
|
|
148
|
+
}
|
|
149
|
+
|
|
139
150
|
if (changes.length === 0) {
|
|
140
151
|
console.log('[sync-deps] package.json already matches dependency policy')
|
|
141
152
|
process.exit(0)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copy default cliff configs into a consumer repo when missing and ensure the
|
|
4
|
+
* generate-release-notes npm script is present.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node node_modules/fg-devkit/scripts/sync-release-notes-config.mjs
|
|
8
|
+
* pnpm exec fg-devkit sync-release-notes-config
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'node:fs'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import { fgRoot } from './release-notes-common.mjs'
|
|
14
|
+
|
|
15
|
+
const REQUIRED_SCRIPT = 'generate:release-notes'
|
|
16
|
+
const REQUIRED_SCRIPT_VALUE = 'pnpm exec fgd generate-release-notes'
|
|
17
|
+
|
|
18
|
+
const CONFIG_FILES = ['cliff.toml', 'cliff.user.toml']
|
|
19
|
+
|
|
20
|
+
function copyIfMissing(fileName, cwd) {
|
|
21
|
+
const target = path.join(cwd, fileName)
|
|
22
|
+
if (fs.existsSync(target)) {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const source = path.join(fgRoot, 'configs', fileName)
|
|
27
|
+
if (!fs.existsSync(source)) {
|
|
28
|
+
console.error(`[sync-release-notes-config] Missing template: ${source}`)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fs.copyFileSync(source, target)
|
|
33
|
+
return fileName
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function ensureUserChangelog(cwd) {
|
|
37
|
+
const target = path.join(cwd, 'CHANGELOG.user.md')
|
|
38
|
+
if (fs.existsSync(target)) {
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fs.writeFileSync(target, '')
|
|
43
|
+
return 'CHANGELOG.user.md'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function main() {
|
|
47
|
+
const cwd = process.cwd()
|
|
48
|
+
const packageJsonPath = path.join(cwd, 'package.json')
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
51
|
+
console.error(`[sync-release-notes-config] package.json not found at ${packageJsonPath}`)
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const copied = []
|
|
56
|
+
for (const fileName of CONFIG_FILES) {
|
|
57
|
+
const result = copyIfMissing(fileName, cwd)
|
|
58
|
+
if (result) {
|
|
59
|
+
copied.push(result)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const createdChangelog = ensureUserChangelog(cwd)
|
|
64
|
+
if (createdChangelog) {
|
|
65
|
+
copied.push(createdChangelog)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let pkg
|
|
69
|
+
try {
|
|
70
|
+
pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error(`[sync-release-notes-config] Failed to parse package.json: ${err.message}`)
|
|
73
|
+
process.exit(1)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pkg.scripts = pkg.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : {}
|
|
77
|
+
let scriptUpdated = false
|
|
78
|
+
if (pkg.scripts[REQUIRED_SCRIPT] !== REQUIRED_SCRIPT_VALUE) {
|
|
79
|
+
pkg.scripts[REQUIRED_SCRIPT] = REQUIRED_SCRIPT_VALUE
|
|
80
|
+
scriptUpdated = true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (scriptUpdated) {
|
|
84
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (copied.length === 0 && !scriptUpdated) {
|
|
88
|
+
console.log('[sync-release-notes-config] Release-notes config already up to date.')
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (copied.length > 0) {
|
|
93
|
+
console.log('[sync-release-notes-config] Added files:')
|
|
94
|
+
for (const fileName of copied) {
|
|
95
|
+
console.log(` - ${fileName}`)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (scriptUpdated) {
|
|
100
|
+
console.log(`[sync-release-notes-config] Updated script: ${REQUIRED_SCRIPT}`)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
main()
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Aligns scaffold/vue/package.json dependency versions with dependency-policy.vue-workspace.json.
|
|
4
|
-
* Also pins scaffold devDependency `fg-devkit` to the fg-devkit package version at publish time
|
|
4
|
+
* Also pins scaffold devDependency `fg-devkit` to the fg-devkit package version at publish time,
|
|
5
|
+
* and syncs toolchain.packageManager into scaffold package.json and Dockerfile.local.
|
|
5
6
|
*
|
|
6
7
|
* Invoked automatically after refresh-dependency-policy-from-repos.mjs writes the policy, and
|
|
7
8
|
* can be run manually: node …/sync-scaffold-vue-deps.mjs [policy-file]
|
|
@@ -12,13 +13,27 @@ import path from 'node:path'
|
|
|
12
13
|
import { fileURLToPath } from 'node:url'
|
|
13
14
|
|
|
14
15
|
import { resolveDependencyPolicyPath } from './resolve-dependency-policy-path.mjs'
|
|
16
|
+
import {
|
|
17
|
+
extractPnpmVersion,
|
|
18
|
+
resolveExpectedPackageManager,
|
|
19
|
+
syncPolicyToolchainFromFgDevkit,
|
|
20
|
+
} from './dependency-policy-toolchain.mjs'
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
23
|
* @param {{ fgDevkitRoot: string, policyPath: string }} options
|
|
18
24
|
*/
|
|
19
25
|
export function syncScaffoldVueDepsFromPolicy(options) {
|
|
20
26
|
const { fgDevkitRoot, policyPath } = options
|
|
21
|
-
|
|
27
|
+
let policy = JSON.parse(fs.readFileSync(policyPath, 'utf8'))
|
|
28
|
+
const { policy: syncedPolicy, changed: toolchainChanged } = syncPolicyToolchainFromFgDevkit(
|
|
29
|
+
policy,
|
|
30
|
+
fgDevkitRoot,
|
|
31
|
+
)
|
|
32
|
+
policy = syncedPolicy
|
|
33
|
+
if (toolchainChanged) {
|
|
34
|
+
fs.writeFileSync(policyPath, `${JSON.stringify(policy, null, 2)}\n`)
|
|
35
|
+
console.log('[sync-scaffold-vue-deps] Updated toolchain in', policyPath)
|
|
36
|
+
}
|
|
22
37
|
const scaffoldPkgPath = path.join(fgDevkitRoot, 'scaffold/vue/package.json')
|
|
23
38
|
if (!fs.existsSync(scaffoldPkgPath)) {
|
|
24
39
|
console.warn('[sync-scaffold-vue-deps] No scaffold package at', scaffoldPkgPath)
|
|
@@ -53,6 +68,24 @@ export function syncScaffoldVueDepsFromPolicy(options) {
|
|
|
53
68
|
applySection('dependencies')
|
|
54
69
|
applySection('devDependencies')
|
|
55
70
|
|
|
71
|
+
const expectedPackageManager = resolveExpectedPackageManager(policy, fgDevkitRoot)
|
|
72
|
+
if (expectedPackageManager !== undefined) {
|
|
73
|
+
pkg.packageManager = expectedPackageManager
|
|
74
|
+
const dockerfilePath = path.join(fgDevkitRoot, 'scaffold/vue/Dockerfile.local')
|
|
75
|
+
const pnpmVersion = extractPnpmVersion(expectedPackageManager)
|
|
76
|
+
if (pnpmVersion && fs.existsSync(dockerfilePath)) {
|
|
77
|
+
const dockerfile = fs.readFileSync(dockerfilePath, 'utf8')
|
|
78
|
+
const updatedDockerfile = dockerfile.replace(
|
|
79
|
+
/corepack prepare pnpm@[^\s]+ --activate/,
|
|
80
|
+
`corepack prepare pnpm@${pnpmVersion} --activate`,
|
|
81
|
+
)
|
|
82
|
+
if (updatedDockerfile !== dockerfile) {
|
|
83
|
+
fs.writeFileSync(dockerfilePath, updatedDockerfile)
|
|
84
|
+
console.log('[sync-scaffold-vue-deps] Updated', dockerfilePath)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
56
89
|
fs.writeFileSync(scaffoldPkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
|
|
57
90
|
console.log('[sync-scaffold-vue-deps] Updated', scaffoldPkgPath)
|
|
58
91
|
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Interactive verification step before committing release notes.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node node_modules/fg-devkit/scripts/verify-release-notes.mjs
|
|
7
|
+
* pnpm exec fg-devkit verify-release-notes
|
|
8
|
+
*
|
|
9
|
+
* Exit codes: 0 = approved, 1 = aborted/error, 2 = skip release notes
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawnSync } from 'node:child_process'
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import confirm from '@inquirer/confirm'
|
|
16
|
+
import select from '@inquirer/select'
|
|
17
|
+
import {
|
|
18
|
+
hasReleaseNotesConfig,
|
|
19
|
+
readPreviewFiles,
|
|
20
|
+
runSyncUserChangelog,
|
|
21
|
+
} from './release-notes-common.mjs'
|
|
22
|
+
|
|
23
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
24
|
+
const cwd = process.cwd()
|
|
25
|
+
|
|
26
|
+
function showPreview() {
|
|
27
|
+
const previews = readPreviewFiles(cwd)
|
|
28
|
+
|
|
29
|
+
if (previews.length === 0) {
|
|
30
|
+
console.log('[fg-devkit] No changelog files to preview yet.')
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const { file, content } of previews) {
|
|
35
|
+
const divider = '─'.repeat(Math.max(8, 50 - file.length))
|
|
36
|
+
console.log(`\n── ${file} ${divider}`)
|
|
37
|
+
console.log(content.trimEnd())
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function runGenerateReleaseNotes() {
|
|
42
|
+
const generateScript = path.join(scriptDir, 'generate-release-notes.mjs')
|
|
43
|
+
const tagArg = process.env.FG_DEVKIT_RELEASE_TAG
|
|
44
|
+
const args = [generateScript]
|
|
45
|
+
if (tagArg) {
|
|
46
|
+
args.push(`--tag=${tagArg}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const r = spawnSync(process.execPath, args, {
|
|
50
|
+
stdio: 'inherit',
|
|
51
|
+
cwd,
|
|
52
|
+
env: process.env,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return r.status ?? 1
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function openEditor(fileName) {
|
|
59
|
+
const editor = process.env.EDITOR ?? process.env.VISUAL ?? 'nano'
|
|
60
|
+
const filePath = path.join(cwd, fileName)
|
|
61
|
+
const r = spawnSync(editor, [filePath], {
|
|
62
|
+
stdio: 'inherit',
|
|
63
|
+
cwd,
|
|
64
|
+
env: process.env,
|
|
65
|
+
shell: false,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
if (r.error) {
|
|
69
|
+
console.error(`[fg-devkit] Could not open ${editor}: ${r.error.message}`)
|
|
70
|
+
return 1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return r.status ?? 1
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function main() {
|
|
77
|
+
if (!hasReleaseNotesConfig(cwd)) {
|
|
78
|
+
console.log('[fg-devkit] No release-notes config; skipping verification.')
|
|
79
|
+
return 0
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
while (true) {
|
|
83
|
+
showPreview()
|
|
84
|
+
|
|
85
|
+
const action = await select({
|
|
86
|
+
message: 'Granska release notes innan commit',
|
|
87
|
+
choices: [
|
|
88
|
+
{ name: 'Godkänn och fortsätt', value: 'accept' },
|
|
89
|
+
{ name: 'Hoppa över release notes (GitHub-genererade notes)', value: 'skip' },
|
|
90
|
+
{ name: 'Redigera CHANGELOG.user.draft.md i $EDITOR', value: 'edit-draft' },
|
|
91
|
+
{ name: 'Redigera CHANGELOG.user.md i $EDITOR', value: 'edit-user' },
|
|
92
|
+
{ name: 'Synka user changelog efter redigering', value: 'sync' },
|
|
93
|
+
{ name: 'Generera om från commits', value: 'regen' },
|
|
94
|
+
{ name: 'Avbryt release', value: 'abort' },
|
|
95
|
+
],
|
|
96
|
+
default: 'accept',
|
|
97
|
+
loop: false,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
if (action === 'abort') {
|
|
101
|
+
console.log('[fg-devkit] Release avbruten.')
|
|
102
|
+
return 1
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (action === 'skip') {
|
|
106
|
+
console.log('[fg-devkit] Release notes hoppas över.')
|
|
107
|
+
return 2
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (action === 'regen') {
|
|
111
|
+
const code = runGenerateReleaseNotes()
|
|
112
|
+
if (code !== 0) {
|
|
113
|
+
return code
|
|
114
|
+
}
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (action === 'edit-draft') {
|
|
119
|
+
const code = openEditor('CHANGELOG.user.draft.md')
|
|
120
|
+
if (code !== 0) {
|
|
121
|
+
return code
|
|
122
|
+
}
|
|
123
|
+
const syncCode = runSyncUserChangelog(cwd)
|
|
124
|
+
if (syncCode !== 0) {
|
|
125
|
+
return syncCode
|
|
126
|
+
}
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (action === 'edit-user') {
|
|
131
|
+
const code = openEditor('CHANGELOG.user.md')
|
|
132
|
+
if (code !== 0) {
|
|
133
|
+
return code
|
|
134
|
+
}
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (action === 'sync') {
|
|
139
|
+
const syncCode = runSyncUserChangelog(cwd)
|
|
140
|
+
if (syncCode !== 0) {
|
|
141
|
+
return syncCode
|
|
142
|
+
}
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const ok = await confirm({
|
|
147
|
+
message: 'Commit:a version + changelogs och skapa GitHub-release?',
|
|
148
|
+
default: true,
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
if (ok) {
|
|
152
|
+
return 0
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const code = await main()
|
|
158
|
+
process.exit(code)
|