mirror of
https://github.com/zhu1090093659/dsh-web.git
synced 2026-09-28 14:24:03 +08:00
The skin, pet and community packages left this repository, so packages/skins/ never exists in a checkout of it. Every scanner that still walked it as a second root did so defensively — family-packages.mjs and coverage-gate.discoverPackages listed it first and guarded the walk with existsSync, aggregate.mjs looked for a second aggregate manifest and a second package index under it, e2e-mount-rewrite searched it for workspace packages, and seven test files built fixtures under it. The walkers now name packages/ as the single root and the fixtures and comments follow; modules that only carried the root in a doc comment are reworded. scripts/coverage-baseline.json loses the three packages no longer measured here; its sixteen keys now name exactly the sixteen package directories on disk. The in-repo fallback paths in scripts/market-build stay untouched: they are the documented route for building from a checkout that still carries the packages, and the fixture-based market-build tests populate them deliberately.
128 lines
5.1 KiB
JavaScript
128 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Download the published npm tarballs for a release tag so the pipeline can
|
|
* attach them to the GitHub Release as real installable artifacts (a
|
|
* bare `gh release create` leaves only GitHub's auto-generated source
|
|
* archives).
|
|
*
|
|
* Walks the same package set as verify-version.mjs (packages/*,
|
|
* non-recursive), reads each package.json name + version,
|
|
* skips private packages (pnpm -r publish never pushes them, so they would
|
|
* 404 forever — v0.2.4: dsh-chat-recovery), waits for every publishable
|
|
* version to become readable on the npm registry (fresh publishes are
|
|
* eventually consistent and can 404 briefly), and runs
|
|
* `npm pack <name>@<version>` against the registry. Packing from the
|
|
* registry (not the working tree) makes every uploaded tarball
|
|
* byte-identical to what `pnpm -r publish` pushed moments earlier.
|
|
*
|
|
* Prints one tarball path per line plus a summary. Fails (exit 1) when a
|
|
* package version does not match the tag or npm pack fails.
|
|
*
|
|
* Usage: node scripts/release-assets.mjs <vX.Y.Z> <outDir>
|
|
*/
|
|
import { execFileSync } from 'node:child_process'
|
|
import { mkdirSync, readFileSync } from 'node:fs'
|
|
import { dirname, join, resolve } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { walkFamilyPackages } from './lib/family-packages.mjs'
|
|
|
|
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url))
|
|
const REPO_ROOT = resolve(SCRIPT_DIR, '..')
|
|
|
|
/** Every package.json under packages/ (non-recursive, both roots). */
|
|
export function packageFiles(cwd) {
|
|
return walkFamilyPackages(cwd).map(({ pkgPath }) => pkgPath)
|
|
}
|
|
|
|
/**
|
|
* Pack one published package from the registry into outDir and return the
|
|
* resulting tarball path. `run` mirrors execFileSync and is injectable for
|
|
* tests.
|
|
*/
|
|
export function packOne(name, version, outDir, run = (file, args, options) => execFileSync(file, args, options)) {
|
|
const stdout = run('npm', ['pack', name + '@' + version, '--pack-destination', outDir, '--json'], { encoding: 'utf8' })
|
|
const filename = JSON.parse(stdout)[0]?.filename
|
|
if (typeof filename !== 'string' || filename === '') {
|
|
throw new Error('npm pack returned no filename for ' + name + '@' + version)
|
|
}
|
|
return join(outDir, filename)
|
|
}
|
|
|
|
function defaultView(name, version) {
|
|
execFileSync('npm', ['view', name + '@' + version, 'version'], { encoding: 'utf8' })
|
|
}
|
|
|
|
/**
|
|
* Family packages that actually reach the registry: private packages are
|
|
* bundled into a carrier (or referenced as a workspace dep) and `pnpm -r
|
|
* publish` skips them, so packing them from the registry can never succeed.
|
|
*/
|
|
export function publishablePackages(packages) {
|
|
return packages.filter(pkg => pkg.private !== true)
|
|
}
|
|
|
|
/**
|
|
* The npm registry is eventually consistent for fresh publishes: a version
|
|
* `pnpm publish` reported as pushed can still 404 for a short window
|
|
* (v0.1.17 and v0.1.18 both failed here). Wait until every release package
|
|
* version is readable via `npm view` (bounded), so the pack loop never hits
|
|
* a transient 404. `view` is injectable for tests.
|
|
*/
|
|
export function waitForPublished(packages, version, view = defaultView, { attempts = 30, delayMs = 10000 } = {}) {
|
|
const missing = new Set(packages.map(pkg => pkg.name))
|
|
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
for (const name of [...missing]) {
|
|
try {
|
|
view(name, version)
|
|
missing.delete(name)
|
|
} catch {
|
|
// Not propagated yet; retried in the next attempt.
|
|
}
|
|
}
|
|
if (missing.size === 0) return
|
|
if (attempt < attempts) {
|
|
console.warn('[release-assets] waiting for npm propagation: ' + [...missing].join(', ') + ' (attempt ' + attempt + '/' + attempts + ')')
|
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMs)
|
|
}
|
|
}
|
|
throw new Error('[release-assets] timed out waiting for npm propagation: ' + [...missing].join(', '))
|
|
}
|
|
|
|
function main() {
|
|
const tag = process.argv[2] ?? ''
|
|
const outDir = process.argv[3] ?? ''
|
|
if (!/^v?\d+\.\d+\.\d+$/.test(tag) || outDir === '') {
|
|
console.error('usage: node scripts/release-assets.mjs <vX.Y.Z> <outDir>')
|
|
process.exit(2)
|
|
}
|
|
const version = tag.replace(/^v/, '')
|
|
mkdirSync(outDir, { recursive: true })
|
|
const files = packageFiles(REPO_ROOT)
|
|
if (files.length === 0) {
|
|
console.error('no package.json found under packages/')
|
|
process.exit(1)
|
|
}
|
|
const packages = []
|
|
for (const file of files) {
|
|
const pkg = JSON.parse(readFileSync(file, 'utf8'))
|
|
if (pkg.version !== version) {
|
|
console.error('::error file=' + file + '::version ' + pkg.version + ' does not match tag ' + tag)
|
|
process.exit(1)
|
|
}
|
|
packages.push(pkg)
|
|
}
|
|
// Version parity above covers every family package; only publishable
|
|
// packages can ever be packed back from the registry.
|
|
const publishable = publishablePackages(packages)
|
|
waitForPublished(publishable, version)
|
|
const packed = []
|
|
for (const pkg of publishable) {
|
|
const tgz = packOne(pkg.name, version, outDir)
|
|
packed.push(tgz)
|
|
console.log(tgz)
|
|
}
|
|
console.log('[release-assets] packed ' + packed.length + ' tarballs into ' + outDir)
|
|
}
|
|
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) main()
|