@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.
Files changed (179) hide show
  1. package/README.md +78 -24
  2. package/dist/ModulesDetail.svelte +15 -22
  3. package/dist/ModulesDetail.svelte.d.ts.map +1 -1
  4. package/dist/ModulesNav.svelte +4 -1
  5. package/dist/ModulesPage.svelte +1 -3
  6. package/dist/ModulesPage.svelte.d.ts.map +1 -1
  7. package/dist/PageHeader.svelte +2 -2
  8. package/dist/PageHeader.svelte.d.ts +0 -3
  9. package/dist/PageHeader.svelte.d.ts.map +1 -1
  10. package/dist/PullRequestsDetail.svelte +10 -9
  11. package/dist/PullRequestsDetail.svelte.d.ts.map +1 -1
  12. package/dist/PullRequestsPage.svelte +1 -3
  13. package/dist/PullRequestsPage.svelte.d.ts.map +1 -1
  14. package/dist/ReposTable.svelte +27 -48
  15. package/dist/ReposTable.svelte.d.ts.map +1 -1
  16. package/dist/ReposTree.svelte +5 -19
  17. package/dist/ReposTree.svelte.d.ts.map +1 -1
  18. package/dist/ReposTreeNav.svelte +5 -7
  19. package/dist/ReposTreeNav.svelte.d.ts.map +1 -1
  20. package/dist/TablePage.svelte +1 -3
  21. package/dist/TablePage.svelte.d.ts.map +1 -1
  22. package/dist/TreeItemPage.svelte +1 -3
  23. package/dist/TreeItemPage.svelte.d.ts.map +1 -1
  24. package/dist/TreePage.svelte +1 -3
  25. package/dist/TreePage.svelte.d.ts.map +1 -1
  26. package/dist/ci_reconcile.d.ts +6 -10
  27. package/dist/ci_reconcile.d.ts.map +1 -1
  28. package/dist/ci_reconcile.js +6 -8
  29. package/dist/dependency_graph.d.ts +26 -49
  30. package/dist/dependency_graph.d.ts.map +1 -1
  31. package/dist/dependency_graph.js +37 -80
  32. package/dist/dependency_updater.d.ts +1 -1
  33. package/dist/dependency_updater.d.ts.map +1 -1
  34. package/dist/dependency_updater.js +6 -1
  35. package/dist/fetch_repo_data.d.ts +34 -8
  36. package/dist/fetch_repo_data.d.ts.map +1 -1
  37. package/dist/fetch_repo_data.js +44 -22
  38. package/dist/fs_fetch_value_cache.d.ts +1 -1
  39. package/dist/fs_fetch_value_cache.js +1 -1
  40. package/dist/git_operations.d.ts +6 -49
  41. package/dist/git_operations.d.ts.map +1 -1
  42. package/dist/git_operations.js +13 -126
  43. package/dist/github.d.ts +17 -2
  44. package/dist/github.d.ts.map +1 -1
  45. package/dist/github.js +15 -6
  46. package/dist/github_helpers.d.ts.map +1 -1
  47. package/dist/github_helpers.js +3 -11
  48. package/dist/gitops_analyze.task.d.ts +24 -2
  49. package/dist/gitops_analyze.task.d.ts.map +1 -1
  50. package/dist/gitops_analyze.task.js +43 -30
  51. package/dist/gitops_config.d.ts +34 -79
  52. package/dist/gitops_config.d.ts.map +1 -1
  53. package/dist/gitops_config.js +76 -53
  54. package/dist/gitops_constants.d.ts +6 -4
  55. package/dist/gitops_constants.d.ts.map +1 -1
  56. package/dist/gitops_constants.js +6 -4
  57. package/dist/gitops_plan.task.d.ts +29 -3
  58. package/dist/gitops_plan.task.d.ts.map +1 -1
  59. package/dist/gitops_plan.task.js +80 -58
  60. package/dist/gitops_publish.task.d.ts +62 -2
  61. package/dist/gitops_publish.task.d.ts.map +1 -1
  62. package/dist/gitops_publish.task.js +187 -102
  63. package/dist/gitops_run.task.d.ts +74 -0
  64. package/dist/gitops_run.task.d.ts.map +1 -1
  65. package/dist/gitops_run.task.js +174 -160
  66. package/dist/gitops_sync.task.d.ts +65 -3
  67. package/dist/gitops_sync.task.d.ts.map +1 -1
  68. package/dist/gitops_sync.task.js +154 -30
  69. package/dist/gitops_task_helpers.d.ts +86 -59
  70. package/dist/gitops_task_helpers.d.ts.map +1 -1
  71. package/dist/gitops_task_helpers.js +120 -79
  72. package/dist/gitops_validate.task.d.ts +1 -2
  73. package/dist/gitops_validate.task.d.ts.map +1 -1
  74. package/dist/gitops_validate.task.js +15 -22
  75. package/dist/graph_validation.d.ts +16 -27
  76. package/dist/graph_validation.d.ts.map +1 -1
  77. package/dist/graph_validation.js +23 -64
  78. package/dist/local_repo.d.ts +50 -62
  79. package/dist/local_repo.d.ts.map +1 -1
  80. package/dist/local_repo.js +80 -193
  81. package/dist/log_helpers.d.ts +8 -8
  82. package/dist/log_helpers.d.ts.map +1 -1
  83. package/dist/multi_repo_publisher.d.ts +10 -0
  84. package/dist/multi_repo_publisher.d.ts.map +1 -1
  85. package/dist/multi_repo_publisher.js +115 -51
  86. package/dist/npm_registry.d.ts +23 -19
  87. package/dist/npm_registry.d.ts.map +1 -1
  88. package/dist/npm_registry.js +20 -47
  89. package/dist/operations.d.ts +75 -180
  90. package/dist/operations.d.ts.map +1 -1
  91. package/dist/operations.js +15 -7
  92. package/dist/operations_defaults.d.ts +17 -2
  93. package/dist/operations_defaults.d.ts.map +1 -1
  94. package/dist/operations_defaults.js +104 -121
  95. package/dist/output_helpers.d.ts +36 -0
  96. package/dist/output_helpers.d.ts.map +1 -1
  97. package/dist/output_helpers.js +37 -3
  98. package/dist/preflight_checks.d.ts +20 -27
  99. package/dist/preflight_checks.d.ts.map +1 -1
  100. package/dist/preflight_checks.js +37 -126
  101. package/dist/publish_steps.d.ts +2 -4
  102. package/dist/publish_steps.d.ts.map +1 -1
  103. package/dist/publish_steps.js +2 -6
  104. package/dist/publishing_event.d.ts +2 -8
  105. package/dist/publishing_event.d.ts.map +1 -1
  106. package/dist/publishing_event.js +5 -4
  107. package/dist/publishing_event_handler.d.ts +10 -8
  108. package/dist/publishing_event_handler.d.ts.map +1 -1
  109. package/dist/publishing_event_handler.js +14 -12
  110. package/dist/publishing_plan.d.ts +16 -1
  111. package/dist/publishing_plan.d.ts.map +1 -1
  112. package/dist/publishing_plan.js +328 -250
  113. package/dist/publishing_plan_logging.d.ts +1 -1
  114. package/dist/publishing_plan_logging.d.ts.map +1 -1
  115. package/dist/publishing_plan_logging.js +21 -14
  116. package/dist/repo.svelte.d.ts +7 -0
  117. package/dist/repo.svelte.d.ts.map +1 -1
  118. package/dist/repo.svelte.js +3 -0
  119. package/dist/repo_readiness.d.ts +210 -0
  120. package/dist/repo_readiness.d.ts.map +1 -0
  121. package/dist/repo_readiness.js +516 -0
  122. package/dist/repos_status.d.ts +3340 -0
  123. package/dist/repos_status.d.ts.map +1 -0
  124. package/dist/repos_status.js +555 -0
  125. package/dist/repos_status_load.d.ts +65 -0
  126. package/dist/repos_status_load.d.ts.map +1 -0
  127. package/dist/repos_status_load.js +131 -0
  128. package/package.json +7 -27
  129. package/src/lib/ci_reconcile.ts +7 -11
  130. package/src/lib/dependency_graph.ts +45 -97
  131. package/src/lib/dependency_updater.ts +7 -1
  132. package/src/lib/fetch_repo_data.ts +66 -29
  133. package/src/lib/fs_fetch_value_cache.ts +1 -1
  134. package/src/lib/git_operations.ts +15 -187
  135. package/src/lib/github.ts +20 -8
  136. package/src/lib/github_helpers.ts +5 -12
  137. package/src/lib/gitops_analyze.task.ts +79 -41
  138. package/src/lib/gitops_config.ts +88 -137
  139. package/src/lib/gitops_constants.ts +6 -4
  140. package/src/lib/gitops_plan.task.ts +113 -61
  141. package/src/lib/gitops_publish.task.ts +255 -122
  142. package/src/lib/gitops_run.task.ts +250 -178
  143. package/src/lib/gitops_sync.task.ts +208 -33
  144. package/src/lib/gitops_task_helpers.ts +166 -110
  145. package/src/lib/gitops_validate.task.ts +17 -27
  146. package/src/lib/graph_validation.ts +25 -86
  147. package/src/lib/local_repo.ts +109 -316
  148. package/src/lib/log_helpers.ts +8 -18
  149. package/src/lib/multi_repo_publisher.ts +138 -53
  150. package/src/lib/npm_registry.ts +37 -62
  151. package/src/lib/operations.ts +78 -150
  152. package/src/lib/operations_defaults.ts +108 -154
  153. package/src/lib/output_helpers.ts +55 -3
  154. package/src/lib/preflight_checks.ts +47 -183
  155. package/src/lib/publish_steps.ts +15 -13
  156. package/src/lib/publishing_event.ts +5 -4
  157. package/src/lib/publishing_event_handler.ts +18 -16
  158. package/src/lib/publishing_plan.ts +451 -287
  159. package/src/lib/publishing_plan_logging.ts +32 -23
  160. package/src/lib/repo.svelte.ts +8 -0
  161. package/src/lib/repo_readiness.ts +647 -0
  162. package/src/lib/repos_status.ts +671 -0
  163. package/src/lib/repos_status_load.ts +173 -0
  164. package/dist/config_reconcile.d.ts +0 -46
  165. package/dist/config_reconcile.d.ts.map +0 -1
  166. package/dist/config_reconcile.js +0 -64
  167. package/dist/paths.d.ts +0 -7
  168. package/dist/paths.d.ts.map +0 -1
  169. package/dist/paths.js +0 -6
  170. package/dist/repo_ops.d.ts +0 -59
  171. package/dist/repo_ops.d.ts.map +0 -1
  172. package/dist/repo_ops.js +0 -170
  173. package/dist/resolved_gitops_config.d.ts +0 -9
  174. package/dist/resolved_gitops_config.d.ts.map +0 -1
  175. package/dist/resolved_gitops_config.js +0 -12
  176. package/src/lib/config_reconcile.ts +0 -98
  177. package/src/lib/paths.ts +0 -6
  178. package/src/lib/repo_ops.ts +0 -216
  179. package/src/lib/resolved_gitops_config.ts +0 -27
