node-publisher 3.7.0 → 3.8.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
@@ -72,7 +72,7 @@ npx node-publisher eject
72
72
  After ejecting, a `.release.yml` file will appear in the root directory of your package. You can override the default behaviour by modifying this file.
73
73
 
74
74
  ## Custom branch
75
- Using the `--branch` release param, it is possible to specify which branch should be checked out during the `prepare` [lifecycle](#lifecycle) step. When no `branch` is specified, the `master` branch will be checked out by default.
75
+ Using the `--branch` release param, it is possible to specify which branch should be checked out during the `prepare` [lifecycle](#lifecycle) step. When no `branch` is specified, the default branch of the repository is detected automatically: first via `origin/HEAD`, then by checking whether a `main` or `master` branch exists locally. If nothing can be detected, `main` is used as a fallback.
76
76
 
77
77
  ## Multiple configuration files
78
78
 
@@ -95,7 +95,7 @@ Example:
95
95
 
96
96
  The default release process assumes the following:
97
97
 
98
- - The master branch is called `master`.
98
+ - The default branch is `main` or `master` (any other branch can be specified with the `--branch` release param).
99
99
  - A `.nvmrc` file is present in the root of your package. In case it is missing, the release fails in its preparation phase.
100
100
  - The tool expects the Node version to match the one in `.nvmrc` during the release process. If the expectation is not met, the release fails in its preparation phase.
101
101
  - The tool expects the build generation script to be called `build`. Otherwise, the build step is skipped.
@@ -105,7 +105,7 @@ The default release process assumes the following:
105
105
 
106
106
  # Lifecycle
107
107
 
108
- 1. `prepare`: The process that prepares the workspace for releasing a new version of your package. It might checkout to master, check whether the working tree is clean, check the current node version, etc. Between this step and `test`, a rollback point is created for your git repo.
108
+ 1. `prepare`: The process that prepares the workspace for releasing a new version of your package. It might checkout to the default branch, check whether the working tree is clean, check the current node version, etc. Between this step and `test`, a rollback point is created for your git repo.
109
109
 
110
110
  2. `test`: Runs the tests and/or linting. You might want to configure the tool to run the same command as your CI tool does.
111
111
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-publisher",
3
- "version": "3.7.0",
3
+ "version": "3.8.0",
4
4
  "description": "A configurable release automation tool inspired by create-react-app and Travis CI.",
5
5
  "author": "Attila Večerek <avecerek@zendesk.com>",
6
6
  "homepage": "https://github.com/zendesk/node-publisher#readme",
@@ -25,6 +25,7 @@
25
25
  "resolutions": {
26
26
  "tmp": "^0.2.6",
27
27
  "form-data": "^3.0.5",
28
+ "js-yaml": "^3.15.2",
28
29
  "lodash": "^4.18.1",
29
30
  "flatted": "^3.4.2",
30
31
  "filelist/minimatch": "^5.1.9",
@@ -1,4 +1,5 @@
1
1
  const { packageJson } = require('../utils/package');
2
+ const { getDefaultBranch } = require('../utils');
2
3
 
3
4
  const version = str => {
4
5
  if (str[0] === 'v') {
@@ -16,7 +17,9 @@ const publish = ({ nextVersion, execCommand, env }) => {
16
17
  execCommand(`git add package.json yarn.lock`);
17
18
  execCommand(`git commit -m "${version(pkg.version)}"`);
18
19
  execCommand(`git tag "${version(pkg.version)}"`);
19
- execCommand(`git push origin ${env.branch || 'master'} --follow-tags`);
20
+ execCommand(
21
+ `git push origin ${env.branch || getDefaultBranch()} --follow-tags`
22
+ );
20
23
  execCommand(`yarn npm publish`);
21
24
  };
22
25
 
@@ -59,7 +59,7 @@ describe('publish', () => {
59
59
  const options = {
60
60
  nextVersion: 'major',
61
61
  preid: 'alpha',
62
- env: { main: 'master' },
62
+ env: { branch: 'master' },
63
63
  execCommand: jest.fn()
64
64
  };
65
65
 
@@ -2,9 +2,9 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const childProcess = require('child_process');
4
4
  const { ask, addScript } = require('../utils');
5
- const { DEFAULT_BRANCH } = require('../../utils/constants');
6
5
 
7
6
  const GIT_PATH = path.resolve(process.env.PWD, '.git');
7
+ const DEFAULT_BRANCHES = ['main', 'master'];
8
8
 
9
9
  const isGitRepo = () => fs.existsSync(GIT_PATH);
10
10
 
@@ -23,9 +23,17 @@ const gitBranches = () => {
23
23
  };
24
24
 
25
25
  const prioritizeDefaultBranch = branches => {
26
- const index = branches.indexOf(DEFAULT_BRANCH);
26
+ const defaultBranch = DEFAULT_BRANCHES.find(branch =>
27
+ branches.includes(branch)
28
+ );
27
29
 
28
- return index === -1 ? branches : branches.splice(index, 1).concat(branches);
30
+ if (!defaultBranch) {
31
+ return branches;
32
+ }
33
+
34
+ return [defaultBranch].concat(
35
+ branches.filter(branch => branch !== defaultBranch)
36
+ );
29
37
  };
30
38
 
31
39
  const question = () => ({
@@ -44,10 +52,7 @@ async function releaseBranchStep() {
44
52
  }
45
53
 
46
54
  const releaseBranch = await ask(question());
47
- const releaseCommand =
48
- releaseBranch === 'master'
49
- ? 'node-publisher release'
50
- : `node-publisher release --branch=${releaseBranch}`;
55
+ const releaseCommand = `node-publisher release --branch=${releaseBranch}`;
51
56
 
52
57
  addScript('release', releaseCommand);
53
58
  }
@@ -3,7 +3,7 @@ const os = require('os');
3
3
 
4
4
  const DEFAULT_TEST_RUNNER = 'travis';
5
5
  const VALID_TEST_RUNNERS = [DEFAULT_TEST_RUNNER, 'ci'];
6
- const DEFAULT_BRANCH = 'master';
6
+ const DEFAULT_BRANCH = 'main';
7
7
  const DEFAULT_CONFIG_PATH = '.release.yml';
8
8
  const PACKAGE_JSON_PATH = path.resolve(process.env.PWD, 'package.json');
9
9
  const LERNA_JSON_PATH = path.resolve(process.env.PWD, 'lerna.json');
@@ -1,25 +1,25 @@
1
- const path = require("path");
2
- const fs = require("fs");
3
- const command = require("./command");
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const command = require('./command');
4
4
  const {
5
5
  DEFAULT_CONFIG_PATH,
6
6
  DEFAULT_BRANCH,
7
7
  VALID_TEST_RUNNERS,
8
8
  DEFAULT_TEST_RUNNER
9
- } = require("./constants");
10
- const { npmClient, publishClient } = require("./client");
11
- const { readReleaseConfig, buildReleaseConfig } = require("./config");
12
- const { packageJson } = require("./package");
9
+ } = require('./constants');
10
+ const { npmClient, publishClient } = require('./client');
11
+ const { readReleaseConfig, buildReleaseConfig } = require('./config');
12
+ const { packageJson } = require('./package');
13
13
  const {
14
14
  validateNodeVersion,
15
15
  validatePkgRoot,
16
16
  validateTestRunner,
17
17
  validateLerna,
18
18
  hasBuildScript
19
- } = require("./validations");
19
+ } = require('./validations');
20
20
 
21
21
  const buildReleaseEnvironment = ({
22
- branch = DEFAULT_BRANCH,
22
+ branch,
23
23
  configPath = DEFAULT_CONFIG_PATH,
24
24
  quiet = false
25
25
  }) => {
@@ -40,14 +40,14 @@ const buildReleaseEnvironment = ({
40
40
  }
41
41
 
42
42
  const client = publishClient();
43
- if (client === "lerna") {
43
+ if (client === 'lerna') {
44
44
  validateLerna();
45
45
  }
46
46
 
47
47
  return {
48
48
  publishClient: publishClient(),
49
49
  npmClient: npmClient(),
50
- branch: branch,
50
+ branch: branch || getDefaultBranch(),
51
51
  configPath: configPath,
52
52
  testRunner: testRunner,
53
53
  withBuildStep: hasBuildScript()
@@ -60,7 +60,7 @@ const loadReleaseConfig = env => {
60
60
  : path.resolve(process.env.PWD, env.configPath);
61
61
 
62
62
  if (fs.existsSync(configPath)) {
63
- return readReleaseConfig(fs.readFileSync(configPath, "utf8"));
63
+ return readReleaseConfig(fs.readFileSync(configPath, 'utf8'));
64
64
  } else if (env.configPath !== DEFAULT_CONFIG_PATH) {
65
65
  throw new Error(
66
66
  `The configuration file \`${env.configPath}\` does not exist.`
@@ -82,20 +82,57 @@ const execCommands = configCommands => {
82
82
 
83
83
  const currentCommitId = () =>
84
84
  command
85
- .exec("git rev-parse HEAD", {})
85
+ .exec('git rev-parse HEAD', {})
86
86
  .toString()
87
87
  .trim();
88
88
 
89
+ const getDefaultBranch = () => {
90
+ try {
91
+ return command
92
+ .exec('git symbolic-ref --short refs/remotes/origin/HEAD', {
93
+ stdio: ['ignore', 'pipe', 'ignore']
94
+ })
95
+ .toString()
96
+ .replace(/^origin\//, '')
97
+ .trim();
98
+ } catch (e) {
99
+ // origin/HEAD is not always set (e.g. repos cloned before the remote
100
+ // existed or with an old git version), so fall through to local branches.
101
+ }
102
+
103
+ try {
104
+ const branches = command
105
+ .exec('git branch', { stdio: ['ignore', 'pipe', 'ignore'] })
106
+ .toString()
107
+ .split('\n')
108
+ .map(line => line.replace(/^\*?\s*/, '').trim())
109
+ .filter(Boolean);
110
+
111
+ const detected = ['main', 'master'].find(branch =>
112
+ branches.includes(branch)
113
+ );
114
+
115
+ if (detected) {
116
+ return detected;
117
+ }
118
+ } catch (e) {
119
+ // No local branches to inspect either; fall through to the default.
120
+ }
121
+
122
+ return DEFAULT_BRANCH;
123
+ };
124
+
89
125
  const rollbackCommit = commitId => command.exec(`git reset --hard ${commitId}`);
90
126
 
91
127
  const versionTransformer = (version, _answers, flags) =>
92
- flags.isFinal && version[0] !== "v" ? `v${version}` : version;
128
+ flags.isFinal && version[0] !== 'v' ? `v${version}` : version;
93
129
 
94
130
  module.exports = {
95
131
  buildReleaseEnvironment,
96
132
  loadReleaseConfig,
97
133
  execCommands,
98
134
  currentCommitId,
135
+ getDefaultBranch,
99
136
  rollbackCommit,
100
137
  versionTransformer
101
138
  };
@@ -40,6 +40,8 @@ describe('buildReleaseEnvironment', () => {
40
40
  throw new Error();
41
41
  };
42
42
 
43
+ afterEach(() => execSync.mockClear());
44
+
43
45
  describe('when branch is passed', () => {
44
46
  beforeAll(() => mockTestRunner('test'));
45
47
 
@@ -51,11 +53,94 @@ describe('buildReleaseEnvironment', () => {
51
53
  });
52
54
 
53
55
  describe('when branch is not passed', () => {
56
+ afterEach(() => execSync.mockClear());
57
+
54
58
  it('contains the default branch', () => {
55
59
  const env = buildReleaseEnvironment({});
56
60
 
57
61
  expect(env.branch).toBe(DEFAULT_BRANCH);
58
62
  });
63
+
64
+ describe('and the origin HEAD points to main', () => {
65
+ beforeAll(() => {
66
+ childProcess.__permitCommands(['git']);
67
+ childProcess.__setReturnValues({
68
+ 'git symbolic-ref --short refs/remotes/origin/HEAD': Buffer.from(
69
+ 'origin/main\n',
70
+ 'utf8'
71
+ )
72
+ });
73
+ });
74
+
75
+ afterAll(() => {
76
+ childProcess.__permitCommands(['mycommand', 'mySecondCommand']);
77
+ childProcess.__setReturnValues({
78
+ 'git symbolic-ref --short refs/remotes/origin/HEAD': undefined
79
+ });
80
+ });
81
+
82
+ it('contains the detected default branch', () => {
83
+ const env = buildReleaseEnvironment({});
84
+
85
+ expect(env.branch).toBe('main');
86
+ });
87
+ });
88
+
89
+ describe('and the origin HEAD is not set but master exists locally', () => {
90
+ beforeAll(() => {
91
+ childProcess.__permitCommands(['git']);
92
+ childProcess.__setReturnValues({
93
+ 'git symbolic-ref --short refs/remotes/origin/HEAD': undefined,
94
+ 'git branch': Buffer.from(' feature-x\n* master\n', 'utf8')
95
+ });
96
+ });
97
+
98
+ afterAll(() => {
99
+ childProcess.__permitCommands(['mycommand', 'mySecondCommand']);
100
+ childProcess.__setReturnValues({
101
+ 'git symbolic-ref --short refs/remotes/origin/HEAD': undefined,
102
+ 'git branch': undefined
103
+ });
104
+ });
105
+
106
+ it('falls back to the local master branch', () => {
107
+ const env = buildReleaseEnvironment({});
108
+
109
+ expect(env.branch).toBe('master');
110
+ });
111
+ });
112
+
113
+ describe('and the origin HEAD is not set but main exists locally', () => {
114
+ beforeAll(() => {
115
+ childProcess.__permitCommands(['git']);
116
+ childProcess.__setReturnValues({
117
+ 'git symbolic-ref --short refs/remotes/origin/HEAD': undefined,
118
+ 'git branch': Buffer.from(' main\n* feature-x\n', 'utf8')
119
+ });
120
+ });
121
+
122
+ afterAll(() => {
123
+ childProcess.__permitCommands(['mycommand', 'mySecondCommand']);
124
+ childProcess.__setReturnValues({
125
+ 'git symbolic-ref --short refs/remotes/origin/HEAD': undefined,
126
+ 'git branch': undefined
127
+ });
128
+ });
129
+
130
+ it('falls back to the local main branch', () => {
131
+ const env = buildReleaseEnvironment({});
132
+
133
+ expect(env.branch).toBe('main');
134
+ });
135
+ });
136
+
137
+ describe('and the origin HEAD cannot be detected', () => {
138
+ it('falls back to the default branch', () => {
139
+ const env = buildReleaseEnvironment({});
140
+
141
+ expect(env.branch).toBe(DEFAULT_BRANCH);
142
+ });
143
+ });
59
144
  });
60
145
 
61
146
  describe('when configPath is passed', () => {