@fuzdev/fuz_gitops 0.78.0 → 0.79.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 (196) hide show
  1. package/README.md +91 -17
  2. package/dist/ModulesDetail.svelte +22 -27
  3. package/dist/ModulesDetail.svelte.d.ts.map +1 -1
  4. package/dist/ModulesNav.svelte +8 -5
  5. package/dist/ModulesNav.svelte.d.ts.map +1 -1
  6. package/dist/ModulesPage.svelte +3 -5
  7. package/dist/ModulesPage.svelte.d.ts.map +1 -1
  8. package/dist/PageFooter.svelte +2 -2
  9. package/dist/PageHeader.svelte +6 -6
  10. package/dist/PageHeader.svelte.d.ts +0 -3
  11. package/dist/PageHeader.svelte.d.ts.map +1 -1
  12. package/dist/PullRequestsDetail.svelte +29 -20
  13. package/dist/PullRequestsDetail.svelte.d.ts.map +1 -1
  14. package/dist/PullRequestsPage.svelte +4 -6
  15. package/dist/PullRequestsPage.svelte.d.ts.map +1 -1
  16. package/dist/ReposTable.svelte +43 -55
  17. package/dist/ReposTable.svelte.d.ts.map +1 -1
  18. package/dist/ReposTree.svelte +9 -21
  19. package/dist/ReposTree.svelte.d.ts.map +1 -1
  20. package/dist/ReposTreeNav.svelte +9 -12
  21. package/dist/ReposTreeNav.svelte.d.ts.map +1 -1
  22. package/dist/TablePage.svelte +3 -5
  23. package/dist/TablePage.svelte.d.ts.map +1 -1
  24. package/dist/TreeItemPage.svelte +3 -5
  25. package/dist/TreeItemPage.svelte.d.ts.map +1 -1
  26. package/dist/TreePage.svelte +3 -5
  27. package/dist/TreePage.svelte.d.ts.map +1 -1
  28. package/dist/changeset_generator.d.ts.map +1 -1
  29. package/dist/changeset_generator.js +2 -2
  30. package/dist/changeset_reader.d.ts.map +1 -1
  31. package/dist/changeset_reader.js +3 -3
  32. package/dist/ci_reconcile.d.ts +6 -10
  33. package/dist/ci_reconcile.d.ts.map +1 -1
  34. package/dist/ci_reconcile.js +6 -8
  35. package/dist/dependency_graph.d.ts +26 -49
  36. package/dist/dependency_graph.d.ts.map +1 -1
  37. package/dist/dependency_graph.js +40 -83
  38. package/dist/dependency_updater.d.ts +1 -1
  39. package/dist/dependency_updater.d.ts.map +1 -1
  40. package/dist/dependency_updater.js +14 -9
  41. package/dist/fetch_repo_data.d.ts +34 -8
  42. package/dist/fetch_repo_data.d.ts.map +1 -1
  43. package/dist/fetch_repo_data.js +44 -22
  44. package/dist/fs_fetch_value_cache.d.ts +1 -1
  45. package/dist/fs_fetch_value_cache.d.ts.map +1 -1
  46. package/dist/fs_fetch_value_cache.js +3 -3
  47. package/dist/git_operations.d.ts +6 -49
  48. package/dist/git_operations.d.ts.map +1 -1
  49. package/dist/git_operations.js +13 -126
  50. package/dist/github.d.ts +17 -2
  51. package/dist/github.d.ts.map +1 -1
  52. package/dist/github.js +17 -8
  53. package/dist/github_helpers.d.ts.map +1 -1
  54. package/dist/github_helpers.js +3 -11
  55. package/dist/gitops_analyze.task.d.ts +24 -2
  56. package/dist/gitops_analyze.task.d.ts.map +1 -1
  57. package/dist/gitops_analyze.task.js +47 -34
  58. package/dist/gitops_config.d.ts +34 -79
  59. package/dist/gitops_config.d.ts.map +1 -1
  60. package/dist/gitops_config.js +76 -53
  61. package/dist/gitops_constants.d.ts +6 -4
  62. package/dist/gitops_constants.d.ts.map +1 -1
  63. package/dist/gitops_constants.js +6 -4
  64. package/dist/gitops_plan.task.d.ts +29 -3
  65. package/dist/gitops_plan.task.d.ts.map +1 -1
  66. package/dist/gitops_plan.task.js +83 -61
  67. package/dist/gitops_publish.task.d.ts +62 -2
  68. package/dist/gitops_publish.task.d.ts.map +1 -1
  69. package/dist/gitops_publish.task.js +193 -105
  70. package/dist/gitops_run.task.d.ts +74 -0
  71. package/dist/gitops_run.task.d.ts.map +1 -1
  72. package/dist/gitops_run.task.js +176 -162
  73. package/dist/gitops_sync.task.d.ts +65 -3
  74. package/dist/gitops_sync.task.d.ts.map +1 -1
  75. package/dist/gitops_sync.task.js +163 -37
  76. package/dist/gitops_task_helpers.d.ts +86 -59
  77. package/dist/gitops_task_helpers.d.ts.map +1 -1
  78. package/dist/gitops_task_helpers.js +120 -79
  79. package/dist/gitops_validate.task.d.ts +1 -2
  80. package/dist/gitops_validate.task.d.ts.map +1 -1
  81. package/dist/gitops_validate.task.js +25 -32
  82. package/dist/graph_validation.d.ts +16 -27
  83. package/dist/graph_validation.d.ts.map +1 -1
  84. package/dist/graph_validation.js +24 -65
  85. package/dist/local_repo.d.ts +50 -62
  86. package/dist/local_repo.d.ts.map +1 -1
  87. package/dist/local_repo.js +83 -196
  88. package/dist/log_helpers.d.ts +8 -8
  89. package/dist/log_helpers.d.ts.map +1 -1
  90. package/dist/multi_repo_publisher.d.ts +10 -0
  91. package/dist/multi_repo_publisher.d.ts.map +1 -1
  92. package/dist/multi_repo_publisher.js +125 -61
  93. package/dist/npm_registry.d.ts +23 -19
  94. package/dist/npm_registry.d.ts.map +1 -1
  95. package/dist/npm_registry.js +21 -48
  96. package/dist/operations.d.ts +75 -180
  97. package/dist/operations.d.ts.map +1 -1
  98. package/dist/operations.js +15 -7
  99. package/dist/operations_defaults.d.ts +17 -2
  100. package/dist/operations_defaults.d.ts.map +1 -1
  101. package/dist/operations_defaults.js +111 -128
  102. package/dist/output_helpers.d.ts +36 -0
  103. package/dist/output_helpers.d.ts.map +1 -1
  104. package/dist/output_helpers.js +37 -3
  105. package/dist/preflight_checks.d.ts +20 -27
  106. package/dist/preflight_checks.d.ts.map +1 -1
  107. package/dist/preflight_checks.js +37 -126
  108. package/dist/publish_gate.d.ts.map +1 -1
  109. package/dist/publish_steps.d.ts +2 -4
  110. package/dist/publish_steps.d.ts.map +1 -1
  111. package/dist/publish_steps.js +4 -8
  112. package/dist/publishing_event.d.ts +2 -8
  113. package/dist/publishing_event.d.ts.map +1 -1
  114. package/dist/publishing_event.js +20 -17
  115. package/dist/publishing_event_handler.d.ts +10 -8
  116. package/dist/publishing_event_handler.d.ts.map +1 -1
  117. package/dist/publishing_event_handler.js +19 -17
  118. package/dist/publishing_plan.d.ts +16 -1
  119. package/dist/publishing_plan.d.ts.map +1 -1
  120. package/dist/publishing_plan.js +332 -254
  121. package/dist/publishing_plan_helpers.d.ts.map +1 -1
  122. package/dist/publishing_plan_helpers.js +3 -3
  123. package/dist/publishing_plan_logging.d.ts +1 -1
  124. package/dist/publishing_plan_logging.d.ts.map +1 -1
  125. package/dist/publishing_plan_logging.js +22 -15
  126. package/dist/repo.svelte.d.ts +7 -0
  127. package/dist/repo.svelte.d.ts.map +1 -1
  128. package/dist/repo.svelte.js +3 -0
  129. package/dist/repo_readiness.d.ts +210 -0
  130. package/dist/repo_readiness.d.ts.map +1 -0
  131. package/dist/repo_readiness.js +516 -0
  132. package/dist/repos_status.d.ts +3340 -0
  133. package/dist/repos_status.d.ts.map +1 -0
  134. package/dist/repos_status.js +555 -0
  135. package/dist/repos_status_load.d.ts +65 -0
  136. package/dist/repos_status_load.d.ts.map +1 -0
  137. package/dist/repos_status_load.js +131 -0
  138. package/dist/version_utils.js +1 -1
  139. package/package.json +15 -34
  140. package/src/lib/cargo_toml.ts +3 -3
  141. package/src/lib/changeset_generator.ts +19 -19
  142. package/src/lib/changeset_reader.ts +18 -18
  143. package/src/lib/ci_reconcile.ts +12 -16
  144. package/src/lib/dependency_graph.ts +61 -113
  145. package/src/lib/dependency_updater.ts +36 -30
  146. package/src/lib/fetch_repo_data.ts +71 -34
  147. package/src/lib/fs_fetch_value_cache.ts +15 -11
  148. package/src/lib/git_operations.ts +22 -194
  149. package/src/lib/github.ts +33 -21
  150. package/src/lib/github_helpers.ts +9 -16
  151. package/src/lib/gitops_analyze.task.ts +104 -66
  152. package/src/lib/gitops_config.ts +90 -139
  153. package/src/lib/gitops_constants.ts +6 -4
  154. package/src/lib/gitops_plan.task.ts +128 -76
  155. package/src/lib/gitops_publish.task.ts +280 -144
  156. package/src/lib/gitops_run.task.ts +265 -193
  157. package/src/lib/gitops_sync.task.ts +236 -52
  158. package/src/lib/gitops_task_helpers.ts +170 -114
  159. package/src/lib/gitops_validate.task.ts +44 -54
  160. package/src/lib/graph_validation.ts +28 -89
  161. package/src/lib/local_repo.ts +128 -335
  162. package/src/lib/log_helpers.ts +18 -28
  163. package/src/lib/multi_repo_publisher.ts +192 -110
  164. package/src/lib/npm_registry.ts +42 -67
  165. package/src/lib/operations.ts +99 -160
  166. package/src/lib/operations_defaults.ts +157 -201
  167. package/src/lib/output_helpers.ts +58 -6
  168. package/src/lib/preflight_checks.ts +52 -188
  169. package/src/lib/publish_gate.ts +7 -9
  170. package/src/lib/publish_steps.ts +27 -25
  171. package/src/lib/publishing_event.ts +26 -23
  172. package/src/lib/publishing_event_handler.ts +24 -22
  173. package/src/lib/publishing_plan.ts +474 -310
  174. package/src/lib/publishing_plan_helpers.ts +16 -12
  175. package/src/lib/publishing_plan_logging.ts +53 -44
  176. package/src/lib/repo.svelte.ts +16 -8
  177. package/src/lib/repo_readiness.ts +647 -0
  178. package/src/lib/repos_status.ts +671 -0
  179. package/src/lib/repos_status_load.ts +173 -0
  180. package/src/lib/version_utils.ts +3 -3
  181. package/dist/config_reconcile.d.ts +0 -46
  182. package/dist/config_reconcile.d.ts.map +0 -1
  183. package/dist/config_reconcile.js +0 -60
  184. package/dist/paths.d.ts +0 -7
  185. package/dist/paths.d.ts.map +0 -1
  186. package/dist/paths.js +0 -6
  187. package/dist/repo_ops.d.ts +0 -59
  188. package/dist/repo_ops.d.ts.map +0 -1
  189. package/dist/repo_ops.js +0 -170
  190. package/dist/resolved_gitops_config.d.ts +0 -9
  191. package/dist/resolved_gitops_config.d.ts.map +0 -1
  192. package/dist/resolved_gitops_config.js +0 -12
  193. package/src/lib/config_reconcile.ts +0 -94
  194. package/src/lib/paths.ts +0 -6
  195. package/src/lib/repo_ops.ts +0 -216
  196. package/src/lib/resolved_gitops_config.ts +0 -27
