Skip to content

Commit

Permalink
feat: use upstream release-please (#334)
Browse files Browse the repository at this point in the history
This also adds 100% test coverage to `release-please` and
`release-manager` using `nock`.

Fixes #177
lukekarrys authored Nov 28, 2023
1 parent c892260 commit 2daff23
Showing 40 changed files with 1,916 additions and 654 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -123,3 +123,23 @@ trivial to swap out this content directory for a different one as it is only
referenced in a single place in `lib/config.js`. However, it's not currently
possible to change this value at runtime, but that might become possible in
future versions of this package.

### Testing

The files `test/release/release-manager.js` and `test/release/release-please.js`
use recorded `nock` fixtures to generate snapshots. To update these fixtures run:

```sh
GITHUB_TOKEN=<YOUR_PAT> npm run test:record --- test/release/release-{please,manager}.js
```

If you only need to update fixtures for one, it's best to only run that single
test file.

#### `test/release/release-please.js`

This test file uses the repo `npm/npm-cli-release-please` to record its
fixtures. It expects `https://github.com/npm/npm-cli-release-please` to be
checked out in a sibling directory to this repo. It also needs the current
branch set to `template-oss-mock-testing-branch-do-not-delete` before the tests
are run.
2 changes: 0 additions & 2 deletions bin/release-manager.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env node

const { join } = require('path')
const core = require('@actions/core')
const { parseArgs } = require('util')
const ReleaseManager = require('../lib/release/release-manager')
@@ -10,7 +9,6 @@ ReleaseManager.run({
token: process.env.GITHUB_TOKEN,
repo: process.env.GITHUB_REPOSITORY,
cwd: process.cwd(),
pkg: require(join(process.cwd(), 'package.json')),
...parseArgs({
options: {
pr: { type: 'string' },
16 changes: 13 additions & 3 deletions lib/content/release-please-config-json.hbs
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
{
"separate-pull-requests": {{{ del }}},
"plugins": {{#if isMonoPublic }}["node-workspace"]{{ else }}{{{ del }}}{{/if}},
"exclude-packages-from-root": true,
{{#if isMonoPublic }}
"plugins": [
"node-workspace",
"node-workspace-format"
],
{{/if}}
"exclude-packages-from-root": {{{ del }}},
"group-pull-request-title-pattern": "chore: release ${version}",
"pull-request-title-pattern": "chore: release${component} ${version}",
"changelog-sections": {{{ json changelogTypes }}},
"prerelease-type": "pre",
"packages": {
"{{ pkgPath }}": {
{{#if isRoot}}"package-name": ""{{/if}}
{{#if isRoot}}
"package-name": ""{{#if workspacePaths}},
"exclude-paths": {{{ json workspacePaths }}}
{{/if}}
{{/if}}
}
}
}
6 changes: 3 additions & 3 deletions lib/release/changelog.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { link, code, specRe, list, dateFmt, makeGitHubUrl } = require('./util')
const { link, code, wrapSpecs, list, formatDate, makeGitHubUrl } = require('./util')

class Changelog {
static BREAKING = 'breaking'
@@ -11,7 +11,7 @@ class Changelog {
}

constructor ({ version, url, sections }) {
this.#title = `## ${url ? link(version, url) : version} (${dateFmt()})`
this.#title = `## ${url ? link(version, url) : version} (${formatDate()})`
for (const section of sections) {
this.#types.add(section.type)
this.#titles[section.type] = section.section
@@ -136,7 +136,7 @@ class ChangelogNotes {

// The title of the commit, with the optional scope as a prefix
const scope = commit.scope && `${commit.scope}:`
const subject = commit.bareMessage.replace(specRe, code('$1'))
const subject = wrapSpecs(commit.bareMessage)
entry.push([scope, subject].filter(Boolean).join(' '))

// A list og the authors github handles or names
107 changes: 107 additions & 0 deletions lib/release/node-workspace-format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
const localeCompare = require('@isaacs/string-locale-compare')('en')
const { ManifestPlugin } = require('release-please/build/src/plugin.js')
const { addPath } = require('release-please/build/src/plugins/workspace.js')
const { TagName } = require('release-please/build/src/util/tag-name.js')
const { ROOT_PROJECT_PATH } = require('release-please/build/src/manifest.js')
const { DEPS, link, wrapSpecs } = require('./util.js')

const WORKSPACE_MESSAGE = (name, version) => `${DEPS}(workspace): ${name}@${version}`
const WORKSPACE_SCOPE = /(?<scope>workspace): `?(?<name>\S+?)[@\s](?<version>\S+?)`?$/gm

module.exports = class extends ManifestPlugin {
static WORKSPACE_MESSAGE = WORKSPACE_MESSAGE

#releasesByPackage = new Map()
#pathsByComponent = new Map()

async preconfigure (strategiesByPath) {
// First build a list of all releases that will happen based on
// the conventional commits
for (const path in strategiesByPath) {
const component = await strategiesByPath[path].getComponent()
const packageName = await await strategiesByPath[path].getDefaultPackageName()
this.#pathsByComponent.set(component, path)
this.#releasesByPackage.set(packageName, { path, component })
}

return strategiesByPath
}

run (candidates) {
this.#rewriteWorkspaceChangelogItems(candidates)
this.#sortReleases(candidates)
return candidates
}

// I don't like how release-please formats workspace changelog entries
// so this rewrites them to look like the rest of our changelog. This can't
// be part of the changelog plugin since they are written after that by the
// node-workspace plugin. A possible PR to release-please could add an option
// to customize these or run them through the changelog notes generator.
#rewriteWorkspaceChangelogItems (candidates) {
for (const candidate of candidates) {
for (const release of candidate.pullRequest.body.releaseData) {
// Update notes with a link to each workspaces release notes
// now that we have all of the releases in a single pull request
release.notes =
release.notes.replace(WORKSPACE_SCOPE, (...args) => {
const { scope, name, version } = args.pop()
const { path, component } = this.#releasesByPackage.get(name)
const { tagSeparator, includeVInTag } = this.repositoryConfig[path]
const { repository: { owner, repo } } = this.github
const tag = new TagName(version, component, tagSeparator, includeVInTag).toString()
const url = `https://github.com/${owner}/${repo}/releases/tag/${tag}`
return `${link(scope, url)}: ${wrapSpecs(`${name}@${version}`)}`
})

// remove the other release please dependencies list which always starts with
// the following line and then each line is indented. so we search for the line
// and remove and indented lines until the next non indented line.
let foundRemoveStart = false
let foundRemoveEnd = false
release.notes = release.notes
.split('\n')
.filter((line) => {
if (line === '* The following workspace dependencies were updated') {
foundRemoveStart = true
} else if (foundRemoveStart && !foundRemoveEnd) {
// TODO: test when inserted dependencies is not the last thing in the changelog
/* istanbul ignore next */
if (!line || !line.startsWith(' ')) {
foundRemoveEnd = true
}
}
// If we found the start, remove all lines until we've found the end
return foundRemoveStart ? foundRemoveEnd : true
})
.join('\n')

// Find the associated changelog and update that too
const path = this.#pathsByComponent.get(release.component)
for (const update of candidate.pullRequest.updates) {
if (update.path === addPath(path, 'CHANGELOG.md')) {
update.updater.changelogEntry = release.notes
}
}
}
}
}

// Sort root release to the top of the pull request
// release please pre sorts based on graph order so
#sortReleases (candidates) {
for (const candidate of candidates) {
candidate.pullRequest.body.releaseData.sort((a, b) => {
const aPath = this.#pathsByComponent.get(a.component)
const bPath = this.#pathsByComponent.get(b.component)
if (aPath === ROOT_PROJECT_PATH) {
return -1
}
if (bPath === ROOT_PROJECT_PATH) {
return 1
}
return localeCompare(aPath, bPath)
})
}
}
}
208 changes: 55 additions & 153 deletions lib/release/node-workspace.js
Original file line number Diff line number Diff line change
@@ -1,192 +1,94 @@
const localeCompare = require('@isaacs/string-locale-compare')('en')
const { NodeWorkspace } = require('release-please/build/src/plugins/node-workspace.js')
const { RawContent } = require('release-please/build/src/updaters/raw-content.js')
const { jsonStringify } = require('release-please/build/src/util/json-stringify.js')
const { addPath } = require('release-please/build/src/plugins/workspace.js')
const { TagName } = require('release-please/build/src/util/tag-name.js')
const { ROOT_PROJECT_PATH } = require('release-please/build/src/manifest.js')
const { link, code, makeGitHubUrl } = require('./util.js')

const SCOPE = '__REPLACE_WORKSPACE_DEP__'
const WORKSPACE_DEP = new RegExp(`${SCOPE}: (\\S+) (\\S+)`, 'gm')

const { NodeWorkspace } = require('release-please/build/src/plugins/node-workspace')
const { parseConventionalCommits } = require('release-please/build/src/commit')
const { DEPS } = require('./util')
const { WORKSPACE_MESSAGE } = require('./node-workspace-format')

// This adds a preconfigure method to the release-please node-workspace plugin
// which fixes https://github.com/googleapis/release-please/issues/2089 for our
// use case. We should attempt to upstream this to release-please but it
// fundamentally changes the way the node-workspace plugin behaves so it might
// not be easy to land. For now we extend the base plugin and add one method
// which is much better than previously when we needed to fork and maintain
// release-please ourselves.
class NpmNodeWorkspace extends NodeWorkspace {
#ghUrl
#releasesByPackage = new Map()
#pathsByComponent = new Map()

constructor (github, ...args) {
super(github, ...args)
this.#ghUrl = makeGitHubUrl(github.repository.owner, github.repository.repo)
}

async preconfigure (strategiesByPath, commitsByPath, releasesByPath) {
// First build a list of all releases that will happen based on
// the conventional commits
// First build a list of all releases that will happen based on the
// conventional commits
const candidates = []
for (const path in strategiesByPath) {
const pullRequest = await strategiesByPath[path].buildReleasePullRequest(
commitsByPath[path],
parseConventionalCommits(commitsByPath[path]),
releasesByPath[path]
)
if (pullRequest?.version) {
candidates.push({ path, pullRequest })
// Release please types say this sometimes will return undefined but I could not
// get any scenario where that was the case. If it was undefined we would want to
// just ignore it anyway.
/* istanbul ignore else */
if (pullRequest) {
candidates.push({
path,
pullRequest,
config: this.repositoryConfig[path],
})
}
}

// Then build the graph of all those releases + any other connected workspaces
const { allPackages, candidatesByPackage } = await this.buildAllPackages(candidates)
const orderedPackages = this.buildGraphOrder(
await this.buildGraph(allPackages),
Object.keys(candidatesByPackage)
)
const graph = await this.buildGraph(allPackages)
const packageNamesToUpdate = this.packageNamesToUpdate(graph, candidatesByPackage)
const graphPackages = this.buildGraphOrder(graph, packageNamesToUpdate)

// Then build a list of all the updated versions
const updatedVersions = new Map()
for (const pkg of orderedPackages) {
const updatedVersions = {}
for (const pkg of graphPackages) {
const path = this.pathFromPackage(pkg)
const packageName = this.packageNameFromPackage(pkg)

let version = null
const existingCandidate = candidatesByPackage[packageName]

if (existingCandidate) {
// If there is an existing pull request use that version
version = existingCandidate.pullRequest.version
updatedVersions[packageName] = existingCandidate.pullRequest?.version
} else {
// Otherwise build another pull request (that will be discarded) just
// to see what the version would be if it only contained a deps commit.
// This is to make sure we use any custom versioning or release strategy.
const strategy = strategiesByPath[path]
const depsSection = strategy.changelogSections.find(c => c.section === 'Dependencies')
const releasePullRequest = await strategiesByPath[path].buildReleasePullRequest(
[{ message: `${depsSection.type}:` }],
parseConventionalCommits([{ message: `${DEPS}: ${Math.random()}` }]),
releasesByPath[path]
)
version = releasePullRequest.version
updatedVersions[packageName] = releasePullRequest?.version
}

updatedVersions.set(packageName, version)
}

// Now save some data about the preconfiugred releases so we can look it up later
// when rewriting the changelogs

// Then go through all the packages again and add deps commits
// for each updated workspace
for (const pkg of orderedPackages) {
// Then go through all the packages again and add deps commits for each
// updated workspace
for (const pkg of graphPackages) {
const path = this.pathFromPackage(pkg)
const packageName = this.packageNameFromPackage(pkg)
const graphPackage = this.packageGraph.get(pkg.name)

// Update dependency versions
for (const [depName, resolved] of graphPackage.localDependencies) {
const depVersion = updatedVersions.get(depName)
const isNotDir = resolved.type !== 'directory'

// Due to some bugs in release-please we need to create `deps:` commits
// for all dev and prod dependencies even though we normally would only
// do a `chore:` commit to bump a dev dep. The tradeoff is an extra
// patch release to workspaces that depend on other workspaces as dev
// dependencies.
if (depVersion && isNotDir) {
commitsByPath[path].push({
message: `deps(${SCOPE}): ${depName} ${depVersion.toString()}`,
})
}
}

const component = await strategiesByPath[path].getComponent()
this.#pathsByComponent.set(component, path)
this.#releasesByPackage.set(packageName, {
path,
component,
})
}

return strategiesByPath
}

// This is copied from the release-please node-workspace plugin
// except it only updates the package.json instead of appending
// anything to changelogs since we've already done that in preconfigure.
updateCandidate (candidate, pkg, updatedVersions) {
const newVersion = updatedVersions.get(pkg.name)
const graphPackage = this.packageGraph.get(pkg.name)

const updatedPackage = pkg.clone()
updatedPackage.version = newVersion.toString()
for (const [depName, resolved] of graphPackage.localDependencies) {
const depVersion = updatedVersions.get(depName)
if (depVersion && resolved.type !== 'directory') {
updatedPackage.updateLocalDependency(resolved, depVersion.toString(), '^')
}
}

for (const update of candidate.pullRequest.updates) {
if (update.path === addPath(candidate.path, 'package.json')) {
update.updater = new RawContent(
jsonStringify(updatedPackage.toJSON(), updatedPackage.rawContent)
)
}
}

return candidate
}

postProcessCandidates (candidates) {
for (const candidate of candidates) {
for (const release of candidate.pullRequest.body.releaseData) {
// Update notes with a link to each workspaces release notes
// now that we have all of the releases in a single pull request
release.notes = release.notes.replace(WORKSPACE_DEP, (_, depName, depVersion) => {
const { path, component } = this.#releasesByPackage.get(depName)
const url = this.#ghUrl('releases/tag', new TagName(
depVersion,
component,
this.repositoryConfig[path].tagSeparator,
this.repositoryConfig[path].includeVInTag
).toString())
return `${link('Workspace', url)}: ${code(`${depName}@${depVersion}`)}`
})

// Find the associated changelog and update that too
const path = this.#pathsByComponent.get(release.component)
for (const update of candidate.pullRequest.updates) {
if (update.path === addPath(path, 'CHANGELOG.md')) {
update.updater.changelogEntry = release.notes
const graphPackage = graph.get(packageName)

// Update dependency versions add a deps commit to each path so it gets
// processed later. This else never happens in our cases because our
// packages always have deps, but keeping this around to make it easier to
// upstream in the future.
/* istanbul ignore else */
if (graphPackage.deps) {
for (const depName of graphPackage.deps) {
const depVersion = updatedVersions[depName]
// Same as the above check, we always have a version here but technically
// we could not in which it would be safe to ignore it.
/* istanbul ignore else */
if (depVersion) {
commitsByPath[path].push({
message: WORKSPACE_MESSAGE(depName, depVersion.toString()),
})
}
}
}

// Sort root release to the top of the pull request
candidate.pullRequest.body.releaseData.sort((a, b) => {
const aPath = this.#pathsByComponent.get(a.component)
const bPath = this.#pathsByComponent.get(b.component)
if (aPath === ROOT_PROJECT_PATH) {
return -1
}
// release please pre sorts based on graph order so
// this is never called in normal circumstances
/* istanbul ignore next */
if (bPath === ROOT_PROJECT_PATH) {
return 1
}
return localeCompare(aPath, bPath)
})
}

return candidates
}

// Stub these methods with errors since the preconfigure method should negate these
// ever being called from the release please base class. If they are called then
// something has changed that would likely break us in other ways.
bumpVersion () {
throw new Error('Should not bump packages. This should be done in preconfigure.')
}

newCandidate () {
throw new Error('Should not create new candidates. This should be done in preconfigure.')
return strategiesByPath
}
}

194 changes: 112 additions & 82 deletions lib/release/release-manager.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,54 @@
const { Octokit } = require('@octokit/rest')
const core = require('@actions/core')
const { join } = require('path')
const semver = require('semver')
const assert = require('assert')
const dedent = require('dedent')
const mapWorkspaces = require('@npmcli/map-workspaces')
const { getPublishTag } = require('./util')
const { request: fetch } = require('undici')
const { getPublishTag, block, noop } = require('./util')

class ReleaseManager {
#octokit
#owner
#repo
#cwd
#pkg
#pr
#backport
#defaultTag
#lockfile
#publish

#info

constructor ({
token,
repo,
cwd,
pkg,
cwd = process.cwd(),
pr,
backport,
defaultTag,
lockfile,
publish,
silent,
}) {
assert(token, 'GITHUB_TOKEN is required')
assert(repo, 'GITHUB_REPOSITORY is required')
assert(cwd, 'cwd is required')
assert(pkg, 'pkg is required')
assert(pr, 'pr is required')
assert(defaultTag, 'defaultTag is required')

this.#octokit = new Octokit({ auth: token })
this.#owner = repo.split('/')[0]
this.#repo = repo.split('/')[1]
this.#cwd = cwd
this.#pkg = pkg
this.#pr = pr
this.#backport = backport
this.#defaultTag = defaultTag
this.#lockfile = lockfile
this.#publish = publish

this.#info = silent ? noop : core.info
}

static async run (options) {
@@ -59,11 +63,11 @@ class ReleaseManager {
pull_number: this.#pr,
})

const [release, workspaces = []] = await this.#getPrReleases({ pullRequest })
const [release, workspaces] = await this.#getPrReleases({ pullRequest })
const releaseItems = await this.#getReleaseProcess({ release, workspaces })

core.info(`Filtered ${releaseItems.length} release process items:`)
core.info(releaseItems.map(r => r.split('\n')[0].replace('- [ ] ', '')).join(', '))
this.#info(`Filtered ${releaseItems.length} release process items:`)
this.#info(releaseItems.map(r => r.split('\n')[0].replace('- [ ] ', '')).join(', '))

const checklist = releaseItems
.join('\n\n')
@@ -81,87 +85,105 @@ class ReleaseManager {
}

async #getPrReleases ({ pullRequest }) {
const RELEASE_SEPARATOR = /<details><summary>.*<\/summary>/g
const MONO_VERSIONS = /<details><summary>(?:(.*?):\s)?(.*?)<\/summary>/
const ROOT_VERSION = /\n##\s\[(.*?)\]/
return /<details><summary>.*<\/summary>/.test(pullRequest.body)
? await this.#getPrMonoRepoReleases({ pullRequest })
: [this.#getPrRootRelease({ pullRequest }), []]
}

async #getPrMonoRepoReleases ({ pullRequest }) {
const releases = pullRequest.body.match(/<details><summary>.*<\/summary>/g)
this.#info(`Found ${releases.length} releases`)

const workspaces = [...await mapWorkspaces({ pkg: this.#pkg, cwd: this.#cwd })]
const workspacesComponents = [...await mapWorkspaces({
cwd: this.#cwd,
pkg: require(join(this.#cwd, 'package.json')),
})]
.reduce((acc, [k]) => {
const wsComponentName = k.startsWith('@') ? k.split('/')[1] : k
acc[wsComponentName] = k
return acc
}, {})

const getReleaseInfo = ({ name, version: rawVersion }) => {
const version = semver.parse(rawVersion)
const prerelease = !!version.prerelease.length
const tag = `${name ? `${name}-` : ''}v${rawVersion}`
const workspace = workspaces[name]
const publishTag = getPublishTag(rawVersion, {
backport: this.#backport,
defaultTag: this.#defaultTag,
})

return {
name,
tag,
prerelease,
version: rawVersion,
major: version.major,
url: `https://github.com/${pullRequest.base.repo.full_name}/releases/tag/${tag}`,
flags: `${name ? `-w ${workspace}` : ''} --tag=${publishTag}`.trim(),
}
}

const releases = pullRequest.body.match(RELEASE_SEPARATOR)

if (!releases) {
core.info('Found no monorepo, checking for single root version')
const [, version] = pullRequest.body.match(ROOT_VERSION) || []

if (!version) {
throw new Error('Could not find version with:', ROOT_VERSION)
}

core.info(`Found version: ${version}`)
return [getReleaseInfo({ version })]
}

core.info(`Found ${releases.length} releases`)
const MONO_VERSIONS = /<details><summary>(?:(.*?):\s)?(.*?)<\/summary>/

return releases.reduce((acc, r) => {
const [, name, version] = r.match(MONO_VERSIONS)
const release = getReleaseInfo({ name, version })

if (!name) {
core.info(`Found root: ${JSON.stringify(release)}`)
const release = this.#getPrReleaseInfo({
pullRequest,
name,
version,
workspaces: workspacesComponents,
})

if (release.isRoot) {
this.#info(`Found root: ${JSON.stringify(release)}`)
acc[0] = release
} else {
core.info(`Found workspace: ${JSON.stringify(release)}`)
this.#info(`Found workspace: ${JSON.stringify(release)}`)
acc[1].push(release)
}

return acc
}, [null, []])
}

#getPrRootRelease ({ pullRequest }) {
this.#info('Found no monorepo, checking for single root version')

const match = pullRequest.body.match(/\n##\s\[(.*?)\]/)
assert(match, 'Could not find single root version in body')

const version = match[1]
this.#info(`Found version: ${version}`)

return this.#getPrReleaseInfo({ pullRequest, version })
}

#getPrReleaseInfo ({ pullRequest, workspaces = {}, name = null, version: rawVersion }) {
const version = semver.parse(rawVersion)
const prerelease = !!version.prerelease.length
const tag = `${name ? `${name}-` : ''}v${rawVersion}`
const publishTag = getPublishTag(rawVersion, {
backport: this.#backport,
defaultTag: this.#defaultTag,
})

return {
isRoot: !name,
tag,
prerelease,
version: rawVersion,
major: version.major,
url: `https://github.com/${pullRequest.base.repo.full_name}/releases/tag/${tag}`,
flags: [
workspaces[name] ? `-w ${workspaces[name]}` : null,
`--tag=${publishTag}`,
].filter(Boolean).join(' '),
}
}

async #getReleaseProcess ({ release, workspaces }) {
const RELEASE_LIST_ITEM = /^\d+\.\s/gm

core.info(`Fetching release process from repo wiki: ${this.#owner}/${this.#repo}`)
this.#info(`Fetching release process from repo wiki: ${this.#owner}/${this.#repo}`)

const releaseProcess = await fetch(
`https://raw.githubusercontent.com/wiki/${this.#owner}/${this.#repo}/Release-Process.md`
)
.then(r => {
if (r.status === 200) {
core.info('Found release process from wiki')
return r.text()
} else if (r.status === 404) {
core.info('No release process found in wiki, falling back to default process')
// If the url fails with anything but a 404 we want the process to blow
// up because that means something is very wrong. This is a rare edge
// case that isn't worth testing.
/* istanbul ignore else */
if (r.statusCode === 200) {
this.#info('Found release process from wiki')
return r.body.text()
} else if (r.statusCode === 404) {
this.#info('No release process found in wiki, falling back to default process')
return this.#getReleaseSteps()
} else {
throw new Error(`Release process fetch failed with status: ${r.status}`)
throw new Error(`Release process fetch failed with status: ${r.statusCode}`)
}
})

@@ -172,9 +194,9 @@ class ReleaseManager {

return section
.split({
[Symbol.split] (str) {
[Symbol.split]: (str) => {
const [, ...matches] = str.split(RELEASE_LIST_ITEM)
core.info(`Found ${matches.length} release items`)
this.#info(`Found ${matches.length} release items`)
return matches.map((m) => `- [ ] <STEP_INDEX>. ${m}`.trim())
},
})
@@ -194,63 +216,71 @@ class ReleaseManager {

#getReleaseSteps () {
const R = `-R ${this.#owner}/${this.#repo}`
/* eslint-disable max-len */
const MANUAL_PUBLISH_STEPS = dedent`

const manualSteps = `
1. Checkout the release branch and test
\`\`\`sh
${block('sh')}
gh pr checkout <PR-NUMBER> --force
npm ${this.#lockfile ? 'ci' : 'update'}
npm test
gh pr checks <PR-NUMBER> ${R} --watch
\`\`\`
${block()}
1. Publish workspaces
\`\`\`sh
${block('sh')}
npm publish -w <WS-PKG-N>
\`\`\`
${block()}
1. Publish
\`\`\`sh
${block('sh')}
npm publish <PUBLISH-FLAGS>
\`\`\`
${block()}
1. Merge release PR
\`\`\`sh
gh pr merge <PR-NUMBER> ${R} --rebase
${block('sh')}
gh pr merge <PR-NUMBER> ${R} --squash
git checkout <BASE-BRANCH>
git fetch
git reset --hard origin/<BASE-BRANCH>
\`\`\`
${block()}
`

const AUTO_PUBLISH_STEPS = dedent`
const autoSteps = `
1. Approve this PR
\`\`\`sh
${block('sh')}
gh pr review <PR-NUMBER> ${R} --approve
\`\`\`
${block()}
1. Merge release PR :rotating_light: Merging this will auto publish :rotating_light:
\`\`\`sh
gh pr merge <PR-NUMBER> ${R} --rebase
\`\`\`
${block('sh')}
gh pr merge <PR-NUMBER> ${R} --squash
${block()}
`

return (this.#publish ? AUTO_PUBLISH_STEPS : MANUAL_PUBLISH_STEPS) + dedent`
/* eslint-disable max-len */
const alwaysSteps = `
1. Check For Release Tags
Release Please will run on the just pushed release commit and create GitHub releases and tags for each package.
\`\`\`
${block('sh')}
gh run watch ${R} $(gh run list ${R} -w release -b <BASE-BRANCH> -L 1 --json databaseId -q ".[0].databaseId")
\`\`\`
${block()}
`
/* eslint-enable max-len */

return [
this.#publish ? autoSteps : manualSteps,
alwaysSteps,
]
.map(v => dedent(v))
.join('\n\n')
}
}

64 changes: 41 additions & 23 deletions lib/release/release-please.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
const {
Manifest,
GitHub,
registerChangelogNotes,
registerVersioningStrategy,
registerPlugin,
} = require('release-please')
const RP = require('release-please')
const { ROOT_PROJECT_PATH } = require('release-please/build/src/manifest.js')
const { CheckpointLogger, logger } = require('release-please/build/src/util/logger.js')
const assert = require('assert')
const core = require('@actions/core')
const omit = require('just-omit')
const ChangelogNotes = require('./changelog.js')
const Version = require('./version.js')
const NodeWs = require('./node-workspace.js')
const { getPublishTag } = require('./util.js')
const NodeWorkspace = require('./node-workspace.js')
const NodeWorkspaceFormat = require('./node-workspace-format.js')
const { getPublishTag, noop } = require('./util.js')

class ReleasePlease {
#token
@@ -21,6 +17,9 @@ class ReleasePlease {
#backport
#defaultTag
#overrides
#silent
#trace
#info

#github
#octokit
@@ -33,10 +32,13 @@ class ReleasePlease {
backport,
defaultTag,
overrides,
silent,
trace,
}) {
assert(token, 'token is required')
assert(repo, 'repo is required')
assert(branch, 'branch is required')
assert(defaultTag, 'defaultTag is required')

this.#token = token
this.#owner = repo.split('/')[0]
@@ -45,6 +47,8 @@ class ReleasePlease {
this.#backport = backport
this.#defaultTag = defaultTag
this.#overrides = overrides
this.#silent = silent
this.#trace = trace
}

static async run (options) {
@@ -54,20 +58,33 @@ class ReleasePlease {
}

async init () {
registerChangelogNotes('default', ({ github, ...options }) =>
new ChangelogNotes(github, options))
registerVersioningStrategy('default', ({ github, ...options }) =>
new Version(github, options))
registerPlugin('node-workspace', ({ github, targetBranch, repositoryConfig, ...options }) =>
new NodeWs(github, targetBranch, repositoryConfig, options))

this.#github = await GitHub.create({
RP.registerChangelogNotes('default', ({ github, ...o }) =>
new ChangelogNotes(github, o))
RP.registerPlugin('node-workspace', ({ github, targetBranch, repositoryConfig, ...o }) =>
new NodeWorkspace(github, targetBranch, repositoryConfig, o))
RP.registerPlugin('node-workspace-format', ({ github, targetBranch, repositoryConfig, ...o }) =>
new NodeWorkspaceFormat(github, targetBranch, repositoryConfig, o))

if (this.#silent) {
this.#info = noop
RP.setLogger(Object.entries(logger).reduce((acc, [k, v]) => {
if (typeof v === 'function') {
acc[k] = noop
}
return acc
}, {}))
} else {
this.#info = core.info
RP.setLogger(new CheckpointLogger(true, !!this.#trace))
}

this.#github = await RP.GitHub.create({
owner: this.#owner,
repo: this.#repo,
token: this.#token,
})
this.#octokit = this.#github.octokit
this.#manifest = await Manifest.fromManifest(
this.#manifest = await RP.Manifest.fromManifest(
this.#github,
this.#branch,
undefined,
@@ -81,7 +98,7 @@ class ReleasePlease {
const releases = await this.#getReleases()

if (rootPr) {
core.info(`root pr: ${JSON.stringify(omit(rootPr, 'body'), null, 2)}`)
this.#info(`root pr: ${JSON.stringify(omit(rootPr, 'body'))}`)

// release please does not guarantee that the release PR will have the latest sha,
// but we always need it so we can attach the relevant checks to the sha.
@@ -93,10 +110,10 @@ class ReleasePlease {
}

if (releases) {
core.info(`found releases: ${releases.length}`)
this.#info(`found releases: ${releases.length}`)

for (const release of releases) {
core.info(`release: ${JSON.stringify(omit(release, 'notes'), null, 2)}`)
this.#info(`release: ${JSON.stringify(omit(release, 'notes'))}`)

release.publishTag = getPublishTag(release.version, {
backport: this.#backport,
@@ -139,7 +156,8 @@ class ReleasePlease {
const workspaceReleases = []

for (const release of rawReleases) {
if (!rootRelease && release.path === '.') {
if (release.path === ROOT_PROJECT_PATH) {
assert(!rootRelease, 'Multiple root releases detected. This should never happen.')
rootRelease = release
} else {
workspaceReleases.push(release)
37 changes: 26 additions & 11 deletions lib/release/util.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
const semver = require('semver')

module.exports.specRe = new RegExp(`([^\\s]+@${semver.src[semver.tokens.FULLPLAIN]})`, 'g')

module.exports.code = (c) => `\`${c}\``
module.exports.link = (text, url) => `[${text}](${url})`
module.exports.list = (text) => `* ${text}`

module.exports.dateFmt = (date = new Date()) => {
const SPEC = new RegExp(`([^\\s]+@${semver.src[semver.tokens.FULLPLAIN]})`, 'g')
const code = (c) => `\`${c}\``
const wrapSpecs = (str) => str.replace(SPEC, code('$1'))
const block = (lang) => `\`\`\`${lang ? `${lang}` : ''}`
const link = (text, url) => `[${text}](${url})`
const list = (text) => `* ${text}`
const formatDate = (date = new Date()) => {
const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')
return [year, month, day].join('-')
}

// TODO: test this
/* istanbul ignore next */
module.exports.getPublishTag = (v, { backport, defaultTag }) => {
const getPublishTag = (v, { backport, defaultTag }) => {
const version = semver.parse(v)
return version.prerelease.length
? `prerelease-${version.major}`
: backport ? `latest-${backport}`
: defaultTag.replace(/{{\s*major\s*}}/, version.major)
}

module.exports.makeGitHubUrl = (owner, repo) =>
const makeGitHubUrl = (owner, repo) =>
(...p) => `https://github.com/${owner}/${repo}/${p.join('/')}`

const noop = () => {}

module.exports = {
// we use this string a lot, its duplicated in the changelog sections but its hard
// to access from within release please so we keep it here also.
DEPS: 'deps',
wrapSpecs,
code,
block,
link,
list,
formatDate,
getPublishTag,
makeGitHubUrl,
noop,
}
101 changes: 0 additions & 101 deletions lib/release/version.js

This file was deleted.

12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -19,7 +19,8 @@
"postlint": "template-oss-check",
"postinstall": "template-oss-apply",
"test-all": "npm run test -ws -iwr --if-present",
"lint-all": "npm run lint -ws -iwr --if-present"
"lint-all": "npm run lint -ws -iwr --if-present",
"test:record": "TAP_SNAPSHOT=1 NOCK_RECORD=1 tap"
},
"repository": {
"type": "git",
@@ -55,8 +56,9 @@
"minimatch": "^9.0.2",
"npm-package-arg": "^11.0.1",
"proc-log": "^3.0.0",
"release-please": "npm:@npmcli/release-please@^14.2.6",
"release-please": "^16.3.1",
"semver": "^7.3.5",
"undici": "^5.27.2",
"yaml": "^2.1.1"
},
"files": [
@@ -66,6 +68,7 @@
"devDependencies": {
"@npmcli/eslint-config": "^4.0.0",
"@npmcli/template-oss": "file:./",
"nock": "^13.3.8",
"tap": "^16.0.0"
},
"tap": {
@@ -76,7 +79,10 @@
"--exclude",
"tap-snapshots/**"
],
"test-ignore": "^(workspace/test-workspace)/"
"test-ignore": "^(workspace/test-workspace)/",
"node-arg": [
"--no-experimental-fetch"
]
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
9 changes: 6 additions & 3 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -28,10 +28,13 @@
],
"packages": {
".": {
"package-name": ""
"package-name": "",
"exclude-paths": [
"workspace/test-workspace"
]
}
},
"exclude-packages-from-root": true,
"group-pull-request-title-pattern": "chore: release ${version}",
"pull-request-title-pattern": "chore: release${component} ${version}"
"pull-request-title-pattern": "chore: release${component} ${version}",
"prerelease-type": "pre"
}
18 changes: 12 additions & 6 deletions tap-snapshots/test/apply/source-snapshots.js.test.cjs
Original file line number Diff line number Diff line change
@@ -1358,7 +1358,6 @@ package.json
release-please-config.json
========================================
{
"exclude-packages-from-root": true,
"group-pull-request-title-pattern": "chore: release \${version}",
"pull-request-title-pattern": "chore: release\${component} \${version}",
"changelog-sections": [
@@ -1388,6 +1387,7 @@ release-please-config.json
"hidden": false
}
],
"prerelease-type": "pre",
"packages": {
".": {
"package-name": ""
@@ -3009,9 +3009,9 @@ release-please-config.json
========================================
{
"plugins": [
"node-workspace"
"node-workspace",
"node-workspace-format"
],
"exclude-packages-from-root": true,
"group-pull-request-title-pattern": "chore: release \${version}",
"pull-request-title-pattern": "chore: release\${component} \${version}",
"changelog-sections": [
@@ -3041,9 +3041,14 @@ release-please-config.json
"hidden": false
}
],
"prerelease-type": "pre",
"packages": {
".": {
"package-name": ""
"package-name": "",
"exclude-paths": [
"workspaces/a",
"workspaces/b"
]
},
"workspaces/a": {},
"workspaces/b": {}
@@ -4375,9 +4380,9 @@ release-please-config.json
========================================
{
"plugins": [
"node-workspace"
"node-workspace",
"node-workspace-format"
],
"exclude-packages-from-root": true,
"group-pull-request-title-pattern": "chore: release \${version}",
"pull-request-title-pattern": "chore: release\${component} \${version}",
"changelog-sections": [
@@ -4407,6 +4412,7 @@ release-please-config.json
"hidden": false
}
],
"prerelease-type": "pre",
"packages": {
"workspaces/a": {},
"workspaces/b": {}
42 changes: 42 additions & 0 deletions tap-snapshots/test/release/release-manager-cwd.js.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/release/release-manager.js TAP cwd > expect resolving Promise 1`] = `
### Release Checklist for v10.2.5
- [ ] 1. Checkout the release branch and test
\`\`\` sh
gh pr checkout 7009 --force
npm update
npm test
gh pr checks 7009 -R npm/cli --watch
\`\`\`
- [ ] 2. Publish
\`\`\` sh
npm publish --tag=latest
\`\`\`
- [ ] 3. Merge release PR
\`\`\` sh
gh pr merge 7009 -R npm/cli --rebase
git checkout latest
git fetch
git reset --hard origin/latest
\`\`\`
- [ ] 4. Check For Release Tags
Release Please will run on the just pushed release commit and create GitHub releases and tags for each package.
\`\`\`
gh run watch -R npm/cli $(gh run list -R npm/cli -w release -b latest -L 1 --json databaseId -q ".[0].databaseId")
\`\`\`
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/release/release-manager.js TAP mock release manager > must match snapshot 1`] = `
### Release Checklist for v4.0.5
- [ ] 1. Checkout the release branch and test
\`\`\`sh
gh pr checkout 207 --force
npm update
npm test
gh pr checks 207 -R npm/npm-cli-release-please --watch
\`\`\`
- [ ] 2. Publish workspaces
\`\`\`sh
npm publish --tag=latest
npm publish --tag=latest
npm publish --tag=latest
\`\`\`
- [ ] 3. Publish
\`\`\`sh
npm publish --tag=latest
\`\`\`
- [ ] 4. Merge release PR
\`\`\`sh
gh pr merge 207 -R npm/npm-cli-release-please --squash
git checkout main
git fetch
git reset --hard origin/main
\`\`\`
- [ ] 5. Check For Release Tags
Release Please will run on the just pushed release commit and create GitHub releases and tags for each package.
\`\`\`sh
gh run watch -R npm/npm-cli-release-please $(gh run list -R npm/npm-cli-release-please -w release -b main -L 1 --json databaseId -q ".[0].databaseId")
\`\`\`
`
161 changes: 161 additions & 0 deletions tap-snapshots/test/release/release-manager-npm-cli.js.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/release/release-manager.js TAP npm/cli > must match snapshot 1`] = `
### Release Checklist for v10.2.5
- [ ] 1. Checkout the release branch
Ensure git status is not dirty on this branch after resetting deps. If it is, then something is probably wrong with the automated release process.
\`\`\`sh
gh pr checkout 7009 --force
\`\`\`
\`\`\`sh
npm run resetdeps
\`\`\`
\`\`\`sh
node scripts/git-dirty.js
\`\`\`
- [ ] 2. Check CI status
\`\`\`sh
gh pr checks --watch
\`\`\`
- [ ] 3. Publish the CLI and workspaces
> **Warning**:
> This will publish all updated workspaces to \`latest\`, \`prerelease\` or \`backport\` depending on their version, and will publish the CLI with the dist-tag set to \`next-10\`.
> **Note**:
> The \`--test\` argument can optionally be omitted to run the publish process without running any tests locally.
\`\`\`sh
node scripts/publish.js --test
\`\`\`
- [ ] 4. Optionally install and test \`npm@10.2.5\` locally
\`\`\`sh
npm i -g npm@10.2.5
\`\`\`
\`\`\`sh
npm --version
npm whoami
npm help install
# etc
\`\`\`
- [ ] 5. Set \`latest\` \`dist-tag\` to newly published version
> **Warning**:
> NOT FOR PRERELEASE: Do not run this step for prereleases or if \`10\` is not being set to \`latest\`.
\`\`\`sh
node . dist-tag add npm@10.2.5 latest
\`\`\`
- [ ] 6. Trigger \`docs.npmjs.com\` update
\`\`\`sh
gh workflow run update-cli.yml --repo npm/documentation
\`\`\`
- [ ] 7. Merge release PR
\`\`\`sh
gh pr merge --rebase
\`\`\`
\`\`\`sh
git checkout latest
\`\`\`
\`\`\`sh
git fetch
\`\`\`
\`\`\`sh
git reset --hard origin/latest
\`\`\`
\`\`\`sh
node . run resetdeps
\`\`\`
- [ ] 8. Wait For Release Tags
> **Warning**:
> The remaining steps all require the GitHub tags and releases to be created first. These are done once this PR has been labelled with \`autorelease: tagged\`.
Release Please will run on the just merged release commit and create GitHub releases and tags for each package. The release bot will will comment on this PR when the releases and tags are created.
> **Note**:
> The release workflow also includes the Node integration tests which do not need to finish before continuing.
You can watch the release workflow in your terminal with the following command:
\`\`\`
gh run watch \`gh run list -R npm/cli -w release -b latest -L 1 --json databaseId -q ".[0].databaseId"\`
\`\`\`
- [ ] 9. Mark GitHub Release as \`latest\`
> **Warning**:
> You must wait for CI to create the release tags before running this step. These are done once this PR has been labelled with \`autorelease: tagged\`.
Release Please will make GitHub Releases for the CLI and all workspaces, but GitHub has UI affordances for which release should appear as the "latest", which should always be the CLI. To mark the CLI release as latest run this command:
\`\`\`sh
gh release -R npm/cli edit v10.2.5 --latest
\`\`\`
- [ ] 10. Open \`nodejs/node\` PR to update \`npm\` to latest
> **Warning**:
> You must wait for CI to create the release tags before running this step. These are done once this PR has been labelled with \`autorelease: tagged\`.
Trigger the [**Create Node PR** action](https://github.com/npm/cli/actions/workflows/create-node-pr.yml). This will open a PR on \`nodejs/node\` to the \`main\` branch.
> **Note**:
> The resulting PR *may* need to be labelled if it is not intended to land on old Node versions.
First, sync our fork of node with the upstream source:
\`\`\`sh
gh repo sync npm/node --source nodejs/node --force
\`\`\`
Then, if we are opening a PR against the latest version of node:
\`\`\`sh
gh workflow run create-node-pr.yml -f spec=next-10
\`\`\`
If the PR should be opened on a different branch (such as when doing backports) then run it with \`-f branch=<BRANCH_NAME>\`. There is also a shortcut to target a specific Node version by specifying a major version number with \`-f branch=18\` (or similar).
For example, this will create a PR on nodejs/node to the \`v16.x-staging\` branch:
\`\`\`sh
gh workflow run create-node-pr.yml -f spec=next-10 -f branch=16
\`\`\`
- [ ] 11. Label and fast-track \`nodejs/node\` PR
> **Note**:
> This requires being a \`nodejs\` collaborator. Currently @lukekarrys is so ping them to do these steps!
- Thumbs-up reaction on the Fast-track comment
- Add an LGTM / Approval
- Add \`request-ci\` label to get it running CI
- Add \`commit-queue\` label once everything is green
`
152 changes: 152 additions & 0 deletions tap-snapshots/test/release/release-manager-prerelease.js.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/release/release-manager.js TAP prerelease > must match snapshot 1`] = `
### Release Checklist for v10.0.0-pre.0
- [ ] 1. Checkout the release branch
Ensure git status is not dirty on this branch after resetting deps. If it is, then something is probably wrong with the automated release process.
\`\`\`sh
gh pr checkout 6673 --force
\`\`\`
\`\`\`sh
npm run resetdeps
\`\`\`
\`\`\`sh
node scripts/git-dirty.js
\`\`\`
- [ ] 2. Check CI status
\`\`\`sh
gh pr checks --watch
\`\`\`
- [ ] 3. Publish the CLI and workspaces
> **Warning**:
> This will publish all updated workspaces to \`latest\`, \`prerelease\` or \`backport\` depending on their version, and will publish the CLI with the dist-tag set to \`next-10\`.
> **Note**:
> The \`--test\` argument can optionally be omitted to run the publish process without running any tests locally.
\`\`\`sh
node scripts/publish.js --test
\`\`\`
- [ ] 4. Optionally install and test \`npm@10.0.0-pre.0\` locally
\`\`\`sh
npm i -g npm@10.0.0-pre.0
\`\`\`
\`\`\`sh
npm --version
npm whoami
npm help install
# etc
\`\`\`
- [ ] 5. Trigger \`docs.npmjs.com\` update
\`\`\`sh
gh workflow run update-cli.yml --repo npm/documentation
\`\`\`
- [ ] 6. Merge release PR
\`\`\`sh
gh pr merge --rebase
\`\`\`
\`\`\`sh
git checkout latest
\`\`\`
\`\`\`sh
git fetch
\`\`\`
\`\`\`sh
git reset --hard origin/latest
\`\`\`
\`\`\`sh
node . run resetdeps
\`\`\`
- [ ] 7. Wait For Release Tags
> **Warning**:
> The remaining steps all require the GitHub tags and releases to be created first. These are done once this PR has been labelled with \`autorelease: tagged\`.
Release Please will run on the just merged release commit and create GitHub releases and tags for each package. The release bot will will comment on this PR when the releases and tags are created.
> **Note**:
> The release workflow also includes the Node integration tests which do not need to finish before continuing.
You can watch the release workflow in your terminal with the following command:
\`\`\`
gh run watch \`gh run list -R npm/cli -w release -b latest -L 1 --json databaseId -q ".[0].databaseId"\`
\`\`\`
- [ ] 8. Mark GitHub Release as \`latest\`
> **Warning**:
> You must wait for CI to create the release tags before running this step. These are done once this PR has been labelled with \`autorelease: tagged\`.
Release Please will make GitHub Releases for the CLI and all workspaces, but GitHub has UI affordances for which release should appear as the "latest", which should always be the CLI. To mark the CLI release as latest run this command:
\`\`\`sh
gh release -R npm/cli edit v10.0.0-pre.0 --latest
\`\`\`
- [ ] 9. Open \`nodejs/node\` PR to update \`npm\` to latest
> **Warning**:
> You must wait for CI to create the release tags before running this step. These are done once this PR has been labelled with \`autorelease: tagged\`.
Trigger the [**Create Node PR** action](https://github.com/npm/cli/actions/workflows/create-node-pr.yml). This will open a PR on \`nodejs/node\` to the \`main\` branch.
> **Note**:
> The resulting PR *may* need to be labelled if it is not intended to land on old Node versions.
First, sync our fork of node with the upstream source:
\`\`\`sh
gh repo sync npm/node --source nodejs/node --force
\`\`\`
Then, if we are opening a PR against the latest version of node:
\`\`\`sh
gh workflow run create-node-pr.yml -f spec=next-10
\`\`\`
If the PR should be opened on a different branch (such as when doing backports) then run it with \`-f branch=<BRANCH_NAME>\`. There is also a shortcut to target a specific Node version by specifying a major version number with \`-f branch=18\` (or similar).
For example, this will create a PR on nodejs/node to the \`v16.x-staging\` branch:
\`\`\`sh
gh workflow run create-node-pr.yml -f spec=next-10 -f branch=16
\`\`\`
- [ ] 10. Label and fast-track \`nodejs/node\` PR
> **Note**:
> This requires being a \`nodejs\` collaborator. Currently @lukekarrys is so ping them to do these steps!
- Thumbs-up reaction on the Fast-track comment
- Add an LGTM / Approval
- Add \`request-ci\` label to get it running CI
- Add \`commit-queue\` label once everything is green
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/release/release-manager.js TAP publish and lockfile > must match snapshot 1`] = `
### Release Checklist for v7.5.4
- [ ] 1. Approve this PR
\`\`\`sh
gh pr review 586 -R npm/node-semver --approve
\`\`\`
- [ ] 2. Merge release PR :rotating_light: Merging this will auto publish :rotating_light:
\`\`\`sh
gh pr merge 586 -R npm/node-semver --squash
\`\`\`
- [ ] 3. Check For Release Tags
Release Please will run on the just pushed release commit and create GitHub releases and tags for each package.
\`\`\`sh
gh run watch -R npm/node-semver $(gh run list -R npm/node-semver -w release -b main -L 1 --json databaseId -q ".[0].databaseId")
\`\`\`
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/release/release-manager.js TAP single release > must match snapshot 1`] = `
### Release Checklist for v7.5.4
- [ ] 1. Checkout the release branch and test
\`\`\`sh
gh pr checkout 586 --force
npm update
npm test
gh pr checks 586 -R npm/node-semver --watch
\`\`\`
- [ ] 2. Publish
\`\`\`sh
npm publish --tag=latest
\`\`\`
- [ ] 3. Merge release PR
\`\`\`sh
gh pr merge 586 -R npm/node-semver --squash
git checkout main
git fetch
git reset --hard origin/main
\`\`\`
- [ ] 4. Check For Release Tags
Release Please will run on the just pushed release commit and create GitHub releases and tags for each package.
\`\`\`sh
gh run watch -R npm/node-semver $(gh run list -R npm/node-semver -w release -b main -L 1 --json databaseId -q ".[0].databaseId")
\`\`\`
`
161 changes: 161 additions & 0 deletions tap-snapshots/test/release/release-manager-workspace-names.js.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/release/release-manager.js TAP workspace names > expect resolving Promise 1`] = `
### Release Checklist for v10.2.2
- [ ] 1. Checkout the release branch
Ensure git status is not dirty on this branch after resetting deps. If it is, then something is probably wrong with the automated release process.
\`\`\`sh
gh pr checkout 6923 --force
\`\`\`
\`\`\`sh
npm run resetdeps
\`\`\`
\`\`\`sh
node scripts/git-dirty.js
\`\`\`
- [ ] 2. Check CI status
\`\`\`sh
gh pr checks --watch
\`\`\`
- [ ] 3. Publish the CLI and workspaces
> **Warning**:
> This will publish all updated workspaces to \`latest\`, \`prerelease\` or \`backport\` depending on their version, and will publish the CLI with the dist-tag set to \`next-10\`.
> **Note**:
> The \`--test\` argument can optionally be omitted to run the publish process without running any tests locally.
\`\`\`sh
node scripts/publish.js --test
\`\`\`
- [ ] 4. Optionally install and test \`npm@10.2.2\` locally
\`\`\`sh
npm i -g npm@10.2.2
\`\`\`
\`\`\`sh
npm --version
npm whoami
npm help install
# etc
\`\`\`
- [ ] 5. Set \`latest\` \`dist-tag\` to newly published version
> **Warning**:
> NOT FOR PRERELEASE: Do not run this step for prereleases or if \`10\` is not being set to \`latest\`.
\`\`\`sh
node . dist-tag add npm@10.2.2 latest
\`\`\`
- [ ] 6. Trigger \`docs.npmjs.com\` update
\`\`\`sh
gh workflow run update-cli.yml --repo npm/documentation
\`\`\`
- [ ] 7. Merge release PR
\`\`\`sh
gh pr merge --rebase
\`\`\`
\`\`\`sh
git checkout latest
\`\`\`
\`\`\`sh
git fetch
\`\`\`
\`\`\`sh
git reset --hard origin/latest
\`\`\`
\`\`\`sh
node . run resetdeps
\`\`\`
- [ ] 8. Wait For Release Tags
> **Warning**:
> The remaining steps all require the GitHub tags and releases to be created first. These are done once this PR has been labelled with \`autorelease: tagged\`.
Release Please will run on the just merged release commit and create GitHub releases and tags for each package. The release bot will will comment on this PR when the releases and tags are created.
> **Note**:
> The release workflow also includes the Node integration tests which do not need to finish before continuing.
You can watch the release workflow in your terminal with the following command:
\`\`\`
gh run watch \`gh run list -R npm/cli -w release -b latest -L 1 --json databaseId -q ".[0].databaseId"\`
\`\`\`
- [ ] 9. Mark GitHub Release as \`latest\`
> **Warning**:
> You must wait for CI to create the release tags before running this step. These are done once this PR has been labelled with \`autorelease: tagged\`.
Release Please will make GitHub Releases for the CLI and all workspaces, but GitHub has UI affordances for which release should appear as the "latest", which should always be the CLI. To mark the CLI release as latest run this command:
\`\`\`sh
gh release -R npm/cli edit v10.2.2 --latest
\`\`\`
- [ ] 10. Open \`nodejs/node\` PR to update \`npm\` to latest
> **Warning**:
> You must wait for CI to create the release tags before running this step. These are done once this PR has been labelled with \`autorelease: tagged\`.
Trigger the [**Create Node PR** action](https://github.com/npm/cli/actions/workflows/create-node-pr.yml). This will open a PR on \`nodejs/node\` to the \`main\` branch.
> **Note**:
> The resulting PR *may* need to be labelled if it is not intended to land on old Node versions.
First, sync our fork of node with the upstream source:
\`\`\`sh
gh repo sync npm/node --source nodejs/node --force
\`\`\`
Then, if we are opening a PR against the latest version of node:
\`\`\`sh
gh workflow run create-node-pr.yml -f spec=next-10
\`\`\`
If the PR should be opened on a different branch (such as when doing backports) then run it with \`-f branch=<BRANCH_NAME>\`. There is also a shortcut to target a specific Node version by specifying a major version number with \`-f branch=18\` (or similar).
For example, this will create a PR on nodejs/node to the \`v16.x-staging\` branch:
\`\`\`sh
gh workflow run create-node-pr.yml -f spec=next-10 -f branch=16
\`\`\`
- [ ] 11. Label and fast-track \`nodejs/node\` PR
> **Note**:
> This requires being a \`nodejs\` collaborator. Currently @lukekarrys is so ping them to do these steps!
- Thumbs-up reaction on the Fast-track comment
- Add an LGTM / Approval
- Add \`request-ci\` label to get it running CI
- Add \`commit-queue\` label once everything is green
`
438 changes: 438 additions & 0 deletions tap-snapshots/test/release/release-please-cases-fix-all.js.test.cjs

Large diffs are not rendered by default.

175 changes: 175 additions & 0 deletions tap-snapshots/test/release/release-please-cases-fix-one.js.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/release/release-please.js TAP cases fix one > pr fix: update pkg3 1`] = `
Object {
"baseBranchName": "template-oss-mock-testing-branch-do-not-delete",
"body": String(
:robot: I have created a release *beep* *boop*
---
<details><summary>2012.0.4</summary>
## [2012.0.4](https://github.com/npm/npm-cli-release-please/compare/v2012.0.3...v2012.0.4) (2023-11-28)
### Bug Fixes
* [\`0a2bf35\`](https://github.com/npm/npm-cli-release-please/commit/0a2bf35304e69331138d4981bd7ecc75d86e83de) update pkg3 (@lukekarrys)
### Dependencies
* [workspace](https://github.com/npm/npm-cli-release-please/releases/tag/pkg1-v2012.0.4): \`pkg1@2012.0.4\`
* [workspace](https://github.com/npm/npm-cli-release-please/releases/tag/pkg2-v2012.0.4): \`pkg2@2012.0.4\`
</details>
<details><summary>pkg1: 2012.0.4</summary>
## [2012.0.4](https://github.com/npm/npm-cli-release-please/compare/pkg1-v2012.0.3...pkg1-v2012.0.4) (2023-11-28)
### Dependencies
* [workspace](https://github.com/npm/npm-cli-release-please/releases/tag/pkg2-v2012.0.4): \`pkg2@2012.0.4\`
</details>
<details><summary>pkg2: 2012.0.4</summary>
## [2012.0.4](https://github.com/npm/npm-cli-release-please/compare/pkg2-v2012.0.3...pkg2-v2012.0.4) (2023-11-28)
### Dependencies
* [workspace](https://github.com/npm/npm-cli-release-please/releases/tag/pkg3-v2012.0.4): \`@npmcli/pkg3@2012.0.4\`
</details>
<details><summary>pkg3: 2012.0.4</summary>
## [2012.0.4](https://github.com/npm/npm-cli-release-please/compare/pkg3-v2012.0.3...pkg3-v2012.0.4) (2023-11-28)
### Bug Fixes
* [\`0a2bf35\`](https://github.com/npm/npm-cli-release-please/commit/0a2bf35304e69331138d4981bd7ecc75d86e83de) update pkg3 (@lukekarrys)
</details>
---
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
),
"files": Array [],
"headBranchName": "release-please--branches--template-oss-mock-testing-branch-do-not-delete",
"labels": Array [
"autorelease: pending",
],
"number": 251,
"sha": "2523cec182cd2199f1c41d818c69bdc07a74f80f",
"title": "chore: release 2012.0.4",
}
`

exports[`test/release/release-please.js TAP cases fix one > releases update pkg3 1`] = `
Array [
Object {
"draft": false,
"id": 131773216,
"major": 2012,
"minor": 0,
"name": "v2012.0.4",
"notes": String(
## [2012.0.4](https://github.com/npm/npm-cli-release-please/compare/v2012.0.3...v2012.0.4) (2023-11-28)
### Bug Fixes
* [\`0a2bf35\`](https://github.com/npm/npm-cli-release-please/commit/0a2bf35304e69331138d4981bd7ecc75d86e83de) update pkg3 (@lukekarrys)
### Dependencies
* [workspace](https://github.com/npm/npm-cli-release-please/releases/tag/pkg1-v2012.0.4): \`pkg1@2012.0.4\`
* [workspace](https://github.com/npm/npm-cli-release-please/releases/tag/pkg2-v2012.0.4): \`pkg2@2012.0.4\`
),
"patch": 4,
"path": ".",
"pkgName": "npm-cli-release-please",
"prNumber": 251,
"publishTag": "latest",
"sha": "731c147ec26b668c8fc52d346ded534d6cde1241",
"tagName": "v2012.0.4",
"uploadUrl": "https://uploads.github.com/repos/npm/npm-cli-release-please/releases/131773216/assets{?name,label}",
"url": "https://github.com/npm/npm-cli-release-please/releases/tag/v2012.0.4",
"version": "2012.0.4",
},
Object {
"draft": false,
"id": 131773219,
"major": 2012,
"minor": 0,
"name": "pkg1: v2012.0.4",
"notes": String(
## [2012.0.4](https://github.com/npm/npm-cli-release-please/compare/pkg1-v2012.0.3...pkg1-v2012.0.4) (2023-11-28)
### Dependencies
* [workspace](https://github.com/npm/npm-cli-release-please/releases/tag/pkg2-v2012.0.4): \`pkg2@2012.0.4\`
),
"patch": 4,
"path": "pkg1",
"pkgName": "pkg1",
"prNumber": 251,
"publishTag": "latest",
"sha": "731c147ec26b668c8fc52d346ded534d6cde1241",
"tagName": "pkg1-v2012.0.4",
"uploadUrl": "https://uploads.github.com/repos/npm/npm-cli-release-please/releases/131773219/assets{?name,label}",
"url": "https://github.com/npm/npm-cli-release-please/releases/tag/pkg1-v2012.0.4",
"version": "2012.0.4",
},
Object {
"draft": false,
"id": 131773224,
"major": 2012,
"minor": 0,
"name": "pkg2: v2012.0.4",
"notes": String(
## [2012.0.4](https://github.com/npm/npm-cli-release-please/compare/pkg2-v2012.0.3...pkg2-v2012.0.4) (2023-11-28)
### Dependencies
* [workspace](https://github.com/npm/npm-cli-release-please/releases/tag/pkg3-v2012.0.4): \`@npmcli/pkg3@2012.0.4\`
),
"patch": 4,
"path": "pkg2",
"pkgName": "pkg2",
"prNumber": 251,
"publishTag": "latest",
"sha": "731c147ec26b668c8fc52d346ded534d6cde1241",
"tagName": "pkg2-v2012.0.4",
"uploadUrl": "https://uploads.github.com/repos/npm/npm-cli-release-please/releases/131773224/assets{?name,label}",
"url": "https://github.com/npm/npm-cli-release-please/releases/tag/pkg2-v2012.0.4",
"version": "2012.0.4",
},
Object {
"draft": false,
"id": 131773227,
"major": 2012,
"minor": 0,
"name": "pkg3: v2012.0.4",
"notes": String(
## [2012.0.4](https://github.com/npm/npm-cli-release-please/compare/pkg3-v2012.0.3...pkg3-v2012.0.4) (2023-11-28)
### Bug Fixes
* [\`0a2bf35\`](https://github.com/npm/npm-cli-release-please/commit/0a2bf35304e69331138d4981bd7ecc75d86e83de) update pkg3 (@lukekarrys)
),
"patch": 4,
"path": "pkg3",
"pkgName": "@npmcli/pkg3",
"prNumber": 251,
"publishTag": "latest",
"sha": "731c147ec26b668c8fc52d346ded534d6cde1241",
"tagName": "pkg3-v2012.0.4",
"uploadUrl": "https://uploads.github.com/repos/npm/npm-cli-release-please/releases/131773227/assets{?name,label}",
"url": "https://github.com/npm/npm-cli-release-please/releases/tag/pkg3-v2012.0.4",
"version": "2012.0.4",
},
]
`
7 changes: 6 additions & 1 deletion test/apply/release-config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
const t = require('tap')
const setup = require('../setup.js')

const PLUGINS = [
'node-workspace',
'node-workspace-format',
]

t.test('root only', async (t) => {
const prvt = { private: true }
const cases = [
@@ -20,7 +25,7 @@ t.test('root only', async (t) => {
const releaseConfig = await s.readJson('release-please-config.json').catch(() => ({}))
const pr = await s.exists('.github', 'workflows', 'pull-request.yml')

t.strictSame(releaseConfig.plugins, expected.plugins ? ['node-workspace'] : undefined)
t.strictSame(releaseConfig.plugins, expected.plugins ? PLUGINS : undefined)
t.equal(pr, expected.pr)
})
}
106 changes: 106 additions & 0 deletions test/fixtures/mock-release.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const nock = require('nock')
const { resolve, relative, dirname, sep, join, basename } = require('path')
const fs = require('fs')

const DIR = __dirname
const CWD = process.cwd()
const RECORD = 'NOCK_RECORD' in process.env ? true : undefined

// These are the live GitHub repo and branch that are used to
// record fixtures for these tests
const REPO = 'npm/npm-cli-release-please'
const BRANCH = 'template-oss-mock-testing-branch-do-not-delete'

const getPath = (t) => {
const fixtureName = relative(CWD, t.testdirName).split(`${sep}tap-testdir-`)[1]
return {
fixtureName: basename(fixtureName),
fixturePath: resolve(DIR, 'nocks', `${fixtureName}.json`),
}
}

const setup = (t) => {
const { fixtureName, fixturePath } = getPath(t)

// name snapshots by the fixture name so they are all in separate files. this
// helps when trying to record/update snapshots so they dont delete other
// snapshots.
const snapshotDir = dirname(t.snapshotFile)
const snapshotFile = basename(t.snapshotFile)
t.snapshotFile = join(snapshotDir, fixtureName + snapshotFile.slice(snapshotFile.indexOf('.')))

const record = typeof RECORD === 'boolean' ? RECORD : !fs.existsSync(fixturePath)
const token = record ? process.env.GITHUB_TOKEN : 'mock_token'

if (record) {
if (!token) {
t.fail('process.env.GITHUB_TOKEN must be set to record tests')
}
if (process.env.CI) {
t.fail('cannot record fixtures in CI, only locally')
}
fs.mkdirSync(dirname(fixturePath), { recursive: true })
fs.rmSync(fixturePath, { force: true })
nock.recorder.rec({ output_objects: true, dont_print: true })
} else {
nock.define(nock.loadDefs(fixturePath))
nock.disableNetConnect()
}

t.teardown(() => {
if (record) {
fs.writeFileSync(fixturePath, JSON.stringify(nock.recorder.play()), 'utf-8')
nock.recorder.clear()
nock.restore()
} else {
nock.enableNetConnect()
}
})

return { token, record }
}

const releasePlease = async (t, { setup: s, ...opts } = {}) => {
const ReleasePlease = t.mock('../../lib/release/release-please.js')
try {
return await ReleasePlease.run({
token: s.token,
repo: REPO,
branch: BRANCH,
silent: !s.record,
trace: true,
defaultTag: 'latest',
...opts,
})
} catch (e) {
// These errors are extremely verbose so we remove some properties and
// then explicitly call fail with the updated error
delete e?.cause?.request?.body
t.fail(e)
}
}

const releaseManager = (t, {
cwd = t.testdir({ 'package.json': '{"name":"pkg"}' }),
...opts
} = {}) => {
const s = setup(t)
const ReleaseManager = t.mock('../../lib/release/release-manager.js')
return ReleaseManager.run({
token: s.token,
repo: REPO,
silent: !s.record,
cwd,
defaultTag: 'latest',
...opts,
})
}

module.exports = {
setup,
releasePlease,
releaseManager,
BRANCH,
REPO,
REPO_DIR: resolve(process.cwd(), '../..', REPO),
}
1 change: 1 addition & 0 deletions test/fixtures/nocks/release-manager-cwd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"scope":"https://api.github.com:443","method":"GET","path":"/repos/npm/npm-cli-release-please/pulls/207","body":"","status":200,"response":["1f8b0800000000000003ed5c6b73dbb612fd2b1c653293e45a2645bd3949eba44edbdcb989f370eeb475330a444212628a64f990aea2f17fbf67c1a764c5b2024dbf9471e2881470b05c008bc59e05d78d24741b566316c74164e93a0bc4e954c4b3647c6afb733de4811fe95e30a77f4ddb15cd90bb9c45bc19c8fff42071dd48378d7ee3a4219c86d5ea1903d3ec778de149c3f31d3ea29b8db7ef47d7cbf38b5fbbcbdfe783eec4ecfed542f9593c77479bcd579aded368d6a6232613258c53428030018bed991a9284203d4451c2b7a00ed5abc4c815eb25f3310f1b16d47cd288621673e8d40fb887c65cdfbee6d0f184b9113f69c42276e95b7be687dcd2b2ded23aa7c66917a5938870d6a835151e8aa5fa6e323b16be175d8dfdf853d6919dd66038300766b51b5f9fbfeebd9b0f8d37e72f3b1797d3d5c5f9ab6728ce162c66e1f603cb9b513698a85ddbf762eec5725c094f6f75dbbdc18f8b671d204cc30c438e1692f3ae41496091be29fac3ee0b08ffb07b8eca778f2b1604db955167e2bbaebf04eef6636cce873b9bd60b9002507853754080ac753f9e71e8188f7e430a1351ac24aa04584389518c294a90185761c81d1571330808bbf420e75a9a0f899d8c233b14811c654a2d548100ec8753e689af4c19184011f0a4bd5311500200882f30d495905284b51e8462c1ec15a931e436170bf4913afa1614c0e3554086e3851f538789988f98332723212dcbcd4963ec3b2b14b0421f33cdd25e6933b6e09a1d72d823476385ad7932e63c78a23d19fb7ef0e44fafd96cfee9d1cf5387c74cb8d10f4fa3643e67e1ea0769959eeaf925157af040bb92b73f3dca97a4fbad09b02a010bb9bea0da9dd3d353f9a1fb587b641a66bbd96a354de371dac203ed9cc3783adcb3058fe8de13ed6ae987d751c06c7e68c399898df4984df5e07ada6a2e7ab2654bfb4c9767f2eaf3711b319b8b6ea511f34c5ea191a77aaee5dd2a27892c4d8a745bf1f2f6a1cf9f2bbe7c74d27e55117f7b17a868c7b434a9cbdbda91b715b493499569a790f16fd74ebb99ce111aa067f0b2e0d5d1b86d9fc99973bf31d4b6a44bd1b9ad25795b414b9974ed740c15b2eed4d28b64aafd2cfe57cce1cf6cd2b307ede1e7ef687f2e623dab3e66e6c0ee0d79b737680ffa666b321edabc6bf75b835687b5cc41efb146fad230f2514b7b74e626d7fc1aabe72a8285d99c81d2fa5dce44a4bd7daf2d59a44d39964669319770b3b5abf7a93fadbd95fef44ec1a7be3fc5b78188b0a056bdefc7a7da07ceb52bc7b79339d61bb90c1e06f1601b11c63f33e92316c3dc97d6f3d268599d8ed5eeff813249e0d043ec2cd3b5cc3695b15d3fca8a78d8269c34e63c9cdebe314af5388a660ced75f8c4e1666fc2ccbe3de8b49863b607accfb93d18b06e9b77c6fd216b71d6063e8b2231f53856ad143ebf8e1ad6d5275a2dff820b4d42867c21383977f8a2f47dcb5e0316399edd8ed932069b0eefbbde7f7f7be3da5f3e","1aafbfbc5c5e3c7ff69d0e6fa2a7e80a0eef86bc77fbb81b450f726dcb9adfefcddec25071602b604a3e6b05e7786e6a1554d533ad601dea8c56aa1eee7f562a1fc7e5dc9066c3612dbdcc8fb40fdd7433e330e1371bd336e66c9ecd65978db99b4e5f9aa99d4ebfd71b18c610fbe132caf09fe79528c3e039febcf829fed0f6df91bdba6b43b937ca9136afb324c6ae5a5a60eba169900f89a10d748fcdc977ae7eaf95dfdabeeb63d7dde00efda0b8c3272c71615db34dbbc38bdd516acb480f73e1c27af95e61de9c904dca3aa9d1dcb3c3d8fb6045f846cff0205d6a2da555debf8739b00502bcd5c4ddfbad7bb6908bbbd6d36809ed978ef408652c466a297b068ac3603baeda03398cce5a1d7bcc8de1a4dd326db3cbbbed4ed799b4ecfe70c058b73f31faed21773a069e6bc619260156329a15185870e2accd75bcd91c87ccb3673c6a36e70cbb38ead6098aee2d96aec20708b31d528230688d6669cfe8c37d323617d497ab8bebe1ea0ff3e784fd16cc9c5fdcc5f8cbefcb375fa7edd75f9e7ff7e29a35a5b0baa662dfbdaca6650e5a4f298af9dd61a1b2b2ca0a4a284a4b27011c6fcd9468aa8b25811cba4a529dc39747aa759c75316dff1b0be245259085e9b32bfe42b2d3ac97ae6adb689bed5eab559d5c08b24fb320fb1218d9a284667704eef1fd043ef9a82cf48d083f0aca0814c59cb3e54a46f7aa516434514f7944b5d3e830f5733de553fea8d4483de5532b828992455aef31e5f72e47df9eb41b3e65e339a2b381af4dfc508be155623929c2b52991a7453ca6db91e67b88e5ba08336bbf88f8d7642c6b9e6a328681bf9e1f6ba08b12e6ba2b2d48c6ae886614fef5564b1014fc940c0b42a985b15072bc3330551f8b0422b7f39aaf54a10862ade377c69ac0b78717e623a2e3efe38ef63ab21b586bf89a253479b3723ba4e82ba75b2a7890be7fad8a2531c8ee4bbaf33e94c45e0d649e76bede969efc51d08b0958048a14b559e0aca5e9a74ecadd6e45e41c66ada79f64ffb3a96a97110409e9fa635528b032bac459ebd832907cb8333a828c044c301bb8d8bc1c4360822970e3902befdea4b08453a01e7b5bb8ceb4eb326f9ab0a9b2c4050e7a8c9cfa29fbba97f5de3b6f4b20a012cf1f8a31c54a54fbac0a45f2a6db06e442a80257904a5c494f2b4edcaa9b2375411481aab419cac68c380e328ddf6df423454d760564d2a522fb4651d3d95ab1ab992cd3445ded326125d2d7ff426acf2c8b2811cbac283a64965cf57a8c48e20d98ae354574085f3224aa72a7204063a13d8323a728ed3a87811b3567b14ceb9890b00e3682aecf1c55790b1c60a6bdaa28710a528d04ca38a722aac4a88216115a55e412a80a0f4f5b4c847d9fdc96bd067a036bfd63243c9b9fc0853fc1388c852d30bee1fc53a7a6a16e4555a5207818c4c50934cf915084cd61d67a9ab2e4f0c0f557c788525790c86aa7392d05b969368d41d31c5eb6fa56dbb0cc6f10a02863b42e0dc3ea802795046890d0de688b23ed5c9a06e23756cba422e434a55b247c42fe257ee7b9971447d81dbca1a43ad48da23c6111d767654d6b6f4d30b3deb65938a8e5c5f63a79afda9079e6cf79003706a1687a02f1159f069d6ebfb3e18dd87ee281eae89e3496945349eb7d792bf76000f16fa40d7e90896504cda2513af31b169149e99d20f4bf701bac43e55e69702a3797e25a6c542467abd8c612b823223b01e78c1cb6e2bedc5ae6c2211a371761e867b990192b9d1ae1324513306cec56723629933393bc82e40a9b7b51496e537ee2889a23ca29935b44a398cf03b71a9d5bf271c1a8831ef7911f4b4cb8406e5f9925ea07c2263a8da8038a0e3640382d4424c6c215316579c99dbd0dada67b670b8f56111379be65d7c87eca28ad51ba5d","0180641d6e286d0c6bdc3659b14149641719f1906670ec4dc1806435f1b03b67bb8e426e67b1d75148b86c35f150738d3bcf786cdb8b9a6bcc3db05233ff34aeb1261e52c74771c352130ff939b19a78a0c07b4d3cd4c403889c9a78a8890724a31784464d3c4802b1261eb243d5397f51130f297f51130f35f170bfd711d4c4434d3cecce76ad8987c63f8c7818b9c243821a32a623eee2ecc3ba314bcf40a8bfeb04b40645487660de8b07936f4d912f300190a4ac762029bc3104a8b92f7554e0f2f80b5ad83a26748486360f23c96336b7da395a33b7f34632ad215de7688da08b8b835580cf73c38e807ff8b121e2e270400def8519210ce1db42261680a9fbe9e2cde5fb572f3e5e5ebca71005f2b6463283a438568b97c32089845e340326914554a97a9eb76015652da236737a32e444fe55ef144546f400c402db384e47c793b2b3c163908f29783982c135de1a6c385544bc215e2881b308239b79a3b98f37f9a076764881aacbae446de63842be2104270771bed7c111beecd2c4b7f60ca975a0e92774e00ee55b37ff07d074949615490000"],"rawHeaders":["Server","GitHub.com","Date","Fri, 24 Nov 2023 20:55:37 GMT","Content-Type","application/json; charset=utf-8","Transfer-Encoding","chunked","Cache-Control","private, max-age=60, s-maxage=60","Vary","Accept, Authorization, Cookie, X-GitHub-OTP","ETag","W/\"ee837c9b1f32c05af1d9358ed1d8c37b6c99860add81ac377d4b443594426d10\"","Last-Modified","Mon, 20 Nov 2023 01:45:23 GMT","X-OAuth-Scopes","admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, admin:ssh_signing_key, audit_log, codespace, copilot, delete:packages, delete_repo, gist, notifications, project, repo, user, workflow, write:discussion, write:packages","X-Accepted-OAuth-Scopes","","X-GitHub-Media-Type","github.v3; format=json","x-github-api-version-selected","2022-11-28","X-RateLimit-Limit","5000","X-RateLimit-Remaining","4534","X-RateLimit-Reset","1700860229","X-RateLimit-Used","466","X-RateLimit-Resource","core","Access-Control-Expose-Headers","ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","Access-Control-Allow-Origin","*","Strict-Transport-Security","max-age=31536000; includeSubdomains; preload","X-Frame-Options","deny","X-Content-Type-Options","nosniff","X-XSS-Protection","0","Referrer-Policy","origin-when-cross-origin, strict-origin-when-cross-origin","Content-Security-Policy","default-src 'none'","Vary","Accept-Encoding, Accept, X-Requested-With","Content-Encoding","gzip","X-GitHub-Request-Id","EC83:54C8:2C2151:5D79AA:65610DC8"],"responseIsBinary":false}]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
1 change: 1 addition & 0 deletions test/fixtures/nocks/release-manager-npm-cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"scope":"https://api.github.com:443","method":"GET","path":"/repos/npm/cli/pulls/7009","body":"","status":200,"response":["1f8b0800000000000003ed5bff73dab812ff573c74faa6ed0bb18d8118a6f7a56dd24e6f2ea54d797dd7e63a9c6c04283196cf36d084c9fffe3e2bd9069394d4e17e79334ca74930da8f56f26ab5fb5969599bc541ad5b9ba46994744d9345e2702cd2c9cc3bf4e5d48c792413338ca6a61f08339a0541621e5956a7765013c35ad76edbb6dd6cd856f3a016ca211fd0c3dafbb3c1e5e2b8f7eab5e77ff25be3572fdf7f46fb493a0d06e5ced63a5aef22ef612846a30a1287d41e1d452cf52755e494008d2849667c4370eb7c28816242c2d9d4e371ad4bf373504b5296724c868c7808ec40fa971c93336241c20f6aa94803fad69fc898778d98079c25dcb0adc3c6610bcd6709212d21361621dae999aa333f15324cce3d997ecd5e41d3763b6ec36dacbf80d3e3d3f68769c77a777cd2ecf5c757bde3b73fa1399bb394c59b03540f93eca553bfbe0c531ea6eafd8bd0b45b4edbfd65fe531308e338c350ef99f4dc663c04969865d51fb75e42f9c7ad63086fb70816459bc29019c920900be06e0ea36cb75bbb360b90025084e3dd0101b234653ae198630cfd86264c24e94eaa2a8025263149b1b808128615c77cb88bba1904945d84d073a996b9c29e79891f8b4859d94e3dac030158c663168a6bb633308012e029bfb48b820a00407c0e53df0949232ccd281673e65fd134c6dce7628e77b43bfa0614c0d3ab883cc74b99d20b13291fb0e1949c84722d3707354f0eafd0a01b4bacb4aef1d698b03937fc98c3210d0d56389b671ee7d133e3992765f4eccfb05eafff19d2bfe7439e3211243f3f4f66d3298baf7ed66ee9b9997fa6568f1e19e7faf9d727f9e671873f87138958cccdb96adb3c3c3cd47fb59e1a4f1a56c3a9db76bd613fd5888f8c97b3b1f15a7ce3093d78669cffd56eb48f46ade65ff7753215a999b575fc06f36da7e95b23b7e93823d76bbaed5687399d66c71b61b37a6a9c3f82936e6dc5a4bd8e3622281ac570e2717065f8b4511853b87d2c7503becf20efce6383c7b18c0d191a02ae33262f8d1987d8344a8d27aa2f0cf7d745ccb0b38e598ce13e37f359a691aab9ef4f4462bc3f33162c31c604abded70222c6f959b641bc57fbc49d8a8fa51ce3db482458ceaa593d52bf9e1e1a1f3937ce87d29f4d61ed6a11568378b48908d3cb0c6ac05218dbea55f6ed56d7b2bb4dfb0bdacca2210de2ce368d6ecba2367e2093ac4988393fa84d793cbefd60004bc24b1e241386fe187318f3aca6e3729733ee78476e8bd98ee7e2a1e78ffca1d36e36dda6077c86b7350e39d68c86cf3f27b5eef9575aab7f630f2725633e179cb696cd2f52cea6f470590bd994d6deabdfdfd6fb7808741505b94edb76dbe51df843fbd39b4f13af7fd23cbdfefcad77fc9976e024988d218f58aa4ea07832e485c3d5c0c6bf8c1e8ccaf82867b1cf8d93100100e731195cd665e668140e4d1d5042998a918079e2cd0e129ea6688eefd71f27031e322f50cdb7eeda25576db6ad23f7a861","99a4ae990d1408dbb76e72d22a68543347a1633edc29a718e91e7f7bbf066686b3ccfed04e171e5da432167c77fc7c7bc0ba57cb5d5210462e0163277f16c2e8c99c6e604101f378a0ed83acc1715dd7768fac466bdd207e7fb116131f858b0fececbe17518ebd752f269b61847a79771f372c58ca90de35bad2a6b9febdb1fad69781444059e343faa7ec6ec46601469105a4253bcc4736150196860c8bb5338cd96825a357e43d93fdbd0cc2cca4a18b5e786a81dfbf196fc523f15b80dbc384bbf0724d96a68ee9c9bc1ea2dd5a7ea0869ba947a90102c44a1397cb98dcf56dcb716dabd1761cafed74ac866739a3b6e5780e6f5b4db7e5b746dcb5689172067b441241064aee209a76cb8ebc5ef76216fa139ed4eb013c75423145cc4768fc030db52baea0d066560385d01f2d9acccd945de8c955efb273f5a5f17ac6fe8826c337c1dcbbf8bc78773d764e2f5e903b7d404233cb3dda0e198d567bbb0bd46d2a652be4301f9c99ac8477c9420865a78c8300feb9ec42a1ed9a491048d5ac8164aa670824f5cf6403baff52120173cf22ffde5a2e85a777a500a43bad7c159d389d0ebc45039cc48aa2393d3e59f482df02ff4de79afd7136f7c3cb6fa7d797add3fe8babd38b4b5a5cd98682cd1b1f46d8eb06d913524e3f559108b11cd926a2d2c975da024df70b1c348aa62368e2f60b5c138bab19d92f70ed332a2df07b379f6c8596d30b505346c4fc4b36e6c69485f8151b2324aebf6123fda8781f5aea32be2c56f4d64ca1143a659295821aea8ae2b54b7e55498eda2f4dfccc283144b78871241266795f6e510ef74a824bc4682b1c8af954fa52257cd49922222f292f2b092a0172958a82fe11daa83c902cd2cc779f55d85a1daab0c62251ae320385d052393d9ac53cc4ac0293cb2c4dfd977a1b6c5c694ea93d751f48af921c582c53092d4d44b8d4339e0caaf64e2824530241685d5915922940d29857cb1b941a245440ec947d2cb3190958389ec17f541a4d218429a52071ccaeefe502ca66be920204d52962e151425c498d7539d2440798f7d3121baaacc456208a38af62e7eb9b9f1a12715c1547a3444a66f60018b2934cac807a48ca7b57eaac9d5af64d95d9c9bcda5d985991aae254a9c256622eff8d92df845636a089cea9a25426622e3d70ab3720b49794671398222e2b69a42520ca627f02ceb88a1ecb5c06bbe894a5aa8a3322358608c303c9869534298400a0a7bd8a2e5a629d2e51a5e22a104a601da1209f2ac1aca4d6b1ca44e88f0735664970f90b787f9f1f30b0d4b09a54f80276847c9b665df3735546ac25a026983b42c818974ac3cd6596a62e0e0e7914c8abca34da9a182d0a5d2aca597bdbad5b4775abd56f38dd46bbdb6a7c9fd96ff6ed0ea8ffae7d446da25932b945fe37fab6db6db4ba4e9b9ad0ceaa5f07fec2f103fccc8f1e64692615a6d13049f2923e3effba6ad62d3703231e6eaea2ef63ce377df5eda6e87a22a73cc25e07522c2f74a194921ca2e78b4455c9e96c04e928aed1c86e3b76eba8b4c3f9724684f151bb8173120b2a22d1be577a9aef8de8a4148d4f5832d0ababd64de3198e0ed01394962eb80fae33cfbce9e16a09afb55c884b5192a44dbb2c26127fa628eed5731598e70a3a600fe0dc04d5b8f4cbca0a29da678162c8396491e80a43c1078080ceb4cfc1daae8db5227c1e2698ab2585fe18b22a9a63063386a1977d4ca2e1377da4e45defc5c78f2767fdb7bd7768a62c462bb14e69f8edfe9be0e2cb7f5bd75ffae007510ac54a958b018d461544f4048a6490f269447c67a1fa827b8a7fa60213aa4512074fa8302450682fdaa432123ef1fcb50be44bba6c433ae3dccb05059a9aecc0cf7ac68ee84cab9e655a68914a8942014a067391084f0422a5c147330f13","82af753ad4d5f34d95c5e2cdab59cb0d2737a48cc21fe808194019837b433560ec4d9bd4ef06c15b7cd434ee8f164f69fa370ea7ec599e4d4e63cff26ccec89ee57900cbb3a7711bd6be4e533a90b9e24df7751a7dc6e0ffb94eb3a771f734eefa01eb3d8d4be9549968dbd3b8c82e9198ee69dccddacd8afe4552b2a7718985203bd179ec8a11cee9d32adcd45d94eb9ec6fddebd983d8d5bbe22b4a7712b9eeedbd3b87b1a97ae85ed69dcdae9f19ec655077189c61d0422c4b910f0e4090f702a77599be8d3b955af6b028b72cd3b106e973cd4354f7d0713626ad7bf43ee872f4602238f267680599d9f06dec681f1aab0ab6bac5b501f067a3b6eca868f12fdc310e936691ed25149233fbe5115adfa017232405c27c00dd5014ef5485fa8eb25a833bceabdeb9fbd7df99f7e8fee30d08d83818a808a1b36eafed380aebca28ac212125abfda5354549414958cf2d24ccca970b1fea46832a001500dcec7a526ba519b5d13f25042d1e02b2b43b1e49689589060b89e85ffb89fe9b3703095b8300ce9ac7a9585cd28e0614cc3a1505715f10982435cb8c83ea280e74f70f80575cd11dd8ea8759b37ff0357b6c9c6443d0000"],"rawHeaders":["Server","GitHub.com","Date","Fri, 24 Nov 2023 20:55:38 GMT","Content-Type","application/json; charset=utf-8","Transfer-Encoding","chunked","Cache-Control","private, max-age=60, s-maxage=60","Vary","Accept, Authorization, Cookie, X-GitHub-OTP","ETag","W/\"cec58319cbdabd0d7dffd1c9d36a177123eaa66e8362f8e72bcacf8f6c003f27\"","Last-Modified","Tue, 21 Nov 2023 15:02:50 GMT","X-OAuth-Scopes","admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, admin:ssh_signing_key, audit_log, codespace, copilot, delete:packages, delete_repo, gist, notifications, project, repo, user, workflow, write:discussion, write:packages","X-Accepted-OAuth-Scopes","","X-GitHub-Media-Type","github.v3; format=json","x-github-api-version-selected","2022-11-28","X-RateLimit-Limit","5000","X-RateLimit-Remaining","4533","X-RateLimit-Reset","1700860229","X-RateLimit-Used","467","X-RateLimit-Resource","core","Access-Control-Expose-Headers","ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","Access-Control-Allow-Origin","*","Strict-Transport-Security","max-age=31536000; includeSubdomains; preload","X-Frame-Options","deny","X-Content-Type-Options","nosniff","X-XSS-Protection","0","Referrer-Policy","origin-when-cross-origin, strict-origin-when-cross-origin","Content-Security-Policy","default-src 'none'","Vary","Accept-Encoding, Accept, X-Requested-With","Content-Encoding","gzip","X-GitHub-Request-Id","EC83:54C8:2C2213:5D7B4F:65610DC9"],"responseIsBinary":false}]
1 change: 1 addition & 0 deletions test/fixtures/nocks/release-manager-prerelease.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"scope":"https://api.github.com:443","method":"GET","path":"/repos/npm/cli/pulls/6673","body":"","status":200,"response":["1f8b0800000000000003ed5c6b72dbc811be0a8aae4dd98e49e2fd60d9bb7ec876bc5949bbb2bcdeb5bda106c080840402081e9265aecf90fff99563e43cb940ae90af07004950b22488bb55498ae5b22482d33d3d83ee9eeeaf1b98f7ca2cea8d7ad3a248f3d170c8d27030098b69e90ebc6436cc789ae4c3389d0dbd281ca66514e543d3b4b4de835ee8f7468aaedb96aa2a96fca017273e1fd3c5def707e393b39dfd672f5cef47cf78fbd6b54c8c9f16b368dc9e6c65a2d5299a19fc30083a500c683c264a59e14dbbd009025a519e977c8df0cafd10048b0d89cb99cbb3de88f6e7412f2f58c1b1195e94e4dc07f728f14ef0c7286051ce1ff48ab088c4f7d324e32329e3116739971479807ffd34e3031944654e1ce7209e843146573bd6675e112671fede4d8a5fea5ba12bb663abb6ba7a23767776cd1f668ebcb7f35cdf3f9c9cefefbc7a84e1ec94152c5b5fa8b898d7379fe6f592b8e07121f4208c878aa199f637a78f74709864350f71bf49ceab948898e5c3b6e85f194f21fc57c60e88afd60c96a6ebc4a00992284acec0777d196dfdbd72eae182c98261184f36670826f361524c39f6184bff4c1b16e6c546a20a06736c625ec0c88825142ccbb8bf89b8350b087b1643ceb93077c1bb74732f0b53a1651bcdb0ca088c936cc2e2f013db983118e5e027fcd326020a0660c44fa1ea1b71aa38cc8769169e32ef9cb631e31e0f4f718f36e7bec60acc8bf394fcc7d3a4a01b16167cccfc193909e1603e3fe8b9897f8e01a32c81a58da457d2949d72c9cb381c932fb185cbb9ef729ede97eebb4992deff10f7fbfd0f31fd7be8f3828551fef5c3bc9ccd5876fef5aa737a386caed2d83b77a4f7abdffe72b739502ef1f1702829cbf8f0d419d8036530189cae92de93eeaab2aaf565abaf9af72ae677a47ffdfd1ffffee7dfa4a707cf9ffcf9d5de4be9d99f9eecbd7cfe9abebe2fc1d4a40f3d2fecc76cc63ff42478ae209c60bdb984b5c558e92cc16da0a13867a43891a2249ef04c2a6833726906470ea3952a9dcf25d031294fb917b248f2c82d87b17496642779ca3c2ecd70d20d24e90dae1f8501f9ea1cca73241589144e62b8f3758683e5cc67525ea6699261563a30a5a3bf28d8036b204bbffe2a7dfd489507c6403e6a56fd02e295605f2df3fd916b7a81a26847d76def2c2c86f5d8c0501cc7d73c550b4c4df55dae19be2dabb2a1189ee999aa764f7a7f0747967e254f3af9e958d6ef49392f241ee338e2d8a5d897b0fce6f00a924c6cb0224b77054fdccbc75179c24fe0aacef3c5cd7c5a4ea417e1c7e5bab8ec6bdcf76fb4ae7aace3c876606bb2c65cd77174c5f57d3530b9c398a6b89ee35bd5ba74e526ebd2957bb5925ca647771f9f650c31d1846558c27de9fd91ac2976a0eb3792b71eab06dcf4145bb50c53b698e11b96ee69b616c8b6a1db3a37f92de50d6769147a61212d3511da3d093d29c89299949571bf52eba502e7b82bab2b22f3bd23ed245e39831e","0bdfdc281c3734cd66c18d165a8fd53cac4e619c59b6a663976c0741a2c279a0b94a6069069442289c7a931b63a98b1b1385f1099998cfa1719ef06147a46cd0c77a8547307c382ae8a4d03e90de7dcce942c9a37ec0331f7f2e747087a73cf679ec850b357cdb58f89592d5ca9e0f0b3619569ea67f0a031ec8f746d2d1638844a172f5c563711de60ca5b905f72874c12e2ddd28cca7fd53bb99a475fdb1b88c391e0e1b877db9f7ae441a4942a68bee5b5cbe72e58ddf6e166d0e54e1bedb9bf0bbba6ff2f45396f97d0fced387b1c2c74f9f449324837dcee0fa4f5954c25d93776d9cbccbc9b7a72c471c8ea3224bcac9540a2256ec57f14d73829431a235e259ccd2ab0f910b52cc7891855edecf380568d9f997c9711454e6f63fef037dd7519865ddc835d463654d0d745945ce68fb86e9fa5c761c577535cb340238421f164baee1163e7b5d0d84ef5bb9c76b1e8f7c3862334365373b4bebb1becf64c755fcc03165c6655df50d0dee3c506c5fd74ddf086e2d7f4be52e9e37709ca6c5d88df6ba1eabc926d79861c85cf5206ce02a2693354be686e3eabac7eb73bff35e57aabed0f426d66acb7c9d2b6a39b091243cd8458f242edfc823ad394a8b822838a64bfde7efea9f5a334a676114ad382256147c9616145bc223455c626591cc70de7a083ee035a4249674d9c1873c45de85f8ca2f33e1bbaa13e0ffc777c8cc52b8ebde489febb19aaf702bf0986cf99ac399a973235098e7ab2ac70fc3f0bada5e5e2469b5efb4c5f5d6d3518670aa58f3176d7d1619d2e11487ccf707d21992850947fa2c2292331c43d2fb831accf95e603a976af02449a00140977224dd62583f15bfee0da4d79c4beffdd570ac1b8b3beb1c9120d669df9815480917f99571a89823591e69da3b8c29539f16b136c63a94d591ec8c0c93c65470d6da10f35055478a33d26c1a32e3d9e402974b868c295a0b8b713e6590c9a0a8ddf16d0b37d6b2641c099a6d9b76e0e816fecb8aef5ab2acb9048ae124479ec591fdc6484b969ff3dee8fd2f9475ff15a81c2d24e3a721279068fd8b82b3195d9cf72863c4eccfbe7bd53fc4457017b8a6ad998a6db6b1b41fcc1f5ffe38750f9febbb9f7efeb8bff333616979544e08c58bc23e31c5159f2fa0938ab1f407691fb1a6f43a2933648fcf45eec48561d753d69081e053a38571528441883017c1f818216e81e1f87ef5723ee6317323012e5e89bfb5409721d20f60b6f290c41dd60b0587ab4138825b040c2c768ec0e066b9334ea8e735c8c9f5120c6b3ef3fa8f0a3e0136131688edf8e6fc1ba027e59948f713825329adc5da098d886118a44e9fa14111737954e9076983865453712cc504acbb44b9bf7bb282725b891de5d97537a28da657b30ce910a80d76f4952a23ab80f58053ad99ab5f4b8b2fbd244a000cf738a26031dae7012b23aca186975b5ad8ac6b1646308c245e588e9fb1604953d9e3355bfda58a00321eb2e60a9423b313e67d3da876253f22c7e22a3b6e185e0df75dc6af91643eac307a52aedb48b782f78be5d6e211d48fd4a1d3c6353480d55dcdd17cc7d67ccbf002e4c71eb374dd725dae2b96c27ccfb798692964a29c411b510c20f5246790ce466d57dfefbb198bbd29cffb7d2438b8d962f7020cbec1c0ca11771068bd3a0181301f994ced64da0ef4f9f9fe8973fe4e7d51b29fd2a9ff323a758f7f3edbfb34d1768f9fdcb2305136fe6c83ca4425f6d50eb01ad3a9ea40eef2d6158625f126d504e2b251e58018fc765502c16dd38a0031e98afe134d77a49fa87e1b54bf9abf550c80a1d408fefe4a4d04572f83f24976b27c119b688ea3c8aaeaac19d7d97ef46de4bd743eb19f0e4ebdf8e4e3eea71363f7f0c9f9eef10919577d9ee0e8c6870027ddb8be42c2555745","1c4255cbfa101165a1d5f223866e0d1ce5d0aaac481bb735f0aa5160b9235b03af7c462703bff6f0a92db49d5c100e898ad4099ba0e4c462fcca24aac17c8beafc6b51bf255307aabdb0e82bf38456e85453760a6a682a8ad74ef879273a1a3f1fe2675dda46748b1827414a9d5c9759b4c3bd16e11c31da920fc57c2279e9123e56792222af2439e9442808c8558a96929b947fdb0ba923cde6f45986addd592db4b1499b3b2d644134174e8f76b10931bb6c6443331f567f89bbc1269d44a1f1347d94b89de850811e0aa2f910112ecd8c2be3aeb31317a269314168dd5914a259304101ba5bde20c420a2058b8db28f79bd23118b2725fc47a7d52c88b0a514244ed8a76b9180b69a2fa9c082fa8db2d0a574b89318ab74244915605e0f4aac89b2245b321105a22e7abe7af88925899cb80b879aa4a566b760437ab2ceea3629ef65a973e5d4ea6fbaacadf66a97f1a49bd8152c686886f33fa2856f5a67f504e674110ad2886e94b90bf4f533aa0673cab3899900323bddbc8a02a42cf3a6e8fce922c7bca1c1298aa280e8c60a480c1f61789430bf93240b2230a8b6bd8b2c15c52a5c225a3fbbb01004ab1c16e05327364baa555e6d18f4e641cdb04538ff0675188f3f600091a102a8c384d023e4dbb4eb153ad765c51505c404a44c1c9a46812e3c1a9af9b06af243b343949c7786d156c8c80b552d5f0d68afd8d45b251b87aa3652cd91a15e8efd2b4a5fd50f81eaebca48b1684c5ae6d316b04f43d443c51ea9c64813e5013a59abdb81bfd04e7cb1fb8b1a4c3130cf9b165d7c7ebc1c36aab3d17a18f0f078dd8abeccf374dd575f1c8aa9a7c98ca738eb008a354d6a28b6e403cc7c9c8b6e576ae02019c34f1804d85731acd609e72525c1c596a9ea0f7a67d43d4ce75eeb6a7336629256348e42f5b8b2aedea8c84a3402d395344b8eb907acb3c9bce9e2d28457469e8527618b920eed3659987ba500b897d74560de08a8013d80730bb32ca99b80eb324ae5b3962dca7e9857f585051e8032462d7dc3ccb415d84ae871d42b092940108f258be657ec608d30ecd71ff3d4ff58b588efed3f79fdfaf9c1e1abfd3d0c131a5309b1c4d777773cf3f06574fceeadf1e9dd21f041b434c25293b331ad469443aa0d0cf331d55509ef5c6cc4197717e525eac943233995854234cc2e3bb093143d1ba802f58e912f55451b92199d25c714685660077ef66b74a4cab4fa75a685114592a04c8082c16998876e1885052d5e546b3d7c5da543a36abf93e5de011ea55d6b14a751a41ac21f57113218d508ee67eae5c4d9b40efdae01bc8b8f158c2ba335c1f234dfb53d8e122a737c6e29aaec5ab61b30d336d1ccad68aa69d3f6af35996f519e754c638bf2acefc816e5b905cab3857155795ba7693d60b5c44db7759aaac3e07fb94eb38571b730eeea03935b1897d2a936d0b68571915d2231ddc2b8ebb59b25fc4bc9f716c6adf5640be3dee4d9f42d8c7bd1d56e61dc2d8cbb8571b730ee16c62530f7bf00c61dd343ac809ce7bd9c47e8ca9df7a655776ed7d7af0012a65cf3120e174b1ee2b52dd53b554026ce844be86efca213f0680ab81bb059f64f835fbbbf5beccfedf6e50aaeb7637ab1545d2f9f3add371393388059d3bed1955bf70672aa23e06902bc6f668cae9e042f37a0ee06d4199eedef1d1ebc7afae670ff0081373d70301685ecc5f33578db0c6ad9f4021b5451584e4455b5a67ad4a729c4884f54316abece38d52d56af2c868c497e2ac195f1498c023726ae9f1b72514359bef066f9f6008ca0f657435715d96ef7bee2c99c9ff622eff88dbc7bfcfc6cffc9a347b48eee6fb9298715f70d30ca96bc57c341ada19d3acb9794b7ef3fbdc063933ef315661bc1982b7c7ebbaef355a69b369faff0eada83be42da1d","e25c21fe6d3ad25bd27ca131fd0dd505db0de954716d1d00a8245ff0de7819d88c857893049e73cbc61e8bc778634a18c0aeebbef23aa3456d1d66eafba1781b10aaa26869f7f13064fd514171dd9ba2310d3d07013db9d41b399fff03b7c7a14db04c0000"],"rawHeaders":["Server","GitHub.com","Date","Fri, 24 Nov 2023 20:55:38 GMT","Content-Type","application/json; charset=utf-8","Transfer-Encoding","chunked","Cache-Control","private, max-age=60, s-maxage=60","Vary","Accept, Authorization, Cookie, X-GitHub-OTP","ETag","W/\"9fa11508a7cdca6ca9096203916d4af9cd8d113b525480761c31036ceaecb900\"","Last-Modified","Thu, 27 Jul 2023 02:09:56 GMT","X-OAuth-Scopes","admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, admin:ssh_signing_key, audit_log, codespace, copilot, delete:packages, delete_repo, gist, notifications, project, repo, user, workflow, write:discussion, write:packages","X-Accepted-OAuth-Scopes","","X-GitHub-Media-Type","github.v3; format=json","x-github-api-version-selected","2022-11-28","X-RateLimit-Limit","5000","X-RateLimit-Remaining","4532","X-RateLimit-Reset","1700860229","X-RateLimit-Used","468","X-RateLimit-Resource","core","Access-Control-Expose-Headers","ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","Access-Control-Allow-Origin","*","Strict-Transport-Security","max-age=31536000; includeSubdomains; preload","X-Frame-Options","deny","X-Content-Type-Options","nosniff","X-XSS-Protection","0","Referrer-Policy","origin-when-cross-origin, strict-origin-when-cross-origin","Content-Security-Policy","default-src 'none'","Vary","Accept-Encoding, Accept, X-Requested-With","Content-Encoding","gzip","X-GitHub-Request-Id","EC83:54C8:2C229E:5D7C7E:65610DCA"],"responseIsBinary":false}]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"scope":"https://api.github.com:443","method":"GET","path":"/repos/npm/node-semver/pulls/586","body":"","status":200,"response":["1f8b0800000000000003ed5cfb6fdb4612fe57080501e2d4329f7a1108dab84e83e4ea387594bb5cdc425d922b891645f2f890620bfedfef9be543a4acd816951fee00a16d22513bdfce0ef731f3cd4e57ad34f25a666b9a24616cca320bdd93899b4c53ebc40ee672c4c32096fd702efb81c3db319f2f782487a9e7c572a7df6d1db75ca765aa8666f40c65d0e91db7a8dd881eb63e5e8e66cbb38bd7a79fd4eb7ee77377f0de41fb6932f746f53e2bfd6deb29efc871c7e3dd054f480cdd862cb1a70dc4851c0d338e53be21ff245b09c1c2587e3ab778d43261bae3569cb084c34eb617c49c4ce305f60c1fcc31f3627edc4adcc413bf4f83889b52c43dce622ef54e3a27065aa73121ad2035717d34cbacd86676e2067e7c6505c95ff9eb31d4fea0aff5b5eacb393f3beffe311f281fcede1817c3c9cdc5d9bb5768ce162c61d1e638c5c3389f17d4af1df809f71331455c5f563b7ab7fff3e215a93589720c310748cf87e61781c5725df5e79d5328ffbc7306e187670b0bc34d61c88c03cf0b96c0dd1c467d6a3fd8b55c829480ae3fd91f10202b3948a61c36c6d0efc8606e9ceca5aa0058c1887182854790985951c49d7dd4cd21a0ecd2879e2bb11308ecd48aedc80dc52cdbab872a10808368c27cf796ed0d0ca0187862ebda4741010020bec054df0b294358c961e42e987d43668cb8cddd05ded1fee81b50004f6e42da384e83845e989bf01173e6b449889de5eeb86505ce0d1a98518095664aefa4295b70c98e3876244762e55ef3d2e23c7c29bdb482207cf9a7df6eb7fff4e99f67cfa42bb10ffdf5a238381ed8c4b14b842ce2f28244f4939313f1c138925e688aa6b7951efe3dca609f49a7e944facdfdc6637af052bafadbb6bb63876b7f3fb5abb99bc8b98ca31bd6a0635b5d457154addb3506ce40eb0e14d5723436e829fa9174f5acd3ef3f099a0e3d1c45fd232989dcb9c4993d9522e64fb814f344b2f818bbb48471c658e3d28b5fbc74c667588637318646e3180c1c6cc2bd9dc691cb74d42e53a1b23556b47e57d175ae71adcb6dd6b5ba035b198cb371e8bb8c0363b703ec1276e2dd08b5b9e405d0dc4a5dcf915c2796582c2d98e73ad20bd808cd37c6944f87e1d48da58f97d212cd271c7b8598424bb810d2d5657e647d1427d756ed264130c1afa11b638711cddaa1f8ebe844fac4b974e504763ac70214fbc26e10cf3611b11af2393e6209e6ff7afa0d55c354faa6a27c459b34746810f7da68aaa92aa6a1519becd0ded644e999fa809acc7934d98e526f32c2fac0a41dc553069df4aeede8bad153fadc56c7fd0eeba986d1e96adad852fb5dc3b239e7cad8e0e880c5b13bf13996ba8fb9b9fe1eb7ccabbf688bf90f3c0f1a48c4172ea71371f38784b379fed06316f7e8f34ab87486a20efaea007f54bd86df5f575cbade62fef51f6db2d74327fc760f32eb4d6669825523deb7f95c53123681c580e8b3396d60d59fa5f2473bf002783e2da3736a9c910beaf0","314b3dbcd0dc7172787942910b72075bcc5d0fa608fcd2564ec4c66b89ec0d3cb2c76f1f49e90bcb390814caec2ddeebe347c7536009e51eeec367db43b085622b397348e940da47d9b58f2b8c906b4bfe2d9c9c46562d646555733a1c73d1e83b3d8d5b8aa30c1c5b57785fd73b035bed295d4de920f4c008a69c61e2c221a6998c178f58c2acef00edb6851ddb9ef2b8dd9e331c88f4aac668fa68b36c6deea0cca6770e65d01badacaed2ebf734a5ee8cbfb9b9980d6ebe6abfa5ec4b3875de7a0bebfadfcb0fb713fdfcfa7543c73c95f3aef6f0cc33b51ff6c2b3363b79dd14e635f6b0d7c2fb78d384b297e74c003fce4b1668fb7ac404b2abf74b32bb7bba24f563bcdaacff9a338c85927bb0179598004fb7b9b2a43bad7ac143e89d9e3a186c2cade585f7deb3df0e6ed997cb85edcfbe9ddf7ed6cf87b30e10f383a6426ee0e118a7e9a8f8a54e7de057e1c453e09e9f362240aa06e218d161a98318c8026c7abf87a59eb1696b8b1c967ab67becb4d41f3d86aa242590eb7ee0704a911af19759b8134988d8245af9d20b1032129c430993552297e5887681209a958b7c7717374768e4fd50d7e4efcdf84d2379925bc9f833e783e031c3290a109a058fb162dbddc61ac00a4ede1a8f7c471146347147b3f803ae5b10cc1a010841da6d053dfb142e65fb00730fb638d2d66e7173c8728217615aa30196c22bb19f92b50b1fb689c10bd9959c7d126f8f4d1aa94672a48e17588de4c118c9427825c3bd264df064d4541b4223d91a18bcfbc6aa916c099644bc592023d422e112ea878445abdc621e08a8944d9ae9560ac3f4e4c94ed8eda3acf9f6e5b3960614250722d7a2d0be91f1abf2a459e615236fd208ad22be0613ec7593f5533db7c55089b969a4572e5a9bae7bc0d13cdb84dc27a4df4611649b6cfe4b13ebe5bbec36ec3ca3d4d094221b15cbab9f90ae9be66c0651ce4d94847682ad5e5960a5eec056af8858205041e835d2309304048bec2968ff267aad0a59780773968894cc98d47210817801731a69560a03287b3d4d74cb24ab7492a0c59a4009c12a5249dd35825b4b5731fd2071c7aefd945cd3f60daf06b0fa199cbfcd8f1948584c9dc4b55dcc439013f47632a6b389253249a80d3294907292aa91190ad9959c65081d1e7ac14d6376b2224ebb6096372ac870556d2b5a5bd5868a622a5d53d1b773ea68a6e94355378dbea90ac23c4ce3698d30a726ca10b4bbae99baa0e6c943c87c717cc27585ef679d285b0d81382eb2fef8fecbbab949716925eccf9b83d6f73757e7e37d2c36cf92ef8b40a56930e721ce6ce2a689d3b8c52755e98311ac1cc27690fae0a78dbe8e9b024bba8440e75fed69717c03e73d72ee9f445696f0593cca1664cb4ca2143709e8491805d748f680e15f3f5b2ffecac3a53b73cbb84748927f517be2b8b19d22f9800c70f95cc42c85821d5d27ce3d8a82fc2e419ea0c876bff515070031cbabdc790842eee7ca97830594e7dadc8f61a715c544183014c048738ee6dda75fa5dff3163062e87ccb2ea0e0391a3d18bae5c0b19c03969757cecfeceef0ad77fdf55f9ddbafc337cb16b2a658dfc1724403c5da2eece8c6a384cf43af4a092db955267590a10970718592312e72f2ebdb1d41e8da946f219eba6d7b6e0b698a851bbb96ebb9098d314c2da8871164e1a0298c5a311066071e1473a3982b79326494f9f7801144f71d257d719e6df2e335163cff9271dd4f4da19285376ea21c08b04dbae740806d5ae440803520c00e5c37d1ca87b4d6d6ab9a6b72f990d6caaeacfe3fa7b50e5cf713ef221fb86ef8783bddde3e70ddbbdf573a70dd4f280b40d47be0ba775b8b07aefbc1f07c3bf946f3ecc0751fb8ee5d0fbe03d7dd883b3e70dd05550eaeebc0751fb8ee8c903d70","dda24af17f93eb1e79ae8f8b41c817c4dcc30def556b9addf4de29525ad7b9823ea7887c0bd0f7533da24c5614af425a045d5bc477ad210554913edf1f6d7d5b1fb01b450b4dd1ebf510a214e01ef87ed8f7af0fe446c14d8cfd90f1aeca2a0e601637759a82ee5ec440691a14bda0e077848b5f81ed8a0c35fc9c5f2f3e0c2fdf9d7e1e5e5ce210a2ba9891b8535096fea0ea17d70aa88218a9261693509673cbaa908a6c95f84629b7e2e788535aa8faa46c3222fd294b99fa331f770cd0715ed264213db52e3c5e57baa1055dc3ee181af299f55bd87f74fff9e583675f7f56ceafdf2c2f5ebf7a45e3d8bdda389533f43d6a1a6afa3eccb3d59aee54e1b0966c7efbf91ec63ef50e15b0bdf8e10ace8fab7ea882ee5b0451c1dab516a222ba3b775c11fe319511356dbe5320f19952aff5c2084aefd74e09d4fddfdbdb71d980b2bd09fe4329bacdfcd13cc0ff2901eb3aaf6ac8e36bdc4cc032751c575465b74cd45638a8d7cbbf01d99e5209ac331a53815dcbd4effe0b7c784f9651420000"],"rawHeaders":["Server","GitHub.com","Date","Fri, 24 Nov 2023 20:55:39 GMT","Content-Type","application/json; charset=utf-8","Transfer-Encoding","chunked","Cache-Control","private, max-age=60, s-maxage=60","Vary","Accept, Authorization, Cookie, X-GitHub-OTP","ETag","W/\"6e68c268cc778919dc2780a3f89205ab48ea9f27740e07b67e625dfac7196b17\"","Last-Modified","Fri, 07 Jul 2023 21:10:42 GMT","X-OAuth-Scopes","admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, admin:ssh_signing_key, audit_log, codespace, copilot, delete:packages, delete_repo, gist, notifications, project, repo, user, workflow, write:discussion, write:packages","X-Accepted-OAuth-Scopes","","X-GitHub-Media-Type","github.v3; format=json","x-github-api-version-selected","2022-11-28","X-RateLimit-Limit","5000","X-RateLimit-Remaining","4531","X-RateLimit-Reset","1700860229","X-RateLimit-Used","469","X-RateLimit-Resource","core","Access-Control-Expose-Headers","ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","Access-Control-Allow-Origin","*","Strict-Transport-Security","max-age=31536000; includeSubdomains; preload","X-Frame-Options","deny","X-Content-Type-Options","nosniff","X-XSS-Protection","0","Referrer-Policy","origin-when-cross-origin, strict-origin-when-cross-origin","Content-Security-Policy","default-src 'none'","Vary","Accept-Encoding, Accept, X-Requested-With","Content-Encoding","gzip","X-GitHub-Request-Id","EC83:54C8:2C2329:5D7D9D:65610DCA"],"responseIsBinary":false}]
1 change: 1 addition & 0 deletions test/fixtures/nocks/release-manager-repo-doesnt-exist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"scope":"https://api.github.com:443","method":"GET","path":"/repos/npm/cliiiiiiiii/pulls/7009","body":"","status":404,"response":["1f8b080000000000000325c9310e80200c40d1ab98ba8aec1cc0d12b18d4064d80226d27e3ddc5b8fce1bf1b1232fb80e06026e926d2bcc3003b6d9a308b9793f2a235363f440a3b6b9bf1184e39741d374ab6228b2d1a23ffed038af1e63ba6e2a58de179015b6b668569000000"],"rawHeaders":["Server","GitHub.com","Date","Fri, 24 Nov 2023 20:55:39 GMT","Content-Type","application/json; charset=utf-8","Transfer-Encoding","chunked","X-OAuth-Scopes","admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, admin:ssh_signing_key, audit_log, codespace, copilot, delete:packages, delete_repo, gist, notifications, project, repo, user, workflow, write:discussion, write:packages","X-Accepted-OAuth-Scopes","repo","X-GitHub-Media-Type","github.v3; format=json","x-github-api-version-selected","2022-11-28","X-RateLimit-Limit","5000","X-RateLimit-Remaining","4529","X-RateLimit-Reset","1700860229","X-RateLimit-Used","471","X-RateLimit-Resource","core","Access-Control-Expose-Headers","ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","Access-Control-Allow-Origin","*","Strict-Transport-Security","max-age=31536000; includeSubdomains; preload","X-Frame-Options","deny","X-Content-Type-Options","nosniff","X-XSS-Protection","0","Referrer-Policy","origin-when-cross-origin, strict-origin-when-cross-origin","Content-Security-Policy","default-src 'none'","Vary","Accept-Encoding, Accept, X-Requested-With","Content-Encoding","gzip","X-GitHub-Request-Id","EC83:54C8:2C2422:5D7FAB:65610DCB"],"responseIsBinary":false}]
Loading

0 comments on commit 2daff23

Please sign in to comment.