release-it 15.1.4 → 15.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -110,6 +110,8 @@ Use `--dry-run` to show the interactivity and the commands it _would_ execute.
110
110
 
111
111
  To print the next version without releasing anything, add the `--release-version` flag.
112
112
 
113
+ To print the changelog without releasing anything, add the `--changelog` flag.
114
+
113
115
  ## Configuration
114
116
 
115
117
  Out of the box, release-it has sane defaults, and [plenty of options](./config/release-it.json) to configure it. Most
@@ -275,6 +277,7 @@ latestVersion
275
277
  changelog
276
278
  name
277
279
  repo.remote, repo.protocol, repo.host, repo.owner, repo.repository, repo.project
280
+ branchName
278
281
  ```
279
282
 
280
283
  All variables are available in all hooks. The only exception is that the additional variables listed above are not yet
@@ -297,16 +300,16 @@ Using Inquirer.js inside custom hook scripts might cause issues (since release-i
297
300
 
298
301
  Since v11, release-it can be extended in many, many ways. Here are some plugins:
299
302
 
300
- | Plugin | Description |
301
- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
302
- | [@release-it/bumper](https://github.com/release-it/bumper) | Read & write the version from/to any file |
303
- | [@release-it/conventional-changelog](https://github.com/release-it/conventional-changelog) | Provides recommended bump, conventional-changelog, and updates `CHANGELOG.md` |
304
- | [@release-it/keep-a-changelog](https://github.com/release-it/keep-a-changelog) | Maintain CHANGELOG.md using the Keep a Changelog standards |
305
- | [@release-it-plugins/lerna-changelog](https://github.com/@release-it-plugins/lerna-changelog)| Integrates lerna-changelog into the release-it pipeline |
306
- | [@release-it-plugins/workspaces](https://github.com/@release-it-plugins/workspaces) | Releases each of your projects configured workspaces |
307
- | [release-it-calver-plugin](https://github.com/casmith/release-it-calver-plugin) | Enables Calendar Versioning (calver) with release-it |
308
- | [@grupoboticario/news-fragments](https://github.com/grupoboticario/news-fragments) | An easy way to generate your changelog file |
309
- | [@j-ulrich/release-it-regex-bumper](https://github.com/j-ulrich/release-it-regex-bumper) | Regular expression based version read/write plugin for release-it |
303
+ | Plugin | Description |
304
+ | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
305
+ | [@release-it/bumper](https://github.com/release-it/bumper) | Read & write the version from/to any file |
306
+ | [@release-it/conventional-changelog](https://github.com/release-it/conventional-changelog) | Provides recommended bump, conventional-changelog, and updates `CHANGELOG.md` |
307
+ | [@release-it/keep-a-changelog](https://github.com/release-it/keep-a-changelog) | Maintain CHANGELOG.md using the Keep a Changelog standards |
308
+ | [@release-it-plugins/lerna-changelog](https://github.com/release-it-plugins/lerna-changelog) | Integrates lerna-changelog into the release-it pipeline |
309
+ | [@release-it-plugins/workspaces](https://github.com/release-it-plugins/workspaces) | Releases each of your projects configured workspaces |
310
+ | [release-it-calver-plugin](https://github.com/casmith/release-it-calver-plugin) | Enables Calendar Versioning (calver) with release-it |
311
+ | [@grupoboticario/news-fragments](https://github.com/grupoboticario/news-fragments) | An easy way to generate your changelog file |
312
+ | [@j-ulrich/release-it-regex-bumper](https://github.com/j-ulrich/release-it-regex-bumper) | Regular expression based version read/write plugin for release-it |
310
313
 
311
314
  Internally, release-it uses its own plugin architecture (for Git, GitHub, GitLab, npm).
312
315
 
package/lib/cli.js CHANGED
@@ -20,6 +20,7 @@ const helpText = `Release It! v${pkg.version}
20
20
  --only-version Prompt only for version, no further interaction
21
21
  -v --version Print release-it version number
22
22
  --release-version Print version number to be released
23
+ --changelog Print changelog for the version to be released
23
24
  -V --verbose Verbose output (user hooks output)
24
25
  -VV Extra verbose output (also internal commands output)
25
26
 
package/lib/config.js CHANGED
@@ -109,7 +109,7 @@ class Config {
109
109
  }
110
110
 
111
111
  get isCI() {
112
- return Boolean(this.options.ci) || this.isReleaseVersion;
112
+ return Boolean(this.options.ci) || this.isReleaseVersion || this.isChangelog;
113
113
  }