@@ -1,227 +1,55 @@
1
- import {spawn_out, spawn_result_to_message} from '@fuzdev/fuz_util/process.ts';
2
- import type {SpawnOptions} from 'node:child_process';
3
- import {
4
- git_check_clean_workspace as gro_git_check_clean_workspace,
5
- git_checkout as gro_git_checkout,
6
- git_pull as gro_git_pull,
7
- git_current_branch_name as gro_git_current_branch_name,
8
- git_current_commit_hash as gro_git_current_commit_hash,
9
- type GitBranch,
10
- type GitOrigin,
11
- } from '@fuzdev/fuz_util/git.ts';
1
+ import { spawn_out, spawn_result_to_message } from '@fuzdev/fuz_util/process.ts';
2
+ import type { SpawnOptions } from 'node:child_process';
3
+ import { git_current_commit_hash as gro_git_current_commit_hash } from '@fuzdev/fuz_util/git.ts';
12
4
 
13
5
  /**
14
6
  * Adds files to git staging area and throws if anything goes wrong.
15
7
  */
16
8
  export const git_add = async (
17
9
  files: string | Array<string>,
18
- options?: SpawnOptions,
10
+ options?: SpawnOptions
19
11
  ): Promise<void> => {
20
12
  const file_list = Array.isArray(files) ? files : [files];
21
- const {result, stderr} = await spawn_out('git', ['add', ...file_list], options);
13
+ const { result, stderr } = await spawn_out('git', ['add', ...file_list], options);
22
14
  if (!result.ok) {
23
15
  throw Error(
24
- `git_add failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`,
16
+ `git_add failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`
25
17
  );
26
18
  }
27
19
  };
