@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
package/package.json
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fuzdev/fuz_gitops",
|
|
3
|
+
"version": "0.57.0",
|
|
4
|
+
"description": "a tool for managing many repos",
|
|
5
|
+
"glyph": "🪄",
|
|
6
|
+
"logo": "logo.svg",
|
|
7
|
+
"logo_alt": "a friendly blue spider facing you",
|
|
8
|
+
"public": true,
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"homepage": "https://gitops.fuz.dev/",
|
|
11
|
+
"repository": "https://github.com/fuzdev/fuz_gitops",
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Ryan Atkinson",
|
|
14
|
+
"email": "mail@ryanatkn.com",
|
|
15
|
+
"url": "https://www.ryanatkn.com/"
|
|
16
|
+
},
|
|
17
|
+
"bugs": "https://github.com/fuzdev/fuz_gitops/issues",
|
|
18
|
+
"funding": "https://www.ryanatkn.com/funding",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"start": "gro dev",
|
|
21
|
+
"dev": "gro dev",
|
|
22
|
+
"build": "gro build",
|
|
23
|
+
"check": "gro check",
|
|
24
|
+
"test": "gro test",
|
|
25
|
+
"preview": "vite preview",
|
|
26
|
+
"deploy": "gro deploy"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.15"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@fuzdev/fuz_css": ">=0.40.0",
|
|
34
|
+
"@fuzdev/fuz_ui": ">=0.169.0",
|
|
35
|
+
"@fuzdev/fuz_util": ">=0.42.0",
|
|
36
|
+
"@ryanatkn/gro": ">=0.181.0",
|
|
37
|
+
"@sveltejs/kit": "^2",
|
|
38
|
+
"svelte": "^5",
|
|
39
|
+
"zod": "^4.1.13"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@changesets/changelog-git": "^0.2.1",
|
|
43
|
+
"@fuzdev/fuz_code": "^0.37.0",
|
|
44
|
+
"@fuzdev/fuz_css": "^0.40.0",
|
|
45
|
+
"@fuzdev/fuz_ui": "^0.169.0",
|
|
46
|
+
"@fuzdev/fuz_util": "^0.42.0",
|
|
47
|
+
"@ryanatkn/eslint-config": "^0.9.0",
|
|
48
|
+
"@ryanatkn/gro": "^0.181.0",
|
|
49
|
+
"@sveltejs/adapter-static": "^3.0.10",
|
|
50
|
+
"@sveltejs/kit": "^2.49.0",
|
|
51
|
+
"@sveltejs/package": "^2.5.7",
|
|
52
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
53
|
+
"@types/node": "^24.10.1",
|
|
54
|
+
"eslint": "^9.39.1",
|
|
55
|
+
"eslint-plugin-svelte": "^3.13.0",
|
|
56
|
+
"prettier": "^3.6.2",
|
|
57
|
+
"prettier-plugin-svelte": "^3.4.0",
|
|
58
|
+
"svelte": "^5.45.2",
|
|
59
|
+
"svelte-check": "^4.3.4",
|
|
60
|
+
"tslib": "^2.8.1",
|
|
61
|
+
"typescript": "^5.9.3",
|
|
62
|
+
"typescript-eslint": "^8.48.0",
|
|
63
|
+
"vitest": "^4.0.14"
|
|
64
|
+
},
|
|
65
|
+
"prettier": {
|
|
66
|
+
"plugins": [
|
|
67
|
+
"prettier-plugin-svelte"
|
|
68
|
+
],
|
|
69
|
+
"useTabs": true,
|
|
70
|
+
"printWidth": 100,
|
|
71
|
+
"singleQuote": true,
|
|
72
|
+
"bracketSpacing": false,
|
|
73
|
+
"overrides": [
|
|
74
|
+
{
|
|
75
|
+
"files": "package.json",
|
|
76
|
+
"options": {
|
|
77
|
+
"useTabs": false
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
"sideEffects": [
|
|
83
|
+
"**/*.css"
|
|
84
|
+
],
|
|
85
|
+
"files": [
|
|
86
|
+
"dist",
|
|
87
|
+
"src/lib/**/*.ts",
|
|
88
|
+
"!src/lib/**/*.test.*",
|
|
89
|
+
"!dist/**/*.test.*"
|
|
90
|
+
],
|
|
91
|
+
"exports": {
|
|
92
|
+
"./package.json": "./package.json",
|
|
93
|
+
"./*.js": {
|
|
94
|
+
"types": "./dist/*.d.ts",
|
|
95
|
+
"default": "./dist/*.js"
|
|
96
|
+
},
|
|
97
|
+
"./*.ts": {
|
|
98
|
+
"types": "./dist/*.d.ts",
|
|
99
|
+
"default": "./dist/*.js"
|
|
100
|
+
},
|
|
101
|
+
"./*.svelte": {
|
|
102
|
+
"types": "./dist/*.svelte.d.ts",
|
|
103
|
+
"svelte": "./dist/*.svelte",
|
|
104
|
+
"default": "./dist/*.svelte"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
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
|
+
|
|
8
|
+
import {writeFile, mkdir} from 'node:fs/promises';
|
|
9
|
+
import {join} from 'node:path';
|
|
10
|
+
import {existsSync} from 'node:fs';
|
|
11
|
+
import type {Logger} from '@fuzdev/fuz_util/log.js';
|
|
12
|
+
import type {LocalRepo} from './local_repo.js';
|
|
13
|
+
import type {PublishedVersion} from './multi_repo_publisher.js';
|
|
14
|
+
import {strip_version_prefix} from './version_utils.js';
|
|
15
|
+
|
|
16
|
+
export interface DependencyVersionChange {
|
|
17
|
+
package_name: string;
|
|
18
|
+
from_version: string;
|
|
19
|
+
to_version: string;
|
|
20
|
+
bump_type: 'major' | 'minor' | 'patch';
|
|
21
|
+
breaking: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates a changeset file for dependency updates.
|
|
26
|
+
* Returns the path to the created changeset file.
|
|
27
|
+
*/
|
|
28
|
+
export const create_changeset_for_dependency_updates = async (
|
|
29
|
+
repo: LocalRepo,
|
|
30
|
+
updates: Array<DependencyVersionChange>,
|
|
31
|
+
options: {log?: Logger} = {},
|
|
32
|
+
): Promise<string> => {
|
|
33
|
+
const {log} = options;
|
|
34
|
+
const changesets_dir = join(repo.repo_dir, '.changeset');
|
|
35
|
+
|
|
36
|
+
// Ensure .changeset directory exists
|
|
37
|
+
if (!existsSync(changesets_dir)) {
|
|
38
|
+
await mkdir(changesets_dir, {recursive: true});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Generate a unique filename
|
|
42
|
+
const timestamp = Date.now();
|
|
43
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
44
|
+
const filename = `dependency-update-${timestamp}-${random}.md`;
|
|
45
|
+
const filepath = join(changesets_dir, filename);
|
|
46
|
+
|
|
47
|
+
// Determine the required bump type based on updates
|
|
48
|
+
const required_bump = calculate_required_bump(repo, updates);
|
|
49
|
+
|
|
50
|
+
// Generate changeset content
|
|
51
|
+
const content = generate_changeset_content(repo.library.name, updates, required_bump);
|
|
52
|
+
|
|
53
|
+
// Write the changeset file
|
|
54
|
+
await writeFile(filepath, content, 'utf8');
|
|
55
|
+
|
|
56
|
+
log?.info(` Created changeset: ${filename}`);
|
|
57
|
+
|
|
58
|
+
return filepath;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const calculate_required_bump = (
|
|
62
|
+
repo: LocalRepo,
|
|
63
|
+
updates: Array<DependencyVersionChange>,
|
|
64
|
+
): 'major' | 'minor' | 'patch' => {
|
|
65
|
+
const current_version = repo.library.package_json.version || '0.0.0';
|
|
66
|
+
const [major] = current_version.split('.').map(Number);
|
|
67
|
+
const is_pre_1_0 = major === 0;
|
|
68
|
+
|
|
69
|
+
// Check if any dependency had breaking changes
|
|
70
|
+
const has_breaking = updates.some((u) => u.breaking);
|
|
71
|
+
|
|
72
|
+
if (has_breaking) {
|
|
73
|
+
// Breaking changes propagate
|
|
74
|
+
// Pre-1.0: use minor for breaking changes
|
|
75
|
+
// 1.0+: use major for breaking changes
|
|
76
|
+
return is_pre_1_0 ? 'minor' : 'major';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// For non-breaking dependency updates, always use patch
|
|
80
|
+
return 'patch';
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generates markdown changeset content for dependency updates.
|
|
85
|
+
*
|
|
86
|
+
* Creates properly formatted changeset with YAML frontmatter, summary,
|
|
87
|
+
* and categorized list of breaking vs regular updates. Output format
|
|
88
|
+
* matches changesets CLI for consistency.
|
|
89
|
+
*
|
|
90
|
+
* @param package_name package receiving the dependency updates
|
|
91
|
+
* @param updates list of dependency changes with version info
|
|
92
|
+
* @param bump_type required bump type (calculated from breaking changes)
|
|
93
|
+
* @returns markdown content ready to write to .changeset/*.md file
|
|
94
|
+
*/
|
|
95
|
+
export const generate_changeset_content = (
|
|
96
|
+
package_name: string,
|
|
97
|
+
updates: Array<DependencyVersionChange>,
|
|
98
|
+
bump_type: 'major' | 'minor' | 'patch',
|
|
99
|
+
): string => {
|
|
100
|
+
// Group updates by type
|
|
101
|
+
const breaking_updates = updates.filter((u) => u.breaking);
|
|
102
|
+
const regular_updates = updates.filter((u) => !u.breaking);
|
|
103
|
+
|
|
104
|
+
let message = 'Update dependencies';
|
|
105
|
+
|
|
106
|
+
if (breaking_updates.length > 0) {
|
|
107
|
+
message = 'Update dependencies (BREAKING CHANGES)';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const lines: Array<string> = ['---', `"${package_name}": ${bump_type}`, '---', '', message, ''];
|
|
111
|
+
|
|
112
|
+
if (breaking_updates.length > 0) {
|
|
113
|
+
lines.push('Breaking dependency changes:');
|
|
114
|
+
for (const update of breaking_updates) {
|
|
115
|
+
lines.push(
|
|
116
|
+
`- ${update.package_name}: ${update.from_version} → ${update.to_version} (${update.bump_type})`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
lines.push('');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (regular_updates.length > 0) {
|
|
123
|
+
if (breaking_updates.length > 0) {
|
|
124
|
+
lines.push('Other dependency updates:');
|
|
125
|
+
} else {
|
|
126
|
+
lines.push('Updated dependencies:');
|
|
127
|
+
}
|
|
128
|
+
for (const update of regular_updates) {
|
|
129
|
+
lines.push(
|
|
130
|
+
`- ${update.package_name}: ${update.from_version} → ${update.to_version} (${update.bump_type})`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
lines.push('');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return lines.join('\n');
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const create_dependency_updates = (
|
|
140
|
+
dependencies: Map<string, string>,
|
|
141
|
+
published_versions: Map<string, PublishedVersion>,
|
|
142
|
+
): Array<DependencyVersionChange> => {
|
|
143
|
+
const updates: Array<DependencyVersionChange> = [];
|
|
144
|
+
|
|
145
|
+
for (const [dep_name, current_version] of dependencies) {
|
|
146
|
+
const published = published_versions.get(dep_name);
|
|
147
|
+
if (published) {
|
|
148
|
+
// Strip version prefix (^, ~, etc)
|
|
149
|
+
const clean_current = strip_version_prefix(current_version);
|
|
150
|
+
|
|
151
|
+
updates.push({
|
|
152
|
+
package_name: dep_name,
|
|
153
|
+
from_version: clean_current,
|
|
154
|
+
to_version: published.new_version,
|
|
155
|
+
bump_type: published.bump_type,
|
|
156
|
+
breaking: published.breaking,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return updates;
|
|
162
|
+
};
|
|
@@ -0,0 +1,218 @@
|
|
|
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
|
+
|
|
8
|
+
import type {Logger} from '@fuzdev/fuz_util/log.js';
|
|
9
|
+
import {existsSync} from 'node:fs';
|
|
10
|
+
import {readdir, readFile} from 'node:fs/promises';
|
|
11
|
+
import {join} from 'node:path';
|
|
12
|
+
|
|
13
|
+
import type {LocalRepo} from './local_repo.js';
|
|
14
|
+
import type {BumpType} from './semver.js';
|
|
15
|
+
import {compare_bump_types, calculate_next_version} from './version_utils.js';
|
|
16
|
+
|
|
17
|
+
export interface ChangesetInfo {
|
|
18
|
+
filename: string;
|
|
19
|
+
packages: Array<{name: string; bump_type: BumpType}>;
|
|
20
|
+
summary: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parses changeset content string from markdown format.
|
|
25
|
+
*
|
|
26
|
+
* Pure function for testability - no file I/O, just string parsing.
|
|
27
|
+
* Extracts package names, bump types, and summary from YAML frontmatter format.
|
|
28
|
+
* Returns null if format is invalid or no packages found.
|
|
29
|
+
*
|
|
30
|
+
* Expected format:
|
|
31
|
+
* ---
|
|
32
|
+
* "package-name": patch
|
|
33
|
+
* "@scope/package": minor
|
|
34
|
+
* ---
|
|
35
|
+
*
|
|
36
|
+
* Summary of changes
|
|
37
|
+
*
|
|
38
|
+
* @param content changeset markdown with YAML frontmatter
|
|
39
|
+
* @param filename optional filename for error reporting context
|
|
40
|
+
* @returns parsed changeset info or null if invalid format
|
|
41
|
+
*/
|
|
42
|
+
export const parse_changeset_content = (
|
|
43
|
+
content: string,
|
|
44
|
+
filename = 'changeset.md',
|
|
45
|
+
): ChangesetInfo | null => {
|
|
46
|
+
// Match frontmatter between --- markers
|
|
47
|
+
const frontmatter_match = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)/.exec(content);
|
|
48
|
+
if (!frontmatter_match) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const frontmatter = frontmatter_match[1]!;
|
|
53
|
+
const summary = frontmatter_match[2]!.trim();
|
|
54
|
+
|
|
55
|
+
// Parse package entries
|
|
56
|
+
const packages: Array<{name: string; bump_type: BumpType}> = [];
|
|
57
|
+
|
|
58
|
+
// Match lines like: "package-name": patch
|
|
59
|
+
// or: '@scope/package': minor
|
|
60
|
+
// Allow leading whitespace
|
|
61
|
+
const package_regex = /^\s*["']([^"']+)["']\s*:\s*(major|minor|patch)\s*$/gm;
|
|
62
|
+
let match;
|
|
63
|
+
|
|
64
|
+
while ((match = package_regex.exec(frontmatter)) !== null) {
|
|
65
|
+
packages.push({
|
|
66
|
+
name: match[1]!,
|
|
67
|
+
bump_type: match[2]! as BumpType,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (packages.length === 0) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
filename,
|
|
77
|
+
packages,
|
|
78
|
+
summary,
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const parse_changeset_file = async (
|
|
83
|
+
filepath: string,
|
|
84
|
+
log?: Logger,
|
|
85
|
+
): Promise<ChangesetInfo | null> => {
|
|
86
|
+
try {
|
|
87
|
+
const content = await readFile(filepath, 'utf8');
|
|
88
|
+
const filename = filepath.split('/').pop() || '';
|
|
89
|
+
|
|
90
|
+
const result = parse_changeset_content(content, filename);
|
|
91
|
+
|
|
92
|
+
if (!result) {
|
|
93
|
+
log?.warn(` Invalid changeset format in ${filepath}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return result;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
log?.error(` Failed to parse changeset ${filepath}: ${error}`);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const read_changesets = async (
|
|
104
|
+
repo: LocalRepo,
|
|
105
|
+
log?: Logger,
|
|
106
|
+
): Promise<Array<ChangesetInfo>> => {
|
|
107
|
+
const changesets_dir = join(repo.repo_dir, '.changeset');
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const files = await readdir(changesets_dir);
|
|
111
|
+
const changeset_files = files.filter((f) => f.endsWith('.md') && f !== 'README.md');
|
|
112
|
+
|
|
113
|
+
const changesets: Array<ChangesetInfo> = [];
|
|
114
|
+
|
|
115
|
+
for (const file of changeset_files) {
|
|
116
|
+
const filepath = join(changesets_dir, file);
|
|
117
|
+
const changeset = await parse_changeset_file(filepath, log); // eslint-disable-line no-await-in-loop
|
|
118
|
+
if (changeset) {
|
|
119
|
+
changesets.push(changeset);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return changesets;
|
|
124
|
+
} catch (_error) {
|
|
125
|
+
// No .changeset directory or error reading
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Determines the bump type for a package from its changesets.
|
|
132
|
+
*
|
|
133
|
+
* When multiple changesets exist for the same package, returns the highest
|
|
134
|
+
* bump type (major > minor > patch) to ensure the most significant change
|
|
135
|
+
* is reflected in the version bump.
|
|
136
|
+
*
|
|
137
|
+
* @returns the highest bump type, or null if package has no changesets
|
|
138
|
+
*/
|
|
139
|
+
export const determine_bump_from_changesets = (
|
|
140
|
+
changesets: Array<ChangesetInfo>,
|
|
141
|
+
package_name: string,
|
|
142
|
+
): BumpType | null => {
|
|
143
|
+
let highest_bump: BumpType | null = null;
|
|
144
|
+
|
|
145
|
+
for (const changeset of changesets) {
|
|
146
|
+
for (const pkg of changeset.packages) {
|
|
147
|
+
if (pkg.name === package_name) {
|
|
148
|
+
if (!highest_bump || compare_bump_types(pkg.bump_type, highest_bump) > 0) {
|
|
149
|
+
highest_bump = pkg.bump_type;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return highest_bump;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Checks if a repo has any changeset files (excluding README.md).
|
|
160
|
+
*
|
|
161
|
+
* Used by preflight checks and publishing workflow to determine which packages
|
|
162
|
+
* need to be published. Returns false if .changeset directory doesn't exist
|
|
163
|
+
* or contains only README.md.
|
|
164
|
+
*
|
|
165
|
+
* @returns true if repo has unpublished changesets
|
|
166
|
+
*/
|
|
167
|
+
export const has_changesets = async (repo: LocalRepo): Promise<boolean> => {
|
|
168
|
+
const changesets_dir = join(repo.repo_dir, '.changeset');
|
|
169
|
+
if (!existsSync(changesets_dir)) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const files = await readdir(changesets_dir);
|
|
175
|
+
// Look for markdown files that aren't the README
|
|
176
|
+
return files.some((file) => file.endsWith('.md') && file !== 'README.md');
|
|
177
|
+
} catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Predicts the next version by analyzing all changesets in a repo.
|
|
184
|
+
*
|
|
185
|
+
* Reads all changesets, determines the highest bump type for the package,
|
|
186
|
+
* and calculates the next version. Returns null if no changesets found.
|
|
187
|
+
*
|
|
188
|
+
* Critical for dry-run mode accuracy - allows simulating publishes without
|
|
189
|
+
* actually running `gro publish` which consumes changesets.
|
|
190
|
+
*
|
|
191
|
+
* @returns predicted version and bump type, or null if no changesets
|
|
192
|
+
*/
|
|
193
|
+
export const predict_next_version = async (
|
|
194
|
+
repo: LocalRepo,
|
|
195
|
+
log?: Logger,
|
|
196
|
+
): Promise<{version: string; bump_type: BumpType} | null> => {
|
|
197
|
+
const changesets = await read_changesets(repo, log);
|
|
198
|
+
if (changesets.length === 0) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const bump_type = determine_bump_from_changesets(changesets, repo.library.name);
|
|
203
|
+
if (!bump_type) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const current_version = repo.library.package_json.version || '0.0.0';
|
|
208
|
+
const next_version = calculate_next_version(current_version, bump_type);
|
|
209
|
+
|
|
210
|
+
log?.debug(
|
|
211
|
+
` Predicted ${repo.library.name}: ${current_version} → ${next_version} (${bump_type})`,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
version: next_version,
|
|
216
|
+
bump_type,
|
|
217
|
+
};
|
|
218
|
+
};
|
|
@@ -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;
|