@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
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads fleet git state from the Rust `repos` binary: runs
|
|
3
|
+
* `repos status <keys…> --json` through the injected `ReposOperations` and
|
|
4
|
+
* parses what it printed with the `repos_status.ts` mirror.
|
|
5
|
+
*
|
|
6
|
+
* Parsing is pure (`parse_repos_status_output`): the format version is read
|
|
7
|
+
* first, so a binary and npm package out of step fail with both versions and
|
|
8
|
+
* the install command rather than a schema error; then the document is parsed
|
|
9
|
+
* strictly as a report or, when it carries `error`, as an error document,
|
|
10
|
+
* whose kind maps to a message saying what to change.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Result } from '@fuzdev/fuz_util/result.ts';
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
REPOS_STATUS_FORMAT_VERSION,
|
|
20
|
+
ReposStatusErrorReport,
|
|
21
|
+
ReposStatusReport,
|
|
22
|
+
type ReposStatusErrorBody
|
|
23
|
+
} from './repos_status.ts';
|
|
24
|
+
import type { ReposCommandOutput, ReposOperations } from './operations.ts';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* How to install the `repos` binary, run in a `fuz_repos` checkout. The npm
|
|
28
|
+
* package doesn't carry the binary, so the two are installed separately.
|
|
29
|
+
*/
|
|
30
|
+
export const REPOS_INSTALL_COMMAND = 'cargo install --path crates/fuz_repos --locked';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The `repos` invocation a suggested fix names: `repos`, or `repos --registry
|
|
34
|
+
* <path>` when the run passed one, the path POSIX-shell-quoted when it needs it.
|
|
35
|
+
*
|
|
36
|
+
* @param registry - the run's `--registry`, if any
|
|
37
|
+
* @returns the command prefix, ready for a subcommand
|
|
38
|
+
*/
|
|
39
|
+
export const to_repos_command = (registry: string | undefined): string =>
|
|
40
|
+
registry === undefined ? 'repos' : `repos --registry ${shell_quote(registry)}`;
|
|
41
|
+
|
|
42
|
+
const shell_quote = (arg: string): string =>
|
|
43
|
+
/^[\w@%+=:,./-]+$/.test(arg) ? arg : `'${arg.replaceAll("'", `'\\''`)}'`;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Why `repos status` gave no report: a message saying what to change, and the
|
|
47
|
+
* error document when `repos` printed one.
|
|
48
|
+
*/
|
|
49
|
+
export interface ReposStatusLoadFailure {
|
|
50
|
+
message: string;
|
|
51
|
+
error?: ReposStatusErrorBody;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Parses what `repos status --json` printed into its report, or a message
|
|
56
|
+
* saying what went wrong and what to change.
|
|
57
|
+
*
|
|
58
|
+
* @param output - the command's stdout, stderr, and exit code
|
|
59
|
+
* @returns the report, or a message naming the error document's kind and hint, a format version mismatch, or output that isn't a status document; an error document rides along as `error`
|
|
60
|
+
*/
|
|
61
|
+
export const parse_repos_status_output = (
|
|
62
|
+
output: ReposCommandOutput
|
|
63
|
+
): Result<{ report: ReposStatusReport }, ReposStatusLoadFailure> => {
|
|
64
|
+
const { stdout, stderr, exit_code } = output;
|
|
65
|
+
|
|
66
|
+
let json: unknown;
|
|
67
|
+
try {
|
|
68
|
+
json = JSON.parse(stdout);
|
|
69
|
+
} catch {
|
|
70
|
+
// exit 1 is a fatal I/O error, which may print no JSON
|
|
71
|
+
const detail = stderr.trim() || stdout.trim() || 'no output';
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
message: `\`repos status --json\` exited ${exit_code} without a JSON document: ${detail}`
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (typeof json !== 'object' || json === null || Array.isArray(json)) {
|
|
79
|
+
return { ok: false, message: '`repos status --json` printed JSON that is not a document' };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const version = 'version' in json ? json.version : undefined;
|
|
83
|
+
if (version !== REPOS_STATUS_FORMAT_VERSION) {
|
|
84
|
+
const fix =
|
|
85
|
+
typeof version === 'number' && version > REPOS_STATUS_FORMAT_VERSION
|
|
86
|
+
? "upgrade @fuzdev/fuz_gitops to match the binary, or install the binary from a fuz_repos checkout at this package's version"
|
|
87
|
+
: "install the binary from a fuz_repos checkout at this package's version";
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
message:
|
|
91
|
+
`the \`repos\` binary prints status format ${JSON.stringify(version ?? null)}, ` +
|
|
92
|
+
`but this @fuzdev/fuz_gitops parses format ${REPOS_STATUS_FORMAT_VERSION}: ` +
|
|
93
|
+
`${fix} (\`${REPOS_INSTALL_COMMAND}\`) — the npm package and the binary are installed separately`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if ('error' in json) {
|
|
98
|
+
const parsed = ReposStatusErrorReport.safeParse(json);
|
|
99
|
+
if (!parsed.success) {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
message: `\`repos status --json\` printed an error document this package can't parse:\n${z.prettifyError(parsed.error)}`
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
message: format_repos_status_error(parsed.data.error),
|
|
108
|
+
error: parsed.data.error
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const parsed = ReposStatusReport.safeParse(json);
|
|
113
|
+
if (!parsed.success) {
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
message: `\`repos status --json\` printed a report this package can't parse:\n${z.prettifyError(parsed.error)}`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return { ok: true, report: parsed.data };
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const format_repos_status_error = (error: ReposStatusErrorBody): string => {
|
|
123
|
+
switch (error.kind) {
|
|
124
|
+
case 'unknown_entry': {
|
|
125
|
+
const suggestions = error.suggestions.length
|
|
126
|
+
? ` — did you mean ${error.suggestions.map((s) => `\`${s}\``).join(', ')}?`
|
|
127
|
+
: '';
|
|
128
|
+
return `the gitops config lists \`${error.name}\`, which the registry (repos.toml) doesn't name${suggestions}`;
|
|
129
|
+
}
|
|
130
|
+
case 'registry_not_found':
|
|
131
|
+
return (
|
|
132
|
+
`${error.message}: the gitops tasks read repo state from a repos.toml registry — ` +
|
|
133
|
+
`run inside its workspace, or pass \`--registry <path>\``
|
|
134
|
+
);
|
|
135
|
+
default: {
|
|
136
|
+
const hint = error.hint ? `\n hint: ${error.hint}` : '';
|
|
137
|
+
return `\`repos status\` failed (${error.kind}): ${error.message}${hint}`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Runs `repos status <keys…> --json` and parses its report.
|
|
144
|
+
*
|
|
145
|
+
* @param options.keys - the registry keys to report on; must not be empty, since no targets means every entry
|
|
146
|
+
* @param options.registry - a `repos.toml` to use instead of the one found walking up from the cwd
|
|
147
|
+
* @param options.fetch - fetch each entry from origin first (`--fetch`), which writes remote-tracking refs and nothing else
|
|
148
|
+
* @param options.repos_ops - the `repos` runner
|
|
149
|
+
* @returns the report, or a message saying what went wrong and what to change
|
|
150
|
+
*/
|
|
151
|
+
export const load_repos_status = async (options: {
|
|
152
|
+
keys: Array<string>;
|
|
153
|
+
registry?: string;
|
|
154
|
+
fetch?: boolean;
|
|
155
|
+
repos_ops: ReposOperations;
|
|
156
|
+
}): Promise<Result<{ report: ReposStatusReport }, ReposStatusLoadFailure>> => {
|
|
157
|
+
const { keys, registry, fetch, repos_ops } = options;
|
|
158
|
+
if (keys.length === 0) {
|
|
159
|
+
// `repos status` with no targets reports every entry
|
|
160
|
+
return { ok: false, message: 'no registry keys to report on' };
|
|
161
|
+
}
|
|
162
|
+
const ran = await repos_ops.status(fetch ? { keys, registry, fetch } : { keys, registry });
|
|
163
|
+
if (!ran.ok) {
|
|
164
|
+
return {
|
|
165
|
+
ok: false,
|
|
166
|
+
message:
|
|
167
|
+
ran.kind === 'not_found'
|
|
168
|
+
? `the \`repos\` binary was not found on PATH: install it from a fuz_repos checkout with \`${REPOS_INSTALL_COMMAND}\` — the npm package doesn't carry it`
|
|
169
|
+
: `\`repos status\` didn't run: ${ran.message}`
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return parse_repos_status_output(ran.output);
|
|
173
|
+
};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reconciles repo metadata across multiple gitops configs.
|
|
3
|
-
*
|
|
4
|
-
* The ecosystem keeps several gitops configs — a canonical superset plus
|
|
5
|
-
* narrower per-project subsets. They can't share one source of truth (the
|
|
6
|
-
* superset includes private repos that public configs may not reference), so the
|
|
7
|
-
* same repo is declared independently in each. This check flags when a subset's
|
|
8
|
-
* intrinsic fields drift from the canonical config, or when a subset lists a
|
|
9
|
-
* repo the canonical config doesn't. `repo_dir` is per-config and ignored.
|
|
10
|
-
*
|
|
11
|
-
* @module
|
|
12
|
-
*/
|
|
13
|
-
import type { GitopsRepoConfig } from './gitops_config.js';
|
|
14
|
-
/** Repo fields that must agree across every config that lists the repo. */
|
|
15
|
-
declare const INTRINSIC_FIELDS: readonly ["visibility", "ci", "archived", "branch"];
|
|
16
|
-
export type IntrinsicField = (typeof INTRINSIC_FIELDS)[number];
|
|
17
|
-
export type ConfigDriftKind =
|
|
18
|
-
/** The repo is in a subset config but absent from the canonical config. */
|
|
19
|
-
'missing_from_canonical'
|
|
20
|
-
/** An intrinsic field disagrees between the subset and the canonical config. */
|
|
21
|
-
| 'field_mismatch';
|
|
22
|
-
export interface ConfigDrift {
|
|
23
|
-
repo_url: string;
|
|
24
|
-
/** Name of the subset config whose entry diverges. */
|
|
25
|
-
config: string;
|
|
26
|
-
kind: ConfigDriftKind;
|
|
27
|
-
/** The field that disagrees, set when `kind` is `'field_mismatch'`. */
|
|
28
|
-
field?: IntrinsicField;
|
|
29
|
-
/** The canonical config's value, set when `kind` is `'field_mismatch'`. */
|
|
30
|
-
canonical_value?: string;
|
|
31
|
-
/** The subset config's value, set when `kind` is `'field_mismatch'`. */
|
|
32
|
-
config_value?: string;
|
|
33
|
-
}
|
|
34
|
-
export interface NamedRepos {
|
|
35
|
-
name: string;
|
|
36
|
-
repos: Array<GitopsRepoConfig>;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Compares each subset config against the canonical config. Repos that live only
|
|
40
|
-
* in the canonical config are fine — the subsets are intentional partial views.
|
|
41
|
-
* @returns one `ConfigDrift` per subset entry that is missing from the canonical
|
|
42
|
-
* config or whose intrinsic fields disagree with it
|
|
43
|
-
*/
|
|
44
|
-
export declare const reconcile_configs: (canonical: NamedRepos, subsets: Array<NamedRepos>) => Array<ConfigDrift>;
|
|
45
|
-
export {};
|
|
46
|
-
//# sourceMappingURL=config_reconcile.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config_reconcile.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/config_reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,2EAA2E;AAC3E,QAAA,MAAM,gBAAgB,qDAAsD,CAAC;AAE7E,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,MAAM,MAAM,eAAe;AAC1B,2EAA2E;AACzE,wBAAwB;AAC1B,gFAAgF;GAC9E,gBAAgB,CAAC;AAEpB,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,uEAAuE;IACvE,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;CAC/B;AAeD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC7B,WAAW,UAAU,EACrB,SAAS,KAAK,CAAC,UAAU,CAAC,KACxB,KAAK,CAAC,WAAW,CA+BnB,CAAC"}
|
package/dist/config_reconcile.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reconciles repo metadata across multiple gitops configs.
|
|
3
|
-
*
|
|
4
|
-
* The ecosystem keeps several gitops configs — a canonical superset plus
|
|
5
|
-
* narrower per-project subsets. They can't share one source of truth (the
|
|
6
|
-
* superset includes private repos that public configs may not reference), so the
|
|
7
|
-
* same repo is declared independently in each. This check flags when a subset's
|
|
8
|
-
* intrinsic fields drift from the canonical config, or when a subset lists a
|
|
9
|
-
* repo the canonical config doesn't. `repo_dir` is per-config and ignored.
|
|
10
|
-
*
|
|
11
|
-
* @module
|
|
12
|
-
*/
|
|
13
|
-
/** Repo fields that must agree across every config that lists the repo. */
|
|
14
|
-
const INTRINSIC_FIELDS = ['visibility', 'ci', 'archived', 'branch'];
|
|
15
|
-
const field_value = (repo, field) => {
|
|
16
|
-
switch (field) {
|
|
17
|
-
case 'visibility':
|
|
18
|
-
return repo.visibility;
|
|
19
|
-
case 'ci':
|
|
20
|
-
return String(repo.ci);
|
|
21
|
-
case 'archived':
|
|
22
|
-
return String(repo.archived);
|
|
23
|
-
case 'branch':
|
|
24
|
-
return repo.branch;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* Compares each subset config against the canonical config. Repos that live only
|
|
29
|
-
* in the canonical config are fine — the subsets are intentional partial views.
|
|
30
|
-
* @returns one `ConfigDrift` per subset entry that is missing from the canonical
|
|
31
|
-
* config or whose intrinsic fields disagree with it
|
|
32
|
-
*/
|
|
33
|
-
export const reconcile_configs = (canonical, subsets) => {
|
|
34
|
-
const drift = [];
|
|
35
|
-
const canonical_by_url = new Map(canonical.repos.map((r) => [r.repo_url, r]));
|
|
36
|
-
for (const subset of subsets) {
|
|
37
|
-
for (const repo of subset.repos) {
|
|
38
|
-
const canon = canonical_by_url.get(repo.repo_url);
|
|
39
|
-
if (!canon) {
|
|
40
|
-
drift.push({
|
|
41
|
-
repo_url: repo.repo_url,
|
|
42
|
-
config: subset.name,
|
|
43
|
-
kind: 'missing_from_canonical'
|
|
44
|
-
});
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
for (const field of INTRINSIC_FIELDS) {
|
|
48
|
-
const canonical_value = field_value(canon, field);
|
|
49
|
-
const config_value = field_value(repo, field);
|
|
50
|
-
if (canonical_value !== config_value) {
|
|
51
|
-
drift.push({
|
|
52
|
-
repo_url: repo.repo_url,
|
|
53
|
-
config: subset.name,
|
|
54
|
-
kind: 'field_mismatch',
|
|
55
|
-
field,
|
|
56
|
-
canonical_value,
|
|
57
|
-
config_value
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return drift;
|
|
64
|
-
};
|
package/dist/paths.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default repos directory relative to gitops config file.
|
|
3
|
-
* Resolves to the parent of the directory with the config
|
|
4
|
-
* (e.g., `~/dev/repo/gitops.config.ts` resolves to `~/dev/`).
|
|
5
|
-
*/
|
|
6
|
-
export declare const DEFAULT_REPOS_DIR = "..";
|
|
7
|
-
//# sourceMappingURL=paths.d.ts.map
|
package/dist/paths.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC"}
|
package/dist/paths.js
DELETED
package/dist/repo_ops.d.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic repository operations for scripts that work across repos.
|
|
3
|
-
*
|
|
4
|
-
* Provides lightweight utilities for:
|
|
5
|
-
* - Getting repo paths from gitops config (without full git sync)
|
|
6
|
-
* - Walking files in repos with sensible exclusions
|
|
7
|
-
* - Common exclusion patterns for node/svelte projects
|
|
8
|
-
*
|
|
9
|
-
* For full git sync/clone functionality, use `get_gitops_ready()` from `gitops_task_helpers`.
|
|
10
|
-
*
|
|
11
|
-
* @module
|
|
12
|
-
*/
|
|
13
|
-
/** Default directories to exclude from file walking */
|
|
14
|
-
export declare const DEFAULT_EXCLUDE_DIRS: readonly ["node_modules", ".git", ".gro", ".svelte-kit", ".deno", ".vscode", ".idea", "dist", "build", "coverage", ".cache", ".turbo"];
|
|
15
|
-
/** Default binary/non-text extensions to exclude from content processing */
|
|
16
|
-
export declare const DEFAULT_EXCLUDE_EXTENSIONS: readonly [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".woff", ".woff2", ".ttf", ".eot", ".mp4", ".webm", ".mp3", ".wav", ".ogg", ".zip", ".tar", ".gz", ".lock", ".pdf"];
|
|
17
|
-
export interface WalkOptions {
|
|
18
|
-
/** Additional directories to exclude (merged with defaults) */
|
|
19
|
-
exclude_dirs?: Array<string>;
|
|
20
|
-
/** Additional extensions to exclude (merged with defaults) */
|
|
21
|
-
exclude_extensions?: Array<string>;
|
|
22
|
-
/** Maximum file size in bytes (default: 10MB) */
|
|
23
|
-
max_file_size?: number;
|
|
24
|
-
/** Include directories in output (default: false) */
|
|
25
|
-
include_dirs?: boolean;
|
|
26
|
-
/** Use only provided exclusions, ignoring defaults */
|
|
27
|
-
no_defaults?: boolean;
|
|
28
|
-
}
|
|
29
|
-
export interface RepoPath {
|
|
30
|
-
name: string;
|
|
31
|
-
path: string;
|
|
32
|
-
url: string;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Get repo paths from gitops config without full git sync.
|
|
36
|
-
* Lighter weight than `get_gitops_ready()` - just resolves paths.
|
|
37
|
-
*
|
|
38
|
-
* @param config_path - path to the gitops config file (defaults to `gitops.config.ts`)
|
|
39
|
-
* @returns array of repo info with name, path, and url
|
|
40
|
-
*/
|
|
41
|
-
export declare const get_repo_paths: (config_path?: string) => Promise<Array<RepoPath>>;
|
|
42
|
-
/**
|
|
43
|
-
* Check if a path should be excluded based on options.
|
|
44
|
-
*/
|
|
45
|
-
export declare const should_exclude_path: (file_path: string, options?: WalkOptions) => boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Walk files in a directory, respecting common exclusions.
|
|
48
|
-
* Yields absolute paths to files (and optionally directories).
|
|
49
|
-
*
|
|
50
|
-
* @param dir - directory to walk
|
|
51
|
-
* @param options - walk options for exclusions and filtering
|
|
52
|
-
*/
|
|
53
|
-
export declare function walk_repo_files(dir: string, options?: WalkOptions): AsyncGenerator<string, void, undefined>;
|
|
54
|
-
/**
|
|
55
|
-
* Collect all files from `walk_repo_files` into an array.
|
|
56
|
-
* Convenience function for when you need all paths upfront.
|
|
57
|
-
*/
|
|
58
|
-
export declare const collect_repo_files: (dir: string, options?: WalkOptions) => Promise<Array<string>>;
|
|
59
|
-
//# sourceMappingURL=repo_ops.d.ts.map
|
package/dist/repo_ops.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"repo_ops.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/repo_ops.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,wIAavB,CAAC;AAEX,4EAA4E;AAC5E,eAAO,MAAM,0BAA0B,yLAsB7B,CAAC;AAEX,MAAM,WAAW,WAAW;IAC3B,+DAA+D;IAC/D,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACnC,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,sDAAsD;IACtD,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAU,cAAc,MAAM,KAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CA4BlF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,WAAW,MAAM,EAAE,UAAU,WAAW,KAAG,OA2B9E,CAAC;AAEF;;;;;;GAMG;AACH,wBAAuB,eAAe,CACrC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAwCzC;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC9B,KAAK,MAAM,EACX,UAAU,WAAW,KACnB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAMvB,CAAC"}
|
package/dist/repo_ops.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic repository operations for scripts that work across repos.
|
|
3
|
-
*
|
|
4
|
-
* Provides lightweight utilities for:
|
|
5
|
-
* - Getting repo paths from gitops config (without full git sync)
|
|
6
|
-
* - Walking files in repos with sensible exclusions
|
|
7
|
-
* - Common exclusion patterns for node/svelte projects
|
|
8
|
-
*
|
|
9
|
-
* For full git sync/clone functionality, use `get_gitops_ready()` from `gitops_task_helpers`.
|
|
10
|
-
*
|
|
11
|
-
* @module
|
|
12
|
-
*/
|
|
13
|
-
import { existsSync } from 'node:fs';
|
|
14
|
-
import { readdir, stat } from 'node:fs/promises';
|
|
15
|
-
import { join, resolve, dirname } from 'node:path';
|
|
16
|
-
import { load_gitops_config } from './gitops_config.js';
|
|
17
|
-
import { DEFAULT_REPOS_DIR } from './paths.js';
|
|
18
|
-
import { GITOPS_CONFIG_PATH_DEFAULT } from './gitops_constants.js';
|
|
19
|
-
/** Default directories to exclude from file walking */
|
|
20
|
-
export const DEFAULT_EXCLUDE_DIRS = [
|
|
21
|
-
'node_modules',
|
|
22
|
-
'.git',
|
|
23
|
-
'.gro',
|
|
24
|
-
'.svelte-kit',
|
|
25
|
-
'.deno',
|
|
26
|
-
'.vscode',
|
|
27
|
-
'.idea',
|
|
28
|
-
'dist',
|
|
29
|
-
'build',
|
|
30
|
-
'coverage',
|
|
31
|
-
'.cache',
|
|
32
|
-
'.turbo'
|
|
33
|
-
];
|
|
34
|
-
/** Default binary/non-text extensions to exclude from content processing */
|
|
35
|
-
export const DEFAULT_EXCLUDE_EXTENSIONS = [
|
|
36
|
-
'.png',
|
|
37
|
-
'.jpg',
|
|
38
|
-
'.jpeg',
|
|
39
|
-
'.gif',
|
|
40
|
-
'.svg',
|
|
41
|
-
'.ico',
|
|
42
|
-
'.webp',
|
|
43
|
-
'.woff',
|
|
44
|
-
'.woff2',
|
|
45
|
-
'.ttf',
|
|
46
|
-
'.eot',
|
|
47
|
-
'.mp4',
|
|
48
|
-
'.webm',
|
|
49
|
-
'.mp3',
|
|
50
|
-
'.wav',
|
|
51
|
-
'.ogg',
|
|
52
|
-
'.zip',
|
|
53
|
-
'.tar',
|
|
54
|
-
'.gz',
|
|
55
|
-
'.lock',
|
|
56
|
-
'.pdf'
|
|
57
|
-
];
|
|
58
|
-
/**
|
|
59
|
-
* Get repo paths from gitops config without full git sync.
|
|
60
|
-
* Lighter weight than `get_gitops_ready()` - just resolves paths.
|
|
61
|
-
*
|
|
62
|
-
* @param config_path - path to the gitops config file (defaults to `gitops.config.ts`)
|
|
63
|
-
* @returns array of repo info with name, path, and url
|
|
64
|
-
*/
|
|
65
|
-
export const get_repo_paths = async (config_path) => {
|
|
66
|
-
const resolved_config_path = resolve(config_path ?? GITOPS_CONFIG_PATH_DEFAULT);
|
|
67
|
-
const config = await load_gitops_config(resolved_config_path);
|
|
68
|
-
if (!config) {
|
|
69
|
-
throw new Error(`No gitops config found at ${resolved_config_path}`);
|
|
70
|
-
}
|
|
71
|
-
const config_dir = dirname(resolved_config_path);
|
|
72
|
-
const repos_dir = resolve(config_dir, config.repos_dir || DEFAULT_REPOS_DIR);
|
|
73
|
-
const repos = [];
|
|
74
|
-
for (const repo_config of config.repos) {
|
|
75
|
-
const url = repo_config.repo_url;
|
|
76
|
-
const name = url.split('/').at(-1);
|
|
77
|
-
if (!name)
|
|
78
|
-
continue;
|
|
79
|
-
const path = repo_config.repo_dir
|
|
80
|
-
? resolve(config_dir, repo_config.repo_dir)
|
|
81
|
-
: join(repos_dir, name);
|
|
82
|
-
if (existsSync(path)) {
|
|
83
|
-
repos.push({ name, path, url });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return repos;
|
|
87
|
-
};
|
|
88
|
-
/**
|
|
89
|
-
* Check if a path should be excluded based on options.
|
|
90
|
-
*/
|
|
91
|
-
export const should_exclude_path = (file_path, options) => {
|
|
92
|
-
const exclude_dirs = options?.no_defaults
|
|
93
|
-
? (options.exclude_dirs ?? [])
|
|
94
|
-
: [...DEFAULT_EXCLUDE_DIRS, ...(options?.exclude_dirs ?? [])];
|
|
95
|
-
const exclude_extensions = options?.no_defaults
|
|
96
|
-
? (options.exclude_extensions ?? [])
|
|
97
|
-
: [...DEFAULT_EXCLUDE_EXTENSIONS, ...(options?.exclude_extensions ?? [])];
|
|
98
|
-
const normalized = file_path.toLowerCase();
|
|
99
|
-
// Check excluded directories
|
|
100
|
-
for (const dir of exclude_dirs) {
|
|
101
|
-
// Must match as a full directory component, not a prefix
|
|
102
|
-
if (normalized.includes(`/${dir}/`) || normalized.endsWith(`/${dir}`)) {
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
// Check excluded extensions
|
|
107
|
-
for (const ext of exclude_extensions) {
|
|
108
|
-
if (normalized.endsWith(ext)) {
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return false;
|
|
113
|
-
};
|
|
114
|
-
/**
|
|
115
|
-
* Walk files in a directory, respecting common exclusions.
|
|
116
|
-
* Yields absolute paths to files (and optionally directories).
|
|
117
|
-
*
|
|
118
|
-
* @param dir - directory to walk
|
|
119
|
-
* @param options - walk options for exclusions and filtering
|
|
120
|
-
*/
|
|
121
|
-
export async function* walk_repo_files(dir, options) {
|
|
122
|
-
const max_file_size = options?.max_file_size ?? 10 * 1024 * 1024;
|
|
123
|
-
const include_dirs = options?.include_dirs ?? false;
|
|
124
|
-
async function* walk(current_dir) {
|
|
125
|
-
let entries;
|
|
126
|
-
try {
|
|
127
|
-
entries = await readdir(current_dir, { withFileTypes: true });
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
// Skip directories we can't read
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
for (const entry of entries) {
|
|
134
|
-
const full_path = join(current_dir, entry.name);
|
|
135
|
-
if (should_exclude_path(full_path, options)) {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
if (entry.isDirectory()) {
|
|
139
|
-
if (include_dirs) {
|
|
140
|
-
yield full_path;
|
|
141
|
-
}
|
|
142
|
-
yield* walk(full_path);
|
|
143
|
-
}
|
|
144
|
-
else if (entry.isFile()) {
|
|
145
|
-
// Check file size
|
|
146
|
-
try {
|
|
147
|
-
const file_stat = await stat(full_path);
|
|
148
|
-
if (file_stat.size <= max_file_size) {
|
|
149
|
-
yield full_path;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
// Skip files we can't stat
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
yield* walk(dir);
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Collect all files from `walk_repo_files` into an array.
|
|
162
|
-
* Convenience function for when you need all paths upfront.
|
|
163
|
-
*/
|
|
164
|
-
export const collect_repo_files = async (dir, options) => {
|
|
165
|
-
const files = [];
|
|
166
|
-
for await (const file of walk_repo_files(dir, options)) {
|
|
167
|
-
files.push(file);
|
|
168
|
-
}
|
|
169
|
-
return files;
|
|
170
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { GitopsConfig } from './gitops_config.js';
|
|
2
|
-
import { type LocalRepoPath, type LocalRepoMissing } from './local_repo.js';
|
|
3
|
-
export interface ResolvedGitopsConfig {
|
|
4
|
-
local_repos: Array<LocalRepoPath | LocalRepoMissing> | null;
|
|
5
|
-
local_repo_paths: Array<LocalRepoPath> | null;
|
|
6
|
-
local_repos_missing: Array<LocalRepoMissing> | null;
|
|
7
|
-
}
|
|
8
|
-
export declare const resolve_gitops_config: (gitops_config: GitopsConfig, repos_dir: string) => ResolvedGitopsConfig;
|
|
9
|
-
//# sourceMappingURL=resolved_gitops_config.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolved_gitops_config.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/resolved_gitops_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAqB,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE/F,MAAM,WAAW,oBAAoB;IACpC,WAAW,EAAE,KAAK,CAAC,aAAa,GAAG,gBAAgB,CAAC,GAAG,IAAI,CAAC;IAC5D,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;IAC9C,mBAAmB,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;CACpD;AAED,eAAO,MAAM,qBAAqB,GACjC,eAAe,YAAY,EAC3B,WAAW,MAAM,KACf,oBAcF,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { local_repo_locate } from './local_repo.js';
|
|
2
|
-
export const resolve_gitops_config = (gitops_config, repos_dir) => {
|
|
3
|
-
const local_repos = gitops_config.repos.map((r) => local_repo_locate({ repo_config: r, repos_dir }));
|
|
4
|
-
const local_repo_paths = local_repos.filter((r) => r.type === 'local_repo_path');
|
|
5
|
-
const local_repos_missing = local_repos.filter((r) => r.type === 'local_repo_missing');
|
|
6
|
-
const config = {
|
|
7
|
-
local_repos: local_repos.length ? local_repos : null,
|
|
8
|
-
local_repo_paths: local_repo_paths.length ? local_repo_paths : null,
|
|
9
|
-
local_repos_missing: local_repos_missing.length ? local_repos_missing : null
|
|
10
|
-
};
|
|
11
|
-
return config;
|
|
12
|
-
};
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reconciles repo metadata across multiple gitops configs.
|
|
3
|
-
*
|
|
4
|
-
* The ecosystem keeps several gitops configs — a canonical superset plus
|
|
5
|
-
* narrower per-project subsets. They can't share one source of truth (the
|
|
6
|
-
* superset includes private repos that public configs may not reference), so the
|
|
7
|
-
* same repo is declared independently in each. This check flags when a subset's
|
|
8
|
-
* intrinsic fields drift from the canonical config, or when a subset lists a
|
|
9
|
-
* repo the canonical config doesn't. `repo_dir` is per-config and ignored.
|
|
10
|
-
*
|
|
11
|
-
* @module
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { GitopsRepoConfig } from './gitops_config.ts';
|
|
15
|
-
|
|
16
|
-
/** Repo fields that must agree across every config that lists the repo. */
|
|
17
|
-
const INTRINSIC_FIELDS = ['visibility', 'ci', 'archived', 'branch'] as const;
|
|
18
|
-
|
|
19
|
-
export type IntrinsicField = (typeof INTRINSIC_FIELDS)[number];
|
|
20
|
-
|
|
21
|
-
export type ConfigDriftKind =
|
|
22
|
-
/** The repo is in a subset config but absent from the canonical config. */
|
|
23
|
-
| 'missing_from_canonical'
|
|
24
|
-
/** An intrinsic field disagrees between the subset and the canonical config. */
|
|
25
|
-
| 'field_mismatch';
|
|
26
|
-
|
|
27
|
-
export interface ConfigDrift {
|
|
28
|
-
repo_url: string;
|
|
29
|
-
/** Name of the subset config whose entry diverges. */
|
|
30
|
-
config: string;
|
|
31
|
-
kind: ConfigDriftKind;
|
|
32
|
-
/** The field that disagrees, set when `kind` is `'field_mismatch'`. */
|
|
33
|
-
field?: IntrinsicField;
|
|
34
|
-
/** The canonical config's value, set when `kind` is `'field_mismatch'`. */
|
|
35
|
-
canonical_value?: string;
|
|
36
|
-
/** The subset config's value, set when `kind` is `'field_mismatch'`. */
|
|
37
|
-
config_value?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface NamedRepos {
|
|
41
|
-
name: string;
|
|
42
|
-
repos: Array<GitopsRepoConfig>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const field_value = (repo: GitopsRepoConfig, field: IntrinsicField): string => {
|
|
46
|
-
switch (field) {
|
|
47
|
-
case 'visibility':
|
|
48
|
-
return repo.visibility;
|
|
49
|
-
case 'ci':
|
|
50
|
-
return String(repo.ci);
|
|
51
|
-
case 'archived':
|
|
52
|
-
return String(repo.archived);
|
|
53
|
-
case 'branch':
|
|
54
|
-
return repo.branch;
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Compares each subset config against the canonical config. Repos that live only
|
|
60
|
-
* in the canonical config are fine — the subsets are intentional partial views.
|
|
61
|
-
* @returns one `ConfigDrift` per subset entry that is missing from the canonical
|
|
62
|
-
* config or whose intrinsic fields disagree with it
|
|
63
|
-
*/
|
|
64
|
-
export const reconcile_configs = (
|
|
65
|
-
canonical: NamedRepos,
|
|
66
|
-
subsets: Array<NamedRepos>
|
|
67
|
-
): Array<ConfigDrift> => {
|
|
68
|
-
const drift: Array<ConfigDrift> = [];
|
|
69
|
-
const canonical_by_url = new Map(canonical.repos.map((r) => [r.repo_url, r] as const));
|
|
70
|
-
for (const subset of subsets) {
|
|
71
|
-
for (const repo of subset.repos) {
|
|
72
|
-
const canon = canonical_by_url.get(repo.repo_url);
|
|
73
|
-
if (!canon) {
|
|
74
|
-
drift.push({
|
|
75
|
-
repo_url: repo.repo_url,
|
|
76
|
-
config: subset.name,
|
|
77
|
-
kind: 'missing_from_canonical'
|
|
78
|
-
});
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
for (const field of INTRINSIC_FIELDS) {
|
|
82
|
-
const canonical_value = field_value(canon, field);
|
|
83
|
-
const config_value = field_value(repo, field);
|
|
84
|
-
if (canonical_value !== config_value) {
|
|
85
|
-
drift.push({
|
|
86
|
-
repo_url: repo.repo_url,
|
|
87
|
-
config: subset.name,
|
|
88
|
-
kind: 'field_mismatch',
|
|
89
|
-
field,
|
|
90
|
-
canonical_value,
|
|
91
|
-
config_value
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return drift;
|
|
98
|
-
};
|