@wwkit/shared 1.0.3 → 1.0.5
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/package.json +2 -2
- package/src/release.js +138 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wwkit/shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"author": "bluesliu <langcai163@163.com>",
|
|
5
5
|
"description": "Shared utilities for @wwkit packages — config loader, platform detection",
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
"scripts": {
|
|
36
36
|
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --no-cache tests/unit",
|
|
37
37
|
"test:all": "node --experimental-vm-modules node_modules/jest/bin/jest.js --no-cache tests/unit",
|
|
38
|
-
"release": "
|
|
38
|
+
"release": "node bin/release-pkg.js"
|
|
39
39
|
}
|
|
40
40
|
}
|
package/src/release.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { resolve } from 'node:path'
|
|
4
|
+
|
|
5
|
+
// ── helpers ──────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
export function bumpPatch(version) {
|
|
8
|
+
const parts = version.split('.').map(Number)
|
|
9
|
+
parts[2]++
|
|
10
|
+
return parts.join('.')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function runQuiet(cmd, opts = {}) {
|
|
14
|
+
return execSync(cmd, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], ...opts }).trim()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function run(cmd, { dryRun = false, ...execOpts } = {}) {
|
|
18
|
+
if (dryRun) { console.log(` $ ${cmd}`); return }
|
|
19
|
+
execSync(cmd, { stdio: 'inherit', ...execOpts })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function readPackage(pkgDir) {
|
|
23
|
+
const pkgPath = resolve(pkgDir, 'package.json')
|
|
24
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
|
|
25
|
+
return { pkg, pkgPath, name: pkg.name, version: pkg.version }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ── preflight ────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
// 解析实际发布用的 registry:优先 package.json 的 publishConfig.registry,
|
|
31
|
+
// 否则用该目录下 npm 配置的 registry。.npmrc 可能指向镜像,但发布走 publishConfig。
|
|
32
|
+
function resolvePublishRegistry(pkgDir) {
|
|
33
|
+
try {
|
|
34
|
+
const { pkg } = readPackage(pkgDir)
|
|
35
|
+
if (pkg.publishConfig?.registry) return pkg.publishConfig.registry
|
|
36
|
+
} catch {}
|
|
37
|
+
try {
|
|
38
|
+
return runQuiet('npm config get registry', { cwd: pkgDir })
|
|
39
|
+
} catch {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 发布前检查: git 工作区干净 + npm 已登录。失败抛出 Error。
|
|
45
|
+
// pkgDir 给定时,针对该包 publishConfig.registry 检查登录(而非 .npmrc 里可能指向镜像的 registry)。
|
|
46
|
+
export function preflight({ dryRun = false, pkgDir = null } = {}) {
|
|
47
|
+
const gitStatus = runQuiet('git status --porcelain')
|
|
48
|
+
if (gitStatus) {
|
|
49
|
+
console.error('✗ Git 工作区不干净,请先提交或 stash')
|
|
50
|
+
console.error(gitStatus)
|
|
51
|
+
throw new Error('git-not-clean')
|
|
52
|
+
}
|
|
53
|
+
console.log(' ✓ Git 工作区干净')
|
|
54
|
+
const registry = pkgDir ? resolvePublishRegistry(pkgDir) : null
|
|
55
|
+
const whoamiCmd = registry ? `npm whoami --registry=${registry}` : 'npm whoami'
|
|
56
|
+
try {
|
|
57
|
+
const who = runQuiet(whoamiCmd)
|
|
58
|
+
console.log(` ✓ npm 已登录: ${who}${registry ? ` (${registry})` : ''}`)
|
|
59
|
+
} catch {
|
|
60
|
+
console.error('✗ 未登录 npm,请先执行 npm login')
|
|
61
|
+
if (registry) console.error(` (检查的 registry: ${registry})`)
|
|
62
|
+
throw new Error('npm-not-logged-in')
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── single-package release ───────────────────────────────────
|
|
67
|
+
|
|
68
|
+
// 单包发布: bump patch → publish → commit + scoped tag(不推送)。
|
|
69
|
+
// 成功返回 { name, oldVersion, version, tag };失败抛出(调用方可 rollbackVersion)。
|
|
70
|
+
export function releasePackage({ pkgDir, dryRun = false } = {}) {
|
|
71
|
+
const { pkgPath, name, version: oldVer } = readPackage(pkgDir)
|
|
72
|
+
const newVer = bumpPatch(oldVer)
|
|
73
|
+
if (!name || !oldVer) throw new Error(`无法从 ${pkgPath} 读取 name/version`)
|
|
74
|
+
|
|
75
|
+
console.log(`━━━ 发布 ${name}@${newVer}(原 ${oldVer})━━━`)
|
|
76
|
+
|
|
77
|
+
// 1. bump version (no git tag — scoped tag created manually below)
|
|
78
|
+
run('pnpm version patch --no-git-tag-version', { cwd: pkgDir, dryRun })
|
|
79
|
+
|
|
80
|
+
// 2. publish (prepublishOnly hook runs tests automatically)
|
|
81
|
+
run('pnpm publish --no-git-checks', { cwd: pkgDir, dryRun })
|
|
82
|
+
|
|
83
|
+
// 3. commit + scoped tag
|
|
84
|
+
if (!dryRun) {
|
|
85
|
+
execSync(`git add "${pkgPath}"`, { stdio: 'inherit' })
|
|
86
|
+
execSync(`git commit -m "release ${name}@${newVer}"`, { stdio: 'inherit' })
|
|
87
|
+
execSync(`git tag ${name}@${newVer}`, { stdio: 'inherit' })
|
|
88
|
+
} else {
|
|
89
|
+
console.log(` $ git add + commit "release ${name}@${newVer}" + tag ${name}@${newVer}`)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { name, oldVersion: oldVer, version: newVer, tag: `${name}@${newVer}` }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 回滚 package.json 版本号
|
|
96
|
+
export function rollbackVersion(pkgDir, { dryRun = false } = {}) {
|
|
97
|
+
if (dryRun) return
|
|
98
|
+
try {
|
|
99
|
+
execSync('git checkout -- package.json', { cwd: pkgDir, stdio: 'pipe' })
|
|
100
|
+
console.log(' 已回滚版本号')
|
|
101
|
+
} catch {
|
|
102
|
+
// version already committed? unlikely in this flow
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── push ─────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
// 推送到远端同名分支 + 指定 tag(s)。返回 true 成功,false 跳过(需手动推)。
|
|
109
|
+
export function pushToRemote(tags, { dryRun = false } = {}) {
|
|
110
|
+
console.log('\n━━━ 推送到远端 ━━━')
|
|
111
|
+
|
|
112
|
+
let remote
|
|
113
|
+
try {
|
|
114
|
+
const remotes = runQuiet('git remote').split('\n').filter(Boolean)
|
|
115
|
+
remote = remotes.includes('origin') ? 'origin' : remotes[0]
|
|
116
|
+
} catch {
|
|
117
|
+
remote = null
|
|
118
|
+
}
|
|
119
|
+
if (!remote) {
|
|
120
|
+
console.error(' ✗ 未配置 git remote,请手动: git push && git push --tags')
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let branch
|
|
125
|
+
try {
|
|
126
|
+
branch = runQuiet('git rev-parse --abbrev-ref HEAD')
|
|
127
|
+
} catch {
|
|
128
|
+
console.error(' ✗ 无法获取当前分支,请手动: git push && git push --tags')
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
run(`git push ${remote} ${branch}`, { dryRun })
|
|
133
|
+
if (tags.length > 0) {
|
|
134
|
+
run(`git push ${remote} ${tags.join(' ')}`, { dryRun })
|
|
135
|
+
}
|
|
136
|
+
if (!dryRun) console.log(' ✓ 推送完成')
|
|
137
|
+
return true
|
|
138
|
+
}
|