@travetto/repo 8.0.0-alpha.8 → 8.0.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/README.md +46 -21
- package/__index__.ts +1 -1
- package/package.json +15 -15
- package/support/bin/exec.ts +139 -35
- package/support/bin/package-manager.ts +89 -21
- package/support/cli.repo_exec.ts +20 -16
- package/support/cli.repo_list.ts +12 -10
- package/support/cli.repo_publish.ts +56 -15
- package/support/cli.repo_version-sync.ts +7 -3
- package/support/cli.repo_version.ts +12 -9
package/README.md
CHANGED
|
@@ -13,21 +13,27 @@ npm install @travetto/repo
|
|
|
13
13
|
yarn add @travetto/repo
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
The repo module aims to provide concise monorepo based tools.
|
|
16
|
+
The repo module aims to provide concise monorepo based tools. The monorepo support within the [Travetto](https://travetto.dev) framework, is backed by the built in functionality of [Npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)/[Yarn](https://yarnpkg.com). This module is not a requirement for monorepo support, but provides some quality of life improvements for:
|
|
17
17
|
* Versioning releases
|
|
18
18
|
* Publishing releases
|
|
19
19
|
* Listing local modules
|
|
20
20
|
* Running commands on all workspace modules
|
|
21
21
|
|
|
22
|
-
## CLI -
|
|
23
|
-
The versioning operation will find all the changed modules (and the modules that depend on the changed), and will update the versions in accordance with the user preferences.
|
|
22
|
+
## CLI - repo:version
|
|
23
|
+
The versioning operation will find all the changed modules (and the modules that depend on the changed), and will update the versions in accordance with the user preferences. The versioning logic is backed by [Npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)/[Yarn](https://yarnpkg.com)'s versioning functionality and so it is identical to using the tool manually. The determination of what has or hasn't changed is relative to the last versioning commit.
|
|
24
24
|
|
|
25
|
-
**Terminal:
|
|
25
|
+
**Terminal: Help for repo:version**
|
|
26
26
|
```bash
|
|
27
27
|
$ trv repo:version --help
|
|
28
28
|
|
|
29
29
|
Usage: repo:version [options] <level:major|minor|patch|premajor|preminor|prepatch|prerelease> [prefix:string]
|
|
30
30
|
|
|
31
|
+
Description:
|
|
32
|
+
Bump workspace module versions and optionally commit/tag release metadata.
|
|
33
|
+
|
|
34
|
+
Supports changed/all/direct module targeting and synchronizes dependency
|
|
35
|
+
versions after the version bump operation completes.
|
|
36
|
+
|
|
31
37
|
Options:
|
|
32
38
|
--mode <all|changed|direct> The mode for versioning
|
|
33
39
|
-f, --force Force operation, even in a dirty workspace (default: false)
|
|
@@ -37,7 +43,7 @@ Options:
|
|
|
37
43
|
--help display help for command
|
|
38
44
|
```
|
|
39
45
|
|
|
40
|
-
Level is a standard semver level of: major, minor, patch or prerelease.
|
|
46
|
+
Level is a standard semver level of: major, minor, patch or prerelease. The prefix argument only applies to the prerelease and allows for determining the prerelease level. For example:
|
|
41
47
|
|
|
42
48
|
**Terminal: Cutting a release candidate**
|
|
43
49
|
```bash
|
|
@@ -56,17 +62,24 @@ Date: Thu Feb 23 17:51:37 2023 -0500
|
|
|
56
62
|
Publish @travetto/asset,@travetto/asset-web,@travetto/auth,@travetto/auth-model,@travetto/auth-web,@travetto/auth-web-jwt,@travetto/auth-web-passport,@travetto/auth-web-session,...
|
|
57
63
|
```
|
|
58
64
|
|
|
59
|
-
## CLI -
|
|
60
|
-
The publish functionality is relatively naive, but consistent.
|
|
65
|
+
## CLI - repo:publish
|
|
66
|
+
The publish functionality is relatively naive, but consistent. The code will look at all modules in the mono-repo and check the listed version against what is available in the npm registry. If the local version is newer, it is a candidate for publishing.
|
|
61
67
|
|
|
62
|
-
**Terminal:
|
|
68
|
+
**Terminal: Help for repo:publish**
|
|
63
69
|
```bash
|
|
64
70
|
$ trv repo:publish --help
|
|
65
71
|
|
|
66
72
|
Usage: repo:publish [options]
|
|
67
73
|
|
|
74
|
+
Description:
|
|
75
|
+
Publish unpublished workspace modules to the package registry.
|
|
76
|
+
|
|
77
|
+
The command performs a staleness scan first, then publishes candidates.
|
|
78
|
+
Dry-run mode is enabled by default.
|
|
79
|
+
|
|
68
80
|
Options:
|
|
69
81
|
--dry-run, --no-dry-run Dry Run? (default: true)
|
|
82
|
+
-o, --otp <string> OTP Token
|
|
70
83
|
--help display help for command
|
|
71
84
|
```
|
|
72
85
|
|
|
@@ -79,15 +92,24 @@ npx trv repo:publish --no-dry-run
|
|
|
79
92
|
|
|
80
93
|
If no modules are currently changed, then the command will indicate there is no work to do, and exit gracefully.
|
|
81
94
|
|
|
82
|
-
## CLI -
|
|
95
|
+
## CLI - repo:list
|
|
83
96
|
The listing functionality provides the ability to get the workspace modules in the following formats:
|
|
97
|
+
* `list` - Standard text list, each module on its own line
|
|
98
|
+
* `graph` - Modules as a digraph, mapping interdependencies
|
|
99
|
+
* `json` - Graph of modules in JSON form, with additional data (useful for quickly building a dependency graph)
|
|
84
100
|
|
|
85
|
-
**Terminal:
|
|
101
|
+
**Terminal: Help for repo:list**
|
|
86
102
|
```bash
|
|
87
103
|
$ trv repo:list --help
|
|
88
104
|
|
|
89
105
|
Usage: repo:list [options]
|
|
90
106
|
|
|
107
|
+
Description:
|
|
108
|
+
List workspace modules and their relationships.
|
|
109
|
+
|
|
110
|
+
Output can be emitted as plain list, Graphviz digraph, or JSON dependency
|
|
111
|
+
graph suitable for automation.
|
|
112
|
+
|
|
91
113
|
Options:
|
|
92
114
|
-c, --changed Only show changed modules (default: false)
|
|
93
115
|
-f, --format <graph|json|list> Output format (default: "list")
|
|
@@ -96,10 +118,6 @@ Options:
|
|
|
96
118
|
--help display help for command
|
|
97
119
|
```
|
|
98
120
|
|
|
99
|
-
* `list` - Standard text list, each module on its own line
|
|
100
|
-
* `graph` - Modules as a digraph, mapping interdependencies
|
|
101
|
-
* `json` - Graph of modules in JSON form, with additional data (useful for quickly building a dependency graph)
|
|
102
|
-
|
|
103
121
|
**Terminal: List execution of Monorepo**
|
|
104
122
|
```bash
|
|
105
123
|
$ trv repo:list
|
|
@@ -127,8 +145,9 @@ module/email
|
|
|
127
145
|
module/email-compiler
|
|
128
146
|
module/email-inky
|
|
129
147
|
module/email-nodemailer
|
|
130
|
-
module/eslint
|
|
131
148
|
module/image
|
|
149
|
+
module/lint
|
|
150
|
+
module/llm-support
|
|
132
151
|
module/log
|
|
133
152
|
module/manifest
|
|
134
153
|
module/model
|
|
@@ -136,6 +155,7 @@ module/model-dynamodb
|
|
|
136
155
|
module/model-elasticsearch
|
|
137
156
|
module/model-file
|
|
138
157
|
module/model-firestore
|
|
158
|
+
module/model-indexed
|
|
139
159
|
module/model-memory
|
|
140
160
|
module/model-mongo
|
|
141
161
|
module/model-mysql
|
|
@@ -151,7 +171,6 @@ module/pack
|
|
|
151
171
|
module/registry
|
|
152
172
|
module/repo
|
|
153
173
|
module/runtime
|
|
154
|
-
module/scaffold
|
|
155
174
|
module/schema
|
|
156
175
|
module/schema-faker
|
|
157
176
|
module/terminal
|
|
@@ -167,15 +186,20 @@ module/worker
|
|
|
167
186
|
related/todo-app
|
|
168
187
|
```
|
|
169
188
|
|
|
170
|
-
## CLI -
|
|
171
|
-
The exec command allows for running commands on all modules, or just changed modules.
|
|
189
|
+
## CLI - repo:exec
|
|
172
190
|
|
|
173
|
-
**Terminal:
|
|
191
|
+
**Terminal: Help for repo:exec**
|
|
174
192
|
```bash
|
|
175
193
|
$ trv repo:exec --help
|
|
176
194
|
|
|
177
195
|
Usage: repo:exec [options] <cmd:string> [args...:string]
|
|
178
196
|
|
|
197
|
+
Description:
|
|
198
|
+
Execute a shell command across workspace modules.
|
|
199
|
+
|
|
200
|
+
Supports running for all modules or only changed modules, with optional
|
|
201
|
+
concurrency and output prefixing controls.
|
|
202
|
+
|
|
179
203
|
Options:
|
|
180
204
|
-c, --changed Only changed modules (default: false)
|
|
181
205
|
-w, --workers <number> Number of concurrent workers (default: 9)
|
|
@@ -213,8 +237,9 @@ global-test/model_auth-session <workspace-root>/global-test/model_auth-session
|
|
|
213
237
|
module/email-compiler <workspace-root>/module/email-compiler
|
|
214
238
|
module/email-inky <workspace-root>/module/email-inky
|
|
215
239
|
module/email-nodemailer <workspace-root>/module/email-nodemailer
|
|
216
|
-
module/eslint <workspace-root>/module/eslint
|
|
217
240
|
module/image <workspace-root>/module/image
|
|
241
|
+
module/lint <workspace-root>/module/lint
|
|
242
|
+
module/llm-support <workspace-root>/module/llm-support
|
|
218
243
|
module/log <workspace-root>/module/log
|
|
219
244
|
module/manifest <workspace-root>/module/manifest
|
|
220
245
|
module/model <workspace-root>/module/model
|
|
@@ -222,6 +247,7 @@ global-test/model_auth-session <workspace-root>/global-test/model_auth-session
|
|
|
222
247
|
module/model-elasticsearch <workspace-root>/module/model-elasticsearch
|
|
223
248
|
module/model-file <workspace-root>/module/model-file
|
|
224
249
|
module/model-firestore <workspace-root>/module/model-firestore
|
|
250
|
+
module/model-indexed <workspace-root>/module/model-indexed
|
|
225
251
|
module/model-memory <workspace-root>/module/model-memory
|
|
226
252
|
module/model-mongo <workspace-root>/module/model-mongo
|
|
227
253
|
module/model-mysql <workspace-root>/module/model-mysql
|
|
@@ -237,7 +263,6 @@ global-test/model_auth-session <workspace-root>/global-test/model_auth-session
|
|
|
237
263
|
module/registry <workspace-root>/module/registry
|
|
238
264
|
module/repo .
|
|
239
265
|
module/runtime <workspace-root>/module/runtime
|
|
240
|
-
module/scaffold <workspace-root>/module/scaffold
|
|
241
266
|
module/schema <workspace-root>/module/schema
|
|
242
267
|
module/schema-faker <workspace-root>/module/schema-faker
|
|
243
268
|
module/terminal <workspace-root>/module/terminal
|
package/__index__.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './support/bin/exec.ts';
|
|
2
|
-
export * from './support/bin/package-manager.ts';
|
|
2
|
+
export * from './support/bin/package-manager.ts';
|
package/package.json
CHANGED
|
@@ -1,32 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/repo",
|
|
3
|
-
"version": "8.0.0
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "8.0.0",
|
|
5
4
|
"description": "Monorepo utilities",
|
|
6
5
|
"keywords": [
|
|
6
|
+
"monorepo",
|
|
7
7
|
"travetto",
|
|
8
|
-
"typescript"
|
|
9
|
-
"monorepo"
|
|
8
|
+
"typescript"
|
|
10
9
|
],
|
|
11
|
-
"main": "__index__.ts",
|
|
12
10
|
"homepage": "https://travetto.io",
|
|
13
11
|
"license": "MIT",
|
|
14
12
|
"author": {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
13
|
+
"name": "Travetto Framework",
|
|
14
|
+
"email": "travetto.framework@gmail.com"
|
|
17
15
|
},
|
|
18
|
-
"files": [
|
|
19
|
-
"support"
|
|
20
|
-
],
|
|
21
16
|
"repository": {
|
|
22
17
|
"url": "git+https://github.com/travetto/travetto.git",
|
|
23
18
|
"directory": "module/repo"
|
|
24
19
|
},
|
|
20
|
+
"files": [
|
|
21
|
+
"support"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "__index__.ts",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
25
28
|
"dependencies": {
|
|
26
|
-
"@travetto/worker": "^8.0.0
|
|
29
|
+
"@travetto/worker": "^8.0.0"
|
|
27
30
|
},
|
|
28
31
|
"peerDependencies": {
|
|
29
|
-
"@travetto/cli": "^8.0.0
|
|
32
|
+
"@travetto/cli": "^8.0.0"
|
|
30
33
|
},
|
|
31
34
|
"peerDependenciesMeta": {
|
|
32
35
|
"@travetto/cli": {
|
|
@@ -35,8 +38,5 @@
|
|
|
35
38
|
},
|
|
36
39
|
"travetto": {
|
|
37
40
|
"displayName": "Repo"
|
|
38
|
-
},
|
|
39
|
-
"publishConfig": {
|
|
40
|
-
"access": "public"
|
|
41
41
|
}
|
|
42
42
|
}
|
package/support/bin/exec.ts
CHANGED
|
@@ -1,27 +1,80 @@
|
|
|
1
1
|
import type { ChildProcess } from 'node:child_process';
|
|
2
2
|
|
|
3
|
-
import { type ExecutionResult, Env, Util, ExecUtil, castTo, CodecUtil } from '@travetto/runtime';
|
|
4
3
|
import { CliModuleUtil } from '@travetto/cli';
|
|
5
4
|
import type { IndexedModule } from '@travetto/manifest';
|
|
6
|
-
import {
|
|
5
|
+
import { AsyncQueue, CodecUtil, Env, ExecUtil, type ExecutionResult, Runtime, RuntimeIndex, Util } from '@travetto/runtime';
|
|
6
|
+
import { type ProgressEvent, StyleUtil, Terminal, TerminalUtil } from '@travetto/terminal';
|
|
7
7
|
import { WorkPool } from '@travetto/worker';
|
|
8
8
|
|
|
9
|
-
const COLORS =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
const COLORS = [
|
|
10
|
+
...([
|
|
11
|
+
'#8787ff',
|
|
12
|
+
'#87afff',
|
|
13
|
+
'#87d7ff',
|
|
14
|
+
'#87ff87',
|
|
15
|
+
'#87ffaf',
|
|
16
|
+
'#87ffd7',
|
|
17
|
+
'#87ffff',
|
|
18
|
+
'#af87ff',
|
|
19
|
+
'#afafd7',
|
|
20
|
+
'#afafff',
|
|
21
|
+
'#afd7af',
|
|
22
|
+
'#afd7d7',
|
|
23
|
+
'#afd7ff',
|
|
24
|
+
'#afff87',
|
|
25
|
+
'#afffaf',
|
|
26
|
+
'#afffd7',
|
|
27
|
+
'#afffff',
|
|
28
|
+
'#d787ff',
|
|
29
|
+
'#d7afaf',
|
|
30
|
+
'#d7afd7',
|
|
31
|
+
'#d7afff',
|
|
32
|
+
'#d7d7af',
|
|
33
|
+
'#d7d7d7',
|
|
34
|
+
'#d7d7ff',
|
|
35
|
+
'#d7ff87',
|
|
36
|
+
'#d7ffaf',
|
|
37
|
+
'#d7ffd7',
|
|
38
|
+
'#d7ffff',
|
|
39
|
+
'#ff8787',
|
|
40
|
+
'#ff87af',
|
|
41
|
+
'#ff87d7',
|
|
42
|
+
'#ff87ff',
|
|
43
|
+
'#ffaf87',
|
|
44
|
+
'#ffafaf',
|
|
45
|
+
'#ffafd7',
|
|
46
|
+
'#ffafff',
|
|
47
|
+
'#ffd787',
|
|
48
|
+
'#ffd7af',
|
|
49
|
+
'#ffd7d7',
|
|
50
|
+
'#ffd7ff',
|
|
51
|
+
'#ffff87',
|
|
52
|
+
'#ffffaf',
|
|
53
|
+
'#ffffd7',
|
|
54
|
+
'#ffffff',
|
|
55
|
+
'#bcbcbc',
|
|
56
|
+
'#c6c6c6',
|
|
57
|
+
'#d0d0d0',
|
|
58
|
+
'#dadada',
|
|
59
|
+
'#e4e4e4',
|
|
60
|
+
'#eeeeee'
|
|
61
|
+
] as const)
|
|
62
|
+
]
|
|
63
|
+
.toSorted(() => (Math.random() < 0.5 ? -1 : 1))
|
|
64
|
+
.map(color => StyleUtil.getStyle(color));
|
|
65
|
+
|
|
66
|
+
type ModuleRunConfig = {
|
|
17
67
|
progressMessage?: (module: IndexedModule | undefined) => string;
|
|
18
68
|
filter?: (module: IndexedModule) => boolean | Promise<boolean>;
|
|
19
|
-
|
|
69
|
+
progressListKey?: (module: IndexedModule) => string;
|
|
70
|
+
isSuccess?: (result: ExecutionResult) => boolean;
|
|
71
|
+
showProgressList?: boolean;
|
|
20
72
|
workerCount?: number;
|
|
21
73
|
prefixOutput?: boolean;
|
|
22
74
|
showStdout?: boolean;
|
|
23
75
|
showStderr?: boolean;
|
|
24
76
|
stableOutput?: boolean;
|
|
77
|
+
includeMonorepoRoot?: boolean;
|
|
25
78
|
};
|
|
26
79
|
|
|
27
80
|
const colorize = (value: string, idx: number): string => COLORS[idx % COLORS.length](value);
|
|
@@ -30,7 +83,6 @@ const colorize = (value: string, idx: number): string => COLORS[idx % COLORS.len
|
|
|
30
83
|
* Tools for running commands across all modules of the monorepo
|
|
31
84
|
*/
|
|
32
85
|
export class RepoExecUtil {
|
|
33
|
-
|
|
34
86
|
/**
|
|
35
87
|
* Build equal sized prefix labels for outputting
|
|
36
88
|
* @param modules
|
|
@@ -45,56 +97,108 @@ export class RepoExecUtil {
|
|
|
45
97
|
/**
|
|
46
98
|
* Run on all modules
|
|
47
99
|
*/
|
|
48
|
-
static async execOnModules
|
|
100
|
+
static async execOnModules(
|
|
49
101
|
mode: 'all' | 'workspace' | 'changed',
|
|
50
102
|
operation: (module: IndexedModule) => ChildProcess,
|
|
51
|
-
config: ModuleRunConfig
|
|
52
|
-
): Promise<Map<IndexedModule,
|
|
53
|
-
|
|
103
|
+
config: ModuleRunConfig = {}
|
|
104
|
+
): Promise<Map<IndexedModule, ExecutionResult>> {
|
|
54
105
|
config.showStdout = config.showStdout ?? (Env.DEBUG.isSet && !Env.DEBUG.isFalse);
|
|
55
106
|
config.showStderr = config.showStderr ?? true;
|
|
56
|
-
const transform = config.transformResult ?? ((module, result): T => castTo(result));
|
|
57
107
|
|
|
58
108
|
const workerCount = config.workerCount ?? WorkPool.DEFAULT_SIZE;
|
|
59
109
|
|
|
60
110
|
const modules = await CliModuleUtil.findModules(mode);
|
|
61
|
-
const results = new Map<IndexedModule,
|
|
111
|
+
const results = new Map<IndexedModule, ExecutionResult>();
|
|
62
112
|
const processes = new Map<IndexedModule, ChildProcess>();
|
|
63
113
|
|
|
64
114
|
const prefixes = config.prefixOutput !== false ? this.#buildPrefixes(modules) : {};
|
|
65
115
|
const stdoutTerm = new Terminal(process.stdout);
|
|
66
116
|
const stderrTerm = new Terminal(process.stderr);
|
|
67
117
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
118
|
+
if (Runtime.workspace.mono && config.includeMonorepoRoot && !modules.some(module => module.name === Runtime.workspace.name)) {
|
|
119
|
+
modules.push(RuntimeIndex.getModule(Runtime.workspace.name)!);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const active = new Set<string>();
|
|
123
|
+
const activeItems = new AsyncQueue<{ idx: number; text: string }>();
|
|
124
|
+
const listHeader = ['', 'Modules in progress', '-------------------'];
|
|
125
|
+
|
|
126
|
+
if (stdoutTerm.interactive && config.showProgressList) {
|
|
127
|
+
stdoutTerm.writer.writeLines(listHeader).commit();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const modulesToRun = (
|
|
131
|
+
config.filter ? await Promise.all(modules.map(async module => ((await config.filter!(module)) ? module : undefined))) : modules
|
|
132
|
+
).filter((module): module is IndexedModule => !!module);
|
|
133
|
+
|
|
134
|
+
const work = WorkPool.runStream<IndexedModule, ExecutionResult>(
|
|
135
|
+
async module => {
|
|
136
|
+
try {
|
|
71
137
|
const prefix = prefixes[module.sourceFolder] ?? '';
|
|
138
|
+
const listKey = config.progressListKey?.(module) ?? ` * ${module.name}`;
|
|
139
|
+
active.add(listKey);
|
|
140
|
+
[...active].sort().forEach((text, idx) => {
|
|
141
|
+
activeItems.add({ idx, text });
|
|
142
|
+
});
|
|
143
|
+
|
|
72
144
|
const subProcess = operation(module);
|
|
73
145
|
processes.set(module, subProcess);
|
|
74
146
|
|
|
75
147
|
if (config.showStdout && subProcess.stdout) {
|
|
76
|
-
CodecUtil.readLines(subProcess.stdout, line =>
|
|
77
|
-
stdoutTerm.writer.writeLine(`${prefix}${line.trimEnd()}`).commit()
|
|
78
|
-
);
|
|
148
|
+
CodecUtil.readLines(subProcess.stdout, line => stdoutTerm.writer.writeLine(`${prefix}${line.trimEnd()}`).commit());
|
|
79
149
|
}
|
|
80
150
|
if (config.showStderr && subProcess.stderr) {
|
|
81
|
-
CodecUtil.readLines(subProcess.stderr, line =>
|
|
82
|
-
stderrTerm.writer.writeLine(`${prefix}${line.trimEnd()}`).commit()
|
|
83
|
-
);
|
|
151
|
+
CodecUtil.readLines(subProcess.stderr, line => stderrTerm.writer.writeLine(`${prefix}${line.trimEnd()}`).commit());
|
|
84
152
|
}
|
|
85
153
|
|
|
86
154
|
const result = await ExecUtil.getResult(subProcess, { catch: true });
|
|
87
|
-
|
|
88
|
-
|
|
155
|
+
|
|
156
|
+
active.delete(listKey);
|
|
157
|
+
[...active].sort().forEach((text, idx) => {
|
|
158
|
+
activeItems.add({ idx, text });
|
|
159
|
+
});
|
|
160
|
+
for (let j = active.size; j < workerCount; j += 1) {
|
|
161
|
+
activeItems.add({ idx: j, text: '' }); // Force update to remove item if needed
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
results.set(module, result);
|
|
165
|
+
return result;
|
|
166
|
+
} finally {
|
|
167
|
+
processes.get(module!)?.kill();
|
|
89
168
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
169
|
+
},
|
|
170
|
+
modulesToRun,
|
|
171
|
+
{
|
|
172
|
+
max: workerCount,
|
|
173
|
+
min: workerCount,
|
|
174
|
+
total: modules.length,
|
|
175
|
+
isSuccess: config.isSuccess ?? ((result: ExecutionResult): boolean => result.valid)
|
|
93
176
|
}
|
|
94
|
-
|
|
177
|
+
);
|
|
95
178
|
|
|
96
179
|
if (config.progressMessage && stdoutTerm.interactive) {
|
|
97
|
-
|
|
180
|
+
if (config.showProgressList) {
|
|
181
|
+
stdoutTerm.streamList(activeItems);
|
|
182
|
+
}
|
|
183
|
+
await stdoutTerm.streamToBottom(
|
|
184
|
+
Util.mapAsyncIterable(
|
|
185
|
+
Util.mapAsyncIterable(work, (event): ProgressEvent<string> => {
|
|
186
|
+
const message =
|
|
187
|
+
config.progressMessage?.(modulesToRun[event.progress.completed]) ?? 'Completed %completed/%total (%failed failed)';
|
|
188
|
+
return { ...event.progress, value: message };
|
|
189
|
+
}),
|
|
190
|
+
TerminalUtil.progressBarUpdater(stdoutTerm, { withWaiting: true })
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
if (stdoutTerm.interactive && config.showProgressList) {
|
|
195
|
+
stdoutTerm.writer
|
|
196
|
+
.changePosition({ y: -listHeader.length, x: 0 })
|
|
197
|
+
.storePosition()
|
|
198
|
+
.writeLines(Array(listHeader.length).fill(''))
|
|
199
|
+
.restoreOnCommit()
|
|
200
|
+
.commit();
|
|
201
|
+
}
|
|
98
202
|
} else {
|
|
99
203
|
for await (const _ of work) {
|
|
100
204
|
// Ensure its all consumed
|
|
@@ -103,4 +207,4 @@ export class RepoExecUtil {
|
|
|
103
207
|
|
|
104
208
|
return results;
|
|
105
209
|
}
|
|
106
|
-
}
|
|
210
|
+
}
|
|
@@ -1,17 +1,79 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { spawn, type ChildProcess } from 'node:child_process';
|
|
1
|
+
import { type ChildProcess, spawn } from 'node:child_process';
|
|
3
2
|
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
|
|
5
|
-
import { JSONUtil, type ExecutionResult, Runtime } from '@travetto/runtime';
|
|
6
|
-
import { type IndexedModule, type Package, PackageUtil } from '@travetto/manifest';
|
|
7
5
|
import { CliModuleUtil } from '@travetto/cli';
|
|
6
|
+
import { type IndexedModule, type Package, PackageUtil } from '@travetto/manifest';
|
|
7
|
+
import { ExecUtil, type ExecutionResult, JSONUtil, Runtime } from '@travetto/runtime';
|
|
8
|
+
import { TerminalUtil } from '@travetto/terminal';
|
|
8
9
|
|
|
9
10
|
export type SemverLevel = 'minor' | 'patch' | 'major' | 'prerelease' | 'premajor' | 'preminor' | 'prepatch';
|
|
10
11
|
|
|
12
|
+
/* cspell:words EOTP EPUBLISHCONFLICT ENEEDAUTH */
|
|
13
|
+
|
|
11
14
|
/**
|
|
12
15
|
* Utilities for working with package managers
|
|
13
16
|
*/
|
|
14
17
|
export class PackageManager {
|
|
18
|
+
/**
|
|
19
|
+
* Check if npm login is needed
|
|
20
|
+
*/
|
|
21
|
+
static async needsLogin(): Promise<boolean> {
|
|
22
|
+
const result = await ExecUtil.getResult(spawn('npm', ['whoami']), { catch: true });
|
|
23
|
+
return !result.valid;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if OTP is needed for publishing
|
|
28
|
+
*/
|
|
29
|
+
static async needsOtp(): Promise<boolean> {
|
|
30
|
+
try {
|
|
31
|
+
const result = await ExecUtil.getResult(spawn('npm', ['profile', 'get', '--json']), { catch: true });
|
|
32
|
+
if (result.valid) {
|
|
33
|
+
const info = JSONUtil.fromUTF8<{ tfa?: { mode?: string } }>(result.stdout);
|
|
34
|
+
return !!info?.tfa?.mode;
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
// Ignore errors checking for OTP profile
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Request an OTP token if required
|
|
44
|
+
*/
|
|
45
|
+
static async requestOtp(): Promise<string | undefined> {
|
|
46
|
+
if (TerminalUtil.isInteractive()) {
|
|
47
|
+
console.log(
|
|
48
|
+
[
|
|
49
|
+
'OTP token is required for publishing. Please provide an OTP token to proceed with publishing.',
|
|
50
|
+
'This value will not be stored, and is only used for the current publish operation.'
|
|
51
|
+
].join(' ')
|
|
52
|
+
);
|
|
53
|
+
return await TerminalUtil.prompt('Enter OTP token: ');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Classify publish error from execution result
|
|
59
|
+
*/
|
|
60
|
+
static classifyPublishError(result: ExecutionResult): string {
|
|
61
|
+
const errorText = ExecUtil.toString(result, 'any');
|
|
62
|
+
if (/EOTP|one-time password|two-factor|OTP/i.test(errorText)) {
|
|
63
|
+
return 'Two-factor authentication (OTP) failed or was missing.';
|
|
64
|
+
}
|
|
65
|
+
if (/EPUBLISHCONFLICT|E403(.){,100}previously published|cannot publish over/i.test(errorText)) {
|
|
66
|
+
return 'Version conflict: This version has already been published to the registry.';
|
|
67
|
+
}
|
|
68
|
+
if (/ENEEDAUTH|E401|login|unauthorized/i.test(errorText)) {
|
|
69
|
+
return 'Authentication failed: Please log in or check your registry credentials.';
|
|
70
|
+
}
|
|
71
|
+
if (/E403|permission|not allowed/i.test(errorText)) {
|
|
72
|
+
return 'Permission denied: You do not have access to publish this package.';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return errorText || 'Unknown publishing error.';
|
|
76
|
+
}
|
|
15
77
|
|
|
16
78
|
/**
|
|
17
79
|
* Is a module already published
|
|
@@ -21,7 +83,9 @@ export class PackageManager {
|
|
|
21
83
|
switch (Runtime.workspace.manager) {
|
|
22
84
|
case 'npm':
|
|
23
85
|
case 'yarn':
|
|
24
|
-
case 'pnpm':
|
|
86
|
+
case 'pnpm':
|
|
87
|
+
args = ['info', `${module.name}@${module.version}`, '--json'];
|
|
88
|
+
break;
|
|
25
89
|
}
|
|
26
90
|
return spawn(Runtime.workspace.manager, args, { cwd: module.sourceFolder });
|
|
27
91
|
}
|
|
@@ -29,14 +93,17 @@ export class PackageManager {
|
|
|
29
93
|
/**
|
|
30
94
|
* Validate published result
|
|
31
95
|
*/
|
|
32
|
-
static validatePublishedResult(result: ExecutionResult
|
|
33
|
-
|
|
34
|
-
|
|
96
|
+
static validatePublishedResult(result: ExecutionResult): boolean {
|
|
97
|
+
const stderr = ExecUtil.toString(result, 'stderr');
|
|
98
|
+
if (!result.valid && !stderr.includes('E404')) {
|
|
99
|
+
throw new Error(stderr);
|
|
35
100
|
}
|
|
36
101
|
|
|
102
|
+
const stdout = ExecUtil.toString(result, 'stdout');
|
|
37
103
|
type PackageInfo = { dist?: { integrity?: string } };
|
|
38
|
-
let parsed = JSONUtil.fromUTF8<PackageInfo | { data: PackageInfo }>(
|
|
39
|
-
if ('data' in parsed) {
|
|
104
|
+
let parsed = JSONUtil.fromUTF8<PackageInfo | { data: PackageInfo }>(stdout || '{}');
|
|
105
|
+
if ('data' in parsed) {
|
|
106
|
+
// Yarn support
|
|
40
107
|
parsed = parsed.data;
|
|
41
108
|
}
|
|
42
109
|
return parsed.dist?.integrity !== undefined;
|
|
@@ -51,7 +118,9 @@ export class PackageManager {
|
|
|
51
118
|
switch (Runtime.workspace.manager) {
|
|
52
119
|
case 'npm':
|
|
53
120
|
case 'yarn':
|
|
54
|
-
case 'pnpm':
|
|
121
|
+
case 'pnpm':
|
|
122
|
+
args = ['version', '--no-workspaces-update', level, ...(preid ? ['--preid', preid] : [])];
|
|
123
|
+
break;
|
|
55
124
|
}
|
|
56
125
|
return spawn(Runtime.workspace.manager, [...args, ...moduleArgs], { cwd: Runtime.workspace.path, stdio: 'inherit' });
|
|
57
126
|
}
|
|
@@ -64,7 +133,9 @@ export class PackageManager {
|
|
|
64
133
|
switch (Runtime.workspace.manager) {
|
|
65
134
|
case 'npm':
|
|
66
135
|
case 'yarn':
|
|
67
|
-
case 'pnpm':
|
|
136
|
+
case 'pnpm':
|
|
137
|
+
args = ['pack', '--dry-run'];
|
|
138
|
+
break;
|
|
68
139
|
}
|
|
69
140
|
return spawn(Runtime.workspace.manager, args, { cwd: module.sourcePath });
|
|
70
141
|
}
|
|
@@ -72,7 +143,7 @@ export class PackageManager {
|
|
|
72
143
|
/**
|
|
73
144
|
* Publish a module
|
|
74
145
|
*/
|
|
75
|
-
static publish(module: IndexedModule, dryRun: boolean | undefined): ChildProcess {
|
|
146
|
+
static publish(module: IndexedModule, dryRun: boolean | undefined, otp?: string): ChildProcess {
|
|
76
147
|
if (dryRun) {
|
|
77
148
|
return this.dryRunPackaging(module);
|
|
78
149
|
}
|
|
@@ -82,7 +153,9 @@ export class PackageManager {
|
|
|
82
153
|
switch (Runtime.workspace.manager) {
|
|
83
154
|
case 'npm':
|
|
84
155
|
case 'yarn':
|
|
85
|
-
case 'pnpm':
|
|
156
|
+
case 'pnpm':
|
|
157
|
+
args = ['publish', '--tag', versionTag, '--access', 'public', ...(otp ? ['--otp', otp] : [])];
|
|
158
|
+
break;
|
|
86
159
|
}
|
|
87
160
|
return spawn(Runtime.workspace.manager, args, { cwd: module.sourcePath });
|
|
88
161
|
}
|
|
@@ -112,12 +185,7 @@ export class PackageManager {
|
|
|
112
185
|
});
|
|
113
186
|
|
|
114
187
|
for (const { pkg } of packages) {
|
|
115
|
-
for (const group of [
|
|
116
|
-
pkg.dependencies ?? {},
|
|
117
|
-
pkg.devDependencies ?? {},
|
|
118
|
-
pkg.optionalDependencies ?? {},
|
|
119
|
-
pkg.peerDependencies ?? {}
|
|
120
|
-
]) {
|
|
188
|
+
for (const group of [pkg.dependencies ?? {}, pkg.devDependencies ?? {}, pkg.optionalDependencies ?? {}, pkg.peerDependencies ?? {}]) {
|
|
121
189
|
for (const [module, ver] of Object.entries(versions)) {
|
|
122
190
|
if (module in group && !/^[*]|(file:.*)$/.test(group[module])) {
|
|
123
191
|
group[module] = ver;
|
|
@@ -132,4 +200,4 @@ export class PackageManager {
|
|
|
132
200
|
|
|
133
201
|
return versions;
|
|
134
202
|
}
|
|
135
|
-
}
|
|
203
|
+
}
|
package/support/cli.repo_exec.ts
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
|
|
3
|
-
import { CliCommand,
|
|
4
|
-
import { WorkPool } from '@travetto/worker';
|
|
3
|
+
import { CliCommand, type CliCommandShape, CliParseUtil } from '@travetto/cli';
|
|
5
4
|
import { Env } from '@travetto/runtime';
|
|
6
5
|
import { Max, Min } from '@travetto/schema';
|
|
6
|
+
import { WorkPool } from '@travetto/worker';
|
|
7
7
|
|
|
8
8
|
import { RepoExecUtil } from './bin/exec.ts';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Execute a shell command across workspace modules.
|
|
12
|
+
*
|
|
13
|
+
* Supports running for all modules or only changed modules, with optional
|
|
14
|
+
* concurrency and output prefixing controls.
|
|
12
15
|
*/
|
|
13
16
|
@CliCommand()
|
|
14
17
|
export class RepoExecCommand implements CliCommandShape {
|
|
15
|
-
|
|
16
18
|
/** Only changed modules */
|
|
17
19
|
changed = false;
|
|
18
20
|
|
|
19
21
|
/** Number of concurrent workers */
|
|
20
|
-
@Min(1)
|
|
22
|
+
@Min(1)
|
|
23
|
+
@Max(WorkPool.MAX_SIZE)
|
|
21
24
|
workers = WorkPool.DEFAULT_SIZE;
|
|
22
25
|
|
|
23
26
|
/** Prefix output by folder */
|
|
@@ -32,23 +35,24 @@ export class RepoExecCommand implements CliCommandShape {
|
|
|
32
35
|
|
|
33
36
|
async main(cmd: string, args: string[] = []): Promise<void> {
|
|
34
37
|
const parsed = CliParseUtil.getState(this);
|
|
35
|
-
const finalArgs = [...args, ...parsed?.unknown ?? []];
|
|
38
|
+
const finalArgs = [...args, ...(parsed?.unknown ?? [])];
|
|
36
39
|
|
|
37
40
|
await RepoExecUtil.execOnModules(
|
|
38
41
|
this.changed ? 'changed' : 'workspace',
|
|
39
|
-
module =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
module =>
|
|
43
|
+
spawn(cmd, finalArgs, {
|
|
44
|
+
cwd: module.sourceFolder,
|
|
45
|
+
env: {
|
|
46
|
+
...process.env,
|
|
47
|
+
...Env.TRV_MODULE.export(module.name),
|
|
48
|
+
...Env.TRV_MANIFEST.export(undefined)
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
47
51
|
{
|
|
48
|
-
progressMessage: module => `Running '${cmd} ${finalArgs.join(' ')}' [%
|
|
52
|
+
progressMessage: module => `Running '${cmd} ${finalArgs.join(' ')}' [%completed/%total] ${module?.sourceFolder ?? ''}`,
|
|
49
53
|
showStdout: this.showStdout,
|
|
50
54
|
prefixOutput: this.prefixOutput,
|
|
51
|
-
workerCount: this.workers
|
|
55
|
+
workerCount: this.workers
|
|
52
56
|
}
|
|
53
57
|
);
|
|
54
58
|
}
|
package/support/cli.repo_list.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { type CliCommandShape,
|
|
1
|
+
import { CliCommand, type CliCommandShape, CliModuleUtil } from '@travetto/cli';
|
|
2
2
|
import { JSONUtil, Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
3
3
|
|
|
4
4
|
const write = (line: string): Promise<void> => new Promise(resolve => process.stdout.write(`${line}\n`, () => resolve()));
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* List workspace modules and their relationships.
|
|
8
|
+
*
|
|
9
|
+
* Output can be emitted as plain list, Graphviz digraph, or JSON dependency
|
|
10
|
+
* graph suitable for automation.
|
|
8
11
|
*/
|
|
9
12
|
@CliCommand()
|
|
10
13
|
export class ListModuleCommand implements CliCommandShape {
|
|
11
|
-
|
|
12
14
|
/** Only show changed modules */
|
|
13
15
|
changed = false;
|
|
14
16
|
|
|
@@ -28,7 +30,6 @@ export class ListModuleCommand implements CliCommandShape {
|
|
|
28
30
|
toHash?: string;
|
|
29
31
|
|
|
30
32
|
async main(): Promise<void> {
|
|
31
|
-
|
|
32
33
|
const modules = await CliModuleUtil.findModules(this.changed ? 'changed' : 'workspace', this.fromHash, this.toHash);
|
|
33
34
|
switch (this.format) {
|
|
34
35
|
case 'list': {
|
|
@@ -39,11 +40,12 @@ export class ListModuleCommand implements CliCommandShape {
|
|
|
39
40
|
}
|
|
40
41
|
case 'json': {
|
|
41
42
|
const outputMap = CliModuleUtil.getDependencyGraph(modules);
|
|
42
|
-
await write(
|
|
43
|
-
|
|
44
|
-
.map(([name, children]) => ({ name, children, workspace: RuntimeIndex.getModule(name)?.workspace })),
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
await write(
|
|
44
|
+
JSONUtil.toUTF8(
|
|
45
|
+
Object.entries(outputMap).map(([name, children]) => ({ name, children, workspace: RuntimeIndex.getModule(name)?.workspace })),
|
|
46
|
+
{ indent: 2 }
|
|
47
|
+
)
|
|
48
|
+
);
|
|
47
49
|
break;
|
|
48
50
|
}
|
|
49
51
|
case 'graph': {
|
|
@@ -60,4 +62,4 @@ export class ListModuleCommand implements CliCommandShape {
|
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
|
-
}
|
|
65
|
+
}
|
|
@@ -1,36 +1,77 @@
|
|
|
1
|
-
import { type CliCommandShape,
|
|
1
|
+
import { CliCommand, type CliCommandShape, cliTpl } from '@travetto/cli';
|
|
2
|
+
import { RuntimeError } from '@travetto/runtime';
|
|
2
3
|
|
|
3
|
-
import { PackageManager } from './bin/package-manager.ts';
|
|
4
4
|
import { RepoExecUtil } from './bin/exec.ts';
|
|
5
|
+
import { PackageManager } from './bin/package-manager.ts';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* Publish
|
|
8
|
+
* Publish unpublished workspace modules to the package registry.
|
|
9
|
+
*
|
|
10
|
+
* The command performs a staleness scan first, then publishes candidates.
|
|
11
|
+
* Dry-run mode is enabled by default.
|
|
8
12
|
*/
|
|
9
13
|
@CliCommand()
|
|
10
14
|
export class RepoPublishCommand implements CliCommandShape {
|
|
11
|
-
|
|
12
15
|
/** Dry Run? */
|
|
13
16
|
dryRun = true;
|
|
14
17
|
|
|
18
|
+
/** OTP Token */
|
|
19
|
+
otp?: string;
|
|
20
|
+
|
|
15
21
|
async main(): Promise<void> {
|
|
16
22
|
const published = await RepoExecUtil.execOnModules('workspace', module => PackageManager.isPublished(module), {
|
|
17
23
|
filter: module => !!module.workspace && !module.internal,
|
|
18
|
-
progressMessage:
|
|
24
|
+
progressMessage: module => `Checking published [%completed/%total] -- ${module?.name ?? ''}`,
|
|
19
25
|
showStderr: false,
|
|
20
|
-
|
|
26
|
+
showProgressList: true,
|
|
27
|
+
isSuccess: () => true
|
|
21
28
|
});
|
|
22
29
|
|
|
30
|
+
const unpublished = [...published.entries()]
|
|
31
|
+
.filter(([, result]) => !PackageManager.validatePublishedResult(result))
|
|
32
|
+
.map(([module]) => module);
|
|
33
|
+
|
|
23
34
|
if (this.dryRun) {
|
|
24
|
-
console.log(
|
|
35
|
+
console.log(
|
|
36
|
+
'Unpublished modules',
|
|
37
|
+
unpublished.map(module => module.sourceFolder)
|
|
38
|
+
);
|
|
25
39
|
}
|
|
26
40
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
let otp = this.otp;
|
|
42
|
+
if (unpublished.length > 0 && !this.dryRun) {
|
|
43
|
+
if (await PackageManager.needsLogin()) {
|
|
44
|
+
throw new RuntimeError('NPM login is required to publish. Please run "npm login" to authenticate.');
|
|
45
|
+
}
|
|
46
|
+
if (!otp && (await PackageManager.needsOtp())) {
|
|
47
|
+
otp = await PackageManager.requestOtp();
|
|
33
48
|
}
|
|
34
|
-
|
|
49
|
+
if (!otp) {
|
|
50
|
+
throw new RuntimeError('OTP token is required for publishing, but was not provided.');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const unpublishedSet = new Set(unpublished);
|
|
55
|
+
|
|
56
|
+
const results = await RepoExecUtil.execOnModules('workspace', module => PackageManager.publish(module, this.dryRun, otp), {
|
|
57
|
+
progressMessage: module => `Publishing [%completed/%total] -- ${module?.name ?? ''} (Failed %failed)`,
|
|
58
|
+
showStdout: false,
|
|
59
|
+
showStderr: false,
|
|
60
|
+
showProgressList: true,
|
|
61
|
+
filter: module => unpublishedSet.has(module)
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const failures = [...results.entries()].filter(([, result]) => !result.valid);
|
|
65
|
+
if (failures.length > 0) {
|
|
66
|
+
console.error(cliTpl`\n${{ title: 'Failed to publish the following modules:' }}`);
|
|
67
|
+
console.error(cliTpl`${'-'.repeat(50)}`);
|
|
68
|
+
const nameWidth = Math.max(...failures.map(([module]) => module.name.length));
|
|
69
|
+
for (const [module, result] of failures) {
|
|
70
|
+
console.error(
|
|
71
|
+
cliTpl`${{ identifier: module.name.padStart(nameWidth, ' ') }}: ${{ description: PackageManager.classifyPublishError(result) }}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
}
|
|
35
76
|
}
|
|
36
|
-
}
|
|
77
|
+
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import { type CliCommandShape
|
|
1
|
+
import { CliCommand, type CliCommandShape } from '@travetto/cli';
|
|
2
|
+
|
|
2
3
|
import { PackageManager } from './bin/package-manager.ts';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
+
* Synchronize package versions and dependency ranges across the monorepo.
|
|
7
|
+
*
|
|
8
|
+
* Ensures package metadata reflects workspace version policy before publishing
|
|
9
|
+
* or release operations.
|
|
6
10
|
*/
|
|
7
11
|
@CliCommand()
|
|
8
12
|
export class VersionSyncCommand implements CliCommandShape {
|
|
9
13
|
async main(): Promise<void> {
|
|
10
14
|
await PackageManager.synchronizeVersions();
|
|
11
15
|
}
|
|
12
|
-
}
|
|
16
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { ExecUtil, Runtime } from '@travetto/runtime';
|
|
3
|
+
import { CliCommand, type CliCommandShape, CliModuleUtil, CliScmUtil } from '@travetto/cli';
|
|
4
|
+
import { ExecUtil, Runtime, RuntimeError } from '@travetto/runtime';
|
|
5
5
|
import { Validator } from '@travetto/schema';
|
|
6
6
|
|
|
7
7
|
import { PackageManager, type SemverLevel } from './bin/package-manager.ts';
|
|
@@ -9,11 +9,14 @@ import { PackageManager, type SemverLevel } from './bin/package-manager.ts';
|
|
|
9
9
|
const CHANGE_LEVELS = new Set<SemverLevel>(['prerelease', 'patch', 'prepatch']);
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Bump workspace module versions and optionally commit/tag release metadata.
|
|
13
|
+
*
|
|
14
|
+
* Supports changed/all/direct module targeting and synchronizes dependency
|
|
15
|
+
* versions after the version bump operation completes.
|
|
13
16
|
*/
|
|
14
17
|
@CliCommand()
|
|
15
|
-
@Validator(async
|
|
16
|
-
if (!cmd.force && await CliScmUtil.isWorkspaceDirty()) {
|
|
18
|
+
@Validator(async cmd => {
|
|
19
|
+
if (!cmd.force && (await CliScmUtil.isWorkspaceDirty())) {
|
|
17
20
|
return { message: 'Cannot update versions with uncommitted changes', path: 'custom', kind: 'invalid' };
|
|
18
21
|
}
|
|
19
22
|
})
|
|
@@ -35,7 +38,7 @@ export class RepoVersionCommand implements CliCommandShape {
|
|
|
35
38
|
async main(level: SemverLevel, prefix?: string): Promise<void> {
|
|
36
39
|
const mode = this.mode ?? (CHANGE_LEVELS.has(level) ? 'changed' : 'workspace');
|
|
37
40
|
|
|
38
|
-
this.tag ??=
|
|
41
|
+
this.tag ??= level === 'minor' || level === 'major';
|
|
39
42
|
|
|
40
43
|
const allModules = await CliModuleUtil.findModules(mode === 'direct' ? 'all' : mode);
|
|
41
44
|
|
|
@@ -43,7 +46,7 @@ export class RepoVersionCommand implements CliCommandShape {
|
|
|
43
46
|
|
|
44
47
|
// Do we have valid changes?
|
|
45
48
|
if (!modules.length) {
|
|
46
|
-
throw new
|
|
49
|
+
throw new RuntimeError('No modules available for versioning');
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
await ExecUtil.getResult(PackageManager.version(modules, level, prefix));
|
|
@@ -57,7 +60,7 @@ export class RepoVersionCommand implements CliCommandShape {
|
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
// Touch package when done to trigger restart of compiler
|
|
60
|
-
await fs.utimes(Runtime.workspaceRelative('package.json'), Date
|
|
63
|
+
await fs.utimes(Runtime.workspaceRelative('package.json'), new Date(), new Date());
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
|
-
}
|
|
66
|
+
}
|