@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,64 @@
1
+ import type {Logger} from '@fuzdev/fuz_util/log.js';
2
+ import {writeFile} from 'node:fs/promises';
3
+
4
+ export type OutputFormat = 'stdout' | 'json' | 'markdown';
5
+
6
+ export interface OutputOptions {
7
+ format: OutputFormat;
8
+ outfile?: string;
9
+ log?: Logger;
10
+ }
11
+
12
+ export interface OutputFormatters<T> {
13
+ json: (data: T) => string;
14
+ markdown: (data: T) => Array<string>;
15
+ /**
16
+ * This function should call log methods directly for colored/styled output.
17
+ */
18
+ stdout: (data: T, log: Logger) => void;
19
+ }
20
+
21
+ /**
22
+ * Formats data and outputs to file or stdout based on options.
23
+ *
24
+ * Supports three formats:
25
+ * - stdout: Uses logger for colored/styled output (cannot use with --outfile)
26
+ * - json: Stringified JSON
27
+ * - markdown: Formatted markdown text
28
+ *
29
+ * @throws {Error} if stdout format used with outfile, or if logger missing for stdout
30
+ */
31
+ export const format_and_output = async <T>(
32
+ data: T,
33
+ formatters: OutputFormatters<T>,
34
+ options: OutputOptions,
35
+ ): Promise<void> => {
36
+ const {format, outfile, log} = options;
37
+
38
+ // Handle stdout format (special case - uses logger directly)
39
+ if (format === 'stdout') {
40
+ if (outfile) {
41
+ throw new Error('--outfile is not supported with stdout format, use json or markdown');
42
+ }
43
+ if (!log) {
44
+ throw new Error('Logger is required for stdout format');
45
+ }
46
+ formatters.stdout(data, log);
47
+ return;
48
+ }
49
+
50
+ // Format data
51
+ const content = format === 'json' ? formatters.json(data) : formatters.markdown(data).join('\n');
52
+
53
+ // Output to file or log
54
+ if (outfile) {
55
+ await writeFile(outfile, content);
56
+ log?.info(`Output written to ${outfile}`);
57
+ } else {
58
+ // Log line by line for better formatting
59
+ const lines = content.split('\n');
60
+ for (const line of lines) {
61
+ log?.info(line);
62
+ }
63
+ }
64
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Base directory for all gitops-generated files.
3
+ */
4
+ export const GITOPS_OUTPUT_DIR = '.gro/fuz_gitops';
5
+
6
+ /**
7
+ * Default repos directory relative to gitops config file.
8
+ * Resolves to the parent of the directory with the config
9
+ * (e.g., ~/dev/repo/gitops.config.ts resolves to ~/dev/).
10
+ */
11
+ export const DEFAULT_REPOS_DIR = '..';
@@ -0,0 +1,269 @@
1
+ import type {Logger} from '@fuzdev/fuz_util/log.js';
2
+ import type {Result} from '@fuzdev/fuz_util/result.js';
3
+ import {spawn_out} from '@fuzdev/fuz_util/process.js';
4
+ import {styleText as st} from 'node:util';
5
+
6
+ import type {LocalRepo} from './local_repo.js';
7
+ import type {
8
+ GitOperations,
9
+ NpmOperations,
10
+ BuildOperations,
11
+ ChangesetOperations,
12
+ } from './operations.js';
13
+ import {
14
+ default_git_operations,
15
+ default_npm_operations,
16
+ default_build_operations,
17
+ default_changeset_operations,
18
+ } from './operations_defaults.js';
19
+
20
+ export interface PreflightOptions {
21
+ skip_changesets?: boolean;
22
+ skip_build_validation?: boolean; // Skip build validation (useful for tests)
23
+ required_branch?: string;
24
+ check_remote?: boolean; // Check if git remote is reachable
25
+ estimate_time?: boolean; // Estimate total publish time
26
+ log?: Logger;
27
+ }
28
+
29
+ export interface PreflightResult {
30
+ ok: boolean;
31
+ warnings: Array<string>;
32
+ errors: Array<string>;
33
+ repos_with_changesets: Set<string>;
34
+ repos_without_changesets: Set<string>;
35
+ estimated_duration?: number; // In seconds
36
+ npm_username?: string;
37
+ }
38
+
39
+ export interface RunPreflightChecksOptions {
40
+ repos: Array<LocalRepo>;
41
+ preflight_options?: PreflightOptions;
42
+ git_ops?: GitOperations;
43
+ npm_ops?: NpmOperations;
44
+ build_ops?: BuildOperations;
45
+ changeset_ops?: ChangesetOperations;
46
+ }
47
+
48
+ /**
49
+ * Validates all requirements before publishing can proceed.
50
+ *
51
+ * Performs comprehensive pre-flight validation:
52
+ * - Clean workspaces (100% clean required - no uncommitted changes)
53
+ * - Correct branch (usually main)
54
+ * - Changesets present (unless skip_changesets=true)
55
+ * - Builds successful (fail-fast to prevent broken state)
56
+ * - Git remote reachability
57
+ * - NPM authentication with username
58
+ * - NPM registry connectivity
59
+ *
60
+ * Build validation runs BEFORE any publishing to prevent the scenario where
61
+ * version is bumped but build fails, leaving repo in broken state.
62
+ *
63
+ * @returns result with ok=false if any errors, plus warnings and detailed status
64
+ */
65
+ export const run_preflight_checks = async ({
66
+ repos,
67
+ preflight_options = {},
68
+ git_ops = default_git_operations,
69
+ npm_ops = default_npm_operations,
70
+ build_ops = default_build_operations,
71
+ changeset_ops = default_changeset_operations,
72
+ }: RunPreflightChecksOptions): Promise<PreflightResult> => {
73
+ const {
74
+ skip_changesets = false,
75
+ skip_build_validation = false,
76
+ required_branch = 'main',
77
+ check_remote = true,
78
+ estimate_time = true,
79
+ log,
80
+ } = preflight_options;
81
+
82
+ const warnings: Array<string> = [];
83
+ const errors: Array<string> = [];
84
+ const repos_with_changesets: Set<string> = new Set();
85
+ const repos_without_changesets: Set<string> = new Set();
86
+ let npm_username: string | undefined;
87
+ let estimated_duration: number | undefined;
88
+
89
+ log?.info(st('cyan', '✅ Running preflight checks...'));
90
+
91
+ // 1. Check clean workspaces - must be 100% clean before publishing
92
+ log?.info(' Checking workspace cleanliness...');
93
+ for (const repo of repos) {
94
+ const clean_result = await git_ops.check_clean_workspace({cwd: repo.repo_dir}); // eslint-disable-line no-await-in-loop
95
+ if (!clean_result.ok) {
96
+ errors.push(`${repo.library.name} failed workspace check: ${clean_result.message}`);
97
+ continue;
98
+ }
99
+
100
+ if (!clean_result.value) {
101
+ // Get list of changed files for better error message
102
+ const files_result = await git_ops.get_changed_files({cwd: repo.repo_dir}); // eslint-disable-line no-await-in-loop
103
+ if (files_result.ok) {
104
+ // No filtering - workspace must be 100% clean
105
+ const unexpected_files = files_result.value;
106
+
107
+ if (unexpected_files.length > 0) {
108
+ errors.push(
109
+ `${repo.library.name} has uncommitted changes in: ${unexpected_files.slice(0, 3).join(', ')}${unexpected_files.length > 3 ? ` and ${unexpected_files.length - 3} more` : ''}`,
110
+ );
111
+ }
112
+ } else {
113
+ errors.push(`${repo.library.name} has uncommitted changes`);
114
+ }
115
+ }
116
+ }
117
+
118
+ // 2. Check correct branch
119
+ log?.info(` Checking branches (expecting ${required_branch})...`);
120
+ for (const repo of repos) {
121
+ const branch_result = await git_ops.current_branch_name({cwd: repo.repo_dir}); // eslint-disable-line no-await-in-loop
122
+ if (!branch_result.ok) {
123
+ errors.push(`${repo.library.name} failed branch check: ${branch_result.message}`);
124
+ continue;
125
+ }
126
+
127
+ if (branch_result.value !== required_branch) {
128
+ errors.push(
129
+ `${repo.library.name} is on branch '${branch_result.value}', expected '${required_branch}'`,
130
+ );
131
+ }
132
+ }
133
+
134
+ // 3. Check changesets (unless skipped)
135
+ if (!skip_changesets) {
136
+ log?.info(' Checking for changesets...');
137
+ for (const repo of repos) {
138
+ const has_result = await changeset_ops.has_changesets({repo}); // eslint-disable-line no-await-in-loop
139
+ if (!has_result.ok) {
140
+ errors.push(`${repo.library.name} failed changeset check: ${has_result.message}`);
141
+ continue;
142
+ }
143
+
144
+ if (has_result.value) {
145
+ repos_with_changesets.add(repo.library.name);
146
+ } else {
147
+ repos_without_changesets.add(repo.library.name);
148
+ warnings.push(`${repo.library.name} has no changesets`);
149
+ }
150
+ }
151
+
152
+ if (repos_without_changesets.size > 0) {
153
+ log?.warn(st('yellow', ` ⚠️ ${repos_without_changesets.size} packages have no changesets`));
154
+ }
155
+ }
156
+
157
+ // 4. Validate builds for packages with changesets
158
+ if (!skip_build_validation && repos_with_changesets.size > 0) {
159
+ log?.info(st('cyan', ` Validating builds for ${repos_with_changesets.size} package(s)...`));
160
+ const repos_to_build = repos.filter((repo) => repos_with_changesets.has(repo.library.name));
161
+
162
+ for (let i = 0; i < repos_to_build.length; i++) {
163
+ const repo = repos_to_build[i]!;
164
+ log?.info(
165
+ st('dim', ` [${i + 1}/${repos_to_build.length}] Building ${repo.library.name}...`),
166
+ );
167
+ const build_result = await build_ops.build_package({repo, log}); // eslint-disable-line no-await-in-loop
168
+ if (!build_result.ok) {
169
+ errors.push(
170
+ `${repo.library.name} failed to build: ${build_result.output || build_result.message || 'unknown error'}`,
171
+ );
172
+ } else {
173
+ log?.info(st('dim', ` ✓ ${repo.library.name} built successfully`));
174
+ }
175
+ }
176
+
177
+ if (errors.some((err) => err.includes('failed to build'))) {
178
+ log?.error(st('red', ' ❌ Build validation failed - fix build errors before publishing'));
179
+ } else {
180
+ log?.info(st('green', ' ✓ All builds validated successfully'));
181
+ }
182
+ }
183
+
184
+ // 5. Check git remote reachability (skip in tests when check_remote is false)
185
+ if (check_remote && repos.length > 0) {
186
+ log?.info(' Checking git remote connectivity...');
187
+ // Only check first repo to avoid slowing down tests with multiple remote checks
188
+ const remote_result = await check_git_remote(repos[0]!.repo_dir);
189
+ if (!remote_result.ok) {
190
+ warnings.push(`git remote may not be reachable - ${remote_result.message}`);
191
+ }
192
+ }
193
+
194
+ // 6. Check npm authentication with username
195
+ log?.info(' Checking npm authentication...');
196
+ const npm_auth_result = await npm_ops.check_auth();
197
+ if (!npm_auth_result.ok) {
198
+ errors.push(`npm authentication failed: ${npm_auth_result.message || 'not logged in'}`);
199
+ } else {
200
+ npm_username = npm_auth_result.username;
201
+ log?.info(st('dim', ` Logged in as: ${npm_username}`));
202
+ }
203
+
204
+ // 7. Check network connectivity (npm registry)
205
+ log?.info(' Checking npm registry connectivity...');
206
+ const registry_result = await npm_ops.check_registry();
207
+ if (!registry_result.ok) {
208
+ warnings.push(`npm registry check failed: ${registry_result.message}`);
209
+ }
210
+
211
+ // 8. Estimate total publish time
212
+ if (estimate_time) {
213
+ const packages_to_publish = repos_with_changesets.size;
214
+ if (packages_to_publish > 0) {
215
+ // Rough estimate: 30s per package + 10s per package for NPM propagation
216
+ estimated_duration = packages_to_publish * 40;
217
+ log?.info(
218
+ st(
219
+ 'dim',
220
+ ` Estimated publish time: ~${Math.ceil(estimated_duration / 60)} minutes for ${packages_to_publish} package(s)`,
221
+ ),
222
+ );
223
+ }
224
+ }
225
+
226
+ // Report results
227
+ const ok = errors.length === 0;
228
+
229
+ if (errors.length > 0) {
230
+ log?.error(st('red', `\n❌ Preflight checks failed with ${errors.length} errors:`));
231
+ for (const error of errors) {
232
+ log?.error(` - ${error}`);
233
+ }
234
+ }
235
+
236
+ if (warnings.length > 0) {
237
+ log?.warn(st('yellow', `\n⚠️ Preflight checks found ${warnings.length} warnings:`));
238
+ for (const warning of warnings) {
239
+ log?.warn(` - ${warning}`);
240
+ }
241
+ }
242
+
243
+ if (ok) {
244
+ log?.info(st('green', '\n✨ All preflight checks passed!'));
245
+ }
246
+
247
+ return {
248
+ ok,
249
+ warnings,
250
+ errors,
251
+ repos_with_changesets,
252
+ repos_without_changesets,
253
+ estimated_duration,
254
+ npm_username,
255
+ };
256
+ };
257
+
258
+ const check_git_remote = async (cwd: string): Promise<Result<object, {message: string}>> => {
259
+ try {
260
+ // Try to fetch refs from remote without downloading objects
261
+ const result = await spawn_out('git', ['ls-remote', '--heads', 'origin'], {cwd});
262
+ if (result.stdout || result.stderr) {
263
+ return {ok: true};
264
+ }
265
+ return {ok: false, message: 'No response from git remote'};
266
+ } catch (error) {
267
+ return {ok: false, message: String(error)};
268
+ }
269
+ };