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 +3 -3
- package/package.json +2 -1
- package/src/client/yarn-berry.js +4 -1
- package/src/client/yarn-berry.spec.js +1 -1
- package/src/setup/release-branch/index.js +12 -7
- package/src/utils/constants.js +1 -1
- package/src/utils/index.js +51 -14
- package/src/utils/index.spec.js +85 -0
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
|
|
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
|
|
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
|
|
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.
|
|
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",
|
package/src/client/yarn-berry.js
CHANGED
|
@@ -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(
|
|
20
|
+
execCommand(
|
|
21
|
+
`git push origin ${env.branch || getDefaultBranch()} --follow-tags`
|
|
22
|
+
);
|
|
20
23
|
execCommand(`yarn npm publish`);
|
|
21
24
|
};
|
|
22
25
|
|
|
@@ -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
|
|
26
|
+
const defaultBranch = DEFAULT_BRANCHES.find(branch =>
|
|
27
|
+
branches.includes(branch)
|
|
28
|
+
);
|
|
27
29
|
|
|
28
|
-
|
|
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
|
}
|
package/src/utils/constants.js
CHANGED
|
@@ -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 = '
|
|
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');
|
package/src/utils/index.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
const path = require(
|
|
2
|
-
const fs = require(
|
|
3
|
-
const command = require(
|
|
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(
|
|
10
|
-
const { npmClient, publishClient } = require(
|
|
11
|
-
const { readReleaseConfig, buildReleaseConfig } = require(
|
|
12
|
-
const { packageJson } = require(
|
|
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(
|
|
19
|
+
} = require('./validations');
|
|
20
20
|
|
|
21
21
|
const buildReleaseEnvironment = ({
|
|
22
|
-
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 ===
|
|
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,
|
|
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(
|
|
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] !==
|
|
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
|
};
|
package/src/utils/index.spec.js
CHANGED
|
@@ -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', () => {
|