@fuzdev/fuz_gitops 0.72.0 → 0.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -8
- package/dist/PageHeader.svelte +3 -3
- package/dist/PageHeader.svelte.d.ts +3 -3
- package/dist/PageHeader.svelte.d.ts.map +1 -1
- package/dist/changeset_generator.d.ts.map +1 -1
- package/dist/changeset_generator.js +2 -16
- package/dist/changeset_reader.d.ts +1 -1
- package/dist/changeset_reader.d.ts.map +1 -1
- package/dist/changeset_reader.js +1 -1
- package/dist/dependency_graph.d.ts.map +1 -1
- package/dist/dependency_graph.js +6 -6
- package/dist/fetch_repo_data.d.ts.map +1 -1
- package/dist/fetch_repo_data.js +3 -2
- package/dist/git_operations.d.ts +5 -0
- package/dist/git_operations.d.ts.map +1 -1
- package/dist/git_operations.js +5 -0
- package/dist/gitops_analyze.task.d.ts +1 -0
- package/dist/gitops_analyze.task.d.ts.map +1 -1
- package/dist/gitops_analyze.task.js +12 -17
- package/dist/gitops_plan.task.d.ts +1 -0
- package/dist/gitops_plan.task.d.ts.map +1 -1
- package/dist/gitops_plan.task.js +9 -2
- package/dist/gitops_publish.task.d.ts +2 -2
- package/dist/gitops_publish.task.d.ts.map +1 -1
- package/dist/gitops_publish.task.js +83 -28
- package/dist/gitops_run.task.d.ts +2 -1
- package/dist/gitops_run.task.d.ts.map +1 -1
- package/dist/gitops_run.task.js +40 -7
- package/dist/gitops_sync.task.d.ts +1 -0
- package/dist/gitops_sync.task.d.ts.map +1 -1
- package/dist/gitops_sync.task.js +16 -2
- package/dist/gitops_task_helpers.d.ts +16 -2
- package/dist/gitops_task_helpers.d.ts.map +1 -1
- package/dist/gitops_task_helpers.js +11 -3
- package/dist/gitops_validate.task.d.ts +1 -0
- package/dist/gitops_validate.task.d.ts.map +1 -1
- package/dist/gitops_validate.task.js +20 -20
- package/dist/graph_validation.d.ts +16 -1
- package/dist/graph_validation.d.ts.map +1 -1
- package/dist/graph_validation.js +15 -0
- package/dist/local_repo.d.ts +25 -12
- package/dist/local_repo.d.ts.map +1 -1
- package/dist/local_repo.js +99 -79
- package/dist/log_helpers.d.ts +0 -5
- package/dist/log_helpers.d.ts.map +1 -1
- package/dist/log_helpers.js +8 -19
- package/dist/multi_repo_publisher.d.ts +21 -2
- package/dist/multi_repo_publisher.d.ts.map +1 -1
- package/dist/multi_repo_publisher.js +279 -287
- package/dist/operations.d.ts +6 -26
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations_defaults.d.ts.map +1 -1
- package/dist/operations_defaults.js +4 -20
- package/dist/output_helpers.d.ts.map +1 -1
- package/dist/output_helpers.js +7 -6
- package/dist/paths.d.ts +0 -4
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +0 -4
- package/dist/publish_gate.d.ts +44 -0
- package/dist/publish_gate.d.ts.map +1 -0
- package/dist/publish_gate.js +32 -0
- package/dist/publish_steps.d.ts +60 -0
- package/dist/publish_steps.d.ts.map +1 -0
- package/dist/publish_steps.js +113 -0
- package/dist/publishing_event.d.ts +12 -19
- package/dist/publishing_event.d.ts.map +1 -1
- package/dist/publishing_event.js +16 -25
- package/dist/publishing_plan.d.ts +1 -2
- package/dist/publishing_plan.d.ts.map +1 -1
- package/dist/publishing_plan.js +21 -4
- package/dist/publishing_plan_helpers.d.ts +1 -1
- package/dist/publishing_plan_helpers.d.ts.map +1 -1
- package/dist/publishing_plan_helpers.js +2 -15
- package/dist/publishing_plan_logging.d.ts.map +1 -1
- package/dist/publishing_plan_logging.js +15 -23
- package/dist/repo.svelte.d.ts +10 -6
- package/dist/repo.svelte.d.ts.map +1 -1
- package/dist/repo.svelte.js +6 -14
- package/dist/version_utils.d.ts +17 -3
- package/dist/version_utils.d.ts.map +1 -1
- package/dist/version_utils.js +22 -0
- package/package.json +14 -15
- package/src/lib/changeset_generator.ts +10 -19
- package/src/lib/changeset_reader.ts +2 -3
- package/src/lib/dependency_graph.ts +6 -8
- package/src/lib/fetch_repo_data.ts +3 -2
- package/src/lib/git_operations.ts +5 -0
- package/src/lib/gitops_analyze.task.ts +18 -23
- package/src/lib/gitops_plan.task.ts +10 -2
- package/src/lib/gitops_publish.task.ts +92 -27
- package/src/lib/gitops_run.task.ts +39 -7
- package/src/lib/gitops_sync.task.ts +18 -3
- package/src/lib/gitops_task_helpers.ts +20 -3
- package/src/lib/gitops_validate.task.ts +30 -22
- package/src/lib/graph_validation.ts +26 -0
- package/src/lib/local_repo.ts +124 -88
- package/src/lib/log_helpers.ts +8 -26
- package/src/lib/multi_repo_publisher.ts +336 -353
- package/src/lib/operations.ts +6 -22
- package/src/lib/operations_defaults.ts +4 -20
- package/src/lib/output_helpers.ts +7 -6
- package/src/lib/paths.ts +0 -5
- package/src/lib/publish_gate.ts +53 -0
- package/src/lib/publish_steps.ts +149 -0
- package/src/lib/publishing_event.ts +16 -25
- package/src/lib/publishing_plan.ts +26 -6
- package/src/lib/publishing_plan_helpers.ts +5 -18
- package/src/lib/publishing_plan_logging.ts +19 -39
- package/src/lib/repo.svelte.ts +12 -15
- package/src/lib/version_utils.ts +30 -9
- package/dist/npm_install_helpers.d.ts +0 -23
- package/dist/npm_install_helpers.d.ts.map +0 -1
- package/dist/npm_install_helpers.js +0 -60
- package/dist/semver.d.ts +0 -26
- package/dist/semver.d.ts.map +0 -1
- package/dist/semver.js +0 -137
- package/dist/serialization_types.d.ts +0 -59
- package/dist/serialization_types.d.ts.map +0 -1
- package/dist/serialization_types.js +0 -42
- package/src/lib/npm_install_helpers.ts +0 -85
- package/src/lib/semver.ts +0 -170
- package/src/lib/serialization_types.ts +0 -92
|
@@ -109,3 +109,29 @@ export const validate_dependency_graph = (
|
|
|
109
109
|
sort_error,
|
|
110
110
|
};
|
|
111
111
|
};
|
|
112
|
+
|
|
113
|
+
/** Cycles, wildcard deps, and missing peers produced by `DependencyGraphBuilder.analyze`. */
|
|
114
|
+
export type DependencyAnalysis = ReturnType<DependencyGraphBuilder['analyze']>;
|
|
115
|
+
|
|
116
|
+
export interface RepoAnalysis {
|
|
117
|
+
graph: DependencyGraph;
|
|
118
|
+
analysis: DependencyAnalysis;
|
|
119
|
+
/** Topological publishing order, or `null` when prod/peer cycles prevent ordering. */
|
|
120
|
+
publishing_order: Array<string> | null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Builds the dependency graph and runs cycle/wildcard analysis, tolerating cycles
|
|
125
|
+
* (reports rather than throws). The shared core of `gitops_analyze` and
|
|
126
|
+
* `gitops_validate`, which format the result themselves — so this stays silent
|
|
127
|
+
* (no `log`) to avoid emitting a redundant "Analyzing dependencies" line.
|
|
128
|
+
*/
|
|
129
|
+
export const analyze_repos = (repos: Array<LocalRepo>): RepoAnalysis => {
|
|
130
|
+
const {graph, publishing_order: order} = validate_dependency_graph(repos, {
|
|
131
|
+
throw_on_prod_cycles: false, // report, don't throw
|
|
132
|
+
log_cycles: false, // callers format their own cycle output
|
|
133
|
+
log_order: false,
|
|
134
|
+
});
|
|
135
|
+
const analysis = new DependencyGraphBuilder().analyze(graph);
|
|
136
|
+
return {graph, analysis, publishing_order: order.length > 0 ? order : null};
|
|
137
|
+
};
|
package/src/lib/local_repo.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {strip_end} from '@fuzdev/fuz_util/string.js';
|
|
2
2
|
import type {LibraryJson} from '@fuzdev/fuz_util/library_json.js';
|
|
3
|
+
import type {PackageJson} from '@fuzdev/fuz_util/package_json.js';
|
|
3
4
|
import {Library} from '@fuzdev/fuz_ui/library.svelte.js';
|
|
4
5
|
import {existsSync} from 'node:fs';
|
|
5
6
|
import {join} from 'node:path';
|
|
@@ -21,7 +22,8 @@ import {GITOPS_CONCURRENCY_DEFAULT} from './gitops_constants.js';
|
|
|
21
22
|
*/
|
|
22
23
|
export interface LocalRepo {
|
|
23
24
|
library: Library;
|
|
24
|
-
|
|
25
|
+
/** The repo's full `package.json` (with `dependencies`/`devDependencies`). */
|
|
26
|
+
package_json: PackageJson;
|
|
25
27
|
repo_dir: string;
|
|
26
28
|
repo_git_ssh_url: string;
|
|
27
29
|
repo_config: GitopsRepoConfig;
|
|
@@ -55,135 +57,164 @@ export interface LocalRepoMissing {
|
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
|
-
* Loads repo data
|
|
60
|
+
* Loads repo data, optionally syncing the working tree first.
|
|
59
61
|
*
|
|
60
|
-
*
|
|
62
|
+
* When `sync` is `false` (the default for read-only diagnostics like
|
|
63
|
+
* `gitops_analyze`/`gitops_plan`), the repo is loaded exactly as it sits on
|
|
64
|
+
* disk — no branch switch, pull, install, or clean-workspace check. This makes
|
|
65
|
+
* those commands safe to run on an active workspace with uncommitted changes or
|
|
66
|
+
* feature branches checked out.
|
|
67
|
+
*
|
|
68
|
+
* When `sync` is `true` (used by `gitops_sync`), the working tree is brought in
|
|
69
|
+
* line with the configured branch first:
|
|
61
70
|
* 1. Records current commit hash (for detecting changes)
|
|
62
|
-
* 2. Switches to target branch if needed (requires clean workspace)
|
|
71
|
+
* 2. Switches to target branch if needed (requires clean workspace unless `allow_dirty`)
|
|
63
72
|
* 3. Pulls latest changes from remote (skipped for local-only repos)
|
|
64
|
-
* 4. Validates workspace is clean after pull
|
|
73
|
+
* 4. Validates workspace is clean after pull (skipped if `allow_dirty`)
|
|
65
74
|
* 5. Auto-installs dependencies if `package.json` changed
|
|
75
|
+
*
|
|
76
|
+
* Either way it then:
|
|
66
77
|
* 6. Loads `library_json` via `library_load_from_repo` (svelte-docinfo analysis)
|
|
67
78
|
* 7. Creates `Library` and extracts dependency maps
|
|
68
79
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* @throws {TaskError} if workspace dirty, branch switch fails, install fails, or analysis fails
|
|
80
|
+
* @param sync - sync the working tree to the configured branch before loading (default `true`)
|
|
81
|
+
* @param allow_dirty - when syncing, tolerate uncommitted changes instead of throwing (default `false`)
|
|
82
|
+
* @throws {TaskError} if syncing fails (dirty workspace, branch switch, install) or analysis fails
|
|
73
83
|
*/
|
|
74
84
|
export const local_repo_load = async ({
|
|
75
85
|
local_repo_path,
|
|
76
86
|
log: _log,
|
|
77
87
|
git_ops = default_git_operations,
|
|
78
88
|
npm_ops = default_npm_operations,
|
|
89
|
+
sync = true,
|
|
90
|
+
allow_dirty = false,
|
|
79
91
|
}: {
|
|
80
92
|
local_repo_path: LocalRepoPath;
|
|
81
93
|
log?: Logger;
|
|
82
94
|
git_ops?: GitOperations;
|
|
83
95
|
npm_ops?: NpmOperations;
|
|
96
|
+
sync?: boolean;
|
|
97
|
+
allow_dirty?: boolean;
|
|
84
98
|
}): Promise<LocalRepo> => {
|
|
85
99
|
const {repo_config, repo_dir, repo_name, repo_git_ssh_url} = local_repo_path;
|
|
86
100
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
`Failed to get commit hash in ${repo_dir}: ${commit_before_result.message}`,
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
const commit_before = commit_before_result.value;
|
|
95
|
-
|
|
96
|
-
// Switch to target branch if needed
|
|
97
|
-
const branch_result = await git_ops.current_branch_name({cwd: repo_dir});
|
|
98
|
-
if (!branch_result.ok) {
|
|
99
|
-
throw new TaskError(`Failed to get current branch in ${repo_dir}: ${branch_result.message}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const switched_branches = branch_result.value !== repo_config.branch;
|
|
103
|
-
if (switched_branches) {
|
|
104
|
-
const clean_result = await git_ops.check_clean_workspace({cwd: repo_dir});
|
|
105
|
-
if (!clean_result.ok) {
|
|
106
|
-
throw new TaskError(`Failed to check workspace in ${repo_dir}: ${clean_result.message}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (!clean_result.value) {
|
|
101
|
+
if (sync) {
|
|
102
|
+
// Record commit hash before any changes
|
|
103
|
+
const commit_before_result = await git_ops.current_commit_hash({cwd: repo_dir});
|
|
104
|
+
if (!commit_before_result.ok) {
|
|
110
105
|
throw new TaskError(
|
|
111
|
-
`
|
|
106
|
+
`Failed to get commit hash in ${repo_dir}: ${commit_before_result.message}`,
|
|
112
107
|
);
|
|
113
108
|
}
|
|
109
|
+
const commit_before = commit_before_result.value;
|
|
114
110
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
);
|
|
111
|
+
// Switch to target branch if needed
|
|
112
|
+
const branch_result = await git_ops.current_branch_name({cwd: repo_dir});
|
|
113
|
+
if (!branch_result.ok) {
|
|
114
|
+
throw new TaskError(`Failed to get current branch in ${repo_dir}: ${branch_result.message}`);
|
|
120
115
|
}
|
|
121
|
-
}
|
|
122
116
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
117
|
+
const switched_branches = branch_result.value !== repo_config.branch;
|
|
118
|
+
if (switched_branches) {
|
|
119
|
+
// Guard the switch on a clean workspace unless the caller opts into `allow_dirty`,
|
|
120
|
+
// in which case we let `git checkout` itself fail loudly if it can't proceed.
|
|
121
|
+
if (!allow_dirty) {
|
|
122
|
+
const clean_result = await git_ops.check_clean_workspace({cwd: repo_dir});
|
|
123
|
+
if (!clean_result.ok) {
|
|
124
|
+
throw new TaskError(`Failed to check workspace in ${repo_dir}: ${clean_result.message}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!clean_result.value) {
|
|
128
|
+
throw new TaskError(
|
|
129
|
+
`Repo ${repo_dir} is not on branch "${repo_config.branch}" and the workspace is unclean, blocking switch`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
128
133
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
const checkout_result = await git_ops.checkout({branch: repo_config.branch, cwd: repo_dir});
|
|
135
|
+
if (!checkout_result.ok) {
|
|
136
|
+
throw new TaskError(
|
|
137
|
+
`Failed to checkout branch "${repo_config.branch}" in ${repo_dir}: ${checkout_result.message}`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
133
140
|
}
|
|
134
|
-
}
|
|
135
141
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (!clean_after_result.value) {
|
|
143
|
-
throw new TaskError(
|
|
144
|
-
`Workspace ${repo_dir} is unclean after pulling branch "${repo_config.branch}"`,
|
|
145
|
-
);
|
|
146
|
-
}
|
|
142
|
+
// 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});
|
|
144
|
+
if (!origin_result.ok) {
|
|
145
|
+
throw new TaskError(`Failed to check for remote in ${repo_dir}: ${origin_result.message}`);
|
|
146
|
+
}
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
if (origin_result.value) {
|
|
149
|
+
// Pull the configured branch explicitly. Without a branch, `git pull origin`
|
|
150
|
+
// targets the remote's default branch (origin/HEAD), which for a repo checked
|
|
151
|
+
// 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});
|
|
153
|
+
if (!pull_result.ok) {
|
|
154
|
+
throw new TaskError(`Failed to pull in ${repo_dir}: ${pull_result.message}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
154
157
|
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
// Check clean workspace after pull to ensure we're in a good state
|
|
159
|
+
// (skipped when `allow_dirty`, since uncommitted changes are expected then)
|
|
160
|
+
if (!allow_dirty) {
|
|
161
|
+
const clean_after_result = await git_ops.check_clean_workspace({cwd: repo_dir});
|
|
162
|
+
if (!clean_after_result.ok) {
|
|
163
|
+
throw new TaskError(
|
|
164
|
+
`Failed to check workspace in ${repo_dir}: ${clean_after_result.message}`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
157
167
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
cwd: repo_dir,
|
|
165
|
-
});
|
|
168
|
+
if (!clean_after_result.value) {
|
|
169
|
+
throw new TaskError(
|
|
170
|
+
`Workspace ${repo_dir} is unclean after pulling branch "${repo_config.branch}"`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
166
174
|
|
|
167
|
-
|
|
175
|
+
// Record commit hash after pull
|
|
176
|
+
const commit_after_result = await git_ops.current_commit_hash({cwd: repo_dir});
|
|
177
|
+
if (!commit_after_result.ok) {
|
|
168
178
|
throw new TaskError(
|
|
169
|
-
`Failed to
|
|
179
|
+
`Failed to get commit hash in ${repo_dir}: ${commit_after_result.message}`,
|
|
170
180
|
);
|
|
171
181
|
}
|
|
182
|
+
const commit_after = commit_after_result.value;
|
|
183
|
+
|
|
184
|
+
// Track if we got new commits
|
|
185
|
+
const got_new_commits = commit_before !== commit_after;
|
|
186
|
+
|
|
187
|
+
// Only install if package.json changed
|
|
188
|
+
if (got_new_commits) {
|
|
189
|
+
const changed_result = await git_ops.has_file_changed({
|
|
190
|
+
from_commit: commit_before,
|
|
191
|
+
to_commit: commit_after,
|
|
192
|
+
file_path: 'package.json',
|
|
193
|
+
cwd: repo_dir,
|
|
194
|
+
});
|
|
172
195
|
|
|
173
|
-
|
|
174
|
-
const install_result = await npm_ops.install({cwd: repo_dir});
|
|
175
|
-
if (!install_result.ok) {
|
|
196
|
+
if (!changed_result.ok) {
|
|
176
197
|
throw new TaskError(
|
|
177
|
-
`Failed to
|
|
198
|
+
`Failed to check if package.json changed in ${repo_dir}: ${changed_result.message}`,
|
|
178
199
|
);
|
|
179
200
|
}
|
|
201
|
+
|
|
202
|
+
if (changed_result.value) {
|
|
203
|
+
const install_result = await npm_ops.install({cwd: repo_dir});
|
|
204
|
+
if (!install_result.ok) {
|
|
205
|
+
throw new TaskError(
|
|
206
|
+
`Failed to install dependencies in ${repo_dir}: ${install_result.message}${install_result.stderr ? `\n${install_result.stderr}` : ''}`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
180
210
|
}
|
|
181
211
|
}
|
|
182
212
|
|
|
183
213
|
// Load library metadata via svelte-docinfo analysis (cached under `.gro/library.json`).
|
|
184
214
|
let library_json: LibraryJson;
|
|
215
|
+
let package_json: PackageJson;
|
|
185
216
|
try {
|
|
186
|
-
library_json = await library_load_from_repo(repo_dir, {log: _log});
|
|
217
|
+
({library_json, package_json} = await library_load_from_repo(repo_dir, {log: _log}));
|
|
187
218
|
} catch (err) {
|
|
188
219
|
const message = err instanceof Error ? err.message : String(err);
|
|
189
220
|
_log?.warn(
|
|
@@ -197,14 +228,13 @@ export const local_repo_load = async ({
|
|
|
197
228
|
|
|
198
229
|
const local_repo: LocalRepo = {
|
|
199
230
|
library,
|
|
200
|
-
|
|
231
|
+
package_json,
|
|
201
232
|
repo_dir,
|
|
202
233
|
repo_git_ssh_url,
|
|
203
234
|
repo_config,
|
|
204
235
|
};
|
|
205
236
|
|
|
206
|
-
// Extract dependencies from package_json
|
|
207
|
-
const {package_json} = library;
|
|
237
|
+
// Extract dependencies from the full package_json
|
|
208
238
|
if (package_json.dependencies) {
|
|
209
239
|
local_repo.dependencies = new Map(Object.entries(package_json.dependencies));
|
|
210
240
|
}
|
|
@@ -275,6 +305,8 @@ export const local_repos_load = async ({
|
|
|
275
305
|
npm_ops = default_npm_operations,
|
|
276
306
|
parallel = true,
|
|
277
307
|
concurrency = GITOPS_CONCURRENCY_DEFAULT,
|
|
308
|
+
sync = true,
|
|
309
|
+
allow_dirty = false,
|
|
278
310
|
}: {
|
|
279
311
|
local_repo_paths: Array<LocalRepoPath>;
|
|
280
312
|
log?: Logger;
|
|
@@ -282,12 +314,16 @@ export const local_repos_load = async ({
|
|
|
282
314
|
npm_ops?: NpmOperations;
|
|
283
315
|
parallel?: boolean;
|
|
284
316
|
concurrency?: number;
|
|
317
|
+
sync?: boolean;
|
|
318
|
+
allow_dirty?: boolean;
|
|
285
319
|
}): Promise<Array<LocalRepo>> => {
|
|
286
320
|
if (!parallel) {
|
|
287
321
|
// Sequential loading (original behavior)
|
|
288
322
|
const loaded: Array<LocalRepo> = [];
|
|
289
323
|
for (const local_repo_path of local_repo_paths) {
|
|
290
|
-
loaded.push(
|
|
324
|
+
loaded.push(
|
|
325
|
+
await local_repo_load({local_repo_path, log, git_ops, npm_ops, sync, allow_dirty}),
|
|
326
|
+
);
|
|
291
327
|
}
|
|
292
328
|
return loaded;
|
|
293
329
|
}
|
|
@@ -297,7 +333,7 @@ export const local_repos_load = async ({
|
|
|
297
333
|
local_repo_paths,
|
|
298
334
|
concurrency,
|
|
299
335
|
async (local_repo_path) => {
|
|
300
|
-
return local_repo_load({local_repo_path, log, git_ops, npm_ops});
|
|
336
|
+
return local_repo_load({local_repo_path, log, git_ops, npm_ops, sync, allow_dirty});
|
|
301
337
|
},
|
|
302
338
|
);
|
|
303
339
|
|
package/src/lib/log_helpers.ts
CHANGED
|
@@ -13,7 +13,10 @@ export const format_wildcard_dependencies = (
|
|
|
13
13
|
if (analysis.wildcard_deps.length === 0) return [];
|
|
14
14
|
|
|
15
15
|
const lines: Array<string> = [];
|
|
16
|
-
|
|
16
|
+
// Blank separator as its own line so the logger prefixes it correctly; embedding
|
|
17
|
+
// a leading `\n` in the header would leave the header line unprefixed.
|
|
18
|
+
lines.push('');
|
|
19
|
+
lines.push(st('yellow', `⚠️ Found ${analysis.wildcard_deps.length} wildcard dependencies:`));
|
|
17
20
|
for (const {pkg, dep, version} of analysis.wildcard_deps) {
|
|
18
21
|
lines.push(` ${pkg} → ${dep} ${st('red', version)}`);
|
|
19
22
|
}
|
|
@@ -30,10 +33,11 @@ export const format_dev_cycles = (
|
|
|
30
33
|
if (analysis.dev_cycles.length === 0) return [];
|
|
31
34
|
|
|
32
35
|
const lines: Array<string> = [];
|
|
36
|
+
lines.push('');
|
|
33
37
|
lines.push(
|
|
34
38
|
st(
|
|
35
39
|
'dim',
|
|
36
|
-
|
|
40
|
+
`ℹ️ Found ${analysis.dev_cycles.length} dev circular dependencies (normal, non-blocking):`,
|
|
37
41
|
),
|
|
38
42
|
);
|
|
39
43
|
for (const cycle of analysis.dev_cycles) {
|
|
@@ -52,10 +56,11 @@ export const format_production_cycles = (
|
|
|
52
56
|
if (analysis.production_cycles.length === 0) return [];
|
|
53
57
|
|
|
54
58
|
const lines: Array<string> = [];
|
|
59
|
+
lines.push('');
|
|
55
60
|
lines.push(
|
|
56
61
|
st(
|
|
57
62
|
'red',
|
|
58
|
-
|
|
63
|
+
`❌ Found ${analysis.production_cycles.length} production/peer circular dependencies (blocks publishing):`,
|
|
59
64
|
),
|
|
60
65
|
);
|
|
61
66
|
for (const cycle of analysis.production_cycles) {
|
|
@@ -122,26 +127,3 @@ export const log_dependency_analysis = (
|
|
|
122
127
|
log_production_cycles(analysis, log, indent);
|
|
123
128
|
log_dev_cycles(analysis, log, indent);
|
|
124
129
|
};
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Logs a simple bulleted list with a header.
|
|
128
|
-
* Common pattern for warnings, info messages, and other lists.
|
|
129
|
-
*/
|
|
130
|
-
export const log_list = (
|
|
131
|
-
items: Array<string>,
|
|
132
|
-
header: string,
|
|
133
|
-
color: 'cyan' | 'yellow' | 'red' | 'dim',
|
|
134
|
-
log: Logger,
|
|
135
|
-
log_method: 'info' | 'warn' | 'error' = 'info',
|
|
136
|
-
): void => {
|
|
137
|
-
if (items.length === 0) return;
|
|
138
|
-
|
|
139
|
-
// Only add newline prefix if header is non-empty
|
|
140
|
-
const formatted_header = header ? `\n${header}` : '';
|
|
141
|
-
if (formatted_header) {
|
|
142
|
-
log[log_method](st(color, formatted_header));
|
|
143
|
-
}
|
|
144
|
-
for (const item of items) {
|
|
145
|
-
log[log_method](st(color, ` • ${item}`));
|
|
146
|
-
}
|
|
147
|
-
};
|