@fuzdev/fuz_gitops 0.57.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/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/ModulesDetail.svelte +180 -0
- package/dist/ModulesDetail.svelte.d.ts +10 -0
- package/dist/ModulesDetail.svelte.d.ts.map +1 -0
- package/dist/ModulesNav.svelte +43 -0
- package/dist/ModulesNav.svelte.d.ts +11 -0
- package/dist/ModulesNav.svelte.d.ts.map +1 -0
- package/dist/ModulesPage.svelte +50 -0
- package/dist/ModulesPage.svelte.d.ts +9 -0
- package/dist/ModulesPage.svelte.d.ts.map +1 -0
- package/dist/PageFooter.svelte +15 -0
- package/dist/PageFooter.svelte.d.ts +19 -0
- package/dist/PageFooter.svelte.d.ts.map +1 -0
- package/dist/PageHeader.svelte +35 -0
- package/dist/PageHeader.svelte.d.ts +19 -0
- package/dist/PageHeader.svelte.d.ts.map +1 -0
- package/dist/PullRequestsDetail.svelte +53 -0
- package/dist/PullRequestsDetail.svelte.d.ts +10 -0
- package/dist/PullRequestsDetail.svelte.d.ts.map +1 -0
- package/dist/PullRequestsPage.svelte +47 -0
- package/dist/PullRequestsPage.svelte.d.ts +11 -0
- package/dist/PullRequestsPage.svelte.d.ts.map +1 -0
- package/dist/ReposTable.svelte +189 -0
- package/dist/ReposTable.svelte.d.ts +9 -0
- package/dist/ReposTable.svelte.d.ts.map +1 -0
- package/dist/ReposTree.svelte +88 -0
- package/dist/ReposTree.svelte.d.ts +11 -0
- package/dist/ReposTree.svelte.d.ts.map +1 -0
- package/dist/ReposTreeNav.svelte +55 -0
- package/dist/ReposTreeNav.svelte.d.ts +11 -0
- package/dist/ReposTreeNav.svelte.d.ts.map +1 -0
- package/dist/TablePage.svelte +46 -0
- package/dist/TablePage.svelte.d.ts +9 -0
- package/dist/TablePage.svelte.d.ts.map +1 -0
- package/dist/TreeItemPage.svelte +75 -0
- package/dist/TreeItemPage.svelte.d.ts +10 -0
- package/dist/TreeItemPage.svelte.d.ts.map +1 -0
- package/dist/TreePage.svelte +64 -0
- package/dist/TreePage.svelte.d.ts +9 -0
- package/dist/TreePage.svelte.d.ts.map +1 -0
- package/dist/changeset_generator.d.ts +38 -0
- package/dist/changeset_generator.d.ts.map +1 -0
- package/dist/changeset_generator.js +110 -0
- package/dist/changeset_reader.d.ts +75 -0
- package/dist/changeset_reader.d.ts.map +1 -0
- package/dist/changeset_reader.js +167 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +8 -0
- package/dist/dependency_graph.d.ts +120 -0
- package/dist/dependency_graph.d.ts.map +1 -0
- package/dist/dependency_graph.js +341 -0
- package/dist/dependency_updater.d.ts +46 -0
- package/dist/dependency_updater.d.ts.map +1 -0
- package/dist/dependency_updater.js +213 -0
- package/dist/fetch_repo_data.d.ts +19 -0
- package/dist/fetch_repo_data.d.ts.map +1 -0
- package/dist/fetch_repo_data.js +49 -0
- package/dist/fs_fetch_value_cache.d.ts +24 -0
- package/dist/fs_fetch_value_cache.d.ts.map +1 -0
- package/dist/fs_fetch_value_cache.js +61 -0
- package/dist/git_operations.d.ts +54 -0
- package/dist/git_operations.d.ts.map +1 -0
- package/dist/git_operations.js +144 -0
- package/dist/github.d.ts +91 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js +94 -0
- package/dist/github_helpers.d.ts +10 -0
- package/dist/github_helpers.d.ts.map +1 -0
- package/dist/github_helpers.js +13 -0
- package/dist/gitops_analyze.task.d.ts +17 -0
- package/dist/gitops_analyze.task.d.ts.map +1 -0
- package/dist/gitops_analyze.task.js +188 -0
- package/dist/gitops_config.d.ts +56 -0
- package/dist/gitops_config.d.ts.map +1 -0
- package/dist/gitops_config.js +63 -0
- package/dist/gitops_plan.task.d.ts +28 -0
- package/dist/gitops_plan.task.d.ts.map +1 -0
- package/dist/gitops_plan.task.js +217 -0
- package/dist/gitops_publish.task.d.ts +29 -0
- package/dist/gitops_publish.task.d.ts.map +1 -0
- package/dist/gitops_publish.task.js +178 -0
- package/dist/gitops_sync.task.d.ts +18 -0
- package/dist/gitops_sync.task.d.ts.map +1 -0
- package/dist/gitops_sync.task.js +95 -0
- package/dist/gitops_task_helpers.d.ts +63 -0
- package/dist/gitops_task_helpers.d.ts.map +1 -0
- package/dist/gitops_task_helpers.js +84 -0
- package/dist/gitops_validate.task.d.ts +12 -0
- package/dist/gitops_validate.task.d.ts.map +1 -0
- package/dist/gitops_validate.task.js +210 -0
- package/dist/graph_validation.d.ts +39 -0
- package/dist/graph_validation.d.ts.map +1 -0
- package/dist/graph_validation.js +79 -0
- package/dist/local_repo.d.ts +84 -0
- package/dist/local_repo.d.ts.map +1 -0
- package/dist/local_repo.js +213 -0
- package/dist/log_helpers.d.ts +43 -0
- package/dist/log_helpers.d.ts.map +1 -0
- package/dist/log_helpers.js +98 -0
- package/dist/multi_repo_publisher.d.ts +34 -0
- package/dist/multi_repo_publisher.d.ts.map +1 -0
- package/dist/multi_repo_publisher.js +364 -0
- package/dist/npm_install_helpers.d.ts +23 -0
- package/dist/npm_install_helpers.d.ts.map +1 -0
- package/dist/npm_install_helpers.js +60 -0
- package/dist/npm_registry.d.ts +46 -0
- package/dist/npm_registry.d.ts.map +1 -0
- package/dist/npm_registry.js +96 -0
- package/dist/operations.d.ts +409 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +34 -0
- package/dist/operations_defaults.d.ts +19 -0
- package/dist/operations_defaults.d.ts.map +1 -0
- package/dist/operations_defaults.js +279 -0
- package/dist/output_helpers.d.ts +27 -0
- package/dist/output_helpers.d.ts.map +1 -0
- package/dist/output_helpers.js +39 -0
- package/dist/paths.d.ts +11 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +10 -0
- package/dist/preflight_checks.d.ts +47 -0
- package/dist/preflight_checks.d.ts.map +1 -0
- package/dist/preflight_checks.js +181 -0
- package/dist/publishing_plan.d.ts +100 -0
- package/dist/publishing_plan.d.ts.map +1 -0
- package/dist/publishing_plan.js +353 -0
- package/dist/publishing_plan_helpers.d.ts +30 -0
- package/dist/publishing_plan_helpers.d.ts.map +1 -0
- package/dist/publishing_plan_helpers.js +112 -0
- package/dist/publishing_plan_logging.d.ts +18 -0
- package/dist/publishing_plan_logging.d.ts.map +1 -0
- package/dist/publishing_plan_logging.js +342 -0
- package/dist/repo.svelte.d.ts +52 -0
- package/dist/repo.svelte.d.ts.map +1 -0
- package/dist/repo.svelte.js +70 -0
- package/dist/repo_ops.d.ts +57 -0
- package/dist/repo_ops.d.ts.map +1 -0
- package/dist/repo_ops.js +167 -0
- package/dist/resolved_gitops_config.d.ts +9 -0
- package/dist/resolved_gitops_config.d.ts.map +1 -0
- package/dist/resolved_gitops_config.js +12 -0
- package/dist/semver.d.ts +24 -0
- package/dist/semver.d.ts.map +1 -0
- package/dist/semver.js +140 -0
- package/dist/serialization_types.d.ts +57 -0
- package/dist/serialization_types.d.ts.map +1 -0
- package/dist/serialization_types.js +40 -0
- package/dist/version_utils.d.ts +48 -0
- package/dist/version_utils.d.ts.map +1 -0
- package/dist/version_utils.js +125 -0
- package/package.json +107 -0
- package/src/lib/changeset_generator.ts +162 -0
- package/src/lib/changeset_reader.ts +218 -0
- package/src/lib/constants.ts +8 -0
- package/src/lib/dependency_graph.ts +423 -0
- package/src/lib/dependency_updater.ts +297 -0
- package/src/lib/fetch_repo_data.ts +64 -0
- package/src/lib/fs_fetch_value_cache.ts +75 -0
- package/src/lib/git_operations.ts +208 -0
- package/src/lib/github.ts +128 -0
- package/src/lib/github_helpers.ts +31 -0
- package/src/lib/gitops_analyze.task.ts +261 -0
- package/src/lib/gitops_config.ts +123 -0
- package/src/lib/gitops_plan.task.ts +272 -0
- package/src/lib/gitops_publish.task.ts +227 -0
- package/src/lib/gitops_sync.task.ts +109 -0
- package/src/lib/gitops_task_helpers.ts +126 -0
- package/src/lib/gitops_validate.task.ts +248 -0
- package/src/lib/graph_validation.ts +109 -0
- package/src/lib/local_repo.ts +359 -0
- package/src/lib/log_helpers.ts +147 -0
- package/src/lib/multi_repo_publisher.ts +464 -0
- package/src/lib/npm_install_helpers.ts +85 -0
- package/src/lib/npm_registry.ts +143 -0
- package/src/lib/operations.ts +334 -0
- package/src/lib/operations_defaults.ts +335 -0
- package/src/lib/output_helpers.ts +64 -0
- package/src/lib/paths.ts +11 -0
- package/src/lib/preflight_checks.ts +269 -0
- package/src/lib/publishing_plan.ts +531 -0
- package/src/lib/publishing_plan_helpers.ts +145 -0
- package/src/lib/publishing_plan_logging.ts +470 -0
- package/src/lib/repo.svelte.ts +95 -0
- package/src/lib/repo_ops.ts +213 -0
- package/src/lib/resolved_gitops_config.ts +27 -0
- package/src/lib/semver.ts +166 -0
- package/src/lib/serialization_types.ts +90 -0
- package/src/lib/version_utils.ts +150 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createInterface } from 'node:readline/promises';
|
|
3
|
+
import { styleText as st } from 'node:util';
|
|
4
|
+
import { get_gitops_ready } from './gitops_task_helpers.js';
|
|
5
|
+
import { publish_repos, } from './multi_repo_publisher.js';
|
|
6
|
+
import { generate_publishing_plan, log_publishing_plan } from './publishing_plan.js';
|
|
7
|
+
import { format_and_output } from './output_helpers.js';
|
|
8
|
+
/** @nodocs */
|
|
9
|
+
export const Args = z.strictObject({
|
|
10
|
+
path: z
|
|
11
|
+
.string()
|
|
12
|
+
.meta({ description: 'path to the gitops config file, absolute or relative to the cwd' })
|
|
13
|
+
.default('gitops.config.ts'),
|
|
14
|
+
dir: z
|
|
15
|
+
.string()
|
|
16
|
+
.meta({ description: 'path containing the repos, defaults to the parent of the `path` dir' })
|
|
17
|
+
.optional(),
|
|
18
|
+
peer_strategy: z
|
|
19
|
+
.enum(['exact', 'caret', 'tilde'])
|
|
20
|
+
.meta({ description: 'version strategy for peer dependencies' })
|
|
21
|
+
.default('caret'),
|
|
22
|
+
dry_run: z
|
|
23
|
+
.boolean()
|
|
24
|
+
.meta({ description: 'perform a dry run without actually publishing' })
|
|
25
|
+
.default(false),
|
|
26
|
+
format: z
|
|
27
|
+
.enum(['stdout', 'json', 'markdown'])
|
|
28
|
+
.meta({ description: 'output format' })
|
|
29
|
+
.default('stdout'),
|
|
30
|
+
deploy: z.boolean().meta({ description: 'deploy all repos after publishing' }).default(false),
|
|
31
|
+
plan: z.boolean().meta({ description: 'dual of no-plan' }).default(true),
|
|
32
|
+
'no-plan': z
|
|
33
|
+
.boolean()
|
|
34
|
+
.meta({ description: 'skip plan confirmation before publishing' })
|
|
35
|
+
.default(false),
|
|
36
|
+
max_wait: z
|
|
37
|
+
.number()
|
|
38
|
+
.meta({ description: 'max time to wait for npm propagation in ms' })
|
|
39
|
+
.default(600000), // 10 minutes
|
|
40
|
+
skip_install: z
|
|
41
|
+
.boolean()
|
|
42
|
+
.meta({ description: 'skip npm install after dependency updates' })
|
|
43
|
+
.default(false),
|
|
44
|
+
outfile: z.string().meta({ description: 'write output to file instead of logging' }).optional(),
|
|
45
|
+
verbose: z.boolean().meta({ description: 'show additional details in plan output' }).default(false),
|
|
46
|
+
});
|
|
47
|
+
/** @nodocs */
|
|
48
|
+
export const task = {
|
|
49
|
+
summary: 'publish all repos in dependency order',
|
|
50
|
+
Args,
|
|
51
|
+
run: async ({ args, log }) => {
|
|
52
|
+
const { path, dir, peer_strategy, dry_run, format, deploy, plan, max_wait, skip_install, outfile, verbose, } = args;
|
|
53
|
+
// Load repos
|
|
54
|
+
const { local_repos: repos } = await get_gitops_ready({
|
|
55
|
+
path,
|
|
56
|
+
dir,
|
|
57
|
+
download: false, // Don't download if missing
|
|
58
|
+
log,
|
|
59
|
+
});
|
|
60
|
+
// Show plan if requested (skip for dry runs)
|
|
61
|
+
if (plan && !dry_run) {
|
|
62
|
+
log.info(st('cyan', 'Publishing Plan'));
|
|
63
|
+
const plan_result = await generate_publishing_plan(repos, { log, verbose });
|
|
64
|
+
log_publishing_plan(plan_result, log, { verbose });
|
|
65
|
+
if (plan_result.errors.length > 0) {
|
|
66
|
+
throw new Error('Cannot proceed with publishing due to errors');
|
|
67
|
+
}
|
|
68
|
+
// Ask for confirmation
|
|
69
|
+
log.info(st('yellow', '⚠️ This will publish the packages shown above.'));
|
|
70
|
+
process.stdout.write('Continue with publishing? (y/n): ');
|
|
71
|
+
const confirmed = await prompt_for_confirmation();
|
|
72
|
+
if (!confirmed) {
|
|
73
|
+
log.info('Publishing cancelled');
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Publishing options
|
|
78
|
+
const options = {
|
|
79
|
+
dry_run,
|
|
80
|
+
update_deps: true, // Always update dependencies
|
|
81
|
+
version_strategy: peer_strategy,
|
|
82
|
+
deploy,
|
|
83
|
+
max_wait,
|
|
84
|
+
skip_install,
|
|
85
|
+
log,
|
|
86
|
+
};
|
|
87
|
+
// Execute publishing (may throw on fatal errors like circular dependencies)
|
|
88
|
+
let result;
|
|
89
|
+
let fatal_error = null;
|
|
90
|
+
try {
|
|
91
|
+
result = await publish_repos(repos, options);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
// Construct a failure result for fatal errors so output can still be generated
|
|
95
|
+
fatal_error = error instanceof Error ? error : new Error(String(error));
|
|
96
|
+
result = {
|
|
97
|
+
ok: false,
|
|
98
|
+
published: [],
|
|
99
|
+
// Note: FATAL_ERROR is a placeholder - only fatal_error.message is displayed in output
|
|
100
|
+
failed: [{ name: 'FATAL_ERROR', error: fatal_error }],
|
|
101
|
+
duration: 0,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// Format and output result (always runs, even on fatal errors)
|
|
105
|
+
// Note: stdout format is handled by publish_repos function's logging
|
|
106
|
+
if (format !== 'stdout') {
|
|
107
|
+
await format_and_output({ result, fatal_error }, create_publish_formatters(), {
|
|
108
|
+
format,
|
|
109
|
+
outfile,
|
|
110
|
+
log,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Exit with error if failed
|
|
114
|
+
if (!result.ok || fatal_error) {
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
const create_publish_formatters = () => ({
|
|
120
|
+
json: (data) => JSON.stringify(data.result, null, 2),
|
|
121
|
+
markdown: (data) => format_result_markdown(data.result, data.fatal_error),
|
|
122
|
+
stdout: () => {
|
|
123
|
+
// stdout format is handled by publish_repos function's logging
|
|
124
|
+
// This should never be called due to early return in task
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
// Format the publishing result as markdown
|
|
128
|
+
const format_result_markdown = (result, fatal_error) => {
|
|
129
|
+
const lines = [];
|
|
130
|
+
lines.push('# Publishing Result');
|
|
131
|
+
lines.push('');
|
|
132
|
+
// Show fatal error prominently if present
|
|
133
|
+
if (fatal_error) {
|
|
134
|
+
lines.push('## ❌ Fatal Error');
|
|
135
|
+
lines.push('');
|
|
136
|
+
lines.push(`**Error**: ${fatal_error.message}`);
|
|
137
|
+
lines.push('');
|
|
138
|
+
lines.push('Publishing could not proceed due to the error above.');
|
|
139
|
+
lines.push('');
|
|
140
|
+
return lines;
|
|
141
|
+
}
|
|
142
|
+
lines.push(`**Status**: ${result.ok ? '✅ Success' : '❌ Failed'}`);
|
|
143
|
+
lines.push(`**Duration**: ${(result.duration / 1000).toFixed(1)}s`);
|
|
144
|
+
lines.push(`**Published**: ${result.published.length} packages`);
|
|
145
|
+
if (result.failed.length > 0) {
|
|
146
|
+
lines.push(`**Failed**: ${result.failed.length} packages`);
|
|
147
|
+
}
|
|
148
|
+
if (result.published.length > 0) {
|
|
149
|
+
lines.push('');
|
|
150
|
+
lines.push('## Published Packages');
|
|
151
|
+
lines.push('');
|
|
152
|
+
for (const pkg of result.published) {
|
|
153
|
+
lines.push(`- \`${pkg.name}\`: ${pkg.old_version} → ${pkg.new_version}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (result.failed.length > 0) {
|
|
157
|
+
lines.push('');
|
|
158
|
+
lines.push('## Failed Packages');
|
|
159
|
+
lines.push('');
|
|
160
|
+
for (const { name, error } of result.failed) {
|
|
161
|
+
lines.push(`- \`${name}\`: ${error.message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return lines;
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Prompts user for y/n confirmation.
|
|
168
|
+
* Returns true if user enters 'y', false otherwise.
|
|
169
|
+
*/
|
|
170
|
+
const prompt_for_confirmation = async () => {
|
|
171
|
+
const rl = createInterface({
|
|
172
|
+
input: process.stdin,
|
|
173
|
+
output: process.stdout,
|
|
174
|
+
});
|
|
175
|
+
const answer = await rl.question('');
|
|
176
|
+
rl.close();
|
|
177
|
+
return answer.toLowerCase() === 'y';
|
|
178
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Task } from '@ryanatkn/gro';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/** @nodocs */
|
|
4
|
+
export declare const Args: z.ZodObject<{
|
|
5
|
+
path: z.ZodDefault<z.ZodString>;
|
|
6
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
7
|
+
outdir: z.ZodOptional<z.ZodString>;
|
|
8
|
+
download: z.ZodDefault<z.ZodBoolean>;
|
|
9
|
+
check: z.ZodDefault<z.ZodBoolean>;
|
|
10
|
+
}, z.core.$strict>;
|
|
11
|
+
export type Args = z.infer<typeof Args>;
|
|
12
|
+
/**
|
|
13
|
+
* This is a task not a `.gen.` file because it makes network calls.
|
|
14
|
+
*
|
|
15
|
+
* @nodocs
|
|
16
|
+
*/
|
|
17
|
+
export declare const task: Task<Args>;
|
|
18
|
+
//# sourceMappingURL=gitops_sync.task.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitops_sync.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_sync.task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,IAAI,EAAC,MAAM,eAAe,CAAC;AACnD,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAetB,cAAc;AACd,eAAO,MAAM,IAAI;;;;;;kBAkBf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CAiE3B,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { TaskError } from '@ryanatkn/gro';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { format_file } from '@ryanatkn/gro/format_file.js';
|
|
5
|
+
import { basename, resolve } from 'node:path';
|
|
6
|
+
import { print_path } from '@ryanatkn/gro/paths.js';
|
|
7
|
+
import { load_from_env } from '@ryanatkn/gro/env.js';
|
|
8
|
+
import { load_package_json } from '@ryanatkn/gro/package_json.js';
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { fetch_repo_data } from './fetch_repo_data.js';
|
|
11
|
+
import { create_fs_fetch_value_cache } from './fs_fetch_value_cache.js';
|
|
12
|
+
import { get_gitops_ready } from './gitops_task_helpers.js';
|
|
13
|
+
// TODO add flag to ignore or invalidate cache -- no-cache? clean?
|
|
14
|
+
/** @nodocs */
|
|
15
|
+
export const Args = z.strictObject({
|
|
16
|
+
path: z
|
|
17
|
+
.string()
|
|
18
|
+
.meta({ description: 'path to the gitops config file, absolute or relative to the cwd' })
|
|
19
|
+
.default('gitops.config.ts'),
|
|
20
|
+
dir: z
|
|
21
|
+
.string()
|
|
22
|
+
.meta({ description: 'path containing the repos, defaults to the parent of the `path` dir' })
|
|
23
|
+
.optional(),
|
|
24
|
+
outdir: z
|
|
25
|
+
.string()
|
|
26
|
+
.meta({ description: 'path to the directory for the generated files, defaults to $routes/' })
|
|
27
|
+
.optional(),
|
|
28
|
+
download: z.boolean().meta({ description: 'download all missing local repos' }).default(false),
|
|
29
|
+
check: z
|
|
30
|
+
.boolean()
|
|
31
|
+
.meta({ description: 'check repos are ready without fetching remote data' })
|
|
32
|
+
.default(false),
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* This is a task not a `.gen.` file because it makes network calls.
|
|
36
|
+
*
|
|
37
|
+
* @nodocs
|
|
38
|
+
*/
|
|
39
|
+
export const task = {
|
|
40
|
+
Args,
|
|
41
|
+
summary: 'syncs local repos and generates UI data from repo metadata',
|
|
42
|
+
run: async ({ args, log, svelte_config, invoke_task }) => {
|
|
43
|
+
const { path, dir, outdir = svelte_config.routes_path, download, check } = args;
|
|
44
|
+
const { local_repos } = await get_gitops_ready({ path, dir, download, log });
|
|
45
|
+
const outfile = resolve(outdir, 'repos.ts');
|
|
46
|
+
// This searches the parent directory for the env var, so we don't use SvelteKit's $env imports
|
|
47
|
+
const token = load_from_env('SECRET_GITHUB_API_TOKEN');
|
|
48
|
+
if (!token) {
|
|
49
|
+
throw new TaskError('the env var SECRET_GITHUB_API_TOKEN was not found');
|
|
50
|
+
}
|
|
51
|
+
// Exit early if only checking repo readiness
|
|
52
|
+
if (check) {
|
|
53
|
+
log.info('repos are ready');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const cache = await create_fs_fetch_value_cache('repos');
|
|
57
|
+
log.info('fetching remote repo data');
|
|
58
|
+
const repos_json = await fetch_repo_data(local_repos, token, cache.data, log);
|
|
59
|
+
// TODO should package_json be provided in the Gro task/gen contexts? check if it's always loaded
|
|
60
|
+
const package_json = await load_package_json();
|
|
61
|
+
const repo_specifier = package_json.name === '@fuzdev/fuz_gitops'
|
|
62
|
+
? '$lib/repo.svelte.js'
|
|
63
|
+
: '@fuzdev/fuz_gitops/repo.svelte.js';
|
|
64
|
+
log.info('generating ' + outfile);
|
|
65
|
+
// TODO the `basename` is used here because we don't have an `origin_id` like with gen,
|
|
66
|
+
// and this file gets re-exported,
|
|
67
|
+
// and we don't want the file to change based on where it's being generated,
|
|
68
|
+
// because for example linking to a local package would change the contents
|
|
69
|
+
const contents = `
|
|
70
|
+
// generated by ${basename(import.meta.filename)} !! do not edit directly or risk lost data
|
|
71
|
+
|
|
72
|
+
import type {RepoJson} from '${repo_specifier}';
|
|
73
|
+
|
|
74
|
+
export const repos_json: Array<RepoJson> = ${JSON.stringify(repos_json, null, '\t')};
|
|
75
|
+
`;
|
|
76
|
+
// TODO think about possibly using the `gen` functionality in this task, not sure what the API design could look like
|
|
77
|
+
const formatted = await format_file(contents, { filepath: outfile });
|
|
78
|
+
const existing = existsSync(outfile) ? await readFile(outfile, 'utf8') : '';
|
|
79
|
+
if (existing === formatted) {
|
|
80
|
+
log.info(`no changes to ${print_path(outfile)}`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
log.info(`writing changes to ${print_path(outfile)}`);
|
|
84
|
+
await writeFile(outfile, formatted);
|
|
85
|
+
await invoke_task('gen');
|
|
86
|
+
}
|
|
87
|
+
const changed = await cache.save();
|
|
88
|
+
if (changed) {
|
|
89
|
+
log.info('repos cache updated');
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
log.info('repos cache did not change');
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared initialization logic for all gitops tasks.
|
|
3
|
+
*
|
|
4
|
+
* Provides `get_gitops_ready()` which orchestrates:
|
|
5
|
+
* - Config loading and normalization
|
|
6
|
+
* - Repo resolution (local path discovery)
|
|
7
|
+
* - Branch switching and syncing
|
|
8
|
+
* - Dependency installation
|
|
9
|
+
*
|
|
10
|
+
* Used by: `gitops_sync.task.ts`, `gitops_analyze.task.ts`, `gitops_plan.task.ts`,
|
|
11
|
+
* `gitops_publish.task.ts`, and `gitops_validate.task.ts`.
|
|
12
|
+
*
|
|
13
|
+
* Accepts `git_ops` and `npm_ops` parameters to support testing via operations pattern
|
|
14
|
+
* (see `operations.ts` for dependency injection details).
|
|
15
|
+
*/
|
|
16
|
+
import type { Logger } from '@fuzdev/fuz_util/log.js';
|
|
17
|
+
import { type GitopsConfig } from './gitops_config.js';
|
|
18
|
+
import { type LocalRepo } from './local_repo.js';
|
|
19
|
+
import type { GitOperations, NpmOperations } from './operations.js';
|
|
20
|
+
export interface GetGitopsReadyOptions {
|
|
21
|
+
path: string;
|
|
22
|
+
dir?: string;
|
|
23
|
+
download: boolean;
|
|
24
|
+
log?: Logger;
|
|
25
|
+
git_ops?: GitOperations;
|
|
26
|
+
npm_ops?: NpmOperations;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Central initialization function for all gitops tasks.
|
|
30
|
+
*
|
|
31
|
+
* Initialization sequence:
|
|
32
|
+
* 1. Loads and normalizes config from `gitops.config.ts`
|
|
33
|
+
* 2. Resolves local repo paths (creates missing with `--download`)
|
|
34
|
+
* 3. Switches branches and pulls latest changes
|
|
35
|
+
* 4. Auto-installs deps if package.json changed during pull
|
|
36
|
+
*
|
|
37
|
+
* Priority for path resolution:
|
|
38
|
+
* - `dir` argument (explicit override)
|
|
39
|
+
* - Config `repos_dir` setting
|
|
40
|
+
* - `DEFAULT_REPOS_DIR` constant
|
|
41
|
+
*
|
|
42
|
+
* @param options.git_ops for testing (defaults to real git operations)
|
|
43
|
+
* @param options.npm_ops for testing (defaults to real npm operations)
|
|
44
|
+
* @returns initialized config and fully loaded repos ready for operations
|
|
45
|
+
* @throws {TaskError} if config loading or repo resolution fails
|
|
46
|
+
*/
|
|
47
|
+
export declare const get_gitops_ready: (options: GetGitopsReadyOptions) => Promise<{
|
|
48
|
+
config_path: string;
|
|
49
|
+
repos_dir: string;
|
|
50
|
+
gitops_config: GitopsConfig;
|
|
51
|
+
local_repos: Array<LocalRepo>;
|
|
52
|
+
}>;
|
|
53
|
+
export interface ResolveGitopsPathsOptions {
|
|
54
|
+
path: string;
|
|
55
|
+
dir?: string;
|
|
56
|
+
config_repos_dir?: string;
|
|
57
|
+
}
|
|
58
|
+
export declare const resolve_gitops_paths: (options: ResolveGitopsPathsOptions) => {
|
|
59
|
+
config_path: string;
|
|
60
|
+
repos_dir: string;
|
|
61
|
+
};
|
|
62
|
+
export declare const import_gitops_config: (config_path: string) => Promise<GitopsConfig>;
|
|
63
|
+
//# sourceMappingURL=gitops_task_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitops_task_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_task_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAqB,KAAK,YAAY,EAAC,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAuC,KAAK,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAGrF,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAElE,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,CAAC,EAAE,aAAa,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,GAC5B,SAAS,qBAAqB,KAC5B,OAAO,CAAC;IACV,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,YAAY,CAAC;IAC5B,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC9B,CA8BA,CAAC;AAEF,MAAM,WAAW,yBAAyB;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,oBAAoB,GAChC,SAAS,yBAAyB,KAChC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAczC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,YAAY,CAMpF,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared initialization logic for all gitops tasks.
|
|
3
|
+
*
|
|
4
|
+
* Provides `get_gitops_ready()` which orchestrates:
|
|
5
|
+
* - Config loading and normalization
|
|
6
|
+
* - Repo resolution (local path discovery)
|
|
7
|
+
* - Branch switching and syncing
|
|
8
|
+
* - Dependency installation
|
|
9
|
+
*
|
|
10
|
+
* Used by: `gitops_sync.task.ts`, `gitops_analyze.task.ts`, `gitops_plan.task.ts`,
|
|
11
|
+
* `gitops_publish.task.ts`, and `gitops_validate.task.ts`.
|
|
12
|
+
*
|
|
13
|
+
* Accepts `git_ops` and `npm_ops` parameters to support testing via operations pattern
|
|
14
|
+
* (see `operations.ts` for dependency injection details).
|
|
15
|
+
*/
|
|
16
|
+
import { TaskError } from '@ryanatkn/gro';
|
|
17
|
+
import { styleText as st } from 'node:util';
|
|
18
|
+
import { resolve, dirname } from 'node:path';
|
|
19
|
+
import { print_path } from '@ryanatkn/gro/paths.js';
|
|
20
|
+
import { load_gitops_config } from './gitops_config.js';
|
|
21
|
+
import { local_repos_load, local_repos_ensure } from './local_repo.js';
|
|
22
|
+
import { resolve_gitops_config } from './resolved_gitops_config.js';
|
|
23
|
+
import { DEFAULT_REPOS_DIR } from './paths.js';
|
|
24
|
+
/**
|
|
25
|
+
* Central initialization function for all gitops tasks.
|
|
26
|
+
*
|
|
27
|
+
* Initialization sequence:
|
|
28
|
+
* 1. Loads and normalizes config from `gitops.config.ts`
|
|
29
|
+
* 2. Resolves local repo paths (creates missing with `--download`)
|
|
30
|
+
* 3. Switches branches and pulls latest changes
|
|
31
|
+
* 4. Auto-installs deps if package.json changed during pull
|
|
32
|
+
*
|
|
33
|
+
* Priority for path resolution:
|
|
34
|
+
* - `dir` argument (explicit override)
|
|
35
|
+
* - Config `repos_dir` setting
|
|
36
|
+
* - `DEFAULT_REPOS_DIR` constant
|
|
37
|
+
*
|
|
38
|
+
* @param options.git_ops for testing (defaults to real git operations)
|
|
39
|
+
* @param options.npm_ops for testing (defaults to real npm operations)
|
|
40
|
+
* @returns initialized config and fully loaded repos ready for operations
|
|
41
|
+
* @throws {TaskError} if config loading or repo resolution fails
|
|
42
|
+
*/
|
|
43
|
+
export const get_gitops_ready = async (options) => {
|
|
44
|
+
const { path, dir, download, log, git_ops, npm_ops } = options;
|
|
45
|
+
const config_path = resolve(path);
|
|
46
|
+
const gitops_config = await import_gitops_config(config_path);
|
|
47
|
+
// Priority: explicit dir arg → config repos_dir → default (two dirs up from config)
|
|
48
|
+
const repos_dir = resolve_gitops_paths({
|
|
49
|
+
path,
|
|
50
|
+
dir,
|
|
51
|
+
config_repos_dir: gitops_config.repos_dir,
|
|
52
|
+
}).repos_dir;
|
|
53
|
+
log?.info(`resolving gitops configs on the filesystem in ${repos_dir}`, gitops_config.repos.map((r) => r.repo_url));
|
|
54
|
+
const resolved_config = resolve_gitops_config(gitops_config, repos_dir);
|
|
55
|
+
const local_repo_paths = await local_repos_ensure({
|
|
56
|
+
resolved_config,
|
|
57
|
+
repos_dir,
|
|
58
|
+
gitops_config,
|
|
59
|
+
download,
|
|
60
|
+
log,
|
|
61
|
+
npm_ops,
|
|
62
|
+
});
|
|
63
|
+
const local_repos = await local_repos_load({ local_repo_paths, log, git_ops, npm_ops });
|
|
64
|
+
return { config_path, repos_dir, gitops_config, local_repos };
|
|
65
|
+
};
|
|
66
|
+
export const resolve_gitops_paths = (options) => {
|
|
67
|
+
const { path, dir, config_repos_dir } = options;
|
|
68
|
+
const config_path = resolve(path);
|
|
69
|
+
const config_dir = dirname(config_path);
|
|
70
|
+
// Priority: explicit dir arg → config repos_dir → default (parent of config dir)
|
|
71
|
+
const repos_dir = dir !== undefined
|
|
72
|
+
? resolve(dir)
|
|
73
|
+
: config_repos_dir !== undefined
|
|
74
|
+
? resolve(config_dir, config_repos_dir)
|
|
75
|
+
: resolve(config_dir, DEFAULT_REPOS_DIR);
|
|
76
|
+
return { config_path, repos_dir };
|
|
77
|
+
};
|
|
78
|
+
export const import_gitops_config = async (config_path) => {
|
|
79
|
+
const gitops_config = await load_gitops_config(config_path);
|
|
80
|
+
if (!gitops_config) {
|
|
81
|
+
throw new TaskError(st('red', `No gitops config found at ${print_path(config_path)}`));
|
|
82
|
+
}
|
|
83
|
+
return gitops_config;
|
|
84
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Task } from '@ryanatkn/gro';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/** @nodocs */
|
|
4
|
+
export declare const Args: z.ZodObject<{
|
|
5
|
+
path: z.ZodDefault<z.ZodString>;
|
|
6
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
7
|
+
verbose: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
}, z.core.$strict>;
|
|
9
|
+
export type Args = z.infer<typeof Args>;
|
|
10
|
+
/** @nodocs */
|
|
11
|
+
export declare const task: Task<Args>;
|
|
12
|
+
//# sourceMappingURL=gitops_validate.task.d.ts.map
|
|
@@ -0,0 +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,EAAC,IAAI,EAAC,MAAM,eAAe,CAAC;AACxC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAUtB,cAAc;AACd,eAAO,MAAM,IAAI;;;;kBAUf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC,cAAc;AACd,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CA6N3B,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { styleText as st } from 'node:util';
|
|
3
|
+
import { get_gitops_ready } from './gitops_task_helpers.js';
|
|
4
|
+
import { validate_dependency_graph } from './graph_validation.js';
|
|
5
|
+
import { DependencyGraphBuilder } from './dependency_graph.js';
|
|
6
|
+
import { generate_publishing_plan, log_publishing_plan } from './publishing_plan.js';
|
|
7
|
+
import { publish_repos } from './multi_repo_publisher.js';
|
|
8
|
+
import { log_dependency_analysis } from './log_helpers.js';
|
|
9
|
+
/** @nodocs */
|
|
10
|
+
export const Args = z.strictObject({
|
|
11
|
+
path: z
|
|
12
|
+
.string()
|
|
13
|
+
.meta({ description: 'path to the gitops config file, absolute or relative to the cwd' })
|
|
14
|
+
.default('gitops.config.ts'),
|
|
15
|
+
dir: z
|
|
16
|
+
.string()
|
|
17
|
+
.meta({ description: 'path containing the repos, defaults to the parent of the `path` dir' })
|
|
18
|
+
.optional(),
|
|
19
|
+
verbose: z.boolean().meta({ description: 'show additional details' }).default(false),
|
|
20
|
+
});
|
|
21
|
+
/** @nodocs */
|
|
22
|
+
export const task = {
|
|
23
|
+
Args,
|
|
24
|
+
summary: 'validate gitops configuration by running all read-only commands and checking for issues',
|
|
25
|
+
run: async ({ args, log }) => {
|
|
26
|
+
const { path, dir, verbose } = args;
|
|
27
|
+
log.info(st('cyan', 'Running Gitops Validation Suite'));
|
|
28
|
+
log.info(st('dim', 'This runs all read-only commands and checks for consistency.'));
|
|
29
|
+
const results = [];
|
|
30
|
+
const start_time = Date.now();
|
|
31
|
+
// Load repos once (shared by all commands)
|
|
32
|
+
log.info(st('dim', 'Loading repositories...'));
|
|
33
|
+
const { local_repos } = await get_gitops_ready({ path, dir, download: false, log });
|
|
34
|
+
log.info(st('dim', ` Found ${local_repos.length} local repos`));
|
|
35
|
+
// 1. Run gitops_analyze
|
|
36
|
+
log.info(st('yellow', 'Running gitops_analyze...'));
|
|
37
|
+
const analyze_start = Date.now();
|
|
38
|
+
try {
|
|
39
|
+
// Build dependency graph and validate (but don't throw on cycles for analyze)
|
|
40
|
+
const { graph } = validate_dependency_graph(local_repos, {
|
|
41
|
+
throw_on_prod_cycles: false, // Analyze should report, not throw
|
|
42
|
+
log_cycles: false, // We'll collect our own statistics
|
|
43
|
+
log_order: false,
|
|
44
|
+
});
|
|
45
|
+
// Perform additional analysis
|
|
46
|
+
const builder = new DependencyGraphBuilder();
|
|
47
|
+
const analysis = builder.analyze(graph);
|
|
48
|
+
const analyze_duration = Date.now() - analyze_start;
|
|
49
|
+
// Collect warnings, info, and errors
|
|
50
|
+
const warning_details = [];
|
|
51
|
+
const info_details = [];
|
|
52
|
+
if (analysis.wildcard_deps.length > 0) {
|
|
53
|
+
warning_details.push('wildcard dependencies');
|
|
54
|
+
}
|
|
55
|
+
if (analysis.dev_cycles.length > 0) {
|
|
56
|
+
info_details.push('dev circular dependencies');
|
|
57
|
+
}
|
|
58
|
+
const warnings = warning_details.length;
|
|
59
|
+
const errors = analysis.production_cycles.length > 0 ? 1 : 0;
|
|
60
|
+
results.push({
|
|
61
|
+
command: 'gitops_analyze',
|
|
62
|
+
success: true,
|
|
63
|
+
warnings,
|
|
64
|
+
errors,
|
|
65
|
+
duration: analyze_duration,
|
|
66
|
+
warning_details,
|
|
67
|
+
info_details,
|
|
68
|
+
analysis,
|
|
69
|
+
});
|
|
70
|
+
log.info(st('green', ` ✓ gitops_analyze completed in ${analyze_duration}ms`));
|
|
71
|
+
// Print detailed analysis
|
|
72
|
+
log_dependency_analysis(analysis, log, ' ');
|
|
73
|
+
if (errors > 0) {
|
|
74
|
+
log.error(st('red', ` ❌ Found ${errors} error(s)`));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const analyze_duration = Date.now() - analyze_start;
|
|
79
|
+
results.push({
|
|
80
|
+
command: 'gitops_analyze',
|
|
81
|
+
success: false,
|
|
82
|
+
warnings: 0,
|
|
83
|
+
errors: 1,
|
|
84
|
+
duration: analyze_duration,
|
|
85
|
+
});
|
|
86
|
+
log.error(st('red', ` ✗ gitops_analyze failed: ${error}`));
|
|
87
|
+
}
|
|
88
|
+
// 2. Run gitops_plan
|
|
89
|
+
log.info(st('yellow', 'Running gitops_plan...'));
|
|
90
|
+
const plan_start = Date.now();
|
|
91
|
+
try {
|
|
92
|
+
const plan = await generate_publishing_plan(local_repos, { log: undefined, verbose });
|
|
93
|
+
const plan_duration = Date.now() - plan_start;
|
|
94
|
+
const warnings = plan.warnings.length;
|
|
95
|
+
const errors = plan.errors.length;
|
|
96
|
+
results.push({
|
|
97
|
+
command: 'gitops_plan',
|
|
98
|
+
success: true,
|
|
99
|
+
warnings,
|
|
100
|
+
errors,
|
|
101
|
+
duration: plan_duration,
|
|
102
|
+
});
|
|
103
|
+
log.info(st('green', ` ✓ gitops_plan completed in ${plan_duration}ms`));
|
|
104
|
+
if (verbose) {
|
|
105
|
+
log_publishing_plan(plan, log, { verbose });
|
|
106
|
+
}
|
|
107
|
+
if (warnings > 0) {
|
|
108
|
+
log.warn(st('yellow', ` ⚠️ Found ${warnings} warning(s)`));
|
|
109
|
+
}
|
|
110
|
+
if (errors > 0) {
|
|
111
|
+
log.error(st('red', ` ❌ Found ${errors} error(s)`));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
const plan_duration = Date.now() - plan_start;
|
|
116
|
+
results.push({
|
|
117
|
+
command: 'gitops_plan',
|
|
118
|
+
success: false,
|
|
119
|
+
warnings: 0,
|
|
120
|
+
errors: 1,
|
|
121
|
+
duration: plan_duration,
|
|
122
|
+
});
|
|
123
|
+
log.error(st('red', ` ✗ gitops_plan failed: ${error}`));
|
|
124
|
+
}
|
|
125
|
+
// 3. Run gitops_publish --dry_run
|
|
126
|
+
log.info(st('yellow', 'Running gitops_publish --dry_run...'));
|
|
127
|
+
const dry_start = Date.now();
|
|
128
|
+
try {
|
|
129
|
+
const options = {
|
|
130
|
+
dry_run: true,
|
|
131
|
+
update_deps: true,
|
|
132
|
+
log: undefined, // Silent for validation
|
|
133
|
+
};
|
|
134
|
+
const result = await publish_repos(local_repos, options);
|
|
135
|
+
const dry_duration = Date.now() - dry_start;
|
|
136
|
+
// Dry run doesn't have warnings/errors in the same format
|
|
137
|
+
// We'll just check if it succeeded
|
|
138
|
+
const errors = result.ok ? 0 : result.failed.length;
|
|
139
|
+
results.push({
|
|
140
|
+
command: 'gitops_publish --dry_run',
|
|
141
|
+
success: result.ok,
|
|
142
|
+
warnings: 0,
|
|
143
|
+
errors,
|
|
144
|
+
duration: dry_duration,
|
|
145
|
+
});
|
|
146
|
+
log.info(st('green', ` ✓ gitops_publish --dry_run completed in ${dry_duration}ms`));
|
|
147
|
+
if (errors > 0) {
|
|
148
|
+
log.error(st('red', ` ❌ Found ${errors} error(s)`));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
const dry_duration = Date.now() - dry_start;
|
|
153
|
+
results.push({
|
|
154
|
+
command: 'gitops_publish --dry_run',
|
|
155
|
+
success: false,
|
|
156
|
+
warnings: 0,
|
|
157
|
+
errors: 1,
|
|
158
|
+
duration: dry_duration,
|
|
159
|
+
});
|
|
160
|
+
log.error(st('red', ` ✗ gitops_publish --dry_run failed: ${error}`));
|
|
161
|
+
}
|
|
162
|
+
// Summary
|
|
163
|
+
const total_duration = Date.now() - start_time;
|
|
164
|
+
const all_success = results.every((r) => r.success);
|
|
165
|
+
const total_warnings = results.reduce((sum, r) => sum + r.warnings, 0);
|
|
166
|
+
const total_errors = results.reduce((sum, r) => sum + r.errors, 0);
|
|
167
|
+
log.info(st('cyan', 'Validation Summary'));
|
|
168
|
+
log.info(` Total duration: ${(total_duration / 1000).toFixed(1)}s`);
|
|
169
|
+
log.info(` Commands run: ${results.length}`);
|
|
170
|
+
log.info(` Commands succeeded: ${results.filter((r) => r.success).length}`);
|
|
171
|
+
log.info(` Commands failed: ${results.filter((r) => !r.success).length}`);
|
|
172
|
+
log.info(` Total warnings: ${total_warnings}`);
|
|
173
|
+
log.info(` Total errors: ${total_errors}`);
|
|
174
|
+
// Individual command results
|
|
175
|
+
log.info(st('cyan', 'Command Results:'));
|
|
176
|
+
for (const result of results) {
|
|
177
|
+
const status_icon = result.success ? '✓' : '✗';
|
|
178
|
+
const status_color = result.success ? 'green' : 'red';
|
|
179
|
+
const duration = (result.duration / 1000).toFixed(1);
|
|
180
|
+
log.info(st(status_color, ` ${status_icon} ${result.command} (${duration}s)`));
|
|
181
|
+
if (result.warnings > 0) {
|
|
182
|
+
const details = result.warning_details?.length
|
|
183
|
+
? ` (${result.warning_details.join(', ')})`
|
|
184
|
+
: '';
|
|
185
|
+
log.info(st('yellow', ` ⚠️ ${result.warnings} warning(s)${details}`));
|
|
186
|
+
}
|
|
187
|
+
if (result.info_details && result.info_details.length > 0) {
|
|
188
|
+
log.info(st('dim', ` ℹ️ ${result.info_details.join(', ')}`));
|
|
189
|
+
}
|
|
190
|
+
if (result.errors > 0) {
|
|
191
|
+
log.info(st('red', ` ❌ ${result.errors} error(s)`));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Final verdict
|
|
195
|
+
log.info('');
|
|
196
|
+
if (all_success && total_errors === 0) {
|
|
197
|
+
log.info(st('green', '✓ All validation checks passed'));
|
|
198
|
+
if (total_warnings > 0) {
|
|
199
|
+
log.warn(st('yellow', `⚠️ Note: ${total_warnings} warning(s) found - review output above.`));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (all_success && total_errors > 0) {
|
|
203
|
+
log.warn(st('yellow', '⚠️ Validation completed but found errors - review output above.'));
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
log.error(st('red', '❌ Validation failed - one or more commands did not complete.'));
|
|
207
|
+
throw new Error('Validation failed');
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
};
|