@youngwat/upload-file 1.0.0 → 1.0.3
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 +21 -1
- package/package.json +5 -4
- package/release/release.js +144 -16
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
## 环境要求
|
|
15
15
|
|
|
16
|
-
- Node.js >=
|
|
16
|
+
- Node.js >= 20
|
|
17
17
|
|
|
18
18
|
## 安装
|
|
19
19
|
|
|
@@ -41,6 +41,8 @@ upload-file
|
|
|
41
41
|
upload-file -m full
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
> 命令同时注册了 `upload-file` 与 `uploadFile` 两个名字,敲哪个都一样。
|
|
45
|
+
|
|
44
46
|
## 配置文件
|
|
45
47
|
|
|
46
48
|
### ftp.config.json —— 上传范围
|
|
@@ -224,6 +226,24 @@ npm run release -- --set-token --token npm_xxxxxxxx
|
|
|
224
226
|
> 想继续用 granular token,就重新生成一个并勾选 **Bypass 2FA**,Permissions 给 `Read and write`。
|
|
225
227
|
> 另外 `npm publish --dry-run`(脚本的 `--dry-run`)**不会**校验发布权限,试跑通过不代表能发,403 只有在真正 publish 时才暴露。
|
|
226
228
|
|
|
229
|
+
### 版本号规则(脚本会强制校验)
|
|
230
|
+
|
|
231
|
+
发布脚本在打包前会校验版本号,以下情况会被直接拦下,避免发出无效包:
|
|
232
|
+
|
|
233
|
+
| 输入 | 结果 |
|
|
234
|
+
| --- | --- |
|
|
235
|
+
| `1.0.0`、`0.1.2`、`1.0.0-alpha`、`1.2.3-beta.1` | ✅ 接受,原样写入 |
|
|
236
|
+
| `v1.0.0` | ✅ 接受,自动剥掉 `v` 前缀 |
|
|
237
|
+
| `0.0.0`、`0.0.0-stage` | ❌ 拒绝(占位版本,只会发成空壳包) |
|
|
238
|
+
| `1.0`、`01.0.0`、`1.0.0.0`、`abc` | ❌ 拒绝(不是合法 semver) |
|
|
239
|
+
|
|
240
|
+
脚本**不会**静默改写你输入的版本号语义。
|
|
241
|
+
|
|
242
|
+
另外两点提醒:
|
|
243
|
+
|
|
244
|
+
- **预发布标签**:`1.0.0-beta.1` 这类版本默认会占用 `latest`,想避开就手动 `npm publish --tag next`。
|
|
245
|
+
- **build metadata**:`1.2.3-rc.2+build.5` 里的 `+build.5` 会被 `npm version` 丢弃(npm 自身行为),最终版本号是 `1.2.3-rc.2`。
|
|
246
|
+
|
|
227
247
|
## License
|
|
228
248
|
|
|
229
249
|
[MIT](./LICENSE)
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youngwat/upload-file",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "基于 MD5 缓存的 FTP / SFTP 增量部署 CLI,支持全量/增量与私钥认证",
|
|
5
5
|
"bin": {
|
|
6
|
-
"upload-file": "src/uploadFile.js"
|
|
6
|
+
"upload-file": "src/uploadFile.js",
|
|
7
|
+
"uploadFile": "src/uploadFile.js"
|
|
7
8
|
},
|
|
8
9
|
"files": [
|
|
9
10
|
"src/uploadFile.js",
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
"pack:check": "npm pack --dry-run"
|
|
21
22
|
},
|
|
22
23
|
"engines": {
|
|
23
|
-
"node": ">=
|
|
24
|
+
"node": ">=20"
|
|
24
25
|
},
|
|
25
26
|
"publishConfig": {
|
|
26
27
|
"access": "public",
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"license": "MIT",
|
|
40
41
|
"dependencies": {
|
|
41
42
|
"ftp-deploy": "^2.4.7",
|
|
42
|
-
"glob": "^
|
|
43
|
+
"glob": "^11.1.0",
|
|
43
44
|
"ssh2-sftp-client": "^12.1.1"
|
|
44
45
|
},
|
|
45
46
|
"overrides": {
|
package/release/release.js
CHANGED
|
@@ -213,10 +213,82 @@ function applyNewToken(token) {
|
|
|
213
213
|
* ───────────────────────────────────────────── */
|
|
214
214
|
|
|
215
215
|
const BUMPS = ["patch", "minor", "major"];
|
|
216
|
-
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 严格 semver 校验(对齐 node-semver 语法)。
|
|
219
|
+
* 上一版正则写错了,把 1.2.3-beta.1 这种合法预发布误判为非法,
|
|
220
|
+
* 同时放过了 0.0.0-stage 这种「占位版本」(曾导致发空壳包出去)。
|
|
221
|
+
*/
|
|
222
|
+
const NUM = "(?:0|[1-9]\\d*)";
|
|
223
|
+
const PRE = "(?:\\d*[A-Za-z-][0-9A-Za-z-]*|0|[1-9]\\d*)";
|
|
224
|
+
const BUILD = "[0-9A-Za-z-]+";
|
|
225
|
+
const SEMVER_RE = new RegExp(
|
|
226
|
+
`^(${NUM})\\.(${NUM})\\.(${NUM})(?:-(${PRE}(?:\\.${PRE})*))?(?:\\+(${BUILD}(?:\\.${BUILD})*))?$`
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
function isStrictSemver(v) {
|
|
230
|
+
return SEMVER_RE.test(v);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 版本号诊断:区分「合法」「占位版本」「语法错误」。
|
|
235
|
+
* 0.0.0 与 0.0.0-* 语法合法但几乎肯定是占位包,单独拦一道。
|
|
236
|
+
*/
|
|
237
|
+
function classifyVersion(v) {
|
|
238
|
+
const m = SEMVER_RE.exec(v);
|
|
239
|
+
if (!m) return { ok: false, reason: "syntax" };
|
|
240
|
+
const [major, minor, patch] = [m[1], m[2], m[3]];
|
|
241
|
+
if (major === "0" && minor === "0" && patch === "0") {
|
|
242
|
+
return { ok: false, reason: "placeholder" };
|
|
243
|
+
}
|
|
244
|
+
return { ok: true, version: v };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* 把用户输入校验成合法 semver。
|
|
249
|
+
* 刻意**不做静默改写**:曾考虑把 1.2.3-beta 自动补成 1.2.3-beta.1,
|
|
250
|
+
* 但那会把用户明确输入的 1.0.0-alpha 也改成 1.0.0-alpha.1,
|
|
251
|
+
* 属于未经同意篡改版本语义。这里只接受或拒绝,并在有风险时给出提示。
|
|
252
|
+
*/
|
|
253
|
+
function normalizeVersionInput(input) {
|
|
254
|
+
if (input === undefined || input === null || String(input).trim() === "") return null;
|
|
255
|
+
const raw = String(input).trim().replace(/^v/i, "");
|
|
256
|
+
const result = classifyVersion(raw);
|
|
257
|
+
|
|
258
|
+
if (!result.ok) {
|
|
259
|
+
if (result.reason === "placeholder") {
|
|
260
|
+
fail(
|
|
261
|
+
`版本号 ${raw} 是占位版本,不能用于正式发布`,
|
|
262
|
+
"0.0.0 及 0.0.0-* 只会把包发成空壳/占位,请用 1.0.0 起步,例如: npm run release 1.0.0"
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
fail(
|
|
266
|
+
`"${input}" 不是合法的 semver 版本号`,
|
|
267
|
+
"正确写法是 主.次.修订,例如 1.0.0;预发布写成 1.2.3-beta.1(版本段不能省,如 1.0 应写 1.0.0)"
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 预发布版本默认会被打成 latest 标签,容易让用户装上未稳定版本,提前提醒
|
|
272
|
+
const pre = /-([0-9A-Za-z.-]+)$/.exec(result.version);
|
|
273
|
+
if (pre) {
|
|
274
|
+
warn(`这是预发布版本(-${pre[1]}),默认会占用 latest 标签`);
|
|
275
|
+
console.log(C.dim(" 要避免,改成: npm publish --tag next(或先发正式版)"));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return result.version;
|
|
279
|
+
}
|
|
217
280
|
|
|
218
281
|
function parseArgs(argv) {
|
|
219
|
-
const out = {
|
|
282
|
+
const out = {
|
|
283
|
+
bump: null,
|
|
284
|
+
versionInput: null,
|
|
285
|
+
yes: false,
|
|
286
|
+
dryRun: false,
|
|
287
|
+
login: false,
|
|
288
|
+
help: false,
|
|
289
|
+
token: null,
|
|
290
|
+
setToken: false,
|
|
291
|
+
};
|
|
220
292
|
for (let i = 0; i < argv.length; i++) {
|
|
221
293
|
const arg = argv[i];
|
|
222
294
|
if (arg === "--token") {
|
|
@@ -225,13 +297,24 @@ function parseArgs(argv) {
|
|
|
225
297
|
out.token = value;
|
|
226
298
|
out.setToken = true;
|
|
227
299
|
i++;
|
|
228
|
-
} else if (BUMPS.includes(arg)
|
|
229
|
-
|
|
230
|
-
else if (arg === "
|
|
231
|
-
|
|
232
|
-
else if (arg === "--
|
|
233
|
-
|
|
234
|
-
else
|
|
300
|
+
} else if (BUMPS.includes(arg)) {
|
|
301
|
+
out.bump = arg;
|
|
302
|
+
} else if (arg === "-y" || arg === "--yes") {
|
|
303
|
+
out.yes = true;
|
|
304
|
+
} else if (arg === "--dry-run") {
|
|
305
|
+
out.dryRun = true;
|
|
306
|
+
} else if (arg === "--login") {
|
|
307
|
+
out.login = true;
|
|
308
|
+
} else if (arg === "--set-token") {
|
|
309
|
+
out.setToken = true;
|
|
310
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
311
|
+
out.help = true;
|
|
312
|
+
} else if (arg.startsWith("-")) {
|
|
313
|
+
fail(`未知参数 ${arg}`, "用 --help 查看用法");
|
|
314
|
+
} else {
|
|
315
|
+
// 任意非选项参数都当版本号处理,后面严格校验,绝不静默忽略
|
|
316
|
+
out.versionInput = arg;
|
|
317
|
+
}
|
|
235
318
|
}
|
|
236
319
|
return out;
|
|
237
320
|
}
|
|
@@ -244,7 +327,7 @@ const HELP = `
|
|
|
244
327
|
|
|
245
328
|
版本(可选):
|
|
246
329
|
patch | minor | major 按语义化版本自增
|
|
247
|
-
1.2.3
|
|
330
|
+
1.2.3 直接指定版本号(预发布写作 1.2.3-beta.1)
|
|
248
331
|
|
|
249
332
|
选项:
|
|
250
333
|
-y, --yes 跳过二次确认
|
|
@@ -304,6 +387,38 @@ function assertPackageName(pkg) {
|
|
|
304
387
|
fail("package.json 缺少 files 白名单", "没有白名单会把本地配置、测试产物一起发出去");
|
|
305
388
|
}
|
|
306
389
|
ok(`files 白名单 ${pkg.files.length} 项`);
|
|
390
|
+
|
|
391
|
+
// 白名单里的路径必须真实存在,否则会被静默忽略(曾出现过占位包只发 2 个文件的情况)
|
|
392
|
+
const missingFiles = pkg.files.filter((f) => !fs.existsSync(path.join(ROOT, f)));
|
|
393
|
+
if (missingFiles.length > 0) {
|
|
394
|
+
fail(`files 白名单里的路径不存在:\n ${missingFiles.join("\n ")}`);
|
|
395
|
+
}
|
|
396
|
+
ok("白名单路径全部存在");
|
|
397
|
+
|
|
398
|
+
// bin 入口必须存在且带 shebang,否则用户装完敲命令就是 MODULE_NOT_FOUND
|
|
399
|
+
if (!pkg.bin || Object.keys(pkg.bin).length === 0) {
|
|
400
|
+
fail("package.json 缺少 bin 字段", "CLI 包没有 bin 就不会注册命令");
|
|
401
|
+
}
|
|
402
|
+
for (const [cmd, rel] of Object.entries(pkg.bin)) {
|
|
403
|
+
const abs = path.join(ROOT, rel);
|
|
404
|
+
if (!fs.existsSync(abs)) fail(`bin "${cmd}" 指向的文件不存在: ${rel}`);
|
|
405
|
+
const head = fs.readFileSync(abs, "utf8").split("\n")[0];
|
|
406
|
+
if (!head.startsWith("#!")) warn(`bin "${cmd}" 的文件缺少 shebang(Unix 下可能无法直接执行)`);
|
|
407
|
+
}
|
|
408
|
+
ok(`bin 入口正常: ${Object.entries(pkg.bin).map(([c, f]) => `${c} → ${f}`).join(", ")}`);
|
|
409
|
+
|
|
410
|
+
const verCheck = classifyVersion(pkg.version);
|
|
411
|
+
if (!verCheck.ok) {
|
|
412
|
+
fail(
|
|
413
|
+
verCheck.reason === "placeholder"
|
|
414
|
+
? `当前版本号 ${pkg.version} 是占位版本,不能发布`
|
|
415
|
+
: `版本号 ${pkg.version} 不是合法 semver`,
|
|
416
|
+
verCheck.reason === "placeholder"
|
|
417
|
+
? "先跑: npm run release 1.0.0"
|
|
418
|
+
: "例如 1.0.0 或 1.0.0-beta.1"
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
ok(`版本号 ${pkg.version} 合法`);
|
|
307
422
|
}
|
|
308
423
|
|
|
309
424
|
function assertGitClean() {
|
|
@@ -359,13 +474,26 @@ function assertLoggedIn(autoLogin, dryRun) {
|
|
|
359
474
|
);
|
|
360
475
|
}
|
|
361
476
|
|
|
362
|
-
function bumpVersion(
|
|
363
|
-
if (!bump) return;
|
|
364
|
-
step(
|
|
477
|
+
function bumpVersion(opts) {
|
|
478
|
+
if (!opts.bump && !opts.versionInput) return;
|
|
479
|
+
step("调整版本号");
|
|
365
480
|
const before = readPkg().version;
|
|
366
|
-
const
|
|
367
|
-
if (
|
|
481
|
+
const args = ["version", "--no-git-tag-version"];
|
|
482
|
+
if (opts.versionInput) {
|
|
483
|
+
const safe = normalizeVersionInput(opts.versionInput);
|
|
484
|
+
if (safe !== opts.versionInput.trim().replace(/^v/, "")) {
|
|
485
|
+
warn(`版本号已归一化为合法 semver: ${opts.versionInput} → ${safe}`);
|
|
486
|
+
}
|
|
487
|
+
args.push(safe);
|
|
488
|
+
} else {
|
|
489
|
+
args.push(opts.bump);
|
|
490
|
+
}
|
|
491
|
+
const r = runCapture("npm", args);
|
|
492
|
+
if (r.status !== 0) fail(`npm version 失败: ${r.stderr || r.stdout}`);
|
|
368
493
|
const after = readPkg().version;
|
|
494
|
+
if (!isStrictSemver(after)) {
|
|
495
|
+
fail(`package.json 里的版本号 ${after} 不是合法 semver`, "手动改回 1.0.0 之类的合法值再试");
|
|
496
|
+
}
|
|
369
497
|
ok(`版本号 ${before} → ${after}`);
|
|
370
498
|
}
|
|
371
499
|
|
|
@@ -489,7 +617,7 @@ async function main() {
|
|
|
489
617
|
}
|
|
490
618
|
|
|
491
619
|
assertPackageName(readPkg());
|
|
492
|
-
bumpVersion(opts
|
|
620
|
+
bumpVersion(opts);
|
|
493
621
|
const pkg = readPkg();
|
|
494
622
|
|
|
495
623
|
assertGitClean();
|