-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathrelease
More file actions
executable file
·254 lines (232 loc) · 9.83 KB
/
release
File metadata and controls
executable file
·254 lines (232 loc) · 9.83 KB
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#!/usr/bin/env node
const { execSync } = require('child_process');
const fs = require('fs');
const os = require('os');
const chalk = require('chalk');
const minimist = require('minimist');
const { resolve } = require('path');
const CWD = process.cwd();
/**
* Parsed command-line arguments object processed using the `minimist` library.
*
* This variable contains the arguments passed to the program from the command-line interface
* after slicing off the first two elements of `process.argv` (node executable and script path).
* The arguments are processed into a structured object with specific parsing rules.
*
* Configuration:
* - `boolean`: Specifies arguments that should be treated as booleans.
* - `dry-run`: Indicates whether the operation is a dry run. Defaults to `false`.
* - `next`: Flag for enabling next-related actions. Defaults to `false`.
* - `string`: Specifies arguments that should be treated as strings.
* - `loglevel`: Defines the logging level, if provided.
* - `default`: Sets default values for parameters.
* - `dry-run`: Defaults to `false`.
* - `next`: Defaults to `false`.
*/
const argv = minimist(process.argv.slice(2), {
boolean: ['dry-run', 'next', 'build', 'test', 'release'],
string: ['loglevel'],
default: { 'dry-run': false, next: false, build: false, test: false, release: false },
});
/**
* Logs messages to the console with an informational label `[INFO]` styled in cyan.
*
* This function accepts multiple arguments and outputs them to the console,
* prefixed by a styled `[INFO]` label for clarity. The styling is achieved using
* the `chalk` library.
*
* @param {...*} msg - One or more arguments to be logged to the console.
*/
const log = (...msg) => console.log(chalk.cyan('[INFO]'), ...msg);
/**
* Logs warning messages to the console in a formatted and visually distinct way.
* Prepends the warning label "[WARN]" in yellow color to the provided messages.
*
* @param {...*} msg - The messages or data to be logged as warnings. Accepts multiple arguments.
*/
const warn = (...msg) => console.warn(chalk.yellow('[WARN]'), ...msg);
/**
* Logs error messages to the console with a standardized [ERROR] prefix in red color.
*
* This function uses the `chalk` library to style the text in red color for better visibility.
* The messages are passed as arguments and are logged to the console using `console.error`.
*
* @param {...any} msg - One or more error messages or data to be logged to the console.
*/
const error = (...msg) => console.error(chalk.red('[ERROR]'), ...msg);
/**
* Resolves the path to a binary installed in the project's `node_modules/.bin` directory.
*
* @param {string} n - The name of the binary file (e.g., "eslint", "jest").
* @returns {string} Absolute path to the binary in `node_modules/.bin`.
*/
const binary = (n) => resolve(CWD, 'node_modules/.bin', n);
/**
* Resolves a path relative to the project root (current working directory).
*
* @param {string} n - A relative path or filename (e.g., "src/index.js", "package.json").
* @returns {string} Absolute path to the file or directory in the project root.
*/
const root = (n) => resolve(CWD, n);
/**
* Executes a shell command synchronously, with optional configuration.
* If the `dry-run` option is enabled, logs the command without executing it.
*
* @param {string} command - The shell command to be executed.
* @param {Object} [options={}] - Optional configuration for command execution.
* The options are passed to the `execSync` method.
*/
const runCmd = (command, options = {}) => {
log(`CMD: ${command}`);
if (argv['dry-run']) {
warn('Dry run enabled — skipping execution');
return;
}
execSync(command, { stdio: 'inherit', cwd: CWD, ...options });
};
/**
* Retrieves the name of the current Git branch.
*
* This function executes a Git command to determine the branch name
* of the repository in the current working directory. It utilizes
* `git rev-parse --abbrev-ref HEAD` to extract the branch name and
* returns it as a trimmed string.
*
* @returns {string} The name of the current Git branch.
*
* @throws {Error} If the Git command fails or if the current
* directory is not part of a Git repository.
*/
const getCurrentBranch = () =>
execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
/**
* Builds print bundles under each element package's module directory (same as scripts/build sharedBuild).
* Only packages that both declare a ./print export and appear in pslb/pslb.config.js `packages` get pslb
* (blacklisted dirs like math-inline, select-text are excluded there).
*/
const buildPrintModules = () => {
// eslint-disable-next-line global-require, import/no-dynamic-require
const pslbConfig = require(resolve(CWD, 'pslb/pslb.config.js'));
const pslbAllowed = new Set(pslbConfig.packages || []);
const packagesRoot = resolve(CWD, 'packages');
const dirNames = fs.readdirSync(packagesRoot);
const withPrintExport = [];
for (const d of dirNames) {
const pkgPath = resolve(packagesRoot, d, 'package.json');
if (!fs.existsSync(pkgPath)) {
continue;
}
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
if (pkg.exports && pkg.exports['./print']) {
withPrintExport.push(pkg.name);
}
}
const printPkgNames = withPrintExport.filter((n) => pslbAllowed.has(n));
const skipped = withPrintExport.filter((n) => !pslbAllowed.has(n));
if (skipped.length > 0) {
warn(
'Skipping pslb for packages with ./print but not in pslb.config.js packages list:',
skipped.join(', '),
);
}
if (printPkgNames.length === 0) {
warn('No pslb-eligible packages with ./print export; skipping pslb');
return;
}
log('print packages for pslb:', printPkgNames.join(', '));
const pkgFlags = printPkgNames.map((n) => `--package ${n}`).join(' ');
runCmd(`yarn pslb --config pslb/pslb.config.js ${pkgFlags} --logLevel silly`);
};
/**
* Executes the build process, which consists of multiple sequential steps including:
*
* 1. Cleanup: Removes generated `lib` directories for all packages in the repository.
* 2. Linting: Runs ESLint across specified source files with defined extensions.
* 3. Transpilation: Uses Babel to build the code from source to output directories (`lib`)
* while generating source maps and respecting defined ignore rules.
* 4. Print modules: Runs pslb for packages with ./print that are listed in pslb.config.js (matches yarn build).
*/
const build = () => {
log(chalk.magenta('--- STEP 1: CLEANUP ---'));
runCmd(
`${binary(`lerna`)} exec -- rm -rf lib`,
);
log(chalk.magenta('--- STEP 2: LINT ---'));
runCmd(
`${binary(`lerna`)} exec -- ${binary('eslint')} --ignore-path ${root('.eslintignore')} --ext .js --ext .jsx src`,
);
log(chalk.magenta('--- STEP 3: BABEL BUILD ---'));
runCmd(
`${binary(`lerna`)} exec -- ${binary('babel')} --ignore \'**/__test__/**\',\'**/__tests__/**\' src -d lib --source-maps --root-mode upward`,
);
log(chalk.magenta('--- STEP 4: PRINT MODULES (pslb) ---'));
buildPrintModules();
};
/**
* Executes the test step of the build process.
*
* This function determines the number of CPU cores available on the host machine.
* It uses that information to calculate the number of workers (`workers`) to be utilized by Jest.
* If no CPU core information is available, a default value of 1 is used.
* The step also logs the number of workers being used for the test process
* and runs the Jest test suite in a worker-based parallel execution mode.
*/
const test = () => {
const workers = os.cpus().length || 1;
log(chalk.magenta(`--- STEP 4: TEST ---`));
runCmd(`${binary('jest')} -w ${workers}`);
};
/**
* Handles the release process, including versioning, pushing tags, and publishing packages.
*
* This function executes the following steps:
* 1. Logs the initiation of the versioning process and versions the packages using Lerna with conventional commits.
* 2. Pushes the tags and updates the branch to a remote git repository.
* 3. Publishes the packages to the package registry using Lerna, with optional customization for next/canary releases and log level.
*
* Conditional behavior:
* - If the `argv.next` flag is provided, the release process will include canary versioning with a preid and dist-tag of "next".
* - If the `argv.loglevel` flag is provided, the specified log level will be included in the publishing command.
*/
const release = () => {
log(chalk.magenta('--- STEP 5: VERSION PACKAGES ---'));
runCmd('npx lerna version --conventional-commits --yes');
log(chalk.magenta('--- STEP 6: PUBLISH PACKAGES ---'));
let releaseCmd = 'npx lerna publish from-git --yes';
if (argv.next) releaseCmd += ' --canary --preid next --dist-tag next';
if (argv.loglevel) releaseCmd += ` --loglevel ${argv.loglevel}`;
runCmd(releaseCmd);
log(chalk.magenta('--- STEP 7: PUSH TAGS ---'));
runCmd(`git push origin ${getCurrentBranch()} --follow-tags`);
};
/**
* Executes the build and release process.
*
* The `run` function coordinates the execution of the build and release workflows.
* If successful, it logs a confirmation message indicating the process completed successfully.
* If an error occurs during the execution, it logs an error message, showing the associated
* error details, and then terminates the process with a non-zero exit code.
*
* This function is designed to encapsulate the complete
* build-and-release lifecycle, handling both success and failure scenarios.
*/
const run = () => {
try {
// If no specific flags, run full workflow
const anyFlag = argv.build || argv.test || argv.release;
if (!anyFlag) {
build();
test();
release();
} else {
if (argv.build) build();
if (argv.test) test();
if (argv.release) release();
}
log(chalk.green('✅ Tasks completed successfully.'));
} catch (err) {
error('❌ Build/release failed:', err.message);
process.exit(1);
}
};
run();