Skip to content

Commit f7b73e5

Browse files
committed
feat: update package.json and create tarball in prepare hook
1 parent 031281d commit f7b73e5

File tree

5 files changed

+122
-24
lines changed

5 files changed

+122
-24
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ Set of [semantic-release](https://github.com/semantic-release/semantic-release)
1313

1414
Verify the presence of the `NPM_TOKEN` environment variable, create or update the `.npmrc` file with the token and verify the token is valid.
1515

16-
## publish
16+
## prepare
17+
18+
Update the `package.json` version and [create](https://docs.npmjs.com/cli/pack) the `npm` package tarball.
1719

18-
Update the `package.json` version, [create](https://docs.npmjs.com/cli/pack) the `npm` package tarball and [publish](https://docs.npmjs.com/cli/publish) to the `npm` registry.
20+
## publish
21+
[Publish](https://docs.npmjs.com/cli/publish) to the `npm` registry.
1922

2023
## Configuration
2124

index.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ const setLegacyToken = require('./lib/set-legacy-token');
44
const getPkg = require('./lib/get-pkg');
55
const verifyNpmConfig = require('./lib/verify-config');
66
const verifyNpmAuth = require('./lib/verify-auth');
7+
const prepareNpm = require('./lib/prepare');
78
const publishNpm = require('./lib/publish');
89

910
let verified;
11+
let prepared;
1012

1113
async function verifyConditions(pluginConfig, {options: {publish}, logger}) {
1214
// If the npm publish plugin is used and has `npmPublish`, `tarballDir` or `pkgRoot` configured, validate them now in order to prevent any release if the configuration is wrong
@@ -38,9 +40,30 @@ async function verifyConditions(pluginConfig, {options: {publish}, logger}) {
3840
verified = true;
3941
}
4042

43+
async function prepare(pluginConfig, {nextRelease: {version}, logger}) {
44+
let pkg;
45+
const errors = verified ? [] : verifyNpmConfig(pluginConfig);
46+
47+
try {
48+
// Reload package.json in case a previous external step updated it
49+
pkg = await getPkg(pluginConfig.pkgRoot);
50+
if (!verified && pluginConfig.npmPublish !== false) {
51+
setLegacyToken();
52+
await verifyNpmAuth(pluginConfig, pkg, logger);
53+
}
54+
} catch (err) {
55+
errors.push(...err);
56+
}
57+
if (errors.length > 0) {
58+
throw new AggregateError(errors);
59+
}
60+
await prepareNpm(pluginConfig, version, logger);
61+
prepared = true;
62+
}
63+
4164
async function publish(pluginConfig, {nextRelease: {version}, logger}) {
4265
let pkg;
43-
const errors = verifyNpmConfig(pluginConfig);
66+
const errors = verified ? [] : verifyNpmConfig(pluginConfig);
4467

4568
setLegacyToken();
4669

@@ -56,7 +79,10 @@ async function publish(pluginConfig, {nextRelease: {version}, logger}) {
5679
if (errors.length > 0) {
5780
throw new AggregateError(errors);
5881
}
82+
if (!prepared) {
83+
await prepareNpm(pluginConfig, version, logger);
84+
}
5985
return publishNpm(pluginConfig, pkg, version, logger);
6086
}
6187

62-
module.exports = {verifyConditions, publish};
88+
module.exports = {verifyConditions, prepare, publish};

lib/prepare.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const path = require('path');
2+
const {move} = require('fs-extra');
3+
const execa = require('execa');
4+
const updatePackageVersion = require('./update-package-version');
5+
6+
module.exports = async ({tarballDir, pkgRoot}, version, logger) => {
7+
const basePath = pkgRoot || '.';
8+
await updatePackageVersion(version, basePath, logger);
9+
10+
if (tarballDir) {
11+
logger.log('Creating npm package version %s', version);
12+
const tarball = (await execa.stdout('npm', ['pack', `./${basePath}`])).split('\n').pop();
13+
await move(tarball, path.join(tarballDir.trim(), tarball));
14+
}
15+
};

lib/publish.js

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
1-
const path = require('path');
2-
const {move} = require('fs-extra');
31
const execa = require('execa');
42
const getRegistry = require('./get-registry');
5-
const updatePackageVersion = require('./update-package-version');
6-
const getReleaseInfo = require('./get-release-info.js');
7-
8-
module.exports = async ({npmPublish, tarballDir, pkgRoot}, {publishConfig, name}, version, logger) => {
9-
const basePath = pkgRoot || '.';
10-
const registry = await getRegistry(publishConfig, name);
11-
await updatePackageVersion(version, basePath, logger);
12-
13-
if (tarballDir) {
14-
logger.log('Creating npm package version %s', version);
15-
const tarball = (await execa.stdout('npm', ['pack', `./${basePath}`])).split('\n').pop();
16-
await move(tarball, path.join(tarballDir.trim(), tarball));
17-
}
3+
const getReleaseInfo = require('./get-release-info');
184

5+
module.exports = async ({npmPublish, pkgRoot}, {publishConfig, name}, version, logger) => {
196
if (npmPublish !== false) {
7+
const basePath = pkgRoot || '.';
8+
const registry = await getRegistry(publishConfig, name);
209
logger.log('Publishing version %s to npm registry', version);
2110
const shell = await execa('npm', ['publish', `./${basePath}`, '--registry', registry]);
2211
process.stdout.write(shell.stdout);
12+
return getReleaseInfo(name, publishConfig, registry);
2313
}
24-
25-
return getReleaseInfo(name, publishConfig, registry);
2614
};

test/integration.test.js

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ test.serial('Create the package and skip publish', async t => {
205205
{logger: t.context.logger, nextRelease: {version: '1.0.0'}}
206206
);
207207

208-
t.deepEqual(result, {name: 'npm package (@latest dist-tag)', url: undefined});
208+
t.falsy(result);
209209
t.is((await readJson('./package.json')).version, '1.0.0');
210210
t.true(await pathExists(`./tarball/${pkg.name}-1.0.0.tgz`));
211211
await t.throws(execa('npm', ['view', pkg.name, 'version']));
@@ -221,7 +221,7 @@ test.serial('Create the package and skip publish from a sub-directory', async t
221221
{logger: t.context.logger, nextRelease: {version: '1.0.0'}}
222222
);
223223

224-
t.deepEqual(result, {name: 'npm package (@latest dist-tag)', url: undefined});
224+
t.falsy(result);
225225
t.is((await readJson('./dist/package.json')).version, '1.0.0');
226226
t.true(await pathExists(`./tarball/${pkg.name}-1.0.0.tgz`));
227227
await t.throws(execa('npm', ['view', pkg.name, 'version']));
@@ -257,12 +257,78 @@ test.serial('Throw SemanticReleaseError Array if config option are not valid in
257257
t.is(errors[3].code, 'ENOPKGNAME');
258258
});
259259

260-
test.serial('Verify token and set up auth only on the fist call', async t => {
260+
test.serial('Prepare the package', async t => {
261+
Object.assign(process.env, npmRegistry.authEnv);
262+
const pkg = {name: 'prepare', version: '0.0.0', publishConfig: {registry: npmRegistry.url}};
263+
await outputJson('./package.json', pkg);
264+
265+
await t.context.m.prepare({}, {logger: t.context.logger, nextRelease: {version: '1.0.0'}});
266+
267+
t.is((await readJson('./package.json')).version, '1.0.0');
268+
t.false(await pathExists(`./${pkg.name}-1.0.0.tgz`));
269+
});
270+
271+
test.serial('Prepare the package from a sub-directory', async t => {
272+
Object.assign(process.env, npmRegistry.authEnv);
273+
const pkg = {name: 'prepare-sub-dir', version: '0.0.0', publishConfig: {registry: npmRegistry.url}};
274+
await outputJson('./dist/package.json', pkg);
275+
276+
await t.context.m.prepare({pkgRoot: 'dist'}, {logger: t.context.logger, nextRelease: {version: '1.0.0'}});
277+
278+
t.is((await readJson('./dist/package.json')).version, '1.0.0');
279+
t.false(await pathExists(`./${pkg.name}-1.0.0.tgz`));
280+
});
281+
282+
test.serial('Create the package in prepare step', async t => {
283+
const pkg = {name: 'prepare-pkg', version: '0.0.0', publishConfig: {registry: npmRegistry.url}};
284+
await outputJson('./package.json', pkg);
285+
286+
await t.context.m.prepare(
287+
{npmPublish: false, tarballDir: 'tarball'},
288+
{logger: t.context.logger, nextRelease: {version: '1.0.0'}}
289+
);
290+
291+
t.is((await readJson('./package.json')).version, '1.0.0');
292+
t.true(await pathExists(`./tarball/${pkg.name}-1.0.0.tgz`));
293+
});
294+
295+
test.serial('Throw SemanticReleaseError Array if config option are not valid in prepare', async t => {
296+
const pkg = {publishConfig: {registry: npmRegistry.url}};
297+
await outputJson('./package.json', pkg);
298+
const npmPublish = 42;
299+
const tarballDir = 42;
300+
const pkgRoot = 42;
301+
302+
const errors = [
303+
...(await t.throws(
304+
t.context.m.prepare(
305+
{npmPublish, tarballDir, pkgRoot},
306+
{
307+
options: {publish: ['@semantic-release/github', '@semantic-release/npm']},
308+
nextRelease: {version: '1.0.0'},
309+
logger: t.context.logger,
310+
}
311+
)
312+
)),
313+
];
314+
315+
t.is(errors[0].name, 'SemanticReleaseError');
316+
t.is(errors[0].code, 'EINVALIDNPMPUBLISH');
317+
t.is(errors[1].name, 'SemanticReleaseError');
318+
t.is(errors[1].code, 'EINVALIDTARBALLDIR');
319+
t.is(errors[2].name, 'SemanticReleaseError');
320+
t.is(errors[2].code, 'EINVALIDPKGROOT');
321+
t.is(errors[3].name, 'SemanticReleaseError');
322+
t.is(errors[3].code, 'ENOPKGNAME');
323+
});
324+
325+
test.serial('Verify token and set up auth only on the fist call, then prepare on prepare call only', async t => {
261326
Object.assign(process.env, npmRegistry.authEnv);
262327
const pkg = {name: 'test-module', version: '0.0.0-dev', publishConfig: {registry: npmRegistry.url}};
263328
await outputJson('./package.json', pkg);
264329

265330
await t.notThrows(t.context.m.verifyConditions({}, {options: {}, logger: t.context.logger}));
331+
await t.context.m.prepare({}, {logger: t.context.logger, nextRelease: {version: '1.0.0'}});
266332

267333
const result = await t.context.m.publish({}, {logger: t.context.logger, nextRelease: {version: '1.0.0'}});
268334
t.deepEqual(result, {name: 'npm package (@latest dist-tag)', url: undefined});

0 commit comments

Comments
 (0)