@fuzdev/fuz_gitops 0.78.0 → 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.
- package/README.md +91 -17
- package/dist/ModulesDetail.svelte +22 -27
- package/dist/ModulesDetail.svelte.d.ts.map +1 -1
- package/dist/ModulesNav.svelte +8 -5
- package/dist/ModulesNav.svelte.d.ts.map +1 -1
- package/dist/ModulesPage.svelte +3 -5
- package/dist/ModulesPage.svelte.d.ts.map +1 -1
- package/dist/PageFooter.svelte +2 -2
- package/dist/PageHeader.svelte +6 -6
- package/dist/PageHeader.svelte.d.ts +0 -3
- package/dist/PageHeader.svelte.d.ts.map +1 -1
- package/dist/PullRequestsDetail.svelte +29 -20
- package/dist/PullRequestsDetail.svelte.d.ts.map +1 -1
- package/dist/PullRequestsPage.svelte +4 -6
- package/dist/PullRequestsPage.svelte.d.ts.map +1 -1
- package/dist/ReposTable.svelte +43 -55
- package/dist/ReposTable.svelte.d.ts.map +1 -1
- package/dist/ReposTree.svelte +9 -21
- package/dist/ReposTree.svelte.d.ts.map +1 -1
- package/dist/ReposTreeNav.svelte +9 -12
- package/dist/ReposTreeNav.svelte.d.ts.map +1 -1
- package/dist/TablePage.svelte +3 -5
- package/dist/TablePage.svelte.d.ts.map +1 -1
- package/dist/TreeItemPage.svelte +3 -5
- package/dist/TreeItemPage.svelte.d.ts.map +1 -1
- package/dist/TreePage.svelte +3 -5
- package/dist/TreePage.svelte.d.ts.map +1 -1
- package/dist/changeset_generator.d.ts.map +1 -1
- package/dist/changeset_generator.js +2 -2
- package/dist/changeset_reader.d.ts.map +1 -1
- package/dist/changeset_reader.js +3 -3
- package/dist/ci_reconcile.d.ts +6 -10
- package/dist/ci_reconcile.d.ts.map +1 -1
- package/dist/ci_reconcile.js +6 -8
- package/dist/dependency_graph.d.ts +26 -49
- package/dist/dependency_graph.d.ts.map +1 -1
- package/dist/dependency_graph.js +40 -83
- package/dist/dependency_updater.d.ts +1 -1
- package/dist/dependency_updater.d.ts.map +1 -1
- package/dist/dependency_updater.js +14 -9
- package/dist/fetch_repo_data.d.ts +34 -8
- package/dist/fetch_repo_data.d.ts.map +1 -1
- package/dist/fetch_repo_data.js +44 -22
- 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 +3 -3
- package/dist/git_operations.d.ts +6 -49
- package/dist/git_operations.d.ts.map +1 -1
- package/dist/git_operations.js +13 -126
- package/dist/github.d.ts +17 -2
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +17 -8
- package/dist/github_helpers.d.ts.map +1 -1
- package/dist/github_helpers.js +3 -11
- package/dist/gitops_analyze.task.d.ts +24 -2
- package/dist/gitops_analyze.task.d.ts.map +1 -1
- package/dist/gitops_analyze.task.js +47 -34
- package/dist/gitops_config.d.ts +34 -79
- package/dist/gitops_config.d.ts.map +1 -1
- package/dist/gitops_config.js +76 -53
- package/dist/gitops_constants.d.ts +6 -4
- package/dist/gitops_constants.d.ts.map +1 -1
- package/dist/gitops_constants.js +6 -4
- package/dist/gitops_plan.task.d.ts +29 -3
- package/dist/gitops_plan.task.d.ts.map +1 -1
- package/dist/gitops_plan.task.js +83 -61
- package/dist/gitops_publish.task.d.ts +62 -2
- package/dist/gitops_publish.task.d.ts.map +1 -1
- package/dist/gitops_publish.task.js +193 -105
- package/dist/gitops_run.task.d.ts +74 -0
- package/dist/gitops_run.task.d.ts.map +1 -1
- package/dist/gitops_run.task.js +176 -162
- package/dist/gitops_sync.task.d.ts +65 -3
- package/dist/gitops_sync.task.d.ts.map +1 -1
- package/dist/gitops_sync.task.js +163 -37
- package/dist/gitops_task_helpers.d.ts +86 -59
- package/dist/gitops_task_helpers.d.ts.map +1 -1
- package/dist/gitops_task_helpers.js +120 -79
- package/dist/gitops_validate.task.d.ts +1 -2
- package/dist/gitops_validate.task.d.ts.map +1 -1
- package/dist/gitops_validate.task.js +25 -32
- package/dist/graph_validation.d.ts +16 -27
- package/dist/graph_validation.d.ts.map +1 -1
- package/dist/graph_validation.js +24 -65
- package/dist/local_repo.d.ts +50 -62
- package/dist/local_repo.d.ts.map +1 -1
- package/dist/local_repo.js +83 -196
- package/dist/log_helpers.d.ts +8 -8
- package/dist/log_helpers.d.ts.map +1 -1
- package/dist/multi_repo_publisher.d.ts +10 -0
- package/dist/multi_repo_publisher.d.ts.map +1 -1
- package/dist/multi_repo_publisher.js +125 -61
- package/dist/npm_registry.d.ts +23 -19
- package/dist/npm_registry.d.ts.map +1 -1
- package/dist/npm_registry.js +21 -48
- package/dist/operations.d.ts +75 -180
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +15 -7
- package/dist/operations_defaults.d.ts +17 -2
- package/dist/operations_defaults.d.ts.map +1 -1
- package/dist/operations_defaults.js +111 -128
- package/dist/output_helpers.d.ts +36 -0
- package/dist/output_helpers.d.ts.map +1 -1
- package/dist/output_helpers.js +37 -3
- package/dist/preflight_checks.d.ts +20 -27
- package/dist/preflight_checks.d.ts.map +1 -1
- package/dist/preflight_checks.js +37 -126
- package/dist/publish_gate.d.ts.map +1 -1
- package/dist/publish_steps.d.ts +2 -4
- package/dist/publish_steps.d.ts.map +1 -1
- package/dist/publish_steps.js +4 -8
- package/dist/publishing_event.d.ts +2 -8
- package/dist/publishing_event.d.ts.map +1 -1
- package/dist/publishing_event.js +20 -17
- package/dist/publishing_event_handler.d.ts +10 -8
- package/dist/publishing_event_handler.d.ts.map +1 -1
- package/dist/publishing_event_handler.js +19 -17
- package/dist/publishing_plan.d.ts +16 -1
- package/dist/publishing_plan.d.ts.map +1 -1
- package/dist/publishing_plan.js +332 -254
- 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 +22 -15
- package/dist/repo.svelte.d.ts +7 -0
- package/dist/repo.svelte.d.ts.map +1 -1
- package/dist/repo.svelte.js +3 -0
- package/dist/repo_readiness.d.ts +210 -0
- package/dist/repo_readiness.d.ts.map +1 -0
- package/dist/repo_readiness.js +516 -0
- package/dist/repos_status.d.ts +3340 -0
- package/dist/repos_status.d.ts.map +1 -0
- package/dist/repos_status.js +555 -0
- package/dist/repos_status_load.d.ts +65 -0
- package/dist/repos_status_load.d.ts.map +1 -0
- package/dist/repos_status_load.js +131 -0
- package/dist/version_utils.js +1 -1
- package/package.json +15 -34
- package/src/lib/cargo_toml.ts +3 -3
- package/src/lib/changeset_generator.ts +19 -19
- package/src/lib/changeset_reader.ts +18 -18
- package/src/lib/ci_reconcile.ts +12 -16
- package/src/lib/dependency_graph.ts +61 -113
- package/src/lib/dependency_updater.ts +36 -30
- package/src/lib/fetch_repo_data.ts +71 -34
- package/src/lib/fs_fetch_value_cache.ts +15 -11
- package/src/lib/git_operations.ts +22 -194
- package/src/lib/github.ts +33 -21
- package/src/lib/github_helpers.ts +9 -16
- package/src/lib/gitops_analyze.task.ts +104 -66
- package/src/lib/gitops_config.ts +90 -139
- package/src/lib/gitops_constants.ts +6 -4
- package/src/lib/gitops_plan.task.ts +128 -76
- package/src/lib/gitops_publish.task.ts +280 -144
- package/src/lib/gitops_run.task.ts +265 -193
- package/src/lib/gitops_sync.task.ts +236 -52
- package/src/lib/gitops_task_helpers.ts +170 -114
- package/src/lib/gitops_validate.task.ts +44 -54
- package/src/lib/graph_validation.ts +28 -89
- package/src/lib/local_repo.ts +128 -335
- package/src/lib/log_helpers.ts +18 -28
- package/src/lib/multi_repo_publisher.ts +192 -110
- package/src/lib/npm_registry.ts +42 -67
- package/src/lib/operations.ts +99 -160
- package/src/lib/operations_defaults.ts +157 -201
- package/src/lib/output_helpers.ts +58 -6
- package/src/lib/preflight_checks.ts +52 -188
- package/src/lib/publish_gate.ts +7 -9
- package/src/lib/publish_steps.ts +27 -25
- package/src/lib/publishing_event.ts +26 -23
- package/src/lib/publishing_event_handler.ts +24 -22
- package/src/lib/publishing_plan.ts +474 -310
- package/src/lib/publishing_plan_helpers.ts +16 -12
- package/src/lib/publishing_plan_logging.ts +53 -44
- package/src/lib/repo.svelte.ts +16 -8
- package/src/lib/repo_readiness.ts +647 -0
- package/src/lib/repos_status.ts +671 -0
- package/src/lib/repos_status_load.ts +173 -0
- package/src/lib/version_utils.ts +3 -3
- package/dist/config_reconcile.d.ts +0 -46
- package/dist/config_reconcile.d.ts.map +0 -1
- package/dist/config_reconcile.js +0 -60
- package/dist/paths.d.ts +0 -7
- package/dist/paths.d.ts.map +0 -1
- package/dist/paths.js +0 -6
- package/dist/repo_ops.d.ts +0 -59
- package/dist/repo_ops.d.ts.map +0 -1
- package/dist/repo_ops.js +0 -170
- package/dist/resolved_gitops_config.d.ts +0 -9
- package/dist/resolved_gitops_config.d.ts.map +0 -1
- package/dist/resolved_gitops_config.js +0 -12
- package/src/lib/config_reconcile.ts +0 -94
- package/src/lib/paths.ts +0 -6
- package/src/lib/repo_ops.ts +0 -216
- package/src/lib/resolved_gitops_config.ts +0 -27
|
@@ -1,228 +1,113 @@
|
|
|
1
|
-
import type {Logger} from '@fuzdev/fuz_util/log.ts';
|
|
2
|
-
import
|
|
3
|
-
import {spawn_out} from '@fuzdev/fuz_util/process.ts';
|
|
4
|
-
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';
|
|
5
3
|
|
|
6
|
-
import type {LocalRepo} from './local_repo.ts';
|
|
7
|
-
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
BuildOperations,
|
|
11
|
-
ChangesetOperations,
|
|
12
|
-
} from './operations.ts';
|
|
13
|
-
import {
|
|
14
|
-
default_git_operations,
|
|
15
|
-
default_npm_operations,
|
|
16
|
-
default_build_operations,
|
|
17
|
-
default_changeset_operations,
|
|
18
|
-
} from './operations_defaults.ts';
|
|
19
|
-
|
|
20
|
-
export interface PreflightOptions {
|
|
21
|
-
skip_changesets?: boolean;
|
|
22
|
-
skip_build_validation?: boolean; // Skip build validation (useful for tests)
|
|
23
|
-
required_branch?: string;
|
|
24
|
-
check_remote?: boolean; // Check if git remote is reachable
|
|
25
|
-
estimate_time?: boolean; // Estimate total publish time
|
|
26
|
-
log?: Logger;
|
|
27
|
-
}
|
|
4
|
+
import type { LocalRepo } from './local_repo.ts';
|
|
5
|
+
import type { VersionChange } from './publishing_plan.ts';
|
|
6
|
+
import type { NpmOperations, BuildOperations } from './operations.ts';
|
|
7
|
+
import { default_npm_operations, default_build_operations } from './operations_defaults.ts';
|
|
28
8
|
|
|
29
9
|
export interface PreflightResult {
|
|
30
10
|
ok: boolean;
|
|
31
11
|
warnings: Array<string>;
|
|
32
12
|
errors: Array<string>;
|
|
33
|
-
repos_with_changesets: Set<string>;
|
|
34
|
-
repos_without_changesets: Set<string>;
|
|
35
|
-
estimated_duration?: number; // In seconds
|
|
36
|
-
npm_username?: string;
|
|
37
13
|
}
|
|
38
14
|
|
|
39
15
|
export interface RunPreflightChecksOptions {
|
|
40
16
|
repos: Array<LocalRepo>;
|
|
41
|
-
|
|
42
|
-
|
|
17
|
+
/**
|
|
18
|
+
* The plan's version changes — every package the run publishes, explicit,
|
|
19
|
+
* escalated, and auto-generated alike. Preflight builds exactly these.
|
|
20
|
+
*/
|
|
21
|
+
version_changes: Array<VersionChange>;
|
|
22
|
+
log?: Logger;
|
|
43
23
|
npm_ops?: NpmOperations;
|
|
44
24
|
build_ops?: BuildOperations;
|
|
45
|
-
changeset_ops?: ChangesetOperations;
|
|
46
25
|
}
|
|
47
26
|
|
|
48
27
|
/**
|
|
49
|
-
* Validates
|
|
28
|
+
* Validates the publish-time requirements beyond repo git state:
|
|
29
|
+
* - every package the plan publishes builds (fail-fast to prevent broken state)
|
|
30
|
+
* - npm authentication
|
|
31
|
+
* - npm registry connectivity
|
|
50
32
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* - NPM authentication with username
|
|
58
|
-
* - NPM registry connectivity
|
|
33
|
+
* What publishes is the plan's to decide, so preflight reads no changesets: it
|
|
34
|
+
* builds each package in `version_changes`. Git state — each repo on its
|
|
35
|
+
* registry branch, clean, idle, in sync with origin or ahead of it, and no live
|
|
36
|
+
* session in its checkout — is the readiness gate's, which `gitops_publish
|
|
37
|
+
* --wetrun` runs before the plan's confirmation prompt (`repo_readiness.ts`), so
|
|
38
|
+
* preflight reads no git.
|
|
59
39
|
*
|
|
60
40
|
* Build validation runs BEFORE any publishing to prevent the scenario where
|
|
61
41
|
* version is bumped but build fails, leaving repo in broken state.
|
|
62
42
|
*
|
|
63
|
-
* @returns result with `ok`=false if any errors, plus warnings
|
|
43
|
+
* @returns result with `ok`=false if any errors, plus warnings
|
|
64
44
|
*/
|
|
65
45
|
export const run_preflight_checks = async ({
|
|
66
46
|
repos,
|
|
67
|
-
|
|
68
|
-
|
|
47
|
+
version_changes,
|
|
48
|
+
log,
|
|
69
49
|
npm_ops = default_npm_operations,
|
|
70
|
-
build_ops = default_build_operations
|
|
71
|
-
changeset_ops = default_changeset_operations,
|
|
50
|
+
build_ops = default_build_operations
|
|
72
51
|
}: RunPreflightChecksOptions): Promise<PreflightResult> => {
|
|
73
|
-
const {
|
|
74
|
-
skip_changesets = false,
|
|
75
|
-
skip_build_validation = false,
|
|
76
|
-
required_branch = 'main',
|
|
77
|
-
check_remote = true,
|
|
78
|
-
estimate_time = true,
|
|
79
|
-
log,
|
|
80
|
-
} = preflight_options;
|
|
81
|
-
|
|
82
52
|
const warnings: Array<string> = [];
|
|
83
53
|
const errors: Array<string> = [];
|
|
84
|
-
const repos_with_changesets: Set<string> = new Set();
|
|
85
|
-
const repos_without_changesets: Set<string> = new Set();
|
|
86
|
-
let npm_username: string | undefined;
|
|
87
|
-
let estimated_duration: number | undefined;
|
|
88
54
|
|
|
89
55
|
log?.info(st('cyan', '✅ Running preflight checks...'));
|
|
90
56
|
|
|
91
|
-
// 1.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (!clean_result.value) {
|
|
101
|
-
// Get list of changed files for better error message
|
|
102
|
-
const files_result = await git_ops.list_uncommitted_files({cwd: repo.repo_dir});
|
|
103
|
-
if (files_result.ok) {
|
|
104
|
-
// No filtering - workspace must be 100% clean
|
|
105
|
-
const unexpected_files = files_result.value;
|
|
106
|
-
|
|
107
|
-
if (unexpected_files.length > 0) {
|
|
108
|
-
errors.push(
|
|
109
|
-
`${repo.library.name} has uncommitted changes in: ${unexpected_files.slice(0, 3).join(', ')}${unexpected_files.length > 3 ? ` and ${unexpected_files.length - 3} more` : ''}`,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
errors.push(`${repo.library.name} has uncommitted changes`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 2. Check correct branch
|
|
119
|
-
log?.info(` Checking branches (expecting ${required_branch})...`);
|
|
120
|
-
for (const repo of repos) {
|
|
121
|
-
const branch_result = await git_ops.current_branch_name({cwd: repo.repo_dir});
|
|
122
|
-
if (!branch_result.ok) {
|
|
123
|
-
errors.push(`${repo.library.name} failed branch check: ${branch_result.message}`);
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (branch_result.value !== required_branch) {
|
|
128
|
-
errors.push(
|
|
129
|
-
`${repo.library.name} is on branch '${branch_result.value}', expected '${required_branch}'`,
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// 3. Check changesets (unless skipped)
|
|
135
|
-
if (!skip_changesets) {
|
|
136
|
-
log?.info(' Checking for changesets...');
|
|
137
|
-
for (const repo of repos) {
|
|
138
|
-
const has_result = await changeset_ops.has_changesets({repo});
|
|
139
|
-
if (!has_result.ok) {
|
|
140
|
-
errors.push(`${repo.library.name} failed changeset check: ${has_result.message}`);
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (has_result.value) {
|
|
145
|
-
repos_with_changesets.add(repo.library.name);
|
|
146
|
-
} else {
|
|
147
|
-
repos_without_changesets.add(repo.library.name);
|
|
148
|
-
warnings.push(`${repo.library.name} has no changesets`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (repos_without_changesets.size > 0) {
|
|
153
|
-
log?.warn(st('yellow', ` ⚠️ ${repos_without_changesets.size} packages have no changesets`));
|
|
57
|
+
// 1. Build every package the plan publishes
|
|
58
|
+
const repo_by_name: Map<string, LocalRepo> = new Map(repos.map((r) => [r.library.name, r]));
|
|
59
|
+
const repos_to_build: Array<LocalRepo> = [];
|
|
60
|
+
for (const change of version_changes) {
|
|
61
|
+
const repo = repo_by_name.get(change.package_name);
|
|
62
|
+
if (repo) {
|
|
63
|
+
repos_to_build.push(repo);
|
|
64
|
+
} else {
|
|
65
|
+
errors.push(`${change.package_name} is in the plan but not among the repos`);
|
|
154
66
|
}
|
|
155
67
|
}
|
|
156
68
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const repos_to_build = repos.filter((repo) => repos_with_changesets.has(repo.library.name));
|
|
161
|
-
|
|
69
|
+
if (repos_to_build.length > 0) {
|
|
70
|
+
log?.info(st('cyan', ` Validating builds for ${repos_to_build.length} package(s)...`));
|
|
71
|
+
let build_failures = 0;
|
|
162
72
|
for (let i = 0; i < repos_to_build.length; i++) {
|
|
163
73
|
const repo = repos_to_build[i]!;
|
|
164
74
|
log?.info(
|
|
165
|
-
st('dim', ` [${i + 1}/${repos_to_build.length}] Building ${repo.library.name}...`)
|
|
75
|
+
st('dim', ` [${i + 1}/${repos_to_build.length}] Building ${repo.library.name}...`)
|
|
166
76
|
);
|
|
167
|
-
const build_result = await build_ops.build_package({repo
|
|
168
|
-
if (
|
|
77
|
+
const build_result = await build_ops.build_package({ repo });
|
|
78
|
+
if (build_result.ok) {
|
|
79
|
+
log?.info(st('dim', ` ✓ ${repo.library.name} built successfully`));
|
|
80
|
+
} else {
|
|
81
|
+
build_failures++;
|
|
169
82
|
errors.push(
|
|
170
|
-
`${repo.library.name} failed to build: ${build_result.output || build_result.message || 'unknown error'}
|
|
83
|
+
`${repo.library.name} failed to build: ${build_result.output || build_result.message || 'unknown error'}`
|
|
171
84
|
);
|
|
172
|
-
} else {
|
|
173
|
-
log?.info(st('dim', ` ✓ ${repo.library.name} built successfully`));
|
|
174
85
|
}
|
|
175
86
|
}
|
|
176
87
|
|
|
177
|
-
if (
|
|
88
|
+
if (build_failures > 0) {
|
|
178
89
|
log?.error(st('red', ' ❌ Build validation failed - fix build errors before publishing'));
|
|
179
90
|
} else {
|
|
180
91
|
log?.info(st('green', ' ✓ All builds validated successfully'));
|
|
181
92
|
}
|
|
182
93
|
}
|
|
183
94
|
|
|
184
|
-
//
|
|
185
|
-
if (check_remote && repos.length > 0) {
|
|
186
|
-
log?.info(' Checking git remote connectivity...');
|
|
187
|
-
// Only check first repo to avoid slowing down tests with multiple remote checks
|
|
188
|
-
const remote_result = await check_git_remote(repos[0]!.repo_dir);
|
|
189
|
-
if (!remote_result.ok) {
|
|
190
|
-
warnings.push(`git remote may not be reachable - ${remote_result.message}`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// 6. Check npm authentication with username
|
|
95
|
+
// 2. Check npm authentication
|
|
195
96
|
log?.info(' Checking npm authentication...');
|
|
196
97
|
const npm_auth_result = await npm_ops.check_auth();
|
|
197
|
-
if (
|
|
198
|
-
|
|
98
|
+
if (npm_auth_result.ok) {
|
|
99
|
+
log?.info(st('dim', ` Logged in as: ${npm_auth_result.username}`));
|
|
199
100
|
} else {
|
|
200
|
-
|
|
201
|
-
log?.info(st('dim', ` Logged in as: ${npm_username}`));
|
|
101
|
+
errors.push(`npm authentication failed: ${npm_auth_result.message || 'not logged in'}`);
|
|
202
102
|
}
|
|
203
103
|
|
|
204
|
-
//
|
|
104
|
+
// 3. Check network connectivity (npm registry)
|
|
205
105
|
log?.info(' Checking npm registry connectivity...');
|
|
206
106
|
const registry_result = await npm_ops.check_registry();
|
|
207
107
|
if (!registry_result.ok) {
|
|
208
108
|
warnings.push(`npm registry check failed: ${registry_result.message}`);
|
|
209
109
|
}
|
|
210
110
|
|
|
211
|
-
// 8. Estimate total publish time
|
|
212
|
-
if (estimate_time) {
|
|
213
|
-
const packages_to_publish = repos_with_changesets.size;
|
|
214
|
-
if (packages_to_publish > 0) {
|
|
215
|
-
// Rough estimate: 30s per package + 10s per package for NPM propagation
|
|
216
|
-
estimated_duration = packages_to_publish * 40;
|
|
217
|
-
log?.info(
|
|
218
|
-
st(
|
|
219
|
-
'dim',
|
|
220
|
-
` Estimated publish time: ~${Math.ceil(estimated_duration / 60)} minutes for ${packages_to_publish} package(s)`,
|
|
221
|
-
),
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
111
|
// Report results
|
|
227
112
|
const ok = errors.length === 0;
|
|
228
113
|
|
|
@@ -244,26 +129,5 @@ export const run_preflight_checks = async ({
|
|
|
244
129
|
log?.info(st('green', '\n✨ All preflight checks passed!'));
|
|
245
130
|
}
|
|
246
131
|
|
|
247
|
-
return {
|
|
248
|
-
ok,
|
|
249
|
-
warnings,
|
|
250
|
-
errors,
|
|
251
|
-
repos_with_changesets,
|
|
252
|
-
repos_without_changesets,
|
|
253
|
-
estimated_duration,
|
|
254
|
-
npm_username,
|
|
255
|
-
};
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
const check_git_remote = async (cwd: string): Promise<Result<object, {message: string}>> => {
|
|
259
|
-
try {
|
|
260
|
-
// Try to fetch refs from remote without downloading objects
|
|
261
|
-
const result = await spawn_out('git', ['ls-remote', '--heads', 'origin'], {cwd});
|
|
262
|
-
if (result.stdout || result.stderr) {
|
|
263
|
-
return {ok: true};
|
|
264
|
-
}
|
|
265
|
-
return {ok: false, message: 'No response from git remote'};
|
|
266
|
-
} catch (error) {
|
|
267
|
-
return {ok: false, message: String(error)};
|
|
268
|
-
}
|
|
132
|
+
return { ok, warnings, errors };
|
|
269
133
|
};
|
package/src/lib/publish_gate.ts
CHANGED
|
@@ -8,14 +8,12 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type {PublishingResult} from './multi_repo_publisher.ts';
|
|
12
|
-
import type {PublishingPlan} from './publishing_plan.ts';
|
|
11
|
+
import type { PublishingResult } from './multi_repo_publisher.ts';
|
|
12
|
+
import type { PublishingPlan } from './publishing_plan.ts';
|
|
13
13
|
|
|
14
14
|
/** What the task should do with a generated plan before executing the cascade. */
|
|
15
15
|
export type PublishGate =
|
|
16
|
-
|
|
17
|
-
| {action: 'confirm'}
|
|
18
|
-
| {action: 'proceed'};
|
|
16
|
+
{ action: 'blocked'; message: string } | { action: 'confirm' } | { action: 'proceed' };
|
|
19
17
|
|
|
20
18
|
export interface PublishGateOptions {
|
|
21
19
|
/** A real publish (`--wetrun`); a dry run never prompts. */
|
|
@@ -36,11 +34,11 @@ export interface PublishGateOptions {
|
|
|
36
34
|
* - `proceed`: a dry run, or a `--no-plan` real publish — no prompt.
|
|
37
35
|
*/
|
|
38
36
|
export const decide_publish_gate = (options: PublishGateOptions): PublishGate => {
|
|
39
|
-
if (!options.wetrun || !options.show_plan) return {action: 'proceed'};
|
|
37
|
+
if (!options.wetrun || !options.show_plan) return { action: 'proceed' };
|
|
40
38
|
if (options.plan.errors.length > 0) {
|
|
41
|
-
return {action: 'blocked', message: 'Cannot proceed with publishing due to errors'};
|
|
39
|
+
return { action: 'blocked', message: 'Cannot proceed with publishing due to errors' };
|
|
42
40
|
}
|
|
43
|
-
return {action: 'confirm'};
|
|
41
|
+
return { action: 'confirm' };
|
|
44
42
|
};
|
|
45
43
|
|
|
46
44
|
/**
|
|
@@ -49,5 +47,5 @@ export const decide_publish_gate = (options: PublishGateOptions): PublishGate =>
|
|
|
49
47
|
*/
|
|
50
48
|
export const publish_run_failed = (
|
|
51
49
|
result: Pick<PublishingResult, 'ok'>,
|
|
52
|
-
fatal_error: Error | null
|
|
50
|
+
fatal_error: Error | null
|
|
53
51
|
): boolean => !result.ok || fatal_error !== null;
|
package/src/lib/publish_steps.ts
CHANGED
|
@@ -8,17 +8,26 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type {BumpType} from './version_utils.ts';
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
import type { BumpType } from './version_utils.ts';
|
|
12
|
+
import {
|
|
13
|
+
version_change_kind,
|
|
14
|
+
type PublishingPlan,
|
|
15
|
+
type VersionChange,
|
|
16
|
+
type VersionChangeKind
|
|
17
|
+
} from './publishing_plan.ts';
|
|
18
|
+
import { UnreachableError } from '@fuzdev/fuz_util/error.ts';
|
|
17
19
|
|
|
18
20
|
/** One ordered side-effect a wetrun would perform. */
|
|
19
21
|
export type PublishStep =
|
|
20
|
-
| {
|
|
21
|
-
|
|
22
|
+
| {
|
|
23
|
+
kind: 'publish';
|
|
24
|
+
repo: string;
|
|
25
|
+
from: string;
|
|
26
|
+
to: string;
|
|
27
|
+
bump: BumpType;
|
|
28
|
+
via: VersionChangeKind;
|
|
29
|
+
}
|
|
30
|
+
| { kind: 'npm_wait'; repo: string; version: string }
|
|
22
31
|
| {
|
|
23
32
|
kind: 'dependency_update';
|
|
24
33
|
dependent: string;
|
|
@@ -27,21 +36,14 @@ export type PublishStep =
|
|
|
27
36
|
dep_type: 'prod' | 'peer';
|
|
28
37
|
creates_changeset: boolean;
|
|
29
38
|
}
|
|
30
|
-
| {kind: 'dev_dep_update'; repo: string; dependency: string; to: string}
|
|
31
|
-
| {kind: 'deploy'; repo: string; builds: boolean};
|
|
39
|
+
| { kind: 'dev_dep_update'; repo: string; dependency: string; to: string }
|
|
40
|
+
| { kind: 'deploy'; repo: string; builds: boolean };
|
|
32
41
|
|
|
33
42
|
export interface DerivePublishStepsOptions {
|
|
34
43
|
/** Include the deploy phase (the publisher only deploys with `--deploy`). */
|
|
35
44
|
deploy?: boolean;
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
const step_via = (change: VersionChange): PublishStepVia =>
|
|
39
|
-
change.needs_bump_escalation
|
|
40
|
-
? 'escalation'
|
|
41
|
-
: change.has_changesets
|
|
42
|
-
? 'changeset'
|
|
43
|
-
: 'auto_changeset';
|
|
44
|
-
|
|
45
47
|
/**
|
|
46
48
|
* Derives the ordered side-effects a wetrun would perform from a frozen plan.
|
|
47
49
|
*
|
|
@@ -51,11 +53,11 @@ const step_via = (change: VersionChange): PublishStepVia =>
|
|
|
51
53
|
*/
|
|
52
54
|
export const derive_publish_steps = (
|
|
53
55
|
plan: PublishingPlan,
|
|
54
|
-
options: DerivePublishStepsOptions = {}
|
|
56
|
+
options: DerivePublishStepsOptions = {}
|
|
55
57
|
): Array<PublishStep> => {
|
|
56
|
-
const {deploy = false} = options;
|
|
58
|
+
const { deploy = false } = options;
|
|
57
59
|
const changes: Map<string, VersionChange> = new Map(
|
|
58
|
-
plan.version_changes.map((vc) => [vc.package_name, vc])
|
|
60
|
+
plan.version_changes.map((vc) => [vc.package_name, vc])
|
|
59
61
|
);
|
|
60
62
|
|
|
61
63
|
const steps: Array<PublishStep> = [];
|
|
@@ -72,9 +74,9 @@ export const derive_publish_steps = (
|
|
|
72
74
|
from: change.from,
|
|
73
75
|
to: change.to,
|
|
74
76
|
bump: change.bump_type,
|
|
75
|
-
via:
|
|
77
|
+
via: version_change_kind(change)
|
|
76
78
|
});
|
|
77
|
-
steps.push({kind: 'npm_wait', repo, version: change.to});
|
|
79
|
+
steps.push({ kind: 'npm_wait', repo, version: change.to });
|
|
78
80
|
changed.add(repo);
|
|
79
81
|
|
|
80
82
|
// Prod/peer dependents are rewritten right after this publishes. A dependent that
|
|
@@ -92,7 +94,7 @@ export const derive_publish_steps = (
|
|
|
92
94
|
dependency: repo,
|
|
93
95
|
to: change.to,
|
|
94
96
|
dep_type: update.type === 'peerDependencies' ? 'peer' : 'prod',
|
|
95
|
-
creates_changeset: republishes
|
|
97
|
+
creates_changeset: republishes
|
|
96
98
|
});
|
|
97
99
|
changed.add(update.dependent_package);
|
|
98
100
|
}
|
|
@@ -109,7 +111,7 @@ export const derive_publish_steps = (
|
|
|
109
111
|
kind: 'dev_dep_update',
|
|
110
112
|
repo: update.dependent_package,
|
|
111
113
|
dependency: update.updated_dependency,
|
|
112
|
-
to: dep_change.to
|
|
114
|
+
to: dep_change.to
|
|
113
115
|
});
|
|
114
116
|
changed.add(update.dependent_package);
|
|
115
117
|
}
|
|
@@ -117,7 +119,7 @@ export const derive_publish_steps = (
|
|
|
117
119
|
// Phase 3: deploy every changed repo (only with --deploy). Each builds fresh.
|
|
118
120
|
if (deploy) {
|
|
119
121
|
for (const repo of changed) {
|
|
120
|
-
steps.push({kind: 'deploy', repo, builds: true});
|
|
122
|
+
steps.push({ kind: 'deploy', repo, builds: true });
|
|
121
123
|
}
|
|
122
124
|
}
|
|
123
125
|
|
|
@@ -14,33 +14,34 @@
|
|
|
14
14
|
* @module
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {z} from 'zod';
|
|
17
|
+
import { z } from 'zod';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Coarse triage classification for a failed package. Lets consumers branch on
|
|
21
21
|
* failure kind without parsing the message.
|
|
22
22
|
*/
|
|
23
23
|
export const PublishingErrorCode = z.enum([
|
|
24
|
+
// the publish step failed — its cause is in the message, not yet classified further
|
|
24
25
|
'publish',
|
|
26
|
+
// the published version never appeared on the npm registry within the wait
|
|
25
27
|
'network',
|
|
26
|
-
'auth',
|
|
27
|
-
'dependency',
|
|
28
|
-
'build',
|
|
29
28
|
// the real published version diverged from the frozen plan's prediction — an
|
|
30
29
|
// invariant violation, distinct from an ordinary publish failure (see fail-loud
|
|
31
30
|
// drift detection in `multi_repo_publisher.ts`)
|
|
32
31
|
'drift',
|
|
33
|
-
'
|
|
32
|
+
// the repo wasn't ready when re-checked right before its `gro publish` (origin moved,
|
|
33
|
+
// a tracked edit, a live session, …) — aborted before any npm side effect
|
|
34
|
+
'not_ready'
|
|
34
35
|
]);
|
|
35
36
|
export type PublishingErrorCode = z.infer<typeof PublishingErrorCode>;
|
|
36
37
|
|
|
37
38
|
/** Tallied outcome of a publishing run, derived from its events via `summarize_events`. */
|
|
38
39
|
export const PublishingRunSummary = z.strictObject({
|
|
39
|
-
total: z.number().meta({description: 'packages in the publishing order (the candidate set)'}),
|
|
40
|
-
published: z.number().meta({description: 'packages published (or, in a dry run, predicted)'}),
|
|
40
|
+
total: z.number().meta({ description: 'packages in the publishing order (the candidate set)' }),
|
|
41
|
+
published: z.number().meta({ description: 'packages published (or, in a dry run, predicted)' }),
|
|
41
42
|
failed: z.number(),
|
|
42
43
|
skipped: z.number(),
|
|
43
|
-
duration: z.number().meta({description: 'wall-clock duration in milliseconds'})
|
|
44
|
+
duration: z.number().meta({ description: 'wall-clock duration in milliseconds' })
|
|
44
45
|
});
|
|
45
46
|
export type PublishingRunSummary = z.infer<typeof PublishingRunSummary>;
|
|
46
47
|
|
|
@@ -51,13 +52,15 @@ export type PublishingRunSummary = z.infer<typeof PublishingRunSummary>;
|
|
|
51
52
|
export const PublishingEvent = z.discriminatedUnion('event', [
|
|
52
53
|
z.strictObject({
|
|
53
54
|
event: z.literal('run_started'),
|
|
54
|
-
wetrun: z
|
|
55
|
-
|
|
55
|
+
wetrun: z
|
|
56
|
+
.boolean()
|
|
57
|
+
.meta({ description: 'false means every package_completed is a prediction' }),
|
|
58
|
+
total: z.number()
|
|
56
59
|
}),
|
|
57
60
|
z.strictObject({
|
|
58
61
|
event: z.literal('package_skipped'),
|
|
59
62
|
name: z.string(),
|
|
60
|
-
reason: z.string()
|
|
63
|
+
reason: z.string()
|
|
61
64
|
}),
|
|
62
65
|
z.strictObject({
|
|
63
66
|
event: z.literal('package_completed'),
|
|
@@ -67,19 +70,19 @@ export const PublishingEvent = z.discriminatedUnion('event', [
|
|
|
67
70
|
// mirrors `BumpType` from `version_utils.ts`; inline so the event schema is self-contained
|
|
68
71
|
bump_type: z.enum(['major', 'minor', 'patch']),
|
|
69
72
|
breaking: z.boolean(),
|
|
70
|
-
commit: z.string().meta({description: "'simulated' in a dry run, otherwise the commit hash"}),
|
|
71
|
-
tag: z.string()
|
|
73
|
+
commit: z.string().meta({ description: "'simulated' in a dry run, otherwise the commit hash" }),
|
|
74
|
+
tag: z.string()
|
|
72
75
|
}),
|
|
73
76
|
z.strictObject({
|
|
74
77
|
event: z.literal('npm_waited'),
|
|
75
78
|
name: z.string(),
|
|
76
|
-
version: z.string().meta({description: 'the version waited on after publishing'})
|
|
79
|
+
version: z.string().meta({ description: 'the version waited on after publishing' })
|
|
77
80
|
}),
|
|
78
81
|
z.strictObject({
|
|
79
82
|
event: z.literal('package_failed'),
|
|
80
83
|
name: z.string(),
|
|
81
84
|
error: z.string(),
|
|
82
|
-
code: PublishingErrorCode
|
|
85
|
+
code: PublishingErrorCode
|
|
83
86
|
}),
|
|
84
87
|
z.strictObject({
|
|
85
88
|
event: z.literal('dependency_updated'),
|
|
@@ -91,25 +94,25 @@ export const PublishingEvent = z.discriminatedUnion('event', [
|
|
|
91
94
|
dep_type: z.enum(['prod', 'peer', 'dev']),
|
|
92
95
|
// true when the update creates an auto-changeset (a publishable dependent republishes);
|
|
93
96
|
// false for dev-dep updates and update-only leaves (private dependents never publish).
|
|
94
|
-
creates_changeset: z.boolean()
|
|
97
|
+
creates_changeset: z.boolean()
|
|
95
98
|
}),
|
|
96
99
|
z.strictObject({
|
|
97
100
|
event: z.literal('deploy_started'),
|
|
98
|
-
name: z.string()
|
|
101
|
+
name: z.string()
|
|
99
102
|
}),
|
|
100
103
|
z.strictObject({
|
|
101
104
|
event: z.literal('deploy_completed'),
|
|
102
|
-
name: z.string()
|
|
105
|
+
name: z.string()
|
|
103
106
|
}),
|
|
104
107
|
z.strictObject({
|
|
105
108
|
event: z.literal('deploy_failed'),
|
|
106
109
|
name: z.string(),
|
|
107
|
-
error: z.string()
|
|
110
|
+
error: z.string()
|
|
108
111
|
}),
|
|
109
112
|
z.strictObject({
|
|
110
113
|
event: z.literal('run_finished'),
|
|
111
|
-
summary: PublishingRunSummary
|
|
112
|
-
})
|
|
114
|
+
summary: PublishingRunSummary
|
|
115
|
+
})
|
|
113
116
|
]);
|
|
114
117
|
export type PublishingEvent = z.infer<typeof PublishingEvent>;
|
|
115
118
|
|
|
@@ -123,7 +126,7 @@ export type PublishingEvent = z.infer<typeof PublishingEvent>;
|
|
|
123
126
|
*/
|
|
124
127
|
export const summarize_events = (
|
|
125
128
|
events: Array<PublishingEvent>,
|
|
126
|
-
duration: number
|
|
129
|
+
duration: number
|
|
127
130
|
): PublishingRunSummary => {
|
|
128
131
|
let total = 0;
|
|
129
132
|
let published = 0;
|
|
@@ -147,5 +150,5 @@ export const summarize_events = (
|
|
|
147
150
|
break;
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
|
-
return {total, published, failed, skipped, duration};
|
|
153
|
+
return { total, published, failed, skipped, duration };
|
|
151
154
|
};
|