28
20
 
29
21
  /**
30
- * Commits staged changes with a message and throws if anything goes wrong.
22
+ * Commits `files` alone with a message, leaving anything else staged out of
23
+ * the commit, and throws if anything goes wrong. `files` must be non-empty:
24
+ * an empty list would commit the whole index.
31
25
  */
32
- export const git_commit = async (message: string, options?: SpawnOptions): Promise<void> => {
33
- const {result, stderr} = await spawn_out('git', ['commit', '-m', message], options);
34
- if (!result.ok) {
35
- throw Error(
36
- `git_commit failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`,
37
- );
38
- }
39
- };
40
-
41
- /**
42
- * Adds files and commits in one operation and throws if anything goes wrong.
43
- */
44
- export const git_add_and_commit = async (
45
- files: string | Array<string>,
26
+ export const git_commit = async (
46
27
  message: string,
47
- options?: SpawnOptions,
28
+ files: Array<string>,
29
+ options?: SpawnOptions
48
30
  ): Promise<void> => {
49
- await git_add(files, options);
50
- await git_commit(message, options);
51
- };
52
-
53
- /**
54
- * Creates a git tag and throws if anything goes wrong.
55
- */
56
- export const git_tag = async (
57
- tag_name: string,
58
- message?: string,
59
- options?: SpawnOptions,
60
- ): Promise<void> => {
61
- const args = message ? ['tag', '-a', tag_name, '-m', message] : ['tag', tag_name];
62
-
63
- const {result, stderr} = await spawn_out('git', args, options);
64
- if (!result.ok) {
65
- throw Error(
66
- `git_tag failed for tag '${tag_name}' with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`,
67
- );
31
+ if (files.length === 0) {
32
+ throw Error('git_commit needs at least one file: an empty list would commit the whole index');
68
33
  }
69
- };
70
-
71
- /**
72
- * Pushes a tag to origin and throws if anything goes wrong.
73
- */
74
- export const git_push_tag = async (
75
- tag_name: string,
76
- origin: GitOrigin = 'origin' as GitOrigin,
77
- options?: SpawnOptions,
78
- ): Promise<void> => {
79
- const {result, stderr} = await spawn_out('git', ['push', origin, tag_name], options);
80
- if (!result.ok) {
81
- throw Error(
82
- `git_push_tag failed for tag '${tag_name}' with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`,
83
- );
84
- }
85
- };
86
-
87
- /**
88
- * Returns `true` if the working tree has any changes — staged, unstaged, or
89
- * untracked (`git status --porcelain`). Broader than `git_list_uncommitted_files`,
90
- * which reports only tracked working-tree changes relative to HEAD.
91
- */
92
- export const git_has_changes = async (options?: SpawnOptions): Promise<boolean> => {
93
- const {stdout} = await spawn_out('git', ['status', '--porcelain'], options);
94
- return stdout ? stdout.trim().length > 0 : false;
95
- };
96
-
97
- /**
98
- * Lists uncommitted files in the working tree (`git diff --name-only HEAD`).
99
- */
100
- export const git_list_uncommitted_files = async (
101
- options?: SpawnOptions,
102
- ): Promise<Array<string>> => {
103
- const {stdout} = await spawn_out('git', ['diff', '--name-only', 'HEAD'], options);
104
- if (!stdout) return [];
105
-
106
- return stdout
107
- .split('\n')
108
- .map((f) => f.trim())
109
- .filter(Boolean);
110
- };
111
-
112
- export const git_has_file_changed = async (
113
- from_commit: string,
114
- to_commit: string,
115
- file_path: string,
116
- options?: SpawnOptions,
117
- ): Promise<boolean> => {
118
- const {stdout} = await spawn_out(
34
+ const { result, stderr } = await spawn_out(
119
35
  'git',
120
- ['diff', '--name-only', from_commit, to_commit, '--', file_path],
121
- options,
36
+ ['commit', '-m', message, '--', ...files],
37
+ options
122
38
  );
123
- return stdout ? stdout.trim().length > 0 : false;
124
- };
125
-
126
- /**
127
- * Stashes current changes and throws if anything goes wrong.
128
- */
129
- export const git_stash = async (message?: string, options?: SpawnOptions): Promise<void> => {
130
- const args = message ? ['stash', 'push', '-m', message] : ['stash', 'push'];
131
-
132
- const {result, stderr} = await spawn_out('git', args, options);
133
- if (!result.ok) {
134
- throw Error(
135
- `git_stash failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`,
136
- );
137
- }
138
- };
139
-
140
- /**
141
- * Applies stashed changes and throws if anything goes wrong.
142
- */
143
- export const git_stash_pop = async (options?: SpawnOptions): Promise<void> => {
144
- const {result, stderr} = await spawn_out('git', ['stash', 'pop'], options);
145
39
  if (!result.ok) {
146
40
  throw Error(
147
- `git_stash_pop failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`,
41
+ `git_commit failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`
148
42
  );
149
43
  }
150
44
  };
151
45
 
152
46
  /**
153
- * Switches to a branch with safety checks and throws if workspace is not clean.
154
- */
155
- export const git_switch_branch = async (
156
- branch: GitBranch,
157
- pull: boolean = true,
158
- options?: SpawnOptions,
159
- ): Promise<void> => {
160
- // Check if workspace is clean first
161
- const error = await gro_git_check_clean_workspace(options);
162
- if (error) {
163
- throw Error(`Cannot switch branch: ${error}`);
164
- }
165
-
166
- // Checkout the branch
167
- await gro_git_checkout(branch, options);
168
-
169
- // Pull latest changes if requested
170
- if (pull) {
171
- await gro_git_pull(undefined, undefined, options);
172
- }
173
-
174
- // Verify workspace is still clean
175
- const error_after = await gro_git_check_clean_workspace(options);
176
- if (error_after) {
177
- throw Error(`Workspace unclean after switching to ${branch}: ${error_after}`);
178
- }
179
- };
180
-
181
- /**
182
- * Wrapper for gro's `git_current_branch_name` that throws if null.
183
- */
184
- export const git_current_branch_name_required = async (options?: SpawnOptions): Promise<string> => {
185
- const branch = await gro_git_current_branch_name(options);
186
- if (!branch) {
187
- throw new Error('Failed to get current branch name');
188
- }
189
- return branch;
190
- };
191
-
192
- /**
193
- * Wrapper for gro's `git_current_commit_hash` that throws if null.
47
+ * Wrapper for gro's `git_current_commit_hash` that reads `HEAD` and throws if null.
194
48
  */
195
- export const git_current_commit_hash_required = async (
196
- branch?: string,
197
- options?: SpawnOptions,
198
- ): Promise<string> => {
199
- const hash = await gro_git_current_commit_hash(branch, options);
49
+ export const git_current_commit_hash_required = async (options?: SpawnOptions): Promise<string> => {
50
+ const hash = await gro_git_current_commit_hash(undefined, options);
200
51
  if (!hash) {
201
- throw new Error(`Failed to get commit hash for branch: ${branch || 'current'}`);
52
+ throw new Error('Failed to get the current commit hash');
202
53
  }
203
54
  return hash;
204
55
  };
205
-
206
- /**
207
- * Wrapper for gro's `git_check_clean_workspace` that returns a boolean.
208
- */
209
- export const git_check_clean_workspace_as_boolean = async (
210
- options?: SpawnOptions,
211
- ): Promise<boolean> => {
212
- const error = await gro_git_check_clean_workspace(options);
213
- return error === null;
214
- };
215
-
216
- export const git_has_remote = async (
217
- remote: string = 'origin',
218
- options?: SpawnOptions,
219
- ): Promise<boolean> => {
220
- const {stdout} = await spawn_out('git', ['remote'], options);
221
- if (!stdout) return false;
222
- const remotes = stdout
223
- .split('\n')
224
- .map((r) => r.trim())
225
- .filter(Boolean);
226
- return remotes.includes(remote);
227
- };
package/src/lib/github.ts CHANGED
@@ -1,9 +1,10 @@
1
- import type {Logger} from '@fuzdev/fuz_util/log.ts';
2
- import {z} from 'zod';
3
- import {fetch_value, type FetchValueCache} from '@fuzdev/fuz_util/fetch.ts';
1
+ import type { Logger } from '@fuzdev/fuz_util/log.ts';
2
+ import { z } from 'zod';
3
+ import { fetch_value, type FetchValueCache } from '@fuzdev/fuz_util/fetch.ts';
4
+ import type { Result } from '@fuzdev/fuz_util/result.ts';
4
5
 
5
6
  /**
6
- * Minimal interface for GitHub API calls - works with both `Pkg` and `Repo`.
7
+ * Minimal interface for GitHub API calls, satisfied structurally by fuz_ui's `Library`.
7
8
  */
8
9
  export interface GithubRepoInfo {
9
10
  owner_name: string | null;
@@ -17,9 +18,9 @@ export const GithubPullRequest = z.object({
17
18
  number: z.number(),
18
19
  title: z.string(),
19
20
  user: z.object({
20
- login: z.string(),
21
+ login: z.string()
21
22
  }),
22
- draft: z.boolean(),
23
+ draft: z.boolean()
23
24
  });
24
25
  export type GithubPullRequest = z.infer<typeof GithubPullRequest>;
25
26
  export const GithubPullRequests = z.array(GithubPullRequest);
@@ -35,24 +36,26 @@ export const fetch_github_pull_requests = async (
35
36
  log?: Logger;
36
37
  token?: string;
37
38
  api_version?: string;
38
- } = {},
39
+ fetch?: typeof globalThis.fetch;
40
+ } = {}
39
41
  ): Promise<GithubPullRequests | null> => {
40
- const {cache, log, token, api_version} = options;
42
+ const { cache, log, token, api_version, fetch } = options;
41
43
  if (!repo_info.owner_name) throw Error('owner_name is required');
42
- const headers = api_version ? new Headers({'x-github-api-version': api_version}) : undefined;
44
+ const headers = api_version ? new Headers({ 'x-github-api-version': api_version }) : undefined;
43
45
  const url = `https://api.github.com/repos/${repo_info.owner_name}/${repo_info.repo_name}/pulls`;
44
46
  const fetched = await fetch_value(url, {
45
- request: {headers},
47
+ request: { headers },
46
48
  parse: GithubPullRequests.parse,
47
49
  token,
48
50
  cache,
49
51
  log,
52
+ fetch
50
53
  });
51
54
  if (!fetched.ok) {
52
55
  // TODO @many this is messy but I think it's the main case we need to worry about?
53
56
  if (fetched.status === 401) {
54
57
  throw Error(
55
- '401 response fetching github pull requests - check your SECRET_GITHUB_API_TOKEN',
58
+ '401 response fetching github pull requests - check your SECRET_GITHUB_API_TOKEN'
56
59
  );
57
60
  }
58
61
  return null;
@@ -67,16 +70,23 @@ export const GithubCheckRunsItem = z.object({
67
70
  status: z.enum(['queued', 'in_progress', 'completed']),
68
71
  conclusion: z
69
72
  .enum(['success', 'failure', 'neutral', 'cancelled', 'skipped', 'timed_out', 'action_required'])
70
- .nullable(),
73
+ .nullable()
71
74
  });
72
75
  export type GithubCheckRunsItem = z.infer<typeof GithubCheckRunsItem>;
73
76
  export const GithubCheckRuns = z.object({
74
77
  total_count: z.number(),
75
- check_runs: z.array(GithubCheckRunsItem),
78
+ check_runs: z.array(GithubCheckRunsItem)
76
79
  });
77
80
  export type GithubCheckRuns = z.infer<typeof GithubCheckRuns>;
78
81
 
79
82
  /**
83
+ * Fetches the check runs on `ref` and reduces them to one overall status and
84
+ * conclusion.
85
+ *
86
+ * @param repo_info - the repo's GitHub owner and name
87
+ * @param options - the cache, logger, token, API version, ref (`main` by default), and `fetch`
88
+ * @returns the reduced check runs, `null` for a ref with none (a repo without CI, or a commit CI skipped), or a failure when the request or its parse failed
89
+ * @throws {Error} on a 401 response (check `SECRET_GITHUB_API_TOKEN`) or a repo without an owner
80
90
  * @see https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#list-check-runs-for-a-git-reference
81
91
  */
82
92
  export const fetch_github_check_runs = async (
@@ -87,27 +97,29 @@ export const fetch_github_check_runs = async (
87
97
  token?: string;
88
98
  api_version?: string;
89
99
  ref?: string;
90
- } = {},
91
- ): Promise<GithubCheckRunsItem | null> => {
92
- const {cache, log, token, api_version, ref = 'main'} = options;
100
+ fetch?: typeof globalThis.fetch;
101
+ } = {}
102
+ ): Promise<Result<{ value: GithubCheckRunsItem | null }, { status: number; message: string }>> => {
103
+ const { cache, log, token, api_version, ref = 'main', fetch } = options;
93
104
  if (!repo_info.owner_name) throw Error('owner_name is required');
94
- const headers = api_version ? new Headers({'x-github-api-version': api_version}) : undefined;
105
+ const headers = api_version ? new Headers({ 'x-github-api-version': api_version }) : undefined;
95
106
  const url = `https://api.github.com/repos/${repo_info.owner_name}/${repo_info.repo_name}/commits/${ref}/check-runs`;
96
107
  const fetched = await fetch_value(url, {
97
- request: {headers},
108
+ request: { headers },
98
109
  parse: (v) => reduce_check_runs(GithubCheckRuns.parse(v).check_runs),
99
110
  token,
100
111
  cache,
101
112
  log,
113
+ fetch
102
114
  });
103
115
  if (!fetched.ok) {
104
116
  // TODO @many this is messy but I think it's the main case we need to worry about?
105
117
  if (fetched.status === 401) {
106
118
  throw Error('401 response fetching github CI status - check your SECRET_GITHUB_API_TOKEN');
107
119
  }
108
- return null;
120
+ return { ok: false, status: fetched.status, message: fetched.message };
109
121
  }
110
- return fetched.value;
122
+ return { ok: true, value: fetched.value };
111
123
  };
112
124
 
113
125
  const reduce_check_runs = (check_runs: Array<GithubCheckRunsItem>): GithubCheckRunsItem | null => {
@@ -123,5 +135,5 @@ const reduce_check_runs = (check_runs: Array<GithubCheckRunsItem>): GithubCheckR
123
135
  conclusion = check_run.conclusion;
124
136
  }
125
137
  }
126
- return {status, conclusion};
138
+ return { status, conclusion };
127
139
  };
@@ -1,7 +1,7 @@
1
- import {ensure_end} from '@fuzdev/fuz_util/string.ts';
1
+ import { ensure_end } from '@fuzdev/fuz_util/string.ts';
2
2
 
3
- import type {GithubPullRequest} from './github.ts';
4
- import type {Repo} from './repo.svelte.ts';
3
+ import type { GithubPullRequest } from './github.ts';
4
+ import type { Repo } from './repo.svelte.ts';
5
5
 
6
6
  export type FilterPullRequest = (pull_request: GithubPullRequest, repo: Repo) => boolean;
7
7
 
@@ -12,20 +12,13 @@ export interface PullRequestMeta {
12
12
 
13
13
  export const to_pull_requests = (
14
14
  repos: Array<Repo>,
15
- filter_pull_request?: FilterPullRequest,
15
+ filter_pull_request?: FilterPullRequest
16
16
  ): Array<PullRequestMeta> =>
17
- repos
18
- .flatMap((repo) => {
19
- if (!repo.pull_requests) return null;
20
- // TODO hacky, figure out the data structure
21
- return repo.pull_requests.map((pull_request) =>
22
- repo.package_json.homepage &&
23
- (!filter_pull_request || filter_pull_request(pull_request, repo))
24
- ? {repo, pull_request}
25
- : null,
26
- );
27
- })
28
- .filter((v) => v !== null);
17
+ repos.flatMap((repo) =>
18
+ (repo.pull_requests ?? [])
19
+ .filter((pull_request) => !filter_pull_request || filter_pull_request(pull_request, repo))
20
+ .map((pull_request) => ({ repo, pull_request }))
21
+ );
29
22
 
30
23
  export const to_pull_url = (repo_url: string, pull: GithubPullRequest): string =>
31
24
  ensure_end(repo_url, '/') + 'pull/' + pull.number;