cross-release-cli 0.0.1-alpha.5 → 0.1.0-alpha.2

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 CHANGED
@@ -22,9 +22,9 @@ yarn add cross-release --global
22
22
 
23
23
  ```json
24
24
  {
25
- "scripts": {
26
- "release": "cross-release"
27
- }
25
+ "scripts": {
26
+ "release": "cross-release"
27
+ }
28
28
  }
29
29
  ```
30
30
 
@@ -54,33 +54,33 @@ You can specify various runtime settings by using the "package.json" file. Here
54
54
 
55
55
  ```json
56
56
  {
57
- "...": "...",
58
- "cross-release": {
57
+ "...": "...",
58
+ "cross-release": {
59
59
  // show the help message
60
- "showHelp": false,
61
- // show the version about cross-release
62
- "showVersion": false,
63
- "version": "",
64
- "isAllYes": false,
65
- "isDry": false,
66
- "isRecursive": false,
67
- "shouldCommit": false,
68
- "shouldPush": false,
69
- "shouldTag": false,
70
- // default exclude folders are `["node_modules", ".git"]`, your config will be append within it
71
- "excludes": ["path/to/exclude"],
72
- "dir": "/path/to/run",
73
- "commit": {
74
- // Whether to invoke git pre-commit and commit-msg hook
75
- "shouldVerify": true,
76
- // Whether to stage all un-staged files or stage only changed files
77
- "shouldStageAll": false,
78
- // the symbol '%s' will be replace to the version number that you specified
79
- "template": "chore: release v%s"
80
- },
81
- "push": {
82
- "shouldFollowTags": false
60
+ "showHelp": false,
61
+ // show the version about cross-release
62
+ "showVersion": false,
63
+ "version": "",
64
+ "isAllYes": false,
65
+ "isDry": false,
66
+ "isRecursive": false,
67
+ "shouldCommit": false,
68
+ "shouldPush": false,
69
+ "shouldTag": false,
70
+ // default exclude folders are `["node_modules", ".git"]`, your config will be append within it
71
+ "excludes": ["path/to/exclude"],
72
+ "dir": "/path/to/run",
73
+ "commit": {
74
+ // Whether to invoke git pre-commit and commit-msg hook
75
+ "shouldVerify": true,
76
+ // Whether to stage all un-staged files or stage only changed files
77
+ "shouldStageAll": false,
78
+ // the symbol '%s' will be replace to the version number that you specified
79
+ "template": "chore: release v%s"
80
+ },
81
+ "push": {
82
+ "shouldFollowTags": false
83
+ }
83
84
  }
84
- }
85
85
  }
86
86
  ```
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict"
4
+
5
+ const App = await import("../dist/app.js")
6
+ const app = await App.default.create()
7
+ void app.run()
package/dist/app.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { ProjectFile } from 'cross-bump';
2
+ import { ReleaseOptions } from './types.js';
3
+
4
+ declare class App {
5
+ #private;
6
+ private _currentVersion;
7
+ private _modifiedFiles;
8
+ private _nextVersion;
9
+ private _options;
10
+ private _projectFiles;
11
+ private _taskQueue;
12
+ private _taskStatus;
13
+ private constructor();
14
+ static create(argv?: string[]): Promise<App>;
15
+ checkGitClean(): void;
16
+ confirmReleaseOptions(): Promise<void>;
17
+ executeTasks(): Promise<void>;
18
+ resolveExecutes(): void;
19
+ resolveNextVersion(): Promise<void>;
20
+ resolveProjectFiles(): void;
21
+ resolveProjects(): void;
22
+ run(): Promise<void>;
23
+ get currentVersion(): string;
24
+ get nextVersion(): string;
25
+ get options(): ReleaseOptions;
26
+ get projectFiles(): ProjectFile[];
27
+ }
28
+
29
+ export { App as default };
package/dist/app.js ADDED
@@ -0,0 +1,616 @@
1
+ // src/app.ts
2
+ import process4 from "node:process";
3
+ import {
4
+ cancel,
5
+ confirm,
6
+ intro,
7
+ isCancel,
8
+ log as log2,
9
+ outro
10
+ } from "@clack/prompts";
11
+ import {
12
+ findProjectFiles,
13
+ getProjectVersion,
14
+ isVersionValid as isVersionValid2,
15
+ upgradeProjectVersion
16
+ } from "cross-bump";
17
+ import { execaSync, parseCommandString } from "execa";
18
+ import isUnicodeSupported from "is-unicode-supported";
19
+ import color2 from "picocolors";
20
+
21
+ // src/cli.ts
22
+ import path from "node:path";
23
+ import { toAbsolute as toAbsolute2 } from "@rainbowatcher/path-extra";
24
+ import { Command } from "commander";
25
+ import { getGitignores } from "cross-bump";
26
+ import { defu as defu2 } from "defu";
27
+
28
+ // package.json
29
+ var version = "0.1.0-alpha.1";
30
+
31
+ // src/constants.ts
32
+ import process from "node:process";
33
+ var CONFIG_DEFAULT = {
34
+ all: false,
35
+ commit: {
36
+ stageAll: false,
37
+ template: "chore: release v%s",
38
+ verify: true
39
+ },
40
+ cwd: process.cwd(),
41
+ debug: false,
42
+ dry: false,
43
+ exclude: ["node_modules", ".git"],
44
+ execute: [],
45
+ main: "javascript",
46
+ push: {
47
+ followTags: false
48
+ },
49
+ recursive: false,
50
+ tag: {
51
+ template: "v%s"
52
+ },
53
+ yes: false
54
+ };
55
+
56
+ // src/util/config.ts
57
+ import process2 from "node:process";
58
+ import { isFileSync } from "@rainbowatcher/fs-extra";
59
+ import { toAbsolute } from "@rainbowatcher/path-extra";
60
+ import defu from "defu";
61
+ import { loadConfig } from "unconfig";
62
+
63
+ // src/util/debug.ts
64
+ import debug from "debug";
65
+ function createDebug(ns) {
66
+ return debug(`cross-release-cli:${ns}`);
67
+ }
68
+ function isDebugEnable(options) {
69
+ if (options.debug) {
70
+ debug.enable("cross-release-cli:*");
71
+ }
72
+ }
73
+
74
+ // src/util/config.ts
75
+ var debug2 = createDebug("config");
76
+ function resolveAltOptions(opts, key, defaultValue) {
77
+ const value = opts[key];
78
+ const _defaultValue = defaultValue ?? {};
79
+ return typeof value === "boolean" ? value ? _defaultValue : {} : { ..._defaultValue, ...value };
80
+ }
81
+ async function loadUserSpecifiedConfigFile(configPath, currentOpts) {
82
+ const absConfigPath = toAbsolute(configPath);
83
+ if (!isFileSync(absConfigPath)) {
84
+ throw new Error(`${absConfigPath} is not a valid file.`);
85
+ }
86
+ const { config, sources } = await loadConfig({
87
+ sources: [{
88
+ files: absConfigPath
89
+ }]
90
+ });
91
+ debug2("load specified config file:", sources);
92
+ return defu({ config: toAbsolute(currentOpts.config ?? "") }, currentOpts, config);
93
+ }
94
+ async function loadUserConfig(cwd = process2.cwd()) {
95
+ const { config: userConfig, sources } = await loadConfig({
96
+ cwd,
97
+ sources: [
98
+ { files: "cross-release.config" },
99
+ {
100
+ extensions: ["json"],
101
+ files: "package",
102
+ rewrite(config) {
103
+ return config["cross-release"];
104
+ }
105
+ }
106
+ ]
107
+ });
108
+ debug2("load user config", sources);
109
+ debug2("user config:", userConfig);
110
+ return userConfig;
111
+ }
112
+
113
+ // src/cli.ts
114
+ var debug3 = createDebug("cli");
115
+ function createCliProgram() {
116
+ const cli = new Command("cross-release");
117
+ cli.configureHelp({
118
+ subcommandTerm: (cmd) => `${cmd.name()} ${cmd.usage()}`
119
+ });
120
+ cli.name("cross-release").version(version).description("A release tool that support multi programming language").usage("[version] [options]").option("-a, --all", "Add all changed files to staged", CONFIG_DEFAULT.commit.stageAll).option("-c, --config [file]", "Config file (auto detect by default)").option("-D, --dry", "Dry run", CONFIG_DEFAULT.dry).option("-d, --debug", "Enable debug mode", CONFIG_DEFAULT.debug).option("-e, --exclude [dir]", "Folders to exclude from search", CONFIG_DEFAULT.exclude).option("-m, --main", "Base project language [e.g. java, rust, javascript]", CONFIG_DEFAULT.main).option("-r, --recursive", "Run the command for each project in the workspace", CONFIG_DEFAULT.recursive).option("-x, --execute [command...]", "Execute the command", CONFIG_DEFAULT.execute).option("-y, --yes", "Answer yes to all prompts", CONFIG_DEFAULT.yes).option("--cwd [dir]", "Set working directory", CONFIG_DEFAULT.cwd).option("--no-commit", "Skip committing changes").option("--no-push", "Skip pushing").option("--no-tag", "Skip tagging").option("-h, --help", "Display this message");
121
+ return cli;
122
+ }
123
+ function toCliReleaseOptions(cli) {
124
+ const { args } = cli;
125
+ const options = cli.opts();
126
+ if (options.help) {
127
+ cli.help();
128
+ }
129
+ return {
130
+ ...options,
131
+ ...args.length > 0 ? { version: args[0] } : {}
132
+ };
133
+ }
134
+ async function resolveOptions(cli) {
135
+ const cliOptions = toCliReleaseOptions(cli);
136
+ let userConfig;
137
+ if (cliOptions.config) {
138
+ userConfig = await loadUserSpecifiedConfigFile(cliOptions.config, cliOptions);
139
+ } else {
140
+ userConfig = await loadUserConfig();
141
+ }
142
+ const parsedArgs = defu2(cliOptions, userConfig);
143
+ isDebugEnable(parsedArgs);
144
+ const set = getGitignores(parsedArgs.cwd);
145
+ for (const i of parsedArgs.exclude) set.add(i);
146
+ parsedArgs.exclude = [...set];
147
+ const shouldBeAbsolute = ["cwd", "config"];
148
+ for (const key of shouldBeAbsolute) {
149
+ if (!parsedArgs[key]) continue;
150
+ if (key === "cwd") {
151
+ const { cwd } = parsedArgs;
152
+ parsedArgs.cwd = toAbsolute2(cwd);
153
+ }
154
+ parsedArgs[key] = path.resolve(parsedArgs.cwd, parsedArgs[key]);
155
+ }
156
+ debug3("parsedArgs:", parsedArgs);
157
+ return parsedArgs;
158
+ }
159
+
160
+ // src/git.ts
161
+ import process3 from "node:process";
162
+ import { log, spinner } from "@clack/prompts";
163
+ import { execaSync as createExeca } from "execa";
164
+ import color from "picocolors";
165
+ var debug4 = createDebug("git");
166
+ var execa = createExeca({ all: true, reject: false });
167
+ function gitTag(options) {
168
+ const {
169
+ cwd = process3.cwd(),
170
+ del = false,
171
+ dry = false,
172
+ force = false,
173
+ message: message2,
174
+ tagName: name
175
+ } = options ?? {};
176
+ const s = spinner();
177
+ s.start("creating tag...");
178
+ const args = [];
179
+ if (del) {
180
+ args.push("--delete");
181
+ } else {
182
+ if (!message2 || message2?.length === 0) {
183
+ log.warn("no message provided, is recommended to provide a message for create an annotated tag");
184
+ } else {
185
+ args.push(
186
+ // Create an annotated tag, which is recommended for releases.
187
+ // See https://git-scm.com/docs/git-tag
188
+ "--annotate",
189
+ // Use the same commit message for the tag
190
+ "--message",
191
+ // formatMessageString(template, nextVersion),
192
+ message2
193
+ );
194
+ }
195
+ }
196
+ if (force) args.push("--force");
197
+ args.push(name);
198
+ debug4(`command: git tag ${args.join(" ")}`);
199
+ if (!dry) {
200
+ const { exitCode, failed, shortMessage, stderr, stdout } = execa("git", ["tag", ...args], { cwd });
201
+ debug4("git tag stdout:", stdout, stderr);
202
+ if (failed) {
203
+ s.stop(color.red(shortMessage), exitCode);
204
+ return false;
205
+ }
206
+ }
207
+ s.stop(`create git tag: ${color.blue(name)}`);
208
+ return true;
209
+ }
210
+ function gitCommit(options) {
211
+ const {
212
+ cwd = process3.cwd(),
213
+ dry = false,
214
+ message: message2,
215
+ modifiedFiles = [],
216
+ stageAll,
217
+ verify
218
+ } = options ?? {};
219
+ const s = spinner();
220
+ s.start("committing...");
221
+ const args = [];
222
+ args.push("--message", message2);
223
+ !verify && args.push("--no-verify");
224
+ if (!stageAll && modifiedFiles.length > 0) {
225
+ args.push("--", ...modifiedFiles);
226
+ } else {
227
+ args.push("--all");
228
+ }
229
+ debug4(`command: git commit ${args.join(" ")}`);
230
+ if (!dry) {
231
+ const { exitCode, failed, shortMessage, stderr, stdout } = execa("git", ["commit", ...args], { cwd });
232
+ debug4("git commit stdout:", stdout, stderr);
233
+ if (failed) {
234
+ s.stop(color.red(shortMessage), exitCode);
235
+ return false;
236
+ }
237
+ }
238
+ s.stop(`commit message: ${color.green(message2)}`);
239
+ return true;
240
+ }
241
+ function gitPush(options = {}) {
242
+ const {
243
+ branch,
244
+ cwd = process3.cwd(),
245
+ dry,
246
+ followTags = true,
247
+ remote
248
+ } = options;
249
+ const s = spinner();
250
+ s.start("pushing...");
251
+ const args = [];
252
+ if (remote) {
253
+ args.push(remote);
254
+ if (branch) {
255
+ args.push(branch);
256
+ }
257
+ }
258
+ followTags && args.push("--follow-tags");
259
+ debug4(`command: git push ${args.join(" ")}`);
260
+ if (!dry) {
261
+ const { exitCode, failed, shortMessage, stderr, stdout } = execa("git", ["push", ...args], { cwd });
262
+ debug4("git push stdout:", stdout, stderr);
263
+ if (failed) {
264
+ s.stop(color.red(shortMessage), exitCode);
265
+ return false;
266
+ }
267
+ }
268
+ const originUrl = gitOriginUrl();
269
+ s.stop(`pushed to repo: ${color.underline(originUrl)}`);
270
+ return true;
271
+ }
272
+ function gitOriginUrl() {
273
+ const command = execa("git", ["remote", "get-url", "origin"]);
274
+ return command.stdout.trim();
275
+ }
276
+ function gitAdd(options = {}) {
277
+ const {
278
+ all = false,
279
+ cwd = process3.cwd(),
280
+ dry = false,
281
+ files = []
282
+ } = options;
283
+ const args = [];
284
+ if (all) {
285
+ args.push("-A");
286
+ } else if (files.length > 0) {
287
+ args.push("--", ...files);
288
+ }
289
+ debug4("command: git add", args.join(" "));
290
+ if (!dry) {
291
+ const { failed, stderr, stdout } = execa("git", ["add", ...args], { cwd });
292
+ debug4("git add stdout:", stdout, stderr);
293
+ if (failed) {
294
+ return false;
295
+ }
296
+ }
297
+ debug4("add files:", files);
298
+ return true;
299
+ }
300
+ function isGitClean(options = {}) {
301
+ const { cwd = process3.cwd() } = options;
302
+ const args = ["diff-index", "--quiet", "HEAD", "--"];
303
+ const { failed, message: message2 } = execa("git", args, { cwd });
304
+ if (message2?.includes("bad revision")) {
305
+ return true;
306
+ }
307
+ return !failed;
308
+ }
309
+ function getStagedFiles(opts = {}) {
310
+ const { cwd } = opts;
311
+ let stagedArr = [];
312
+ const args = ["--name-only", "--staged", "-z", "--diff-filter=ACMR"];
313
+ debug4("command: git diff", args.join(" "));
314
+ const { all, failed } = execa("git", ["diff", ...args], { cwd });
315
+ if (!failed) {
316
+ stagedArr = all.replace(/\0$/, "").split("\0");
317
+ }
318
+ return stagedArr;
319
+ }
320
+
321
+ // src/prompt.ts
322
+ import { select, text } from "@clack/prompts";
323
+ import { getNextVersions, isVersionValid, parseVersion } from "cross-bump";
324
+ async function chooseVersion(currentVersion) {
325
+ const versionObj = parseVersion(currentVersion);
326
+ const {
327
+ nextMajor,
328
+ nextMinor,
329
+ nextPatch,
330
+ nextPreMajor,
331
+ nextPreMinor,
332
+ nextPrePatch,
333
+ nextRelease
334
+ } = getNextVersions(versionObj ?? void 0);
335
+ const C_CUSTOM = "custom";
336
+ const versions = [
337
+ { label: "custom...", value: C_CUSTOM },
338
+ { label: `next (${nextRelease})`, value: nextRelease },
339
+ { label: `keep (${currentVersion})`, value: currentVersion ?? "" },
340
+ { label: `patch (${nextPatch})`, value: nextPatch },
341
+ { label: `minor (${nextMinor})`, value: nextMinor },
342
+ { label: `major (${nextMajor})`, value: nextMajor },
343
+ { label: `pre-patch (${nextPrePatch})`, value: nextPrePatch },
344
+ { label: `pre-minor (${nextPreMinor})`, value: nextPreMinor },
345
+ { label: `pre-major (${nextPreMajor})`, value: nextPreMajor }
346
+ ];
347
+ const selectedValue = await select({
348
+ initialValue: versions[1].value ?? C_CUSTOM,
349
+ message: `Pick a project version. (current: ${currentVersion})`,
350
+ options: versions
351
+ });
352
+ if (!selectedValue || selectedValue === C_CUSTOM) {
353
+ return await text({
354
+ message: "Input your custom version number",
355
+ placeholder: "version number",
356
+ validate: (value) => {
357
+ if (!isVersionValid(value)) {
358
+ return "Invalid";
359
+ }
360
+ }
361
+ });
362
+ } else {
363
+ return selectedValue;
364
+ }
365
+ }
366
+
367
+ // src/util/str.ts
368
+ function formatMessageString(template, nextVersion) {
369
+ return template?.includes("%s") ? template.replaceAll("%s", nextVersion) : template + nextVersion;
370
+ }
371
+
372
+ // src/app.ts
373
+ var debug5 = createDebug("app");
374
+ function message(msg) {
375
+ const bar = isUnicodeSupported() ? "\u2502" : "|";
376
+ console.log(`${color2.gray(bar)} ${msg}`);
377
+ }
378
+ function handleUserCancel(result) {
379
+ if (isCancel(result)) {
380
+ cancel("User cancel");
381
+ process4.exit(2 /* Canceled */);
382
+ }
383
+ return result;
384
+ }
385
+ var App = class _App {
386
+ _currentVersion = "";
387
+ _modifiedFiles = [];
388
+ _nextVersion = "";
389
+ _options;
390
+ _projectFiles = [];
391
+ _taskQueue = [];
392
+ _taskStatus = "pending";
393
+ constructor(opts) {
394
+ this._options = opts;
395
+ }
396
+ static async create(argv = process4.argv) {
397
+ const cli = createCliProgram().parse(argv);
398
+ const opts = await resolveOptions(cli);
399
+ return new _App(opts);
400
+ }
401
+ #addTask(task, idx) {
402
+ const expect = this._taskQueue.length + 1;
403
+ if (idx) {
404
+ this._taskQueue.splice(idx, 0, task);
405
+ } else {
406
+ this._taskQueue.push(task);
407
+ }
408
+ return this._taskQueue.length === expect;
409
+ }
410
+ #check(status) {
411
+ if (Array.isArray(status)) {
412
+ if (status.some((s) => !s)) {
413
+ this._taskStatus = "failed";
414
+ }
415
+ } else if (!status) {
416
+ this._taskStatus = "failed";
417
+ }
418
+ }
419
+ #checkDryRun() {
420
+ if (this._options.dry) {
421
+ log2.message(color2.bgBlue(" DRY RUN "));
422
+ process4.env.DRY = "true";
423
+ }
424
+ }
425
+ #done() {
426
+ if (this._taskStatus === "failed") {
427
+ outro(color2.red("Error"));
428
+ process4.exit(1 /* FatalError */);
429
+ } else {
430
+ outro("Done");
431
+ this._taskStatus = "finished";
432
+ }
433
+ }
434
+ #start() {
435
+ intro("Cross release");
436
+ this.#checkDryRun();
437
+ this._taskStatus = "running";
438
+ }
439
+ checkGitClean() {
440
+ const { cwd } = this._options;
441
+ if (!isGitClean({ cwd })) {
442
+ log2.warn("git is not clean, please commit or stash your changes before release");
443
+ this.#done();
444
+ process4.exit(3 /* GitDirty */);
445
+ }
446
+ }
447
+ async confirmReleaseOptions() {
448
+ const { all, cwd, dry, yes } = this._options;
449
+ const confirmTask = async (name, message2, exec) => {
450
+ if (yes) {
451
+ if (!this._options[name]) return;
452
+ this._options[name] = true;
453
+ } else if (this._options[name]) {
454
+ const confirmation = await confirm({ message: message2 });
455
+ this._options[name] = handleUserCancel(confirmation);
456
+ }
457
+ if (this._options[name]) {
458
+ this.#addTask({ exec, name });
459
+ }
460
+ };
461
+ let commitMessage;
462
+ if (this._options.commit) {
463
+ const {
464
+ stageAll,
465
+ template,
466
+ verify
467
+ } = resolveAltOptions(this._options, "commit", {
468
+ ...CONFIG_DEFAULT.commit,
469
+ stageAll: all
470
+ });
471
+ this.#addTask({
472
+ exec: () => {
473
+ return gitAdd({
474
+ all: stageAll === false ? all : stageAll,
475
+ cwd,
476
+ dry,
477
+ files: this._modifiedFiles
478
+ });
479
+ },
480
+ name: "add"
481
+ });
482
+ commitMessage = formatMessageString(template, this._nextVersion);
483
+ await confirmTask("commit", "should commit?", () => {
484
+ debug5("staged files: %O", getStagedFiles({ cwd }));
485
+ return gitCommit({
486
+ cwd,
487
+ dry,
488
+ message: commitMessage,
489
+ modifiedFiles: this._modifiedFiles,
490
+ // stageAll,
491
+ verify
492
+ });
493
+ });
494
+ }
495
+ if (this._options.tag && commitMessage !== void 0) {
496
+ const { template: tagTpt } = resolveAltOptions(this._options, "tag", CONFIG_DEFAULT.tag);
497
+ await confirmTask("tag", "should create tag?", () => {
498
+ const tagName = formatMessageString(tagTpt, this._nextVersion);
499
+ return gitTag({
500
+ cwd,
501
+ dry,
502
+ message: commitMessage,
503
+ tagName
504
+ });
505
+ });
506
+ }
507
+ if (this._options.push) {
508
+ const { followTags } = resolveAltOptions(this._options, "push", CONFIG_DEFAULT.push);
509
+ await confirmTask("push", "should push to remote?", () => {
510
+ return gitPush({ cwd, dry, followTags });
511
+ });
512
+ }
513
+ }
514
+ async executeTasks() {
515
+ debug5("taskQueue:", this._taskQueue);
516
+ for await (const task of this._taskQueue) {
517
+ if (this._taskStatus === "failed") break;
518
+ this.#check(await task.exec());
519
+ }
520
+ }
521
+ resolveExecutes() {
522
+ const { cwd, execute } = this._options;
523
+ const indexBeforeCommit = this._taskQueue.findIndex((t) => t.name === "commit") - 1;
524
+ const index = indexBeforeCommit === -1 ? this._taskQueue.length : indexBeforeCommit;
525
+ for (const command of execute) {
526
+ if (!command) continue;
527
+ const [cmd, ...args] = parseCommandString(command);
528
+ if (!cmd) continue;
529
+ const exec = () => {
530
+ debug5("exec command: %s %s", cmd, args.join(" "));
531
+ const { exitCode, failed, stdout } = execaSync(cmd, args, { cwd, reject: false });
532
+ debug5("exec stdout:", stdout, exitCode);
533
+ if (failed) {
534
+ log2.error(`exec: ${command}`);
535
+ return false;
536
+ } else {
537
+ log2.success(`exec: ${command}`);
538
+ return true;
539
+ }
540
+ };
541
+ this.#addTask({ exec, name: "anonymous" }, index);
542
+ }
543
+ }
544
+ async resolveNextVersion() {
545
+ const { main, version: version2 } = this._options;
546
+ const mainProjectFile = this._projectFiles.find((file) => file.category === main);
547
+ if (!mainProjectFile) {
548
+ throw new Error(`can't found ${main} project file in the project root`);
549
+ }
550
+ const projectVersion = await getProjectVersion(mainProjectFile);
551
+ this._currentVersion = projectVersion ?? "";
552
+ if (isVersionValid2(version2)) {
553
+ this._nextVersion = version2;
554
+ log2.info(`current version: ${this._currentVersion}, next version: ${color2.blue(this._nextVersion)}`);
555
+ } else {
556
+ const nextVersion = await chooseVersion(this._currentVersion);
557
+ this._nextVersion = handleUserCancel(nextVersion);
558
+ }
559
+ }
560
+ resolveProjectFiles() {
561
+ const { cwd, exclude, recursive } = this._options;
562
+ const projectFiles = findProjectFiles(cwd, exclude, recursive);
563
+ if (projectFiles.length === 0) {
564
+ console.error("can't found any project file in the project root");
565
+ process4.exit(1);
566
+ }
567
+ debug5(`found ${projectFiles.length} project files`);
568
+ this._projectFiles = projectFiles;
569
+ }
570
+ resolveProjects() {
571
+ const { _nextVersion, _projectFiles } = this;
572
+ this.#addTask({
573
+ exec: async () => {
574
+ return await Promise.all(_projectFiles.map(async (projectFile) => {
575
+ try {
576
+ await upgradeProjectVersion(_nextVersion, projectFile);
577
+ this._modifiedFiles.push(projectFile.path);
578
+ message(`upgrade to ${color2.blue(_nextVersion)} for ${color2.gray(projectFile.path)}`);
579
+ } catch (error) {
580
+ log2.error(String(error));
581
+ return false;
582
+ }
583
+ return true;
584
+ }));
585
+ },
586
+ name: "upgradeVersion"
587
+ });
588
+ }
589
+ async run() {
590
+ this.#start();
591
+ this.checkGitClean();
592
+ this.resolveProjectFiles();
593
+ await this.resolveNextVersion();
594
+ this.resolveProjects();
595
+ await this.confirmReleaseOptions();
596
+ this.resolveExecutes();
597
+ await this.executeTasks();
598
+ this.#done();
599
+ }
600
+ get currentVersion() {
601
+ return this._currentVersion;
602
+ }
603
+ get nextVersion() {
604
+ return this._nextVersion;
605
+ }
606
+ get options() {
607
+ return this._options;
608
+ }
609
+ get projectFiles() {
610
+ return this._projectFiles;
611
+ }
612
+ };
613
+ var app_default = App;
614
+ export {
615
+ app_default as default
616
+ };
@@ -0,0 +1,6 @@
1
+ import { DefineConfigOptions } from './types.js';
2
+ import 'cross-bump';
3
+
4
+ declare function defineConfig(config: DefineConfigOptions): DefineConfigOptions;
5
+
6
+ export { defineConfig };
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // src/index.ts
2
+ function defineConfig(config) {
3
+ return config;
4
+ }
5
+ export {
6
+ defineConfig
7
+ };
@@ -0,0 +1,127 @@
1
+ import { ProjectCategory } from 'cross-bump';
2
+
3
+ type Arrayable<T> = T | T[];
4
+ type CliPrimitive = boolean | string | string[];
5
+ type ExcludeType<T, U> = {
6
+ [K in keyof T]: T[K] extends U ? T[K] : Exclude<T[K], U>;
7
+ };
8
+ type KeysOf<T, KeyType = string> = keyof {
9
+ [K in keyof T as T[K] extends KeyType ? K : never]: T[K];
10
+ };
11
+ type ResolvedOptions<T> = T extends boolean ? never : NonNullable<T>;
12
+ type Status = "failed" | "finished" | "pending" | "running";
13
+ type ExtractBooleanKeys<T> = keyof Pick<T, {
14
+ [K in keyof T]: T[K] extends boolean | Record<string, unknown> ? K : never;
15
+ }[keyof T]>;
16
+ type Task = {
17
+ exec: () => boolean | boolean[] | Promise<boolean | boolean[]>;
18
+ name: string;
19
+ };
20
+ type ReleaseOptionsDefault = Omit<ExcludeType<ReleaseOptions, CliPrimitive>, "config" | "version">;
21
+ type DefineConfigOptions = Partial<Omit<ReleaseOptions, "config">>;
22
+ type CliReleaseOptions = ExcludeType<ReleaseOptions, Record<string, unknown>>;
23
+ type ReleaseOptions = {
24
+ /**
25
+ * Wethere add all changed files to staged, shorthand for @type {CommitOptions.stageAll}
26
+ */
27
+ all: boolean;
28
+ /**
29
+ * Indicates whether to commit the changes.
30
+ * @default false
31
+ */
32
+ commit: boolean | CommitOptions;
33
+ /**
34
+ * Specifies the path to the configuration file.
35
+ */
36
+ config: string;
37
+ /**
38
+ * The directory path where the operation will be performed.
39
+ * @default process.cwd()
40
+ */
41
+ cwd: string;
42
+ /**
43
+ * Enable debug log
44
+ */
45
+ debug: boolean;
46
+ /**
47
+ * Whether the operation is being run in a dry-run mode (simulated execution).
48
+ */
49
+ dry: boolean;
50
+ /**
51
+ * The list of directories to exclude from the search.
52
+ * @default ["node_modules", ".git"]
53
+ */
54
+ exclude: string[];
55
+ /**
56
+ * The command to execute before pushing.
57
+ */
58
+ execute: string[];
59
+ /**
60
+ * Specifies the main project category.
61
+ */
62
+ main: ProjectCategory;
63
+ /**
64
+ * Whether push changes to remote and push options
65
+ * @default false
66
+ */
67
+ push: boolean | PushOptions;
68
+ /**
69
+ * Specifies whether the operation should be performed recursively.
70
+ * @default false
71
+ */
72
+ recursive: boolean;
73
+ /**
74
+ * Indicates whether to create a tag for a release.
75
+ * @default false
76
+ */
77
+ tag: boolean | TagOptions;
78
+ /**
79
+ * The version string associated with the command or operation.
80
+ */
81
+ version: string;
82
+ /**
83
+ * Whether all prompts requiring user input will be answered with "yes".
84
+ * @default false
85
+ */
86
+ yes: boolean;
87
+ };
88
+ type CommitOptions = {
89
+ /**
90
+ * Whether to stage all files or only modified files.
91
+ */
92
+ stageAll?: boolean;
93
+ /**
94
+ * The template string for the commit message. if the template contains any "%s" placeholders,
95
+ * then they are replaced with the version number;
96
+ */
97
+ template?: string;
98
+ /**
99
+ * Whether to enable git pre-commit and commit-msg hook.
100
+ * @default true
101
+ */
102
+ verify?: boolean;
103
+ };
104
+ type PushOptions = {
105
+ /**
106
+ * The branch name
107
+ */
108
+ branch?: string;
109
+ /**
110
+ * Whether to follow tags
111
+ */
112
+ followTags?: boolean;
113
+ /**
114
+ * The remote name
115
+ */
116
+ remote?: string;
117
+ };
118
+ type TagOptions = {
119
+ /**
120
+ * The template for tag name, same as @type {CommitOptions.template}
121
+ * if the template contains any "%s" placeholders,
122
+ * then they are replaced with the version number;
123
+ */
124
+ template?: string;
125
+ };
126
+
127
+ export type { Arrayable, CliPrimitive, CliReleaseOptions, CommitOptions, DefineConfigOptions, ExcludeType, ExtractBooleanKeys, KeysOf, PushOptions, ReleaseOptions, ReleaseOptionsDefault, ResolvedOptions, Status, TagOptions, Task };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "cross-release-cli",
3
- "version": "0.0.1-alpha.5",
3
+ "type": "module",
4
+ "version": "0.1.0-alpha.2",
4
5
  "description": "command line app for cross language bump utility",
