@fuzdev/fuz_gitops 0.57.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/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/ModulesDetail.svelte +180 -0
- package/dist/ModulesDetail.svelte.d.ts +10 -0
- package/dist/ModulesDetail.svelte.d.ts.map +1 -0
- package/dist/ModulesNav.svelte +43 -0
- package/dist/ModulesNav.svelte.d.ts +11 -0
- package/dist/ModulesNav.svelte.d.ts.map +1 -0
- package/dist/ModulesPage.svelte +50 -0
- package/dist/ModulesPage.svelte.d.ts +9 -0
- package/dist/ModulesPage.svelte.d.ts.map +1 -0
- package/dist/PageFooter.svelte +15 -0
- package/dist/PageFooter.svelte.d.ts +19 -0
- package/dist/PageFooter.svelte.d.ts.map +1 -0
- package/dist/PageHeader.svelte +35 -0
- package/dist/PageHeader.svelte.d.ts +19 -0
- package/dist/PageHeader.svelte.d.ts.map +1 -0
- package/dist/PullRequestsDetail.svelte +53 -0
- package/dist/PullRequestsDetail.svelte.d.ts +10 -0
- package/dist/PullRequestsDetail.svelte.d.ts.map +1 -0
- package/dist/PullRequestsPage.svelte +47 -0
- package/dist/PullRequestsPage.svelte.d.ts +11 -0
- package/dist/PullRequestsPage.svelte.d.ts.map +1 -0
- package/dist/ReposTable.svelte +189 -0
- package/dist/ReposTable.svelte.d.ts +9 -0
- package/dist/ReposTable.svelte.d.ts.map +1 -0
- package/dist/ReposTree.svelte +88 -0
- package/dist/ReposTree.svelte.d.ts +11 -0
- package/dist/ReposTree.svelte.d.ts.map +1 -0
- package/dist/ReposTreeNav.svelte +55 -0
- package/dist/ReposTreeNav.svelte.d.ts +11 -0
- package/dist/ReposTreeNav.svelte.d.ts.map +1 -0
- package/dist/TablePage.svelte +46 -0
- package/dist/TablePage.svelte.d.ts +9 -0
- package/dist/TablePage.svelte.d.ts.map +1 -0
- package/dist/TreeItemPage.svelte +75 -0
- package/dist/TreeItemPage.svelte.d.ts +10 -0
- package/dist/TreeItemPage.svelte.d.ts.map +1 -0
- package/dist/TreePage.svelte +64 -0
- package/dist/TreePage.svelte.d.ts +9 -0
- package/dist/TreePage.svelte.d.ts.map +1 -0
- package/dist/changeset_generator.d.ts +38 -0
- package/dist/changeset_generator.d.ts.map +1 -0
- package/dist/changeset_generator.js +110 -0
- package/dist/changeset_reader.d.ts +75 -0
- package/dist/changeset_reader.d.ts.map +1 -0
- package/dist/changeset_reader.js +167 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +8 -0
- package/dist/dependency_graph.d.ts +120 -0
- package/dist/dependency_graph.d.ts.map +1 -0
- package/dist/dependency_graph.js +341 -0
- package/dist/dependency_updater.d.ts +46 -0
- package/dist/dependency_updater.d.ts.map +1 -0
- package/dist/dependency_updater.js +213 -0
- package/dist/fetch_repo_data.d.ts +19 -0
- package/dist/fetch_repo_data.d.ts.map +1 -0
- package/dist/fetch_repo_data.js +49 -0
- package/dist/fs_fetch_value_cache.d.ts +24 -0
- package/dist/fs_fetch_value_cache.d.ts.map +1 -0
- package/dist/fs_fetch_value_cache.js +61 -0
- package/dist/git_operations.d.ts +54 -0
- package/dist/git_operations.d.ts.map +1 -0
- package/dist/git_operations.js +144 -0
- package/dist/github.d.ts +91 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js +94 -0
- package/dist/github_helpers.d.ts +10 -0
- package/dist/github_helpers.d.ts.map +1 -0
- package/dist/github_helpers.js +13 -0
- package/dist/gitops_analyze.task.d.ts +17 -0
- package/dist/gitops_analyze.task.d.ts.map +1 -0
- package/dist/gitops_analyze.task.js +188 -0
- package/dist/gitops_config.d.ts +56 -0
- package/dist/gitops_config.d.ts.map +1 -0
- package/dist/gitops_config.js +63 -0
- package/dist/gitops_plan.task.d.ts +28 -0
- package/dist/gitops_plan.task.d.ts.map +1 -0
- package/dist/gitops_plan.task.js +217 -0
- package/dist/gitops_publish.task.d.ts +29 -0
- package/dist/gitops_publish.task.d.ts.map +1 -0
- package/dist/gitops_publish.task.js +178 -0
- package/dist/gitops_sync.task.d.ts +18 -0
- package/dist/gitops_sync.task.d.ts.map +1 -0
- package/dist/gitops_sync.task.js +95 -0
- package/dist/gitops_task_helpers.d.ts +63 -0
- package/dist/gitops_task_helpers.d.ts.map +1 -0
- package/dist/gitops_task_helpers.js +84 -0
- package/dist/gitops_validate.task.d.ts +12 -0
- package/dist/gitops_validate.task.d.ts.map +1 -0
- package/dist/gitops_validate.task.js +210 -0
- package/dist/graph_validation.d.ts +39 -0
- package/dist/graph_validation.d.ts.map +1 -0
- package/dist/graph_validation.js +79 -0
- package/dist/local_repo.d.ts +84 -0
- package/dist/local_repo.d.ts.map +1 -0
- package/dist/local_repo.js +213 -0
- package/dist/log_helpers.d.ts +43 -0
- package/dist/log_helpers.d.ts.map +1 -0
- package/dist/log_helpers.js +98 -0
- package/dist/multi_repo_publisher.d.ts +34 -0
- package/dist/multi_repo_publisher.d.ts.map +1 -0
- package/dist/multi_repo_publisher.js +364 -0
- package/dist/npm_install_helpers.d.ts +23 -0
- package/dist/npm_install_helpers.d.ts.map +1 -0
- package/dist/npm_install_helpers.js +60 -0
- package/dist/npm_registry.d.ts +46 -0
- package/dist/npm_registry.d.ts.map +1 -0
- package/dist/npm_registry.js +96 -0
- package/dist/operations.d.ts +409 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +34 -0
- package/dist/operations_defaults.d.ts +19 -0
- package/dist/operations_defaults.d.ts.map +1 -0
- package/dist/operations_defaults.js +279 -0
- package/dist/output_helpers.d.ts +27 -0
- package/dist/output_helpers.d.ts.map +1 -0
- package/dist/output_helpers.js +39 -0
- package/dist/paths.d.ts +11 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +10 -0
- package/dist/preflight_checks.d.ts +47 -0
- package/dist/preflight_checks.d.ts.map +1 -0
- package/dist/preflight_checks.js +181 -0
- package/dist/publishing_plan.d.ts +100 -0
- package/dist/publishing_plan.d.ts.map +1 -0
- package/dist/publishing_plan.js +353 -0
- package/dist/publishing_plan_helpers.d.ts +30 -0
- package/dist/publishing_plan_helpers.d.ts.map +1 -0
- package/dist/publishing_plan_helpers.js +112 -0
- package/dist/publishing_plan_logging.d.ts +18 -0
- package/dist/publishing_plan_logging.d.ts.map +1 -0
- package/dist/publishing_plan_logging.js +342 -0
- package/dist/repo.svelte.d.ts +52 -0
- package/dist/repo.svelte.d.ts.map +1 -0
- package/dist/repo.svelte.js +70 -0
- package/dist/repo_ops.d.ts +57 -0
- package/dist/repo_ops.d.ts.map +1 -0
- package/dist/repo_ops.js +167 -0
- package/dist/resolved_gitops_config.d.ts +9 -0
- package/dist/resolved_gitops_config.d.ts.map +1 -0
- package/dist/resolved_gitops_config.js +12 -0
- package/dist/semver.d.ts +24 -0
- package/dist/semver.d.ts.map +1 -0
- package/dist/semver.js +140 -0
- package/dist/serialization_types.d.ts +57 -0
- package/dist/serialization_types.d.ts.map +1 -0
- package/dist/serialization_types.js +40 -0
- package/dist/version_utils.d.ts +48 -0
- package/dist/version_utils.d.ts.map +1 -0
- package/dist/version_utils.js +125 -0
- package/package.json +107 -0
- package/src/lib/changeset_generator.ts +162 -0
- package/src/lib/changeset_reader.ts +218 -0
- package/src/lib/constants.ts +8 -0
- package/src/lib/dependency_graph.ts +423 -0
- package/src/lib/dependency_updater.ts +297 -0
- package/src/lib/fetch_repo_data.ts +64 -0
- package/src/lib/fs_fetch_value_cache.ts +75 -0
- package/src/lib/git_operations.ts +208 -0
- package/src/lib/github.ts +128 -0
- package/src/lib/github_helpers.ts +31 -0
- package/src/lib/gitops_analyze.task.ts +261 -0
- package/src/lib/gitops_config.ts +123 -0
- package/src/lib/gitops_plan.task.ts +272 -0
- package/src/lib/gitops_publish.task.ts +227 -0
- package/src/lib/gitops_sync.task.ts +109 -0
- package/src/lib/gitops_task_helpers.ts +126 -0
- package/src/lib/gitops_validate.task.ts +248 -0
- package/src/lib/graph_validation.ts +109 -0
- package/src/lib/local_repo.ts +359 -0
- package/src/lib/log_helpers.ts +147 -0
- package/src/lib/multi_repo_publisher.ts +464 -0
- package/src/lib/npm_install_helpers.ts +85 -0
- package/src/lib/npm_registry.ts +143 -0
- package/src/lib/operations.ts +334 -0
- package/src/lib/operations_defaults.ts +335 -0
- package/src/lib/output_helpers.ts +64 -0
- package/src/lib/paths.ts +11 -0
- package/src/lib/preflight_checks.ts +269 -0
- package/src/lib/publishing_plan.ts +531 -0
- package/src/lib/publishing_plan_helpers.ts +145 -0
- package/src/lib/publishing_plan_logging.ts +470 -0
- package/src/lib/repo.svelte.ts +95 -0
- package/src/lib/repo_ops.ts +213 -0
- package/src/lib/resolved_gitops_config.ts +27 -0
- package/src/lib/semver.ts +166 -0
- package/src/lib/serialization_types.ts +90 -0
- package/src/lib/version_utils.ts +150 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-generation of changesets for dependency updates during publishing.
|
|
3
|
+
*
|
|
4
|
+
* Creates changesets when packages need to republish due to updated dependencies.
|
|
5
|
+
* For parsing existing changesets, see `changeset_reader.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { strip_version_prefix } from './version_utils.js';
|
|
11
|
+
/**
|
|
12
|
+
* Creates a changeset file for dependency updates.
|
|
13
|
+
* Returns the path to the created changeset file.
|
|
14
|
+
*/
|
|
15
|
+
export const create_changeset_for_dependency_updates = async (repo, updates, options = {}) => {
|
|
16
|
+
const { log } = options;
|
|
17
|
+
const changesets_dir = join(repo.repo_dir, '.changeset');
|
|
18
|
+
// Ensure .changeset directory exists
|
|
19
|
+
if (!existsSync(changesets_dir)) {
|
|
20
|
+
await mkdir(changesets_dir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
// Generate a unique filename
|
|
23
|
+
const timestamp = Date.now();
|
|
24
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
25
|
+
const filename = `dependency-update-${timestamp}-${random}.md`;
|
|
26
|
+
const filepath = join(changesets_dir, filename);
|
|
27
|
+
// Determine the required bump type based on updates
|
|
28
|
+
const required_bump = calculate_required_bump(repo, updates);
|
|
29
|
+
// Generate changeset content
|
|
30
|
+
const content = generate_changeset_content(repo.library.name, updates, required_bump);
|
|
31
|
+
// Write the changeset file
|
|
32
|
+
await writeFile(filepath, content, 'utf8');
|
|
33
|
+
log?.info(` Created changeset: ${filename}`);
|
|
34
|
+
return filepath;
|
|
35
|
+
};
|
|
36
|
+
const calculate_required_bump = (repo, updates) => {
|
|
37
|
+
const current_version = repo.library.package_json.version || '0.0.0';
|
|
38
|
+
const [major] = current_version.split('.').map(Number);
|
|
39
|
+
const is_pre_1_0 = major === 0;
|
|
40
|
+
// Check if any dependency had breaking changes
|
|
41
|
+
const has_breaking = updates.some((u) => u.breaking);
|
|
42
|
+
if (has_breaking) {
|
|
43
|
+
// Breaking changes propagate
|
|
44
|
+
// Pre-1.0: use minor for breaking changes
|
|
45
|
+
// 1.0+: use major for breaking changes
|
|
46
|
+
return is_pre_1_0 ? 'minor' : 'major';
|
|
47
|
+
}
|
|
48
|
+
// For non-breaking dependency updates, always use patch
|
|
49
|
+
return 'patch';
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Generates markdown changeset content for dependency updates.
|
|
53
|
+
*
|
|
54
|
+
* Creates properly formatted changeset with YAML frontmatter, summary,
|
|
55
|
+
* and categorized list of breaking vs regular updates. Output format
|
|
56
|
+
* matches changesets CLI for consistency.
|
|
57
|
+
*
|
|
58
|
+
* @param package_name package receiving the dependency updates
|
|
59
|
+
* @param updates list of dependency changes with version info
|
|
60
|
+
* @param bump_type required bump type (calculated from breaking changes)
|
|
61
|
+
* @returns markdown content ready to write to .changeset/*.md file
|
|
62
|
+
*/
|
|
63
|
+
export const generate_changeset_content = (package_name, updates, bump_type) => {
|
|
64
|
+
// Group updates by type
|
|
65
|
+
const breaking_updates = updates.filter((u) => u.breaking);
|
|
66
|
+
const regular_updates = updates.filter((u) => !u.breaking);
|
|
67
|
+
let message = 'Update dependencies';
|
|
68
|
+
if (breaking_updates.length > 0) {
|
|
69
|
+
message = 'Update dependencies (BREAKING CHANGES)';
|
|
70
|
+
}
|
|
71
|
+
const lines = ['---', `"${package_name}": ${bump_type}`, '---', '', message, ''];
|
|
72
|
+
if (breaking_updates.length > 0) {
|
|
73
|
+
lines.push('Breaking dependency changes:');
|
|
74
|
+
for (const update of breaking_updates) {
|
|
75
|
+
lines.push(`- ${update.package_name}: ${update.from_version} → ${update.to_version} (${update.bump_type})`);
|
|
76
|
+
}
|
|
77
|
+
lines.push('');
|
|
78
|
+
}
|
|
79
|
+
if (regular_updates.length > 0) {
|
|
80
|
+
if (breaking_updates.length > 0) {
|
|
81
|
+
lines.push('Other dependency updates:');
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
lines.push('Updated dependencies:');
|
|
85
|
+
}
|
|
86
|
+
for (const update of regular_updates) {
|
|
87
|
+
lines.push(`- ${update.package_name}: ${update.from_version} → ${update.to_version} (${update.bump_type})`);
|
|
88
|
+
}
|
|
89
|
+
lines.push('');
|
|
90
|
+
}
|
|
91
|
+
return lines.join('\n');
|
|
92
|
+
};
|
|
93
|
+
export const create_dependency_updates = (dependencies, published_versions) => {
|
|
94
|
+
const updates = [];
|
|
95
|
+
for (const [dep_name, current_version] of dependencies) {
|
|
96
|
+
const published = published_versions.get(dep_name);
|
|
97
|
+
if (published) {
|
|
98
|
+
// Strip version prefix (^, ~, etc)
|
|
99
|
+
const clean_current = strip_version_prefix(current_version);
|
|
100
|
+
updates.push({
|
|
101
|
+
package_name: dep_name,
|
|
102
|
+
from_version: clean_current,
|
|
103
|
+
to_version: published.new_version,
|
|
104
|
+
bump_type: published.bump_type,
|
|
105
|
+
breaking: published.breaking,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return updates;
|
|
110
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Changeset parsing and version prediction from `.changeset/*.md` files.
|
|
3
|
+
*
|
|
4
|
+
* Reads changesets to determine which packages need publishing and their version bumps.
|
|
5
|
+
* For auto-generating changesets during publishing, see `changeset_generator.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import type { Logger } from '@fuzdev/fuz_util/log.js';
|
|
8
|
+
import type { LocalRepo } from './local_repo.js';
|
|
9
|
+
import type { BumpType } from './semver.js';
|
|
10
|
+
export interface ChangesetInfo {
|
|
11
|
+
filename: string;
|
|
12
|
+
packages: Array<{
|
|
13
|
+
name: string;
|
|
14
|
+
bump_type: BumpType;
|
|
15
|
+
}>;
|
|
16
|
+
summary: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parses changeset content string from markdown format.
|
|
20
|
+
*
|
|
21
|
+
* Pure function for testability - no file I/O, just string parsing.
|
|
22
|
+
* Extracts package names, bump types, and summary from YAML frontmatter format.
|
|
23
|
+
* Returns null if format is invalid or no packages found.
|
|
24
|
+
*
|
|
25
|
+
* Expected format:
|
|
26
|
+
* ---
|
|
27
|
+
* "package-name": patch
|
|
28
|
+
* "@scope/package": minor
|
|
29
|
+
* ---
|
|
30
|
+
*
|
|
31
|
+
* Summary of changes
|
|
32
|
+
*
|
|
33
|
+
* @param content changeset markdown with YAML frontmatter
|
|
34
|
+
* @param filename optional filename for error reporting context
|
|
35
|
+
* @returns parsed changeset info or null if invalid format
|
|
36
|
+
*/
|
|
37
|
+
export declare const parse_changeset_content: (content: string, filename?: string) => ChangesetInfo | null;
|
|
38
|
+
export declare const parse_changeset_file: (filepath: string, log?: Logger) => Promise<ChangesetInfo | null>;
|
|
39
|
+
export declare const read_changesets: (repo: LocalRepo, log?: Logger) => Promise<Array<ChangesetInfo>>;
|
|
40
|
+
/**
|
|
41
|
+
* Determines the bump type for a package from its changesets.
|
|
42
|
+
*
|
|
43
|
+
* When multiple changesets exist for the same package, returns the highest
|
|
44
|
+
* bump type (major > minor > patch) to ensure the most significant change
|
|
45
|
+
* is reflected in the version bump.
|
|
46
|
+
*
|
|
47
|
+
* @returns the highest bump type, or null if package has no changesets
|
|
48
|
+
*/
|
|
49
|
+
export declare const determine_bump_from_changesets: (changesets: Array<ChangesetInfo>, package_name: string) => BumpType | null;
|
|
50
|
+
/**
|
|
51
|
+
* Checks if a repo has any changeset files (excluding README.md).
|
|
52
|
+
*
|
|
53
|
+
* Used by preflight checks and publishing workflow to determine which packages
|
|
54
|
+
* need to be published. Returns false if .changeset directory doesn't exist
|
|
55
|
+
* or contains only README.md.
|
|
56
|
+
*
|
|
57
|
+
* @returns true if repo has unpublished changesets
|
|
58
|
+
*/
|
|
59
|
+
export declare const has_changesets: (repo: LocalRepo) => Promise<boolean>;
|
|
60
|
+
/**
|
|
61
|
+
* Predicts the next version by analyzing all changesets in a repo.
|
|
62
|
+
*
|
|
63
|
+
* Reads all changesets, determines the highest bump type for the package,
|
|
64
|
+
* and calculates the next version. Returns null if no changesets found.
|
|
65
|
+
*
|
|
66
|
+
* Critical for dry-run mode accuracy - allows simulating publishes without
|
|
67
|
+
* actually running `gro publish` which consumes changesets.
|
|
68
|
+
*
|
|
69
|
+
* @returns predicted version and bump type, or null if no changesets
|
|
70
|
+
*/
|
|
71
|
+
export declare const predict_next_version: (repo: LocalRepo, log?: Logger) => Promise<{
|
|
72
|
+
version: string;
|
|
73
|
+
bump_type: BumpType;
|
|
74
|
+
} | null>;
|
|
75
|
+
//# sourceMappingURL=changeset_reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changeset_reader.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/changeset_reader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAKpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAC;AAG1C,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,QAAQ,CAAA;KAAC,CAAC,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,MAAM,EACf,iBAAyB,KACvB,aAAa,GAAG,IAmClB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAChC,UAAU,MAAM,EAChB,MAAM,MAAM,KACV,OAAO,CAAC,aAAa,GAAG,IAAI,CAgB9B,CAAC;AAEF,eAAO,MAAM,eAAe,GAC3B,MAAM,SAAS,EACf,MAAM,MAAM,KACV,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAsB9B,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,GAC1C,YAAY,KAAK,CAAC,aAAa,CAAC,EAChC,cAAc,MAAM,KAClB,QAAQ,GAAG,IAcb,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAU,MAAM,SAAS,KAAG,OAAO,CAAC,OAAO,CAarE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,MAAM,MAAM,KACV,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,QAAQ,CAAA;CAAC,GAAG,IAAI,CAsBvD,CAAC"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Changeset parsing and version prediction from `.changeset/*.md` files.
|
|
3
|
+
*
|
|
4
|
+
* Reads changesets to determine which packages need publishing and their version bumps.
|
|
5
|
+
* For auto-generating changesets during publishing, see `changeset_generator.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { compare_bump_types, calculate_next_version } from './version_utils.js';
|
|
11
|
+
/**
|
|
12
|
+
* Parses changeset content string from markdown format.
|
|
13
|
+
*
|
|
14
|
+
* Pure function for testability - no file I/O, just string parsing.
|
|
15
|
+
* Extracts package names, bump types, and summary from YAML frontmatter format.
|
|
16
|
+
* Returns null if format is invalid or no packages found.
|
|
17
|
+
*
|
|
18
|
+
* Expected format:
|
|
19
|
+
* ---
|
|
20
|
+
* "package-name": patch
|
|
21
|
+
* "@scope/package": minor
|
|
22
|
+
* ---
|
|
23
|
+
*
|
|
24
|
+
* Summary of changes
|
|
25
|
+
*
|
|
26
|
+
* @param content changeset markdown with YAML frontmatter
|
|
27
|
+
* @param filename optional filename for error reporting context
|
|
28
|
+
* @returns parsed changeset info or null if invalid format
|
|
29
|
+
*/
|
|
30
|
+
export const parse_changeset_content = (content, filename = 'changeset.md') => {
|
|
31
|
+
// Match frontmatter between --- markers
|
|
32
|
+
const frontmatter_match = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)/.exec(content);
|
|
33
|
+
if (!frontmatter_match) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const frontmatter = frontmatter_match[1];
|
|
37
|
+
const summary = frontmatter_match[2].trim();
|
|
38
|
+
// Parse package entries
|
|
39
|
+
const packages = [];
|
|
40
|
+
// Match lines like: "package-name": patch
|
|
41
|
+
// or: '@scope/package': minor
|
|
42
|
+
// Allow leading whitespace
|
|
43
|
+
const package_regex = /^\s*["']([^"']+)["']\s*:\s*(major|minor|patch)\s*$/gm;
|
|
44
|
+
let match;
|
|
45
|
+
while ((match = package_regex.exec(frontmatter)) !== null) {
|
|
46
|
+
packages.push({
|
|
47
|
+
name: match[1],
|
|
48
|
+
bump_type: match[2],
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (packages.length === 0) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
filename,
|
|
56
|
+
packages,
|
|
57
|
+
summary,
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export const parse_changeset_file = async (filepath, log) => {
|
|
61
|
+
try {
|
|
62
|
+
const content = await readFile(filepath, 'utf8');
|
|
63
|
+
const filename = filepath.split('/').pop() || '';
|
|
64
|
+
const result = parse_changeset_content(content, filename);
|
|
65
|
+
if (!result) {
|
|
66
|
+
log?.warn(` Invalid changeset format in ${filepath}`);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
log?.error(` Failed to parse changeset ${filepath}: ${error}`);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
export const read_changesets = async (repo, log) => {
|
|
76
|
+
const changesets_dir = join(repo.repo_dir, '.changeset');
|
|
77
|
+
try {
|
|
78
|
+
const files = await readdir(changesets_dir);
|
|
79
|
+
const changeset_files = files.filter((f) => f.endsWith('.md') && f !== 'README.md');
|
|
80
|
+
const changesets = [];
|
|
81
|
+
for (const file of changeset_files) {
|
|
82
|
+
const filepath = join(changesets_dir, file);
|
|
83
|
+
const changeset = await parse_changeset_file(filepath, log); // eslint-disable-line no-await-in-loop
|
|
84
|
+
if (changeset) {
|
|
85
|
+
changesets.push(changeset);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return changesets;
|
|
89
|
+
}
|
|
90
|
+
catch (_error) {
|
|
91
|
+
// No .changeset directory or error reading
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Determines the bump type for a package from its changesets.
|
|
97
|
+
*
|
|
98
|
+
* When multiple changesets exist for the same package, returns the highest
|
|
99
|
+
* bump type (major > minor > patch) to ensure the most significant change
|
|
100
|
+
* is reflected in the version bump.
|
|
101
|
+
*
|
|
102
|
+
* @returns the highest bump type, or null if package has no changesets
|
|
103
|
+
*/
|
|
104
|
+
export const determine_bump_from_changesets = (changesets, package_name) => {
|
|
105
|
+
let highest_bump = null;
|
|
106
|
+
for (const changeset of changesets) {
|
|
107
|
+
for (const pkg of changeset.packages) {
|
|
108
|
+
if (pkg.name === package_name) {
|
|
109
|
+
if (!highest_bump || compare_bump_types(pkg.bump_type, highest_bump) > 0) {
|
|
110
|
+
highest_bump = pkg.bump_type;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return highest_bump;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Checks if a repo has any changeset files (excluding README.md).
|
|
119
|
+
*
|
|
120
|
+
* Used by preflight checks and publishing workflow to determine which packages
|
|
121
|
+
* need to be published. Returns false if .changeset directory doesn't exist
|
|
122
|
+
* or contains only README.md.
|
|
123
|
+
*
|
|
124
|
+
* @returns true if repo has unpublished changesets
|
|
125
|
+
*/
|
|
126
|
+
export const has_changesets = async (repo) => {
|
|
127
|
+
const changesets_dir = join(repo.repo_dir, '.changeset');
|
|
128
|
+
if (!existsSync(changesets_dir)) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const files = await readdir(changesets_dir);
|
|
133
|
+
// Look for markdown files that aren't the README
|
|
134
|
+
return files.some((file) => file.endsWith('.md') && file !== 'README.md');
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Predicts the next version by analyzing all changesets in a repo.
|
|
142
|
+
*
|
|
143
|
+
* Reads all changesets, determines the highest bump type for the package,
|
|
144
|
+
* and calculates the next version. Returns null if no changesets found.
|
|
145
|
+
*
|
|
146
|
+
* Critical for dry-run mode accuracy - allows simulating publishes without
|
|
147
|
+
* actually running `gro publish` which consumes changesets.
|
|
148
|
+
*
|
|
149
|
+
* @returns predicted version and bump type, or null if no changesets
|
|
150
|
+
*/
|
|
151
|
+
export const predict_next_version = async (repo, log) => {
|
|
152
|
+
const changesets = await read_changesets(repo, log);
|
|
153
|
+
if (changesets.length === 0) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const bump_type = determine_bump_from_changesets(changesets, repo.library.name);
|
|
157
|
+
if (!bump_type) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
const current_version = repo.library.package_json.version || '0.0.0';
|
|
161
|
+
const next_version = calculate_next_version(current_version, bump_type);
|
|
162
|
+
log?.debug(` Predicted ${repo.library.name}: ${current_version} → ${next_version} (${bump_type})`);
|
|
163
|
+
return {
|
|
164
|
+
version: next_version,
|
|
165
|
+
bump_type,
|
|
166
|
+
};
|
|
167
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximum number of iterations for fixed-point iteration during publishing.
|
|
3
|
+
* Used in both plan generation and actual publishing to resolve transitive dependency cascades.
|
|
4
|
+
*
|
|
5
|
+
* In practice, most repos converge in 2-3 iterations.
|
|
6
|
+
* Deep dependency chains may require more iterations.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_ITERATIONS = 10;
|
|
9
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/constants.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximum number of iterations for fixed-point iteration during publishing.
|
|
3
|
+
* Used in both plan generation and actual publishing to resolve transitive dependency cascades.
|
|
4
|
+
*
|
|
5
|
+
* In practice, most repos converge in 2-3 iterations.
|
|
6
|
+
* Deep dependency chains may require more iterations.
|
|
7
|
+
*/
|
|
8
|
+
export const MAX_ITERATIONS = 10;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency graph data structure and algorithms for multi-repo publishing.
|
|
3
|
+
*
|
|
4
|
+
* Provides `DependencyGraph` class with topological sort and cycle detection.
|
|
5
|
+
* For validation workflow and publishing order computation, see `graph_validation.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import type { LocalRepo } from './local_repo.js';
|
|
8
|
+
export declare const DEPENDENCY_TYPE: {
|
|
9
|
+
readonly PROD: "prod";
|
|
10
|
+
readonly PEER: "peer";
|
|
11
|
+
readonly DEV: "dev";
|
|
12
|
+
};
|
|
13
|
+
export type DependencyType = (typeof DEPENDENCY_TYPE)[keyof typeof DEPENDENCY_TYPE];
|
|
14
|
+
export interface DependencySpec {
|
|
15
|
+
type: DependencyType;
|
|
16
|
+
version: string;
|
|
17
|
+
resolved?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface DependencyGraphJson {
|
|
20
|
+
nodes: Array<{
|
|
21
|
+
name: string;
|
|
22
|
+
version: string;
|
|
23
|
+
dependencies: Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
spec: DependencySpec;
|
|
26
|
+
}>;
|
|
27
|
+
dependents: Array<string>;
|
|
28
|
+
publishable: boolean;
|
|
29
|
+
}>;
|
|
30
|
+
edges: Array<{
|
|
31
|
+
from: string;
|
|
32
|
+
to: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export interface DependencyNode {
|
|
36
|
+
name: string;
|
|
37
|
+
version: string;
|
|
38
|
+
repo?: LocalRepo;
|
|
39
|
+
dependencies: Map<string, DependencySpec>;
|
|
40
|
+
dependents: Set<string>;
|
|
41
|
+
publishable: boolean;
|
|
42
|
+
}
|
|
43
|
+
export declare class DependencyGraph {
|
|
44
|
+
nodes: Map<string, DependencyNode>;
|
|
45
|
+
edges: Map<string, Set<string>>;
|
|
46
|
+
constructor();
|
|
47
|
+
init_from_repos(repos: Array<LocalRepo>): void;
|
|
48
|
+
get_node(name: string): DependencyNode | undefined;
|
|
49
|
+
get_dependents(name: string): Set<string>;
|
|
50
|
+
get_dependencies(name: string): Map<string, DependencySpec>;
|
|
51
|
+
/**
|
|
52
|
+
* Computes topological sort order for dependency graph.
|
|
53
|
+
*
|
|
54
|
+
* Uses Kahn's algorithm with alphabetical ordering within tiers for
|
|
55
|
+
* deterministic results. Throws if cycles detected.
|
|
56
|
+
*
|
|
57
|
+
* @param exclude_dev if true, excludes dev dependencies to break cycles.
|
|
58
|
+
* Publishing uses exclude_dev=true to handle circular dev deps.
|
|
59
|
+
* @returns array of package names in dependency order (dependencies before dependents)
|
|
60
|
+
* @throws {Error} if circular dependencies detected in included dependency types
|
|
61
|
+
*/
|
|
62
|
+
topological_sort(exclude_dev?: boolean): Array<string>;
|
|
63
|
+
detect_cycles(): Array<Array<string>>;
|
|
64
|
+
/**
|
|
65
|
+
* Detects circular dependencies, categorized by severity.
|
|
66
|
+
*
|
|
67
|
+
* Production/peer cycles prevent publishing (impossible to order packages).
|
|
68
|
+
* Dev cycles are normal (test utils, shared configs) and safely ignored.
|
|
69
|
+
*
|
|
70
|
+
* Uses DFS traversal with recursion stack to identify back edges.
|
|
71
|
+
* Deduplicates cycles using sorted cycle keys.
|
|
72
|
+
*
|
|
73
|
+
* @returns object with production_cycles (errors) and dev_cycles (info)
|
|
74
|
+
*/
|
|
75
|
+
detect_cycles_by_type(): {
|
|
76
|
+
production_cycles: Array<Array<string>>;
|
|
77
|
+
dev_cycles: Array<Array<string>>;
|
|
78
|
+
};
|
|
79
|
+
toJSON(): DependencyGraphJson;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Builder for creating and analyzing dependency graphs.
|
|
83
|
+
*/
|
|
84
|
+
export declare class DependencyGraphBuilder {
|
|
85
|
+
/**
|
|
86
|
+
* Constructs dependency graph from local repos.
|
|
87
|
+
*
|
|
88
|
+
* Two-pass algorithm: first creates nodes, then builds edges (dependents).
|
|
89
|
+
* Prioritizes prod/peer deps over dev deps when same package appears in
|
|
90
|
+
* multiple dependency types (stronger constraint wins).
|
|
91
|
+
*
|
|
92
|
+
* @returns fully initialized dependency graph with all nodes and edges
|
|
93
|
+
*/
|
|
94
|
+
build_from_repos(repos: Array<LocalRepo>): DependencyGraph;
|
|
95
|
+
/**
|
|
96
|
+
* Computes publishing order using topological sort with dev deps excluded.
|
|
97
|
+
*
|
|
98
|
+
* Excludes dev dependencies to break circular dev dependency cycles while
|
|
99
|
+
* preserving production/peer dependency ordering. This allows patterns like
|
|
100
|
+
* shared test utilities that depend on each other for development.
|
|
101
|
+
*
|
|
102
|
+
* @returns package names in safe publishing order (dependencies before dependents)
|
|
103
|
+
* @throws {Error} if production/peer cycles detected (cannot be resolved by exclusion)
|
|
104
|
+
*/
|
|
105
|
+
compute_publishing_order(graph: DependencyGraph): Array<string>;
|
|
106
|
+
analyze(graph: DependencyGraph): {
|
|
107
|
+
production_cycles: Array<Array<string>>;
|
|
108
|
+
dev_cycles: Array<Array<string>>;
|
|
109
|
+
wildcard_deps: Array<{
|
|
110
|
+
pkg: string;
|
|
111
|
+
dep: string;
|
|
112
|
+
version: string;
|
|
113
|
+
}>;
|
|
114
|
+
missing_peers: Array<{
|
|
115
|
+
pkg: string;
|
|
116
|
+
dep: string;
|
|
117
|
+
}>;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=dependency_graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency_graph.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/dependency_graph.ts"],"names":[],"mappings":"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"}
|