@carbon/cli 10.29.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/LICENSE +201 -0
- package/README.md +57 -0
- package/bin/carbon-cli.js +46 -0
- package/docker-compose.yml +13 -0
- package/package.json +56 -0
- package/src/changelog.js +217 -0
- package/src/cli.js +39 -0
- package/src/commands/bundle/bundlers.js +14 -0
- package/src/commands/bundle/javascript.js +142 -0
- package/src/commands/bundle.js +67 -0
- package/src/commands/changelog.js +70 -0
- package/src/commands/check.js +71 -0
- package/src/commands/ci-check.js +51 -0
- package/src/commands/component.js +130 -0
- package/src/commands/contribute/setup.js +232 -0
- package/src/commands/contribute/tools/getGitHubClient.js +73 -0
- package/src/commands/contribute.js +16 -0
- package/src/commands/inline.js +170 -0
- package/src/commands/publish.js +274 -0
- package/src/commands/release.js +302 -0
- package/src/commands/sassdoc/tools.js +405 -0
- package/src/commands/sassdoc.js +90 -0
- package/src/commands/sync/npm.js +43 -0
- package/src/commands/sync/package.js +122 -0
- package/src/commands/sync/readme.js +68 -0
- package/src/commands/sync/remark/remark-monorepo.js +425 -0
- package/src/commands/sync.js +39 -0
- package/src/compile.js +26 -0
- package/src/component/index.js +47 -0
- package/src/component/templates/component.template.js +19 -0
- package/src/component/templates/index.template.js +9 -0
- package/src/component/templates/mdx.template.mdx +37 -0
- package/src/component/templates/story.template.js +22 -0
- package/src/component/templates/test.template.js +35 -0
- package/src/git.js +38 -0
- package/src/logger.js +95 -0
- package/src/workspace.js +60 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2019, 2019
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const execa = require('execa');
|
|
11
|
+
const { prompt } = require('inquirer');
|
|
12
|
+
const semver = require('semver');
|
|
13
|
+
const { generate } = require('../changelog');
|
|
14
|
+
const { fetchLatestFromUpstream } = require('../git');
|
|
15
|
+
const { createLogger, displayBanner } = require('../logger');
|
|
16
|
+
const { getPackages } = require('../workspace');
|
|
17
|
+
|
|
18
|
+
const logger = createLogger('publish');
|
|
19
|
+
// Enqueue tasks to run at the end of the command where we want to "clean-up"
|
|
20
|
+
// the environment
|
|
21
|
+
const deferred = [];
|
|
22
|
+
function defer(thunk) {
|
|
23
|
+
deferred.push(thunk);
|
|
24
|
+
}
|
|
25
|
+
async function cleanup() {
|
|
26
|
+
for (let i = deferred.length - 1; i >= 0; i--) {
|
|
27
|
+
const task = deferred[i];
|
|
28
|
+
await task;
|
|
29
|
+
}
|
|
30
|
+
deferred.length = 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Publish is the counterpart to the `release` command and is responsible for
|
|
35
|
+
* taking the current state of the project and publishing it to a given package
|
|
36
|
+
* registry. In addition, this command will handle local operations like
|
|
37
|
+
* creating git tags, making sure npm dist-tag's for packages are correct, and
|
|
38
|
+
* will generate a changelog to be used in a GitHub release.
|
|
39
|
+
*
|
|
40
|
+
* @param {object} tag
|
|
41
|
+
* @param {string} tag.tag
|
|
42
|
+
* @returns {void}
|
|
43
|
+
*/
|
|
44
|
+
async function publish({ tag, ...flags }) {
|
|
45
|
+
const { gitRemote, noGitTagVersion, noPush, registry, skipReset } = flags;
|
|
46
|
+
const lastTag = await getLastGitTag();
|
|
47
|
+
const packages = await getPackages();
|
|
48
|
+
|
|
49
|
+
displayBanner();
|
|
50
|
+
|
|
51
|
+
logger.start(`Validating the tag: ${tag}`);
|
|
52
|
+
|
|
53
|
+
if (tag[0] !== 'v') {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Expected tag name to match vX.Y.Z, instead received: ${tag}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!semver.valid(tag.slice(1))) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Given tag is not a semantically valid version, received: ${tag}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
logger.stop();
|
|
66
|
+
|
|
67
|
+
logger.start('Resetting the project to a known state');
|
|
68
|
+
|
|
69
|
+
if (!skipReset) {
|
|
70
|
+
const type = semver.diff(lastTag, tag);
|
|
71
|
+
|
|
72
|
+
if (type !== 'patch' && type !== 'prepatch') {
|
|
73
|
+
logger.info('Fetching latest from upstream master');
|
|
74
|
+
await fetchLatestFromUpstream();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
logger.info('Cleaning any local artifacts or node_modules');
|
|
78
|
+
// Make sure that our tooling is defined before running clean
|
|
79
|
+
await execa('yarn', ['install', '--offline']);
|
|
80
|
+
await execa('yarn', ['clean']);
|
|
81
|
+
|
|
82
|
+
logger.info('Installing known dependencies from offline mirror');
|
|
83
|
+
await execa('yarn', ['install', '--offline']);
|
|
84
|
+
|
|
85
|
+
logger.info('Building packages from source');
|
|
86
|
+
await execa('yarn', ['build']);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
logger.stop();
|
|
90
|
+
|
|
91
|
+
logger.start('Checking project for out-of-sync generated files');
|
|
92
|
+
|
|
93
|
+
const { stdout } = await execa('git', ['status', '--porcelain']);
|
|
94
|
+
if (stdout !== '') {
|
|
95
|
+
throw new Error(
|
|
96
|
+
'There are generated files that are out-of-sync. Please wait for ' +
|
|
97
|
+
'these changes to be committed upstream or commit manually'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
logger.stop();
|
|
102
|
+
|
|
103
|
+
logger.start('Publishing packages');
|
|
104
|
+
logger.info('Logging into npm');
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
// This command will fail if the user is unauthenticated
|
|
108
|
+
await execa('npm', ['whoami', '--registry', registry]);
|
|
109
|
+
} catch {
|
|
110
|
+
await execa('npm', ['login'], {
|
|
111
|
+
stdio: 'inherit',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
logger.info(`Setting the npm registry to ${registry}`);
|
|
116
|
+
const { stdout: originalRegistryUrl } = await execa('npm', [
|
|
117
|
+
'get',
|
|
118
|
+
'registry',
|
|
119
|
+
]);
|
|
120
|
+
if (originalRegistryUrl !== registry) {
|
|
121
|
+
await execa('npm', ['set', 'registry', registry]);
|
|
122
|
+
defer(() => execa('npm', ['set', 'registry', originalRegistryUrl]));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Publish packages using `lerna`, we default to placing these under the
|
|
126
|
+
// `next` tag instead of `latest` so that we can verify the release.
|
|
127
|
+
await execa(
|
|
128
|
+
'yarn',
|
|
129
|
+
[
|
|
130
|
+
'lerna',
|
|
131
|
+
'publish',
|
|
132
|
+
'from-package',
|
|
133
|
+
'--dist-tag',
|
|
134
|
+
'next',
|
|
135
|
+
'--registry',
|
|
136
|
+
registry,
|
|
137
|
+
],
|
|
138
|
+
{
|
|
139
|
+
stdio: 'inherit',
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
logger.stop();
|
|
144
|
+
|
|
145
|
+
// We specify a stopping point so that the operator can verify the packages
|
|
146
|
+
// published as intended before setting the `latest` dist-tag
|
|
147
|
+
const answers = await prompt([
|
|
148
|
+
{
|
|
149
|
+
type: 'confirm',
|
|
150
|
+
name: 'tags',
|
|
151
|
+
message: 'Would you like to update the package tags from next to latest?',
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
if (answers.tags) {
|
|
156
|
+
logger.start('Setting npm dist tags to latest');
|
|
157
|
+
|
|
158
|
+
for (const { name, version } of packages) {
|
|
159
|
+
logger.info(`Setting npm dist-tag for ${name}@${version} to latest`);
|
|
160
|
+
await execa('npm', ['dist-tag', 'add', `${name}@${version}`, 'latest']);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
logger.stop();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!noGitTagVersion) {
|
|
167
|
+
logger.start(`Generating the git tag ${tag}`);
|
|
168
|
+
await execa('git', ['tag', '-a', tag, '-m', tag]);
|
|
169
|
+
logger.stop();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!noPush) {
|
|
173
|
+
const { remote } = await prompt([
|
|
174
|
+
{
|
|
175
|
+
type: 'confirm',
|
|
176
|
+
name: 'remote',
|
|
177
|
+
message: `Should we push the tag ${tag} to the ${gitRemote} remote?`,
|
|
178
|
+
},
|
|
179
|
+
]);
|
|
180
|
+
|
|
181
|
+
if (remote) {
|
|
182
|
+
logger.start(`Pushing the tag ${tag} to the ${gitRemote} remote`);
|
|
183
|
+
await execa('git', ['push', gitRemote, tag]);
|
|
184
|
+
logger.stop();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
logger.start('Generating the changelog');
|
|
189
|
+
logger.info(`Using commits from ${lastTag}...${tag}`);
|
|
190
|
+
|
|
191
|
+
const changelog = await generate(packages, lastTag, tag);
|
|
192
|
+
logger.stop();
|
|
193
|
+
|
|
194
|
+
const { display } = await prompt([
|
|
195
|
+
{
|
|
196
|
+
type: 'confirm',
|
|
197
|
+
name: 'display',
|
|
198
|
+
message: 'Display contents of generated changelog for release?',
|
|
199
|
+
},
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
if (display) {
|
|
203
|
+
// eslint-disable-next-line no-console
|
|
204
|
+
console.log(changelog);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Gets the last known git tag from the `git tag` sub-command.
|
|
210
|
+
* @returns {string}
|
|
211
|
+
*/
|
|
212
|
+
async function getLastGitTag() {
|
|
213
|
+
const { stdout: tagInfo } = await execa('git', [
|
|
214
|
+
'tag',
|
|
215
|
+
'-l',
|
|
216
|
+
'--sort=-v:refname',
|
|
217
|
+
]);
|
|
218
|
+
const tags = tagInfo.split('\n');
|
|
219
|
+
return tags[0];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = {
|
|
223
|
+
command: 'publish <tag>',
|
|
224
|
+
desc:
|
|
225
|
+
'publish packages that have different versions from the package registry',
|
|
226
|
+
builder(yargs) {
|
|
227
|
+
yargs.positional('tag', {
|
|
228
|
+
describe: 'the version tag associated with the release',
|
|
229
|
+
type: 'string',
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
yargs.option('skip-reset', {
|
|
233
|
+
demandOption: false,
|
|
234
|
+
default: false,
|
|
235
|
+
describe: 'Skip the project reset step',
|
|
236
|
+
type: 'boolean',
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
yargs.option('registry', {
|
|
240
|
+
demandOption: false,
|
|
241
|
+
describe: 'Specify registry URL',
|
|
242
|
+
default: 'https://registry.npmjs.org/',
|
|
243
|
+
type: 'string',
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
yargs.option('no-git-tag-version', {
|
|
247
|
+
demandOption: false,
|
|
248
|
+
describe: 'Do not commit or tag version changes.',
|
|
249
|
+
default: false,
|
|
250
|
+
type: 'boolean',
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
yargs.option('git-remote', {
|
|
254
|
+
demandOption: false,
|
|
255
|
+
describe: 'Push git changes to the specified remote.',
|
|
256
|
+
default: 'upstream',
|
|
257
|
+
type: 'string',
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
yargs.option('no-push', {
|
|
261
|
+
demandOption: false,
|
|
262
|
+
describe: 'Do not push tagged commit to git remote.',
|
|
263
|
+
default: false,
|
|
264
|
+
type: 'boolean',
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
async handler(...args) {
|
|
268
|
+
try {
|
|
269
|
+
await publish(...args);
|
|
270
|
+
} finally {
|
|
271
|
+
await cleanup();
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
};
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2019, 2019
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const parse = require('@commitlint/parse');
|
|
11
|
+
const execa = require('execa');
|
|
12
|
+
const { prompt } = require('inquirer');
|
|
13
|
+
const semver = require('semver');
|
|
14
|
+
const { fetchLatestFromUpstream } = require('../git');
|
|
15
|
+
const { createLogger, displayBanner } = require('../logger');
|
|
16
|
+
|
|
17
|
+
// All supported commit types from our conventional-changelog preset
|
|
18
|
+
const types = [
|
|
19
|
+
'build',
|
|
20
|
+
'ci',
|
|
21
|
+
'chore',
|
|
22
|
+
'docs',
|
|
23
|
+
'feat',
|
|
24
|
+
'fix',
|
|
25
|
+
'perf',
|
|
26
|
+
'refactor',
|
|
27
|
+
'revert',
|
|
28
|
+
'style',
|
|
29
|
+
'test',
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// Filter supported commit types per release bump
|
|
33
|
+
const typesByReleaseBump = {
|
|
34
|
+
minor: types,
|
|
35
|
+
patch: types.filter((type) => type !== 'feat'),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const logger = createLogger('release');
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a branch with the commits necessary to generate a release for the
|
|
42
|
+
* given release bump. This command will execute the commands a developer may
|
|
43
|
+
* run in their terminal, but helps to create consistency around how to
|
|
44
|
+
* determine the appropriate names for tags or branches, it also helps with
|
|
45
|
+
* automatically determining which commits to cherry-pick over from the `master`
|
|
46
|
+
* into the current release branch
|
|
47
|
+
* @param {object} config
|
|
48
|
+
* @param {string} config.bump
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
51
|
+
async function release({ bump }) {
|
|
52
|
+
displayBanner();
|
|
53
|
+
|
|
54
|
+
logger.start('Getting latest tag');
|
|
55
|
+
|
|
56
|
+
// Make sure we've fetched the latest tags from upstream
|
|
57
|
+
await execa('git', ['pull', 'upstream', 'master', '--tags']);
|
|
58
|
+
const { stdout: tagInfo } = await execa('git', [
|
|
59
|
+
'tag',
|
|
60
|
+
'-l',
|
|
61
|
+
'--sort=-v:refname',
|
|
62
|
+
]);
|
|
63
|
+
const tags = tagInfo.split('\n');
|
|
64
|
+
const [latestTag] = tags;
|
|
65
|
+
|
|
66
|
+
if (!latestTag) {
|
|
67
|
+
throw new Error('Unable to find latest tag from git tags');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
logger.stop();
|
|
71
|
+
logger.start(`Finding next available version for bump: ${bump}`);
|
|
72
|
+
|
|
73
|
+
const nextTag = semver.inc(latestTag, bump);
|
|
74
|
+
|
|
75
|
+
logger.info(`Bumping ${latestTag} to ${nextTag}`);
|
|
76
|
+
logger.stop();
|
|
77
|
+
|
|
78
|
+
const branchName = `chore/release-${nextTag}`;
|
|
79
|
+
logger.start(`Creating branch: ${branchName}`);
|
|
80
|
+
|
|
81
|
+
if (bump === 'patch') {
|
|
82
|
+
logger.info(`Using tag ${latestTag} as base`);
|
|
83
|
+
// If we're bumping for a patch release, we'll need to base our release off
|
|
84
|
+
// of the previous known tag
|
|
85
|
+
await execa('git', ['checkout', latestTag]);
|
|
86
|
+
} else {
|
|
87
|
+
logger.info(`Using master branch as base`);
|
|
88
|
+
// If we're publishing other releases, we'll need to base our release off of
|
|
89
|
+
// the latest stable `master` branch
|
|
90
|
+
await fetchLatestFromUpstream();
|
|
91
|
+
}
|
|
92
|
+
await execa('git', ['checkout', '-b', branchName]);
|
|
93
|
+
|
|
94
|
+
logger.stop();
|
|
95
|
+
|
|
96
|
+
if (bump === 'patch') {
|
|
97
|
+
const commitRange = `${latestTag}...master`;
|
|
98
|
+
await cherryPickCommitsFrom(commitRange, bump);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// After making sure our base branch is up-to-date, let's go ahead and reset
|
|
102
|
+
// our project and rebuild everything from a known state. This is helpful for
|
|
103
|
+
// getting rid of any local inconsistencies. Ultimately this process
|
|
104
|
+
// replicates what we do in our Continuous Integration checks.
|
|
105
|
+
await resetProjectState();
|
|
106
|
+
|
|
107
|
+
// Just in case there are any freshly generated files after running the steps
|
|
108
|
+
// above, we'll check to see if the local branch is dirty before proceeding
|
|
109
|
+
await checkIfBranchIsDirty();
|
|
110
|
+
|
|
111
|
+
// Call out to lerna to handle versioning changed packages
|
|
112
|
+
await execa(
|
|
113
|
+
'yarn',
|
|
114
|
+
['lerna', 'version', bump, '--no-push', '--no-git-tag-version', '--exact'],
|
|
115
|
+
{
|
|
116
|
+
stdio: 'inherit',
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
logger.start('Creating final commit');
|
|
121
|
+
logger.info(
|
|
122
|
+
'The next step will be to manually create a Pull Request for this branch'
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const versionCommitMessage = 'chore(release): update package versions';
|
|
126
|
+
await execa('git', ['add', '-A']);
|
|
127
|
+
await execa('git', ['commit', '-m', versionCommitMessage]);
|
|
128
|
+
|
|
129
|
+
logger.stop();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* When working with patch releases, we'll want to cherry pick commits that are
|
|
134
|
+
* found in the commit range between two tags. This helper also considers the
|
|
135
|
+
* version bump and the types of commits found. Depending on the bump certain
|
|
136
|
+
* commit types will be included. If an appropriate commit type is not found,
|
|
137
|
+
* we'll prompt the user for whether or not to include it. If a merge conflict
|
|
138
|
+
* occurs, we'll prompt the user to address it before proceeding.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} commitRange - the two tags we'll want to grab commits from.
|
|
141
|
+
* The format should follow `tagA...tagB`, where tagA is older than tagB.
|
|
142
|
+
* @param {string} bump - the version bump
|
|
143
|
+
* @returns {void}
|
|
144
|
+
*/
|
|
145
|
+
async function cherryPickCommitsFrom(commitRange, bump) {
|
|
146
|
+
logger.start(`Getting commits to cherry-pick from ${commitRange}`);
|
|
147
|
+
|
|
148
|
+
const { stdout: commitInfo } = await execa('git', [
|
|
149
|
+
'--no-pager',
|
|
150
|
+
'log',
|
|
151
|
+
'--oneline',
|
|
152
|
+
commitRange,
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
const commits = [];
|
|
156
|
+
for (const commit of commitInfo.split('\n').reverse()) {
|
|
157
|
+
const hash = commit.slice(0, 9);
|
|
158
|
+
const body = commit.slice(10);
|
|
159
|
+
const info = await parse(body);
|
|
160
|
+
const result = {
|
|
161
|
+
info,
|
|
162
|
+
hash,
|
|
163
|
+
body,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// If we cannot derive the commit type, we'll need to manually confirm if we
|
|
167
|
+
// should include the commit
|
|
168
|
+
if (info.type === null) {
|
|
169
|
+
const answers = await prompt([
|
|
170
|
+
{
|
|
171
|
+
type: 'confirm',
|
|
172
|
+
name: 'confirmed',
|
|
173
|
+
message: `Should we include the commit: ${commit}`,
|
|
174
|
+
},
|
|
175
|
+
]);
|
|
176
|
+
|
|
177
|
+
// Override the type with "manual" that we can use when we filter to see
|
|
178
|
+
// what commits we need to cherry pick
|
|
179
|
+
if (answers.confirmed) {
|
|
180
|
+
result.info.type = 'manual';
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
commits.push(result);
|
|
185
|
+
}
|
|
186
|
+
const commitsToCherryPick = commits.filter((commit) => {
|
|
187
|
+
return (
|
|
188
|
+
typesByReleaseBump[bump].includes(commit.info.type) ||
|
|
189
|
+
commit.info.type === 'manual'
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
logger.info(`Found ${commitsToCherryPick.length} commits to cherry-pick`);
|
|
194
|
+
|
|
195
|
+
for (const commit of commitsToCherryPick) {
|
|
196
|
+
logger.info(`${commit.hash} ${commit.body}`);
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
await execa('git', [
|
|
200
|
+
'cherry-pick',
|
|
201
|
+
'--strategy=recursive',
|
|
202
|
+
'-X',
|
|
203
|
+
'theirs',
|
|
204
|
+
commit.hash,
|
|
205
|
+
]);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (!error.stderr.startsWith('error: could not apply')) {
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
const answers = await prompt([
|
|
211
|
+
{
|
|
212
|
+
type: 'confirm',
|
|
213
|
+
name: 'proceed',
|
|
214
|
+
message:
|
|
215
|
+
`Applying the commit ${commit.hash} lead to a conflict. ` +
|
|
216
|
+
`Please fix the conflict and then confirm to proceed.`,
|
|
217
|
+
default: false,
|
|
218
|
+
},
|
|
219
|
+
]);
|
|
220
|
+
if (!answers.proceed) {
|
|
221
|
+
await execa('git', ['cherry-pick', '--abort']);
|
|
222
|
+
throw new Error(
|
|
223
|
+
'Unable to proceed with cherry-picking after failed conflict ' +
|
|
224
|
+
'resolution attempt'
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
logger.stop();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* When working with multiple local environments, it's helpful to reset the
|
|
235
|
+
* project to a known state. This helper will try and clean everything up so
|
|
236
|
+
* that the environment is clean and good-to-go moving forward. Most of the
|
|
237
|
+
* steps in this method ultimately reflect what we do in Continuous Integration
|
|
238
|
+
* environments, with the addition of a `clean` command to remove generated
|
|
239
|
+
* artifacts locally.
|
|
240
|
+
*
|
|
241
|
+
* @returns {void}
|
|
242
|
+
*/
|
|
243
|
+
async function resetProjectState() {
|
|
244
|
+
logger.start('Resetting the project to a known state');
|
|
245
|
+
|
|
246
|
+
logger.info('Cleaning any local artifacts or node_modules');
|
|
247
|
+
// Make sure that our tooling is defined before running clean
|
|
248
|
+
await execa('yarn', ['install', '--offline']);
|
|
249
|
+
await execa('yarn', ['clean']);
|
|
250
|
+
|
|
251
|
+
logger.info('Installing known dependencies from offline mirror');
|
|
252
|
+
await execa('yarn', ['install', '--offline']);
|
|
253
|
+
|
|
254
|
+
logger.info('Building packages from source');
|
|
255
|
+
await execa('yarn', ['build']);
|
|
256
|
+
|
|
257
|
+
logger.stop();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* When working with generated files, sometimes we'll want to check if the
|
|
262
|
+
* working branch is dirty and if the caller wants to commit these files as part
|
|
263
|
+
* of the release process.
|
|
264
|
+
*
|
|
265
|
+
* @returns {void}
|
|
266
|
+
*/
|
|
267
|
+
async function checkIfBranchIsDirty() {
|
|
268
|
+
const { stdout } = await execa('git', ['status', '--porcelain']);
|
|
269
|
+
if (stdout !== '') {
|
|
270
|
+
const { confirmed } = await prompt([
|
|
271
|
+
{
|
|
272
|
+
type: 'confirm',
|
|
273
|
+
name: 'confirmed',
|
|
274
|
+
message:
|
|
275
|
+
'The git status of the project is currently not clean. Would ' +
|
|
276
|
+
'you like to commit these changes to the project?',
|
|
277
|
+
},
|
|
278
|
+
]);
|
|
279
|
+
|
|
280
|
+
if (confirmed) {
|
|
281
|
+
await execa('git', ['add', '-A']);
|
|
282
|
+
await execa('git', [
|
|
283
|
+
'commit',
|
|
284
|
+
'-m',
|
|
285
|
+
'chore(project): sync generated files [skip ci]',
|
|
286
|
+
]);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
module.exports = {
|
|
292
|
+
command: 'release [bump]',
|
|
293
|
+
desc: 'run the release step for the given version bump',
|
|
294
|
+
builder(yargs) {
|
|
295
|
+
yargs.positional('bump', {
|
|
296
|
+
describe: 'choose a release version to bump',
|
|
297
|
+
choices: ['minor', 'patch'],
|
|
298
|
+
default: 'patch',
|
|
299
|
+
});
|
|
300
|
+
},
|
|
301
|
+
handler: release,
|
|
302
|
+
};
|