@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.
Files changed (190) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/dist/ModulesDetail.svelte +180 -0
  4. package/dist/ModulesDetail.svelte.d.ts +10 -0
  5. package/dist/ModulesDetail.svelte.d.ts.map +1 -0
  6. package/dist/ModulesNav.svelte +43 -0
  7. package/dist/ModulesNav.svelte.d.ts +11 -0
  8. package/dist/ModulesNav.svelte.d.ts.map +1 -0
  9. package/dist/ModulesPage.svelte +50 -0
  10. package/dist/ModulesPage.svelte.d.ts +9 -0
  11. package/dist/ModulesPage.svelte.d.ts.map +1 -0
  12. package/dist/PageFooter.svelte +15 -0
  13. package/dist/PageFooter.svelte.d.ts +19 -0
  14. package/dist/PageFooter.svelte.d.ts.map +1 -0
  15. package/dist/PageHeader.svelte +35 -0
  16. package/dist/PageHeader.svelte.d.ts +19 -0
  17. package/dist/PageHeader.svelte.d.ts.map +1 -0
  18. package/dist/PullRequestsDetail.svelte +53 -0
  19. package/dist/PullRequestsDetail.svelte.d.ts +10 -0
  20. package/dist/PullRequestsDetail.svelte.d.ts.map +1 -0
  21. package/dist/PullRequestsPage.svelte +47 -0
  22. package/dist/PullRequestsPage.svelte.d.ts +11 -0
  23. package/dist/PullRequestsPage.svelte.d.ts.map +1 -0
  24. package/dist/ReposTable.svelte +189 -0
  25. package/dist/ReposTable.svelte.d.ts +9 -0
  26. package/dist/ReposTable.svelte.d.ts.map +1 -0
  27. package/dist/ReposTree.svelte +88 -0
  28. package/dist/ReposTree.svelte.d.ts +11 -0
  29. package/dist/ReposTree.svelte.d.ts.map +1 -0
  30. package/dist/ReposTreeNav.svelte +55 -0
  31. package/dist/ReposTreeNav.svelte.d.ts +11 -0
  32. package/dist/ReposTreeNav.svelte.d.ts.map +1 -0
  33. package/dist/TablePage.svelte +46 -0
  34. package/dist/TablePage.svelte.d.ts +9 -0
  35. package/dist/TablePage.svelte.d.ts.map +1 -0
  36. package/dist/TreeItemPage.svelte +75 -0
  37. package/dist/TreeItemPage.svelte.d.ts +10 -0
  38. package/dist/TreeItemPage.svelte.d.ts.map +1 -0
  39. package/dist/TreePage.svelte +64 -0
  40. package/dist/TreePage.svelte.d.ts +9 -0
  41. package/dist/TreePage.svelte.d.ts.map +1 -0
  42. package/dist/changeset_generator.d.ts +38 -0
  43. package/dist/changeset_generator.d.ts.map +1 -0
  44. package/dist/changeset_generator.js +110 -0
  45. package/dist/changeset_reader.d.ts +75 -0
  46. package/dist/changeset_reader.d.ts.map +1 -0
  47. package/dist/changeset_reader.js +167 -0
  48. package/dist/constants.d.ts +9 -0
  49. package/dist/constants.d.ts.map +1 -0
  50. package/dist/constants.js +8 -0
  51. package/dist/dependency_graph.d.ts +120 -0
  52. package/dist/dependency_graph.d.ts.map +1 -0
  53. package/dist/dependency_graph.js +341 -0
  54. package/dist/dependency_updater.d.ts +46 -0
  55. package/dist/dependency_updater.d.ts.map +1 -0
  56. package/dist/dependency_updater.js +213 -0
  57. package/dist/fetch_repo_data.d.ts +19 -0
  58. package/dist/fetch_repo_data.d.ts.map +1 -0
  59. package/dist/fetch_repo_data.js +49 -0
  60. package/dist/fs_fetch_value_cache.d.ts +24 -0
  61. package/dist/fs_fetch_value_cache.d.ts.map +1 -0
  62. package/dist/fs_fetch_value_cache.js +61 -0
  63. package/dist/git_operations.d.ts +54 -0
  64. package/dist/git_operations.d.ts.map +1 -0
  65. package/dist/git_operations.js +144 -0
  66. package/dist/github.d.ts +91 -0
  67. package/dist/github.d.ts.map +1 -0
  68. package/dist/github.js +94 -0
  69. package/dist/github_helpers.d.ts +10 -0
  70. package/dist/github_helpers.d.ts.map +1 -0
  71. package/dist/github_helpers.js +13 -0
  72. package/dist/gitops_analyze.task.d.ts +17 -0
  73. package/dist/gitops_analyze.task.d.ts.map +1 -0
  74. package/dist/gitops_analyze.task.js +188 -0
  75. package/dist/gitops_config.d.ts +56 -0
  76. package/dist/gitops_config.d.ts.map +1 -0
  77. package/dist/gitops_config.js +63 -0
  78. package/dist/gitops_plan.task.d.ts +28 -0
  79. package/dist/gitops_plan.task.d.ts.map +1 -0
  80. package/dist/gitops_plan.task.js +217 -0
  81. package/dist/gitops_publish.task.d.ts +29 -0
  82. package/dist/gitops_publish.task.d.ts.map +1 -0
  83. package/dist/gitops_publish.task.js +178 -0
  84. package/dist/gitops_sync.task.d.ts +18 -0
  85. package/dist/gitops_sync.task.d.ts.map +1 -0
  86. package/dist/gitops_sync.task.js +95 -0
  87. package/dist/gitops_task_helpers.d.ts +63 -0
  88. package/dist/gitops_task_helpers.d.ts.map +1 -0
  89. package/dist/gitops_task_helpers.js +84 -0
  90. package/dist/gitops_validate.task.d.ts +12 -0
  91. package/dist/gitops_validate.task.d.ts.map +1 -0
  92. package/dist/gitops_validate.task.js +210 -0
  93. package/dist/graph_validation.d.ts +39 -0
  94. package/dist/graph_validation.d.ts.map +1 -0
  95. package/dist/graph_validation.js +79 -0
  96. package/dist/local_repo.d.ts +84 -0
  97. package/dist/local_repo.d.ts.map +1 -0
  98. package/dist/local_repo.js +213 -0
  99. package/dist/log_helpers.d.ts +43 -0
  100. package/dist/log_helpers.d.ts.map +1 -0
  101. package/dist/log_helpers.js +98 -0
  102. package/dist/multi_repo_publisher.d.ts +34 -0
  103. package/dist/multi_repo_publisher.d.ts.map +1 -0
  104. package/dist/multi_repo_publisher.js +364 -0
  105. package/dist/npm_install_helpers.d.ts +23 -0
  106. package/dist/npm_install_helpers.d.ts.map +1 -0
  107. package/dist/npm_install_helpers.js +60 -0
  108. package/dist/npm_registry.d.ts +46 -0
  109. package/dist/npm_registry.d.ts.map +1 -0
  110. package/dist/npm_registry.js +96 -0
  111. package/dist/operations.d.ts +409 -0
  112. package/dist/operations.d.ts.map +1 -0
  113. package/dist/operations.js +34 -0
  114. package/dist/operations_defaults.d.ts +19 -0
  115. package/dist/operations_defaults.d.ts.map +1 -0
  116. package/dist/operations_defaults.js +279 -0
  117. package/dist/output_helpers.d.ts +27 -0
  118. package/dist/output_helpers.d.ts.map +1 -0
  119. package/dist/output_helpers.js +39 -0
  120. package/dist/paths.d.ts +11 -0
  121. package/dist/paths.d.ts.map +1 -0
  122. package/dist/paths.js +10 -0
  123. package/dist/preflight_checks.d.ts +47 -0
  124. package/dist/preflight_checks.d.ts.map +1 -0
  125. package/dist/preflight_checks.js +181 -0
  126. package/dist/publishing_plan.d.ts +100 -0
  127. package/dist/publishing_plan.d.ts.map +1 -0
  128. package/dist/publishing_plan.js +353 -0
  129. package/dist/publishing_plan_helpers.d.ts +30 -0
  130. package/dist/publishing_plan_helpers.d.ts.map +1 -0
  131. package/dist/publishing_plan_helpers.js +112 -0
  132. package/dist/publishing_plan_logging.d.ts +18 -0
  133. package/dist/publishing_plan_logging.d.ts.map +1 -0
  134. package/dist/publishing_plan_logging.js +342 -0
  135. package/dist/repo.svelte.d.ts +52 -0
  136. package/dist/repo.svelte.d.ts.map +1 -0
  137. package/dist/repo.svelte.js +70 -0
  138. package/dist/repo_ops.d.ts +57 -0
  139. package/dist/repo_ops.d.ts.map +1 -0
  140. package/dist/repo_ops.js +167 -0
  141. package/dist/resolved_gitops_config.d.ts +9 -0
  142. package/dist/resolved_gitops_config.d.ts.map +1 -0
  143. package/dist/resolved_gitops_config.js +12 -0
  144. package/dist/semver.d.ts +24 -0
  145. package/dist/semver.d.ts.map +1 -0
  146. package/dist/semver.js +140 -0
  147. package/dist/serialization_types.d.ts +57 -0
  148. package/dist/serialization_types.d.ts.map +1 -0
  149. package/dist/serialization_types.js +40 -0
  150. package/dist/version_utils.d.ts +48 -0
  151. package/dist/version_utils.d.ts.map +1 -0
  152. package/dist/version_utils.js +125 -0
  153. package/package.json +107 -0
  154. package/src/lib/changeset_generator.ts +162 -0
  155. package/src/lib/changeset_reader.ts +218 -0
  156. package/src/lib/constants.ts +8 -0
  157. package/src/lib/dependency_graph.ts +423 -0
  158. package/src/lib/dependency_updater.ts +297 -0
  159. package/src/lib/fetch_repo_data.ts +64 -0
  160. package/src/lib/fs_fetch_value_cache.ts +75 -0
  161. package/src/lib/git_operations.ts +208 -0
  162. package/src/lib/github.ts +128 -0
  163. package/src/lib/github_helpers.ts +31 -0
  164. package/src/lib/gitops_analyze.task.ts +261 -0
  165. package/src/lib/gitops_config.ts +123 -0
  166. package/src/lib/gitops_plan.task.ts +272 -0
  167. package/src/lib/gitops_publish.task.ts +227 -0
  168. package/src/lib/gitops_sync.task.ts +109 -0
  169. package/src/lib/gitops_task_helpers.ts +126 -0
  170. package/src/lib/gitops_validate.task.ts +248 -0
  171. package/src/lib/graph_validation.ts +109 -0
  172. package/src/lib/local_repo.ts +359 -0
  173. package/src/lib/log_helpers.ts +147 -0
  174. package/src/lib/multi_repo_publisher.ts +464 -0
  175. package/src/lib/npm_install_helpers.ts +85 -0
  176. package/src/lib/npm_registry.ts +143 -0
  177. package/src/lib/operations.ts +334 -0
  178. package/src/lib/operations_defaults.ts +335 -0
  179. package/src/lib/output_helpers.ts +64 -0
  180. package/src/lib/paths.ts +11 -0
  181. package/src/lib/preflight_checks.ts +269 -0
  182. package/src/lib/publishing_plan.ts +531 -0
  183. package/src/lib/publishing_plan_helpers.ts +145 -0
  184. package/src/lib/publishing_plan_logging.ts +470 -0
  185. package/src/lib/repo.svelte.ts +95 -0
  186. package/src/lib/repo_ops.ts +213 -0
  187. package/src/lib/resolved_gitops_config.ts +27 -0
  188. package/src/lib/semver.ts +166 -0
  189. package/src/lib/serialization_types.ts +90 -0
  190. package/src/lib/version_utils.ts +150 -0
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Production implementations of operations interfaces.
3
+ *
4
+ * Provides real git, npm, fs, and build operations for production use.
5
+ * For interface definitions and dependency injection pattern, see `operations.ts`.
6
+ */
7
+ import { spawn, spawn_out } from '@fuzdev/fuz_util/process.js';
8
+ import { readFile, writeFile } from 'node:fs/promises';
9
+ import { git_checkout } from '@fuzdev/fuz_util/git.js';
10
+ import { EMPTY_OBJECT } from '@fuzdev/fuz_util/object.js';
11
+ import { has_changesets, read_changesets, predict_next_version } from './changeset_reader.js';
12
+ import { wait_for_package, check_package_available } from './npm_registry.js';
13
+ import { run_preflight_checks } from './preflight_checks.js';
14
+ import { git_add, git_commit, git_add_and_commit, git_tag, git_push_tag, git_has_changes, git_get_changed_files, git_has_file_changed, git_stash, git_stash_pop, git_switch_branch, git_current_branch_name_required, git_current_commit_hash_required, git_check_clean_workspace_as_boolean, git_has_remote, } from './git_operations.js';
15
+ /** Wrap an async function that returns a value */
16
+ const wrap_with_value = async (fn) => {
17
+ try {
18
+ const value = await fn();
19
+ return { ok: true, value };
20
+ }
21
+ catch (error) {
22
+ return { ok: false, message: String(error) };
23
+ }
24
+ };
25
+ /** Wrap an async function, ignoring its return value */
26
+ const wrap_void = async (fn) => {
27
+ try {
28
+ await fn();
29
+ return { ok: true };
30
+ }
31
+ catch (error) {
32
+ return { ok: false, message: String(error) };
33
+ }
34
+ };
35
+ export const default_changeset_operations = {
36
+ has_changesets: async (options) => {
37
+ const { repo } = options;
38
+ return wrap_with_value(() => has_changesets(repo));
39
+ },
40
+ read_changesets: async (options) => {
41
+ const { repo, log } = options;
42
+ return wrap_with_value(() => read_changesets(repo, log));
43
+ },
44
+ predict_next_version: async (options) => {
45
+ const { repo, log } = options;
46
+ try {
47
+ const result = await predict_next_version(repo, log);
48
+ if (result === null) {
49
+ return null;
50
+ }
51
+ return { ok: true, ...result };
52
+ }
53
+ catch (error) {
54
+ return { ok: false, message: String(error) };
55
+ }
56
+ },
57
+ };
58
+ export const default_git_operations = {
59
+ // Core git info
60
+ current_branch_name: async (options) => {
61
+ const { cwd } = options ?? EMPTY_OBJECT;
62
+ return wrap_with_value(() => git_current_branch_name_required(cwd ? { cwd } : undefined));
63
+ },
64
+ current_commit_hash: async (options) => {
65
+ const { branch, cwd } = options ?? EMPTY_OBJECT;
66
+ return wrap_with_value(() => git_current_commit_hash_required(branch, cwd ? { cwd } : undefined));
67
+ },
68
+ check_clean_workspace: async (options) => {
69
+ const { cwd } = options ?? EMPTY_OBJECT;
70
+ return wrap_with_value(() => git_check_clean_workspace_as_boolean(cwd ? { cwd } : undefined));
71
+ },
72
+ // Branch operations
73
+ checkout: async (options) => {
74
+ const { branch, cwd } = options;
75
+ return wrap_void(() => git_checkout(branch, cwd ? { cwd } : undefined));
76
+ },
77
+ pull: async (options) => {
78
+ const { origin, branch, cwd } = options ?? EMPTY_OBJECT;
79
+ return wrap_void(() => spawn('git', ['pull', origin || 'origin', branch || ''], cwd ? { cwd } : undefined));
80
+ },
81
+ switch_branch: async (options) => {
82
+ const { branch, pull, cwd } = options;
83
+ return wrap_void(() => git_switch_branch(branch, pull, cwd ? { cwd } : undefined));
84
+ },
85
+ has_remote: async (options) => {
86
+ const { remote, cwd } = options ?? EMPTY_OBJECT;
87
+ return wrap_with_value(() => git_has_remote(remote, cwd ? { cwd } : undefined));
88
+ },
89
+ // Staging and committing
90
+ add: async (options) => {
91
+ const { files, cwd } = options;
92
+ return wrap_void(() => git_add(files, cwd ? { cwd } : undefined));
93
+ },
94
+ commit: async (options) => {
95
+ const { message, cwd } = options;
96
+ return wrap_void(() => git_commit(message, cwd ? { cwd } : undefined));
97
+ },
98
+ add_and_commit: async (options) => {
99
+ const { files, message, cwd } = options;
100
+ return wrap_void(() => git_add_and_commit(files, message, cwd ? { cwd } : undefined));
101
+ },
102
+ has_changes: async (options) => {
103
+ const { cwd } = options ?? EMPTY_OBJECT;
104
+ return wrap_with_value(() => git_has_changes(cwd ? { cwd } : undefined));
105
+ },
106
+ get_changed_files: async (options) => {
107
+ const { cwd } = options ?? EMPTY_OBJECT;
108
+ return wrap_with_value(() => git_get_changed_files(cwd ? { cwd } : undefined));
109
+ },
110
+ // Tagging
111
+ tag: async (options) => {
112
+ const { tag_name, message, cwd } = options;
113
+ return wrap_void(() => git_tag(tag_name, message, cwd ? { cwd } : undefined));
114
+ },
115
+ push_tag: async (options) => {
116
+ const { tag_name, origin, cwd } = options;
117
+ return wrap_void(() => git_push_tag(tag_name, origin, cwd ? { cwd } : undefined));
118
+ },
119
+ // Stashing
120
+ stash: async (options) => {
121
+ const { message, cwd } = options ?? EMPTY_OBJECT;
122
+ return wrap_void(() => git_stash(message, cwd ? { cwd } : undefined));
123
+ },
124
+ stash_pop: async (options) => {
125
+ const { cwd } = options ?? EMPTY_OBJECT;
126
+ return wrap_void(() => git_stash_pop(cwd ? { cwd } : undefined));
127
+ },
128
+ // File change detection
129
+ has_file_changed: async (options) => {
130
+ const { from_commit, to_commit, file_path, cwd } = options;
131
+ return wrap_with_value(() => git_has_file_changed(from_commit, to_commit, file_path, cwd ? { cwd } : undefined));
132
+ },
133
+ };
134
+ export const default_process_operations = {
135
+ spawn: async (options) => {
136
+ const { cmd, args, spawn_options } = options;
137
+ try {
138
+ const spawned = await spawn_out(cmd, args, spawn_options);
139
+ if (spawned.result.ok) {
140
+ return {
141
+ ok: true,
142
+ stdout: spawned.stdout || undefined,
143
+ stderr: spawned.stderr || undefined,
144
+ };
145
+ }
146
+ else {
147
+ return {
148
+ ok: false,
149
+ message: 'Command failed',
150
+ stderr: spawned.stderr || undefined,
151
+ };
152
+ }
153
+ }
154
+ catch (error) {
155
+ return { ok: false, message: String(error) };
156
+ }
157
+ },
158
+ };
159
+ export const default_npm_operations = {
160
+ wait_for_package: async (options) => {
161
+ const { pkg, version, wait_options, log } = options;
162
+ try {
163
+ await wait_for_package(pkg, version, { ...wait_options, log });
164
+ return { ok: true };
165
+ }
166
+ catch (error) {
167
+ return { ok: false, message: String(error), timeout: true };
168
+ }
169
+ },
170
+ check_package_available: async (options) => {
171
+ const { pkg, version, log } = options;
172
+ return wrap_with_value(() => check_package_available(pkg, version, { log }));
173
+ },
174
+ check_auth: async () => {
175
+ try {
176
+ const result = await spawn_out('npm', ['whoami']);
177
+ if (result.stdout) {
178
+ const username = result.stdout.trim();
179
+ if (username) {
180
+ return { ok: true, username };
181
+ }
182
+ }
183
+ return { ok: false, message: 'Not logged in to npm' };
184
+ }
185
+ catch (error) {
186
+ return { ok: false, message: String(error) };
187
+ }
188
+ },
189
+ check_registry: async () => {
190
+ try {
191
+ const result = await spawn_out('npm', ['ping']);
192
+ if (result.stdout) {
193
+ return { ok: true };
194
+ }
195
+ return { ok: false, message: 'Failed to ping npm registry' };
196
+ }
197
+ catch (error) {
198
+ return { ok: false, message: String(error) };
199
+ }
200
+ },
201
+ install: async (options) => {
202
+ const { cwd } = options ?? EMPTY_OBJECT;
203
+ try {
204
+ const spawned = await spawn_out('npm', ['install'], cwd ? { cwd } : undefined);
205
+ if (spawned.result.ok) {
206
+ return { ok: true };
207
+ }
208
+ else {
209
+ return { ok: false, message: 'Install failed', stderr: spawned.stderr || undefined };
210
+ }
211
+ }
212
+ catch (error) {
213
+ return { ok: false, message: String(error) };
214
+ }
215
+ },
216
+ cache_clean: async () => {
217
+ try {
218
+ const spawned = await spawn_out('npm', ['cache', 'clean', '--force']);
219
+ if (spawned.result.ok) {
220
+ return { ok: true };
221
+ }
222
+ else {
223
+ return { ok: false, message: 'Cache clean failed' };
224
+ }
225
+ }
226
+ catch (error) {
227
+ return { ok: false, message: String(error) };
228
+ }
229
+ },
230
+ };
231
+ export const default_preflight_operations = {
232
+ run_preflight_checks: async (options) => {
233
+ return run_preflight_checks(options);
234
+ },
235
+ };
236
+ export const default_fs_operations = {
237
+ readFile: async (options) => {
238
+ const { path, encoding } = options;
239
+ return wrap_with_value(() => readFile(path, encoding));
240
+ },
241
+ writeFile: async (options) => {
242
+ const { path, content } = options;
243
+ return wrap_void(() => writeFile(path, content));
244
+ },
245
+ };
246
+ export const default_build_operations = {
247
+ build_package: async (options) => {
248
+ const { repo, log } = options;
249
+ try {
250
+ log?.info(` Building ${repo.library.name}...`);
251
+ const spawned = await spawn_out('gro', ['build'], { cwd: repo.repo_dir });
252
+ if (spawned.result.ok) {
253
+ return { ok: true };
254
+ }
255
+ else {
256
+ return {
257
+ ok: false,
258
+ message: 'Build failed',
259
+ output: spawned.stderr || spawned.stdout || 'Build failed',
260
+ };
261
+ }
262
+ }
263
+ catch (error) {
264
+ return { ok: false, message: String(error) };
265
+ }
266
+ },
267
+ };
268
+ /**
269
+ * Combined default operations for all gitops functionality.
270
+ */
271
+ export const default_gitops_operations = {
272
+ changeset: default_changeset_operations,
273
+ git: default_git_operations,
274
+ process: default_process_operations,
275
+ npm: default_npm_operations,
276
+ preflight: default_preflight_operations,
277
+ fs: default_fs_operations,
278
+ build: default_build_operations,
279
+ };
@@ -0,0 +1,27 @@
1
+ import type { Logger } from '@fuzdev/fuz_util/log.js';
2
+ export type OutputFormat = 'stdout' | 'json' | 'markdown';
3
+ export interface OutputOptions {
4
+ format: OutputFormat;
5
+ outfile?: string;
6
+ log?: Logger;
7
+ }
8
+ export interface OutputFormatters<T> {
9
+ json: (data: T) => string;
10
+ markdown: (data: T) => Array<string>;
11
+ /**
12
+ * This function should call log methods directly for colored/styled output.
13
+ */
14
+ stdout: (data: T, log: Logger) => void;
15
+ }
16
+ /**
17
+ * Formats data and outputs to file or stdout based on options.
18
+ *
19
+ * Supports three formats:
20
+ * - stdout: Uses logger for colored/styled output (cannot use with --outfile)
21
+ * - json: Stringified JSON
22
+ * - markdown: Formatted markdown text
23
+ *
24
+ * @throws {Error} if stdout format used with outfile, or if logger missing for stdout
25
+ */
26
+ export declare const format_and_output: <T>(data: T, formatters: OutputFormatters<T>, options: OutputOptions) => Promise<void>;
27
+ //# sourceMappingURL=output_helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/output_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAE1D,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAClC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAC1B,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;IACrC;;OAEG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAU,CAAC,EACxC,MAAM,CAAC,EACP,YAAY,gBAAgB,CAAC,CAAC,CAAC,EAC/B,SAAS,aAAa,KACpB,OAAO,CAAC,IAAI,CA6Bd,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ /**
3
+ * Formats data and outputs to file or stdout based on options.
4
+ *
5
+ * Supports three formats:
6
+ * - stdout: Uses logger for colored/styled output (cannot use with --outfile)
7
+ * - json: Stringified JSON
8
+ * - markdown: Formatted markdown text
9
+ *
10
+ * @throws {Error} if stdout format used with outfile, or if logger missing for stdout
11
+ */
12
+ export const format_and_output = async (data, formatters, options) => {
13
+ const { format, outfile, log } = options;
14
+ // Handle stdout format (special case - uses logger directly)
15
+ if (format === 'stdout') {
16
+ if (outfile) {
17
+ throw new Error('--outfile is not supported with stdout format, use json or markdown');
18
+ }
19
+ if (!log) {
20
+ throw new Error('Logger is required for stdout format');
21
+ }
22
+ formatters.stdout(data, log);
23
+ return;
24
+ }
25
+ // Format data
26
+ const content = format === 'json' ? formatters.json(data) : formatters.markdown(data).join('\n');
27
+ // Output to file or log
28
+ if (outfile) {
29
+ await writeFile(outfile, content);
30
+ log?.info(`Output written to ${outfile}`);
31
+ }
32
+ else {
33
+ // Log line by line for better formatting
34
+ const lines = content.split('\n');
35
+ for (const line of lines) {
36
+ log?.info(line);
37
+ }
38
+ }
39
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Base directory for all gitops-generated files.
3
+ */
4
+ export declare const GITOPS_OUTPUT_DIR = ".gro/fuz_gitops";
5
+ /**
6
+ * Default repos directory relative to gitops config file.
7
+ * Resolves to the parent of the directory with the config
8
+ * (e.g., ~/dev/repo/gitops.config.ts resolves to ~/dev/).
9
+ */
10
+ export declare const DEFAULT_REPOS_DIR = "..";
11
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/paths.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC"}
package/dist/paths.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Base directory for all gitops-generated files.
3
+ */
4
+ export const GITOPS_OUTPUT_DIR = '.gro/fuz_gitops';
5
+ /**
6
+ * Default repos directory relative to gitops config file.
7
+ * Resolves to the parent of the directory with the config
8
+ * (e.g., ~/dev/repo/gitops.config.ts resolves to ~/dev/).
9
+ */
10
+ export const DEFAULT_REPOS_DIR = '..';
@@ -0,0 +1,47 @@
1
+ import type { Logger } from '@fuzdev/fuz_util/log.js';
2
+ import type { LocalRepo } from './local_repo.js';
3
+ import type { GitOperations, NpmOperations, BuildOperations, ChangesetOperations } from './operations.js';
4
+ export interface PreflightOptions {
5
+ skip_changesets?: boolean;
6
+ skip_build_validation?: boolean;
7
+ required_branch?: string;
8
+ check_remote?: boolean;
9
+ estimate_time?: boolean;
10
+ log?: Logger;
11
+ }
12
+ export interface PreflightResult {
13
+ ok: boolean;
14
+ warnings: Array<string>;
15
+ errors: Array<string>;
16
+ repos_with_changesets: Set<string>;
17
+ repos_without_changesets: Set<string>;
18
+ estimated_duration?: number;
19
+ npm_username?: string;
20
+ }
21
+ export interface RunPreflightChecksOptions {
22
+ repos: Array<LocalRepo>;
23
+ preflight_options?: PreflightOptions;
24
+ git_ops?: GitOperations;
25
+ npm_ops?: NpmOperations;
26
+ build_ops?: BuildOperations;
27
+ changeset_ops?: ChangesetOperations;
28
+ }
29
+ /**
30
+ * Validates all requirements before publishing can proceed.
31
+ *
32
+ * Performs comprehensive pre-flight validation:
33
+ * - Clean workspaces (100% clean required - no uncommitted changes)
34
+ * - Correct branch (usually main)
35
+ * - Changesets present (unless skip_changesets=true)
36
+ * - Builds successful (fail-fast to prevent broken state)
37
+ * - Git remote reachability
38
+ * - NPM authentication with username
39
+ * - NPM registry connectivity
40
+ *
41
+ * Build validation runs BEFORE any publishing to prevent the scenario where
42
+ * version is bumped but build fails, leaving repo in broken state.
43
+ *
44
+ * @returns result with ok=false if any errors, plus warnings and detailed status
45
+ */
46
+ export declare const run_preflight_checks: ({ repos, preflight_options, git_ops, npm_ops, build_ops, changeset_ops, }: RunPreflightChecksOptions) => Promise<PreflightResult>;
47
+ //# sourceMappingURL=preflight_checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight_checks.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/preflight_checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAKpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EACX,aAAa,EACb,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,MAAM,iBAAiB,CAAC;AAQzB,MAAM,WAAW,gBAAgB;IAChC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtB,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,wBAAwB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACzC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACxB,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;IACrC,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,oBAAoB,GAAU,2EAOxC,yBAAyB,KAAG,OAAO,CAAC,eAAe,CAwLrD,CAAC"}
@@ -0,0 +1,181 @@
1
+ import { spawn_out } from '@fuzdev/fuz_util/process.js';
2
+ import { styleText as st } from 'node:util';
3
+ import { default_git_operations, default_npm_operations, default_build_operations, default_changeset_operations, } from './operations_defaults.js';
4
+ /**
5
+ * Validates all requirements before publishing can proceed.
6
+ *
7
+ * Performs comprehensive pre-flight validation:
8
+ * - Clean workspaces (100% clean required - no uncommitted changes)
9
+ * - Correct branch (usually main)
10
+ * - Changesets present (unless skip_changesets=true)
11
+ * - Builds successful (fail-fast to prevent broken state)
12
+ * - Git remote reachability
13
+ * - NPM authentication with username
14
+ * - NPM registry connectivity
15
+ *
16
+ * Build validation runs BEFORE any publishing to prevent the scenario where
17
+ * version is bumped but build fails, leaving repo in broken state.
18
+ *
19
+ * @returns result with ok=false if any errors, plus warnings and detailed status
20
+ */
21
+ export const run_preflight_checks = async ({ repos, preflight_options = {}, git_ops = default_git_operations, npm_ops = default_npm_operations, build_ops = default_build_operations, changeset_ops = default_changeset_operations, }) => {
22
+ const { skip_changesets = false, skip_build_validation = false, required_branch = 'main', check_remote = true, estimate_time = true, log, } = preflight_options;
23
+ const warnings = [];
24
+ const errors = [];
25
+ const repos_with_changesets = new Set();
26
+ const repos_without_changesets = new Set();
27
+ let npm_username;
28
+ let estimated_duration;
29
+ log?.info(st('cyan', '✅ Running preflight checks...'));
30
+ // 1. Check clean workspaces - must be 100% clean before publishing
31
+ log?.info(' Checking workspace cleanliness...');
32
+ for (const repo of repos) {
33
+ const clean_result = await git_ops.check_clean_workspace({ cwd: repo.repo_dir }); // eslint-disable-line no-await-in-loop
34
+ if (!clean_result.ok) {
35
+ errors.push(`${repo.library.name} failed workspace check: ${clean_result.message}`);
36
+ continue;
37
+ }
38
+ if (!clean_result.value) {
39
+ // Get list of changed files for better error message
40
+ const files_result = await git_ops.get_changed_files({ cwd: repo.repo_dir }); // eslint-disable-line no-await-in-loop
41
+ if (files_result.ok) {
42
+ // No filtering - workspace must be 100% clean
43
+ const unexpected_files = files_result.value;
44
+ if (unexpected_files.length > 0) {
45
+ errors.push(`${repo.library.name} has uncommitted changes in: ${unexpected_files.slice(0, 3).join(', ')}${unexpected_files.length > 3 ? ` and ${unexpected_files.length - 3} more` : ''}`);
46
+ }
47
+ }
48
+ else {
49
+ errors.push(`${repo.library.name} has uncommitted changes`);
50
+ }
51
+ }
52
+ }
53
+ // 2. Check correct branch
54
+ log?.info(` Checking branches (expecting ${required_branch})...`);
55
+ for (const repo of repos) {
56
+ const branch_result = await git_ops.current_branch_name({ cwd: repo.repo_dir }); // eslint-disable-line no-await-in-loop
57
+ if (!branch_result.ok) {
58
+ errors.push(`${repo.library.name} failed branch check: ${branch_result.message}`);
59
+ continue;
60
+ }
61
+ if (branch_result.value !== required_branch) {
62
+ errors.push(`${repo.library.name} is on branch '${branch_result.value}', expected '${required_branch}'`);
63
+ }
64
+ }
65
+ // 3. Check changesets (unless skipped)
66
+ if (!skip_changesets) {
67
+ log?.info(' Checking for changesets...');
68
+ for (const repo of repos) {
69
+ const has_result = await changeset_ops.has_changesets({ repo }); // eslint-disable-line no-await-in-loop
70
+ if (!has_result.ok) {
71
+ errors.push(`${repo.library.name} failed changeset check: ${has_result.message}`);
72
+ continue;
73
+ }
74
+ if (has_result.value) {
75
+ repos_with_changesets.add(repo.library.name);
76
+ }
77
+ else {
78
+ repos_without_changesets.add(repo.library.name);
79
+ warnings.push(`${repo.library.name} has no changesets`);
80
+ }
81
+ }
82
+ if (repos_without_changesets.size > 0) {
83
+ log?.warn(st('yellow', ` ⚠️ ${repos_without_changesets.size} packages have no changesets`));
84
+ }
85
+ }
86
+ // 4. Validate builds for packages with changesets
87
+ if (!skip_build_validation && repos_with_changesets.size > 0) {
88
+ log?.info(st('cyan', ` Validating builds for ${repos_with_changesets.size} package(s)...`));
89
+ const repos_to_build = repos.filter((repo) => repos_with_changesets.has(repo.library.name));
90
+ for (let i = 0; i < repos_to_build.length; i++) {
91
+ const repo = repos_to_build[i];
92
+ log?.info(st('dim', ` [${i + 1}/${repos_to_build.length}] Building ${repo.library.name}...`));
93
+ const build_result = await build_ops.build_package({ repo, log }); // eslint-disable-line no-await-in-loop
94
+ if (!build_result.ok) {
95
+ errors.push(`${repo.library.name} failed to build: ${build_result.output || build_result.message || 'unknown error'}`);
96
+ }
97
+ else {
98
+ log?.info(st('dim', ` ✓ ${repo.library.name} built successfully`));
99
+ }
100
+ }
101
+ if (errors.some((err) => err.includes('failed to build'))) {
102
+ log?.error(st('red', ' ❌ Build validation failed - fix build errors before publishing'));
103
+ }
104
+ else {
105
+ log?.info(st('green', ' ✓ All builds validated successfully'));
106
+ }
107
+ }
108
+ // 5. Check git remote reachability (skip in tests when check_remote is false)
109
+ if (check_remote && repos.length > 0) {
110
+ log?.info(' Checking git remote connectivity...');
111
+ // Only check first repo to avoid slowing down tests with multiple remote checks
112
+ const remote_result = await check_git_remote(repos[0].repo_dir);
113
+ if (!remote_result.ok) {
114
+ warnings.push(`git remote may not be reachable - ${remote_result.message}`);
115
+ }
116
+ }
117
+ // 6. Check npm authentication with username
118
+ log?.info(' Checking npm authentication...');
119
+ const npm_auth_result = await npm_ops.check_auth();
120
+ if (!npm_auth_result.ok) {
121
+ errors.push(`npm authentication failed: ${npm_auth_result.message || 'not logged in'}`);
122
+ }
123
+ else {
124
+ npm_username = npm_auth_result.username;
125
+ log?.info(st('dim', ` Logged in as: ${npm_username}`));
126
+ }
127
+ // 7. Check network connectivity (npm registry)
128
+ log?.info(' Checking npm registry connectivity...');
129
+ const registry_result = await npm_ops.check_registry();
130
+ if (!registry_result.ok) {
131
+ warnings.push(`npm registry check failed: ${registry_result.message}`);
132
+ }
133
+ // 8. Estimate total publish time
134
+ if (estimate_time) {
135
+ const packages_to_publish = repos_with_changesets.size;
136
+ if (packages_to_publish > 0) {
137
+ // Rough estimate: 30s per package + 10s per package for NPM propagation
138
+ estimated_duration = packages_to_publish * 40;
139
+ log?.info(st('dim', ` Estimated publish time: ~${Math.ceil(estimated_duration / 60)} minutes for ${packages_to_publish} package(s)`));
140
+ }
141
+ }
142
+ // Report results
143
+ const ok = errors.length === 0;
144
+ if (errors.length > 0) {
145
+ log?.error(st('red', `\n❌ Preflight checks failed with ${errors.length} errors:`));
146
+ for (const error of errors) {
147
+ log?.error(` - ${error}`);
148
+ }
149
+ }
150
+ if (warnings.length > 0) {
151
+ log?.warn(st('yellow', `\n⚠️ Preflight checks found ${warnings.length} warnings:`));
152
+ for (const warning of warnings) {
153
+ log?.warn(` - ${warning}`);
154
+ }
155
+ }
156
+ if (ok) {
157
+ log?.info(st('green', '\n✨ All preflight checks passed!'));
158
+ }
159
+ return {
160
+ ok,
161
+ warnings,
162
+ errors,
163
+ repos_with_changesets,
164
+ repos_without_changesets,
165
+ estimated_duration,
166
+ npm_username,
167
+ };
168
+ };
169
+ const check_git_remote = async (cwd) => {
170
+ try {
171
+ // Try to fetch refs from remote without downloading objects
172
+ const result = await spawn_out('git', ['ls-remote', '--heads', 'origin'], { cwd });
173
+ if (result.stdout || result.stderr) {
174
+ return { ok: true };
175
+ }
176
+ return { ok: false, message: 'No response from git remote' };
177
+ }
178
+ catch (error) {
179
+ return { ok: false, message: String(error) };
180
+ }
181
+ };