@fuzdev/fuz_gitops 0.77.0 → 0.78.1

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 (162) hide show
  1. package/README.md +25 -5
  2. package/dist/ModulesDetail.svelte +14 -12
  3. package/dist/ModulesDetail.svelte.d.ts.map +1 -1
  4. package/dist/ModulesNav.svelte +6 -6
  5. package/dist/ModulesNav.svelte.d.ts.map +1 -1
  6. package/dist/ModulesPage.svelte +2 -2
  7. package/dist/ModulesPage.svelte.d.ts.map +1 -1
  8. package/dist/PageFooter.svelte +2 -2
  9. package/dist/PageHeader.svelte +5 -5
  10. package/dist/PageHeader.svelte.d.ts +1 -1
  11. package/dist/PageHeader.svelte.d.ts.map +1 -1
  12. package/dist/PullRequestsDetail.svelte +25 -17
  13. package/dist/PullRequestsDetail.svelte.d.ts.map +1 -1
  14. package/dist/PullRequestsPage.svelte +3 -3
  15. package/dist/PullRequestsPage.svelte.d.ts.map +1 -1
  16. package/dist/ReposTable.svelte +24 -15
  17. package/dist/ReposTable.svelte.d.ts.map +1 -1
  18. package/dist/ReposTree.svelte +8 -6
  19. package/dist/ReposTree.svelte.d.ts.map +1 -1
  20. package/dist/ReposTreeNav.svelte +10 -11
  21. package/dist/ReposTreeNav.svelte.d.ts.map +1 -1
  22. package/dist/TablePage.svelte +2 -2
  23. package/dist/TablePage.svelte.d.ts.map +1 -1
  24. package/dist/TreeItemPage.svelte +2 -2
  25. package/dist/TreeItemPage.svelte.d.ts.map +1 -1
  26. package/dist/TreePage.svelte +2 -2
  27. package/dist/TreePage.svelte.d.ts.map +1 -1
  28. package/dist/cargo_toml.d.ts +33 -0
  29. package/dist/cargo_toml.d.ts.map +1 -0
  30. package/dist/cargo_toml.js +74 -0
  31. package/dist/changeset_generator.d.ts +1 -1
  32. package/dist/changeset_generator.d.ts.map +1 -1
  33. package/dist/changeset_generator.js +2 -2
  34. package/dist/changeset_reader.d.ts +1 -1
  35. package/dist/changeset_reader.d.ts.map +1 -1
  36. package/dist/changeset_reader.js +3 -3
  37. package/dist/config_reconcile.d.ts.map +1 -1
  38. package/dist/config_reconcile.js +6 -2
  39. package/dist/dependency_graph.d.ts +2 -2
  40. package/dist/dependency_graph.d.ts.map +1 -1
  41. package/dist/dependency_graph.js +9 -9
  42. package/dist/dependency_updater.d.ts +1 -1
  43. package/dist/dependency_updater.d.ts.map +1 -1
  44. package/dist/dependency_updater.js +8 -8
  45. package/dist/fetch_repo_data.d.ts +2 -2
  46. package/dist/fetch_repo_data.d.ts.map +1 -1
  47. package/dist/fetch_repo_data.js +4 -4
  48. package/dist/fs_fetch_value_cache.d.ts +1 -1
  49. package/dist/fs_fetch_value_cache.d.ts.map +1 -1
  50. package/dist/fs_fetch_value_cache.js +5 -5
  51. package/dist/git_operations.d.ts +1 -1
  52. package/dist/git_operations.d.ts.map +1 -1
  53. package/dist/git_operations.js +2 -2
  54. package/dist/github.d.ts +2 -2
  55. package/dist/github.d.ts.map +1 -1
  56. package/dist/github.js +7 -7
  57. package/dist/github_helpers.d.ts.map +1 -1
  58. package/dist/github_helpers.js +1 -1
  59. package/dist/gitops_analyze.task.d.ts.map +1 -1
  60. package/dist/gitops_analyze.task.js +21 -14
  61. package/dist/gitops_config.d.ts +2 -2
  62. package/dist/gitops_config.d.ts.map +1 -1
  63. package/dist/gitops_config.js +5 -5
  64. package/dist/gitops_plan.task.d.ts.map +1 -1
  65. package/dist/gitops_plan.task.js +8 -8
  66. package/dist/gitops_publish.task.d.ts.map +1 -1
  67. package/dist/gitops_publish.task.js +15 -12
  68. package/dist/gitops_run.task.d.ts.map +1 -1
  69. package/dist/gitops_run.task.js +10 -10
  70. package/dist/gitops_sync.task.d.ts.map +1 -1
  71. package/dist/gitops_sync.task.js +15 -13
  72. package/dist/gitops_task_helpers.d.ts +1 -1
  73. package/dist/gitops_task_helpers.d.ts.map +1 -1
  74. package/dist/gitops_task_helpers.js +4 -4
  75. package/dist/gitops_validate.task.d.ts.map +1 -1
  76. package/dist/gitops_validate.task.js +14 -14
  77. package/dist/graph_validation.d.ts +2 -2
  78. package/dist/graph_validation.d.ts.map +1 -1
  79. package/dist/graph_validation.js +5 -3
  80. package/dist/local_repo.d.ts +21 -7
  81. package/dist/local_repo.d.ts.map +1 -1
  82. package/dist/local_repo.js +64 -16
  83. package/dist/log_helpers.d.ts +1 -1
  84. package/dist/log_helpers.d.ts.map +1 -1
  85. package/dist/multi_repo_publisher.d.ts +3 -3
  86. package/dist/multi_repo_publisher.d.ts.map +1 -1
  87. package/dist/multi_repo_publisher.js +24 -20
  88. package/dist/npm_registry.d.ts +1 -1
  89. package/dist/npm_registry.d.ts.map +1 -1
  90. package/dist/npm_registry.js +4 -4
  91. package/dist/operations.d.ts +4 -4
  92. package/dist/operations.d.ts.map +1 -1
  93. package/dist/operations_defaults.d.ts.map +1 -1
  94. package/dist/operations_defaults.js +16 -16
  95. package/dist/output_helpers.d.ts +1 -1
  96. package/dist/output_helpers.d.ts.map +1 -1
  97. package/dist/preflight_checks.d.ts +2 -2
  98. package/dist/preflight_checks.d.ts.map +1 -1
  99. package/dist/preflight_checks.js +5 -5
  100. package/dist/publish_gate.d.ts.map +1 -1
  101. package/dist/publish_steps.d.ts.map +1 -1
  102. package/dist/publish_steps.js +4 -4
  103. package/dist/publishing_event.d.ts.map +1 -1
  104. package/dist/publishing_event.js +16 -14
  105. package/dist/publishing_event_handler.d.ts.map +1 -1
  106. package/dist/publishing_event_handler.js +6 -6
  107. package/dist/publishing_plan.d.ts +3 -3
  108. package/dist/publishing_plan.d.ts.map +1 -1
  109. package/dist/publishing_plan.js +25 -15
  110. package/dist/publishing_plan_helpers.d.ts.map +1 -1
  111. package/dist/publishing_plan_helpers.js +3 -3
  112. package/dist/publishing_plan_logging.d.ts +1 -1
  113. package/dist/publishing_plan_logging.d.ts.map +1 -1
  114. package/dist/publishing_plan_logging.js +2 -2
  115. package/dist/repo.svelte.d.ts +5 -5
  116. package/dist/repo.svelte.d.ts.map +1 -1
  117. package/dist/repo.svelte.js +2 -2
  118. package/dist/repo_ops.js +2 -2
  119. package/dist/resolved_gitops_config.d.ts.map +1 -1
  120. package/dist/resolved_gitops_config.js +1 -1
  121. package/dist/version_utils.js +1 -1
  122. package/package.json +16 -15
  123. package/src/lib/cargo_toml.ts +82 -0
  124. package/src/lib/changeset_generator.ts +20 -20
  125. package/src/lib/changeset_reader.ts +18 -18
  126. package/src/lib/ci_reconcile.ts +5 -5
  127. package/src/lib/config_reconcile.ts +8 -4
  128. package/src/lib/dependency_graph.ts +28 -28
  129. package/src/lib/dependency_updater.ts +30 -30
  130. package/src/lib/fetch_repo_data.ts +11 -11
  131. package/src/lib/fs_fetch_value_cache.ts +14 -10
  132. package/src/lib/git_operations.ts +31 -31
  133. package/src/lib/github.ts +19 -19
  134. package/src/lib/github_helpers.ts +6 -6
  135. package/src/lib/gitops_analyze.task.ts +56 -48
  136. package/src/lib/gitops_config.ts +14 -14
  137. package/src/lib/gitops_plan.task.ts +32 -32
  138. package/src/lib/gitops_publish.task.ts +52 -49
  139. package/src/lib/gitops_run.task.ts +29 -29
  140. package/src/lib/gitops_sync.task.ts +39 -30
  141. package/src/lib/gitops_task_helpers.ts +21 -21
  142. package/src/lib/gitops_validate.task.ts +38 -38
  143. package/src/lib/graph_validation.ts +14 -13
  144. package/src/lib/local_repo.ts +130 -64
  145. package/src/lib/log_helpers.ts +15 -15
  146. package/src/lib/multi_repo_publisher.ts +75 -74
  147. package/src/lib/npm_registry.ts +13 -13
  148. package/src/lib/operations.ts +50 -39
  149. package/src/lib/operations_defaults.ts +108 -106
  150. package/src/lib/output_helpers.ts +4 -4
  151. package/src/lib/preflight_checks.ts +28 -28
  152. package/src/lib/publish_gate.ts +7 -9
  153. package/src/lib/publish_steps.ts +15 -15
  154. package/src/lib/publishing_event.ts +22 -20
  155. package/src/lib/publishing_event_handler.ts +8 -8
  156. package/src/lib/publishing_plan.ts +69 -57
  157. package/src/lib/publishing_plan_helpers.ts +16 -12
  158. package/src/lib/publishing_plan_logging.ts +23 -23
  159. package/src/lib/repo.svelte.ts +8 -8
  160. package/src/lib/repo_ops.ts +12 -12
  161. package/src/lib/resolved_gitops_config.ts +5 -5
  162. package/src/lib/version_utils.ts +3 -3
