get-pnpm moves from pnpm/get.pnpm.io into pnpm/npm/get-pnpm, at 0.0.9 (get.pnpm.io be4e983, where its history stays). - The pnpm v12 npm package depends on it with `workspace:*`, so a pnpm release bundles get-pnpm's source as of that release. A wrapper fix no longer needs a get-pnpm release, a catalog bump, and a minimumReleaseAge exclusion first. bundle-node-gyp builds it before the deploy injects its build output. - It is released on its own: a `get-pnpm` product in bump.ts and create-release-pr.yml (off by default), `get-pnpm@<version>` tags, and build-get-pnpm/publish-get-pnpm jobs in release.yml. It is private in the workspace so the TypeScript release's recursive publish skips it; the packed manifest drops the flag. - Its runtime dependencies stay exact, through a `get-pnpm` named catalog. Its lint is the root ESLint config. Its TLS tests use the fixture the Rust network tests use. - The npm-wrapper CI job runs its tests on Node.js 24 and 22, beside the wrapper's. The forwarding proxy in its download tests forwards only to the servers the tests start. npm's registry signing keys were pinned in five places: v11, v12, get-pnpm, and get.pnpm.io's install.sh and install.ps1. Only v11's copy was refreshed, by the release PR, and the two repositories handled a withdrawn key in opposite ways. They now come from .config/npm-signing-keys.json: - .github/scripts/update-npm-signing-keys.mjs refreshes it from npm on every release PR, not only v11 ones. A key npm stops listing stays, marked withdrawn. - The meta-updater renders it into the v11, v12, and get-pnpm copies, so `pnpm lint:meta` fails on a copy edited by hand. pnpm v11 and v12 keep trusting withdrawn keys, so packages signed before a rotation verify. get-pnpm drops them, as get.pnpm.io's installers do. - get.pnpm.io's update script is to read this file.
144 lines
6.2 KiB
TypeScript
144 lines
6.2 KiB
TypeScript
import fs from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
|
|
import { afterEach, beforeEach, describe, expect, test } from '@jest/globals'
|
|
|
|
import { findRepoRoot, parkPublishedPrivateChangelogs, parseSelectedProducts, releaseFilterArgs } from '../src/bump.js'
|
|
|
|
describe('findRepoRoot', () => {
|
|
let dir: string
|
|
beforeEach(() => {
|
|
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bump-root-'))
|
|
})
|
|
afterEach(() => fs.rmSync(dir, { recursive: true, force: true }))
|
|
|
|
test('walks up to the directory containing .changeset', () => {
|
|
fs.mkdirSync(path.join(dir, '.changeset'))
|
|
const nested = path.join(dir, 'pnpm11', '__utils__', 'scripts', 'src')
|
|
fs.mkdirSync(nested, { recursive: true })
|
|
expect(findRepoRoot(nested)).toBe(dir)
|
|
})
|
|
|
|
test('throws when no .changeset directory exists above', () => {
|
|
expect(() => findRepoRoot(dir)).toThrow(/No \.changeset directory/)
|
|
})
|
|
})
|
|
|
|
describe('parseSelectedProducts', () => {
|
|
test('collects the products named by --release', () => {
|
|
expect(parseSelectedProducts(['--release', 'pnpm11', '--release', 'pnpr']))
|
|
.toEqual(new Set(['pnpm11', 'pnpr']))
|
|
})
|
|
|
|
test('is empty when no argument is passed (a bare `pnpm bump` releases everything)', () => {
|
|
expect(parseSelectedProducts([])).toEqual(new Set())
|
|
})
|
|
|
|
test('skips the leading `--` separator forwarded by `pnpm run bump -- …`', () => {
|
|
expect(parseSelectedProducts(['--', '--release', 'pnpm', '--release', 'pnpr']))
|
|
.toEqual(new Set(['pnpm', 'pnpr']))
|
|
expect(parseSelectedProducts(['--'])).toEqual(new Set())
|
|
})
|
|
|
|
test('rejects a `--` that is not in the leading position', () => {
|
|
expect(() => parseSelectedProducts(['--release', 'pnpm', '--'])).toThrow(/Unexpected bump argument/)
|
|
})
|
|
|
|
test('throws on an unknown product', () => {
|
|
expect(() => parseSelectedProducts(['--release', 'bogus'])).toThrow(/Unknown --release product/)
|
|
})
|
|
|
|
test('fails closed on a misspelled flag instead of releasing everything', () => {
|
|
expect(() => parseSelectedProducts(['--releases', 'pnpr'])).toThrow(/Unexpected bump argument/)
|
|
})
|
|
})
|
|
|
|
describe('releaseFilterArgs', () => {
|
|
test('releases everything (no filter) when nothing is selected', () => {
|
|
expect(releaseFilterArgs(new Set())).toEqual([])
|
|
})
|
|
|
|
test('releases everything (no filter) when every product is selected', () => {
|
|
expect(releaseFilterArgs(new Set(['pnpm11', 'pnpm', 'pnpr', 'get-pnpm']))).toEqual([])
|
|
})
|
|
|
|
test('excludes the unselected separate products when pnpm11 is selected', () => {
|
|
expect(releaseFilterArgs(new Set(['pnpm11'])))
|
|
.toEqual(['--filter=!pacquet', '--filter=!@pnpm/napi', '--filter=!@pnpm/pnpr', '--filter=!get-pnpm'])
|
|
expect(releaseFilterArgs(new Set(['pnpm11', 'pnpr', 'get-pnpm'])))
|
|
.toEqual(['--filter=!pacquet', '--filter=!@pnpm/napi'])
|
|
})
|
|
|
|
test('releases get-pnpm alone', () => {
|
|
expect(releaseFilterArgs(new Set(['get-pnpm']))).toEqual(['--filter=get-pnpm'])
|
|
})
|
|
|
|
test('includes only the selected separate products when pnpm11 is not selected', () => {
|
|
expect(releaseFilterArgs(new Set(['pnpm'])))
|
|
.toEqual(['--filter=pacquet', '--filter=@pnpm/napi'])
|
|
expect(releaseFilterArgs(new Set(['pnpm', 'pnpr'])))
|
|
.toEqual(['--filter=pacquet', '--filter=@pnpm/napi', '--filter=@pnpm/pnpr'])
|
|
expect(releaseFilterArgs(new Set(['pnpr'])))
|
|
.toEqual(['--filter=@pnpm/pnpr'])
|
|
})
|
|
})
|
|
|
|
describe('parkPublishedPrivateChangelogs', () => {
|
|
let dir: string
|
|
beforeEach(() => {
|
|
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bump-park-'))
|
|
})
|
|
afterEach(() => fs.rmSync(dir, { recursive: true, force: true }))
|
|
|
|
function writeProject (projectDir: string, manifest: { name: string, version: string }, changelog?: string): void {
|
|
fs.mkdirSync(path.join(dir, projectDir), { recursive: true })
|
|
fs.writeFileSync(path.join(dir, projectDir, 'package.json'), JSON.stringify(manifest))
|
|
if (changelog != null) {
|
|
fs.writeFileSync(path.join(dir, projectDir, 'CHANGELOG.md'), changelog)
|
|
}
|
|
}
|
|
|
|
const section = '## 12.8.0\n\n### Minor Changes\n\n- A change.\n\n### Patch Changes\n\n- A fix.\n'
|
|
|
|
test('moves the committed changelog to the parked section of the released version', () => {
|
|
writeProject('pnpm/npm/pnpm', { name: 'pacquet', version: '12.8.0' }, `# pacquet\n\n${section}`)
|
|
writeProject('pnpm/npm/napi', { name: '@pnpm/napi', version: '12.8.0' }, '# @pnpm/napi\n\n## 12.8.0\n')
|
|
|
|
parkPublishedPrivateChangelogs(dir, ['pnpm/npm/pnpm', 'pnpm/npm/napi'])
|
|
|
|
expect(fs.readFileSync(path.join(dir, '.changeset/changelogs/pacquet@12.8.0.md'), 'utf8')).toBe(section)
|
|
expect(fs.readFileSync(path.join(dir, '.changeset/changelogs/@pnpm!napi@12.8.0.md'), 'utf8')).toBe('## 12.8.0\n')
|
|
expect(fs.existsSync(path.join(dir, 'pnpm/npm/pnpm/CHANGELOG.md'))).toBe(false)
|
|
expect(fs.existsSync(path.join(dir, 'pnpm/npm/napi/CHANGELOG.md'))).toBe(false)
|
|
})
|
|
|
|
test('skips a project the release did not bump', () => {
|
|
writeProject('pnpr/npm/pnpr', { name: '@pnpm/pnpr', version: '0.1.0-alpha.14' })
|
|
|
|
parkPublishedPrivateChangelogs(dir, ['pnpr/npm/pnpr'])
|
|
|
|
expect(fs.existsSync(path.join(dir, '.changeset/changelogs'))).toBe(false)
|
|
})
|
|
|
|
test('refuses a changelog that holds more than the released section', () => {
|
|
writeProject('pnpm/npm/pnpm', { name: 'pacquet', version: '12.9.0' }, `# pacquet\n\n## 12.9.0\n\n- New.\n\n${section}`)
|
|
|
|
expect(() => parkPublishedPrivateChangelogs(dir, ['pnpm/npm/pnpm'])).toThrow(/exactly one section, for pacquet@12\.9\.0/)
|
|
expect(fs.existsSync(path.join(dir, 'pnpm/npm/pnpm/CHANGELOG.md'))).toBe(true)
|
|
})
|
|
|
|
test('refuses a changelog that holds an older section before the released one', () => {
|
|
writeProject('pnpm/npm/pnpm', { name: 'pacquet', version: '12.8.0' }, `# pacquet\n\n## 12.7.0\n\n- Old.\n\n${section}`)
|
|
|
|
expect(() => parkPublishedPrivateChangelogs(dir, ['pnpm/npm/pnpm'])).toThrow(/exactly one section, for pacquet@12\.8\.0/)
|
|
expect(fs.existsSync(path.join(dir, 'pnpm/npm/pnpm/CHANGELOG.md'))).toBe(true)
|
|
})
|
|
|
|
test('refuses a changelog without a section for the released version', () => {
|
|
writeProject('pnpm/npm/pnpm', { name: 'pacquet', version: '12.9.0' }, `# pacquet\n\n${section}`)
|
|
|
|
expect(() => parkPublishedPrivateChangelogs(dir, ['pnpm/npm/pnpm'])).toThrow(/exactly one section/)
|
|
})
|
|
})
|