@@ -2,13 +2,11 @@ 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.ts';
6
- import { analyze_repos, type DependencyAnalysis } from './graph_validation.ts';
7
- import {
8
- generate_publishing_plan,
9
- log_publishing_plan,
10
- type PublishingPlan
11
- } from './publishing_plan.ts';
5
+ import { get_gitops_ready, log_readiness_block } from './gitops_task_helpers.ts';
6
+ import { analyze_repos } from './graph_validation.ts';
7
+ import type { DependencyAnalysis } from './dependency_graph.ts';
8
+ import { generate_publishing_plan, type PublishingPlan } from './publishing_plan.ts';
9
+ import { log_publishing_plan } from './publishing_plan_logging.ts';
12
10
  import { execute_publishing_plan, type PublishingOptions } from './multi_repo_publisher.ts';
13
11
  import { log_dependency_analysis } from './log_helpers.ts';
14
12
  import { GITOPS_CONFIG_PATH_DEFAULT } from './gitops_constants.ts';
@@ -20,18 +18,14 @@ export const Args = z.strictObject({
20
18
  .string()
21
19
  .meta({ description: 'path to the gitops config file, absolute or relative to the cwd' })
22
20
  .default(GITOPS_CONFIG_PATH_DEFAULT),
23
- dir: z
21
+ registry: z
24
22
  .string()
25
- .meta({ description: 'path containing the repos, defaults to the parent of the config dir' })
26
- .optional(),
27
- verbose: z.boolean().meta({ description: 'show additional details' }).default(false),
28
- sync: z
29
- .boolean()
30
23
  .meta({
31
24
  description:
32
- 'sync repos (switch branch, pull, install) before validating instead of reading the working tree as-is'
25
+ 'path to the repos.toml registry, when `repos` would not find it walking up from the cwd'
33
26
  })
34
- .default(false)
27
+ .optional(),
28
+ verbose: z.boolean().meta({ description: 'show additional details' }).default(false)
35
29
  });