5
6
  "author": {
6
7
  "name": "rainbowatcher",
@@ -16,26 +17,29 @@
16
17
  "bugs": {
17
18
  "url": "https://github.com/rainbowatcher/cross-release/issues"
18
19
  },
19
- "main": "dist/index.mjs",
20
+ "main": "dist/index.js",
20
21
  "bin": {
21
22
  "cross-release": "./bin/cross-release.js"
22
23
  },
23
24
  "files": [
24
- "dist",
25
- "bin"
25
+ "bin",
26
+ "dist"
26
27
  ],
27
28
  "dependencies": {
28
29
  "@clack/prompts": "^0.7.0",
29
- "cac": "^6.7.14",
30
- "colorette": "^2.0.20",
31
- "debug": "^4.3.4",
32
- "defu": "^6.1.2",
33
- "execa": "^8.0.1",
34
- "is-unicode-supported": "^1.3.0",
35
- "cross-bump": "0.0.1-alpha.5"
30
+ "@rainbowatcher/fs-extra": "^0.2.3",
31
+ "@rainbowatcher/path-extra": "^0.2.3",
32
+ "commander": "^12.1.0",
33
+ "debug": "^4.3.6",
34
+ "defu": "^6.1.4",
35
+ "execa": "^9.3.1",
36
+ "is-unicode-supported": "^2.0.0",
37
+ "picocolors": "^1.1.0",
38
+ "unconfig": "^0.5.5",
39
+ "cross-bump": "0.1.0-alpha.2"
36
40
  },
37
41
  "scripts": {
38
42
  "clean": "rimraf dist/*",
39
- "build": "pkgroll"
43
+ "build": "tsup"
40
44
  }
41
45
  }
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict"
3
-
4
- import("../dist/index.mjs")
package/dist/index.mjs DELETED
@@ -1,404 +0,0 @@
1
- import path$1 from 'path';
2
- import { spinner, log, isCancel, intro, outro, cancel, select, text, confirm } from '@clack/prompts';
3
- import { findProjectFiles, getProjectVersion, isVersionValid, upgradeProjectVersion, parseVersion, getNextVersions } from 'cross-bump';
4
- import { red, blue, green, gray, bgBlue } from 'colorette';
5
- import isUnicodeSupported from 'is-unicode-supported';
6
- import * as path from 'node:path';
7
- import * as fs from 'node:fs';
8
- import cac from 'cac';
9
- import { defu } from 'defu';
10
- import { execa } from 'execa';
11
- import debug$1 from 'debug';
12
-
13
- var version = "0.0.1-alpha.5";
14
-
15
- const configDefaults = {
16
- showHelp: false,
17
- showVersion: false,
18
- version: "",
19
- isAllYes: false,
20
- isDry: false,
21
- isRecursive: false,
22
- shouldCommit: false,
23
- shouldPush: false,
24
- shouldTag: false,
25
- excludes: ["node_modules", ".git"],
26
- dir: process.cwd(),
27
- main: "javascript",
28
- commit: {
29
- shouldVerify: true,
30
- shouldStageAll: true,
31
- template: "chore: release v%s"
32
- },
33
- push: {
34
- shouldFollowTags: false
35
- }
36
- };
37
- function loadUserConfig(overrides) {
38
- const file = fs.readFileSync(path.resolve(process.cwd(), "package.json"), "utf-8");
39
- const userConfig = JSON.parse(file)["cross-release"];
40
- return defu(overrides, userConfig, configDefaults);
41
- }
42
- function parseOptions() {
43
- const cli = cac("cross-release").version(version).usage("[flags] version").option("-r, --recursive", "Run the command for each project in the workspace (default: false)").option("-c, --commit", "Commit current changes (default: false)").option("-d, --dry", "Dry run (default: false)").option("-e, --exclude [dir]", "Folders to exclude from search (default: [node_modules, .git])").option("-m, --main", "Base project language (e.g. java, rust, javascript default: javascript)").option("-D, --dir [dir]", "Set working directory (default: project root)").option("-p, --push", "Push the project to remote (default: false)").option("-t, --tag", "Create a tag for current version (default: false)").option("-y, --yes", "Answer yes to all prompts (default: false)").help().parse();
44
- const { args, options } = cli;
45
- const parsedArgs = loadUserConfig({
46
- dir: options.dir,
47
- excludes: options.exclude,
48
- isRecursive: options.recursive,
49
- isDry: options.dry,
50
- isAllYes: options.yes,
51
- showHelp: options.help,
52
- showVersion: options.version,
53
- shouldCommit: options.commit,
54
- shouldPush: options.push,
55
- shouldTag: options.tag,
56
- version: args[0]
57
- });
58
- return parsedArgs;
59
- }
60
-
61
- function createDebug(ns) {
62
- return debug$1(`cross-release-cli:${ns}`);
63
- }
64
-
65
- const debug = createDebug("git");
66
- async function gitTag(tagName, options) {
67
- const s = spinner();
68
- s.start("creating tag");
69
- const { isForce = false, isDel = false, message } = options || {};
70
- const args = [];
71
- if (isDel) {
72
- args.push("--delete");
73
- } else {
74
- if (!message || message?.length === 0) {
75
- log.warn("no message provided, is recommended to provide a message for create an annotated tag");
76
- } else {
77
- args.push(
78
- // Create an annotated tag, which is recommended for releases.
79
- // See https://git-scm.com/docs/git-tag
80
- "--annotate",
81
- // Use the same commit message for the tag
82
- "--message",
83
- // formatMessageString(template, nextVersion),
84
- message
85
- );
86
- }
87
- }
88
- if (isForce)
89
- args.push("--force");
90
- args.push(tagName);
91
- if (!process.env.DRY) {
92
- try {
93
- const { command } = await execa("git", ["tag", ...args]);
94
- debug(`command: ${command}`);
95
- } catch (e) {
96
- s.stop(red(e.shortMessage));
97
- return false;
98
- }
99
- }
100
- s.stop(`create git tag: ${blue(tagName)}`);
101
- return true;
102
- }
103
- async function gitCommit(message, options) {
104
- const s = spinner();
105
- s.start("committing");
106
- const { modifiedFiles = [], shouldStageAll, shouldVerify } = options || {};
107
- const args = [];
108
- if (process.env.DRY) {
109
- args.push("--dry-run");
110
- }
111
- args.push("--message", message);
112
- if (!shouldStageAll && modifiedFiles.length) {
113
- args.push("--", ...modifiedFiles);
114
- } else {
115
- args.push("--all");
116
- }
117
- if (!shouldVerify) {
118
- args.push("--no-verify");
119
- }
120
- try {
121
- const { command } = await execa("git", ["commit", ...args]);
122
- debug(`command: ${command}`);
123
- s.stop(`commit message: ${green(message)}`);
124
- } catch (e) {
125
- s.stop(red(e.shortMessage));
126
- return false;
127
- }
128
- return true;
129
- }
130
- async function gitPush(options) {
131
- const { shouldFollowTags = true, remote, branch } = options || {};
132
- const s = spinner();
133
- s.start("pushing");
134
- const originUrl = await gitOriginUrl();
135
- const args = [];
136
- if (shouldFollowTags) {
137
- args.push("--follow-tags");
138
- }
139
- if (remote) {
140
- args.push(remote);
141
- if (branch) {
142
- args.push(branch);
143
- }
144
- }
145
- if (process.env.DRY) {
146
- args.push("--dry-run");
147
- }
148
- try {
149
- const { command } = await execa("git", ["push", ...args]);
150
- debug(`command: ${command}`);
151
- s.stop(`pushed to repo: ${gray(originUrl)}`);
152
- } catch (e) {
153
- s.stop(red(e.shortMessage));
154
- return false;
155
- }
156
- return true;
157
- }
158
- async function gitOriginUrl() {
159
- return (await execa("git", ["remote", "get-url", "origin"])).stdout.trim();
160
- }
161
-
162
- var ExitCode = /* @__PURE__ */ ((ExitCode2) => {
163
- ExitCode2[ExitCode2["Success"] = 0] = "Success";
164
- ExitCode2[ExitCode2["FatalError"] = 1] = "FatalError";
165
- ExitCode2[ExitCode2["InvalidArgument"] = 9] = "InvalidArgument";
166
- return ExitCode2;
167
- })(ExitCode || {});
168
-
169
- var __accessCheck = (obj, member, msg) => {
170
- if (!member.has(obj))
171
- throw TypeError("Cannot " + msg);
172
- };
173
- var __privateAdd = (obj, member, value) => {
174
- if (member.has(obj))
175
- throw TypeError("Cannot add the same private member more than once");
176
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
177
- };
178
- var __privateMethod = (obj, member, method) => {
179
- __accessCheck(obj, member, "access private method");
180
- return method;
181
- };
182
- var _checkDryRun, checkDryRun_fn, _getNextVersion, getNextVersion_fn, _getProjects, getProjects_fn, _confirmReleaseOptions, confirmReleaseOptions_fn, _addTask, addTask_fn, _start, start_fn, _done, done_fn, _check, check_fn;
183
- function message(msg) {
184
- const bar = isUnicodeSupported() ? "\u2502" : "|";
185
- console.log(`${gray(bar)} ${msg}`);
186
- }
187
- function handleUserCancel() {
188
- cancel("User cancel");
189
- process.exit(ExitCode.InvalidArgument);
190
- }
191
- async function chooseVersion(currentVersion) {
192
- const versionObj = parseVersion(currentVersion);
193
- const {
194
- nextMajor,
195
- nextMinor,
196
- nextPatch,
197
- nextRelease,
198
- nextPreMajor,
199
- nextPreMinor,
200
- nextPrePatch
201
- } = getNextVersions(versionObj ?? void 0);
202
- const C_CUSTOM = "custom";
203
- const versions = [
204
- { label: "custom...", value: C_CUSTOM },
205
- { label: `next (${nextRelease})`, value: nextRelease },
206
- { label: `keep (${currentVersion})`, value: currentVersion || "" },
207
- { label: `patch (${nextPatch})`, value: nextPatch },
208
- { label: `minor (${nextMinor})`, value: nextMinor },
209
- { label: `major (${nextMajor})`, value: nextMajor },
210
- { label: `pre-patch (${nextPrePatch})`, value: nextPrePatch },
211
- { label: `pre-minor (${nextPreMinor})`, value: nextPreMinor },
212
- { label: `pre-major (${nextPreMajor})`, value: nextPreMajor }
213
- ];
214
- const selectedValue = await select({
215
- message: `Pick a project version. (current: ${currentVersion})`,
216
- options: versions,
217
- initialValue: versions[1].value ?? C_CUSTOM
218
- });
219
- if (!selectedValue || selectedValue === C_CUSTOM) {
220
- return await text({
221
- message: "Input your custom version number",
222
- placeholder: "version number",
223
- validate: (value) => {
224
- if (!isVersionValid(value)) {
225
- return "Invalid";
226
- }
227
- }
228
- });
229
- } else {
230
- return selectedValue;
231
- }
232
- }
233
- class App {
234
- constructor() {
235
- __privateAdd(this, _checkDryRun);
236
- __privateAdd(this, _getNextVersion);
237
- __privateAdd(this, _getProjects);
238
- __privateAdd(this, _confirmReleaseOptions);
239
- __privateAdd(this, _addTask);
240
- __privateAdd(this, _start);
241
- __privateAdd(this, _done);
242
- __privateAdd(this, _check);
243
- this.currentVersion = "";
244
- this.nextVersion = "";
245
- this.modifiedFiles = [];
246
- this.taskQueue = [];
247
- this.taskStatus = "pending";
248
- this.options = parseOptions();
249
- }
250
- async run() {
251
- __privateMethod(this, _start, start_fn).call(this);
252
- await __privateMethod(this, _getNextVersion, getNextVersion_fn).call(this);
253
- await __privateMethod(this, _getProjects, getProjects_fn).call(this);
254
- await __privateMethod(this, _confirmReleaseOptions, confirmReleaseOptions_fn).call(this);
255
- for await (const task of this.taskQueue) {
256
- if (this.taskStatus === "failed") {
257
- break;
258
- } else {
259
- await task.exec();
260
- }
261
- }
262
- __privateMethod(this, _done, done_fn).call(this);
263
- }
264
- /**
265
- * Accepts a message string template (e.g. "release %s" or "This is the %s release").
266
- * If the template contains any "%s" placeholders, then they are replaced with the version number;
267
- * otherwise, the version number is appended to the string.
268
- */
269
- formatMessageString(template, nextVersion) {
270
- if (template.includes("%s")) {
271
- return template.replace(/%s/g, nextVersion);
272
- } else {
273
- return template + nextVersion;
274
- }
275
- }
276
- }
277
- _checkDryRun = new WeakSet();
278
- checkDryRun_fn = function() {
279
- if (this.options.isDry) {
280
- log.message(bgBlue(" DRY RUN "));
281
- process.env.DRY = "true";
282
- }
283
- };
284
- _getNextVersion = new WeakSet();
285
- getNextVersion_fn = async function() {
286
- const { dir, excludes, version } = this.options;
287
- const projectFiles = await findProjectFiles(dir, excludes);
288
- if (!projectFiles.length) {
289
- throw new Error("can't found any project file in the project root");
290
- }
291
- const mainProjectFile = projectFiles.find((file) => file.category === this.options.main);
292
- if (!mainProjectFile) {
293
- throw new Error(`can't found ${this.options.main} project file in the project root`);
294
- }
295
- const projectVersion = await getProjectVersion(mainProjectFile);
296
- this.currentVersion = projectVersion || "";
297
- if (isVersionValid(version)) {
298
- this.nextVersion = version;
299
- } else {
300
- const nextVersion = await chooseVersion(this.currentVersion);
301
- if (isCancel(nextVersion)) {
302
- handleUserCancel();
303
- } else {
304
- this.nextVersion = nextVersion;
305
- }
306
- }
307
- };
308
- _getProjects = new WeakSet();
309
- getProjects_fn = async function() {
310
- const { options: { dir, excludes, isRecursive }, nextVersion } = this;
311
- const projectFiles = await findProjectFiles(dir, excludes, isRecursive);
312
- __privateMethod(this, _addTask, addTask_fn).call(this, {
313
- name: "upgradeVersion",
314
- exec: () => {
315
- return Promise.all(projectFiles.map(async (projectFile) => {
316
- try {
317
- await upgradeProjectVersion(nextVersion, projectFile);
318
- this.modifiedFiles.push(projectFile.path);
319
- message(`upgrade to ${blue(nextVersion)} for ${gray(path$1.relative(dir, projectFile.path))}`);
320
- } catch (e) {
321
- this.taskStatus = "failed";
322
- log.error(String(e));
323
- }
324
- }));
325
- }
326
- });
327
- };
328
- _confirmReleaseOptions = new WeakSet();
329
- confirmReleaseOptions_fn = async function() {
330
- const { isAllYes, commit } = this.options;
331
- const confirmAndSet = async (optionName, message3, taskName, execFn) => {
332
- if (isAllYes) {
333
- this.options[optionName] = true;
334
- } else if (!this.options[optionName]) {
335
- const confirmation = await confirm({ message: message3 });
336
- if (!isCancel(confirmation)) {
337
- this.options[optionName] = confirmation;
338
- } else {
339
- handleUserCancel();
340
- }
341
- }
342
- if (this.options[optionName]) {
343
- __privateMethod(this, _addTask, addTask_fn).call(this, {
344
- name: taskName,
345
- exec: execFn
346
- });
347
- }
348
- };
349
- const message2 = this.formatMessageString(commit.template, this.nextVersion);
350
- await confirmAndSet(
351
- "shouldCommit",
352
- "should commit?",
353
- "commit",
354
- async () => {
355
- __privateMethod(this, _check, check_fn).call(this, await gitCommit(message2, {
356
- modifiedFiles: this.modifiedFiles,
357
- shouldStageAll: commit.shouldStageAll,
358
- shouldVerify: commit.shouldVerify
359
- }));
360
- }
361
- );
362
- const tagName = `v${this.nextVersion}`;
363
- await confirmAndSet(
364
- "shouldTag",
365
- "should create tag?",
366
- "tag",
367
- async () => {
368
- __privateMethod(this, _check, check_fn).call(this, await gitTag(tagName, { message: message2 }));
369
- }
370
- );
371
- await confirmAndSet(
372
- "shouldPush",
373
- "should push to remote?",
374
- "push",
375
- async () => {
376
- __privateMethod(this, _check, check_fn).call(this, await gitPush({ shouldFollowTags: this.options.push.shouldFollowTags }));
377
- }
378
- );
379
- };
380
- _addTask = new WeakSet();
381
- addTask_fn = function(task) {
382
- const expect = this.taskQueue.length + 1;
383
- return this.taskQueue.push(task) === expect;
384
- };
385
- _start = new WeakSet();
386
- start_fn = function() {
387
- intro("Cross release");
388
- __privateMethod(this, _checkDryRun, checkDryRun_fn).call(this);
389
- this.taskStatus = "running";
390
- };
391
- _done = new WeakSet();
392
- done_fn = function() {
393
- outro("Done");
394
- this.taskStatus = "finished";
395
- };
396
- _check = new WeakSet();
397
- check_fn = function(status) {
398
- if (!status) {
399
- this.taskStatus = "failed";
400
- }
401
- };
402
- var app = new App();
403
-
404
- void app.run();