@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,11 +2,14 @@ import { TaskError } from '@fuzdev/gro';
2
2
  import { z } from 'zod';
3
3
  import { map_concurrent_settled } from '@fuzdev/fuz_util/async.ts';
4
4
  import { spawn_out } from '@fuzdev/fuz_util/process.ts';
5
+ import { to_error_message } from '@fuzdev/fuz_util/error.ts';
5
6
  import { writeFile } from 'node:fs/promises';
6
7
  import { styleText as st } from 'node:util';
7
- import { resolve } from 'node:path';
8
- import { get_repo_paths } from './repo_ops.js';
8
+ import { resolve_gitops_repos } from './gitops_task_helpers.js';
9
9
  import { GITOPS_CONCURRENCY_DEFAULT, GITOPS_CONFIG_PATH_DEFAULT } from './gitops_constants.js';
10
+ import { default_repos_operations } from './operations_defaults.js';
11
+ import { output_is_machine, route_human_output } from './output_helpers.js';
12
+ /** @nodocs */
10
13
  export const Args = z.strictObject({
11
14
  // Positional rest args (gro convention) so `gro gitops_run "npm test"` works;
12
15
  // joined with spaces and passed to `sh -c`, so quote commands that contain flags.
@@ -15,6 +18,12 @@ export const Args = z.strictObject({
15
18
  .string()
16
19
  .meta({ description: 'path to the gitops config file' })
17
20
  .default(GITOPS_CONFIG_PATH_DEFAULT),
21
+ registry: z
22
+ .string()
23
+ .meta({
24
+ description: 'path to the repos.toml registry, when `repos` would not find it walking up from the cwd'
25
+ })
26
+ .optional(),
18
27
  concurrency: z
19
28
  .number()
20
29
  .int()
@@ -27,180 +36,185 @@ export const Args = z.strictObject({
27
36
  .meta({ description: 'with --format json, write clean JSON to this file instead of stdout' })
28
37
  .optional()
29
38
  });
39
+ const default_gitops_run_deps = {
40
+ repos_ops: default_repos_operations,
41
+ run_command: async ({ command, cwd }) => {
42
+ // through a shell, so pipes, redirects, and globs work
43
+ const { result, stdout, stderr } = await spawn_out('sh', ['-c', command], { cwd });
44
+ const outcome = result.kind === 'exited'
45
+ ? { kind: 'exited', code: result.code }
46
+ : result.kind === 'signaled'
47
+ ? { kind: 'signaled', signal: result.signal }
48
+ : { kind: 'error', message: to_error_message(result.error) };
49
+ return { outcome, stdout: stdout ?? '', stderr: stderr ?? '' };
50
+ }
51
+ };
52
+ /** @nodocs */
30
53
  export const task = {
31
54
  Args,
32
55
  summary: 'run a shell command across all repos in parallel',
33
56
  run: async ({ args, log }) => {
34
- const { _, config, concurrency, format, outfile } = args;
35
- const command = _.join(' ').trim();
36
- if (!command) {
37
- throw new TaskError('No command provided, e.g. `gro gitops_run "npm test"`');
57
+ await run_gitops_run(args, log);
58
+ }
59
+ };
60
+ /**
61
+ * Runs `gro gitops_run`: resolves the config's repos through `repos status`,
62
+ * refusing a missing one, runs the command in each, `concurrency` at a time,
63
+ * and reports every result. Under `--format json` without `--outfile`, the log
64
+ * goes to stderr and stdout carries the JSON alone (`route_human_output`).
65
+ *
66
+ * @throws {TaskError} when no command is given, resolving the repos fails, or the command fails in any repo (after reporting)
67
+ * @nodocs
68
+ */
69
+ export const run_gitops_run = async (args, log, deps = {}) => {
70
+ const { repos_ops, run_command } = { ...default_gitops_run_deps, ...deps };
71
+ const { _, config, registry, concurrency, format, outfile } = args;
72
+ const write_stdout = route_human_output(log, output_is_machine(format, outfile));
73
+ const command = _.join(' ').trim();
74
+ if (!command) {
75
+ throw new TaskError('No command provided, e.g. `gro gitops_run "npm test"`');
76
+ }
77
+ // Resolve repo paths through `repos status` (no library-metadata loading needed);
78
+ // a configured repo that's missing or not a repo fails the run, naming it
79
+ const { local_repo_paths: repos } = await resolve_gitops_repos({
80
+ config,
81
+ registry,
82
+ log,
83
+ repos_ops
84
+ });
85
+ log.info(`Running ${st('cyan', command)} across ${repos.length} repos (concurrency: ${concurrency})`);
86
+ const start_time = performance.now();
87
+ const settled = await map_concurrent_settled(repos, concurrency, async ({ repo_name, repo_dir }) => {
88
+ const repo_start = performance.now();
89
+ const { outcome, stdout, stderr } = await run_command({ command, cwd: repo_dir });
90
+ return {
91
+ repo_name,
92
+ repo_dir,
93
+ status: outcome.kind === 'exited' && outcome.code === 0 ? 'success' : 'failure',
94
+ exit_code: outcome.kind === 'exited' ? outcome.code : null,
95
+ signal: outcome.kind === 'signaled' ? outcome.signal : null,
96
+ stdout,
97
+ stderr,
98
+ duration_ms: performance.now() - repo_start,
99
+ ...(outcome.kind === 'error' && { error: outcome.message })
100
+ };
101
+ });
102
+ const total_duration_ms = performance.now() - start_time;
103
+ // Process results; a rejection is `run_command` throwing, which the default never does
104
+ const successes = [];
105
+ const failures = [];
106
+ settled.forEach((result, i) => {
107
+ if (result.status === 'fulfilled') {
108
+ (result.value.status === 'success' ? successes : failures).push(result.value);
38
109
  }
39
- // Get repo paths (lightweight, no library-metadata loading needed)
40
- const config_path = resolve(config);
41
- const repos = await get_repo_paths(config_path);
42
- if (repos.length === 0) {
43
- throw new TaskError('No repos found in config');
110
+ else {
111
+ const { repo_name, repo_dir } = repos[i];
112
+ failures.push({
113
+ repo_name,
114
+ repo_dir,
115
+ status: 'failure',
116
+ exit_code: null,
117
+ signal: null,
118
+ stdout: '',
119
+ stderr: '',
120
+ duration_ms: 0,
121
+ error: to_error_message(result.reason)
122
+ });
44
123
  }
45
- log.info(`Running ${st('cyan', command)} across ${repos.length} repos (concurrency: ${concurrency})`);
46
- const start_time = performance.now();
47
- // Run command in parallel across all repos
48
- const results = await map_concurrent_settled(repos, concurrency, async (repo) => {
49
- const repo_start = performance.now();
50
- const repo_name = repo.name;
51
- const repo_dir = repo.path;
52
- try {
53
- // Parse command into cmd + args for spawn
54
- // For now, we use shell mode to support pipes/redirects/etc
55
- const spawned = await spawn_out('sh', ['-c', command], {
56
- cwd: repo_dir
57
- });
58
- const duration_ms = performance.now() - repo_start;
59
- const success = spawned.result.ok;
60
- const result = {
61
- repo_name,
62
- repo_dir,
63
- status: success ? 'success' : 'failure',
64
- exit_code: spawned.result.kind === 'exited' ? spawned.result.code : 0,
65
- stdout: spawned.stdout || '',
66
- stderr: spawned.stderr || '',
67
- duration_ms
68
- };
69
- return result;
70
- }
71
- catch (error) {
72
- const duration_ms = performance.now() - repo_start;
73
- return {
74
- repo_name,
75
- repo_dir,
76
- status: 'failure',
77
- exit_code: -1,
78
- stdout: '',
79
- stderr: '',
80
- duration_ms,
81
- error: String(error)
82
- };
83
- }
84
- });
85
- const total_duration_ms = performance.now() - start_time;
86
- // Process results
87
- const successes = [];
88
- const failures = [];
89
- for (const result of results) {
90
- if (result.status === 'fulfilled') {
91
- const run_result = result.value;
92
- if (run_result.status === 'success') {
93
- successes.push(run_result);
94
- }
95
- else {
96
- failures.push(run_result);
97
- }
98
- }
99
- else {
100
- // This shouldn't happen since we catch errors in the task fn
101
- // but handle it anyway
102
- failures.push({
103
- repo_name: 'unknown',
104
- repo_dir: 'unknown',
105
- status: 'failure',
106
- exit_code: -1,
107
- stdout: '',
108
- stderr: '',
109
- duration_ms: 0,
110
- error: String(result.reason)
111
- });
124
+ });
125
+ // Output results based on format
126
+ if (format === 'json') {
127
+ const json_output = {
128
+ command,
129
+ concurrency,
130
+ repos: [...successes, ...failures],
131
+ summary: {
132
+ total: repos.length,
133
+ success: successes.length,
134
+ failure: failures.length,
135
+ duration_ms: Math.round(total_duration_ms)
112
136
  }
137
+ };
138
+ const json = JSON.stringify(json_output, null, 2);
139
+ if (outfile) {
140
+ // Clean machine-readable output, free of gro's logging prefixes.
141
+ await writeFile(outfile, json);
142
+ log.info(`wrote JSON output to ${outfile}`);
113
143
  }
114
- // Output results based on format
115
- if (format === 'json') {
116
- const json_output = {
117
- command,
118
- concurrency,
119
- repos: [...successes, ...failures],
120
- summary: {
121
- total: repos.length,
122
- success: successes.length,
123
- failure: failures.length,
124
- duration_ms: Math.round(total_duration_ms)
125
- }
126
- };
127
- const json = JSON.stringify(json_output, null, 2);
128
- if (outfile) {
129
- // Clean machine-readable output, free of gro's logging prefixes.
130
- await writeFile(outfile, json);
131
- log.info(`wrote JSON output to ${outfile}`);
144
+ else {
145
+ write_stdout(json);
146
+ }
147
+ }
148
+ else {
149
+ log_text_results({ successes, failures, total: repos.length, total_duration_ms }, log);
150
+ }
151
+ // Exit with error if any failures (so CI fails)
152
+ if (failures.length > 0) {
153
+ throw new TaskError(`${failures.length} repos failed`);
154
+ }
155
+ };
156
+ const log_text_results = (data, log) => {
157
+ const { successes, failures, total, total_duration_ms } = data;
158
+ log.info(''); // blank line
159
+ // Show successes, including the command's output so read-only commands
160
+ // (`git rev-parse`, `cat package.json`, …) are useful without `--format json`.
161
+ if (successes.length > 0) {
162
+ log.info(st('green', `✓ ${successes.length} succeeded:`));
163
+ for (const result of successes) {
164
+ const duration = st('blue', `(${Math.round(result.duration_ms)}ms)`);
165
+ const label = st('gray', ` ${result.repo_name}`);
166
+ const out = result.stdout.trim();
167
+ const out_lines = out ? out.split('\n') : [];
168
+ if (out_lines.length <= 1) {
169
+ // single-line (or empty) output inline after the repo name
170
+ log.info(out ? `${label} ${out} ${duration}` : `${label} ${duration}`);
132
171
  }
133
172
  else {
134
- // eslint-disable-next-line no-console
135
- console.log(json);
173
+ // multi-line output indented under the repo
174
+ log.info(`${label} ${duration}`);
175
+ for (const line of out_lines) {
176
+ log.info(st('gray', ` ${line}`));
177
+ }
136
178
  }
137
179
  }
138
- else {
139
- // Text format
140
- log.info(''); // blank line
141
- // Show successes, including the command's output so read-only commands
142
- // (`git rev-parse`, `cat package.json`, …) are useful without `--format json`.
143
- if (successes.length > 0) {
144
- log.info(st('green', `✓ ${successes.length} succeeded:`));
145
- for (const result of successes) {
146
- const duration = st('blue', `(${Math.round(result.duration_ms)}ms)`);
147
- const label = st('gray', ` ${result.repo_name}`);
148
- const out = result.stdout.trim();
149
- const out_lines = out ? out.split('\n') : [];
150
- if (out_lines.length <= 1) {
151
- // single-line (or empty) output inline after the repo name
152
- log.info(out ? `${label} ${out} ${duration}` : `${label} ${duration}`);
153
- }
154
- else {
155
- // multi-line output indented under the repo
156
- log.info(`${label} ${duration}`);
157
- for (const line of out_lines) {
158
- log.info(st('gray', ` ${line}`));
159
- }
160
- }
161
- }
180
+ }
181
+ // Show failures with details
182
+ if (failures.length > 0) {
183
+ log.info(''); // blank line
184
+ log.error(st('red', `✗ ${failures.length} failed:`));
185
+ for (const result of failures) {
186
+ const duration = `${Math.round(result.duration_ms)}ms`;
187
+ log.error(st('gray', ` ${result.repo_name} ${st('blue', `(${duration})`)}`));
188
+ if (result.error) {
189
+ log.error(st('gray', ` Error: ${result.error}`));
162
190
  }
163
- // Show failures with details
164
- if (failures.length > 0) {
165
- log.info(''); // blank line
166
- log.error(st('red', `✗ ${failures.length} failed:`));
167
- for (const result of failures) {
168
- const duration = `${Math.round(result.duration_ms)}ms`;
169
- log.error(st('gray', ` ${result.repo_name} ${st('blue', `(${duration})`)}`));
170
- if (result.error) {
171
- log.error(st('gray', ` Error: ${result.error}`));
172
- }
173
- else if (result.exit_code !== 0) {
174
- log.error(st('gray', ` Exit code: ${result.exit_code}`));
175
- }
176
- if (result.stderr) {
177
- // Show first few lines of stderr
178
- const stderr_lines = result.stderr.trim().split('\n');
179
- const preview_lines = stderr_lines.slice(0, 3);
180
- for (const line of preview_lines) {
181
- log.error(st('gray', ` ${line}`));
182
- }
183
- if (stderr_lines.length > 3) {
184
- log.error(st('gray', ` ... (${stderr_lines.length - 3} more lines)`));
185
- }
186
- }
187
- }
191
+ else if (result.signal) {
192
+ log.error(st('gray', ` Killed by ${result.signal}`));
188
193
  }
189
- // Summary
190
- log.info(''); // blank line
191
- const total = repos.length;
192
- const success_rate = ((successes.length / total) * 100).toFixed(0);
193
- const duration = `${Math.round(total_duration_ms)}ms`;
194
- if (failures.length === 0) {
195
- log.info(st('green', `✓ All ${total} repos succeeded in ${duration} (${success_rate}% success rate)`));
194
+ else if (result.exit_code !== null) {
195
+ log.error(st('gray', ` Exit code: ${result.exit_code}`));
196
196
  }
197
- else {
198
- log.info(st('yellow', `⚠ ${successes.length}/${total} repos succeeded in ${duration} (${success_rate}% success rate)`));
197
+ if (result.stderr) {
198
+ // Show first few lines of stderr
199
+ const stderr_lines = result.stderr.trim().split('\n');
200
+ const preview_lines = stderr_lines.slice(0, 3);
201
+ for (const line of preview_lines) {
202
+ log.error(st('gray', ` ${line}`));
203
+ }
204
+ if (stderr_lines.length > 3) {
205
+ log.error(st('gray', ` ... (${stderr_lines.length - 3} more lines)`));
206
+ }
199
207
  }
200
208
  }
201
- // Exit with error if any failures (so CI fails)
202
- if (failures.length > 0) {
203
- throw new TaskError(`${failures.length} repos failed`);
204
- }
209
+ }
210
+ // Summary
211
+ log.info(''); // blank line
212
+ const success_rate = ((successes.length / total) * 100).toFixed(0);
213
+ const duration = `${Math.round(total_duration_ms)}ms`;
214
+ if (failures.length === 0) {
215
+ log.info(st('green', `✓ All ${total} repos succeeded in ${duration} (${success_rate}% success rate)`));
216
+ }
217
+ else {
218
+ log.info(st('yellow', `⚠ ${successes.length}/${total} repos succeeded in ${duration} (${success_rate}% success rate)`));
205
219
  }
206
220
  };
@@ -1,19 +1,81 @@
1
1
  import { type Task } from '@fuzdev/gro';
2
2
  import { z } from 'zod';
3
+ import type { Logger } from '@fuzdev/fuz_util/log.ts';
4
+ import { type LocalRepo, type LocalRepoPath } from './local_repo.js';
5
+ import type { ReposOperations } from './operations.js';
3
6
  /** @nodocs */
4
7
  export declare const Args: z.ZodObject<{
5
8
  config: z.ZodDefault<z.ZodString>;
6
- dir: z.ZodOptional<z.ZodString>;
9
+ registry: z.ZodOptional<z.ZodString>;
7
10
  outdir: z.ZodOptional<z.ZodString>;
8
- download: z.ZodDefault<z.ZodBoolean>;
9
11
  check: z.ZodDefault<z.ZodBoolean>;
10
12
  allow_dirty: z.ZodDefault<z.ZodBoolean>;
11
13
  }, z.core.$strict>;
12
14
  export type Args = z.infer<typeof Args>;
13
15
  /**
14
- * This is a task not a `.gen.` file because it makes network calls.
16
+ * Generates the dashboard's site data (`repos.json` and `repos.ts`) from each
17
+ * configured repo's working tree and its GitHub CI status and pull requests.
18
+ * It never moves a repo: see `prepare_gitops_sync` for what it reads and
19
+ * refuses. This is a task not a `.gen.` file because it makes network calls.
15
20
  *
16
21
  * @nodocs
17
22
  */
18
23
  export declare const task: Task<Args>;
24
+ /**
25
+ * The side effects `prepare_gitops_sync` reaches through, injectable for tests.
26
+ *
27
+ * @nodocs
28
+ */
29
+ export interface GitopsSyncDeps {
30
+ /** Runs `repos status`. */
31
+ repos_ops: ReposOperations;
32
+ /** Loads each resolved repo's library as it sits (`local_repos_load`). */
33
+ load_repos: (options: {
34
+ local_repo_paths: Array<LocalRepoPath>;
35
+ log?: Logger;
36
+ }) => Promise<Array<LocalRepo>>;
37
+ /** Reads `SECRET_GITHUB_API_TOKEN`; `undefined` when it isn't set. */
38
+ load_token: () => string | undefined;
39
+ /** Whether a path exists, for the analysis-setup warning. */
40
+ exists: (path: string) => boolean;
41
+ /** Reads a file as UTF-8, for the analysis-setup warning. */
42
+ read_file: (path: string) => string;
43
+ }
44
+ /**
45
+ * Everything `gitops_sync` does before it fetches from GitHub, in order:
46
+ *
47
+ * 1. resolves the config's repos from `repos status <keys…> --json` (local
48
+ * refs), refusing a private repo when the host package is public
49
+ * 2. checks each repo is ready to generate from (`check_gen_readiness`): on
50
+ * its registry branch, clean, and idle, unless `allow_dirty` — refusing
51
+ * before any network. With `check`, that report is the whole run: its
52
+ * warnings are logged, and it throws when a real run would refuse
53
+ * 3. reads `SECRET_GITHUB_API_TOKEN`
54
+ * 4. fetches the repos from origin (`repos status <keys…> --fetch --json`,
55
+ * which writes remote-tracking refs and nothing else) and checks them
56
+ * again, warning on a followed branch not in sync with origin and a
57
+ * failed fetch
58
+ * 5. warns on each npm repo the library analysis can't fully read: no
59
+ * `node_modules`, or no `.svelte-kit/tsconfig.json` when its tsconfig
60
+ * extends it (external types then read as `any`) — it installs nothing
61
+ * 6. loads each repo's library as it sits
62
+ *
63
+ * @returns the loaded repos and the token, or `null` when `check` passed
64
+ * @throws {TaskError} if resolving fails, a repo isn't ready, or the token is missing
65
+ * @nodocs
66
+ */
67
+ export declare const prepare_gitops_sync: (options: {
68
+ config: string;
69
+ registry?: string;
70
+ host?: {
71
+ name: string;
72
+ private: boolean;
73
+ };
74
+ check?: boolean;
75
+ allow_dirty?: boolean;
76
+ log: Logger;
77
+ }, deps?: Partial<GitopsSyncDeps>) => Promise<{
78
+ local_repos: Array<LocalRepo>;
79
+ token: string;
80
+ } | null>;
19
81
  //# sourceMappingURL=gitops_sync.task.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gitops_sync.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_sync.task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiBxB,cAAc;AACd,eAAO,MAAM,IAAI;;;;;;;kBAyBf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CAsG3B,CAAC"}
1
+ {"version":3,"file":"gitops_sync.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_sync.task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAOtD,OAAO,EAGN,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,cAAc;AACd,eAAO,MAAM,IAAI;;;;;;kBA8Bf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CA8F3B,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,2BAA2B;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,0EAA0E;IAC1E,UAAU,EAAE,CAAC,OAAO,EAAE;QACrB,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;QACvC,GAAG,CAAC,EAAE,MAAM,CAAC;KACb,KAAK,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAChC,sEAAsE;IACtE,UAAU,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACrC,6DAA6D;IAC7D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,6DAA6D;IAC7D,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACpC;AAWD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS;IACR,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACZ,EACD,OAAM,OAAO,CAAC,cAAc,CAAM,KAChC,OAAO,CAAC;IAAE,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAmEjE,CAAC"}