@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,103 +1,144 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared initialization logic for all gitops tasks.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
4
|
+
* `resolve_gitops_repos()` loads the config's registry keys, runs
|
|
5
|
+
* `repos status <keysâĻ> --json`, and resolves each key to its checkout.
|
|
6
|
+
* `get_gitops_ready()` then loads each repo's library as its working tree
|
|
7
|
+
* sits. `gate_publish_readiness()` is the read-only gate a real publish runs
|
|
8
|
+
* before its prompt, and `log_readiness_block()` the diagnostics' report of
|
|
9
|
+
* repos not at rest (both over `repo_readiness.ts`).
|
|
9
10
|
*
|
|
10
11
|
* Used by: `gitops_sync.task.ts`, `gitops_analyze.task.ts`, `gitops_plan.task.ts`,
|
|
11
|
-
* `gitops_publish.task.ts`, and `
|
|
12
|
+
* `gitops_publish.task.ts`, `gitops_validate.task.ts`, and `gitops_run.task.ts`.
|
|
12
13
|
*
|
|
13
|
-
* Accepts `
|
|
14
|
-
*
|
|
14
|
+
* Accepts `repos_ops` to support testing via the operations pattern (see
|
|
15
|
+
* `operations.ts` for dependency injection details).
|
|
15
16
|
*
|
|
16
17
|
* @module
|
|
17
18
|
*/
|
|
18
19
|
import { TaskError } from '@fuzdev/gro';
|
|
19
20
|
import { styleText as st } from 'node:util';
|
|
20
|
-
import { resolve
|
|
21
|
-
import {
|
|
21
|
+
import { resolve } from 'node:path';
|
|
22
|
+
import { to_error_message } from '@fuzdev/fuz_util/error.ts';
|
|
22
23
|
import { load_gitops_config } from './gitops_config.js';
|
|
23
|
-
import { local_repos_load,
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
24
|
+
import { local_repos_load, local_repos_resolve, repo_is_npm } from './local_repo.js';
|
|
25
|
+
import { load_repos_status, to_repos_command } from './repos_status_load.js';
|
|
26
|
+
import { check_publish_readiness, format_readiness_ahead, format_readiness_block, repos_not_at_rest } from './repo_readiness.js';
|
|
27
|
+
import { default_repos_operations } from './operations_defaults.js';
|
|
26
28
|
/**
|
|
27
|
-
*
|
|
29
|
+
* Resolves the gitops config's repos through `repos status`: loads the
|
|
30
|
+
* config's registry keys, reports on them, and resolves each to its checkout,
|
|
31
|
+
* in config order. Reads nothing but git state and writes nothing.
|
|
28
32
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
* @returns the config, the `repos status` report, and each repo's path and entry
|
|
34
|
+
* @throws {TaskError} if the config is missing, invalid, or lists no repos, `repos status` fails, or any configured repo is unknown, a reference, missing, not a repo, unprobed, or private under a public `host`
|
|
35
|
+
*/
|
|
36
|
+
export const resolve_gitops_repos = async (options) => {
|
|
37
|
+
const { config, registry, host, log, repos_ops = default_repos_operations } = options;
|
|
38
|
+
const config_path = resolve(config);
|
|
39
|
+
const gitops_config = await import_gitops_config(config_path);
|
|
40
|
+
const keys = gitops_config.repos;
|
|
41
|
+
if (keys.length === 0) {
|
|
42
|
+
throw new TaskError(`No repos are configured in ${config_path}`);
|
|
43
|
+
}
|
|
44
|
+
log?.info(`reading the state of ${keys.length} repos from \`repos status\``);
|
|
45
|
+
log?.debug('repos status targets', keys);
|
|
46
|
+
const loaded = await load_repos_status({ keys, registry, repos_ops });
|
|
47
|
+
if (!loaded.ok) {
|
|
48
|
+
// an unknown key is the config's to fix, so name the config
|
|
49
|
+
throw new TaskError(loaded.error?.kind === 'unknown_entry' ? `${config_path}: ${loaded.message}` : loaded.message);
|
|
50
|
+
}
|
|
51
|
+
const { report } = loaded;
|
|
52
|
+
const resolved = local_repos_resolve({ keys, report, host, registry });
|
|
53
|
+
if (!resolved.ok) {
|
|
54
|
+
throw new TaskError(`${config_path}: ${resolved.message}`);
|
|
55
|
+
}
|
|
56
|
+
return { config_path, gitops_config, report, local_repo_paths: resolved.value };
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Central initialization function for the gitops tasks that load libraries:
|
|
60
|
+
* resolves the config's repos through `repos status` (`resolve_gitops_repos`),
|
|
61
|
+
* then loads each repo's library as its working tree sits (`local_repos_load`).
|
|
62
|
+
* Moves no ref; gro caches each library at `.gro/library.json` in its repo, at
|
|
63
|
+
* a clean commit.
|
|
34
64
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
|
|
65
|
+
* @returns the loaded repos, in config order
|
|
66
|
+
* @throws {TaskError} if resolving the repos or loading them fails
|
|
67
|
+
*/
|
|
68
|
+
export const get_gitops_ready = async (options) => {
|
|
69
|
+
const { local_repo_paths } = await resolve_gitops_repos(options);
|
|
70
|
+
const local_repos = await local_repos_load({ local_repo_paths, log: options.log });
|
|
71
|
+
return { local_repos };
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* The readiness gate `gitops_publish --wetrun` runs before its confirmation
|
|
75
|
+
* prompt: fetches every npm repo from origin (`repos status <keysâĻ> --fetch
|
|
76
|
+
* --json`, which writes remote-tracking refs and nothing else) and refuses
|
|
77
|
+
* unless each is ready (`check_publish_readiness`) â on its registry branch,
|
|
78
|
+
* clean, idle, in sync with origin or ahead of it, fetched without error, no
|
|
79
|
+
* other live session in its checkout, and nothing left to a person. Each
|
|
80
|
+
* ready repo ahead of origin is logged, saying whether its release push
|
|
81
|
+
* carries those commits or they stay unpushed.
|
|
38
82
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
83
|
+
* Every npm repo, not just those the plan publishes or rewrites: the plan
|
|
84
|
+
* reads each one's working tree (changesets, versions, dependency ranges), so
|
|
85
|
+
* a repo off its branch or behind origin can hide a changeset and leave the
|
|
86
|
+
* plan wrong about what to publish. Changes nothing.
|
|
43
87
|
*
|
|
44
|
-
* @param options
|
|
45
|
-
* @
|
|
46
|
-
* @param options.parallel - whether to load repos in parallel (default: true)
|
|
47
|
-
* @param options.concurrency - max concurrent repo loads (default: 5)
|
|
48
|
-
* @param options.sync - sync working trees before loading (default: true)
|
|
49
|
-
* @param options.allow_dirty - when syncing, tolerate uncommitted changes (default: false)
|
|
50
|
-
* @returns initialized config and fully loaded repos ready for operations
|
|
51
|
-
* @throws {TaskError} if config loading or repo resolution fails
|
|
88
|
+
* @param options - the loaded repos, the `--registry` if any, the package names the plan publishes, a logger, and the `repos` runner
|
|
89
|
+
* @throws {TaskError} if `repos status` fails or any npm repo isn't ready, naming each problem and its fix
|
|
52
90
|
*/
|
|
53
|
-
export const
|
|
54
|
-
const {
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
git_ops,
|
|
77
|
-
npm_ops,
|
|
78
|
-
parallel,
|
|
79
|
-
concurrency,
|
|
80
|
-
sync,
|
|
81
|
-
allow_dirty,
|
|
82
|
-
});
|
|
83
|
-
return { config_path, repos_dir, gitops_config, local_repos };
|
|
91
|
+
export const gate_publish_readiness = async (options) => {
|
|
92
|
+
const { local_repos, registry, publishing, log, repos_ops = default_repos_operations } = options;
|
|
93
|
+
const npm_repos = local_repos.filter(repo_is_npm);
|
|
94
|
+
const keys = npm_repos.map((r) => r.entry.key);
|
|
95
|
+
if (keys.length === 0)
|
|
96
|
+
return;
|
|
97
|
+
log?.info(`fetching ${keys.length} npm repos to check they're ready to publish`);
|
|
98
|
+
const loaded = await load_repos_status({ keys, registry, fetch: true, repos_ops });
|
|
99
|
+
if (!loaded.ok) {
|
|
100
|
+
throw new TaskError(`the readiness check failed: ${loaded.message}`);
|
|
101
|
+
}
|
|
102
|
+
const repos_command = to_repos_command(registry);
|
|
103
|
+
const checked = check_publish_readiness({ report: loaded.report, keys, repos_command });
|
|
104
|
+
if (!checked.ok) {
|
|
105
|
+
throw new TaskError(checked.message);
|
|
106
|
+
}
|
|
107
|
+
log?.info(st('green', `all ${keys.length} npm repos are ready to publish`));
|
|
108
|
+
const name_by_key = new Map(npm_repos.map((r) => [r.entry.key, r.library.name]));
|
|
109
|
+
for (const ahead of checked.ahead) {
|
|
110
|
+
const name = name_by_key.get(ahead.key);
|
|
111
|
+
const publishes = name !== undefined && publishing?.has(name) === true;
|
|
112
|
+
log?.info(st('yellow', format_readiness_ahead(ahead, publishes, { repos_command })));
|
|
113
|
+
}
|
|
84
114
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Logs the diagnostics' readiness block as warnings (stderr, so a `--format
|
|
117
|
+
* json` or `markdown` document on stdout stays clean): each npm repo not at
|
|
118
|
+
* rest, and how. Logs nothing when every one is at rest. Reads the entries
|
|
119
|
+
* `repos status` already reported, from local refs.
|
|
120
|
+
*
|
|
121
|
+
* @param local_repos - the loaded repos; the npm ones are reported
|
|
122
|
+
* @param log - where the warnings go
|
|
123
|
+
* @param now - the current time in unix seconds (defaults to the clock)
|
|
124
|
+
*/
|
|
125
|
+
export const log_readiness_block = (local_repos, log, now = Math.floor(Date.now() / 1000)) => {
|
|
126
|
+
const not_ready = repos_not_at_rest(local_repos.filter(repo_is_npm).map((r) => r.entry));
|
|
127
|
+
for (const line of format_readiness_block(not_ready, now)) {
|
|
128
|
+
log.warn(st('yellow', line));
|
|
129
|
+
}
|
|
96
130
|
};
|
|
97
|
-
|
|
98
|
-
|
|
131
|
+
const import_gitops_config = async (config_path) => {
|
|
132
|
+
let gitops_config;
|
|
133
|
+
try {
|
|
134
|
+
gitops_config = await load_gitops_config(config_path);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
// an invalid config is the user's to fix, not an unexpected task failure
|
|
138
|
+
throw new TaskError(to_error_message(err));
|
|
139
|
+
}
|
|
99
140
|
if (!gitops_config) {
|
|
100
|
-
throw new TaskError(st('red', `No gitops config found at ${
|
|
141
|
+
throw new TaskError(st('red', `No gitops config found at ${config_path}`));
|
|
101
142
|
}
|
|
102
143
|
return gitops_config;
|
|
103
144
|
};
|
|
@@ -3,9 +3,8 @@ import { z } from 'zod';
|
|
|
3
3
|
/** @nodocs */
|
|
4
4
|
export declare const Args: z.ZodObject<{
|
|
5
5
|
config: z.ZodDefault<z.ZodString>;
|
|
6
|
-
|
|
6
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
7
7
|
verbose: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
-
sync: z.ZodDefault<z.ZodBoolean>;
|
|
9
8
|
}, z.core.$strict>;
|
|
10
9
|
export type Args = z.infer<typeof Args>;
|
|
11
10
|
/** @nodocs */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitops_validate.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_validate.task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"gitops_validate.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_validate.task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,cAAc;AACd,eAAO,MAAM,IAAI;;;;kBAaf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC,cAAc;AACd,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CA0Q3B,CAAC"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { styleText as st } from 'node:util';
|
|
3
|
-
import { get_gitops_ready } from './gitops_task_helpers.js';
|
|
3
|
+
import { get_gitops_ready, log_readiness_block } from './gitops_task_helpers.js';
|
|
4
4
|
import { analyze_repos } from './graph_validation.js';
|
|
5
|
-
import { generate_publishing_plan
|
|
5
|
+
import { generate_publishing_plan } from './publishing_plan.js';
|
|
6
|
+
import { log_publishing_plan } from './publishing_plan_logging.js';
|
|
6
7
|
import { execute_publishing_plan } from './multi_repo_publisher.js';
|
|
7
8
|
import { log_dependency_analysis } from './log_helpers.js';
|
|
8
9
|
import { GITOPS_CONFIG_PATH_DEFAULT } from './gitops_constants.js';
|
|
@@ -13,32 +14,29 @@ export const Args = z.strictObject({
|
|
|
13
14
|
.string()
|
|
14
15
|
.meta({ description: 'path to the gitops config file, absolute or relative to the cwd' })
|
|
15
16
|
.default(GITOPS_CONFIG_PATH_DEFAULT),
|
|
16
|
-
|
|
17
|
+
registry: z
|
|
17
18
|
.string()
|
|
18
|
-
.meta({ description: 'path containing the repos, defaults to the parent of the config dir' })
|
|
19
|
-
.optional(),
|
|
20
|
-
verbose: z.boolean().meta({ description: 'show additional details' }).default(false),
|
|
21
|
-
sync: z
|
|
22
|
-
.boolean()
|
|
23
19
|
.meta({
|
|
24
|
-
description: '
|
|
20
|
+
description: 'path to the repos.toml registry, when `repos` would not find it walking up from the cwd'
|
|
25
21
|
})
|
|
26
|
-
.
|
|
22
|
+
.optional(),
|
|
23
|
+
verbose: z.boolean().meta({ description: 'show additional details' }).default(false)
|
|
27
24
|
});
|
|
28
25
|
/** @nodocs */
|
|
29
26
|
export const task = {
|
|
30
27
|
Args,
|
|
31
28
|
summary: 'validate gitops configuration by running all read-only commands and checking for issues',
|
|
32
29
|
run: async ({ args, log }) => {
|
|
33
|
-
const { config,
|
|
30
|
+
const { config, registry, verbose } = args;
|
|
34
31
|
log.info(st('cyan', 'Running Gitops Validation Suite'));
|
|
35
32
|
log.info(st('dim', 'This runs all read-only commands and checks for consistency.'));
|
|
36
33
|
const results = [];
|
|
37
34
|
const start_time = Date.now();
|
|
38
|
-
// Load repos once (shared by all commands)
|
|
35
|
+
// Load repos once (shared by all commands), as they sit, and say which aren't at rest
|
|
39
36
|
log.info(st('dim', 'Loading repositories...'));
|
|
40
|
-
const { local_repos } = await get_gitops_ready({ config,
|
|
37
|
+
const { local_repos } = await get_gitops_ready({ config, registry, log });
|
|
41
38
|
log.info(st('dim', ` Found ${local_repos.length} local repos`));
|
|
39
|
+
log_readiness_block(local_repos, log);
|
|
42
40
|
// 1. Run gitops_analyze
|
|
43
41
|
log.info(st('yellow', 'Running gitops_analyze...'));
|
|
44
42
|
const analyze_start = Date.now();
|
|
@@ -65,7 +63,7 @@ export const task = {
|
|
|
65
63
|
duration: analyze_duration,
|
|
66
64
|
warning_details,
|
|
67
65
|
info_details,
|
|
68
|
-
analysis
|
|
66
|
+
analysis
|
|
69
67
|
});
|
|
70
68
|
log.info(st('green', ` â gitops_analyze completed in ${analyze_duration}ms`));
|
|
71
69
|
// Print detailed analysis
|
|
@@ -81,7 +79,7 @@ export const task = {
|
|
|
81
79
|
success: false,
|
|
82
80
|
warnings: 0,
|
|
83
81
|
errors: 1,
|
|
84
|
-
duration: analyze_duration
|
|
82
|
+
duration: analyze_duration
|
|
85
83
|
});
|
|
86
84
|
log.error(st('red', ` â gitops_analyze failed: ${error}`));
|
|
87
85
|
}
|
|
@@ -100,7 +98,7 @@ export const task = {
|
|
|
100
98
|
success: errors === 0,
|
|
101
99
|
warnings,
|
|
102
100
|
errors,
|
|
103
|
-
duration: plan_duration
|
|
101
|
+
duration: plan_duration
|
|
104
102
|
});
|
|
105
103
|
log.info(st('green', ` â gitops_plan completed in ${plan_duration}ms`));
|
|
106
104
|
if (verbose) {
|
|
@@ -120,7 +118,7 @@ export const task = {
|
|
|
120
118
|
success: false,
|
|
121
119
|
warnings: 0,
|
|
122
120
|
errors: 1,
|
|
123
|
-
duration: plan_duration
|
|
121
|
+
duration: plan_duration
|
|
124
122
|
});
|
|
125
123
|
log.error(st('red', ` â gitops_plan failed: ${error}`));
|
|
126
124
|
}
|
|
@@ -130,7 +128,7 @@ export const task = {
|
|
|
130
128
|
try {
|
|
131
129
|
const options = {
|
|
132
130
|
wetrun: false,
|
|
133
|
-
log: undefined
|
|
131
|
+
log: undefined // Silent for validation
|
|
134
132
|
};
|
|
135
133
|
// Reuse the plan from step 2 (regenerate only if that step failed to produce one).
|
|
136
134
|
const result = await execute_publishing_plan(local_repos, publishing_plan ?? (await generate_publishing_plan(local_repos, { verbose })), options);
|
|
@@ -143,7 +141,7 @@ export const task = {
|
|
|
143
141
|
success: result.ok,
|
|
144
142
|
warnings: 0,
|
|
145
143
|
errors,
|
|
146
|
-
duration: dry_duration
|
|
144
|
+
duration: dry_duration
|
|
147
145
|
});
|
|
148
146
|
log.info(st('green', ` â gitops_publish (dry run) completed in ${dry_duration}ms`));
|
|
149
147
|
if (errors > 0) {
|
|
@@ -157,24 +155,19 @@ export const task = {
|
|
|
157
155
|
success: false,
|
|
158
156
|
warnings: 0,
|
|
159
157
|
errors: 1,
|
|
160
|
-
duration: dry_duration
|
|
158
|
+
duration: dry_duration
|
|
161
159
|
});
|
|
162
160
|
log.error(st('red', ` â gitops_publish (dry run) failed: ${error}`));
|
|
163
161
|
}
|
|
164
|
-
// 4. Reconcile each repo's declared `ci` against actual workflow files on disk.
|
|
162
|
+
// 4. Reconcile each repo's registry-declared `ci` against actual workflow files on disk.
|
|
165
163
|
log.info(st('yellow', 'Running ci_reconcile...'));
|
|
166
164
|
const ci_start = Date.now();
|
|
167
165
|
try {
|
|
168
166
|
const ci_drift = reconcile_ci(local_repos.map((r) => ({
|
|
169
|
-
repo_url: r.
|
|
170
|
-
ci: r.
|
|
167
|
+
repo_url: r.entry.url,
|
|
168
|
+
ci: r.entry.ci,
|
|
171
169
|
has_workflows: repo_has_workflows(r.repo_dir),
|
|
172
|
-
|
|
173
|
-
// throws in `local_repos_ensure` before we reach here â so `checkable` is
|
|
174
|
-
// always `true` today. The gate exists for a future caller that loads a
|
|
175
|
-
// partial set; until then the skip path is inert and untested.
|
|
176
|
-
checkable: true,
|
|
177
|
-
archived: r.repo_config.archived,
|
|
170
|
+
archived: r.entry.archived
|
|
178
171
|
})));
|
|
179
172
|
const ci_duration = Date.now() - ci_start;
|
|
180
173
|
const drift_details = ci_drift.map((d) => d.kind === 'missing_ci'
|
|
@@ -185,7 +178,7 @@ export const task = {
|
|
|
185
178
|
success: ci_drift.length === 0,
|
|
186
179
|
warnings: 0,
|
|
187
180
|
errors: ci_drift.length,
|
|
188
|
-
duration: ci_duration
|
|
181
|
+
duration: ci_duration
|
|
189
182
|
});
|
|
190
183
|
if (ci_drift.length === 0) {
|
|
191
184
|
log.info(st('green', ` â ci_reconcile completed in ${ci_duration}ms`));
|
|
@@ -204,7 +197,7 @@ export const task = {
|
|
|
204
197
|
success: false,
|
|
205
198
|
warnings: 0,
|
|
206
199
|
errors: 1,
|
|
207
|
-
duration: ci_duration
|
|
200
|
+
duration: ci_duration
|
|
208
201
|
});
|
|
209
202
|
log.error(st('red', ` â ci_reconcile failed: ${error}`));
|
|
210
203
|
}
|
|
@@ -255,5 +248,5 @@ export const task = {
|
|
|
255
248
|
log.error(st('red', 'â Validation failed - review the errors above.'));
|
|
256
249
|
throw new Error('Validation failed');
|
|
257
250
|
}
|
|
258
|
-
}
|
|
251
|
+
}
|
|
259
252
|
};
|
|
@@ -1,45 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Dependency graph validation shared by the publishing plan and the analysis tasks.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Builds the graph, detects cycles by type, and computes the publishing order,
|
|
5
|
+
* reporting cycles and sort failures in the result rather than throwing, so
|
|
6
|
+
* each caller decides how to surface them: the plan turns them into errors,
|
|
7
|
+
* `gitops_analyze` and `gitops_validate` format them.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
* reporting, while publish must throw on production cycles.
|
|
10
|
-
*
|
|
11
|
-
* See also: `dependency_graph.ts` for core graph data structure and algorithms.
|
|
9
|
+
* See also: `dependency_graph.ts` for the core graph data structure and algorithms.
|
|
12
10
|
*
|
|
13
11
|
* @module
|
|
14
12
|
*/
|
|
15
|
-
import type
|
|
16
|
-
import { DependencyGraph, DependencyGraphBuilder } from './dependency_graph.js';
|
|
13
|
+
import { DependencyGraph, type DependencyAnalysis } from './dependency_graph.js';
|
|
17
14
|
import { type LocalRepo } from './local_repo.js';
|
|
18
15
|
export interface GraphValidationResult {
|
|
19
16
|
graph: DependencyGraph;
|
|
20
17
|
publishing_order: Array<string>;
|
|
21
18
|
production_cycles: Array<Array<string>>;
|
|
22
19
|
dev_cycles: Array<Array<string>>;
|
|
20
|
+
/** Why the topological sort failed, when it did; `publishing_order` is then empty. */
|
|
23
21
|
sort_error?: string;
|
|
24
22
|
}
|
|
25
23
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* Builds the dependency graph, detects cycles, and computes the publishing order
|
|
25
|
+
* (prod/peer dependencies only, so dev cycles don't block it).
|
|
26
|
+
*
|
|
27
|
+
* Never throws on cycles: a production/peer cycle leaves `publishing_order` empty
|
|
28
|
+
* and sets `sort_error`, and the caller reports it.
|
|
28
29
|
*
|
|
29
|
-
* @
|
|
30
|
-
* @param options.log_cycles - whether to log cycle information (default: true)
|
|
31
|
-
* @param options.log_order - whether to log publishing order (default: true)
|
|
32
|
-
* @returns graph validation result with graph, publishing order, and detected cycles
|
|
33
|
-
* @throws {TaskError} if production cycles detected and `throw_on_prod_cycles` is true
|
|
30
|
+
* @returns the graph, publishing order, and detected cycles
|
|
34
31
|
*/
|
|
35
|
-
export declare const validate_dependency_graph: (repos: Array<LocalRepo
|
|
36
|
-
log?: Logger;
|
|
37
|
-
throw_on_prod_cycles?: boolean;
|
|
38
|
-
log_cycles?: boolean;
|
|
39
|
-
log_order?: boolean;
|
|
40
|
-
}) => GraphValidationResult;
|
|
41
|
-
/** Cycles, wildcard deps, and missing peers produced by `DependencyGraphBuilder.analyze`. */
|
|
42
|
-
export type DependencyAnalysis = ReturnType<DependencyGraphBuilder['analyze']>;
|
|
32
|
+
export declare const validate_dependency_graph: (repos: Array<LocalRepo>) => GraphValidationResult;
|
|
43
33
|
export interface RepoAnalysis {
|
|
44
34
|
graph: DependencyGraph;
|
|
45
35
|
analysis: DependencyAnalysis;
|
|
@@ -49,8 +39,7 @@ export interface RepoAnalysis {
|
|
|
49
39
|
/**
|
|
50
40
|
* Builds the dependency graph and runs cycle/wildcard analysis, tolerating cycles
|
|
51
41
|
* (reports rather than throws). The shared core of `gitops_analyze` and
|
|
52
|
-
* `gitops_validate`, which format the result themselves
|
|
53
|
-
* (no `log`) to avoid emitting a redundant "Analyzing dependencies" line.
|
|
42
|
+
* `gitops_validate`, which format the result themselves.
|
|
54
43
|
*/
|
|
55
44
|
export declare const analyze_repos: (repos: Array<LocalRepo>) => RepoAnalysis;
|
|
56
45
|
//# sourceMappingURL=graph_validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph_validation.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/graph_validation.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"graph_validation.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/graph_validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,WAAW,qBAAqB;IACrC,KAAK,EAAE,eAAe,CAAC;IACvB,gBAAgB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAChC,iBAAiB,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACjC,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GAAI,OAAO,KAAK,CAAC,SAAS,CAAC,KAAG,qBAoBnE,CAAC;AAEF,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,sFAAsF;IACtF,gBAAgB,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACvC;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,CAAC,SAAS,CAAC,KAAG,YAQvD,CAAC"}
|
package/dist/graph_validation.js
CHANGED
|
@@ -1,75 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Dependency graph validation shared by the publishing plan and the analysis tasks.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Builds the graph, detects cycles by type, and computes the publishing order,
|
|
5
|
+
* reporting cycles and sort failures in the result rather than throwing, so
|
|
6
|
+
* each caller decides how to surface them: the plan turns them into errors,
|
|
7
|
+
* `gitops_analyze` and `gitops_validate` format them.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
* reporting, while publish must throw on production cycles.
|
|
10
|
-
*
|
|
11
|
-
* See also: `dependency_graph.ts` for core graph data structure and algorithms.
|
|
9
|
+
* See also: `dependency_graph.ts` for the core graph data structure and algorithms.
|
|
12
10
|
*
|
|
13
11
|
* @module
|
|
14
12
|
*/
|
|
15
|
-
import {
|
|
16
|
-
import { styleText as st } from 'node:util';
|
|
17
|
-
import { DependencyGraph, DependencyGraphBuilder } from './dependency_graph.js';
|
|
13
|
+
import { DependencyGraph } from './dependency_graph.js';
|
|
18
14
|
import { repo_is_npm } from './local_repo.js';
|
|
19
15
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* Builds the dependency graph, detects cycles, and computes the publishing order
|
|
17
|
+
* (prod/peer dependencies only, so dev cycles don't block it).
|
|
18
|
+
*
|
|
19
|
+
* Never throws on cycles: a production/peer cycle leaves `publishing_order` empty
|
|
20
|
+
* and sets `sort_error`, and the caller reports it.
|
|
22
21
|
*
|
|
23
|
-
* @
|
|
24
|
-
* @param options.log_cycles - whether to log cycle information (default: true)
|
|
25
|
-
* @param options.log_order - whether to log publishing order (default: true)
|
|
26
|
-
* @returns graph validation result with graph, publishing order, and detected cycles
|
|
27
|
-
* @throws {TaskError} if production cycles detected and `throw_on_prod_cycles` is true
|
|
22
|
+
* @returns the graph, publishing order, and detected cycles
|
|
28
23
|
*/
|
|
29
|
-
export const validate_dependency_graph = (repos
|
|
30
|
-
const
|
|
31
|
-
// Build dependency graph
|
|
32
|
-
log?.info('đ Analyzing dependencies...');
|
|
33
|
-
const builder = new DependencyGraphBuilder();
|
|
34
|
-
const graph = builder.build_from_repos(repos);
|
|
35
|
-
// Check for cycles
|
|
24
|
+
export const validate_dependency_graph = (repos) => {
|
|
25
|
+
const graph = new DependencyGraph(repos);
|
|
36
26
|
const { production_cycles, dev_cycles } = graph.detect_cycles_by_type();
|
|
37
|
-
// Log production cycles
|
|
38
|
-
if (production_cycles.length > 0 && log_cycles) {
|
|
39
|
-
log?.error(st('red', 'â Production/peer dependency cycles detected:'));
|
|
40
|
-
for (const cycle of production_cycles) {
|
|
41
|
-
log?.error(` ${cycle.join(' â ')}`);
|
|
42
|
-
}
|
|
43
|
-
if (throw_on_prod_cycles) {
|
|
44
|
-
throw new TaskError(`Cannot publish with production/peer dependency cycles. ` +
|
|
45
|
-
`These must be resolved before publishing.`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// Log dev cycles (informational, not an error)
|
|
49
|
-
if (dev_cycles.length > 0 && log_cycles) {
|
|
50
|
-
log?.info(st('dim', 'âšī¸ Dev dependency cycles detected (this is normal):'));
|
|
51
|
-
for (const cycle of dev_cycles) {
|
|
52
|
-
log?.info(st('dim', ` ${cycle.join(' â ')}`));
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// Compute publishing order
|
|
56
27
|
let publishing_order;
|
|
57
28
|
let sort_error;
|
|
58
29
|
try {
|
|
59
30
|
publishing_order = graph.topological_sort(true); // exclude dev deps to break cycles
|
|
60
|
-
if (log_order && publishing_order.length > 0) {
|
|
61
|
-
log?.info(` Publishing order: ${publishing_order.join(' â ')}`);
|
|
62
|
-
}
|
|
63
31
|
}
|
|
64
32
|
catch (error) {
|
|
65
|
-
// Capture the sort error message for callers to report
|
|
66
33
|
sort_error = 'Failed to compute publishing order: ' + error;
|
|
67
|
-
// If topological sort fails (due to cycles), return empty array
|
|
68
|
-
// Only throw if production cycles exist AND throw_on_prod_cycles is true
|
|
69
|
-
if (production_cycles.length > 0 && throw_on_prod_cycles) {
|
|
70
|
-
throw new TaskError(sort_error);
|
|
71
|
-
}
|
|
72
|
-
// Otherwise, return empty publishing order (let caller handle it)
|
|
73
34
|
publishing_order = [];
|
|
74
35
|
}
|
|
75
36
|
return {
|
|
@@ -77,22 +38,20 @@ export const validate_dependency_graph = (repos, options = {}) => {
|
|
|
77
38
|
publishing_order,
|
|
78
39
|
production_cycles,
|
|
79
40
|
dev_cycles,
|
|
80
|
-
sort_error
|
|
41
|
+
sort_error
|
|
81
42
|
};
|
|
82
43
|
};
|
|
83
44
|
/**
|
|
84
45
|
* Builds the dependency graph and runs cycle/wildcard analysis, tolerating cycles
|
|
85
46
|
* (reports rather than throws). The shared core of `gitops_analyze` and
|
|
86
|
-
* `gitops_validate`, which format the result themselves
|
|
87
|
-
* (no `log`) to avoid emitting a redundant "Analyzing dependencies" line.
|
|
47
|
+
* `gitops_validate`, which format the result themselves.
|
|
88
48
|
*/
|
|
89
49
|
export const analyze_repos = (repos) => {
|
|
90
|
-
//
|
|
91
|
-
const { graph, publishing_order
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return { graph, analysis, publishing_order: order.length > 0 ? order : null };
|
|
50
|
+
// only npm packages form the dependency graph; non-npm repos (e.g. cargo) are excluded
|
|
51
|
+
const { graph, publishing_order } = validate_dependency_graph(repos.filter(repo_is_npm));
|
|
52
|
+
return {
|
|
53
|
+
graph,
|
|
54
|
+
analysis: graph.analyze(),
|
|
55
|
+
publishing_order: publishing_order.length > 0 ? publishing_order : null
|
|
56
|
+
};
|
|
98
57
|
};
|