@bushizhanghan/dsh-publish-gate 0.13.0 → 0.13.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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.1
4
+
5
+ - The tag check now asks `git ls-remote origin` instead of `git tag --points-at HEAD`. When the commit is pushed
6
+ through the GitHub API the local HEAD is not the remote one, so the local form reported `git-tag-missing` on
7
+ seven of my nine plugins whose tag existed all along. It now compares the tag against the shipped branch
8
+ head and stays silent when the remote cannot be reached — a check that cannot see the truth must not claim
9
+ a defect.
3
10
  ## 0.13.0
4
11
 
5
12
  - `market-fork-of-listed` / `market-fork-undisclosed` (warn): the repo is a GitHub fork whose parent is
package/README.md CHANGED
@@ -223,7 +223,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
223
223
  ## 开发
224
224
 
225
225
  ```sh
226
- node --test # 107 个引擎单测
226
+ node --test # 108 个引擎单测
227
227
  node test/integration.test.mjs # 27 条集成断言(9 个真实插件作 fixture)
228
228
  ```
229
229
 
package/lib/engine.js CHANGED
@@ -2303,11 +2303,43 @@ export async function checkTraceability(dir, opts = {}) {
2303
2303
  }
2304
2304
  }
2305
2305
 
2306
- // 2. No tag on HEAD: nothing points at these bytes afterwards.
2306
+ // 2. A tag on the bytes that actually shipped. When the commit was pushed
2307
+ // through the GitHub API the local HEAD is not the remote one, so a local
2308
+ // `git tag --points-at HEAD` reports a missing tag that exists — ask the
2309
+ // remote instead, and say nothing when it cannot be reached.
2310
+ const remote = opts.remote === undefined ? await readRemoteRefs(dir) : opts.remote
2307
2311
  const atHead = opts.atHead === undefined ? await gitOut(dir, ['tag', '--points-at', 'HEAD']) : opts.atHead
2308
- const tags = typeof atHead === 'string' ? atHead.split('\n').map((t) => t.trim()).filter(Boolean) : []
2312
+ const localTags = typeof atHead === 'string' ? atHead.split('\n').map((t) => t.trim()).filter(Boolean) : []
2309
2313
  const wanted = [`v${version}`, version]
2310
- if (typeof atHead === 'string' && !wanted.some((t) => tags.includes(t))) {
2314
+ const tagOnRemote = remote ? remote.tags[`v${version}`] || remote.tags[version] || null : null
2315
+ const tagIsOnShipped = Boolean(remote && tagOnRemote && tagOnRemote === remote.head)
2316
+ const localHasIt = wanted.some((t) => localTags.includes(t))
2317
+
2318
+ if (remote) {
2319
+ // The remote is the truth: the tag either points at the shipped commit or not.
2320
+ if (tagIsOnShipped) return out
2321
+ const exists = Boolean(tagOnRemote) || (opts.tagExists !== undefined ? Boolean(opts.tagExists) : Boolean(await gitOut(dir, ['rev-parse', '--verify', '--quiet', `refs/tags/v${version}`])))
2322
+ out.push(
2323
+ exists
2324
+ ? F(
2325
+ 'git-tag-elsewhere',
2326
+ 'warn',
2327
+ `tag v${version} does not point at the shipped commit`,
2328
+ `The tag is on ${String(tagOnRemote || 'another commit').slice(0, 12)} but the branch head is ${String(remote.head).slice(0, 12)}. A release built from that tag is not what you just published.`,
2329
+ `Move it: git tag -f v${version} <shipped-sha> && git push -f origin v${version}`,
2330
+ )
2331
+ : F(
2332
+ 'git-tag-missing',
2333
+ 'warn',
2334
+ `no git tag v${version} on the shipped commit`,
2335
+ 'After publishing, the only record of these bytes is the registry. A GitHub release, a changelog entry or a market listing cannot point at a commit that was never tagged.',
2336
+ `git tag v${version} && git push origin v${version}`,
2337
+ ),
2338
+ )
2339
+ return out
2340
+ }
2341
+
2342
+ if (typeof atHead === 'string' && !localHasIt) {
2311
2343
  const exists = opts.tagExists === undefined ? await gitOut(dir, ['rev-parse', '--verify', '--quiet', `refs/tags/v${version}`]) : opts.tagExists ? 'sha' : null
2312
2344
  out.push(
2313
2345
  exists
@@ -2330,6 +2362,22 @@ export async function checkTraceability(dir, opts = {}) {
2330
2362
  return out
2331
2363
  }
2332
2364
 
2365
+ /** `{ head, tags }` from one `git ls-remote`, or null when it cannot be read. */
2366
+ async function readRemoteRefs(dir) {
2367
+ const raw = await gitOut(dir, ['ls-remote', '--heads', '--tags', 'origin'])
2368
+ if (typeof raw !== 'string' || raw.length === 0) return null
2369
+ const tags = {}
2370
+ let head = null
2371
+ for (const line of raw.split('\n')) {
2372
+ const [sha, ref] = line.split('\t')
2373
+ if (!sha || !ref) continue
2374
+ if (ref === 'refs/heads/main' || ref === 'refs/heads/master') head = sha
2375
+ const m = /^refs\/tags\/(.+?)(?:\^\{\})?$/.exec(ref)
2376
+ if (m) tags[m[1]] = sha // a dereferenced annotated tag overwrites the tag object, which is what we want
2377
+ }
2378
+ return head ? { head, tags } : null
2379
+ }
2380
+
2333
2381
  // =====================================================================
2334
2382
  // Orchestration
2335
2383
  // =====================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bushizhanghan/dsh-publish-gate",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "Pre-publish gate for DSH plugins: manifest, dependencies, artifact, runtime, browser half, traceability and market readiness — plus an input fuzzer that drives every declared parameter with the wrong type. 发布前的七层门禁 + 入参模糊测试。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",