Files
pnpm/.github/scripts/prepare-docs-sync.test.mjs
T
Zoltan KochanandClaude Opus 5.5 5567c623d5 ci(docs): sync documentation when a GitHub release is published (#16530)
Sync documentation ran on the completion of the Release workflow. The
release only stages pnpm on npm, so the publication check always failed
and every sync had to be dispatched by hand after approval.

The maintainer publishes the draft GitHub release after approving the
staged packages, so that event now starts the sync. The tag commit must
match the commit the release event names, and the npm publication check
stays.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:54:52 +02:00

121 lines
5.8 KiB
JavaScript

import assert from 'node:assert/strict'
import { execFileSync } from 'node:child_process'
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { test } from 'node:test'
import { prepareDocsSync } from './prepare-docs-sync.mjs'
function fixture (t, tag = 'v12.8.2') {
const repo = mkdtempSync(path.join(os.tmpdir(), 'pnpm-docs-release-'))
t.after(() => rmSync(repo, { recursive: true, force: true }))
const git = (...args) => execFileSync('git', args, { cwd: repo, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim()
const version = tag.startsWith('pnpr@') ? tag.slice(5) : tag.slice(1)
for (const [product, current] of [['pnpm/npm/pnpm', '12.8.2'], ['pnpm11/pnpm', '11.28.2'], ['pnpr/npm/pnpr', '0.1.0-alpha.14']]) {
mkdirSync(path.join(repo, product), { recursive: true })
const productVersion = version.split('.')[0] === current.split('.')[0] ? version : current
writeFileSync(path.join(repo, product, 'package.json'), JSON.stringify({ version: productVersion }))
}
git('init', '--quiet')
git('config', 'commit.gpgsign', 'false')
git('config', 'user.name', 'Test')
git('config', 'user.email', 'test@example.com')
git('add', '.')
git('commit', '--quiet', '-m', 'chore: release')
git('update-ref', `refs/tags/${tag}`, 'HEAD')
const sha = git('rev-parse', 'HEAD')
let verified = false
const options = {
eventName: 'release',
releaseTag: tag,
githubSha: sha,
git: (...args) => {
if (args[0] === 'verify-tag') { verified = true; return '' }
return git(...args)
},
publicationState: spec => {
assert.equal(verified, true)
assert.equal(spec, `${tag.startsWith('pnpr@') ? '@pnpm/pnpr' : 'pnpm'}@${version}`)
return 'published'
},
}
return { repo, git, sha, options }
}
test('selects only the signed and published release snapshot', t => {
const f = fixture(t)
assert.deepEqual(prepareDocsSync(f.options), {
publish: true, line: '12.x', version: '12.8.2', release_commit: f.sha, docs_commit: f.sha,
})
assert.throws(() => prepareDocsSync({ ...f.options, publicationState: () => 'missing' }), /not been published/)
assert.throws(() => prepareDocsSync({ ...f.options, git: (...args) => args[0] === 'verify-tag' ? f.git(...args) : f.options.git(...args) }))
assert.throws(() => prepareDocsSync({ ...f.options, githubSha: 'a'.repeat(40) }), /does not match/)
})
test('ignores verification runs and releases outside stable v11 and v12', () => {
assert.equal(prepareDocsSync({ eventName: 'release', releaseTag: 'pnpm-wasm-v1.0.0' }), undefined)
assert.equal(prepareDocsSync({ eventName: 'workflow_dispatch', releaseTag: 'v12.9.0-beta.1' }), undefined)
for (const tag of ['v10.30.0', 'v13.0.0']) {
assert.equal(prepareDocsSync({ eventName: 'release', releaseTag: tag }), undefined)
assert.throws(() => prepareDocsSync({ eventName: 'workflow_dispatch', releaseTag: tag }), /v11 and v12/)
}
})
test('manual corrections must contain only the selected version docs', t => {
const f = fixture(t)
mkdirSync(path.join(f.repo, 'pnpm/docs'), { recursive: true })
writeFileSync(path.join(f.repo, 'pnpm/docs/install.md'), 'correction')
f.git('add', '.')
f.git('commit', '--quiet', '-m', 'docs: correction')
const options = { ...f.options, eventName: 'workflow_dispatch', releaseTag: 'v12.8.2', docsRef: f.git('rev-parse', 'HEAD') }
assert.equal(prepareDocsSync(options).docs_commit, options.docsRef)
assert.throws(() => prepareDocsSync({ ...options, docsRef: 'main' }), /full commit SHA/)
writeFileSync(path.join(f.repo, 'unreleased.js'), 'new behavior')
f.git('add', '.')
f.git('commit', '--quiet', '-m', 'feat: unreleased behavior')
assert.throws(() => prepareDocsSync({ ...options, docsRef: f.git('rev-parse', 'HEAD') }), /change only/)
})
test('v11 corrections use the TypeScript product documentation', t => {
const f = fixture(t, 'v11.28.3')
mkdirSync(path.join(f.repo, 'pnpm11/docs'), { recursive: true })
writeFileSync(path.join(f.repo, 'pnpm11/docs/install.md'), 'correction')
f.git('add', '.')
f.git('commit', '--quiet', '-m', 'docs: v11 correction')
const result = prepareDocsSync({ ...f.options, eventName: 'workflow_dispatch', releaseTag: 'v11.28.3', docsRef: f.git('rev-parse', 'HEAD') })
assert.equal(result.line, '11.x')
})
test('pnpr alpha releases and corrections select only the registry documentation', t => {
const f = fixture(t, 'pnpr@0.1.0-alpha.15')
assert.equal(prepareDocsSync(f.options).line, 'pnpr')
mkdirSync(path.join(f.repo, 'pnpr/docs'), { recursive: true })
writeFileSync(path.join(f.repo, 'pnpr/docs/installation.md'), 'correction')
f.git('add', '.')
f.git('commit', '--quiet', '-m', 'docs: registry correction')
const result = prepareDocsSync({ ...f.options, eventName: 'workflow_dispatch', releaseTag: 'pnpr@0.1.0-alpha.15', docsRef: f.git('rev-parse', 'HEAD') })
assert.equal(result.line, 'pnpr')
assert.equal(result.docs_commit, f.git('rev-parse', 'HEAD'))
})
test('manual publication from main needs no release or npm publication', t => {
const f = fixture(t)
assert.deepEqual(prepareDocsSync({
eventName: 'workflow_dispatch',
githubRef: 'refs/heads/main',
git: f.git,
publicationState: () => assert.fail('main publication must not query npm'),
}), { publish: true, main_sync: true, docs_commit: f.sha })
})
test('manual publication without a release is restricted to main and its checked-out snapshot', () => {
for (const githubRef of [undefined, 'refs/heads/feature', 'refs/tags/v12.8.2']) {
assert.throws(() => prepareDocsSync({ eventName: 'workflow_dispatch', githubRef }), /must run from main/)
}
assert.throws(() => prepareDocsSync({
eventName: 'workflow_dispatch', githubRef: 'refs/heads/main', docsRef: 'a'.repeat(40),
}), /docs_commit requires a release_tag/)
})