@fuzdev/fuz_gitops 0.78.1 → 0.79.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 +78 -24
- package/dist/ModulesDetail.svelte +15 -22
- package/dist/ModulesDetail.svelte.d.ts.map +1 -1
- package/dist/ModulesNav.svelte +4 -1
- package/dist/ModulesPage.svelte +1 -3
- package/dist/ModulesPage.svelte.d.ts.map +1 -1
- package/dist/PageHeader.svelte +2 -2
- package/dist/PageHeader.svelte.d.ts +0 -3
- package/dist/PageHeader.svelte.d.ts.map +1 -1
- package/dist/PullRequestsDetail.svelte +10 -9
- package/dist/PullRequestsDetail.svelte.d.ts.map +1 -1
- package/dist/PullRequestsPage.svelte +1 -3
- package/dist/PullRequestsPage.svelte.d.ts.map +1 -1
- package/dist/ReposTable.svelte +27 -48
- package/dist/ReposTable.svelte.d.ts.map +1 -1
- package/dist/ReposTree.svelte +5 -19
- package/dist/ReposTree.svelte.d.ts.map +1 -1
- package/dist/ReposTreeNav.svelte +5 -7
- package/dist/ReposTreeNav.svelte.d.ts.map +1 -1
- package/dist/TablePage.svelte +1 -3
- package/dist/TablePage.svelte.d.ts.map +1 -1
- package/dist/TreeItemPage.svelte +1 -3
- package/dist/TreeItemPage.svelte.d.ts.map +1 -1
- package/dist/TreePage.svelte +1 -3
- package/dist/TreePage.svelte.d.ts.map +1 -1
- package/dist/ci_reconcile.d.ts +6 -10
- package/dist/ci_reconcile.d.ts.map +1 -1
- package/dist/ci_reconcile.js +6 -8
- package/dist/dependency_graph.d.ts +26 -49
- package/dist/dependency_graph.d.ts.map +1 -1
- package/dist/dependency_graph.js +37 -80
- package/dist/dependency_updater.d.ts +1 -1
- package/dist/dependency_updater.d.ts.map +1 -1
- package/dist/dependency_updater.js +6 -1
- package/dist/fetch_repo_data.d.ts +34 -8
- package/dist/fetch_repo_data.d.ts.map +1 -1
- package/dist/fetch_repo_data.js +44 -22
- package/dist/fs_fetch_value_cache.d.ts +1 -1
- package/dist/fs_fetch_value_cache.js +1 -1
- package/dist/git_operations.d.ts +6 -49
- package/dist/git_operations.d.ts.map +1 -1
- package/dist/git_operations.js +13 -126
- package/dist/github.d.ts +17 -2
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +15 -6
- package/dist/github_helpers.d.ts.map +1 -1
- package/dist/github_helpers.js +3 -11
- package/dist/gitops_analyze.task.d.ts +24 -2
- package/dist/gitops_analyze.task.d.ts.map +1 -1
- package/dist/gitops_analyze.task.js +43 -30
- package/dist/gitops_config.d.ts +34 -79
- package/dist/gitops_config.d.ts.map +1 -1
- package/dist/gitops_config.js +76 -53
- package/dist/gitops_constants.d.ts +6 -4
- package/dist/gitops_constants.d.ts.map +1 -1
- package/dist/gitops_constants.js +6 -4
- package/dist/gitops_plan.task.d.ts +29 -3
- package/dist/gitops_plan.task.d.ts.map +1 -1
- package/dist/gitops_plan.task.js +80 -58
- package/dist/gitops_publish.task.d.ts +62 -2
- package/dist/gitops_publish.task.d.ts.map +1 -1
- package/dist/gitops_publish.task.js +187 -102
- package/dist/gitops_run.task.d.ts +74 -0
- package/dist/gitops_run.task.d.ts.map +1 -1
- package/dist/gitops_run.task.js +174 -160
- package/dist/gitops_sync.task.d.ts +65 -3
- package/dist/gitops_sync.task.d.ts.map +1 -1
- package/dist/gitops_sync.task.js +154 -30
- package/dist/gitops_task_helpers.d.ts +86 -59
- package/dist/gitops_task_helpers.d.ts.map +1 -1
- package/dist/gitops_task_helpers.js +120 -79
- package/dist/gitops_validate.task.d.ts +1 -2
- package/dist/gitops_validate.task.d.ts.map +1 -1
- package/dist/gitops_validate.task.js +15 -22
- package/dist/graph_validation.d.ts +16 -27
- package/dist/graph_validation.d.ts.map +1 -1
- package/dist/graph_validation.js +23 -64
- package/dist/local_repo.d.ts +50 -62
- package/dist/local_repo.d.ts.map +1 -1
- package/dist/local_repo.js +80 -193
- package/dist/log_helpers.d.ts +8 -8
- package/dist/log_helpers.d.ts.map +1 -1
- package/dist/multi_repo_publisher.d.ts +10 -0
- package/dist/multi_repo_publisher.d.ts.map +1 -1
- package/dist/multi_repo_publisher.js +115 -51
- package/dist/npm_registry.d.ts +23 -19
- package/dist/npm_registry.d.ts.map +1 -1
- package/dist/npm_registry.js +20 -47
- package/dist/operations.d.ts +75 -180
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +15 -7
- package/dist/operations_defaults.d.ts +17 -2
- package/dist/operations_defaults.d.ts.map +1 -1
- package/dist/operations_defaults.js +104 -121
- package/dist/output_helpers.d.ts +36 -0
- package/dist/output_helpers.d.ts.map +1 -1
- package/dist/output_helpers.js +37 -3
- package/dist/preflight_checks.d.ts +20 -27
- package/dist/preflight_checks.d.ts.map +1 -1
- package/dist/preflight_checks.js +37 -126
- package/dist/publish_steps.d.ts +2 -4
- package/dist/publish_steps.d.ts.map +1 -1
- package/dist/publish_steps.js +2 -6
- package/dist/publishing_event.d.ts +2 -8
- package/dist/publishing_event.d.ts.map +1 -1
- package/dist/publishing_event.js +5 -4
- package/dist/publishing_event_handler.d.ts +10 -8
- package/dist/publishing_event_handler.d.ts.map +1 -1
- package/dist/publishing_event_handler.js +14 -12
- package/dist/publishing_plan.d.ts +16 -1
- package/dist/publishing_plan.d.ts.map +1 -1
- package/dist/publishing_plan.js +328 -250
- package/dist/publishing_plan_logging.d.ts +1 -1
- package/dist/publishing_plan_logging.d.ts.map +1 -1
- package/dist/publishing_plan_logging.js +21 -14
- package/dist/repo.svelte.d.ts +7 -0
- package/dist/repo.svelte.d.ts.map +1 -1
- package/dist/repo.svelte.js +3 -0
- package/dist/repo_readiness.d.ts +210 -0
- package/dist/repo_readiness.d.ts.map +1 -0
- package/dist/repo_readiness.js +516 -0
- package/dist/repos_status.d.ts +3340 -0
- package/dist/repos_status.d.ts.map +1 -0
- package/dist/repos_status.js +555 -0
- package/dist/repos_status_load.d.ts +65 -0
- package/dist/repos_status_load.d.ts.map +1 -0
- package/dist/repos_status_load.js +131 -0
- package/package.json +7 -27
- package/src/lib/ci_reconcile.ts +7 -11
- package/src/lib/dependency_graph.ts +45 -97
- package/src/lib/dependency_updater.ts +7 -1
- package/src/lib/fetch_repo_data.ts +66 -29
- package/src/lib/fs_fetch_value_cache.ts +1 -1
- package/src/lib/git_operations.ts +15 -187
- package/src/lib/github.ts +20 -8
- package/src/lib/github_helpers.ts +5 -12
- package/src/lib/gitops_analyze.task.ts +79 -41
- package/src/lib/gitops_config.ts +88 -137
- package/src/lib/gitops_constants.ts +6 -4
- package/src/lib/gitops_plan.task.ts +113 -61
- package/src/lib/gitops_publish.task.ts +255 -122
- package/src/lib/gitops_run.task.ts +250 -178
- package/src/lib/gitops_sync.task.ts +208 -33
- package/src/lib/gitops_task_helpers.ts +166 -110
- package/src/lib/gitops_validate.task.ts +17 -27
- package/src/lib/graph_validation.ts +25 -86
- package/src/lib/local_repo.ts +109 -316
- package/src/lib/log_helpers.ts +8 -18
- package/src/lib/multi_repo_publisher.ts +138 -53
- package/src/lib/npm_registry.ts +37 -62
- package/src/lib/operations.ts +78 -150
- package/src/lib/operations_defaults.ts +108 -154
- package/src/lib/output_helpers.ts +55 -3
- package/src/lib/preflight_checks.ts +47 -183
- package/src/lib/publish_steps.ts +15 -13
- package/src/lib/publishing_event.ts +5 -4
- package/src/lib/publishing_event_handler.ts +18 -16
- package/src/lib/publishing_plan.ts +451 -287
- package/src/lib/publishing_plan_logging.ts +32 -23
- package/src/lib/repo.svelte.ts +8 -0
- package/src/lib/repo_readiness.ts +647 -0
- package/src/lib/repos_status.ts +671 -0
- package/src/lib/repos_status_load.ts +173 -0
- package/dist/config_reconcile.d.ts +0 -46
- package/dist/config_reconcile.d.ts.map +0 -1
- package/dist/config_reconcile.js +0 -64
- package/dist/paths.d.ts +0 -7
- package/dist/paths.d.ts.map +0 -1
- package/dist/paths.js +0 -6
- package/dist/repo_ops.d.ts +0 -59
- package/dist/repo_ops.d.ts.map +0 -1
- package/dist/repo_ops.js +0 -170
- package/dist/resolved_gitops_config.d.ts +0 -9
- package/dist/resolved_gitops_config.d.ts.map +0 -1
- package/dist/resolved_gitops_config.js +0 -12
- package/src/lib/config_reconcile.ts +0 -98
- package/src/lib/paths.ts +0 -6
- package/src/lib/repo_ops.ts +0 -216
- package/src/lib/resolved_gitops_config.ts +0 -27
package/src/lib/operations.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Operations interfaces for dependency injection.
|
|
3
3
|
*
|
|
4
4
|
* This is the core pattern enabling testability without mocks.
|
|
5
|
-
* All side effects (git, npm, fs, process
|
|
5
|
+
* All side effects (git, npm, fs, process, build, and the `repos` binary) are
|
|
6
|
+
* abstracted into interfaces.
|
|
6
7
|
*
|
|
7
8
|
* **Design principles:**
|
|
8
9
|
* - All operations accept a single `options` object parameter
|
|
@@ -13,23 +14,30 @@
|
|
|
13
14
|
*
|
|
14
15
|
* **Production usage:**
|
|
15
16
|
* ```typescript
|
|
16
|
-
* import {default_gitops_operations} from './operations_defaults.
|
|
17
|
-
* const
|
|
17
|
+
* import {default_gitops_operations} from './operations_defaults.ts';
|
|
18
|
+
* const ops = default_gitops_operations;
|
|
19
|
+
* const result = await ops.git.current_commit_hash({cwd: '/path'});
|
|
18
20
|
* if (!result.ok) {
|
|
19
21
|
* throw new TaskError(result.message);
|
|
20
22
|
* }
|
|
21
|
-
* const
|
|
23
|
+
* const commit = result.value;
|
|
22
24
|
* ```
|
|
23
25
|
*
|
|
24
26
|
* **Test usage:**
|
|
25
27
|
* ```typescript
|
|
26
|
-
*
|
|
27
|
-
* const
|
|
28
|
+
* import {create_mock_gitops_ops} from './test_helpers.ts';
|
|
29
|
+
* const ops = create_mock_gitops_ops({
|
|
30
|
+
* changeset: {has_changesets: async () => ({ok: true, value: false})}
|
|
31
|
+
* });
|
|
32
|
+
* const result = await publish_repos(repos, {...options, ops});
|
|
28
33
|
* // Assert on result without any real git/npm calls
|
|
29
34
|
* ```
|
|
30
35
|
*
|
|
31
|
-
* See `operations_defaults.ts` for real implementations
|
|
32
|
-
*
|
|
36
|
+
* See `operations_defaults.ts` for real implementations, and the test-side mock
|
|
37
|
+
* factories: `create_mock_gitops_ops` in `src/test/test_helpers.ts` (plain
|
|
38
|
+
* objects with per-group overrides) and `create_fixture_gitops_ops` in
|
|
39
|
+
* `src/test/fixtures/mock_operations.ts` (a fixture's changesets over
|
|
40
|
+
* `create_mock_gitops_ops`, its fs empty).
|
|
33
41
|
*
|
|
34
42
|
* @module
|
|
35
43
|
*/
|
|
@@ -40,7 +48,7 @@ import type { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
|
40
48
|
import type { LocalRepo } from './local_repo.ts';
|
|
41
49
|
import type { ChangesetInfo } from './changeset_reader.ts';
|
|
42
50
|
import type { BumpType } from './version_utils.ts';
|
|
43
|
-
import type {
|
|
51
|
+
import type { PreflightResult, RunPreflightChecksOptions } from './preflight_checks.ts';
|
|
44
52
|
import type { WaitOptions } from './npm_registry.ts';
|
|
45
53
|
|
|
46
54
|
/**
|
|
@@ -76,66 +84,19 @@ export interface ChangesetOperations {
|
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
/**
|
|
79
|
-
* Git operations
|
|
80
|
-
*
|
|
87
|
+
* Git operations the publishing executor authors with: staging and committing
|
|
88
|
+
* dependency updates and auto-changesets, and reading the commit it published.
|
|
89
|
+
* All operations return `Result` instead of throwing errors. Where each repo
|
|
90
|
+
* sits (branch, dirt, relation to origin) is `ReposOperations`'s to report.
|
|
81
91
|
*/
|
|
82
92
|
export interface GitOperations {
|
|
83
|
-
/**
|
|
84
|
-
* Gets the current branch name.
|
|
85
|
-
*/
|
|
86
|
-
current_branch_name: (options?: {
|
|
87
|
-
cwd?: string;
|
|
88
|
-
}) => Promise<Result<{ value: string }, { message: string }>>;
|
|
89
|
-
|
|
90
93
|
/**
|
|
91
94
|
* Gets the current commit hash.
|
|
92
95
|
*/
|
|
93
96
|
current_commit_hash: (options?: {
|
|
94
|
-
branch?: string;
|
|
95
97
|
cwd?: string;
|
|
96
98
|
}) => Promise<Result<{ value: string }, { message: string }>>;
|
|
97
99
|
|
|
98
|
-
/**
|
|
99
|
-
* Checks if the workspace is clean (no uncommitted changes).
|
|
100
|
-
*/
|
|
101
|
-
check_clean_workspace: (options?: {
|
|
102
|
-
cwd?: string;
|
|
103
|
-
}) => Promise<Result<{ value: boolean }, { message: string }>>;
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Checks out a branch.
|
|
107
|
-
*/
|
|
108
|
-
checkout: (options: {
|
|
109
|
-
branch: string;
|
|
110
|
-
cwd?: string;
|
|
111
|
-
}) => Promise<Result<object, { message: string }>>;
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Pulls changes from remote.
|
|
115
|
-
*/
|
|
116
|
-
pull: (options?: {
|
|
117
|
-
origin?: string;
|
|
118
|
-
branch?: string;
|
|
119
|
-
cwd?: string;
|
|
120
|
-
}) => Promise<Result<object, { message: string }>>;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Switches to a branch, optionally pulling.
|
|
124
|
-
*/
|
|
125
|
-
switch_branch: (options: {
|
|
126
|
-
branch: string;
|
|
127
|
-
pull?: boolean;
|
|
128
|
-
cwd?: string;
|
|
129
|
-
}) => Promise<Result<object, { message: string }>>;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Checks if a remote exists.
|
|
133
|
-
*/
|
|
134
|
-
has_remote: (options?: {
|
|
135
|
-
remote?: string;
|
|
136
|
-
cwd?: string;
|
|
137
|
-
}) => Promise<Result<{ value: boolean }, { message: string }>>;
|
|
138
|
-
|
|
139
100
|
/**
|
|
140
101
|
* Stages files for commit.
|
|
141
102
|
*/
|
|
@@ -145,93 +106,72 @@ export interface GitOperations {
|
|
|
145
106
|
}) => Promise<Result<object, { message: string }>>;
|
|
146
107
|
|
|
147
108
|
/**
|
|
148
|
-
*
|
|
109
|
+
* Commits `files` alone (`git commit -- <files>`), leaving anything else
|
|
110
|
+
* staged out of the commit; `files` must be non-empty.
|
|
149
111
|
*/
|
|
150
112
|
commit: (options: {
|
|
151
113
|
message: string;
|
|
114
|
+
files: Array<string>;
|
|
152
115
|
cwd?: string;
|
|
153
116
|
}) => Promise<Result<object, { message: string }>>;
|
|
117
|
+
}
|
|
154
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Process operations for the commands the publishing executor runs in a repo
|
|
121
|
+
* (`gro publish`, `gro deploy`).
|
|
122
|
+
*/
|
|
123
|
+
export interface ProcessOperations {
|
|
155
124
|
/**
|
|
156
|
-
*
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
cwd?: string;
|
|
162
|
-
}) => Promise<Result<object, { message: string }>>;
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Checks whether the working tree has any changes — staged, unstaged, or
|
|
166
|
-
* untracked (`git status --porcelain`). Broader than `list_uncommitted_files`,
|
|
167
|
-
* which reports only tracked working-tree changes relative to HEAD.
|
|
168
|
-
*/
|
|
169
|
-
has_changes: (options?: {
|
|
170
|
-
cwd?: string;
|
|
171
|
-
}) => Promise<Result<{ value: boolean }, { message: string }>>;
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Lists uncommitted files in the working tree (`git diff --name-only HEAD`),
|
|
175
|
-
* i.e. working-tree changes relative to HEAD (not a diff between two refs).
|
|
176
|
-
*/
|
|
177
|
-
list_uncommitted_files: (options?: {
|
|
178
|
-
cwd?: string;
|
|
179
|
-
}) => Promise<Result<{ value: Array<string> }, { message: string }>>;
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Creates a git tag.
|
|
183
|
-
*/
|
|
184
|
-
tag: (options: {
|
|
185
|
-
tag_name: string;
|
|
186
|
-
message?: string;
|
|
187
|
-
cwd?: string;
|
|
188
|
-
}) => Promise<Result<object, { message: string }>>;
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Pushes a tag to remote.
|
|
192
|
-
*/
|
|
193
|
-
push_tag: (options: {
|
|
194
|
-
tag_name: string;
|
|
195
|
-
origin?: string;
|
|
196
|
-
cwd?: string;
|
|
197
|
-
}) => Promise<Result<object, { message: string }>>;
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Stashes uncommitted changes.
|
|
125
|
+
* Runs a command in the foreground and waits for it to exit: stdin is the
|
|
126
|
+
* terminal's, so a prompt (npm's 2FA one-time password) can be answered, and
|
|
127
|
+
* the child's output shows live — its stdout on ours, or on our stderr when
|
|
128
|
+
* `stdout` says so, and its stderr on ours. A failure carries the end of
|
|
129
|
+
* what the child wrote to stderr, bounded in lines and characters.
|
|
201
130
|
*/
|
|
202
|
-
|
|
203
|
-
|
|
131
|
+
run_interactive: (options: {
|
|
132
|
+
cmd: string;
|
|
133
|
+
args: Array<string>;
|
|
204
134
|
cwd?: string;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Where the child's stdout goes: our stdout, or our stderr when our stdout
|
|
137
|
+
* carries a machine-readable stream (JSON-lines events, a JSON or markdown
|
|
138
|
+
* report) the child's output would corrupt.
|
|
139
|
+
*
|
|
140
|
+
* @default 'stdout'
|
|
141
|
+
*/
|
|
142
|
+
stdout?: 'stdout' | 'stderr';
|
|
143
|
+
}) => Promise<Result<object, { message: string; stderr_tail?: string }>>;
|
|
144
|
+
}
|
|
211
145
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}) => Promise<Result<{ value: boolean }, { message: string }>>;
|
|
146
|
+
/**
|
|
147
|
+
* What a `repos` command printed and how it exited, unparsed.
|
|
148
|
+
*/
|
|
149
|
+
export interface ReposCommandOutput {
|
|
150
|
+
stdout: string;
|
|
151
|
+
stderr: string;
|
|
152
|
+
/** `0` for a report, `2` for an error document, `1` for a fatal I/O error (maybe no JSON). */
|
|
153
|
+
exit_code: number;
|
|
221
154
|
}
|
|
222
155
|
|
|
223
156
|
/**
|
|
224
|
-
*
|
|
157
|
+
* Operations running the Rust `repos` binary, which owns fleet git state.
|
|
158
|
+
* Parsing its output is `repos_status_load.ts`'s, not the runner's.
|
|
225
159
|
*/
|
|
226
|
-
export interface
|
|
160
|
+
export interface ReposOperations {
|
|
227
161
|
/**
|
|
228
|
-
*
|
|
162
|
+
* Runs `repos [--registry <path>] status [--fetch] <keys…> --json` in the
|
|
163
|
+
* process's cwd and returns what it printed, whatever its exit code. With
|
|
164
|
+
* `fetch`, `repos` fetches each entry from origin first, which writes
|
|
165
|
+
* remote-tracking refs and nothing else. Fails only when the binary didn't
|
|
166
|
+
* run to an exit: `not_found` when it isn't on `PATH`.
|
|
229
167
|
*/
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}) => Promise<
|
|
168
|
+
status: (options: {
|
|
169
|
+
keys: Array<string>;
|
|
170
|
+
registry?: string;
|
|
171
|
+
fetch?: boolean;
|
|
172
|
+
}) => Promise<
|
|
173
|
+
Result<{ output: ReposCommandOutput }, { kind: 'not_found' | 'failed'; message: string }>
|
|
174
|
+
>;
|
|
235
175
|
}
|
|
236
176
|
|
|
237
177
|
/**
|
|
@@ -243,7 +183,6 @@ export interface BuildOperations {
|
|
|
243
183
|
*/
|
|
244
184
|
build_package: (options: {
|
|
245
185
|
repo: LocalRepo;
|
|
246
|
-
log?: Logger;
|
|
247
186
|
}) => Promise<Result<object, { message: string; output?: string }>>;
|
|
248
187
|
}
|
|
249
188
|
|
|
@@ -261,7 +200,7 @@ export interface NpmOperations {
|
|
|
261
200
|
version: string;
|
|
262
201
|
wait_options?: WaitOptions;
|
|
263
202
|
log?: Logger;
|
|
264
|
-
}) => Promise<Result<object, { message: string
|
|
203
|
+
}) => Promise<Result<object, { message: string }>>;
|
|
265
204
|
|
|
266
205
|
/**
|
|
267
206
|
* Checks npm authentication status.
|
|
@@ -272,31 +211,18 @@ export interface NpmOperations {
|
|
|
272
211
|
* Checks if npm registry is reachable.
|
|
273
212
|
*/
|
|
274
213
|
check_registry: () => Promise<Result<object, { message: string }>>;
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Installs npm dependencies.
|
|
278
|
-
*/
|
|
279
|
-
install: (options?: {
|
|
280
|
-
cwd?: string;
|
|
281
|
-
}) => Promise<Result<object, { message: string; stderr?: string }>>;
|
|
282
214
|
}
|
|
283
215
|
|
|
284
216
|
/**
|
|
285
|
-
* Preflight validation operations
|
|
286
|
-
*
|
|
217
|
+
* Preflight validation operations run before publishing: building every
|
|
218
|
+
* package the plan publishes, and npm authentication. Repo git state is the
|
|
219
|
+
* readiness gate's, before preflight (see `repo_readiness.ts`).
|
|
287
220
|
*/
|
|
288
221
|
export interface PreflightOperations {
|
|
289
222
|
/**
|
|
290
223
|
* Runs preflight validation checks before publishing.
|
|
291
224
|
*/
|
|
292
|
-
run_preflight_checks: (options:
|
|
293
|
-
repos: Array<LocalRepo>;
|
|
294
|
-
preflight_options: PreflightOptions;
|
|
295
|
-
git_ops?: GitOperations;
|
|
296
|
-
npm_ops?: NpmOperations;
|
|
297
|
-
build_ops?: BuildOperations;
|
|
298
|
-
changeset_ops?: ChangesetOperations;
|
|
299
|
-
}) => Promise<PreflightResult>;
|
|
225
|
+
run_preflight_checks: (options: RunPreflightChecksOptions) => Promise<PreflightResult>;
|
|
300
226
|
}
|
|
301
227
|
|
|
302
228
|
/**
|
|
@@ -343,4 +269,6 @@ export interface GitopsOperations {
|
|
|
343
269
|
preflight: PreflightOperations;
|
|
344
270
|
fs: FsOperations;
|
|
345
271
|
build: BuildOperations;
|
|
272
|
+
/** `repos status`, for the executor's re-check of each repo right before its publish. */
|
|
273
|
+
repos: ReposOperations;
|
|
346
274
|
}
|
|
@@ -1,38 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Production implementations of operations interfaces.
|
|
3
3
|
*
|
|
4
|
-
* Provides real git, npm, fs, and
|
|
4
|
+
* Provides real git, npm, fs, build, and `repos` operations for production use.
|
|
5
5
|
* For interface definitions and dependency injection pattern, see `operations.ts`.
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { spawn_out } from '@fuzdev/fuz_util/process.ts';
|
|
10
|
+
import { spawn_out, spawn_process, spawn_result_to_message } from '@fuzdev/fuz_util/process.ts';
|
|
11
11
|
import { readFile, writeFile, mkdir, stat } from 'node:fs/promises';
|
|
12
|
-
import {
|
|
12
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
13
13
|
import { fs_classify_error } from '@fuzdev/fuz_util/fs.ts';
|
|
14
14
|
import { EMPTY_OBJECT } from '@fuzdev/fuz_util/object.ts';
|
|
15
15
|
|
|
16
16
|
import { has_changesets, read_changesets, predict_next_version } from './changeset_reader.ts';
|
|
17
17
|
import { wait_for_package } from './npm_registry.ts';
|
|
18
18
|
import { run_preflight_checks } from './preflight_checks.ts';
|
|
19
|
-
import {
|
|
20
|
-
git_add,
|
|
21
|
-
git_commit,
|
|
22
|
-
git_add_and_commit,
|
|
23
|
-
git_tag,
|
|
24
|
-
git_push_tag,
|
|
25
|
-
git_has_changes,
|
|
26
|
-
git_list_uncommitted_files,
|
|
27
|
-
git_has_file_changed,
|
|
28
|
-
git_stash,
|
|
29
|
-
git_stash_pop,
|
|
30
|
-
git_switch_branch,
|
|
31
|
-
git_current_branch_name_required,
|
|
32
|
-
git_current_commit_hash_required,
|
|
33
|
-
git_check_clean_workspace_as_boolean,
|
|
34
|
-
git_has_remote
|
|
35
|
-
} from './git_operations.ts';
|
|
19
|
+
import { git_add, git_commit, git_current_commit_hash_required } from './git_operations.ts';
|
|
36
20
|
import type {
|
|
37
21
|
ChangesetOperations,
|
|
38
22
|
GitOperations,
|
|
@@ -41,7 +25,8 @@ import type {
|
|
|
41
25
|
PreflightOperations,
|
|
42
26
|
FsOperations,
|
|
43
27
|
BuildOperations,
|
|
44
|
-
GitopsOperations
|
|
28
|
+
GitopsOperations,
|
|
29
|
+
ReposOperations
|
|
45
30
|
} from './operations.ts';
|
|
46
31
|
|
|
47
32
|
/** Wrap an async function that returns a value */
|
|
@@ -94,141 +79,120 @@ export const default_changeset_operations: ChangesetOperations = {
|
|
|
94
79
|
};
|
|
95
80
|
|
|
96
81
|
export const default_git_operations: GitOperations = {
|
|
97
|
-
// Core git info
|
|
98
|
-
current_branch_name: async (options) => {
|
|
99
|
-
const { cwd } = options ?? EMPTY_OBJECT;
|
|
100
|
-
return wrap_with_value(() => git_current_branch_name_required(cwd ? { cwd } : undefined));
|
|
101
|
-
},
|
|
102
|
-
|
|
103
82
|
current_commit_hash: async (options) => {
|
|
104
|
-
const { branch, cwd } = options ?? EMPTY_OBJECT;
|
|
105
|
-
return wrap_with_value(() =>
|
|
106
|
-
git_current_commit_hash_required(branch, cwd ? { cwd } : undefined)
|
|
107
|
-
);
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
check_clean_workspace: async (options) => {
|
|
111
83
|
const { cwd } = options ?? EMPTY_OBJECT;
|
|
112
|
-
return wrap_with_value(() =>
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
// Branch operations
|
|
116
|
-
checkout: async (options) => {
|
|
117
|
-
const { branch, cwd } = options;
|
|
118
|
-
return wrap_void(() => git_checkout(branch, cwd ? { cwd } : undefined));
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
pull: async (options) => {
|
|
122
|
-
const { origin, branch, cwd } = options ?? EMPTY_OBJECT;
|
|
123
|
-
try {
|
|
124
|
-
// Omit the branch arg when absent: a trailing `''` makes git pull the remote's
|
|
125
|
-
// default branch (origin/HEAD) instead of the current branch's upstream.
|
|
126
|
-
const spawned = await spawn_out(
|
|
127
|
-
'git',
|
|
128
|
-
['pull', origin || 'origin', ...(branch ? [branch] : [])],
|
|
129
|
-
cwd ? { cwd } : undefined
|
|
130
|
-
);
|
|
131
|
-
if (spawned.result.ok) {
|
|
132
|
-
return { ok: true };
|
|
133
|
-
} else {
|
|
134
|
-
return { ok: false, message: spawned.stderr || 'Pull failed' };
|
|
135
|
-
}
|
|
136
|
-
} catch (error) {
|
|
137
|
-
return { ok: false, message: String(error) };
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
|
|
141
|
-
switch_branch: async (options) => {
|
|
142
|
-
const { branch, pull, cwd } = options;
|
|
143
|
-
return wrap_void(() => git_switch_branch(branch as GitBranch, pull, cwd ? { cwd } : undefined));
|
|
84
|
+
return wrap_with_value(() => git_current_commit_hash_required(cwd ? { cwd } : undefined));
|
|
144
85
|
},
|
|
145
86
|
|
|
146
|
-
has_remote: async (options) => {
|
|
147
|
-
const { remote, cwd } = options ?? EMPTY_OBJECT;
|
|
148
|
-
return wrap_with_value(() => git_has_remote(remote, cwd ? { cwd } : undefined));
|
|
149
|
-
},
|
|
150
|
-
|
|
151
|
-
// Staging and committing
|
|
152
87
|
add: async (options) => {
|
|
153
88
|
const { files, cwd } = options;
|
|
154
89
|
return wrap_void(() => git_add(files, cwd ? { cwd } : undefined));
|
|
155
90
|
},
|
|
156
91
|
|
|
157
92
|
commit: async (options) => {
|
|
158
|
-
const { message, cwd } = options;
|
|
159
|
-
return wrap_void(() => git_commit(message, cwd ? { cwd } : undefined));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
add_and_commit: async (options) => {
|
|
163
|
-
const { files, message, cwd } = options;
|
|
164
|
-
return wrap_void(() => git_add_and_commit(files, message, cwd ? { cwd } : undefined));
|
|
165
|
-
},
|
|
166
|
-
|
|
167
|
-
has_changes: async (options) => {
|
|
168
|
-
const { cwd } = options ?? EMPTY_OBJECT;
|
|
169
|
-
return wrap_with_value(() => git_has_changes(cwd ? { cwd } : undefined));
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
list_uncommitted_files: async (options) => {
|
|
173
|
-
const { cwd } = options ?? EMPTY_OBJECT;
|
|
174
|
-
return wrap_with_value(() => git_list_uncommitted_files(cwd ? { cwd } : undefined));
|
|
175
|
-
},
|
|
176
|
-
|
|
177
|
-
// Tagging
|
|
178
|
-
tag: async (options) => {
|
|
179
|
-
const { tag_name, message, cwd } = options;
|
|
180
|
-
return wrap_void(() => git_tag(tag_name, message, cwd ? { cwd } : undefined));
|
|
181
|
-
},
|
|
93
|
+
const { message, files, cwd } = options;
|
|
94
|
+
return wrap_void(() => git_commit(message, files, cwd ? { cwd } : undefined));
|
|
95
|
+
}
|
|
96
|
+
};
|
|
182
97
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return wrap_void(() => git_push_tag(tag_name, origin as GitOrigin, cwd ? { cwd } : undefined));
|
|
186
|
-
},
|
|
98
|
+
/** The most lines of a child's stderr a failure carries. */
|
|
99
|
+
export const OUTPUT_TAIL_MAX_LINES = 20;
|
|
187
100
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const { message, cwd } = options ?? EMPTY_OBJECT;
|
|
191
|
-
return wrap_void(() => git_stash(message, cwd ? { cwd } : undefined));
|
|
192
|
-
},
|
|
101
|
+
/** The most characters of a child's stderr a failure carries. */
|
|
102
|
+
export const OUTPUT_TAIL_MAX_CHARS = 4096;
|
|
193
103
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
104
|
+
/**
|
|
105
|
+
* The end of a process's output, for a failure message: its last lines, then
|
|
106
|
+
* its last characters, with terminal escape sequences stripped and trailing
|
|
107
|
+
* whitespace trimmed.
|
|
108
|
+
*
|
|
109
|
+
* @param text - the output, or as much of its end as was kept
|
|
110
|
+
* @param max_lines - the most lines to keep
|
|
111
|
+
* @param max_chars - the most characters to keep, applied after `max_lines`
|
|
112
|
+
*/
|
|
113
|
+
export const output_tail = (
|
|
114
|
+
text: string,
|
|
115
|
+
max_lines: number = OUTPUT_TAIL_MAX_LINES,
|
|
116
|
+
max_chars: number = OUTPUT_TAIL_MAX_CHARS
|
|
117
|
+
): string => {
|
|
118
|
+
const lines = stripVTControlCharacters(text).replace(/\r\n?/g, '\n').trimEnd().split('\n');
|
|
119
|
+
const tail = lines.slice(-max_lines).join('\n');
|
|
120
|
+
if (tail.length <= max_chars) return drop_leading_blank_lines(tail);
|
|
121
|
+
// a character cut can split a line, and with it a secret masking would no longer
|
|
122
|
+
// recognize, so the partial first line goes
|
|
123
|
+
const cut = tail.slice(-max_chars);
|
|
124
|
+
if (tail[tail.length - max_chars - 1] === '\n') return drop_leading_blank_lines(cut);
|
|
125
|
+
const nl = cut.indexOf('\n');
|
|
126
|
+
return nl === -1 ? '' : drop_leading_blank_lines(cut.slice(nl + 1));
|
|
206
127
|
};
|
|
207
128
|
|
|
129
|
+
// not `trimStart`, which would strip the first line's indentation
|
|
130
|
+
const drop_leading_blank_lines = (text: string): string => text.replace(/^\n+/, '');
|
|
131
|
+
|
|
208
132
|
export const default_process_operations: ProcessOperations = {
|
|
209
|
-
|
|
210
|
-
const { cmd, args, cwd } = options;
|
|
133
|
+
run_interactive: async (options) => {
|
|
134
|
+
const { cmd, args, cwd, stdout = 'stdout' } = options;
|
|
211
135
|
try {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
136
|
+
// stdin and stdout go straight to the child (fd 2 for stdout routed to our stderr), so
|
|
137
|
+
// they stay a TTY when ours are — npm prompts for a one-time password only on a TTY.
|
|
138
|
+
// Only stderr is piped, to keep its end for the failure message while echoing it live.
|
|
139
|
+
const { child, closed } = spawn_process(cmd, args, {
|
|
140
|
+
cwd,
|
|
141
|
+
stdio: ['inherit', stdout === 'stderr' ? 2 : 'inherit', 'pipe']
|
|
142
|
+
});
|
|
143
|
+
// a rolling window over stderr's end, bounded however much the child writes
|
|
144
|
+
let kept = '';
|
|
145
|
+
child.stderr?.setEncoding('utf8');
|
|
146
|
+
child.stderr?.on('data', (chunk: string) => {
|
|
147
|
+
process.stderr.write(chunk);
|
|
148
|
+
kept += chunk;
|
|
149
|
+
if (kept.length > OUTPUT_TAIL_MAX_CHARS * 4) kept = kept.slice(-OUTPUT_TAIL_MAX_CHARS * 2);
|
|
150
|
+
});
|
|
151
|
+
const result = await closed;
|
|
152
|
+
if (result.ok) return { ok: true };
|
|
153
|
+
const stderr_tail = output_tail(kept);
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
message: `\`${[cmd, ...args].join(' ')}\` failed (${spawn_result_to_message(result)})`,
|
|
157
|
+
stderr_tail: stderr_tail || undefined
|
|
158
|
+
};
|
|
226
159
|
} catch (error) {
|
|
227
160
|
return { ok: false, message: String(error) };
|
|
228
161
|
}
|
|
229
162
|
}
|
|
230
163
|
};
|
|
231
164
|
|
|
165
|
+
export const default_repos_operations: ReposOperations = {
|
|
166
|
+
status: async (options) => {
|
|
167
|
+
const { keys, registry, fetch } = options;
|
|
168
|
+
const args = [...(registry === undefined ? [] : ['--registry', registry]), 'status'];
|
|
169
|
+
if (fetch) args.push('--fetch');
|
|
170
|
+
// `--` so a key can never read as a flag
|
|
171
|
+
args.push('--json', '--', ...keys);
|
|
172
|
+
const spawned = await spawn_out('repos', args);
|
|
173
|
+
const { result } = spawned;
|
|
174
|
+
if (result.kind === 'error') {
|
|
175
|
+
const not_found = (result.error as NodeJS.ErrnoException).code === 'ENOENT';
|
|
176
|
+
return {
|
|
177
|
+
ok: false,
|
|
178
|
+
kind: not_found ? 'not_found' : 'failed',
|
|
179
|
+
message: result.error.message
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (result.kind === 'signaled') {
|
|
183
|
+
return { ok: false, kind: 'failed', message: `repos was killed by ${result.signal}` };
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
ok: true,
|
|
187
|
+
output: {
|
|
188
|
+
stdout: spawned.stdout ?? '',
|
|
189
|
+
stderr: spawned.stderr ?? '',
|
|
190
|
+
exit_code: result.code
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
232
196
|
export const default_npm_operations: NpmOperations = {
|
|
233
197
|
wait_for_package: async (options) => {
|
|
234
198
|
const { pkg, version, wait_options, log } = options;
|
|
@@ -236,7 +200,7 @@ export const default_npm_operations: NpmOperations = {
|
|
|
236
200
|
await wait_for_package(pkg, version, { ...wait_options, log });
|
|
237
201
|
return { ok: true };
|
|
238
202
|
} catch (error) {
|
|
239
|
-
return { ok: false, message: String(error)
|
|
203
|
+
return { ok: false, message: String(error) };
|
|
240
204
|
}
|
|
241
205
|
},
|
|
242
206
|
|
|
@@ -257,25 +221,15 @@ export const default_npm_operations: NpmOperations = {
|
|
|
257
221
|
|
|
258
222
|
check_registry: async () => {
|
|
259
223
|
try {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
return { ok: false, message: 'Failed to ping npm registry' };
|
|
265
|
-
} catch (error) {
|
|
266
|
-
return { ok: false, message: String(error) };
|
|
267
|
-
}
|
|
268
|
-
},
|
|
269
|
-
|
|
270
|
-
install: async (options) => {
|
|
271
|
-
const { cwd } = options ?? EMPTY_OBJECT;
|
|
272
|
-
try {
|
|
273
|
-
const spawned = await spawn_out('npm', ['install'], cwd ? { cwd } : undefined);
|
|
274
|
-
if (spawned.result.ok) {
|
|
224
|
+
// the exit status alone: `npm ping` reports on stderr, writing nothing to stdout
|
|
225
|
+
const { result } = await spawn_out('npm', ['ping']);
|
|
226
|
+
if (result.ok) {
|
|
275
227
|
return { ok: true };
|
|
276
|
-
} else {
|
|
277
|
-
return { ok: false, message: 'Install failed', stderr: spawned.stderr || undefined };
|
|
278
228
|
}
|
|
229
|
+
return {
|
|
230
|
+
ok: false,
|
|
231
|
+
message: `Failed to ping npm registry (${spawn_result_to_message(result)})`
|
|
232
|
+
};
|
|
279
233
|
} catch (error) {
|
|
280
234
|
return { ok: false, message: String(error) };
|
|
281
235
|
}
|
|
@@ -331,9 +285,8 @@ export const default_fs_operations: FsOperations = {
|
|
|
331
285
|
|
|
332
286
|
export const default_build_operations: BuildOperations = {
|
|
333
287
|
build_package: async (options) => {
|
|
334
|
-
const { repo
|
|
288
|
+
const { repo } = options;
|
|
335
289
|
try {
|
|
336
|
-
log?.info(` Building ${repo.library.name}...`);
|
|
337
290
|
const spawned = await spawn_out('gro', ['build'], { cwd: repo.repo_dir });
|
|
338
291
|
if (spawned.result.ok) {
|
|
339
292
|
return { ok: true };
|
|
@@ -360,5 +313,6 @@ export const default_gitops_operations: GitopsOperations = {
|
|
|
360
313
|
npm: default_npm_operations,
|
|
361
314
|
preflight: default_preflight_operations,
|
|
362
315
|
fs: default_fs_operations,
|
|
363
|
-
build: default_build_operations
|
|
316
|
+
build: default_build_operations,
|
|
317
|
+
repos: default_repos_operations
|
|
364
318
|
};
|