@@ -1,26 +1,36 @@
1
- import {strip_end} from '@fuzdev/fuz_util/string.js';
2
- import type {LibraryJson} from '@fuzdev/fuz_util/library_json.js';
3
- import type {PackageJson} from '@fuzdev/fuz_util/package_json.js';
4
- import {Library} from '@fuzdev/fuz_ui/library.svelte.js';
5
- import {existsSync} from 'node:fs';
6
- import {join} from 'node:path';
7
- import {TaskError} from '@fuzdev/gro';
8
- import {library_load_from_repo} from '@fuzdev/gro/library_load.js';
9
- import type {Logger} from '@fuzdev/fuz_util/log.js';
10
- import {spawn_out} from '@fuzdev/fuz_util/process.js';
11
- import {map_concurrent_settled} from '@fuzdev/fuz_util/async.js';
12
- import type {GitOperations, NpmOperations} from './operations.js';
13
- import {default_git_operations, default_npm_operations} from './operations_defaults.js';
14
-
15
- import type {GitopsConfig, GitopsRepoConfig} from './gitops_config.js';
16
- import type {ResolvedGitopsConfig} from './resolved_gitops_config.js';
17
- import {GITOPS_CONCURRENCY_DEFAULT} from './gitops_constants.js';
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';
6
+ import { existsSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { TaskError } from '@fuzdev/gro';
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;
@@ -87,7 +97,7 @@ export const local_repo_load = async ({
87
97
  git_ops = default_git_operations,
88
98
  npm_ops = default_npm_operations,
89
99
  sync = true,
90
- allow_dirty = false,
100
+ allow_dirty = false
91
101
  }: {
92
102
  local_repo_path: LocalRepoPath;
93
103
  log?: Logger;
@@ -96,20 +106,20 @@ export const local_repo_load = async ({
96
106
  sync?: boolean;
97
107
  allow_dirty?: boolean;
98
108
  }): Promise<LocalRepo> => {
99
- const {repo_config, repo_dir, repo_name, repo_git_ssh_url} = local_repo_path;
109
+ const { repo_config, repo_dir, repo_name, repo_git_ssh_url } = local_repo_path;
100
110
 
101
111
  if (sync) {
102
112
  // Record commit hash before any changes
103
- const commit_before_result = await git_ops.current_commit_hash({cwd: repo_dir});
113
+ const commit_before_result = await git_ops.current_commit_hash({ cwd: repo_dir });
104
114
  if (!commit_before_result.ok) {
105
115
  throw new TaskError(
106
- `Failed to get commit hash in ${repo_dir}: ${commit_before_result.message}`,
116
+ `Failed to get commit hash in ${repo_dir}: ${commit_before_result.message}`
107
117
  );
108
118
  }
109
119
  const commit_before = commit_before_result.value;
110
120
 
111
121
  // Switch to target branch if needed
112
- const branch_result = await git_ops.current_branch_name({cwd: repo_dir});
122
+ const branch_result = await git_ops.current_branch_name({ cwd: repo_dir });
113
123
  if (!branch_result.ok) {
114
124
  throw new TaskError(`Failed to get current branch in ${repo_dir}: ${branch_result.message}`);
115
125
  }
@@ -119,28 +129,28 @@ export const local_repo_load = async ({
119
129
  // Guard the switch on a clean workspace unless the caller opts into `allow_dirty`,
120
130
  // in which case we let `git checkout` itself fail loudly if it can't proceed.
121
131
  if (!allow_dirty) {
122
- const clean_result = await git_ops.check_clean_workspace({cwd: repo_dir});
132
+ const clean_result = await git_ops.check_clean_workspace({ cwd: repo_dir });
123
133
  if (!clean_result.ok) {
124
134
  throw new TaskError(`Failed to check workspace in ${repo_dir}: ${clean_result.message}`);
125
135
  }
126
136
 
127
137
  if (!clean_result.value) {
128
138
  throw new TaskError(
129
- `Repo ${repo_dir} is not on branch "${repo_config.branch}" and the workspace is unclean, blocking switch`,
139
+ `Repo ${repo_dir} is not on branch "${repo_config.branch}" and the workspace is unclean, blocking switch`
130
140
  );
131
141
  }
132
142
  }
133
143
 
134
- const checkout_result = await git_ops.checkout({branch: repo_config.branch, cwd: repo_dir});
144
+ const checkout_result = await git_ops.checkout({ branch: repo_config.branch, cwd: repo_dir });
135
145
  if (!checkout_result.ok) {
136
146
  throw new TaskError(
137
- `Failed to checkout branch "${repo_config.branch}" in ${repo_dir}: ${checkout_result.message}`,
147
+ `Failed to checkout branch "${repo_config.branch}" in ${repo_dir}: ${checkout_result.message}`
138
148
  );
139
149
  }
140
150
  }
141
151
 
142
152
  // Only pull if remote exists (skip for local-only repos, test fixtures)
143
- const origin_result = await git_ops.has_remote({remote: 'origin', cwd: repo_dir});
153
+ const origin_result = await git_ops.has_remote({ remote: 'origin', cwd: repo_dir });
144
154
  if (!origin_result.ok) {
145
155
  throw new TaskError(`Failed to check for remote in ${repo_dir}: ${origin_result.message}`);
146
156
  }
@@ -149,7 +159,7 @@ export const local_repo_load = async ({
149
159
  // Pull the configured branch explicitly. Without a branch, `git pull origin`
150
160
  // targets the remote's default branch (origin/HEAD), which for a repo checked
151
161
  // out on a non-default branch rebases the wrong branch onto it.
152
- const pull_result = await git_ops.pull({branch: repo_config.branch, cwd: repo_dir});
162
+ const pull_result = await git_ops.pull({ branch: repo_config.branch, cwd: repo_dir });
153
163
  if (!pull_result.ok) {
154
164
  throw new TaskError(`Failed to pull in ${repo_dir}: ${pull_result.message}`);
155
165
  }
@@ -158,25 +168,25 @@ export const local_repo_load = async ({
158
168
  // Check clean workspace after pull to ensure we're in a good state
159
169
  // (skipped when `allow_dirty`, since uncommitted changes are expected then)
160
170
  if (!allow_dirty) {
161
- const clean_after_result = await git_ops.check_clean_workspace({cwd: repo_dir});
171
+ const clean_after_result = await git_ops.check_clean_workspace({ cwd: repo_dir });
162
172
  if (!clean_after_result.ok) {
163
173
  throw new TaskError(
164
- `Failed to check workspace in ${repo_dir}: ${clean_after_result.message}`,
174
+ `Failed to check workspace in ${repo_dir}: ${clean_after_result.message}`
165
175
  );
166
176
  }
167
177
 
168
178
  if (!clean_after_result.value) {
169
179
  throw new TaskError(
170
- `Workspace ${repo_dir} is unclean after pulling branch "${repo_config.branch}"`,
180
+ `Workspace ${repo_dir} is unclean after pulling branch "${repo_config.branch}"`
171
181
  );
172
182
  }
173
183
  }
174
184
 
175
185
  // Record commit hash after pull
176
- const commit_after_result = await git_ops.current_commit_hash({cwd: repo_dir});
186
+ const commit_after_result = await git_ops.current_commit_hash({ cwd: repo_dir });
177
187
  if (!commit_after_result.ok) {
178
188
  throw new TaskError(
179
- `Failed to get commit hash in ${repo_dir}: ${commit_after_result.message}`,
189
+ `Failed to get commit hash in ${repo_dir}: ${commit_after_result.message}`
180
190
  );
181
191
  }
182
192
  const commit_after = commit_after_result.value;
@@ -190,48 +200,57 @@ export const local_repo_load = async ({
190
200
  from_commit: commit_before,
191
201
  to_commit: commit_after,
192
202
  file_path: 'package.json',
193
- cwd: repo_dir,
203
+ cwd: repo_dir
194
204
  });
195
205
 
196
206
  if (!changed_result.ok) {
197
207
  throw new TaskError(
198
- `Failed to check if package.json changed in ${repo_dir}: ${changed_result.message}`,
208
+ `Failed to check if package.json changed in ${repo_dir}: ${changed_result.message}`
199
209
  );
200
210
  }
201
211
 
202
212
  if (changed_result.value) {
203
- const install_result = await npm_ops.install({cwd: repo_dir});
213
+ const install_result = await npm_ops.install({ cwd: repo_dir });
204
214
  if (!install_result.ok) {
205
215
  throw new TaskError(
206
- `Failed to install dependencies in ${repo_dir}: ${install_result.message}${install_result.stderr ? `\n${install_result.stderr}` : ''}`,
216
+ `Failed to install dependencies in ${repo_dir}: ${install_result.message}${install_result.stderr ? `\n${install_result.stderr}` : ''}`
207
217
  );
208
218
  }
209
219
  }
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
- ({library_json, package_json} = await library_load_from_repo(repo_dir, {log: _log}));
235
+ ({ library_json, package_json } = await library_load_from_repo(repo_dir, { log: _log }));
218
236
  } catch (err) {
219
- const message = err instanceof Error ? err.message : String(err);
237
+ const message = to_error_message(err);
220
238
  _log?.warn(
221
- `Failed to load library metadata for repo "${repo_name}" in ${repo_dir}: ${message}`,
239
+ `Failed to load library metadata for repo "${repo_name}" in ${repo_dir}: ${message}`
222
240
  );
223
241
  throw new TaskError(
224
- `Failed to load library metadata for repo "${repo_name}" in ${repo_dir}: ${message}`,
242
+ `Failed to load library metadata for repo "${repo_name}" in ${repo_dir}: ${message}`
225
243
  );
226
244
  }
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,
233
252
  repo_git_ssh_url,
234
- repo_config,
253
+ repo_config
235
254
  };
236
255
 
237
256
  // Extract dependencies from the full package_json
@@ -248,13 +267,60 @@ 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,
254
320
  gitops_config,
255
321
  download,
256
322
  log,
257
- npm_ops = default_npm_operations,
323
+ npm_ops = default_npm_operations
258
324
  }: {
259
325
  resolved_config: ResolvedGitopsConfig;
260
326
  repos_dir: string;
@@ -273,19 +339,19 @@ export const local_repos_ensure = async ({
273
339
  repos_dir,
274
340
  local_repos_missing: resolved_config.local_repos_missing,
275
341
  log,
276
- npm_ops,
342
+ npm_ops
277
343
  });
278
344
  local_repo_paths = (resolved_config.local_repo_paths ?? [])
279
345
  .concat(downloaded)
280
346
  .sort(
281
347
  (a, b) =>
282
348
  gitops_config.repos.findIndex((r) => r.repo_url === a.repo_url) -
283
- gitops_config.repos.findIndex((r) => r.repo_url === b.repo_url),
349
+ gitops_config.repos.findIndex((r) => r.repo_url === b.repo_url)
284
350
  );
285
351
  } else {
286
352
  log?.error(
287
353
  `Failed to resolve local repos in ${repos_dir} - do you need to pass \`--download\` or configure the directory?`, // TODO leaking task impl details
288
- resolved_config.local_repos_missing.map((r) => r.repo_url),
354
+ resolved_config.local_repos_missing.map((r) => r.repo_url)
289
355
  );
290
356
  throw new TaskError('Failed to resolve local configs');
291
357
  }
@@ -306,7 +372,7 @@ export const local_repos_load = async ({
306
372
  parallel = true,
307
373
  concurrency = GITOPS_CONCURRENCY_DEFAULT,
308
374
  sync = true,
309
- allow_dirty = false,
375
+ allow_dirty = false
310
376
  }: {
311
377
  local_repo_paths: Array<LocalRepoPath>;
312
378
  log?: Logger;
@@ -322,7 +388,7 @@ export const local_repos_load = async ({
322
388
  const loaded: Array<LocalRepo> = [];
323
389
  for (const local_repo_path of local_repo_paths) {
324
390
  loaded.push(
325
- await local_repo_load({local_repo_path, log, git_ops, npm_ops, sync, allow_dirty}),
391
+ await local_repo_load({ local_repo_path, log, git_ops, npm_ops, sync, allow_dirty })
326
392
  );
327
393
  }
328
394
  return loaded;
@@ -333,13 +399,13 @@ export const local_repos_load = async ({
333
399
  local_repo_paths,
334
400
  concurrency,
335
401
  async (local_repo_path) => {
336
- return local_repo_load({local_repo_path, log, git_ops, npm_ops, sync, allow_dirty});
337
- },
402
+ return local_repo_load({ local_repo_path, log, git_ops, npm_ops, sync, allow_dirty });
403
+ }
338
404
  );
339
405
 
340
406
  // Check for failures and collect successes
341
407
  const loaded: Array<LocalRepo> = [];
342
- const errors: Array<{repo_name: string; error: string}> = [];
408
+ const errors: Array<{ repo_name: string; error: string }> = [];
343
409
 
344
410
  for (let i = 0; i < results.length; i++) {
345
411
  const result = results[i]!;
@@ -349,7 +415,7 @@ export const local_repos_load = async ({
349
415
  const repo_path = local_repo_paths[i]!;
350
416
  errors.push({
351
417
  repo_name: repo_path.repo_name,
352
- error: String(result.reason),
418
+ error: String(result.reason)
353
419
  });
354
420
  }
355
421
  }
@@ -365,12 +431,12 @@ export const local_repos_load = async ({
365
431
 
366
432
  export const local_repo_locate = ({
367
433
  repo_config,
368
- repos_dir,
434
+ repos_dir
369
435
  }: {
370
436
  repo_config: GitopsRepoConfig;
371
437
  repos_dir: string;
372
438
  }): LocalRepoPath | LocalRepoMissing => {
373
- const {repo_url} = repo_config;
439
+ const { repo_url } = repo_config;
374
440
  const repo_name = strip_end(repo_url, '/').split('/').at(-1);
375
441
  if (!repo_name) throw Error('Invalid `repo_config.repo_url` ' + repo_url);
376
442
 
@@ -378,7 +444,7 @@ export const local_repo_locate = ({
378
444
 
379
445
  const repo_dir = repo_config.repo_dir ?? join(repos_dir, repo_name);
380
446
  if (!existsSync(repo_dir)) {
381
- return {type: 'local_repo_missing', repo_name, repo_url, repo_git_ssh_url, repo_config};
447
+ return { type: 'local_repo_missing', repo_name, repo_url, repo_git_ssh_url, repo_config };
382
448
  }
383
449
 
384
450
  return {
@@ -387,7 +453,7 @@ export const local_repo_locate = ({
387
453
  repo_dir,
388
454
  repo_url,
389
455
  repo_git_ssh_url,
390
- repo_config,
456
+ repo_config
391
457
  };
392
458
  };
393
459
 
@@ -400,7 +466,7 @@ const download_repos = async ({
400
466
  repos_dir,
401
467
  local_repos_missing,
402
468
  log,
403
- npm_ops = default_npm_operations,
469
+ npm_ops = default_npm_operations
404
470
  }: {
405
471
  repos_dir: string;
406
472
  local_repos_missing: Array<LocalRepoMissing>;
@@ -408,26 +474,26 @@ const download_repos = async ({
408
474
  npm_ops?: NpmOperations;
409
475
  }): Promise<Array<LocalRepoPath>> => {
410
476
  const resolved: Array<LocalRepoPath> = [];
411
- for (const {repo_config, repo_git_ssh_url} of local_repos_missing) {
477
+ for (const { repo_config, repo_git_ssh_url } of local_repos_missing) {
412
478
  log?.info(`cloning repo ${repo_git_ssh_url} to ${repos_dir}`);
413
- const clone_result = await spawn_out('git', ['clone', repo_git_ssh_url], {cwd: repos_dir});
479
+ const clone_result = await spawn_out('git', ['clone', repo_git_ssh_url], { cwd: repos_dir });
414
480
  if (!clone_result.result.ok) {
415
481
  throw new TaskError(
416
- `Failed to clone repo ${repo_git_ssh_url} to ${repos_dir}${clone_result.stderr ? ': ' + clone_result.stderr.trim() : ''}`,
482
+ `Failed to clone repo ${repo_git_ssh_url} to ${repos_dir}${clone_result.stderr ? ': ' + clone_result.stderr.trim() : ''}`
417
483
  );
418
484
  }
419
- const local_repo = local_repo_locate({repo_config, repos_dir});
485
+ const local_repo = local_repo_locate({ repo_config, repos_dir });
420
486
  if (local_repo.type === 'local_repo_missing') {
421
487
  throw new TaskError(
422
- `Failed to clone repo ${repo_git_ssh_url} to ${repos_dir}: directory not found after clone`,
488
+ `Failed to clone repo ${repo_git_ssh_url} to ${repos_dir}: directory not found after clone`
423
489
  );
424
490
  }
425
491
  // Always install dependencies after cloning
426
492
  log?.info(`installing dependencies for newly cloned repo ${local_repo.repo_dir}`);
427
- const install_result = await npm_ops.install({cwd: local_repo.repo_dir});
493
+ const install_result = await npm_ops.install({ cwd: local_repo.repo_dir });
428
494
  if (!install_result.ok) {
429
495
  throw new TaskError(
430
- `Failed to install dependencies in ${local_repo.repo_dir}: ${install_result.message}${install_result.stderr ? `\n${install_result.stderr}` : ''}`,
496
+ `Failed to install dependencies in ${local_repo.repo_dir}: ${install_result.message}${install_result.stderr ? `\n${install_result.stderr}` : ''}`
431
497
  );
432
498
  }
433
499
  resolved.push(local_repo);
@@ -1,14 +1,14 @@
1
- import type {Logger} from '@fuzdev/fuz_util/log.js';
2
- import {styleText as st} from 'node:util';
1
+ import type { Logger } from '@fuzdev/fuz_util/log.ts';
2
+ import { styleText as st } from 'node:util';
3
3
 
4
- import type {DependencyGraphBuilder} from './dependency_graph.js';
4
+ import type { DependencyGraphBuilder } from './dependency_graph.ts';
5
5
 
6
6
  /**
7
7
  * Formats wildcard dependencies as styled strings.
8
8
  * Returns array of lines for inclusion in output.
9
9
  */
10
10
  export const format_wildcard_dependencies = (
11
- analysis: ReturnType<DependencyGraphBuilder['analyze']>,
11
+ analysis: ReturnType<DependencyGraphBuilder['analyze']>
12
12
  ): Array<string> => {
13
13
  if (analysis.wildcard_deps.length === 0) return [];
14
14
 
@@ -17,7 +17,7 @@ export const format_wildcard_dependencies = (
17
17
  // a leading `\n` in the header would leave the header line unprefixed.
18
18
  lines.push('');
19
19
  lines.push(st('yellow', `⚠️ Found ${analysis.wildcard_deps.length} wildcard dependencies:`));
20
- for (const {pkg, dep, version} of analysis.wildcard_deps) {
20
+ for (const { pkg, dep, version } of analysis.wildcard_deps) {
21
21
  lines.push(` ${pkg} → ${dep} ${st('red', version)}`);
22
22
  }
23
23
  return lines;
@@ -28,7 +28,7 @@ export const format_wildcard_dependencies = (
28
28
  * Returns array of lines for inclusion in output.
29
29
  */
30
30
  export const format_dev_cycles = (
31
- analysis: ReturnType<DependencyGraphBuilder['analyze']>,
31
+ analysis: ReturnType<DependencyGraphBuilder['analyze']>
32
32
  ): Array<string> => {
33
33
  if (analysis.dev_cycles.length === 0) return [];
34
34
 
@@ -37,8 +37,8 @@ export const format_dev_cycles = (
37
37
  lines.push(
38
38
  st(
39
39
  'dim',
40
- `ℹ️ Found ${analysis.dev_cycles.length} dev circular dependencies (normal, non-blocking):`,
41
- ),
40
+ `ℹ️ Found ${analysis.dev_cycles.length} dev circular dependencies (normal, non-blocking):`
41
+ )
42
42
  );
43
43
  for (const cycle of analysis.dev_cycles) {
44
44
  lines.push(st('dim', ` ${cycle.join(' → ')}`));
@@ -51,7 +51,7 @@ export const format_dev_cycles = (
51
51
  * Returns array of lines for inclusion in output.
52
52
  */
53
53
  export const format_production_cycles = (
54
- analysis: ReturnType<DependencyGraphBuilder['analyze']>,
54
+ analysis: ReturnType<DependencyGraphBuilder['analyze']>
55
55
  ): Array<string> => {
56
56
  if (analysis.production_cycles.length === 0) return [];
57
57
 
@@ -60,8 +60,8 @@ export const format_production_cycles = (
60
60
  lines.push(
61
61
  st(
62
62
  'red',
63
- `❌ Found ${analysis.production_cycles.length} production/peer circular dependencies (blocks publishing):`,
64
- ),
63
+ `❌ Found ${analysis.production_cycles.length} production/peer circular dependencies (blocks publishing):`
64
+ )
65
65
  );
66
66
  for (const cycle of analysis.production_cycles) {
67
67
  lines.push(` ${st('red', cycle.join(' → '))}`);
@@ -76,7 +76,7 @@ export const format_production_cycles = (
76
76
  export const log_wildcard_dependencies = (
77
77
  analysis: ReturnType<DependencyGraphBuilder['analyze']>,
78
78
  log: Logger,
79
- indent = '',
79
+ indent = ''
80
80
  ): void => {
81
81
  const lines = format_wildcard_dependencies(analysis);
82
82
  for (const line of lines) {
@@ -91,7 +91,7 @@ export const log_wildcard_dependencies = (
91
91
  export const log_dev_cycles = (
92
92
  analysis: ReturnType<DependencyGraphBuilder['analyze']>,
93
93
  log: Logger,
94
- indent = '',
94
+ indent = ''
95
95
  ): void => {
96
96
  const lines = format_dev_cycles(analysis);
97
97
  for (const line of lines) {
@@ -106,7 +106,7 @@ export const log_dev_cycles = (
106
106
  export const log_production_cycles = (
107
107
  analysis: ReturnType<DependencyGraphBuilder['analyze']>,
108
108
  log: Logger,
109
- indent = '',
109
+ indent = ''
110
110
  ): void => {
111
111
  const lines = format_production_cycles(analysis);
112
112
  for (const line of lines) {
@@ -121,7 +121,7 @@ export const log_production_cycles = (
121
121
  export const log_dependency_analysis = (
122
122
  analysis: ReturnType<DependencyGraphBuilder['analyze']>,
123
123
  log: Logger,
124
- indent = '',
124
+ indent = ''
125
125
  ): void => {
126
126
  log_wildcard_dependencies(analysis, log, indent);
127
127
  log_production_cycles(analysis, log, indent);