Skip to content

fix: run postpack after tarball is written #4389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 2 additions & 15 deletions lib/commands/pack.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
const util = require('util')
const pacote = require('pacote')
const libpack = require('libnpmpack')
const npa = require('npm-package-arg')
const path = require('path')
const log = require('../utils/log-shim')
const { getContents, logTar } = require('../utils/tar.js')
const writeFile = util.promisify(require('fs').writeFile)
const BaseCommand = require('../base-command.js')

class Pack extends BaseCommand {
Expand All @@ -28,7 +25,6 @@ class Pack extends BaseCommand {
}

const unicode = this.npm.config.get('unicode')
const dryRun = this.npm.config.get('dry-run')
const json = this.npm.config.get('json')

// Get the manifests and filenames first so we can bail early on manifest
Expand All @@ -40,24 +36,15 @@ class Pack extends BaseCommand {
if (!manifest._id) {
throw new Error('Invalid package, must have name and version')
}

const filename = `${manifest.name}-${manifest.version}.tgz`
.replace(/^@/, '').replace(/\//, '-')
manifests.push({ arg, filename, manifest })
manifests.push({ arg, manifest })
}

// Load tarball names up for printing afterward to isolate from the
// noise generated during packing
const tarballs = []
for (const { arg, filename, manifest } of manifests) {
for (const { arg, manifest } of manifests) {
const tarballData = await libpack(arg, this.npm.flatOptions)
const pkgContents = await getContents(manifest, tarballData)
const tarballFilename = path.resolve(this.npm.config.get('pack-destination'), filename)

if (!dryRun) {
await writeFile(tarballFilename, tarballData)
}

tarballs.push(pkgContents)
}

Expand Down
3 changes: 2 additions & 1 deletion lib/commands/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ class Publish extends BaseCommand {
})
}

const tarballData = await pack(spec, opts)
// we pass dryRun: true to libnpmpack so it doesn't write the file to disk
const tarballData = await pack(spec, { ...opts, dryRun: true })
const pkgContents = await getContents(manifest, tarballData)

// The purpose of re-reading the manifest is in case it changed,
Expand Down
1 change: 1 addition & 0 deletions lib/utils/config/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,7 @@ define('pack-destination', {
description: `
Directory in which \`npm pack\` will save tarballs.
`,
flatten,
})

define('parseable', {
Expand Down
11 changes: 11 additions & 0 deletions workspaces/libnpmpack/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
const pacote = require('pacote')
const npa = require('npm-package-arg')
const runScript = require('@npmcli/run-script')
const path = require('path')
const util = require('util')
const writeFile = util.promisify(require('fs').writeFile)

module.exports = pack
async function pack (spec = 'file:.', opts = {}) {
Expand Down Expand Up @@ -33,6 +36,14 @@ async function pack (spec = 'file:.', opts = {}) {
integrity: manifest._integrity,
})

// check for explicit `false` so the default behavior is to skip writing to disk
if (opts.dryRun === false) {
const filename = `${manifest.name}-${manifest.version}.tgz`
.replace(/^@/, '').replace(/\//, '-')
const destination = path.resolve(opts.packDestination, filename)
await writeFile(destination, tarball)
}

if (spec.type === 'directory') {
// postpack
await runScript({
Expand Down
39 changes: 39 additions & 0 deletions workspaces/libnpmpack/test/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict'

const t = require('tap')
const fs = require('fs')
const path = require('path')
const pack = require('../lib/index.js')
const tnock = require('./fixtures/tnock.js')

Expand Down Expand Up @@ -29,6 +31,43 @@ t.test('packs from local directory', async t => {
})
})

t.test('writes tarball to file when dryRun === false', async t => {
const testDir = t.testdir({
'package.json': JSON.stringify({
name: 'my-cool-pkg',
version: '1.0.0',
scripts: {
prepack: 'touch prepack',
postpack: 'touch postpack',
},
}, null, 2),
})

const cwd = process.cwd()
process.chdir(testDir)

const tarball = await pack('file:.', {
dryRun: false,
packDestination: testDir,
log: { level: 'silent' }, // so the test doesn't try to log
})
t.ok(tarball)
const expectedTarball = path.join(testDir, 'my-cool-pkg-1.0.0.tgz')
t.ok(fs.existsSync(expectedTarball), 'file was written')
t.same(fs.readFileSync(expectedTarball), tarball, 'wrote same data that was returned')

const prepackTimestamp = (await fs.promises.stat(path.join(testDir, 'prepack'))).mtime
const tarballTimestamp = (await fs.promises.stat(expectedTarball)).mtime
const postpackTimestamp = (await fs.promises.stat(path.join(testDir, 'postpack'))).mtime

t.ok(prepackTimestamp < tarballTimestamp, 'prepack ran before tarball was written')
t.ok(tarballTimestamp < postpackTimestamp, 'postpack ran after tarball was written')

t.teardown(async () => {
process.chdir(cwd)
})
})

t.test('packs from local directory with silent loglevel', async t => {
const testDir = t.testdir({
'package.json': JSON.stringify({
Expand Down