-
Notifications
You must be signed in to change notification settings - Fork 47.4k
/
Copy pathpublish.js
executable file
·226 lines (211 loc) · 7.21 KB
/
publish.js
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#!/usr/bin/env node
'use strict';
const ora = require('ora');
const path = require('path');
const yargs = require('yargs');
const {hashElement} = require('folder-hash');
const promptForOTP = require('./prompt-for-otp');
const {PUBLISHABLE_PACKAGES} = require('./shared/packages');
const {
execHelper,
getDateStringForCommit,
spawnHelper,
} = require('./shared/utils');
const {buildPackages} = require('./shared/build-packages');
const {readJson, writeJson} = require('fs-extra');
/**
* Script for publishing PUBLISHABLE_PACKAGES to npm. By default, this runs in tarball mode, meaning
* the script will only print out what the contents of the files included in the npm tarball would
* be.
*
* Please run this first (ie `yarn npm:publish`) and double check the contents of the files that
* will be pushed to npm.
*
* If it looks good, you can run `yarn npm:publish --for-real` to really publish to npm. You must
* have 2FA enabled first and the script will prompt you to enter a 2FA code before proceeding.
* There's a small annoying delay before the packages are actually pushed to give you time to panic
* cancel. In this mode, we will bump the version field of each package's package.json, and git
* commit it. Then, the packages will be published to npm.
*
* Optionally, you can add the `--debug` flag to `yarn npm:publish --debug --for-real` to run all
* steps, but the final npm publish step will have the `--dry-run` flag added to it. This will make
* the command only report what it would have done, instead of actually publishing to npm.
*/
async function main() {
const argv = yargs(process.argv.slice(2))
.option('packages', {
description: 'which packages to publish, defaults to all',
choices: PUBLISHABLE_PACKAGES,
default: PUBLISHABLE_PACKAGES,
})
.option('for-real', {
alias: 'frfr',
description:
'whether to publish to npm (npm publish) or dryrun (npm publish --dry-run)',
type: 'boolean',
default: false,
})
.option('debug', {
description:
'If enabled, will always run npm commands in dry run mode irregardless of the for-real flag',
type: 'boolean',
default: false,
})
.option('ci', {
description: 'Publish packages via CI',
type: 'boolean',
default: false,
})
.option('tag', {
description: 'Tag to publish to npm',
type: 'choices',
choices: ['experimental', 'beta'],
default: 'experimental',
})
.option('version-name', {
description: 'Version name',
type: 'string',
default: '0.0.0',
})
.help('help')
.strict()
.parseSync();
if (argv.debug === false) {
const currBranchName = await execHelper('git rev-parse --abbrev-ref HEAD');
const isPristine = (await execHelper('git status --porcelain')) === '';
if (currBranchName !== 'main' || isPristine === false) {
throw new Error(
'This script must be run from the `main` branch with no uncommitted changes'
);
}
}
let pkgNames = argv.packages;
if (Array.isArray(argv.packages) === false) {
pkgNames = [argv.packages];
}
const spinner = ora(
`Preparing to publish ${argv.versionName}@${argv.tag} ${
argv.forReal === true ? '(for real)' : '(dry run)'
} [debug=${argv.debug}]`
).info();
await buildPackages(pkgNames);
if (argv.forReal === false) {
spinner.info('Dry run: Report tarball contents');
for (const pkgName of pkgNames) {
console.log(`\n========== ${pkgName} ==========\n`);
spinner.start(`Running npm pack --dry-run\n`);
try {
await spawnHelper('npm', ['pack', '--dry-run'], {
cwd: path.resolve(__dirname, `../../packages/${pkgName}`),
stdio: 'inherit',
});
} catch (e) {
spinner.fail(e.toString());
throw e;
}
spinner.stop(`Successfully packed ${pkgName} (dry run)`);
}
spinner.succeed(
'Please confirm contents of packages before publishing. You can run this command again with --for-real to publish to npm'
);
}
if (argv.forReal === true) {
const commit = await execHelper(
'git show -s --no-show-signature --format=%h',
{
cwd: path.resolve(__dirname, '..'),
}
);
const dateString = await getDateStringForCommit(commit);
const otp =
argv.ci === false && argv.debug === false ? await promptForOTP() : null;
const {hash} = await hashElement(path.resolve(__dirname, '../..'), {
encoding: 'hex',
folders: {exclude: ['node_modules']},
files: {exclude: ['.DS_Store']},
});
const truncatedHash = hash.slice(0, 7);
const newVersion = `${argv.versionName}-${argv.tag}-${truncatedHash}-${dateString}`;
for (const pkgName of pkgNames) {
const pkgDir = path.resolve(__dirname, `../../packages/${pkgName}`);
const pkgJsonPath = path.resolve(
__dirname,
`../../packages/${pkgName}/package.json`
);
spinner.start(`Writing package.json for ${pkgName}@${newVersion}`);
await writeJson(
pkgJsonPath,
{
...(await readJson(pkgJsonPath)),
version: newVersion,
},
{spaces: 2}
);
spinner.succeed(`Wrote package.json for ${pkgName}@${newVersion}`);
console.log(`\n========== ${pkgName} ==========\n`);
spinner.start(`Publishing ${pkgName}@${newVersion} to npm\n`);
let opts = [];
if (argv.debug === true) {
opts.push('--dry-run');
}
if (otp != null) {
opts.push(`--otp=${otp}`);
}
/**
* Typically, the `latest` tag is reserved for stable package versions. Since the the compiler
* is still pre-release, until we have a stable release let's only add the
* `latest` tag to non-experimental releases.
*
* `latest` is added by default, so we only override it for experimental releases so that
* those don't get the `latest` tag.
*
* TODO: Update this when we have a stable release.
*/
if (argv.tag === 'experimental') {
opts.push('--tag=experimental');
} else {
opts.push('--tag=latest');
}
try {
await spawnHelper(
'npm',
['publish', ...opts, '--registry=https://registry.npmjs.org'],
{
cwd: pkgDir,
stdio: 'inherit',
}
);
console.log('\n');
} catch (e) {
spinner.fail(e.toString());
throw e;
}
spinner.succeed(`Successfully published ${pkgName} to npm`);
spinner.start('Pushing tags to npm');
if (typeof argv.tag === 'string') {
try {
let opts = ['dist-tag', 'add', `${pkgName}@${newVersion}`, argv.tag];
if (otp != null) {
opts.push(`--otp=${otp}`);
}
if (argv.debug === true) {
spinner.info(`dry-run: npm ${opts.join(' ')}`);
} else {
await spawnHelper('npm', opts, {
cwd: pkgDir,
stdio: 'inherit',
});
}
} catch (e) {
spinner.fail(e.toString());
throw e;
}
spinner.succeed(
`Successfully pushed dist-tag ${argv.tag} for ${pkgName} to npm`
);
}
}
console.log('\n\n✅ All done');
}
}
main();