114
114
 
115
115
  get isPromptOnlyVersion() {
@@ -119,6 +119,10 @@ class Config {
119
119
  get isReleaseVersion() {
120
120
  return Boolean(this.options['release-version']);
121
121
  }
122
+
123
+ get isChangelog() {
124
+ return Boolean(this.options['changelog']);
125
+ }
122
126
  }
123
127
 
124
128
  export default Config;
package/lib/index.js CHANGED
@@ -78,7 +78,7 @@ const runTasks = async (opts, di) => {
78
78
  const action = config.isIncrement ? 'release' : 'update';
79
79
  const suffix = version && config.isIncrement ? `${latestVersion}...${version}` : `currently at ${latestVersion}`;
80
80
 
81
- if (!config.isReleaseVersion) {
81
+ if (!config.isReleaseVersion && !config.isChangelog) {
82
82
  log.obtrusive(`🚀 Let's ${action} ${name} (${suffix})`);
83
83
 
84
84
  log.preview({ title: 'changelog', text: changelog });
@@ -93,6 +93,11 @@ const runTasks = async (opts, di) => {
93
93
  process.exit(0);
94
94
  }
95
95
 
96
+ if (config.isChangelog) {
97
+ console.log(changelog);
98
+ process.exit(0);
99
+ }
100
+
96
101
  if (version) {
97
102
  config.setContext(parseVersion(version));
98
103
 
@@ -7,14 +7,18 @@ const changelogFallback = 'git log --pretty=format:"* %s (%h)"';
7
7
 
8
8
  class GitBase extends Plugin {
9
9
  async init() {
10
- this.remoteUrl = await this.getRemoteUrl();
11
- await this.fetch();
12
- const repo = parseGitUrl(this.remoteUrl);
13
- const latestTag = await this.getLatestTagName(repo);
10
+ const remoteUrl = await this.getRemoteUrl();
11
+ await this.fetch(remoteUrl);
12
+
13
+ const branchName = await this.getBranchName();
14
+ const repo = parseGitUrl(remoteUrl);
15
+ this.setContext({ remoteUrl, branchName, repo });
16
+ this.config.setContext({ remoteUrl, branchName, repo });
17
+
18
+ const latestTag = await this.getLatestTagName();
14
19
  const secondLatestTag = !this.config.isIncrement ? await this.getSecondLatestTagName(latestTag) : null;
15
20
  const tagTemplate = this.options.tagName || ((latestTag || '').match(/^v/) ? 'v${version}' : '${version}');
16
- this.setContext({ repo });
17
- this.config.setContext({ tagTemplate, latestTag, secondLatestTag });
21
+ this.config.setContext({ latestTag, secondLatestTag, tagTemplate });
18
22
  }
19
23
 
20
24
  getName() {
@@ -79,15 +83,15 @@ class GitBase extends Plugin {
79
83
  return this.exec(`git config --get branch.${branch}.remote`, { options }).catch(() => null);
80
84
  }
81
85
 
82
- fetch() {
86
+ fetch(remoteUrl) {
83
87
  return this.exec('git fetch').catch(err => {
84
88
  this.debug(err);
85
- throw new Error(`Unable to fetch from ${this.remoteUrl}${EOL}${err.message}`);
89
+ throw new Error(`Unable to fetch from ${remoteUrl}${EOL}${err.message}`);
86
90
  });
87
91
  }
88
92
 
89
- getLatestTagName(repo) {
90
- const context = Object.assign({ repo }, this.getContext(), { version: '*' });
93
+ getLatestTagName() {
94
+ const context = Object.assign({}, this.config.getContext(), { version: '*' });
91
95
  const match = format(this.options.tagMatch || this.options.tagName || '${version}', context);
92
96
  return this.exec(`git describe --tags --match=${match} --abbrev=0`, { options }).then(
93
97
  stdout => stdout || null,
@@ -37,7 +37,15 @@ const load = async pluginName => {
37
37
  plugin = module.default;
38
38
  }
39
39
  }
40
- return [path.parse(pluginName).name, plugin];
40
+ return [getPluginName(pluginName), plugin];
41
+ };
42
+
43
+ export const getPluginName = pluginName => {
44
+ if (pluginName.startsWith('.')) {
45
+ return path.parse(pluginName).name;
46
+ }
47
+
48
+ return pluginName;
41
49
  };
42
50
 
43
51
  export let getPlugins = async (config, container) => {
@@ -39,7 +39,8 @@ class Git extends GitBase {
39
39
 
40
40
  await super.init();
41
41
 
42
- if (this.options.push && !this.remoteUrl) {
42
+ const remoteUrl = this.getContext('remoteUrl');
43
+ if (this.options.push && !remoteUrl) {
43
44
  throw e(`Could not get remote Git url.${EOL}Please add a remote repository.`, docs);
44
45
  }
45
46
  if (this.options.requireUpstream && !(await this.hasUpstreamBranch())) {
@@ -48,7 +49,6 @@ class Git extends GitBase {
48
49
  if (this.options.requireCommits && (await this.getCommitsSinceLatestTag()) === 0) {
49
50
  throw e(`There are no commits since the latest tag.`, docs);
50
51
  }
51
- this.config.setContext({ repo: this.getContext('repo') });
52
52
  }
53
53
 
54
54
  rollback() {
@@ -29,6 +29,7 @@ class npm extends Plugin {
29
29
  async init() {
30
30
  const { name, version: latestVersion, private: isPrivate, publishConfig } = readJSON(path.resolve(MANIFEST_PATH));
31
31
  this.setContext({ name, latestVersion, private: isPrivate, publishConfig });
32
+ this.config.setContext({ npm: { name } });
32
33
 
33
34
  const { publish, skipChecks } = this.options;
34
35
 
package/lib/util.js CHANGED
@@ -48,10 +48,10 @@ const rejectAfter = (ms, error) =>
48
48
 
49
49
  const parseGitUrl = remoteUrl => {
50
50
  if (!remoteUrl) return { host: null, owner: null, project: null, protocol: null, remote: null, repository: null };
51
- const normalizedUrl = (remoteUrl || '').replace(/\\/g, '/');
51
+ const normalizedUrl = (remoteUrl || '').replace(/^[A-Z]:/, '').replace(/\\/g, '/'); // Hacky workaround for file protocol in Windows
52
52
  const parsedUrl = gitUrlParse(normalizedUrl);
53
53
  const { resource: host, name: project, protocol, href: remote } = parsedUrl;
54
- const owner = protocol === 'file' ? _.last(parsedUrl.owner.split('/')) : parsedUrl.owner;
54
+ const owner = protocol === 'file' ? _.last(parsedUrl.owner.split('/')) : parsedUrl.owner; // Fix owner for file protocol
55
55
  const repository = `${owner}/${project}`;
56
56
  return { host, owner, project, protocol, remote, repository };
57
57
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-it",
3
- "version": "15.1.4",
3
+ "version": "15.4.0",
4
4
  "description": "Generic CLI tool to automate versioning and package publishing related tasks.",
5
5
  "keywords": [
6
6
  "build",
@@ -38,6 +38,7 @@
38
38
  "type": "module",
39
39
  "exports": {
40
40
  ".": "./lib/index.js",
41
+ "./package.json": "./package.json",
41
42
  "./test/util/index.js": "./test/util/index.js"
42
43
  },
43
44
  "files": [
@@ -67,13 +68,13 @@
67
68
  "form-data": "4.0.0",
68
69
  "git-url-parse": "12.0.0",
69
70
  "globby": "13.1.2",
70
- "got": "12.1.0",
71
- "inquirer": "9.0.2",
71
+ "got": "12.3.1",
72
+ "inquirer": "9.1.0",
72
73
  "is-ci": "3.0.1",
73
74
  "lodash": "4.17.21",
74
75
  "mime-types": "2.1.35",
75
76
  "new-github-release-url": "2.0.0",
76
- "node-fetch": "3.2.9",
77
+ "node-fetch": "3.2.10",
77
78
  "open": "8.4.0",
78
79
  "ora": "6.1.2",
79
80
  "os-name": "5.0.1",
@@ -84,17 +85,17 @@
84
85
  "update-notifier": "6.0.2",
85
86
  "url-join": "5.0.0",
86
87
  "wildcard-match": "5.1.2",
87
- "yargs-parser": "21.0.1"
88
+ "yargs-parser": "21.1.1"
88
89
  },
89
90
  "devDependencies": {
90
91
  "@octokit/request-error": "3.0.0",
91
92
  "ava": "4.3.1",
92
- "eslint": "8.20.0",
93
+ "eslint": "8.21.0",
93
94
  "eslint-config-prettier": "8.5.0",
94
95
  "eslint-plugin-ava": "13.2.0",
95
96
  "eslint-plugin-import": "2.26.0",
96
97
  "eslint-plugin-prettier": "4.2.1",
97
- "mock-fs": "5.1.2",
98
+ "mock-fs": "5.1.4",
98
99
  "mock-stdio": "1.0.3",
99
100
  "nock": "13.2.9",
100
101
  "nyc": "15.1.0",
package/test/git.js CHANGED
@@ -294,8 +294,8 @@ test.serial('should roll back when cancelled', async t => {
294
294
  await gitClient.tag();
295
295
  await gitClient.rollbackOnce();
296
296
 
297
- t.is(exec.args[10][0], 'git tag --delete v1.2.4');
298
- t.is(exec.args[11][0], 'git reset --hard HEAD~1');
297
+ t.is(exec.args[11][0], 'git tag --delete v1.2.4');
298
+ t.is(exec.args[12][0], 'git reset --hard HEAD~1');
299
299
  });
300
300
 
301
301
  test.serial('should not touch existing history when rolling back', async t => {
package/test/github.js CHANGED
@@ -357,7 +357,7 @@ test('should not call octokit client in dry run', async t => {
357
357
  await runTasks(github);
358
358
 
359
359
  t.is(spy.get.callCount, 0);
360
- t.is(github.log.exec.args[0][0], 'octokit repos.createRelease "R 1.0.1" (v1.0.1)');
360
+ t.is(github.log.exec.args[1][0], 'octokit repos.createRelease "R 1.0.1" (v1.0.1)');
361
361
  t.is(github.log.exec.lastCall.args[0], 'octokit repos.uploadReleaseAssets');
362
362
  const { isReleased, releaseUrl } = github.getContext();
363
363
  t.true(isReleased);
package/test/gitlab.js CHANGED
@@ -231,8 +231,8 @@ test('should not make requests in dry run', async t => {
231
231
 
232
232
  const { isReleased, releaseUrl } = gitlab.getContext();
233
233
  t.is(spy.get.callCount, 0);
234
- t.is(gitlab.log.exec.args[1][0], 'gitlab releases#uploadAssets');
235
- t.is(gitlab.log.exec.args[2][0], 'gitlab releases#createRelease "R" (1.0.1)');
234
+ t.is(gitlab.log.exec.args[2][0], 'gitlab releases#uploadAssets');
235
+ t.is(gitlab.log.exec.args[3][0], 'gitlab releases#createRelease "R" (1.0.1)');
236
236
  t.true(isReleased);
237
237
  t.is(releaseUrl, `${pushRepo}/-/releases`);
238
238
  spy.get.restore();
@@ -0,0 +1,9 @@
1
+ import test from 'ava';
2
+ import { getPluginName } from '../lib/plugin/factory.js';
3
+
4
+ test('pluginName can return correct name for variants', t => {
5
+ t.is(getPluginName('plain-plugin'), 'plain-plugin');
6
+ t.is(getPluginName('@some/scoped-plugin'), '@some/scoped-plugin');
7
+ t.is(getPluginName('@some/nested/scoped-plugin'), '@some/nested/scoped-plugin');
8
+ t.is(getPluginName('./relative-plugin.cjs'), 'relative-plugin');
9
+ });
package/test/plugins.js CHANGED
@@ -121,6 +121,57 @@ test.serial('should instantiate plugins and execute all release-cycle methods',
121
121
  });
122
122
  });
123
123
 
124
+ test.serial('should instantiate plugins and execute all release-cycle methods for scoped plugins', async t => {
125
+ const { dir } = t.context;
126
+
127
+ const pluginDir = mkTmpDir();
128
+ sh.pushd('-q', pluginDir);
129
+ sh.ShellString(JSON.stringify({ name: '@scoped/my-plugin', version: '1.0.0', type: 'module' })).toEnd(
130
+ join(pluginDir, 'package.json')
131
+ );
132
+ sh.exec(`npm link release-it`);
133
+ const content = "import { Plugin } from 'release-it'; " + MyPlugin.toString() + '; export default MyPlugin;';
134
+ sh.ShellString(content).toEnd(join(pluginDir, 'index.js'));
135
+
136
+ sh.pushd('-q', dir);
137
+ sh.ShellString(JSON.stringify({ name: 'project', version: '1.0.0', type: 'module' })).toEnd(
138
+ join(dir, 'package.json')
139
+ );
140
+ sh.exec(`npm install ${pluginDir}`);
141
+ sh.exec(`npm link release-it`);
142
+
143
+ const config = {
144
+ plugins: {
145
+ '@scoped/my-plugin': {
146
+ name: 'foo'
147
+ }
148
+ }
149
+ };
150
+ const container = getContainer(config);
151
+
152
+ const result = await runTasks({}, container);
153
+
154
+ t.deepEqual(container.log.info.args, [
155
+ ['@scoped/my-plugin:foo:init'],
156
+ ['@scoped/my-plugin:foo:getName'],
157
+ ['@scoped/my-plugin:foo:getLatestVersion'],
158
+ ['@scoped/my-plugin:foo:getIncrement'],
159
+ ['@scoped/my-plugin:foo:getIncrementedVersionCI'],
160
+ ['@scoped/my-plugin:foo:beforeBump'],
161
+ ['@scoped/my-plugin:foo:bump:1.3.0'],
162
+ ['@scoped/my-plugin:foo:beforeRelease'],
163
+ ['@scoped/my-plugin:foo:release'],
164
+ ['@scoped/my-plugin:foo:afterRelease']
165
+ ]);
166
+
167
+ t.deepEqual(result, {
168
+ changelog: undefined,
169
+ name: 'new-project-name',
170
+ latestVersion: '1.2.3',
171
+ version: '1.3.0'
172
+ });
173
+ });
174
+
124
175
  test.serial('should disable core plugins', async t => {
125
176
  const { dir } = t.context;
126
177
  sh.ShellString(JSON.stringify({ name: 'project', version: '1.0.0' })).toEnd(join(dir, 'package.json'));
@@ -60,7 +60,7 @@ const getHooks = plugins => {
60
60
  };
61
61
 
62
62
  test.before(t => {
63
- t.timeout(60 * 1000);
63
+ t.timeout(90 * 1000);
64
64
  });
65
65
 
66
66
  test.serial.beforeEach(t => {
package/test/tasks.js CHANGED
@@ -186,6 +186,49 @@ test.serial('should release all the things (basic)', async t => {
186
186
  exec.restore();
187
187
  });
188
188
 
189
+ test.serial('should release with correct tag name', async t => {
190
+ const { bare, target } = t.context;
191
+ const project = path.basename(bare);
192
+ const pkgName = path.basename(target);
193
+ const owner = path.basename(path.dirname(bare));
194
+ const { stdout } = sh.exec('git rev-parse --abbrev-ref HEAD');
195
+ const branchName = stdout.trim();
196
+ gitAdd(`{"name":"${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
197
+ sh.exec(`git tag ${pkgName}-${branchName}-1.0.0`);
198
+ const sha = gitAdd('line', 'file', 'More file');
199
+
200
+ interceptGitHubCreate({
201
+ owner,
202
+ project,
203
+ body: {
204
+ tag_name: `${pkgName}-${branchName}-1.0.1`,
205
+ name: 'Release 1.0.1',
206
+ body: `* More file (${sha})`,
207
+ prerelease: false
208
+ }
209
+ });
210
+
211
+ const container = getContainer({
212
+ git: { tagName: '${npm.name}-${branchName}-${version}' },
213
+ github: { release: true, skipChecks: true, pushRepo: `https://github.com/${owner}/${project}` }
214
+ });
215
+
216
+ const exec = sinon.spy(container.shell, 'exec');
217
+
218
+ await runTasks({}, container);
219
+
220
+ const gitArgs = getArgs(container.shell.exec.args, 'git');
221
+
222
+ t.true(gitArgs.includes(`git tag --annotate --message Release 1.0.1 ${pkgName}-${branchName}-1.0.1`));
223
+ t.true(
224
+ log.log.secondCall.args[0].endsWith(
225
+ `https://github.com/${owner}/${project}/releases/tag/${pkgName}-${branchName}-1.0.1`
226
+ )
227
+ );
228
+
229
+ exec.restore();
230
+ });
231
+
189
232
  test.serial('should release all the things (pre-release, github, gitlab)', async t => {
190
233
  const { bare, target } = t.context;
191
234
  const project = path.basename(bare);
@@ -19,6 +19,9 @@ const gitAdd = (content, file, message) => {
19
19
  };
20
20
 
21
21
  const getArgs = (args, prefix) =>
22
- args.filter(args => typeof args[0] === 'string' && args[0].startsWith(prefix)).map(args => args[0].trim());
22
+ args
23
+ .map(args => (typeof args[0] !== 'string' ? args[0].join(' ') : args[0]))
24
+ .filter(cmd => cmd.startsWith(prefix))
25
+ .map(cmd => cmd.trim());
23
26
 
24
27
  export { mkTmpDir, readFile, gitAdd, getArgs };