36
30
  export type Args = z.infer<typeof Args>;
37
31
 
@@ -41,7 +35,7 @@ export const task: Task<Args> = {
41
35
  summary:
42
36
  'validate gitops configuration by running all read-only commands and checking for issues',
43
37
  run: async ({ args, log }) => {
44
- const { config, dir, verbose, sync } = args;
38
+ const { config, registry, verbose } = args;
45
39
 
46
40
  log.info(st('cyan', 'Running Gitops Validation Suite'));
47
41
  log.info(st('dim', 'This runs all read-only commands and checks for consistency.'));
@@ -59,10 +53,11 @@ export const task: Task<Args> = {
59
53
 
60
54
  const start_time = Date.now();
61
55
 
62
- // Load repos once (shared by all commands); read the working tree as-is unless `--sync`
56
+ // Load repos once (shared by all commands), as they sit, and say which aren't at rest
63
57
  log.info(st('dim', 'Loading repositories...'));
64
- const { local_repos } = await get_gitops_ready({ config, dir, download: false, sync, log });
58
+ const { local_repos } = await get_gitops_ready({ config, registry, log });
65
59
  log.info(st('dim', ` Found ${local_repos.length} local repos`));
60
+ log_readiness_block(local_repos, log);
66
61
 
67
62
  // 1. Run gitops_analyze
68
63
  log.info(st('yellow', 'Running gitops_analyze...'));
@@ -203,21 +198,16 @@ export const task: Task<Args> = {
203
198
  log.error(st('red', ` ✗ gitops_publish (dry run) failed: ${error}`));
204
199
  }
205
200
 
206
- // 4. Reconcile each repo's declared `ci` against actual workflow files on disk.
201
+ // 4. Reconcile each repo's registry-declared `ci` against actual workflow files on disk.
207
202
  log.info(st('yellow', 'Running ci_reconcile...'));
208
203
  const ci_start = Date.now();
209
204
  try {
210
205
  const ci_drift = reconcile_ci(
211
206
  local_repos.map((r) => ({
212
- repo_url: r.repo_config.repo_url,
213
- ci: r.repo_config.ci,
207
+ repo_url: r.entry.url,
208
+ ci: r.entry.ci,
214
209
  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
210
+ archived: r.entry.archived
221
211
  }))
222
212
  );
223
213
  const ci_duration = Date.now() - ci_start;
@@ -1,23 +1,17 @@
1
1
  /**
2
- * Shared dependency graph validation logic used across multiple workflows.
2
+ * Dependency graph validation shared by the publishing plan and the analysis tasks.
3
3
  *
4
- * Consolidates graph building, cycle detection, and publishing order computation
5
- * that was duplicated in three places: `multi_repo_publisher.ts`, `publishing_plan.ts`,
6
- * and `gitops_analyze.task.ts`.
4
+ * Builds the graph, detects cycles by type, and computes the publishing order,
5
+ * reporting cycles and sort failures in the result rather than throwing, so
6
+ * each caller decides how to surface them: the plan turns them into errors,
7
+ * `gitops_analyze` and `gitops_validate` format them.
7
8
  *
8
- * Options pattern supports different behaviors: analyze can tolerate cycles for
9
- * reporting, while publish must throw on production cycles.
10
- *
11
- * See also: `dependency_graph.ts` for core graph data structure and algorithms.
9
+ * See also: `dependency_graph.ts` for the core graph data structure and algorithms.
12
10
  *
13
11
  * @module
14
12
  */
15
13
 
16
- import type { Logger } from '@fuzdev/fuz_util/log.ts';
17
- import { TaskError } from '@fuzdev/gro';
18
- import { styleText as st } from 'node:util';
19
-
20
- import { DependencyGraph, DependencyGraphBuilder } from './dependency_graph.ts';
14
+ import { DependencyGraph, type DependencyAnalysis } from './dependency_graph.ts';
21
15
  import { repo_is_npm, type LocalRepo } from './local_repo.ts';
22
16
 
23
17
  export interface GraphValidationResult {
@@ -25,79 +19,29 @@ export interface GraphValidationResult {
25
19
  publishing_order: Array<string>;
26
20
  production_cycles: Array<Array<string>>;
27
21
  dev_cycles: Array<Array<string>>;
28
- sort_error?: string; // Error message if topological sort failed
22
+ /** Why the topological sort failed, when it did; `publishing_order` is then empty. */
23
+ sort_error?: string;
29
24
  }
30
25
 
31
26
  /**
32
- * Shared utility for building dependency graph, detecting cycles, and computing publishing order.
33
- * This centralizes logic that was duplicated across `multi_repo_publisher`, `publishing_plan`, and `gitops_analyze`.
27
+ * Builds the dependency graph, detects cycles, and computes the publishing order
28
+ * (prod/peer dependencies only, so dev cycles don't block it).
34
29
  *
35
- * @param options.throw_on_prod_cycles - whether to throw an error if production cycles are detected (default: true)
36
- * @param options.log_cycles - whether to log cycle information (default: true)
37
- * @param options.log_order - whether to log publishing order (default: true)
38
- * @returns graph validation result with graph, publishing order, and detected cycles
39
- * @throws {TaskError} if production cycles detected and `throw_on_prod_cycles` is true
30
+ * Never throws on cycles: a production/peer cycle leaves `publishing_order` empty
31
+ * and sets `sort_error`, and the caller reports it.
32
+ *
33
+ * @returns the graph, publishing order, and detected cycles
40
34
  */
41
- export const validate_dependency_graph = (
42
- repos: Array<LocalRepo>,
43
- options: {
44
- log?: Logger;
45
- throw_on_prod_cycles?: boolean;
46
- log_cycles?: boolean;
47
- log_order?: boolean;
48
- } = {}
49
- ): GraphValidationResult => {
50
- const { log, throw_on_prod_cycles = true, log_cycles = true, log_order = true } = options;
51
-
52
- // Build dependency graph
53
- log?.info('📊 Analyzing dependencies...');
54
- const builder = new DependencyGraphBuilder();
55
- const graph = builder.build_from_repos(repos);
56
-
57
- // Check for cycles
35
+ export const validate_dependency_graph = (repos: Array<LocalRepo>): GraphValidationResult => {
36
+ const graph = new DependencyGraph(repos);
58
37
  const { production_cycles, dev_cycles } = graph.detect_cycles_by_type();
59
38
 
60
- // Log production cycles
61
- if (production_cycles.length > 0 && log_cycles) {
62
- log?.error(st('red', '❌ Production/peer dependency cycles detected:'));
63
- for (const cycle of production_cycles) {
64
- log?.error(` ${cycle.join(' → ')}`);
65
- }
66
-
67
- if (throw_on_prod_cycles) {
68
- throw new TaskError(
69
- `Cannot publish with production/peer dependency cycles. ` +
70
- `These must be resolved before publishing.`
71
- );
72
- }
73
- }
74
-
75
- // Log dev cycles (informational, not an error)
76
- if (dev_cycles.length > 0 && log_cycles) {
77
- log?.info(st('dim', 'â„šī¸ Dev dependency cycles detected (this is normal):'));
78
- for (const cycle of dev_cycles) {
79
- log?.info(st('dim', ` ${cycle.join(' → ')}`));
80
- }
81
- }
82
-
83
- // Compute publishing order
84
39
  let publishing_order: Array<string>;
85
40
  let sort_error: string | undefined;
86
41
  try {
87
42
  publishing_order = graph.topological_sort(true); // exclude dev deps to break cycles
88
- if (log_order && publishing_order.length > 0) {
89
- log?.info(` Publishing order: ${publishing_order.join(' → ')}`);
90
- }
91
43
  } catch (error) {
92
- // Capture the sort error message for callers to report
93
44
  sort_error = 'Failed to compute publishing order: ' + error;
94
-
95
- // If topological sort fails (due to cycles), return empty array
96
- // Only throw if production cycles exist AND throw_on_prod_cycles is true
97
- if (production_cycles.length > 0 && throw_on_prod_cycles) {
98
- throw new TaskError(sort_error);
99
- }
100
- // Otherwise, return empty publishing order (let caller handle it)
101
45
  publishing_order = [];
102
46
  }
103
47
 
@@ -110,9 +54,6 @@ export const validate_dependency_graph = (
110
54
  };
111
55
  };
112
56
 
113
- /** Cycles, wildcard deps, and missing peers produced by `DependencyGraphBuilder.analyze`. */
114
- export type DependencyAnalysis = ReturnType<DependencyGraphBuilder['analyze']>;
115
-
116
57
  export interface RepoAnalysis {
117
58
  graph: DependencyGraph;
118
59
  analysis: DependencyAnalysis;
@@ -123,16 +64,14 @@ export interface RepoAnalysis {
123
64
  /**
124
65
  * Builds the dependency graph and runs cycle/wildcard analysis, tolerating cycles
125
66
  * (reports rather than throws). The shared core of `gitops_analyze` and
126
- * `gitops_validate`, which format the result themselves — so this stays silent
127
- * (no `log`) to avoid emitting a redundant "Analyzing dependencies" line.
67
+ * `gitops_validate`, which format the result themselves.
128
68
  */
129
69
  export const analyze_repos = (repos: Array<LocalRepo>): RepoAnalysis => {
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), {
132
- throw_on_prod_cycles: false, // report, don't throw
133
- log_cycles: false, // callers format their own cycle output
134
- log_order: false
135
- });
136
- const analysis = new DependencyGraphBuilder().analyze(graph);
137
- return { graph, analysis, publishing_order: order.length > 0 ? order : null };
70
+ // only npm packages form the dependency graph; non-npm repos (e.g. cargo) are excluded
71
+ const { graph, publishing_order } = validate_dependency_graph(repos.filter(repo_is_npm));
72
+ return {
73
+ graph,
74
+ analysis: graph.analyze(),
75
+ publishing_order: publishing_order.length > 0 ? publishing_order : null
76
+ };
138
77
  };