Skip to content

Commit

Permalink
chore: correct publish script tag/version logic
Browse files Browse the repository at this point in the history
The publish script will now never attempt to publish a package that already exists.
The tag to publish to is now only figured out after determining if the package already exists on the registry instead of before.
This also added a confirmation prompt with a full table of name/version/tag of everything that will be published
  • Loading branch information
lukekarrys committed Oct 9, 2023
1 parent 97d6771 commit ed8c553
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 51 deletions.
108 changes: 65 additions & 43 deletions scripts/publish.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,74 @@
const semver = require('semver')
const log = require('proc-log')
const pacote = require('pacote')
const read = require('read')
const Table = require('cli-table3')
const { run, git, npm, pkg: cli, spawn } = require('./util.js')

const resetdeps = () => npm('run', 'resetdeps')

const op = () => spawn('op', 'item', 'get', 'npm', '--otp', { out: true, ok: true })

const getVersion = async (s) => {
const mani = await pacote.manifest(s, { preferOnline: true }).catch(() => null)
return mani?.version
}
const getLatestMajor = async (s) => {
const pack = await pacote.packument(s, { preferOnline: true }).catch(() => null)
return pack?.['dist-tags']?.latest ? semver.major(pack['dist-tags'].latest) : 0
}
const getWorkspaceTag = async ({ name, version }) => {
const { prerelease, major } = semver.parse(version)
if (prerelease.length) {
return 'prerelease'
}

const TAG = {
cli: ({ version }) => `next-${semver.major(version)}`,
workspace: async ({ name, version }) => {
const { prerelease, major } = semver.parse(version)
if (prerelease.length) {
return 'prerelease'
}
if (major >= await getLatestMajor(name)) {
return 'latest'
}
return 'backport'
},
}
const pack = await pacote.packument(name, { preferOnline: true }).catch(() => null)

if (!pack) {
// This might never happen but if we were to create a new workspace that has never
// been published before it should be set to latest right away.
return 'latest'
}

const needsPublish = async ({ private, name, version }, { force, getTag }) => {
if (private) {
return
if (major >= semver.major(pack['dist-tags'].latest)) {
// if the major version we are publishing is greater than the major version
// of the latest dist-tag, then this should be latest too
return 'latest'
}

const tag = await getTag({ name, version })
if (force || version !== await getVersion(`${name}@${tag}`)) {
return tag
// Anything else is a backport
return 'backport'
}

const versionNotExists = async ({ name, version }) => {
const spec = `${name}@${version}`
let exists
try {
await pacote.manifest(spec, { preferOnline: true })
exists = true // if it exists, no publish needed
} catch {
exists = false // otherwise its needs publishing
}
log.info(`${spec} exists=${exists}`)
return !exists
}

const getPublishes = async (opts) => {
const publish = []
const getPublishes = async ({ force }) => {
const publishPackages = []

for (const { pkg } of await cli.mapWorkspaces({ public: true })) {
if (force || await versionNotExists(pkg)) {
publishPackages.push({
workspace: true,
name: pkg.name,
version: pkg.version,
tag: await getWorkspaceTag(pkg),
})
}
}

for (const { name, pkg } of await cli.mapWorkspaces()) {
publish.push({
workspace: name,
tag: await needsPublish(pkg, { ...opts, getTag: TAG.workspace }),
if (force || await versionNotExists(cli)) {
publishPackages.push({
name: cli.name,
version: cli.version,
tag: `next-${semver.major(cli.version)}`,
})
}

publish.push({
tag: await needsPublish(cli, { ...opts, getTag: TAG.cli }),
})

return publish.filter(p => p.tag)
return publishPackages
}

const main = async (opts) => {
Expand All @@ -64,12 +77,21 @@ const main = async (opts) => {

if (!publishes.length) {
throw new Error(
'Nothing to publish, exiting. ' +
'Nothing to publish, exiting.\n' +
'All packages to publish should have their version bumped before running this script.'
)
}

log.info('publish', '\n' + publishes.map(JSON.stringify).join('\n'))
const table = new Table({ head: ['name', 'version', 'tag'] })
for (const publish of publishes) {
table.push([publish.name, publish.version, publish.tag])
}

const prompt = `Ready to publish the following packages:\n${table.toString()}\nOk to proceed? `
const confirm = await read({ prompt, default: 'y' })
if (confirm.trim().toLowerCase().charAt(0) !== 'y') {
throw new Error('Aborted')
}

await git('clean', '-fd')
await resetdeps()
Expand All @@ -87,8 +109,8 @@ const main = async (opts) => {
await npm('install', '-w', 'docs', '--ignore-scripts', '--no-audit', '--no-fund')
await git.dirty()

for (const p of publishes) {
const workspace = p.workspace && `--workspace=${p.workspace}`
for (const publish of publishes) {
const workspace = publish.workspace && `--workspace=${publish.name}`
if (packOnly) {
await npm(
'pack',
Expand All @@ -99,7 +121,7 @@ const main = async (opts) => {
await npm(
'publish',
workspace,
`--tag=${p.tag}`,
`--tag=${publish.tag}`,
opts.dryRun && '--dry-run',
opts.otp && `--otp=${opts.otp === 'op' ? await op() : opts.otp}`
)
Expand Down
10 changes: 4 additions & 6 deletions scripts/resetdeps.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ const { CWD, run, pkg, fs, git, npm } = require('./util.js')

const cleanup = async () => {
await git('checkout', 'node_modules/')
for (const { name, path, pkg: wsPkg } of await pkg.mapWorkspaces()) {
if (!wsPkg.private) {
// add symlinks similar to how arborist does for our production
// workspaces, so they are in place before the initial install.
await symlink(path, join(CWD, 'node_modules', name), 'junction')
}
for (const { name, path } of await pkg.mapWorkspaces({ public: true })) {
// add symlinks similar to how arborist does for our production
// workspaces, so they are in place before the initial install.
await symlink(path, join(CWD, 'node_modules', name), 'junction')
}
}

Expand Down
10 changes: 8 additions & 2 deletions scripts/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ const EOL = '\n'
const CWD = resolve(__dirname, '..')

const pkg = require(join(CWD, 'package.json'))
pkg.mapWorkspaces = async () => {
pkg.mapWorkspaces = async ({ public = false } = {}) => {
const ws = []
for (const [name, path] of await mapWorkspaces({ pkg })) {
ws.push({ name, path, pkg: require(join(path, 'package.json')) })
const pkgJson = require(join(path, 'package.json'))

if (public && pkgJson.private) {
continue
}

ws.push({ name, path, pkg: pkgJson })
}
return ws
}
Expand Down

0 comments on commit ed8c553

Please sign in to comment.