@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
package/src/lib/repo.svelte.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import {create_context} from '@fuzdev/fuz_ui/context_helpers.js';
|
|
2
2
|
import type {LibraryJson, SourceJson} from '@fuzdev/fuz_util/library_json.js';
|
|
3
|
+
import type {PkgJson} from '@fuzdev/fuz_util/pkg_json.js';
|
|
3
4
|
import type {PackageJson} from '@fuzdev/fuz_util/package_json.js';
|
|
4
5
|
import type {Url} from '@fuzdev/fuz_util/url.js';
|
|
5
6
|
import {Library} from '@fuzdev/fuz_ui/library.svelte.js';
|
|
6
|
-
import type {Module} from '@fuzdev/fuz_ui/module.svelte.js';
|
|
7
7
|
|
|
8
8
|
import {GithubCheckRunsItem, type GithubPullRequest} from './github.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Serialized repo data as stored in `repos.ts` (JSON).
|
|
12
|
+
*
|
|
13
|
+
* `package_json` is the repo's full `package.json`, carried alongside
|
|
14
|
+
* `library_json` because gitops reads `dependencies`/`devDependencies` for the
|
|
15
|
+
* publishing cascade — fields the curated `LibraryJson.pkg_json` omits.
|
|
12
16
|
*/
|
|
13
17
|
export interface RepoJson {
|
|
14
18
|
library_json: LibraryJson;
|
|
19
|
+
package_json: PackageJson;
|
|
15
20
|
check_runs: GithubCheckRunsItem | null;
|
|
16
21
|
pull_requests: Array<GithubPullRequest> | null;
|
|
17
22
|
}
|
|
@@ -24,6 +29,8 @@ export interface RepoJson {
|
|
|
24
29
|
*/
|
|
25
30
|
export class Repo {
|
|
26
31
|
readonly library: Library;
|
|
32
|
+
/** The repo's full `package.json` (with `dependencies`/`devDependencies`). */
|
|
33
|
+
readonly package_json: PackageJson;
|
|
27
34
|
check_runs: GithubCheckRunsItem | null;
|
|
28
35
|
pull_requests: Array<GithubPullRequest> | null;
|
|
29
36
|
|
|
@@ -37,9 +44,6 @@ export class Repo {
|
|
|
37
44
|
get repo_url(): Url {
|
|
38
45
|
return this.library.repo_url;
|
|
39
46
|
}
|
|
40
|
-
get owner_name(): string | null {
|
|
41
|
-
return this.library.owner_name;
|
|
42
|
-
}
|
|
43
47
|
get homepage_url(): Url | null {
|
|
44
48
|
return this.library.homepage_url;
|
|
45
49
|
}
|
|
@@ -49,30 +53,23 @@ export class Repo {
|
|
|
49
53
|
get logo_alt(): string {
|
|
50
54
|
return this.library.logo_alt;
|
|
51
55
|
}
|
|
52
|
-
get published(): boolean {
|
|
53
|
-
return this.library.published;
|
|
54
|
-
}
|
|
55
56
|
get npm_url(): Url | null {
|
|
56
57
|
return this.library.npm_url;
|
|
57
58
|
}
|
|
58
59
|
get changelog_url(): Url | null {
|
|
59
60
|
return this.library.changelog_url;
|
|
60
61
|
}
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
/** Curated package identity, delegating to `library`. Distinct from the full `package_json`. */
|
|
63
|
+
get pkg_json(): PkgJson {
|
|
64
|
+
return this.library.pkg_json;
|
|
63
65
|
}
|
|
64
66
|
get source_json(): SourceJson {
|
|
65
67
|
return this.library.source_json;
|
|
66
68
|
}
|
|
67
|
-
get modules(): Array<Module> {
|
|
68
|
-
return this.library.modules;
|
|
69
|
-
}
|
|
70
|
-
get org_url(): string | null {
|
|
71
|
-
return this.library.org_url;
|
|
72
|
-
}
|
|
73
69
|
|
|
74
70
|
constructor(repo_json: RepoJson) {
|
|
75
71
|
this.library = new Library(repo_json.library_json);
|
|
72
|
+
this.package_json = repo_json.package_json;
|
|
76
73
|
this.check_runs = repo_json.check_runs;
|
|
77
74
|
this.pull_requests = repo_json.pull_requests;
|
|
78
75
|
}
|
package/src/lib/version_utils.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// TODO: candidate for extraction to `@fuzdev/fuz_util`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/** A semver bump kind: `major`, `minor`, or `patch`. */
|
|
4
|
+
export type BumpType = 'major' | 'minor' | 'patch';
|
|
4
5
|
|
|
5
6
|
export const is_wildcard = (version: string): boolean => {
|
|
6
7
|
return version === '*';
|
|
@@ -100,10 +101,7 @@ export const get_update_prefix = (
|
|
|
100
101
|
* Pre-1.0: minor bumps are breaking
|
|
101
102
|
* 1.0+: major bumps are breaking
|
|
102
103
|
*/
|
|
103
|
-
export const is_breaking_change = (
|
|
104
|
-
old_version: string,
|
|
105
|
-
bump_type: 'major' | 'minor' | 'patch',
|
|
106
|
-
): boolean => {
|
|
104
|
+
export const is_breaking_change = (old_version: string, bump_type: BumpType): boolean => {
|
|
107
105
|
const [major] = old_version.split('.').map(Number);
|
|
108
106
|
const is_pre_1_0 = major === 0;
|
|
109
107
|
|
|
@@ -116,10 +114,33 @@ export const is_breaking_change = (
|
|
|
116
114
|
}
|
|
117
115
|
};
|
|
118
116
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
/**
|
|
118
|
+
* The bump a package must take when one of its prod/peer dependencies updates.
|
|
119
|
+
* Pre-1.0: `minor` for a breaking dependency, otherwise `patch`.
|
|
120
|
+
* 1.0+: `major` for a breaking dependency, otherwise `patch`.
|
|
121
|
+
*
|
|
122
|
+
* Single source of truth for the dependency-driven bump rule, shared by the plan
|
|
123
|
+
* (`get_required_bump_for_dependencies`) and the auto-changeset generator
|
|
124
|
+
* (`calculate_required_bump`) so the two never drift.
|
|
125
|
+
*
|
|
126
|
+
* @param current_version - the package's current version, used to detect the pre-1.0 regime
|
|
127
|
+
* @param has_breaking_deps - whether any updated dependency is a breaking change
|
|
128
|
+
*/
|
|
129
|
+
export const required_bump_for_dependency_update = (
|
|
130
|
+
current_version: string,
|
|
131
|
+
has_breaking_deps: boolean,
|
|
132
|
+
): BumpType => {
|
|
133
|
+
const [major] = current_version.split('.').map(Number);
|
|
134
|
+
const is_pre_1_0 = major === 0;
|
|
135
|
+
if (has_breaking_deps) {
|
|
136
|
+
// Breaking changes propagate: pre-1.0 uses minor, 1.0+ uses major
|
|
137
|
+
return is_pre_1_0 ? 'minor' : 'major';
|
|
138
|
+
}
|
|
139
|
+
// Non-breaking dependency updates only need a patch
|
|
140
|
+
return 'patch';
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const detect_bump_type = (old_version: string, new_version: string): BumpType => {
|
|
123
144
|
const old_parts = old_version.split('.').map(Number);
|
|
124
145
|
const new_parts = new_version.split('.').map(Number);
|
|
125
146
|
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { Logger } from '@fuzdev/fuz_util/log.js';
|
|
2
|
-
import type { LocalRepo } from './local_repo.js';
|
|
3
|
-
import type { GitopsOperations } from './operations.js';
|
|
4
|
-
/**
|
|
5
|
-
* Installs npm dependencies with cache healing on ETARGET errors.
|
|
6
|
-
*
|
|
7
|
-
* **Strategy:**
|
|
8
|
-
* 1. First attempt: regular `npm install`
|
|
9
|
-
* 2. On ETARGET error (stale cache): `npm cache clean --force` then retry
|
|
10
|
-
* 3. On other errors: fail immediately
|
|
11
|
-
*
|
|
12
|
-
* **Why ETARGET errors occur:**
|
|
13
|
-
* After publishing a package and waiting for NPM registry propagation,
|
|
14
|
-
* npm's local cache may still have stale "404" metadata. This healing
|
|
15
|
-
* strategy clears the cache to force fresh metadata fetch.
|
|
16
|
-
*
|
|
17
|
-
* @param repo - the repository to install dependencies for
|
|
18
|
-
* @param ops - gitops operations (for dependency injection)
|
|
19
|
-
* @param log - optional logger
|
|
20
|
-
* @throws Error if install fails (with details about cache healing attempts)
|
|
21
|
-
*/
|
|
22
|
-
export declare const install_with_cache_healing: (repo: LocalRepo, ops: GitopsOperations, log?: Logger) => Promise<void>;
|
|
23
|
-
//# sourceMappingURL=npm_install_helpers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"npm_install_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/npm_install_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AAmBtD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,SAAS,EACf,KAAK,gBAAgB,EACrB,MAAM,MAAM,KACV,OAAO,CAAC,IAAI,CAuCd,CAAC"}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { styleText as st } from 'node:util';
|
|
2
|
-
/**
|
|
3
|
-
* Checks if an npm install error is caused by stale cache (ETARGET).
|
|
4
|
-
* Detects various error message formats:
|
|
5
|
-
* - "code ETARGET"
|
|
6
|
-
* - "ETARGET"
|
|
7
|
-
* - "notarget"
|
|
8
|
-
* - "No matching version found"
|
|
9
|
-
*/
|
|
10
|
-
const is_etarget_error = (message, stderr) => {
|
|
11
|
-
const combined = `${message} ${stderr}`.toLowerCase();
|
|
12
|
-
return (combined.includes('etarget') ||
|
|
13
|
-
combined.includes('notarget') ||
|
|
14
|
-
combined.includes('no matching version found'));
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Installs npm dependencies with cache healing on ETARGET errors.
|
|
18
|
-
*
|
|
19
|
-
* **Strategy:**
|
|
20
|
-
* 1. First attempt: regular `npm install`
|
|
21
|
-
* 2. On ETARGET error (stale cache): `npm cache clean --force` then retry
|
|
22
|
-
* 3. On other errors: fail immediately
|
|
23
|
-
*
|
|
24
|
-
* **Why ETARGET errors occur:**
|
|
25
|
-
* After publishing a package and waiting for NPM registry propagation,
|
|
26
|
-
* npm's local cache may still have stale "404" metadata. This healing
|
|
27
|
-
* strategy clears the cache to force fresh metadata fetch.
|
|
28
|
-
*
|
|
29
|
-
* @param repo - the repository to install dependencies for
|
|
30
|
-
* @param ops - gitops operations (for dependency injection)
|
|
31
|
-
* @param log - optional logger
|
|
32
|
-
* @throws Error if install fails (with details about cache healing attempts)
|
|
33
|
-
*/
|
|
34
|
-
export const install_with_cache_healing = async (repo, ops, log) => {
|
|
35
|
-
// First attempt
|
|
36
|
-
const install_result = await ops.npm.install({ cwd: repo.repo_dir });
|
|
37
|
-
if (install_result.ok) {
|
|
38
|
-
return; // Success
|
|
39
|
-
}
|
|
40
|
-
// Check if error is ETARGET (package not found due to stale cache)
|
|
41
|
-
const stderr = install_result.stderr || '';
|
|
42
|
-
const message = install_result.message || '';
|
|
43
|
-
if (!is_etarget_error(message, stderr)) {
|
|
44
|
-
// Different error - fail immediately without cache healing
|
|
45
|
-
throw new Error(`Failed to install dependencies in ${repo.library.name}: ${install_result.message}${stderr ? `\n${stderr}` : ''}`);
|
|
46
|
-
}
|
|
47
|
-
// ETARGET error - try cache healing
|
|
48
|
-
log?.warn(st('yellow', ` ⚠️ ETARGET error detected - cleaning npm cache...`));
|
|
49
|
-
const cache_result = await ops.npm.cache_clean();
|
|
50
|
-
if (!cache_result.ok) {
|
|
51
|
-
throw new Error(`Failed to clean npm cache: ${cache_result.message}`);
|
|
52
|
-
}
|
|
53
|
-
log?.info(' ✓ Cache cleaned, retrying install...');
|
|
54
|
-
// Retry install after cache clean
|
|
55
|
-
const retry_result = await ops.npm.install({ cwd: repo.repo_dir });
|
|
56
|
-
if (!retry_result.ok) {
|
|
57
|
-
throw new Error(`Failed to install dependencies after cache clean in ${repo.library.name}: ${retry_result.message}${retry_result.stderr ? `\n${retry_result.stderr}` : ''}`);
|
|
58
|
-
}
|
|
59
|
-
log?.info(st('green', ` ✓ Dependencies installed successfully after cache heal`));
|
|
60
|
-
};
|
package/dist/semver.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Semantic Versioning 2.0.0 utilities
|
|
3
|
-
* @see https://semver.org/
|
|
4
|
-
*
|
|
5
|
-
* @module
|
|
6
|
-
*/
|
|
7
|
-
export type BumpType = 'major' | 'minor' | 'patch';
|
|
8
|
-
export interface Semver {
|
|
9
|
-
major: number;
|
|
10
|
-
minor: number;
|
|
11
|
-
patch: number;
|
|
12
|
-
prerelease?: string;
|
|
13
|
-
build?: string;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Compares two semver versions according to SemVer 2.0.0 spec.
|
|
17
|
-
* Returns -1 if a < b, 0 if a === b, 1 if a > b.
|
|
18
|
-
* Build metadata is ignored in precedence comparison.
|
|
19
|
-
*/
|
|
20
|
-
export declare const semver_compare_versions: (a: string, b: string) => number;
|
|
21
|
-
/**
|
|
22
|
-
* Bumps a version according to the specified type.
|
|
23
|
-
* Resets lower version numbers per SemVer spec.
|
|
24
|
-
*/
|
|
25
|
-
export declare const semver_bump_version: (version: string, type: BumpType) => string;
|
|
26
|
-
//# sourceMappingURL=semver.d.ts.map
|
package/dist/semver.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"semver.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/semver.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,MAAM;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAiGD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAqB9D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,EAAE,MAAM,QAAQ,KAAG,MAuBrE,CAAC"}
|
package/dist/semver.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
// TODO: candidate for extraction to `@fuzdev/fuz_util`
|
|
2
|
-
/**
|
|
3
|
-
* SemVer 2.0.0 validation regex.
|
|
4
|
-
* @see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
|
|
5
|
-
*/
|
|
6
|
-
const SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
7
|
-
/**
|
|
8
|
-
* Parses a semver version string.
|
|
9
|
-
* Accepts optional 'v' prefix for convenience.
|
|
10
|
-
*/
|
|
11
|
-
const semver_parse = (version) => {
|
|
12
|
-
// Remove leading 'v' if present (common in git tags)
|
|
13
|
-
const clean = version.replace(/^v/, '');
|
|
14
|
-
// Validate the cleaned version
|
|
15
|
-
if (!SEMVER_REGEX.test(clean)) {
|
|
16
|
-
throw new Error(`Invalid semver: ${version}`);
|
|
17
|
-
}
|
|
18
|
-
const match = SEMVER_REGEX.exec(clean);
|
|
19
|
-
return {
|
|
20
|
-
major: parseInt(match[1], 10),
|
|
21
|
-
minor: parseInt(match[2], 10),
|
|
22
|
-
patch: parseInt(match[3], 10),
|
|
23
|
-
prerelease: match[4],
|
|
24
|
-
build: match[5],
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
const semver_to_string = (semver) => {
|
|
28
|
-
let version = `${semver.major}.${semver.minor}.${semver.patch}`;
|
|
29
|
-
if (semver.prerelease) {
|
|
30
|
-
version += `-${semver.prerelease}`;
|
|
31
|
-
}
|
|
32
|
-
if (semver.build) {
|
|
33
|
-
version += `+${semver.build}`;
|
|
34
|
-
}
|
|
35
|
-
return version;
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Compares two prerelease versions according to SemVer 2.0.0 spec.
|
|
39
|
-
* Returns -1 if a < b, 0 if a === b, 1 if a > b.
|
|
40
|
-
*/
|
|
41
|
-
const semver_compare_prerelease = (a, b) => {
|
|
42
|
-
// Handle missing prereleases
|
|
43
|
-
if (!a && !b)
|
|
44
|
-
return 0;
|
|
45
|
-
if (!a)
|
|
46
|
-
return 1; // normal version > prerelease
|
|
47
|
-
if (!b)
|
|
48
|
-
return -1; // prerelease < normal version
|
|
49
|
-
// Split into identifiers
|
|
50
|
-
const a_parts = a.split('.');
|
|
51
|
-
const b_parts = b.split('.');
|
|
52
|
-
// Compare each identifier
|
|
53
|
-
const min_length = Math.min(a_parts.length, b_parts.length);
|
|
54
|
-
for (let i = 0; i < min_length; i++) {
|
|
55
|
-
const a_part = a_parts[i];
|
|
56
|
-
const b_part = b_parts[i];
|
|
57
|
-
// Check if numeric
|
|
58
|
-
const a_is_numeric = /^\d+$/.test(a_part);
|
|
59
|
-
const b_is_numeric = /^\d+$/.test(b_part);
|
|
60
|
-
if (a_is_numeric && b_is_numeric) {
|
|
61
|
-
// Both numeric - compare numerically
|
|
62
|
-
const a_num = parseInt(a_part, 10);
|
|
63
|
-
const b_num = parseInt(b_part, 10);
|
|
64
|
-
if (a_num !== b_num) {
|
|
65
|
-
return a_num < b_num ? -1 : 1;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
else if (a_is_numeric && !b_is_numeric) {
|
|
69
|
-
// Numeric identifiers always have lower precedence
|
|
70
|
-
return -1;
|
|
71
|
-
}
|
|
72
|
-
else if (!a_is_numeric && b_is_numeric) {
|
|
73
|
-
// Numeric identifiers always have lower precedence
|
|
74
|
-
return 1;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
// Both alphanumeric - compare lexically
|
|
78
|
-
const cmp = a_part.localeCompare(b_part);
|
|
79
|
-
if (cmp !== 0) {
|
|
80
|
-
return cmp < 0 ? -1 : 1;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
// All identifiers equal - larger set has higher precedence
|
|
85
|
-
if (a_parts.length !== b_parts.length) {
|
|
86
|
-
return a_parts.length < b_parts.length ? -1 : 1;
|
|
87
|
-
}
|
|
88
|
-
return 0;
|
|
89
|
-
};
|
|
90
|
-
/**
|
|
91
|
-
* Compares two semver versions according to SemVer 2.0.0 spec.
|
|
92
|
-
* Returns -1 if a < b, 0 if a === b, 1 if a > b.
|
|
93
|
-
* Build metadata is ignored in precedence comparison.
|
|
94
|
-
*/
|
|
95
|
-
export const semver_compare_versions = (a, b) => {
|
|
96
|
-
const v1 = semver_parse(a);
|
|
97
|
-
const v2 = semver_parse(b);
|
|
98
|
-
// Compare major
|
|
99
|
-
if (v1.major !== v2.major) {
|
|
100
|
-
return v1.major < v2.major ? -1 : 1;
|
|
101
|
-
}
|
|
102
|
-
// Compare minor
|
|
103
|
-
if (v1.minor !== v2.minor) {
|
|
104
|
-
return v1.minor < v2.minor ? -1 : 1;
|
|
105
|
-
}
|
|
106
|
-
// Compare patch
|
|
107
|
-
if (v1.patch !== v2.patch) {
|
|
108
|
-
return v1.patch < v2.patch ? -1 : 1;
|
|
109
|
-
}
|
|
110
|
-
// Compare prerelease (build metadata is ignored)
|
|
111
|
-
return semver_compare_prerelease(v1.prerelease, v2.prerelease);
|
|
112
|
-
};
|
|
113
|
-
/**
|
|
114
|
-
* Bumps a version according to the specified type.
|
|
115
|
-
* Resets lower version numbers per SemVer spec.
|
|
116
|
-
*/
|
|
117
|
-
export const semver_bump_version = (version, type) => {
|
|
118
|
-
const semver = semver_parse(version);
|
|
119
|
-
switch (type) {
|
|
120
|
-
case 'major':
|
|
121
|
-
semver.major++;
|
|
122
|
-
semver.minor = 0;
|
|
123
|
-
semver.patch = 0;
|
|
124
|
-
break;
|
|
125
|
-
case 'minor':
|
|
126
|
-
semver.minor++;
|
|
127
|
-
semver.patch = 0;
|
|
128
|
-
break;
|
|
129
|
-
case 'patch':
|
|
130
|
-
semver.patch++;
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
// Remove prerelease and build when bumping
|
|
134
|
-
semver.prerelease = undefined;
|
|
135
|
-
semver.build = undefined;
|
|
136
|
-
return semver_to_string(semver);
|
|
137
|
-
};
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON-serializable types for command output formats.
|
|
3
|
-
*
|
|
4
|
-
* Gitops commands support `--format json` and `--format markdown` output modes
|
|
5
|
-
* in addition to styled terminal output. These types define the JSON schema for:
|
|
6
|
-
* - Dependency graph structures (`SerializedGraph`)
|
|
7
|
-
* - Publishing plan predictions (`SerializedPublishingPlan`)
|
|
8
|
-
*
|
|
9
|
-
* Used by `gitops_analyze`, `gitops_plan`, and `gitops_publish` (dry run) when
|
|
10
|
-
* `--format json` or `--outfile` is specified.
|
|
11
|
-
*
|
|
12
|
-
* @module
|
|
13
|
-
*/
|
|
14
|
-
import type { DependencyGraph } from './dependency_graph.js';
|
|
15
|
-
export interface SerializedNode {
|
|
16
|
-
name: string;
|
|
17
|
-
version: string;
|
|
18
|
-
dependencies: Array<{
|
|
19
|
-
name: string;
|
|
20
|
-
type: string;
|
|
21
|
-
version: string;
|
|
22
|
-
}>;
|
|
23
|
-
dependents: Array<string>;
|
|
24
|
-
publishable: boolean;
|
|
25
|
-
}
|
|
26
|
-
export interface SerializedGraph {
|
|
27
|
-
nodes: Array<SerializedNode>;
|
|
28
|
-
edges: Array<[string, string]>;
|
|
29
|
-
}
|
|
30
|
-
export interface SerializedPublishingPlan {
|
|
31
|
-
publishing_order: Array<string>;
|
|
32
|
-
version_changes: Array<{
|
|
33
|
-
package_name: string;
|
|
34
|
-
from: string;
|
|
35
|
-
to: string;
|
|
36
|
-
bump_type: string;
|
|
37
|
-
breaking: boolean;
|
|
38
|
-
has_changesets: boolean;
|
|
39
|
-
will_generate_changeset?: boolean;
|
|
40
|
-
needs_bump_escalation?: boolean;
|
|
41
|
-
existing_bump?: string;
|
|
42
|
-
required_bump?: string;
|
|
43
|
-
}>;
|
|
44
|
-
dependency_updates: Array<{
|
|
45
|
-
dependent_package: string;
|
|
46
|
-
updated_dependency: string;
|
|
47
|
-
new_version: string;
|
|
48
|
-
type: string;
|
|
49
|
-
causes_republish: boolean;
|
|
50
|
-
}>;
|
|
51
|
-
breaking_cascades: Record<string, Array<string>>;
|
|
52
|
-
warnings: Array<string>;
|
|
53
|
-
errors: Array<string>;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Serializes a dependency graph to a JSON-safe format.
|
|
57
|
-
*/
|
|
58
|
-
export declare const serialize_graph: (graph: DependencyGraph) => SerializedGraph;
|
|
59
|
-
//# sourceMappingURL=serialization_types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serialization_types.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/serialization_types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,KAAK,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACxC,gBAAgB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,KAAK,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,OAAO,CAAC;QAClB,cAAc,EAAE,OAAO,CAAC;QACxB,uBAAuB,CAAC,EAAE,OAAO,CAAC;QAClC,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,aAAa,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC,CAAC;IACH,kBAAkB,EAAE,KAAK,CAAC;QACzB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,gBAAgB,EAAE,OAAO,CAAC;KAC1B,CAAC,CAAC;IACH,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACjD,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,eAAe,KAAG,eA6BxD,CAAC"}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON-serializable types for command output formats.
|
|
3
|
-
*
|
|
4
|
-
* Gitops commands support `--format json` and `--format markdown` output modes
|
|
5
|
-
* in addition to styled terminal output. These types define the JSON schema for:
|
|
6
|
-
* - Dependency graph structures (`SerializedGraph`)
|
|
7
|
-
* - Publishing plan predictions (`SerializedPublishingPlan`)
|
|
8
|
-
*
|
|
9
|
-
* Used by `gitops_analyze`, `gitops_plan`, and `gitops_publish` (dry run) when
|
|
10
|
-
* `--format json` or `--outfile` is specified.
|
|
11
|
-
*
|
|
12
|
-
* @module
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Serializes a dependency graph to a JSON-safe format.
|
|
16
|
-
*/
|
|
17
|
-
export const serialize_graph = (graph) => {
|
|
18
|
-
const nodes = [];
|
|
19
|
-
const edges = [];
|
|
20
|
-
// Serialize nodes
|
|
21
|
-
for (const node of graph.nodes.values()) {
|
|
22
|
-
const dependencies = Array.from(node.dependencies, ([dep_name, spec]) => ({
|
|
23
|
-
name: dep_name,
|
|
24
|
-
type: spec.type,
|
|
25
|
-
version: spec.version,
|
|
26
|
-
}));
|
|
27
|
-
nodes.push({
|
|
28
|
-
name: node.name,
|
|
29
|
-
version: node.version,
|
|
30
|
-
dependencies,
|
|
31
|
-
dependents: Array.from(node.dependents),
|
|
32
|
-
publishable: node.publishable,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
// Serialize edges
|
|
36
|
-
for (const [from, tos] of graph.edges) {
|
|
37
|
-
for (const to of tos) {
|
|
38
|
-
edges.push([from, to]);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return { nodes, edges };
|
|
42
|
-
};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type {Logger} from '@fuzdev/fuz_util/log.js';
|
|
2
|
-
import {styleText as st} from 'node:util';
|
|
3
|
-
|
|
4
|
-
import type {LocalRepo} from './local_repo.js';
|
|
5
|
-
import type {GitopsOperations} from './operations.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Checks if an npm install error is caused by stale cache (ETARGET).
|
|
9
|
-
* Detects various error message formats:
|
|
10
|
-
* - "code ETARGET"
|
|
11
|
-
* - "ETARGET"
|
|
12
|
-
* - "notarget"
|
|
13
|
-
* - "No matching version found"
|
|
14
|
-
*/
|
|
15
|
-
const is_etarget_error = (message: string, stderr: string): boolean => {
|
|
16
|
-
const combined = `${message} ${stderr}`.toLowerCase();
|
|
17
|
-
return (
|
|
18
|
-
combined.includes('etarget') ||
|
|
19
|
-
combined.includes('notarget') ||
|
|
20
|
-
combined.includes('no matching version found')
|
|
21
|
-
);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Installs npm dependencies with cache healing on ETARGET errors.
|
|
26
|
-
*
|
|
27
|
-
* **Strategy:**
|
|
28
|
-
* 1. First attempt: regular `npm install`
|
|
29
|
-
* 2. On ETARGET error (stale cache): `npm cache clean --force` then retry
|
|
30
|
-
* 3. On other errors: fail immediately
|
|
31
|
-
*
|
|
32
|
-
* **Why ETARGET errors occur:**
|
|
33
|
-
* After publishing a package and waiting for NPM registry propagation,
|
|
34
|
-
* npm's local cache may still have stale "404" metadata. This healing
|
|
35
|
-
* strategy clears the cache to force fresh metadata fetch.
|
|
36
|
-
*
|
|
37
|
-
* @param repo - the repository to install dependencies for
|
|
38
|
-
* @param ops - gitops operations (for dependency injection)
|
|
39
|
-
* @param log - optional logger
|
|
40
|
-
* @throws Error if install fails (with details about cache healing attempts)
|
|
41
|
-
*/
|
|
42
|
-
export const install_with_cache_healing = async (
|
|
43
|
-
repo: LocalRepo,
|
|
44
|
-
ops: GitopsOperations,
|
|
45
|
-
log?: Logger,
|
|
46
|
-
): Promise<void> => {
|
|
47
|
-
// First attempt
|
|
48
|
-
const install_result = await ops.npm.install({cwd: repo.repo_dir});
|
|
49
|
-
|
|
50
|
-
if (install_result.ok) {
|
|
51
|
-
return; // Success
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Check if error is ETARGET (package not found due to stale cache)
|
|
55
|
-
const stderr = install_result.stderr || '';
|
|
56
|
-
const message = install_result.message || '';
|
|
57
|
-
|
|
58
|
-
if (!is_etarget_error(message, stderr)) {
|
|
59
|
-
// Different error - fail immediately without cache healing
|
|
60
|
-
throw new Error(
|
|
61
|
-
`Failed to install dependencies in ${repo.library.name}: ${install_result.message}${stderr ? `\n${stderr}` : ''}`,
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ETARGET error - try cache healing
|
|
66
|
-
log?.warn(st('yellow', ` ⚠️ ETARGET error detected - cleaning npm cache...`));
|
|
67
|
-
|
|
68
|
-
const cache_result = await ops.npm.cache_clean();
|
|
69
|
-
if (!cache_result.ok) {
|
|
70
|
-
throw new Error(`Failed to clean npm cache: ${cache_result.message}`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
log?.info(' ✓ Cache cleaned, retrying install...');
|
|
74
|
-
|
|
75
|
-
// Retry install after cache clean
|
|
76
|
-
const retry_result = await ops.npm.install({cwd: repo.repo_dir});
|
|
77
|
-
|
|
78
|
-
if (!retry_result.ok) {
|
|
79
|
-
throw new Error(
|
|
80
|
-
`Failed to install dependencies after cache clean in ${repo.library.name}: ${retry_result.message}${retry_result.stderr ? `\n${retry_result.stderr}` : ''}`,
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
log?.info(st('green', ` ✓ Dependencies installed successfully after cache heal`));
|
|
85
|
-
};
|