zames_pro 2.65.0 → 2.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +32 -10
- package/README.ru.md +502 -0
- package/dist/browser.js +4 -0
- package/dist/config-menu.js +19 -20
- package/dist/index.js +63 -43
- package/dist/input/layout.js +22 -0
- package/dist/input.js +53 -4
- package/dist/spinner.js +5 -5
- package/dist/system-prompt.js +12 -3
- package/package.json +13 -4
- package/scripts/release.mts +157 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import {
|
|
6
|
+
parseConventionalCommit,
|
|
7
|
+
groupCommits,
|
|
8
|
+
renderChangelogDraft,
|
|
9
|
+
} from '../src/changelog.js'
|
|
10
|
+
|
|
11
|
+
// One-command release prep (BACKLOG G4). The manual sequence — bump
|
|
12
|
+
// package.json, write a dated CHANGELOG section, commit `chore: release X.Y.Z`,
|
|
13
|
+
// tag — is easy to get wrong, so this script does the deterministic parts and
|
|
14
|
+
// STOPS before the push. Pushing the branch and the tag is deliberately a
|
|
15
|
+
// separate, explicit step: the tag triggers the npm publish workflow, and that
|
|
16
|
+
// must never happen by accident.
|
|
17
|
+
//
|
|
18
|
+
// Usage:
|
|
19
|
+
// npm run release patch # bug fixes
|
|
20
|
+
// npm run release minor # new features
|
|
21
|
+
// npm run release major # breaking changes
|
|
22
|
+
// npm run release patch --no-tests # skip the local test run (CI still runs)
|
|
23
|
+
// npm run release patch --dry-run # print the plan, change nothing
|
|
24
|
+
//
|
|
25
|
+
// Run via tsx so it can reuse src/changelog.ts (the conventional-commit
|
|
26
|
+
// parser) — a plain .mjs cannot import the TypeScript sources.
|
|
27
|
+
|
|
28
|
+
const NL = String.fromCharCode(10)
|
|
29
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
30
|
+
|
|
31
|
+
function git(args: string[], allowFail = false): string {
|
|
32
|
+
const r = spawnSync('git', args, { cwd: root, encoding: 'utf-8' })
|
|
33
|
+
if (r.status !== 0 && !allowFail) {
|
|
34
|
+
console.error('git ' + args.join(' ') + ' failed:' + NL + (r.stderr || ''))
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}
|
|
37
|
+
return (r.stdout || '').toString()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function fail(msg: string): never {
|
|
41
|
+
console.error('release: ' + msg)
|
|
42
|
+
process.exit(1)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function run(cmd: string, args: string[]): void {
|
|
46
|
+
const r = spawnSync(cmd, args, { cwd: root, stdio: 'inherit', shell: process.platform === 'win32' })
|
|
47
|
+
if (r.status !== 0) fail(cmd + ' ' + args.join(' ') + ' exited ' + r.status)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const argv = process.argv.slice(2)
|
|
51
|
+
const flags = new Set(argv.filter((a) => a.startsWith('--')))
|
|
52
|
+
const bump = argv.find((a) => !a.startsWith('--'))
|
|
53
|
+
const dryRun = flags.has('--dry-run')
|
|
54
|
+
const skipTests = flags.has('--no-tests')
|
|
55
|
+
|
|
56
|
+
if (bump !== 'patch' && bump !== 'minor' && bump !== 'major') {
|
|
57
|
+
fail('usage: npm run release patch|minor|major [--no-tests] [--dry-run]')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 1. Clean tree: a release commit must contain ONLY the version bump and the
|
|
61
|
+
// changelog section, never half-finished work.
|
|
62
|
+
const dirty = git(['status', '--porcelain']).trim()
|
|
63
|
+
if (dirty) {
|
|
64
|
+
fail('working tree is not clean:' + NL + dirty)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. Gates. The git hooks run these on commit/push too, but a release should
|
|
68
|
+
// fail EARLY, before package.json/CHANGELOG are touched.
|
|
69
|
+
if (!skipTests) {
|
|
70
|
+
run('npm', ['run', 'typecheck'])
|
|
71
|
+
run('npm', ['test'])
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 3. Compute the next version from package.json.
|
|
75
|
+
const pkgPath = path.join(root, 'package.json')
|
|
76
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as {
|
|
77
|
+
version: string
|
|
78
|
+
}
|
|
79
|
+
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(pkg.version)
|
|
80
|
+
if (!m) fail('package.json version is not a plain x.y.z: ' + pkg.version)
|
|
81
|
+
let [major, minor, patch] = [Number(m[1]), Number(m[2]), Number(m[3])]
|
|
82
|
+
if (bump === 'major') {
|
|
83
|
+
major += 1
|
|
84
|
+
minor = 0
|
|
85
|
+
patch = 0
|
|
86
|
+
} else if (bump === 'minor') {
|
|
87
|
+
minor += 1
|
|
88
|
+
patch = 0
|
|
89
|
+
} else {
|
|
90
|
+
patch += 1
|
|
91
|
+
}
|
|
92
|
+
const next = major + '.' + minor + '.' + patch
|
|
93
|
+
const tag = 'v' + next
|
|
94
|
+
|
|
95
|
+
if (git(['tag', '--list', tag]).trim()) {
|
|
96
|
+
fail('tag ' + tag + ' already exists')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 4. Build the dated CHANGELOG section from conventional commits since the
|
|
100
|
+
// last tag. English commit subjects become the section body; the operator can
|
|
101
|
+
// still reword the section afterwards.
|
|
102
|
+
const latestTag = git(['describe', '--tags', '--abbrev=0'], true).trim()
|
|
103
|
+
const ref = latestTag || git(['rev-list', '--max-parents=0', 'HEAD']).trim().split(NL).pop() || 'HEAD'
|
|
104
|
+
const subjects = git(['log', '--no-merges', '--pretty=format:%s', ref + '..HEAD'])
|
|
105
|
+
.split(NL)
|
|
106
|
+
.map((s) => s.trim())
|
|
107
|
+
.filter(Boolean)
|
|
108
|
+
const parsed = subjects
|
|
109
|
+
.map(parseConventionalCommit)
|
|
110
|
+
.filter((c): c is NonNullable<typeof c> => c !== null)
|
|
111
|
+
const body = renderChangelogDraft(groupCommits(parsed))
|
|
112
|
+
|
|
113
|
+
// CHANGELOG groups are `### Added` (Keep a Changelog), the draft renders
|
|
114
|
+
// `## Added` — normalize here.
|
|
115
|
+
const sectionBody = body
|
|
116
|
+
.split(NL)
|
|
117
|
+
.map((line) => (line.startsWith('## ') ? '#' + line : line))
|
|
118
|
+
.join(NL)
|
|
119
|
+
const date = new Date().toISOString().slice(0, 10)
|
|
120
|
+
const section =
|
|
121
|
+
'## [' + next + '] - ' + date + NL + NL + (sectionBody || '### Changed' + NL + NL + '- Internal improvements.') + NL
|
|
122
|
+
|
|
123
|
+
if (dryRun) {
|
|
124
|
+
console.log('release(dry-run): ' + pkg.version + ' -> ' + next)
|
|
125
|
+
console.log('tag: ' + tag)
|
|
126
|
+
console.log('since: ' + ref)
|
|
127
|
+
console.log('CHANGELOG section:' + NL + section)
|
|
128
|
+
process.exit(0)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 5. Insert the section right after the `## [Unreleased]` heading.
|
|
132
|
+
const changelogPath = path.join(root, 'CHANGELOG.md')
|
|
133
|
+
const changelog = fs.readFileSync(changelogPath, 'utf-8')
|
|
134
|
+
const anchor = '## [Unreleased]'
|
|
135
|
+
const idx = changelog.indexOf(anchor)
|
|
136
|
+
if (idx < 0) fail('CHANGELOG.md has no "' + anchor + '" heading')
|
|
137
|
+
const afterAnchor = idx + anchor.length
|
|
138
|
+
const updated =
|
|
139
|
+
changelog.slice(0, afterAnchor) +
|
|
140
|
+
NL + NL + section +
|
|
141
|
+
changelog.slice(afterAnchor).replace(/^\n+/, NL)
|
|
142
|
+
fs.writeFileSync(changelogPath, updated)
|
|
143
|
+
|
|
144
|
+
// 6. Bump package.json, preserving its exact formatting (single-line replace
|
|
145
|
+
// of the version value only).
|
|
146
|
+
const pkgText = fs.readFileSync(pkgPath, 'utf-8')
|
|
147
|
+
fs.writeFileSync(pkgPath, pkgText.replace('"version": "' + pkg.version + '"', '"version": "' + next + '"'))
|
|
148
|
+
|
|
149
|
+
// 7. Commit + tag. The push is intentionally NOT done here.
|
|
150
|
+
git(['add', 'package.json', 'CHANGELOG.md'])
|
|
151
|
+
git(['commit', '-m', 'chore: release ' + next])
|
|
152
|
+
git(['tag', tag])
|
|
153
|
+
|
|
154
|
+
console.log(NL + 'release: committed and tagged ' + tag)
|
|
155
|
+
console.log('Next (explicit) step — publish via CI:')
|
|
156
|
+
console.log(' git push')
|
|
157
|
+
console.log(' git push origin ' + tag)
|