@fuzdev/fuz_gitops 0.78.0 → 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 +91 -17
- package/dist/ModulesDetail.svelte +22 -27
- package/dist/ModulesDetail.svelte.d.ts.map +1 -1
- package/dist/ModulesNav.svelte +8 -5
- package/dist/ModulesNav.svelte.d.ts.map +1 -1
- package/dist/ModulesPage.svelte +3 -5
- package/dist/ModulesPage.svelte.d.ts.map +1 -1
- package/dist/PageFooter.svelte +2 -2
- package/dist/PageHeader.svelte +6 -6
- package/dist/PageHeader.svelte.d.ts +0 -3
- package/dist/PageHeader.svelte.d.ts.map +1 -1
- package/dist/PullRequestsDetail.svelte +29 -20
- package/dist/PullRequestsDetail.svelte.d.ts.map +1 -1
- package/dist/PullRequestsPage.svelte +4 -6
- package/dist/PullRequestsPage.svelte.d.ts.map +1 -1
- package/dist/ReposTable.svelte +43 -55
- package/dist/ReposTable.svelte.d.ts.map +1 -1
- package/dist/ReposTree.svelte +9 -21
- package/dist/ReposTree.svelte.d.ts.map +1 -1
- package/dist/ReposTreeNav.svelte +9 -12
- package/dist/ReposTreeNav.svelte.d.ts.map +1 -1
- package/dist/TablePage.svelte +3 -5
- package/dist/TablePage.svelte.d.ts.map +1 -1
- package/dist/TreeItemPage.svelte +3 -5
- package/dist/TreeItemPage.svelte.d.ts.map +1 -1
- package/dist/TreePage.svelte +3 -5
- package/dist/TreePage.svelte.d.ts.map +1 -1
- package/dist/changeset_generator.d.ts.map +1 -1
- package/dist/changeset_generator.js +2 -2
- package/dist/changeset_reader.d.ts.map +1 -1
- package/dist/changeset_reader.js +3 -3
- 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 +40 -83
- package/dist/dependency_updater.d.ts +1 -1
- package/dist/dependency_updater.d.ts.map +1 -1
- package/dist/dependency_updater.js +14 -9
- 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.d.ts.map +1 -1
- package/dist/fs_fetch_value_cache.js +3 -3
- 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 +17 -8
- 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 +47 -34
- 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 +83 -61
- 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 +193 -105
- 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 +176 -162
- 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 +163 -37
- 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 +25 -32
- package/dist/graph_validation.d.ts +16 -27
- package/dist/graph_validation.d.ts.map +1 -1
- package/dist/graph_validation.js +24 -65
- package/dist/local_repo.d.ts +50 -62
- package/dist/local_repo.d.ts.map +1 -1
- package/dist/local_repo.js +83 -196
- 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 +125 -61
- package/dist/npm_registry.d.ts +23 -19
- package/dist/npm_registry.d.ts.map +1 -1
- package/dist/npm_registry.js +21 -48
- 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 +111 -128
- 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_gate.d.ts.map +1 -1
- package/dist/publish_steps.d.ts +2 -4
- package/dist/publish_steps.d.ts.map +1 -1
- package/dist/publish_steps.js +4 -8
- package/dist/publishing_event.d.ts +2 -8
- package/dist/publishing_event.d.ts.map +1 -1
- package/dist/publishing_event.js +20 -17
- 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 +19 -17
- package/dist/publishing_plan.d.ts +16 -1
- package/dist/publishing_plan.d.ts.map +1 -1
- package/dist/publishing_plan.js +332 -254
- package/dist/publishing_plan_helpers.d.ts.map +1 -1
- package/dist/publishing_plan_helpers.js +3 -3
- 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 +22 -15
- 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/dist/version_utils.js +1 -1
- package/package.json +15 -34
- package/src/lib/cargo_toml.ts +3 -3
- package/src/lib/changeset_generator.ts +19 -19
- package/src/lib/changeset_reader.ts +18 -18
- package/src/lib/ci_reconcile.ts +12 -16
- package/src/lib/dependency_graph.ts +61 -113
- package/src/lib/dependency_updater.ts +36 -30
- package/src/lib/fetch_repo_data.ts +71 -34
- package/src/lib/fs_fetch_value_cache.ts +15 -11
- package/src/lib/git_operations.ts +22 -194
- package/src/lib/github.ts +33 -21
- package/src/lib/github_helpers.ts +9 -16
- package/src/lib/gitops_analyze.task.ts +104 -66
- package/src/lib/gitops_config.ts +90 -139
- package/src/lib/gitops_constants.ts +6 -4
- package/src/lib/gitops_plan.task.ts +128 -76
- package/src/lib/gitops_publish.task.ts +280 -144
- package/src/lib/gitops_run.task.ts +265 -193
- package/src/lib/gitops_sync.task.ts +236 -52
- package/src/lib/gitops_task_helpers.ts +170 -114
- package/src/lib/gitops_validate.task.ts +44 -54
- package/src/lib/graph_validation.ts +28 -89
- package/src/lib/local_repo.ts +128 -335
- package/src/lib/log_helpers.ts +18 -28
- package/src/lib/multi_repo_publisher.ts +192 -110
- package/src/lib/npm_registry.ts +42 -67
- package/src/lib/operations.ts +99 -160
- package/src/lib/operations_defaults.ts +157 -201
- package/src/lib/output_helpers.ts +58 -6
- package/src/lib/preflight_checks.ts +52 -188
- package/src/lib/publish_gate.ts +7 -9
- package/src/lib/publish_steps.ts +27 -25
- package/src/lib/publishing_event.ts +26 -23
- package/src/lib/publishing_event_handler.ts +24 -22
- package/src/lib/publishing_plan.ts +474 -310
- package/src/lib/publishing_plan_helpers.ts +16 -12
- package/src/lib/publishing_plan_logging.ts +53 -44
- package/src/lib/repo.svelte.ts +16 -8
- 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/src/lib/version_utils.ts +3 -3
- package/dist/config_reconcile.d.ts +0 -46
- package/dist/config_reconcile.d.ts.map +0 -1
- package/dist/config_reconcile.js +0 -60
- 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 -94
- package/src/lib/paths.ts +0 -6
- package/src/lib/repo_ops.ts +0 -216
- package/src/lib/resolved_gitops_config.ts +0 -27
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { createInterface } from 'node:readline/promises';
|
|
3
3
|
import { styleText as st } from 'node:util';
|
|
4
|
-
import { get_gitops_ready } from './gitops_task_helpers.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { gate_publish_readiness, get_gitops_ready, log_readiness_block } from './gitops_task_helpers.js';
|
|
5
|
+
import { default_gitops_operations, default_repos_operations } from './operations_defaults.js';
|
|
6
|
+
import { execute_publishing_plan } from './multi_repo_publisher.js';
|
|
7
|
+
import { mask_secrets, masking_handler, redact_secrets, stdout_handler } from './publishing_event_handler.js';
|
|
8
|
+
import { generate_publishing_plan } from './publishing_plan.js';
|
|
9
|
+
import { log_publishing_plan } from './publishing_plan_logging.js';
|
|
8
10
|
import { derive_publish_steps, format_publish_steps } from './publish_steps.js';
|
|
9
11
|
import { decide_publish_gate, publish_run_failed } from './publish_gate.js';
|
|
10
|
-
import { format_and_output } from './output_helpers.js';
|
|
12
|
+
import { format_and_output, output_is_machine, route_human_output } from './output_helpers.js';
|
|
11
13
|
import { GITOPS_CONFIG_PATH_DEFAULT, GITOPS_NPM_WAIT_TIMEOUT_DEFAULT } from './gitops_constants.js';
|
|
12
14
|
/** @nodocs */
|
|
13
15
|
export const Args = z.strictObject({
|
|
@@ -15,20 +17,29 @@ export const Args = z.strictObject({
|
|
|
15
17
|
.string()
|
|
16
18
|
.meta({ description: 'path to the gitops config file, absolute or relative to the cwd' })
|
|
17
19
|
.default(GITOPS_CONFIG_PATH_DEFAULT),
|
|
18
|
-
|
|
20
|
+
registry: z
|
|
19
21
|
.string()
|
|
20
|
-
.meta({
|
|
22
|
+
.meta({
|
|
23
|
+
description: 'path to the repos.toml registry, when `repos` would not find it walking up from the cwd'
|
|
24
|
+
})
|
|
21
25
|
.optional(),
|
|
22
26
|
peer_strategy: z
|
|
23
|
-
.enum(['exact', 'caret', 'tilde'])
|
|
24
|
-
.meta({
|
|
27
|
+
.enum(['exact', 'caret', 'tilde', 'gte'])
|
|
28
|
+
.meta({
|
|
29
|
+
description: 'range prefix for a rewritten dependency of any type whose range has none (an existing prefix is kept; a wildcard becomes ^)'
|
|
30
|
+
})
|
|
25
31
|
.default('caret'),
|
|
26
32
|
wetrun: z.boolean().meta({ description: 'actually publish (default is dry run)' }).default(false),
|
|
27
33
|
format: z
|
|
28
34
|
.enum(['stdout', 'json', 'markdown'])
|
|
29
35
|
.meta({ description: 'output format' })
|
|
30
36
|
.default('stdout'),
|
|
31
|
-
deploy: z
|
|
37
|
+
deploy: z
|
|
38
|
+
.boolean()
|
|
39
|
+
.meta({
|
|
40
|
+
description: 'deploy each repo the run changed (published, or any dependency updated) after publishing'
|
|
41
|
+
})
|
|
42
|
+
.default(false),
|
|
32
43
|
plan: z
|
|
33
44
|
.boolean()
|
|
34
45
|
.meta({ description: 'show the plan and confirm before publishing; --no-plan to skip' })
|
|
@@ -42,121 +53,198 @@ export const Args = z.strictObject({
|
|
|
42
53
|
.meta({ description: 'stream structured publishing events as JSON-lines to stdout' })
|
|
43
54
|
.default(false),
|
|
44
55
|
outfile: z.string().meta({ description: 'write output to file instead of logging' }).optional(),
|
|
45
|
-
verbose: z
|
|
46
|
-
sync: z
|
|
56
|
+
verbose: z
|
|
47
57
|
.boolean()
|
|
48
|
-
.meta({
|
|
49
|
-
description: 'sync repos (switch branch, pull, install) before the dry run instead of reading the working tree as-is; always on for --wetrun',
|
|
50
|
-
})
|
|
58
|
+
.meta({ description: 'show additional details in plan output' })
|
|
51
59
|
.default(false),
|
|
52
60
|
preview: z
|
|
53
61
|
.boolean()
|
|
54
62
|
.meta({ description: 'show the ordered side-effects a --wetrun would perform' })
|
|
55
|
-
.default(false)
|
|
63
|
+
.default(false)
|
|
56
64
|
});
|
|
57
65
|
/** @nodocs */
|
|
58
66
|
export const task = {
|
|
59
|
-
summary: 'publish
|
|
67
|
+
summary: 'publish the packages the plan publishes, in dependency order (a dry run unless --wetrun)',
|
|
60
68
|
Args,
|
|
61
69
|
run: async ({ args, log }) => {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const { local_repos: repos } = await get_gitops_ready({
|
|
66
|
-
config,
|
|
67
|
-
dir,
|
|
68
|
-
download: false, // Don't download if missing
|
|
69
|
-
sync: sync || wetrun,
|
|
70
|
-
log,
|
|
71
|
-
});
|
|
72
|
-
// Generate the plan once; the executor consumes this exact plan (no second pass).
|
|
73
|
-
const publishing_plan = await generate_publishing_plan(repos, { verbose });
|
|
74
|
-
const preview_steps = preview ? derive_publish_steps(publishing_plan, { deploy }) : null;
|
|
75
|
-
// Decide whether to show the plan + confirm, block, or proceed (the decision table lives
|
|
76
|
-
// in `decide_publish_gate`; readline + exit stay here at the edge).
|
|
77
|
-
const gate = decide_publish_gate({ wetrun, show_plan: plan, plan: publishing_plan });
|
|
78
|
-
// A real publish that shows its plan prints it first — including before a `blocked` throw,
|
|
79
|
-
// so the operator sees the errors that blocked it.
|
|
80
|
-
if (gate.action !== 'proceed') {
|
|
81
|
-
log.info(st('cyan', 'Publishing Plan'));
|
|
82
|
-
log_publishing_plan(publishing_plan, log, { verbose });
|
|
70
|
+
const outcome = await run_gitops_publish(args, log);
|
|
71
|
+
if (outcome === 'cancelled') {
|
|
72
|
+
process.exit(0);
|
|
83
73
|
}
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
else if (gate.action === 'confirm') {
|
|
88
|
-
if (preview_steps)
|
|
89
|
-
log_preview(preview_steps, log);
|
|
90
|
-
// Ask for confirmation
|
|
91
|
-
log.info(st('yellow', '⚠️ This will publish the packages shown above.'));
|
|
92
|
-
process.stdout.write('Continue with publishing? (y/n): ');
|
|
93
|
-
const confirmed = await prompt_for_confirmation();
|
|
94
|
-
if (!confirmed) {
|
|
95
|
-
log.info('Publishing cancelled');
|
|
96
|
-
process.exit(0);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
else if (preview_steps && format === 'stdout') {
|
|
100
|
-
// proceed (dry run or --no-plan): only render to stdout for the human format;
|
|
101
|
-
// json/markdown carry the preview in their structured output, so logging here too
|
|
102
|
-
// would corrupt that stream.
|
|
103
|
-
log_preview(preview_steps, log);
|
|
104
|
-
}
|
|
105
|
-
// Publishing options
|
|
106
|
-
const options = {
|
|
107
|
-
wetrun,
|
|
108
|
-
version_strategy: peer_strategy,
|
|
109
|
-
deploy,
|
|
110
|
-
max_wait,
|
|
111
|
-
log,
|
|
112
|
-
// Live JSON-lines stream when requested; events also surface on the result.
|
|
113
|
-
events: emit_json ? stdout_handler() : undefined,
|
|
114
|
-
};
|
|
115
|
-
// Execute publishing (may throw on fatal errors like circular dependencies)
|
|
116
|
-
let result;
|
|
117
|
-
let fatal_error = null;
|
|
118
|
-
try {
|
|
119
|
-
result = await execute_publishing_plan(repos, publishing_plan, options);
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
// Construct a failure result for fatal errors so output can still be generated
|
|
123
|
-
fatal_error = error instanceof Error ? error : new Error(String(error));
|
|
124
|
-
result = {
|
|
125
|
-
ok: false,
|
|
126
|
-
published: [],
|
|
127
|
-
// Note: FATAL_ERROR is a placeholder - only fatal_error.message is displayed in output
|
|
128
|
-
failed: [{ name: 'FATAL_ERROR', error: fatal_error }],
|
|
129
|
-
duration: 0,
|
|
130
|
-
events: [],
|
|
131
|
-
summary: { total: 0, published: 0, failed: 1, skipped: 0, duration: 0 },
|
|
132
|
-
plan_errors: publishing_plan.errors,
|
|
133
|
-
plan_warnings: publishing_plan.warnings,
|
|
134
|
-
};
|
|
74
|
+
else if (outcome === 'failed') {
|
|
75
|
+
process.exit(1);
|
|
135
76
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const default_gitops_publish_deps = {
|
|
80
|
+
load_repos: get_gitops_ready,
|
|
81
|
+
repos_ops: default_repos_operations,
|
|
82
|
+
ops: default_gitops_operations,
|
|
83
|
+
confirm: async () => {
|
|
84
|
+
process.stderr.write('Continue with publishing? (y/n): ');
|
|
85
|
+
return prompt_for_confirmation();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Runs `gro gitops_publish`, in order: load the repos as they sit,
|
|
90
|
+
* generate the plan, and — for `--wetrun` — run the readiness gate
|
|
91
|
+
* (`gate_publish_readiness`, read-only: `repos status --fetch`) before
|
|
92
|
+
* printing the plan and asking to confirm, so a refusal or a "no" changes
|
|
93
|
+
* nothing. Then preflight and the executor. A dry run skips the gate and
|
|
94
|
+
* prints the diagnostics' readiness block instead. Under `--emit_json`, or
|
|
95
|
+
* `--format json` or `markdown` without `--outfile`, the log goes to stderr
|
|
96
|
+
* and stdout carries the events or the report alone (`route_human_output`).
|
|
97
|
+
*
|
|
98
|
+
* @returns `cancelled` when the operator declines, `failed` when the run failed (its output already written), else `done`
|
|
99
|
+
* @throws {TaskError} when the readiness gate refuses or loading fails
|
|
100
|
+
* @nodocs
|
|
101
|
+
*/
|
|
102
|
+
export const run_gitops_publish = async (args, log, deps = {}) => {
|
|
103
|
+
const { load_repos, repos_ops, ops, confirm } = { ...default_gitops_publish_deps, ...deps };
|
|
104
|
+
const { config, registry, peer_strategy, wetrun, format, deploy, plan, max_wait, emit_json, outfile, verbose, preview } = args;
|
|
105
|
+
// With a machine-readable stdout, every human line goes to stderr: the log (the plan, the
|
|
106
|
+
// readiness block and gate, the executor's progress) and, in `confirm`, the prompt
|
|
107
|
+
const write_stdout = route_human_output(log, to_child_stdout(args) === 'stderr');
|
|
108
|
+
// Load repos as they sit; a real publish gates on their state below rather than moving them
|
|
109
|
+
const { local_repos: repos } = await load_repos({ config, registry, log });
|
|
110
|
+
// Generate the plan once; the executor consumes this exact plan (no second pass).
|
|
111
|
+
const publishing_plan = await generate_publishing_plan(repos, { verbose, ops: ops.changeset });
|
|
112
|
+
const preview_steps = preview ? derive_publish_steps(publishing_plan, { deploy }) : null;
|
|
113
|
+
// Decide whether to show the plan + confirm, block, or proceed (the decision table lives
|
|
114
|
+
// in `decide_publish_gate`; readline + exit stay at the edge).
|
|
115
|
+
const gate = decide_publish_gate({ wetrun, show_plan: plan, plan: publishing_plan });
|
|
116
|
+
if (wetrun) {
|
|
117
|
+
// The readiness gate: fetch, then refuse unless every npm repo is ready. Read-only, and
|
|
118
|
+
// before the prompt and any side effect. A plan with errors is refused without fetching:
|
|
119
|
+
// below when the plan is shown, else by the executor before anything else it does.
|
|
120
|
+
if (publishing_plan.errors.length === 0) {
|
|
121
|
+
await gate_publish_readiness({
|
|
122
|
+
local_repos: repos,
|
|
123
|
+
registry,
|
|
124
|
+
publishing: new Set(publishing_plan.version_changes.map((vc) => vc.package_name)),
|
|
142
125
|
log,
|
|
126
|
+
repos_ops
|
|
143
127
|
});
|
|
144
128
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
log_readiness_block(repos, log);
|
|
132
|
+
}
|
|
133
|
+
// A real publish that shows its plan prints it first — including before a `blocked` throw,
|
|
134
|
+
// so the operator sees the errors that blocked it.
|
|
135
|
+
if (gate.action !== 'proceed') {
|
|
136
|
+
log.info(st('cyan', 'Publishing Plan'));
|
|
137
|
+
log_publishing_plan(publishing_plan, log, { verbose });
|
|
138
|
+
}
|
|
139
|
+
if (gate.action === 'blocked') {
|
|
140
|
+
throw new Error(gate.message);
|
|
141
|
+
}
|
|
142
|
+
else if (gate.action === 'confirm') {
|
|
143
|
+
if (preview_steps)
|
|
144
|
+
log_preview(preview_steps, log);
|
|
145
|
+
// Ask for confirmation
|
|
146
|
+
log.info(st('yellow', '⚠️ This will publish the packages shown above.'));
|
|
147
|
+
const confirmed = await confirm();
|
|
148
|
+
if (!confirmed) {
|
|
149
|
+
log.info('Publishing cancelled');
|
|
150
|
+
return 'cancelled';
|
|
148
151
|
}
|
|
149
|
-
}
|
|
152
|
+
}
|
|
153
|
+
else if (preview_steps && format === 'stdout') {
|
|
154
|
+
// proceed (dry run or --no-plan): only render to stdout for the human format;
|
|
155
|
+
// json/markdown carry the preview in their structured output, so logging here too
|
|
156
|
+
// would corrupt that stream.
|
|
157
|
+
log_preview(preview_steps, log);
|
|
158
|
+
}
|
|
159
|
+
// Publishing options
|
|
160
|
+
const options = {
|
|
161
|
+
wetrun,
|
|
162
|
+
version_strategy: peer_strategy,
|
|
163
|
+
deploy,
|
|
164
|
+
max_wait,
|
|
165
|
+
log,
|
|
166
|
+
ops,
|
|
167
|
+
registry,
|
|
168
|
+
// Live JSON-lines stream when requested, secrets masked since failure messages carry
|
|
169
|
+
// npm's stderr; events also surface on the result.
|
|
170
|
+
events: emit_json ? masking_handler(stdout_handler(write_stdout)) : undefined,
|
|
171
|
+
child_stdout: to_child_stdout(args)
|
|
172
|
+
};
|
|
173
|
+
// Execute publishing (may throw on fatal errors like circular dependencies)
|
|
174
|
+
let result;
|
|
175
|
+
let fatal_error = null;
|
|
176
|
+
try {
|
|
177
|
+
result = await execute_publishing_plan(repos, publishing_plan, options);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
// Construct a failure result for fatal errors so output can still be generated
|
|
181
|
+
fatal_error = error instanceof Error ? error : new Error(String(error));
|
|
182
|
+
result = {
|
|
183
|
+
ok: false,
|
|
184
|
+
published: [],
|
|
185
|
+
// Note: FATAL_ERROR is a placeholder - only fatal_error.message is displayed in output
|
|
186
|
+
failed: [{ name: 'FATAL_ERROR', error: fatal_error }],
|
|
187
|
+
duration: 0,
|
|
188
|
+
events: [],
|
|
189
|
+
summary: { total: 0, published: 0, failed: 1, skipped: 0, duration: 0 },
|
|
190
|
+
plan_errors: publishing_plan.errors,
|
|
191
|
+
plan_warnings: publishing_plan.warnings
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// Format and output result (always runs, even on fatal errors)
|
|
195
|
+
// Note: stdout format is handled by the executor's logging
|
|
196
|
+
if (format !== 'stdout') {
|
|
197
|
+
await format_and_output({ result, fatal_error, preview_steps }, create_publish_formatters(), {
|
|
198
|
+
format,
|
|
199
|
+
outfile,
|
|
200
|
+
log,
|
|
201
|
+
write_stdout
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return publish_run_failed(result, fatal_error) ? 'failed' : 'done';
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Where `gro publish` and `gro deploy`, streaming live, show their stdout: on
|
|
208
|
+
* our stderr when our stdout carries a machine-readable stream — the
|
|
209
|
+
* `--emit_json` events, or a JSON or markdown report not sent to `--outfile` —
|
|
210
|
+
* so their output can't corrupt it, else on our stdout. The same test sends
|
|
211
|
+
* the task's own log and prompt to stderr.
|
|
212
|
+
*
|
|
213
|
+
* @nodocs
|
|
214
|
+
*/
|
|
215
|
+
export const to_child_stdout = (args) => args.emit_json || output_is_machine(args.format, args.outfile) ? 'stderr' : 'stdout';
|
|
216
|
+
/**
|
|
217
|
+
* A failed package's markdown: the message's first line on the bullet, and the
|
|
218
|
+
* rest — the end of a command's stderr — in an indented fenced block, its fence
|
|
219
|
+
* longer than any backtick run inside so stderr can't close it.
|
|
220
|
+
*
|
|
221
|
+
* @param name - the package name
|
|
222
|
+
* @param message - the failure message
|
|
223
|
+
* @nodocs
|
|
224
|
+
*/
|
|
225
|
+
export const format_failure_markdown = (name, message) => {
|
|
226
|
+
const [head, ...rest] = message.split('\n');
|
|
227
|
+
const lines = [`- \`${name}\`: ${head}`];
|
|
228
|
+
if (rest.length === 0)
|
|
229
|
+
return lines;
|
|
230
|
+
const longest_run = Math.max(0, ...(rest.join('\n').match(/`+/g) ?? []).map((r) => r.length));
|
|
231
|
+
const fence = '`'.repeat(Math.max(3, longest_run + 1));
|
|
232
|
+
lines.push('', ` ${fence}`, ...rest.map((l) => ` ${l}`), ` ${fence}`);
|
|
233
|
+
return lines;
|
|
150
234
|
};
|
|
151
235
|
const create_publish_formatters = () => ({
|
|
152
|
-
json: (data) =>
|
|
236
|
+
json: (data) => {
|
|
237
|
+
// the result's events are the executor's unmasked capture; mask them as the live stream is
|
|
238
|
+
const result = { ...data.result, events: data.result.events.map(mask_secrets) };
|
|
239
|
+
return JSON.stringify(data.preview_steps ? { ...result, preview: data.preview_steps } : result, null, 2);
|
|
240
|
+
},
|
|
153
241
|
markdown: (data) => format_result_markdown(data.result, data.fatal_error, data.preview_steps),
|
|
154
242
|
stdout: () => {
|
|
155
243
|
// stdout format is handled by the executor's logging
|
|
156
244
|
// This should never be called due to early return in task
|
|
157
|
-
}
|
|
245
|
+
}
|
|
158
246
|
});
|
|
159
|
-
/** Logs the ordered side-effect preview
|
|
247
|
+
/** Logs the ordered side-effect preview. */
|
|
160
248
|
const log_preview = (steps, log) => {
|
|
161
249
|
log.info(st('cyan', '\nSide-effect preview (what a real publish would perform):'));
|
|
162
250
|
for (const line of format_publish_steps(steps)) {
|
|
@@ -172,7 +260,7 @@ const format_result_markdown = (result, fatal_error, preview_steps) => {
|
|
|
172
260
|
if (fatal_error) {
|
|
173
261
|
lines.push('## ❌ Fatal Error');
|
|
174
262
|
lines.push('');
|
|
175
|
-
lines.push(`**Error**: ${fatal_error.message}`);
|
|
263
|
+
lines.push(`**Error**: ${redact_secrets(fatal_error.message)}`);
|
|
176
264
|
lines.push('');
|
|
177
265
|
lines.push('Publishing could not proceed due to the error above.');
|
|
178
266
|
lines.push('');
|
|
@@ -197,7 +285,7 @@ const format_result_markdown = (result, fatal_error, preview_steps) => {
|
|
|
197
285
|
lines.push('## Failed Packages');
|
|
198
286
|
lines.push('');
|
|
199
287
|
for (const { name, error } of result.failed) {
|
|
200
|
-
lines.push(
|
|
288
|
+
lines.push(...format_failure_markdown(name, redact_secrets(error.message)));
|
|
201
289
|
}
|
|
202
290
|
}
|
|
203
291
|
if (result.plan_warnings.length > 0) {
|
|
@@ -232,7 +320,7 @@ const format_result_markdown = (result, fatal_error, preview_steps) => {
|
|
|
232
320
|
const prompt_for_confirmation = async () => {
|
|
233
321
|
const rl = createInterface({
|
|
234
322
|
input: process.stdin,
|
|
235
|
-
output: process.
|
|
323
|
+
output: process.stderr
|
|
236
324
|
});
|
|
237
325
|
const answer = await rl.question('');
|
|
238
326
|
rl.close();
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { type Task } from '@fuzdev/gro';
|
|
2
|
+
import type { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
2
3
|
import { z } from 'zod';
|
|
4
|
+
import type { ReposOperations } from './operations.js';
|
|
5
|
+
/** @nodocs */
|
|
3
6
|
export declare const Args: z.ZodObject<{
|
|
4
7
|
_: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
5
8
|
config: z.ZodDefault<z.ZodString>;
|
|
9
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
6
10
|
concurrency: z.ZodDefault<z.ZodNumber>;
|
|
7
11
|
format: z.ZodDefault<z.ZodEnum<{
|
|
8
12
|
text: "text";
|
|
@@ -11,5 +15,75 @@ export declare const Args: z.ZodObject<{
|
|
|
11
15
|
outfile: z.ZodOptional<z.ZodString>;
|
|
12
16
|
}, z.core.$strict>;
|
|
13
17
|
export type Args = z.infer<typeof Args>;
|
|
18
|
+
/**
|
|
19
|
+
* How a command run in one repo ended: it exited with a code, a signal killed
|
|
20
|
+
* it, or it never started.
|
|
21
|
+
*
|
|
22
|
+
* @nodocs
|
|
23
|
+
*/
|
|
24
|
+
export type GitopsRunOutcome = {
|
|
25
|
+
kind: 'exited';
|
|
26
|
+
code: number;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'signaled';
|
|
29
|
+
signal: string;
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'error';
|
|
32
|
+
message: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* What a command run in one repo printed, and how it ended.
|
|
36
|
+
*
|
|
37
|
+
* @nodocs
|
|
38
|
+
*/
|
|
39
|
+
export interface GitopsRunCommandOutput {
|
|
40
|
+
outcome: GitopsRunOutcome;
|
|
41
|
+
stdout: string;
|
|
42
|
+
stderr: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* One repo's result, as the JSON output carries it.
|
|
46
|
+
*
|
|
47
|
+
* @nodocs
|
|
48
|
+
*/
|
|
49
|
+
export interface GitopsRunResult {
|
|
50
|
+
repo_name: string;
|
|
51
|
+
repo_dir: string;
|
|
52
|
+
status: 'success' | 'failure';
|
|
53
|
+
/** The command's exit code; `null` when a signal killed it or it never started. */
|
|
54
|
+
exit_code: number | null;
|
|
55
|
+
/** The signal that killed the command, else `null`. */
|
|
56
|
+
signal: string | null;
|
|
57
|
+
stdout: string;
|
|
58
|
+
stderr: string;
|
|
59
|
+
duration_ms: number;
|
|
60
|
+
/** Why the command never started. */
|
|
61
|
+
error?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The side effects `run_gitops_run` reaches through, injectable for tests.
|
|
65
|
+
*
|
|
66
|
+
* @nodocs
|
|
67
|
+
*/
|
|
68
|
+
export interface GitopsRunDeps {
|
|
69
|
+
/** Runs `repos status` to resolve the config's repos. */
|
|
70
|
+
repos_ops: ReposOperations;
|
|
71
|
+
/** Runs `command` through `sh -c` in `cwd`, capturing its output. */
|
|
72
|
+
run_command: (options: {
|
|
73
|
+
command: string;
|
|
74
|
+
cwd: string;
|
|
75
|
+
}) => Promise<GitopsRunCommandOutput>;
|
|
76
|
+
}
|
|
77
|
+
/** @nodocs */
|
|
14
78
|
export declare const task: Task<Args>;
|
|
79
|
+
/**
|
|
80
|
+
* Runs `gro gitops_run`: resolves the config's repos through `repos status`,
|
|
81
|
+
* refusing a missing one, runs the command in each, `concurrency` at a time,
|
|
82
|
+
* and reports every result. Under `--format json` without `--outfile`, the log
|
|
83
|
+
* goes to stderr and stdout carries the JSON alone (`route_human_output`).
|
|
84
|
+
*
|
|
85
|
+
* @throws {TaskError} when no command is given, resolving the repos fails, or the command fails in any repo (after reporting)
|
|
86
|
+
* @nodocs
|
|
87
|
+
*/
|
|
88
|
+
export declare const run_gitops_run: (args: Args, log: Logger, deps?: Partial<GitopsRunDeps>) => Promise<void>;
|
|
15
89
|
//# sourceMappingURL=gitops_run.task.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitops_run.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_run.task.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"gitops_run.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_run.task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIvD,cAAc;AACd,eAAO,MAAM,IAAI;;;;;;;;;;kBA0Bf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GACzB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,mFAAmF;IACnF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,uDAAuD;IACvD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC7B,yDAAyD;IACzD,SAAS,EAAE,eAAe,CAAC;IAC3B,qEAAqE;IACrE,WAAW,EAAE,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;CAC5F;AAiBD,cAAc;AACd,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CAM3B,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAC1B,MAAM,IAAI,EACV,KAAK,MAAM,EACX,OAAM,OAAO,CAAC,aAAa,CAAM,KAC/B,OAAO,CAAC,IAAI,CAkGd,CAAC"}
|