-
Notifications
You must be signed in to change notification settings - Fork 919
/
Copy pathrelease.ts
201 lines (185 loc) · 5.56 KB
/
release.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { exec } from 'child-process-promise';
import { createPromptModule } from 'inquirer';
import { publish, publishInCI } from './utils/publish';
import { pushReleaseTagsToGithub, cleanTree, hasDiff } from './utils/git';
import {
releaseType as releaseTypePrompt,
validateVersions
} from './utils/inquirer';
import { reinstallDeps, buildPackages } from './utils/yarn';
import { runTests, setupTestDeps } from './utils/tests';
import { bumpVersionForStaging } from './staging';
import { ReleaseType } from './utils/enums';
import { getAllPackages } from './utils/workspace';
const prompt = createPromptModule();
interface releaseOptions {
skipReinstall: boolean;
skipTests: boolean;
ignoreUnstaged: boolean;
releaseType?: string;
dryRun: boolean;
ci: boolean;
}
export async function runRelease({
skipReinstall,
skipTests,
ignoreUnstaged,
releaseType: inputReleaseType,
dryRun,
ci
}: releaseOptions) {
console.log('ci', ci);
try {
/**
* If there are unstaged changes, error.
* Use --ignoreUnstaged to skip, such as if release-cli is continuing from a
* checkpoint.
*/
if (!ignoreUnstaged && (await hasDiff())) {
throw new Error(
'You have unstaged changes, stash your changes before attempting to publish'
);
}
/**
* Log the user who will be publishing the packages
*/
if (!process.env.CI) {
const { stdout: whoami } = await exec('npm whoami');
console.log(`Publishing as ${whoami}`);
}
/**
* Determine if the current release is a staging or production release
*/
const releaseType: string = await (async () => {
/**
* Capture the release type if it was passed to the CLI via args
*/
if (
inputReleaseType &&
(inputReleaseType === ReleaseType.Staging ||
inputReleaseType === ReleaseType.Production)
) {
return inputReleaseType;
}
/**
* Prompt for the release type (i.e. staging/prod)
*/
const responses = await prompt<{ [key: string]: string }>([
releaseTypePrompt
]);
return responses.releaseType;
})();
console.log(`Publishing ${inputReleaseType} release.`);
let packagesToPublish = [];
if (releaseType === ReleaseType.Staging) {
/**
* Bump versions for staging release
* NOTE: For prod, versions are bumped in a PR which should be merged before running this script
*/
const updatedPackages = await bumpVersionForStaging();
if (!ci) {
// We don't need to validate versions for prod releases because prod releases
// are validated in the version bump PR which should be merged before running this script
const { versionCheck } = await prompt([
validateVersions(updatedPackages)
]);
if (!versionCheck) {
throw new Error('Version check failed');
}
}
for (const key of updatedPackages.keys()) {
packagesToPublish.push(key);
}
} else {
/**
* For production releases, pass all packages to publishToCI().
* It will only publish if it finds the local version is newer
* than NPM's.
*/
packagesToPublish = await getAllPackages();
}
/**
* Users can pass --skipReinstall to skip the installation step
*/
if (!skipReinstall) {
/**
* Clean install dependencies
*/
console.log('\r\nVerifying Build');
await cleanTree();
await reinstallDeps();
}
/**
* build packages
*/
await buildPackages();
/**
* Users can pass --skipTests to skip the testing step
*/
if (!skipTests) {
await setupTestDeps();
await runTests();
}
/**
* Publish new versions to NPM using changeset (if manual)
* or using wombot for each package (if CI)
* It will also create tags
*/
if (ci) {
/**
* If the script is calling each individual npm command
* it can add the npm flag --dry-run, to better simulate
* everything in the publish process except publishing.
*/
if (releaseType === ReleaseType.Staging) {
await publishInCI(packagesToPublish, 'next', dryRun);
} else {
// Production.
await publishInCI(packagesToPublish, 'latest', dryRun);
}
} else {
/**
* If the script uses changeset to publish, there's no
* way to add the dry-run flag to each npm CLI command.
*/
if (!dryRun) {
// Uses changeset
await publish(releaseType);
}
}
/**
* Push tags to GitHub for prod releases only.
*/
if (releaseType === ReleaseType.Production && !dryRun) {
/**
* Push release tags created by changeset or publishInCI() to GitHub
*/
await pushReleaseTagsToGithub();
}
} catch (err) {
/**
* Log any errors that happened during the process
*/
console.error(err);
/**
* Exit with an error code
*/
process.exit(1);
}
}