@fuzdev/fuz_gitops 0.76.0 → 0.78.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 +5 -5
- package/dist/ModulesDetail.svelte +1 -1
- package/dist/PageFooter.svelte +2 -2
- package/dist/PageHeader.svelte +1 -1
- package/dist/PageHeader.svelte.d.ts +1 -1
- package/dist/ReposTable.svelte +1 -1
- package/dist/ReposTree.svelte +1 -1
- package/dist/cargo_toml.d.ts +33 -0
- package/dist/cargo_toml.d.ts.map +1 -0
- package/dist/cargo_toml.js +74 -0
- package/dist/changeset_generator.d.ts +1 -1
- package/dist/changeset_reader.d.ts +1 -1
- package/dist/ci_reconcile.d.ts +50 -0
- package/dist/ci_reconcile.d.ts.map +1 -0
- package/dist/ci_reconcile.js +46 -0
- package/dist/config_reconcile.d.ts +46 -0
- package/dist/config_reconcile.d.ts.map +1 -0
- package/dist/config_reconcile.js +60 -0
- package/dist/dependency_graph.d.ts +2 -2
- package/dist/dependency_graph.js +4 -4
- package/dist/dependency_updater.d.ts +1 -1
- package/dist/fetch_repo_data.d.ts +2 -2
- package/dist/fetch_repo_data.js +1 -1
- package/dist/fs_fetch_value_cache.d.ts +1 -1
- package/dist/fs_fetch_value_cache.js +3 -3
- package/dist/git_operations.d.ts +1 -1
- package/dist/git_operations.js +2 -2
- package/dist/github.d.ts +2 -2
- package/dist/github.js +1 -1
- package/dist/github_helpers.js +1 -1
- package/dist/gitops_analyze.task.d.ts.map +1 -1
- package/dist/gitops_analyze.task.js +15 -8
- package/dist/gitops_config.d.ts +27 -2
- package/dist/gitops_config.d.ts.map +1 -1
- package/dist/gitops_config.js +13 -2
- package/dist/gitops_run.task.js +2 -2
- package/dist/gitops_sync.task.js +4 -4
- package/dist/gitops_task_helpers.d.ts +1 -1
- package/dist/gitops_task_helpers.js +1 -1
- package/dist/gitops_validate.task.d.ts.map +1 -1
- package/dist/gitops_validate.task.js +54 -6
- package/dist/graph_validation.d.ts +2 -2
- package/dist/graph_validation.d.ts.map +1 -1
- package/dist/graph_validation.js +3 -1
- package/dist/local_repo.d.ts +17 -3
- package/dist/local_repo.d.ts.map +1 -1
- package/dist/local_repo.js +54 -6
- package/dist/log_helpers.d.ts +1 -1
- package/dist/multi_repo_publisher.d.ts +3 -3
- package/dist/multi_repo_publisher.d.ts.map +1 -1
- package/dist/multi_repo_publisher.js +5 -1
- package/dist/npm_registry.d.ts +1 -1
- package/dist/npm_registry.js +2 -2
- package/dist/operations.d.ts +4 -4
- package/dist/operations_defaults.js +4 -4
- package/dist/output_helpers.d.ts +1 -1
- package/dist/preflight_checks.d.ts +1 -1
- package/dist/preflight_checks.js +1 -1
- package/dist/publish_steps.js +1 -1
- package/dist/publishing_plan.d.ts +3 -3
- package/dist/publishing_plan.d.ts.map +1 -1
- package/dist/publishing_plan.js +11 -1
- package/dist/publishing_plan_logging.d.ts +1 -1
- package/dist/repo.svelte.d.ts +5 -5
- package/dist/repo.svelte.js +2 -2
- package/package.json +10 -10
- package/src/lib/cargo_toml.ts +82 -0
- package/src/lib/changeset_generator.ts +6 -6
- package/src/lib/changeset_reader.ts +3 -3
- package/src/lib/ci_reconcile.ts +73 -0
- package/src/lib/config_reconcile.ts +94 -0
- package/src/lib/dependency_graph.ts +5 -5
- package/src/lib/dependency_updater.ts +7 -7
- package/src/lib/fetch_repo_data.ts +6 -6
- package/src/lib/fs_fetch_value_cache.ts +3 -3
- package/src/lib/git_operations.ts +2 -2
- package/src/lib/github.ts +2 -2
- package/src/lib/github_helpers.ts +3 -3
- package/src/lib/gitops_analyze.task.ts +26 -18
- package/src/lib/gitops_config.ts +45 -5
- package/src/lib/gitops_plan.task.ts +4 -4
- package/src/lib/gitops_publish.task.ts +9 -9
- package/src/lib/gitops_run.task.ts +4 -4
- package/src/lib/gitops_sync.task.ts +8 -8
- package/src/lib/gitops_task_helpers.ts +7 -7
- package/src/lib/gitops_validate.task.ts +63 -11
- package/src/lib/graph_validation.ts +5 -4
- package/src/lib/local_repo.ts +81 -15
- package/src/lib/log_helpers.ts +2 -2
- package/src/lib/multi_repo_publisher.ts +15 -11
- package/src/lib/npm_registry.ts +3 -3
- package/src/lib/operations.ts +9 -9
- package/src/lib/operations_defaults.ts +9 -9
- package/src/lib/output_helpers.ts +1 -1
- package/src/lib/preflight_checks.ts +6 -6
- package/src/lib/publish_gate.ts +2 -2
- package/src/lib/publish_steps.ts +3 -3
- package/src/lib/publishing_event_handler.ts +1 -1
- package/src/lib/publishing_plan.ts +22 -10
- package/src/lib/publishing_plan_helpers.ts +3 -3
- package/src/lib/publishing_plan_logging.ts +2 -2
- package/src/lib/repo.svelte.ts +7 -7
- package/src/lib/repo_ops.ts +3 -3
- package/src/lib/resolved_gitops_config.ts +2 -2
package/src/lib/gitops_config.ts
CHANGED
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
* @module
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import type {Url} from '@fuzdev/fuz_util/url.
|
|
14
|
+
import type {Url} from '@fuzdev/fuz_util/url.ts';
|
|
15
15
|
import {existsSync} from 'node:fs';
|
|
16
|
-
import {strip_end} from '@fuzdev/fuz_util/string.
|
|
17
|
-
import type {GitBranch} from '@fuzdev/fuz_util/git.
|
|
16
|
+
import {strip_end} from '@fuzdev/fuz_util/string.ts';
|
|
17
|
+
import type {GitBranch} from '@fuzdev/fuz_util/git.ts';
|
|
18
18
|
|
|
19
|
-
import {DEFAULT_REPOS_DIR} from './paths.
|
|
19
|
+
import {DEFAULT_REPOS_DIR} from './paths.ts';
|
|
20
20
|
|
|
21
21
|
export interface GitopsConfig {
|
|
22
22
|
repos: Array<GitopsRepoConfig>;
|
|
@@ -32,6 +32,13 @@ export interface RawGitopsConfig {
|
|
|
32
32
|
repos_dir?: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Visibility of a repo on its host, mirroring the host's own model
|
|
37
|
+
* (e.g. GitHub's `visibility` field). Named to avoid confusion with the npm
|
|
38
|
+
* `package.json` `private` flag, which is a separate publishing concern.
|
|
39
|
+
*/
|
|
40
|
+
export type GitopsRepoVisibility = 'public' | 'private';
|
|
41
|
+
|
|
35
42
|
export interface GitopsRepoConfig {
|
|
36
43
|
/**
|
|
37
44
|
* The HTTPS URL to the repo. Does not include a `.git` suffix.
|
|
@@ -60,12 +67,34 @@ export interface GitopsRepoConfig {
|
|
|
60
67
|
* The branch name to use when fetching the repo. Defaults to `main`.
|
|
61
68
|
*/
|
|
62
69
|
branch: GitBranch;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Visibility of the repo on its host. Defaults to `'public'`.
|
|
73
|
+
*/
|
|
74
|
+
visibility: GitopsRepoVisibility;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Whether the repo runs CI. Defaults to `true` for public repos and `false`
|
|
78
|
+
* for private repos, unless set explicitly.
|
|
79
|
+
*/
|
|
80
|
+
ci: boolean;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Whether the repo is archived (read-only) on its host. Defaults to `false`.
|
|
84
|
+
*/
|
|
85
|
+
archived: boolean;
|
|
63
86
|
}
|
|
64
87
|
|
|
65
88
|
export interface RawGitopsRepoConfig {
|
|
66
89
|
repo_url: Url;
|
|
67
90
|
repo_dir?: string | null;
|
|
68
91
|
branch?: GitBranch;
|
|
92
|
+
/** Visibility of the repo on its host. Defaults to `'public'`. */
|
|
93
|
+
visibility?: GitopsRepoVisibility;
|
|
94
|
+
/** Whether the repo runs CI. Defaults to `true` for public, `false` for private. */
|
|
95
|
+
ci?: boolean;
|
|
96
|
+
/** Whether the repo is archived (read-only) on its host. Defaults to `false`. */
|
|
97
|
+
archived?: boolean;
|
|
69
98
|
}
|
|
70
99
|
|
|
71
100
|
export const create_empty_gitops_config = (): GitopsConfig => ({
|
|
@@ -91,12 +120,23 @@ export const normalize_gitops_config = (raw_config: RawGitopsConfig): GitopsConf
|
|
|
91
120
|
|
|
92
121
|
const parse_fuz_repo_config = (r: Url | RawGitopsRepoConfig): GitopsRepoConfig => {
|
|
93
122
|
if (typeof r === 'string') {
|
|
94
|
-
return {
|
|
123
|
+
return {
|
|
124
|
+
repo_url: r,
|
|
125
|
+
repo_dir: null,
|
|
126
|
+
branch: 'main' as GitBranch, // TODO @zts use flavored for GitBranch
|
|
127
|
+
visibility: 'public',
|
|
128
|
+
ci: true,
|
|
129
|
+
archived: false,
|
|
130
|
+
};
|
|
95
131
|
}
|
|
132
|
+
const visibility = r.visibility ?? 'public';
|
|
96
133
|
return {
|
|
97
134
|
repo_url: strip_end(r.repo_url, '.git'),
|
|
98
135
|
repo_dir: r.repo_dir ?? null,
|
|
99
136
|
branch: r.branch ?? ('main' as GitBranch), // TODO @zts use flavored for GitBranch
|
|
137
|
+
visibility,
|
|
138
|
+
ci: r.ci ?? visibility === 'public',
|
|
139
|
+
archived: r.archived ?? false,
|
|
100
140
|
};
|
|
101
141
|
};
|
|
102
142
|
|
|
@@ -2,15 +2,15 @@ import type {Task} from '@fuzdev/gro';
|
|
|
2
2
|
import {z} from 'zod';
|
|
3
3
|
import {styleText as st} from 'node:util';
|
|
4
4
|
|
|
5
|
-
import {get_gitops_ready} from './gitops_task_helpers.
|
|
5
|
+
import {get_gitops_ready} from './gitops_task_helpers.ts';
|
|
6
6
|
import {
|
|
7
7
|
generate_publishing_plan,
|
|
8
8
|
log_publishing_plan,
|
|
9
9
|
type PublishingPlan,
|
|
10
10
|
type LogPlanOptions,
|
|
11
|
-
} from './publishing_plan.
|
|
12
|
-
import {format_and_output, type OutputFormatters} from './output_helpers.
|
|
13
|
-
import {GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.
|
|
11
|
+
} from './publishing_plan.ts';
|
|
12
|
+
import {format_and_output, type OutputFormatters} from './output_helpers.ts';
|
|
13
|
+
import {GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.ts';
|
|
14
14
|
|
|
15
15
|
/** @nodocs */
|
|
16
16
|
export const Args = z.strictObject({
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import type {Task} from '@fuzdev/gro';
|
|
2
|
-
import type {Logger} from '@fuzdev/fuz_util/log.
|
|
2
|
+
import type {Logger} from '@fuzdev/fuz_util/log.ts';
|
|
3
3
|
import {z} from 'zod';
|
|
4
4
|
import {createInterface} from 'node:readline/promises';
|
|
5
5
|
import {styleText as st} from 'node:util';
|
|
6
6
|
|
|
7
|
-
import {get_gitops_ready} from './gitops_task_helpers.
|
|
7
|
+
import {get_gitops_ready} from './gitops_task_helpers.ts';
|
|
8
8
|
import {
|
|
9
9
|
execute_publishing_plan,
|
|
10
10
|
type PublishingOptions,
|
|
11
11
|
type PublishingResult,
|
|
12
|
-
} from './multi_repo_publisher.
|
|
13
|
-
import {stdout_handler} from './publishing_event_handler.
|
|
14
|
-
import {generate_publishing_plan, log_publishing_plan} from './publishing_plan.
|
|
15
|
-
import {derive_publish_steps, format_publish_steps, type PublishStep} from './publish_steps.
|
|
16
|
-
import {decide_publish_gate, publish_run_failed} from './publish_gate.
|
|
17
|
-
import {format_and_output, type OutputFormatters} from './output_helpers.
|
|
18
|
-
import {GITOPS_CONFIG_PATH_DEFAULT, GITOPS_NPM_WAIT_TIMEOUT_DEFAULT} from './gitops_constants.
|
|
12
|
+
} from './multi_repo_publisher.ts';
|
|
13
|
+
import {stdout_handler} from './publishing_event_handler.ts';
|
|
14
|
+
import {generate_publishing_plan, log_publishing_plan} from './publishing_plan.ts';
|
|
15
|
+
import {derive_publish_steps, format_publish_steps, type PublishStep} from './publish_steps.ts';
|
|
16
|
+
import {decide_publish_gate, publish_run_failed} from './publish_gate.ts';
|
|
17
|
+
import {format_and_output, type OutputFormatters} from './output_helpers.ts';
|
|
18
|
+
import {GITOPS_CONFIG_PATH_DEFAULT, GITOPS_NPM_WAIT_TIMEOUT_DEFAULT} from './gitops_constants.ts';
|
|
19
19
|
|
|
20
20
|
/** @nodocs */
|
|
21
21
|
export const Args = z.strictObject({
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {TaskError, type Task} from '@fuzdev/gro';
|
|
2
2
|
import {z} from 'zod';
|
|
3
|
-
import {map_concurrent_settled} from '@fuzdev/fuz_util/async.
|
|
4
|
-
import {spawn_out} from '@fuzdev/fuz_util/process.
|
|
3
|
+
import {map_concurrent_settled} from '@fuzdev/fuz_util/async.ts';
|
|
4
|
+
import {spawn_out} from '@fuzdev/fuz_util/process.ts';
|
|
5
5
|
import {writeFile} from 'node:fs/promises';
|
|
6
6
|
import {styleText as st} from 'node:util';
|
|
7
7
|
import {resolve} from 'node:path';
|
|
8
8
|
|
|
9
|
-
import {get_repo_paths} from './repo_ops.
|
|
10
|
-
import {GITOPS_CONCURRENCY_DEFAULT, GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.
|
|
9
|
+
import {get_repo_paths} from './repo_ops.ts';
|
|
10
|
+
import {GITOPS_CONCURRENCY_DEFAULT, GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.ts';
|
|
11
11
|
|
|
12
12
|
export const Args = z.strictObject({
|
|
13
13
|
// Positional rest args (gro convention) so `gro gitops_run "npm test"` works;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {TaskError, type Task} from '@fuzdev/gro';
|
|
2
2
|
import {z} from 'zod';
|
|
3
3
|
import {readFile, writeFile} from 'node:fs/promises';
|
|
4
|
-
import {format_file} from '@fuzdev/gro/format_file.
|
|
4
|
+
import {format_file} from '@fuzdev/gro/format_file.ts';
|
|
5
5
|
import {basename, resolve} from 'node:path';
|
|
6
|
-
import {print_path} from '@fuzdev/gro/paths.
|
|
7
|
-
import {load_from_env} from '@fuzdev/gro/env.
|
|
8
|
-
import {package_json_load} from '@fuzdev/gro/package_json.
|
|
6
|
+
import {print_path} from '@fuzdev/gro/paths.ts';
|
|
7
|
+
import {load_from_env} from '@fuzdev/gro/env.ts';
|
|
8
|
+
import {package_json_load} from '@fuzdev/gro/package_json.ts';
|
|
9
9
|
import {existsSync} from 'node:fs';
|
|
10
10
|
import {compactReplacer} from 'svelte-docinfo';
|
|
11
11
|
|
|
12
|
-
import {fetch_repo_data} from './fetch_repo_data.
|
|
13
|
-
import {create_fs_fetch_value_cache} from './fs_fetch_value_cache.
|
|
14
|
-
import {get_gitops_ready} from './gitops_task_helpers.
|
|
15
|
-
import {GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.
|
|
12
|
+
import {fetch_repo_data} from './fetch_repo_data.ts';
|
|
13
|
+
import {create_fs_fetch_value_cache} from './fs_fetch_value_cache.ts';
|
|
14
|
+
import {get_gitops_ready} from './gitops_task_helpers.ts';
|
|
15
|
+
import {GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.ts';
|
|
16
16
|
|
|
17
17
|
// TODO add flag to ignore or invalidate cache -- no-cache? clean?
|
|
18
18
|
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
import {TaskError} from '@fuzdev/gro';
|
|
20
20
|
import {styleText as st} from 'node:util';
|
|
21
21
|
import {resolve, dirname} from 'node:path';
|
|
22
|
-
import {print_path} from '@fuzdev/gro/paths.
|
|
23
|
-
import type {Logger} from '@fuzdev/fuz_util/log.
|
|
22
|
+
import {print_path} from '@fuzdev/gro/paths.ts';
|
|
23
|
+
import type {Logger} from '@fuzdev/fuz_util/log.ts';
|
|
24
24
|
|
|
25
|
-
import {load_gitops_config, type GitopsConfig} from './gitops_config.
|
|
26
|
-
import {local_repos_load, local_repos_ensure, type LocalRepo} from './local_repo.
|
|
27
|
-
import {resolve_gitops_config} from './resolved_gitops_config.
|
|
28
|
-
import {DEFAULT_REPOS_DIR} from './paths.
|
|
29
|
-
import type {GitOperations, NpmOperations} from './operations.
|
|
25
|
+
import {load_gitops_config, type GitopsConfig} from './gitops_config.ts';
|
|
26
|
+
import {local_repos_load, local_repos_ensure, type LocalRepo} from './local_repo.ts';
|
|
27
|
+
import {resolve_gitops_config} from './resolved_gitops_config.ts';
|
|
28
|
+
import {DEFAULT_REPOS_DIR} from './paths.ts';
|
|
29
|
+
import type {GitOperations, NpmOperations} from './operations.ts';
|
|
30
30
|
|
|
31
31
|
export interface GetGitopsReadyOptions {
|
|
32
32
|
config: string;
|
|
@@ -2,16 +2,17 @@ import type {Task} from '@fuzdev/gro';
|
|
|
2
2
|
import {z} from 'zod';
|
|
3
3
|
import {styleText as st} from 'node:util';
|
|
4
4
|
|
|
5
|
-
import {get_gitops_ready} from './gitops_task_helpers.
|
|
6
|
-
import {analyze_repos, type DependencyAnalysis} from './graph_validation.
|
|
5
|
+
import {get_gitops_ready} from './gitops_task_helpers.ts';
|
|
6
|
+
import {analyze_repos, type DependencyAnalysis} from './graph_validation.ts';
|
|
7
7
|
import {
|
|
8
8
|
generate_publishing_plan,
|
|
9
9
|
log_publishing_plan,
|
|
10
10
|
type PublishingPlan,
|
|
11
|
-
} from './publishing_plan.
|
|
12
|
-
import {execute_publishing_plan, type PublishingOptions} from './multi_repo_publisher.
|
|
13
|
-
import {log_dependency_analysis} from './log_helpers.
|
|
14
|
-
import {GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.
|
|
11
|
+
} from './publishing_plan.ts';
|
|
12
|
+
import {execute_publishing_plan, type PublishingOptions} from './multi_repo_publisher.ts';
|
|
13
|
+
import {log_dependency_analysis} from './log_helpers.ts';
|
|
14
|
+
import {GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.ts';
|
|
15
|
+
import {reconcile_ci, repo_has_workflows} from './ci_reconcile.ts';
|
|
15
16
|
|
|
16
17
|
/** @nodocs */
|
|
17
18
|
export const Args = z.strictObject({
|
|
@@ -86,7 +87,7 @@ export const task: Task<Args> = {
|
|
|
86
87
|
|
|
87
88
|
results.push({
|
|
88
89
|
command: 'gitops_analyze',
|
|
89
|
-
success:
|
|
90
|
+
success: errors === 0,
|
|
90
91
|
warnings,
|
|
91
92
|
errors,
|
|
92
93
|
duration: analyze_duration,
|
|
@@ -129,7 +130,7 @@ export const task: Task<Args> = {
|
|
|
129
130
|
|
|
130
131
|
results.push({
|
|
131
132
|
command: 'gitops_plan',
|
|
132
|
-
success:
|
|
133
|
+
success: errors === 0,
|
|
133
134
|
warnings,
|
|
134
135
|
errors,
|
|
135
136
|
duration: plan_duration,
|
|
@@ -202,6 +203,56 @@ export const task: Task<Args> = {
|
|
|
202
203
|
log.error(st('red', ` ✗ gitops_publish (dry run) failed: ${error}`));
|
|
203
204
|
}
|
|
204
205
|
|
|
206
|
+
// 4. Reconcile each repo's declared `ci` against actual workflow files on disk.
|
|
207
|
+
log.info(st('yellow', 'Running ci_reconcile...'));
|
|
208
|
+
const ci_start = Date.now();
|
|
209
|
+
try {
|
|
210
|
+
const ci_drift = reconcile_ci(
|
|
211
|
+
local_repos.map((r) => ({
|
|
212
|
+
repo_url: r.repo_config.repo_url,
|
|
213
|
+
ci: r.repo_config.ci,
|
|
214
|
+
has_workflows: repo_has_workflows(r.repo_dir),
|
|
215
|
+
// TODO: `local_repos` only ever holds checked-out repos — a missing repo
|
|
216
|
+
// throws in `local_repos_ensure` before we reach here — so `checkable` is
|
|
217
|
+
// always `true` today. The gate exists for a future caller that loads a
|
|
218
|
+
// partial set; until then the skip path is inert and untested.
|
|
219
|
+
checkable: true,
|
|
220
|
+
archived: r.repo_config.archived,
|
|
221
|
+
})),
|
|
222
|
+
);
|
|
223
|
+
const ci_duration = Date.now() - ci_start;
|
|
224
|
+
const drift_details = ci_drift.map((d) =>
|
|
225
|
+
d.kind === 'missing_ci'
|
|
226
|
+
? `${d.repo_url}: ci=true but no workflow files`
|
|
227
|
+
: `${d.repo_url}: ci=false but workflow files present`,
|
|
228
|
+
);
|
|
229
|
+
results.push({
|
|
230
|
+
command: 'ci_reconcile',
|
|
231
|
+
success: ci_drift.length === 0,
|
|
232
|
+
warnings: 0,
|
|
233
|
+
errors: ci_drift.length,
|
|
234
|
+
duration: ci_duration,
|
|
235
|
+
});
|
|
236
|
+
if (ci_drift.length === 0) {
|
|
237
|
+
log.info(st('green', ` ✓ ci_reconcile completed in ${ci_duration}ms`));
|
|
238
|
+
} else {
|
|
239
|
+
log.error(st('red', ` ❌ ci_reconcile found ${ci_drift.length} drift(s)`));
|
|
240
|
+
for (const detail of drift_details) {
|
|
241
|
+
log.error(st('red', ` - ${detail}`));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
const ci_duration = Date.now() - ci_start;
|
|
246
|
+
results.push({
|
|
247
|
+
command: 'ci_reconcile',
|
|
248
|
+
success: false,
|
|
249
|
+
warnings: 0,
|
|
250
|
+
errors: 1,
|
|
251
|
+
duration: ci_duration,
|
|
252
|
+
});
|
|
253
|
+
log.error(st('red', ` ✗ ci_reconcile failed: ${error}`));
|
|
254
|
+
}
|
|
255
|
+
|
|
205
256
|
// Summary
|
|
206
257
|
const total_duration = Date.now() - start_time;
|
|
207
258
|
const all_success = results.every((r) => r.success);
|
|
@@ -247,10 +298,11 @@ export const task: Task<Args> = {
|
|
|
247
298
|
st('yellow', `⚠️ Note: ${total_warnings} warning(s) found - review output above.`),
|
|
248
299
|
);
|
|
249
300
|
}
|
|
250
|
-
} else if (all_success && total_errors > 0) {
|
|
251
|
-
log.warn(st('yellow', '⚠️ Validation completed but found errors - review output above.'));
|
|
252
301
|
} else {
|
|
253
|
-
|
|
302
|
+
// Hard-fail on any error or failed command. These run manually (and
|
|
303
|
+
// increasingly via agents), so a clear problem should stop the pipeline
|
|
304
|
+
// rather than scroll past in the summary. Warnings stay non-fatal.
|
|
305
|
+
log.error(st('red', '❌ Validation failed - review the errors above.'));
|
|
254
306
|
throw new Error('Validation failed');
|
|
255
307
|
}
|
|
256
308
|
},
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
* @module
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import type {Logger} from '@fuzdev/fuz_util/log.
|
|
16
|
+
import type {Logger} from '@fuzdev/fuz_util/log.ts';
|
|
17
17
|
import {TaskError} from '@fuzdev/gro';
|
|
18
18
|
import {styleText as st} from 'node:util';
|
|
19
19
|
|
|
20
|
-
import {DependencyGraph, DependencyGraphBuilder} from './dependency_graph.
|
|
21
|
-
import type
|
|
20
|
+
import {DependencyGraph, DependencyGraphBuilder} from './dependency_graph.ts';
|
|
21
|
+
import {repo_is_npm, type LocalRepo} from './local_repo.ts';
|
|
22
22
|
|
|
23
23
|
export interface GraphValidationResult {
|
|
24
24
|
graph: DependencyGraph;
|
|
@@ -127,7 +127,8 @@ export interface RepoAnalysis {
|
|
|
127
127
|
* (no `log`) to avoid emitting a redundant "Analyzing dependencies" line.
|
|
128
128
|
*/
|
|
129
129
|
export const analyze_repos = (repos: Array<LocalRepo>): RepoAnalysis => {
|
|
130
|
-
|
|
130
|
+
// Only npm packages form the dependency graph; non-npm repos (e.g. cargo) are excluded.
|
|
131
|
+
const {graph, publishing_order: order} = validate_dependency_graph(repos.filter(repo_is_npm), {
|
|
131
132
|
throw_on_prod_cycles: false, // report, don't throw
|
|
132
133
|
log_cycles: false, // callers format their own cycle output
|
|
133
134
|
log_order: false,
|
package/src/lib/local_repo.ts
CHANGED
|
@@ -1,26 +1,36 @@
|
|
|
1
|
-
import {strip_end} from '@fuzdev/fuz_util/string.
|
|
2
|
-
import
|
|
3
|
-
import type
|
|
4
|
-
import {
|
|
1
|
+
import {strip_end} from '@fuzdev/fuz_util/string.ts';
|
|
2
|
+
import {to_error_message} from '@fuzdev/fuz_util/error.ts';
|
|
3
|
+
import {library_json_from_modules, type LibraryJson} from '@fuzdev/fuz_util/library_json.ts';
|
|
4
|
+
import type {PackageJson} from '@fuzdev/fuz_util/package_json.ts';
|
|
5
|
+
import {Library} from '@fuzdev/fuz_ui/library.svelte.ts';
|
|
5
6
|
import {existsSync} from 'node:fs';
|
|
6
7
|
import {join} from 'node:path';
|
|
7
8
|
import {TaskError} from '@fuzdev/gro';
|
|
8
|
-
import {library_load_from_repo} from '@fuzdev/gro/library_load.
|
|
9
|
-
import type {Logger} from '@fuzdev/fuz_util/log.
|
|
10
|
-
import {spawn_out} from '@fuzdev/fuz_util/process.
|
|
11
|
-
import {map_concurrent_settled} from '@fuzdev/fuz_util/async.
|
|
12
|
-
import type {GitOperations, NpmOperations} from './operations.
|
|
13
|
-
import {default_git_operations, default_npm_operations} from './operations_defaults.
|
|
14
|
-
|
|
15
|
-
import type {GitopsConfig, GitopsRepoConfig} from './gitops_config.
|
|
16
|
-
import type {ResolvedGitopsConfig} from './resolved_gitops_config.
|
|
17
|
-
import {GITOPS_CONCURRENCY_DEFAULT} from './gitops_constants.
|
|
9
|
+
import {library_load_from_repo} from '@fuzdev/gro/library_load.ts';
|
|
10
|
+
import type {Logger} from '@fuzdev/fuz_util/log.ts';
|
|
11
|
+
import {spawn_out} from '@fuzdev/fuz_util/process.ts';
|
|
12
|
+
import {map_concurrent_settled} from '@fuzdev/fuz_util/async.ts';
|
|
13
|
+
import type {GitOperations, NpmOperations} from './operations.ts';
|
|
14
|
+
import {default_git_operations, default_npm_operations} from './operations_defaults.ts';
|
|
15
|
+
|
|
16
|
+
import type {GitopsConfig, GitopsRepoConfig} from './gitops_config.ts';
|
|
17
|
+
import type {ResolvedGitopsConfig} from './resolved_gitops_config.ts';
|
|
18
|
+
import {GITOPS_CONCURRENCY_DEFAULT} from './gitops_constants.ts';
|
|
19
|
+
import {cargo_toml_load} from './cargo_toml.ts';
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Fully loaded local repo with `Library` and extracted dependency data.
|
|
21
23
|
* Does not extend `LocalRepoPath` - `Library` is source of truth for name/repo_url/etc.
|
|
22
24
|
*/
|
|
23
25
|
export interface LocalRepo {
|
|
26
|
+
/**
|
|
27
|
+
* Which packaging ecosystem the repo belongs to. `npm` repos (with a
|
|
28
|
+
* `package.json`) take part in the changeset publishing cascade; `cargo` repos
|
|
29
|
+
* (a Rust `Cargo.toml`, no `package.json`) are dashboard-only — fetched and
|
|
30
|
+
* rendered like any repo but excluded from publishing/analysis. See
|
|
31
|
+
* `repo_is_npm`.
|
|
32
|
+
*/
|
|
33
|
+
kind: 'npm' | 'cargo';
|
|
24
34
|
library: Library;
|
|
25
35
|
/** The repo's full `package.json` (with `dependencies`/`devDependencies`). */
|
|
26
36
|
package_json: PackageJson;
|
|
@@ -210,13 +220,21 @@ export const local_repo_load = async ({
|
|
|
210
220
|
}
|
|
211
221
|
}
|
|
212
222
|
|
|
223
|
+
// A repo with no `package.json` but a Rust `Cargo.toml` isn't an npm package and can't be
|
|
224
|
+
// analyzed as a library. Load it as a dashboard-only `cargo` repo (CI, PRs, identity) that
|
|
225
|
+
// publishing/analysis skips. Anything else falls through to the npm loader below, whose
|
|
226
|
+
// error covers a genuinely missing or unreadable manifest.
|
|
227
|
+
if (!existsSync(join(repo_dir, 'package.json')) && existsSync(join(repo_dir, 'Cargo.toml'))) {
|
|
228
|
+
return local_repo_load_cargo({local_repo_path});
|
|
229
|
+
}
|
|
230
|
+
|
|
213
231
|
// Load library metadata via svelte-docinfo analysis (cached under `.gro/library.json`).
|
|
214
232
|
let library_json: LibraryJson;
|
|
215
233
|
let package_json: PackageJson;
|
|
216
234
|
try {
|
|
217
235
|
({library_json, package_json} = await library_load_from_repo(repo_dir, {log: _log}));
|
|
218
236
|
} catch (err) {
|
|
219
|
-
const message =
|
|
237
|
+
const message = to_error_message(err);
|
|
220
238
|
_log?.warn(
|
|
221
239
|
`Failed to load library metadata for repo "${repo_name}" in ${repo_dir}: ${message}`,
|
|
222
240
|
);
|
|
@@ -227,6 +245,7 @@ export const local_repo_load = async ({
|
|
|
227
245
|
const library = new Library(library_json);
|
|
228
246
|
|
|
229
247
|
const local_repo: LocalRepo = {
|
|
248
|
+
kind: 'npm',
|
|
230
249
|
library,
|
|
231
250
|
package_json,
|
|
232
251
|
repo_dir,
|
|
@@ -248,6 +267,53 @@ export const local_repo_load = async ({
|
|
|
248
267
|
return local_repo;
|
|
249
268
|
};
|
|
250
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Whether a repo is an npm package and so participates in publishing and
|
|
272
|
+
* dependency analysis. Non-npm repos (e.g. Rust `cargo` repos) are still synced
|
|
273
|
+
* and rendered on the dashboard, but excluded from the changeset cascade.
|
|
274
|
+
*/
|
|
275
|
+
export const repo_is_npm = (repo: LocalRepo): boolean => repo.kind === 'npm';
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Loads a non-npm Rust repo as a dashboard-only `LocalRepo`. It has no
|
|
279
|
+
* `package.json`, so there's no `svelte-docinfo` analysis and no npm dependency
|
|
280
|
+
* graph — a lightweight `Library` is synthesized from the repo's `Cargo.toml`
|
|
281
|
+
* (best-effort name/version/description) and its configured repo URL, which is
|
|
282
|
+
* what the dashboard renders (CI status, PRs, identity). Marked `private` so it
|
|
283
|
+
* never reads as an npm publish target, and tagged `cargo` so the publishing and
|
|
284
|
+
* analysis paths skip it (see `repo_is_npm`).
|
|
285
|
+
*/
|
|
286
|
+
const local_repo_load_cargo = async ({
|
|
287
|
+
local_repo_path,
|
|
288
|
+
}: {
|
|
289
|
+
local_repo_path: LocalRepoPath;
|
|
290
|
+
}): Promise<LocalRepo> => {
|
|
291
|
+
const {repo_config, repo_dir, repo_name, repo_url, repo_git_ssh_url} = local_repo_path;
|
|
292
|
+
|
|
293
|
+
const cargo = await cargo_toml_load(repo_dir);
|
|
294
|
+
|
|
295
|
+
// A Cargo workspace root has no `name`, so fall back to the repo name parsed from
|
|
296
|
+
// the URL; the config's `repo_url` is the authoritative `repository`.
|
|
297
|
+
const package_json: PackageJson = {
|
|
298
|
+
name: cargo?.name ?? repo_name,
|
|
299
|
+
version: cargo?.version ?? '0.0.0',
|
|
300
|
+
repository: cargo?.repository ?? repo_url,
|
|
301
|
+
private: true,
|
|
302
|
+
...(cargo?.description ? {description: cargo.description} : null),
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const library = new Library(library_json_from_modules(package_json, []));
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
kind: 'cargo',
|
|
309
|
+
library,
|
|
310
|
+
package_json,
|
|
311
|
+
repo_dir,
|
|
312
|
+
repo_git_ssh_url,
|
|
313
|
+
repo_config,
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
|
|
251
317
|
export const local_repos_ensure = async ({
|
|
252
318
|
resolved_config,
|
|
253
319
|
repos_dir,
|
package/src/lib/log_helpers.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {Logger} from '@fuzdev/fuz_util/log.
|
|
1
|
+
import type {Logger} from '@fuzdev/fuz_util/log.ts';
|
|
2
2
|
import {styleText as st} from 'node:util';
|
|
3
3
|
|
|
4
|
-
import type {DependencyGraphBuilder} from './dependency_graph.
|
|
4
|
+
import type {DependencyGraphBuilder} from './dependency_graph.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Formats wildcard dependencies as styled strings.
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import type {Logger} from '@fuzdev/fuz_util/log.
|
|
1
|
+
import type {Logger} from '@fuzdev/fuz_util/log.ts';
|
|
2
2
|
import {TaskError} from '@fuzdev/gro';
|
|
3
3
|
import {join} from 'node:path';
|
|
4
4
|
import {styleText as st} from 'node:util';
|
|
5
5
|
|
|
6
|
-
import type
|
|
7
|
-
import {update_package_json, type VersionStrategy} from './dependency_updater.
|
|
6
|
+
import {repo_is_npm, type LocalRepo} from './local_repo.ts';
|
|
7
|
+
import {update_package_json, type VersionStrategy} from './dependency_updater.ts';
|
|
8
8
|
import {
|
|
9
9
|
generate_publishing_plan,
|
|
10
10
|
type VersionChange,
|
|
11
11
|
type DependencyUpdate,
|
|
12
12
|
type PublishingPlan,
|
|
13
|
-
} from './publishing_plan.
|
|
14
|
-
import type {PreflightOptions} from './preflight_checks.
|
|
15
|
-
import type {GitopsOperations} from './operations.
|
|
16
|
-
import {default_gitops_operations} from './operations_defaults.
|
|
17
|
-
import {GITOPS_NPM_WAIT_TIMEOUT_DEFAULT} from './gitops_constants.
|
|
13
|
+
} from './publishing_plan.ts';
|
|
14
|
+
import type {PreflightOptions} from './preflight_checks.ts';
|
|
15
|
+
import type {GitopsOperations} from './operations.ts';
|
|
16
|
+
import {default_gitops_operations} from './operations_defaults.ts';
|
|
17
|
+
import {GITOPS_NPM_WAIT_TIMEOUT_DEFAULT} from './gitops_constants.ts';
|
|
18
18
|
import {
|
|
19
19
|
type PublishingEvent,
|
|
20
20
|
type PublishingRunSummary,
|
|
21
21
|
summarize_events,
|
|
22
|
-
} from './publishing_event.
|
|
22
|
+
} from './publishing_event.ts';
|
|
23
23
|
import {
|
|
24
24
|
type PublishingEventHandler,
|
|
25
25
|
capture_handler,
|
|
26
26
|
multi_handler,
|
|
27
|
-
} from './publishing_event_handler.
|
|
27
|
+
} from './publishing_event_handler.ts';
|
|
28
28
|
|
|
29
29
|
export interface PublishingOptions {
|
|
30
30
|
wetrun: boolean;
|
|
@@ -83,13 +83,17 @@ export const publish_repos = async (
|
|
|
83
83
|
* `ok: false`. This is the single error gate — `--no-plan` can't bypass it.
|
|
84
84
|
*/
|
|
85
85
|
export const execute_publishing_plan = async (
|
|
86
|
-
|
|
86
|
+
all_repos: Array<LocalRepo>,
|
|
87
87
|
plan: PublishingPlan,
|
|
88
88
|
options: PublishingOptions,
|
|
89
89
|
): Promise<PublishingResult> => {
|
|
90
90
|
const start_time = Date.now();
|
|
91
91
|
const {wetrun, log, ops = default_gitops_operations} = options;
|
|
92
92
|
|
|
93
|
+
// Only npm repos publish; drop any non-npm repos (e.g. cargo) so preflight and the
|
|
94
|
+
// executor's name lookup never touch them. The plan already excludes them too.
|
|
95
|
+
const repos = all_repos.filter(repo_is_npm);
|
|
96
|
+
|
|
93
97
|
// Fail loud on an incomplete plan. Executing one with errors would silently skip the
|
|
94
98
|
// affected packages. A wetrun aborts before touching npm or git; a dry run proceeds
|
|
95
99
|
// (no side effects) but the result is not ok (see the summary below).
|
package/src/lib/npm_registry.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {Logger} from '@fuzdev/fuz_util/log.
|
|
2
|
-
import {spawn_out} from '@fuzdev/fuz_util/process.
|
|
3
|
-
import {wait} from '@fuzdev/fuz_util/async.
|
|
1
|
+
import type {Logger} from '@fuzdev/fuz_util/log.ts';
|
|
2
|
+
import {spawn_out} from '@fuzdev/fuz_util/process.ts';
|
|
3
|
+
import {wait} from '@fuzdev/fuz_util/async.ts';
|
|
4
4
|
import {styleText as st} from 'node:util';
|
|
5
5
|
|
|
6
6
|
export interface WaitOptions {
|
package/src/lib/operations.ts
CHANGED
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
* @module
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
import type {Result} from '@fuzdev/fuz_util/result.
|
|
38
|
-
import type {FsError} from '@fuzdev/fuz_util/fs.
|
|
39
|
-
import type {Logger} from '@fuzdev/fuz_util/log.
|
|
40
|
-
import type {LocalRepo} from './local_repo.
|
|
41
|
-
import type {ChangesetInfo} from './changeset_reader.
|
|
42
|
-
import type {BumpType} from './version_utils.
|
|
43
|
-
import type {PreflightOptions, PreflightResult} from './preflight_checks.
|
|
44
|
-
import type {WaitOptions} from './npm_registry.
|
|
37
|
+
import type {Result} from '@fuzdev/fuz_util/result.ts';
|
|
38
|
+
import type {FsError} from '@fuzdev/fuz_util/fs.ts';
|
|
39
|
+
import type {Logger} from '@fuzdev/fuz_util/log.ts';
|
|
40
|
+
import type {LocalRepo} from './local_repo.ts';
|
|
41
|
+
import type {ChangesetInfo} from './changeset_reader.ts';
|
|
42
|
+
import type {BumpType} from './version_utils.ts';
|
|
43
|
+
import type {PreflightOptions, PreflightResult} from './preflight_checks.ts';
|
|
44
|
+
import type {WaitOptions} from './npm_registry.ts';
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Changeset operations for reading and predicting versions from `.changeset/*.md` files.
|
|
@@ -293,7 +293,7 @@ export interface PreflightOperations {
|
|
|
293
293
|
*
|
|
294
294
|
* Errors are typed via `FsError` (`not_found | permission_denied |
|
|
295
295
|
* already_exists | io_error`) so callers can branch on `kind` instead of
|
|
296
|
-
* regex-matching `message`. See `@fuzdev/fuz_util/fs.
|
|
296
|
+
* regex-matching `message`. See `@fuzdev/fuz_util/fs.ts`.
|
|
297
297
|
*/
|
|
298
298
|
export interface FsOperations {
|
|
299
299
|
/**
|
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {spawn_out} from '@fuzdev/fuz_util/process.
|
|
10
|
+
import {spawn_out} from '@fuzdev/fuz_util/process.ts';
|
|
11
11
|
import {readFile, writeFile, mkdir, stat} from 'node:fs/promises';
|
|
12
|
-
import {git_checkout, type GitBranch, type GitOrigin} from '@fuzdev/fuz_util/git.
|
|
13
|
-
import {fs_classify_error} from '@fuzdev/fuz_util/fs.
|
|
14
|
-
import {EMPTY_OBJECT} from '@fuzdev/fuz_util/object.
|
|
12
|
+
import {git_checkout, type GitBranch, type GitOrigin} from '@fuzdev/fuz_util/git.ts';
|
|
13
|
+
import {fs_classify_error} from '@fuzdev/fuz_util/fs.ts';
|
|
14
|
+
import {EMPTY_OBJECT} from '@fuzdev/fuz_util/object.ts';
|
|
15
15
|
|
|
16
|
-
import {has_changesets, read_changesets, predict_next_version} from './changeset_reader.
|
|
17
|
-
import {wait_for_package} from './npm_registry.
|
|
18
|
-
import {run_preflight_checks} from './preflight_checks.
|
|
16
|
+
import {has_changesets, read_changesets, predict_next_version} from './changeset_reader.ts';
|
|
17
|
+
import {wait_for_package} from './npm_registry.ts';
|
|
18
|
+
import {run_preflight_checks} from './preflight_checks.ts';
|
|
19
19
|
import {
|
|
20
20
|
git_add,
|
|
21
21
|
git_commit,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
git_current_commit_hash_required,
|
|
33
33
|
git_check_clean_workspace_as_boolean,
|
|
34
34
|
git_has_remote,
|
|
35
|
-
} from './git_operations.
|
|
35
|
+
} from './git_operations.ts';
|
|
36
36
|
import type {
|
|
37
37
|
ChangesetOperations,
|
|
38
38
|
GitOperations,
|
|
@@ -42,7 +42,7 @@ import type {
|
|
|
42
42
|
FsOperations,
|
|
43
43
|
BuildOperations,
|
|
44
44
|
GitopsOperations,
|
|
45
|
-
} from './operations.
|
|
45
|
+
} from './operations.ts';
|
|
46
46
|
|
|
47
47
|
/** Wrap an async function that returns a value */
|
|
48
48
|
const wrap_with_value = async <T>(
|