@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.
- package/README.md +25 -5
- package/dist/ModulesDetail.svelte +14 -12
- package/dist/ModulesDetail.svelte.d.ts.map +1 -1
- package/dist/ModulesNav.svelte +6 -6
- package/dist/ModulesNav.svelte.d.ts.map +1 -1
- package/dist/ModulesPage.svelte +2 -2
- package/dist/ModulesPage.svelte.d.ts.map +1 -1
- package/dist/PageFooter.svelte +2 -2
- package/dist/PageHeader.svelte +5 -5
- package/dist/PageHeader.svelte.d.ts +1 -1
- package/dist/PageHeader.svelte.d.ts.map +1 -1
- package/dist/PullRequestsDetail.svelte +25 -17
- package/dist/PullRequestsDetail.svelte.d.ts.map +1 -1
- package/dist/PullRequestsPage.svelte +3 -3
- package/dist/PullRequestsPage.svelte.d.ts.map +1 -1
- package/dist/ReposTable.svelte +24 -15
- package/dist/ReposTable.svelte.d.ts.map +1 -1
- package/dist/ReposTree.svelte +8 -6
- package/dist/ReposTree.svelte.d.ts.map +1 -1
- package/dist/ReposTreeNav.svelte +10 -11
- package/dist/ReposTreeNav.svelte.d.ts.map +1 -1
- package/dist/TablePage.svelte +2 -2
- package/dist/TablePage.svelte.d.ts.map +1 -1
- package/dist/TreeItemPage.svelte +2 -2
- package/dist/TreeItemPage.svelte.d.ts.map +1 -1
- package/dist/TreePage.svelte +2 -2
- package/dist/TreePage.svelte.d.ts.map +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_generator.d.ts.map +1 -1
- package/dist/changeset_generator.js +2 -2
- package/dist/changeset_reader.d.ts +1 -1
- package/dist/changeset_reader.d.ts.map +1 -1
- package/dist/changeset_reader.js +3 -3
- package/dist/config_reconcile.d.ts.map +1 -1
- package/dist/config_reconcile.js +6 -2
- package/dist/dependency_graph.d.ts +2 -2
- package/dist/dependency_graph.d.ts.map +1 -1
- package/dist/dependency_graph.js +9 -9
- package/dist/dependency_updater.d.ts +1 -1
- package/dist/dependency_updater.d.ts.map +1 -1
- package/dist/dependency_updater.js +8 -8
- package/dist/fetch_repo_data.d.ts +2 -2
- package/dist/fetch_repo_data.d.ts.map +1 -1
- package/dist/fetch_repo_data.js +4 -4
- package/dist/fs_fetch_value_cache.d.ts +1 -1
- package/dist/fs_fetch_value_cache.d.ts.map +1 -1
- package/dist/fs_fetch_value_cache.js +5 -5
- package/dist/git_operations.d.ts +1 -1
- package/dist/git_operations.d.ts.map +1 -1
- package/dist/git_operations.js +2 -2
- package/dist/github.d.ts +2 -2
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +7 -7
- package/dist/github_helpers.d.ts.map +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 +21 -14
- package/dist/gitops_config.d.ts +2 -2
- package/dist/gitops_config.d.ts.map +1 -1
- package/dist/gitops_config.js +5 -5
- package/dist/gitops_plan.task.d.ts.map +1 -1
- package/dist/gitops_plan.task.js +8 -8
- package/dist/gitops_publish.task.d.ts.map +1 -1
- package/dist/gitops_publish.task.js +15 -12
- package/dist/gitops_run.task.d.ts.map +1 -1
- package/dist/gitops_run.task.js +10 -10
- package/dist/gitops_sync.task.d.ts.map +1 -1
- package/dist/gitops_sync.task.js +15 -13
- package/dist/gitops_task_helpers.d.ts +1 -1
- package/dist/gitops_task_helpers.d.ts.map +1 -1
- package/dist/gitops_task_helpers.js +4 -4
- package/dist/gitops_validate.task.d.ts.map +1 -1
- package/dist/gitops_validate.task.js +14 -14
- package/dist/graph_validation.d.ts +2 -2
- package/dist/graph_validation.d.ts.map +1 -1
- package/dist/graph_validation.js +5 -3
- package/dist/local_repo.d.ts +21 -7
- package/dist/local_repo.d.ts.map +1 -1
- package/dist/local_repo.js +64 -16
- package/dist/log_helpers.d.ts +1 -1
- package/dist/log_helpers.d.ts.map +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 +24 -20
- package/dist/npm_registry.d.ts +1 -1
- package/dist/npm_registry.d.ts.map +1 -1
- package/dist/npm_registry.js +4 -4
- package/dist/operations.d.ts +4 -4
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations_defaults.d.ts.map +1 -1
- package/dist/operations_defaults.js +16 -16
- package/dist/output_helpers.d.ts +1 -1
- package/dist/output_helpers.d.ts.map +1 -1
- package/dist/preflight_checks.d.ts +2 -2
- package/dist/preflight_checks.d.ts.map +1 -1
- package/dist/preflight_checks.js +5 -5
- package/dist/publish_gate.d.ts.map +1 -1
- package/dist/publish_steps.d.ts.map +1 -1
- package/dist/publish_steps.js +4 -4
- package/dist/publishing_event.d.ts.map +1 -1
- package/dist/publishing_event.js +16 -14
- package/dist/publishing_event_handler.d.ts.map +1 -1
- package/dist/publishing_event_handler.js +6 -6
- package/dist/publishing_plan.d.ts +3 -3
- package/dist/publishing_plan.d.ts.map +1 -1
- package/dist/publishing_plan.js +25 -15
- package/dist/publishing_plan_helpers.d.ts.map +1 -1
- package/dist/publishing_plan_helpers.js +3 -3
- package/dist/publishing_plan_logging.d.ts +1 -1
- package/dist/publishing_plan_logging.d.ts.map +1 -1
- package/dist/publishing_plan_logging.js +2 -2
- package/dist/repo.svelte.d.ts +5 -5
- package/dist/repo.svelte.d.ts.map +1 -1
- package/dist/repo.svelte.js +2 -2
- package/dist/repo_ops.js +2 -2
- package/dist/resolved_gitops_config.d.ts.map +1 -1
- package/dist/resolved_gitops_config.js +1 -1
- package/dist/version_utils.js +1 -1
- package/package.json +16 -15
- package/src/lib/cargo_toml.ts +82 -0
- package/src/lib/changeset_generator.ts +20 -20
- package/src/lib/changeset_reader.ts +18 -18
- package/src/lib/ci_reconcile.ts +5 -5
- package/src/lib/config_reconcile.ts +8 -4
- package/src/lib/dependency_graph.ts +28 -28
- package/src/lib/dependency_updater.ts +30 -30
- package/src/lib/fetch_repo_data.ts +11 -11
- package/src/lib/fs_fetch_value_cache.ts +14 -10
- package/src/lib/git_operations.ts +31 -31
- package/src/lib/github.ts +19 -19
- package/src/lib/github_helpers.ts +6 -6
- package/src/lib/gitops_analyze.task.ts +56 -48
- package/src/lib/gitops_config.ts +14 -14
- package/src/lib/gitops_plan.task.ts +32 -32
- package/src/lib/gitops_publish.task.ts +52 -49
- package/src/lib/gitops_run.task.ts +29 -29
- package/src/lib/gitops_sync.task.ts +39 -30
- package/src/lib/gitops_task_helpers.ts +21 -21
- package/src/lib/gitops_validate.task.ts +38 -38
- package/src/lib/graph_validation.ts +14 -13
- package/src/lib/local_repo.ts +130 -64
- package/src/lib/log_helpers.ts +15 -15
- package/src/lib/multi_repo_publisher.ts +75 -74
- package/src/lib/npm_registry.ts +13 -13
- package/src/lib/operations.ts +50 -39
- package/src/lib/operations_defaults.ts +108 -106
- package/src/lib/output_helpers.ts +4 -4
- package/src/lib/preflight_checks.ts +28 -28
- package/src/lib/publish_gate.ts +7 -9
- package/src/lib/publish_steps.ts +15 -15
- package/src/lib/publishing_event.ts +22 -20
- package/src/lib/publishing_event_handler.ts +8 -8
- package/src/lib/publishing_plan.ts +69 -57
- package/src/lib/publishing_plan_helpers.ts +16 -12
- package/src/lib/publishing_plan_logging.ts +23 -23
- package/src/lib/repo.svelte.ts +8 -8
- package/src/lib/repo_ops.ts +12 -12
- package/src/lib/resolved_gitops_config.ts +5 -5
- package/src/lib/version_utils.ts +3 -3
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 {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import type {
|
|
17
|
-
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';
|
|
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 =
|
|
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);
|
package/src/lib/log_helpers.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type {Logger} from '@fuzdev/fuz_util/log.
|
|
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.
|
|
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);
|