@fuzdev/fuz_gitops 0.78.1 → 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 (179) hide show
  1. package/README.md +78 -24
  2. package/dist/ModulesDetail.svelte +15 -22
  3. package/dist/ModulesDetail.svelte.d.ts.map +1 -1
  4. package/dist/ModulesNav.svelte +4 -1
  5. package/dist/ModulesPage.svelte +1 -3
  6. package/dist/ModulesPage.svelte.d.ts.map +1 -1
  7. package/dist/PageHeader.svelte +2 -2
  8. package/dist/PageHeader.svelte.d.ts +0 -3
  9. package/dist/PageHeader.svelte.d.ts.map +1 -1
  10. package/dist/PullRequestsDetail.svelte +10 -9
  11. package/dist/PullRequestsDetail.svelte.d.ts.map +1 -1
  12. package/dist/PullRequestsPage.svelte +1 -3
  13. package/dist/PullRequestsPage.svelte.d.ts.map +1 -1
  14. package/dist/ReposTable.svelte +27 -48
  15. package/dist/ReposTable.svelte.d.ts.map +1 -1
  16. package/dist/ReposTree.svelte +5 -19
  17. package/dist/ReposTree.svelte.d.ts.map +1 -1
  18. package/dist/ReposTreeNav.svelte +5 -7
  19. package/dist/ReposTreeNav.svelte.d.ts.map +1 -1
  20. package/dist/TablePage.svelte +1 -3
  21. package/dist/TablePage.svelte.d.ts.map +1 -1
  22. package/dist/TreeItemPage.svelte +1 -3
  23. package/dist/TreeItemPage.svelte.d.ts.map +1 -1
  24. package/dist/TreePage.svelte +1 -3
  25. package/dist/TreePage.svelte.d.ts.map +1 -1
  26. package/dist/ci_reconcile.d.ts +6 -10
  27. package/dist/ci_reconcile.d.ts.map +1 -1
  28. package/dist/ci_reconcile.js +6 -8
  29. package/dist/dependency_graph.d.ts +26 -49
  30. package/dist/dependency_graph.d.ts.map +1 -1
  31. package/dist/dependency_graph.js +37 -80
  32. package/dist/dependency_updater.d.ts +1 -1
  33. package/dist/dependency_updater.d.ts.map +1 -1
  34. package/dist/dependency_updater.js +6 -1
  35. package/dist/fetch_repo_data.d.ts +34 -8
  36. package/dist/fetch_repo_data.d.ts.map +1 -1
  37. package/dist/fetch_repo_data.js +44 -22
  38. package/dist/fs_fetch_value_cache.d.ts +1 -1
  39. package/dist/fs_fetch_value_cache.js +1 -1
  40. package/dist/git_operations.d.ts +6 -49
  41. package/dist/git_operations.d.ts.map +1 -1
  42. package/dist/git_operations.js +13 -126
  43. package/dist/github.d.ts +17 -2
  44. package/dist/github.d.ts.map +1 -1
  45. package/dist/github.js +15 -6
  46. package/dist/github_helpers.d.ts.map +1 -1
  47. package/dist/github_helpers.js +3 -11
  48. package/dist/gitops_analyze.task.d.ts +24 -2
  49. package/dist/gitops_analyze.task.d.ts.map +1 -1
  50. package/dist/gitops_analyze.task.js +43 -30
  51. package/dist/gitops_config.d.ts +34 -79
  52. package/dist/gitops_config.d.ts.map +1 -1
  53. package/dist/gitops_config.js +76 -53
  54. package/dist/gitops_constants.d.ts +6 -4
  55. package/dist/gitops_constants.d.ts.map +1 -1
  56. package/dist/gitops_constants.js +6 -4
  57. package/dist/gitops_plan.task.d.ts +29 -3
  58. package/dist/gitops_plan.task.d.ts.map +1 -1
  59. package/dist/gitops_plan.task.js +80 -58
  60. package/dist/gitops_publish.task.d.ts +62 -2
  61. package/dist/gitops_publish.task.d.ts.map +1 -1
  62. package/dist/gitops_publish.task.js +187 -102
  63. package/dist/gitops_run.task.d.ts +74 -0
  64. package/dist/gitops_run.task.d.ts.map +1 -1
  65. package/dist/gitops_run.task.js +174 -160
  66. package/dist/gitops_sync.task.d.ts +65 -3
  67. package/dist/gitops_sync.task.d.ts.map +1 -1
  68. package/dist/gitops_sync.task.js +154 -30
  69. package/dist/gitops_task_helpers.d.ts +86 -59
  70. package/dist/gitops_task_helpers.d.ts.map +1 -1
  71. package/dist/gitops_task_helpers.js +120 -79
  72. package/dist/gitops_validate.task.d.ts +1 -2
  73. package/dist/gitops_validate.task.d.ts.map +1 -1
  74. package/dist/gitops_validate.task.js +15 -22
  75. package/dist/graph_validation.d.ts +16 -27
  76. package/dist/graph_validation.d.ts.map +1 -1
  77. package/dist/graph_validation.js +23 -64
  78. package/dist/local_repo.d.ts +50 -62
  79. package/dist/local_repo.d.ts.map +1 -1
  80. package/dist/local_repo.js +80 -193
  81. package/dist/log_helpers.d.ts +8 -8
  82. package/dist/log_helpers.d.ts.map +1 -1
  83. package/dist/multi_repo_publisher.d.ts +10 -0
  84. package/dist/multi_repo_publisher.d.ts.map +1 -1
  85. package/dist/multi_repo_publisher.js +115 -51
  86. package/dist/npm_registry.d.ts +23 -19
  87. package/dist/npm_registry.d.ts.map +1 -1
  88. package/dist/npm_registry.js +20 -47
  89. package/dist/operations.d.ts +75 -180
  90. package/dist/operations.d.ts.map +1 -1
  91. package/dist/operations.js +15 -7
  92. package/dist/operations_defaults.d.ts +17 -2
  93. package/dist/operations_defaults.d.ts.map +1 -1
  94. package/dist/operations_defaults.js +104 -121
  95. package/dist/output_helpers.d.ts +36 -0
  96. package/dist/output_helpers.d.ts.map +1 -1
  97. package/dist/output_helpers.js +37 -3
  98. package/dist/preflight_checks.d.ts +20 -27
  99. package/dist/preflight_checks.d.ts.map +1 -1
  100. package/dist/preflight_checks.js +37 -126
  101. package/dist/publish_steps.d.ts +2 -4
  102. package/dist/publish_steps.d.ts.map +1 -1
  103. package/dist/publish_steps.js +2 -6
  104. package/dist/publishing_event.d.ts +2 -8
  105. package/dist/publishing_event.d.ts.map +1 -1
  106. package/dist/publishing_event.js +5 -4
  107. package/dist/publishing_event_handler.d.ts +10 -8
  108. package/dist/publishing_event_handler.d.ts.map +1 -1
  109. package/dist/publishing_event_handler.js +14 -12
  110. package/dist/publishing_plan.d.ts +16 -1
  111. package/dist/publishing_plan.d.ts.map +1 -1
  112. package/dist/publishing_plan.js +328 -250
  113. package/dist/publishing_plan_logging.d.ts +1 -1
  114. package/dist/publishing_plan_logging.d.ts.map +1 -1
  115. package/dist/publishing_plan_logging.js +21 -14
  116. package/dist/repo.svelte.d.ts +7 -0
  117. package/dist/repo.svelte.d.ts.map +1 -1
  118. package/dist/repo.svelte.js +3 -0
  119. package/dist/repo_readiness.d.ts +210 -0
  120. package/dist/repo_readiness.d.ts.map +1 -0
  121. package/dist/repo_readiness.js +516 -0
  122. package/dist/repos_status.d.ts +3340 -0
  123. package/dist/repos_status.d.ts.map +1 -0
  124. package/dist/repos_status.js +555 -0
  125. package/dist/repos_status_load.d.ts +65 -0
  126. package/dist/repos_status_load.d.ts.map +1 -0
  127. package/dist/repos_status_load.js +131 -0
  128. package/package.json +7 -27
  129. package/src/lib/ci_reconcile.ts +7 -11
  130. package/src/lib/dependency_graph.ts +45 -97
  131. package/src/lib/dependency_updater.ts +7 -1
  132. package/src/lib/fetch_repo_data.ts +66 -29
  133. package/src/lib/fs_fetch_value_cache.ts +1 -1
  134. package/src/lib/git_operations.ts +15 -187
  135. package/src/lib/github.ts +20 -8
  136. package/src/lib/github_helpers.ts +5 -12
  137. package/src/lib/gitops_analyze.task.ts +79 -41
  138. package/src/lib/gitops_config.ts +88 -137
  139. package/src/lib/gitops_constants.ts +6 -4
  140. package/src/lib/gitops_plan.task.ts +113 -61
  141. package/src/lib/gitops_publish.task.ts +255 -122
  142. package/src/lib/gitops_run.task.ts +250 -178
  143. package/src/lib/gitops_sync.task.ts +208 -33
  144. package/src/lib/gitops_task_helpers.ts +166 -110
  145. package/src/lib/gitops_validate.task.ts +17 -27
  146. package/src/lib/graph_validation.ts +25 -86
  147. package/src/lib/local_repo.ts +109 -316
  148. package/src/lib/log_helpers.ts +8 -18
  149. package/src/lib/multi_repo_publisher.ts +138 -53
  150. package/src/lib/npm_registry.ts +37 -62
  151. package/src/lib/operations.ts +78 -150
  152. package/src/lib/operations_defaults.ts +108 -154
  153. package/src/lib/output_helpers.ts +55 -3
  154. package/src/lib/preflight_checks.ts +47 -183
  155. package/src/lib/publish_steps.ts +15 -13
  156. package/src/lib/publishing_event.ts +5 -4
  157. package/src/lib/publishing_event_handler.ts +18 -16
  158. package/src/lib/publishing_plan.ts +451 -287
  159. package/src/lib/publishing_plan_logging.ts +32 -23
  160. package/src/lib/repo.svelte.ts +8 -0
  161. package/src/lib/repo_readiness.ts +647 -0
  162. package/src/lib/repos_status.ts +671 -0
  163. package/src/lib/repos_status_load.ts +173 -0
  164. package/dist/config_reconcile.d.ts +0 -46
  165. package/dist/config_reconcile.d.ts.map +0 -1
  166. package/dist/config_reconcile.js +0 -64
  167. package/dist/paths.d.ts +0 -7
  168. package/dist/paths.d.ts.map +0 -1
  169. package/dist/paths.js +0 -6
  170. package/dist/repo_ops.d.ts +0 -59
  171. package/dist/repo_ops.d.ts.map +0 -1
  172. package/dist/repo_ops.js +0 -170
  173. package/dist/resolved_gitops_config.d.ts +0 -9
  174. package/dist/resolved_gitops_config.d.ts.map +0 -1
  175. package/dist/resolved_gitops_config.js +0 -12
  176. package/src/lib/config_reconcile.ts +0 -98
  177. package/src/lib/paths.ts +0 -6
  178. package/src/lib/repo_ops.ts +0 -216
  179. package/src/lib/resolved_gitops_config.ts +0 -27
@@ -1,59 +1,16 @@
1
1
  import type { SpawnOptions } from 'node:child_process';
2
- import { type GitBranch, type GitOrigin } from '@fuzdev/fuz_util/git.ts';
3
2
  /**
4
3
  * Adds files to git staging area and throws if anything goes wrong.
5
4
  */
6
5
  export declare const git_add: (files: string | Array<string>, options?: SpawnOptions) => Promise<void>;
7
6
  /**
8
- * Commits staged changes with a message and throws if anything goes wrong.
7
+ * Commits `files` alone with a message, leaving anything else staged out of
8
+ * the commit, and throws if anything goes wrong. `files` must be non-empty:
9
+ * an empty list would commit the whole index.
9
10
  */
10
- export declare const git_commit: (message: string, options?: SpawnOptions) => Promise<void>;
11
+ export declare const git_commit: (message: string, files: Array<string>, options?: SpawnOptions) => Promise<void>;
11
12
  /**
12
- * Adds files and commits in one operation and throws if anything goes wrong.
13
+ * Wrapper for gro's `git_current_commit_hash` that reads `HEAD` and throws if null.
13
14
  */
14
- export declare const git_add_and_commit: (files: string | Array<string>, message: string, options?: SpawnOptions) => Promise<void>;
15
- /**
16
- * Creates a git tag and throws if anything goes wrong.
17
- */
18
- export declare const git_tag: (tag_name: string, message?: string, options?: SpawnOptions) => Promise<void>;
19
- /**
20
- * Pushes a tag to origin and throws if anything goes wrong.
21
- */
22
- export declare const git_push_tag: (tag_name: string, origin?: GitOrigin, options?: SpawnOptions) => Promise<void>;
23
- /**
24
- * Returns `true` if the working tree has any changes — staged, unstaged, or
25
- * untracked (`git status --porcelain`). Broader than `git_list_uncommitted_files`,
26
- * which reports only tracked working-tree changes relative to HEAD.
27
- */
28
- export declare const git_has_changes: (options?: SpawnOptions) => Promise<boolean>;
29
- /**
30
- * Lists uncommitted files in the working tree (`git diff --name-only HEAD`).
31
- */
32
- export declare const git_list_uncommitted_files: (options?: SpawnOptions) => Promise<Array<string>>;
33
- export declare const git_has_file_changed: (from_commit: string, to_commit: string, file_path: string, options?: SpawnOptions) => Promise<boolean>;
34
- /**
35
- * Stashes current changes and throws if anything goes wrong.
36
- */
37
- export declare const git_stash: (message?: string, options?: SpawnOptions) => Promise<void>;
38
- /**
39
- * Applies stashed changes and throws if anything goes wrong.
40
- */
41
- export declare const git_stash_pop: (options?: SpawnOptions) => Promise<void>;
42
- /**
43
- * Switches to a branch with safety checks and throws if workspace is not clean.
44
- */
45
- export declare const git_switch_branch: (branch: GitBranch, pull?: boolean, options?: SpawnOptions) => Promise<void>;
46
- /**
47
- * Wrapper for gro's `git_current_branch_name` that throws if null.
48
- */
49
- export declare const git_current_branch_name_required: (options?: SpawnOptions) => Promise<string>;
50
- /**
51
- * Wrapper for gro's `git_current_commit_hash` that throws if null.
52
- */
53
- export declare const git_current_commit_hash_required: (branch?: string, options?: SpawnOptions) => Promise<string>;
54
- /**
55
- * Wrapper for gro's `git_check_clean_workspace` that returns a boolean.
56
- */
57
- export declare const git_check_clean_workspace_as_boolean: (options?: SpawnOptions) => Promise<boolean>;
58
- export declare const git_has_remote: (remote?: string, options?: SpawnOptions) => Promise<boolean>;
15
+ export declare const git_current_commit_hash_required: (options?: SpawnOptions) => Promise<string>;
59
16
  //# sourceMappingURL=git_operations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"git_operations.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/git_operations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAMN,KAAK,SAAS,EACd,KAAK,SAAS,EACd,MAAM,yBAAyB,CAAC;AAEjC;;GAEG;AACH,eAAO,MAAM,OAAO,GACnB,OAAO,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,EAC7B,UAAU,YAAY,KACpB,OAAO,CAAC,IAAI,CAQd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,GAAU,SAAS,MAAM,EAAE,UAAU,YAAY,KAAG,OAAO,CAAC,IAAI,CAOtF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC9B,OAAO,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,EAC7B,SAAS,MAAM,EACf,UAAU,YAAY,KACpB,OAAO,CAAC,IAAI,CAGd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,OAAO,GACnB,UAAU,MAAM,EAChB,UAAU,MAAM,EAChB,UAAU,YAAY,KACpB,OAAO,CAAC,IAAI,CASd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GACxB,UAAU,MAAM,EAChB,SAAQ,SAAiC,EACzC,UAAU,YAAY,KACpB,OAAO,CAAC,IAAI,CAOd,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAU,UAAU,YAAY,KAAG,OAAO,CAAC,OAAO,CAG7E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,GACtC,UAAU,YAAY,KACpB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAQvB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAChC,aAAa,MAAM,EACnB,WAAW,MAAM,EACjB,WAAW,MAAM,EACjB,UAAU,YAAY,KACpB,OAAO,CAAC,OAAO,CAOjB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,GAAU,UAAU,MAAM,EAAE,UAAU,YAAY,KAAG,OAAO,CAAC,IAAI,CAStF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAU,UAAU,YAAY,KAAG,OAAO,CAAC,IAAI,CAOxE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAC7B,QAAQ,SAAS,EACjB,OAAM,OAAc,EACpB,UAAU,YAAY,KACpB,OAAO,CAAC,IAAI,CAoBd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gCAAgC,GAAU,UAAU,YAAY,KAAG,OAAO,CAAC,MAAM,CAM7F,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gCAAgC,GAC5C,SAAS,MAAM,EACf,UAAU,YAAY,KACpB,OAAO,CAAC,MAAM,CAMhB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oCAAoC,GAChD,UAAU,YAAY,KACpB,OAAO,CAAC,OAAO,CAGjB,CAAC;AAEF,eAAO,MAAM,cAAc,GAC1B,SAAQ,MAAiB,EACzB,UAAU,YAAY,KACpB,OAAO,CAAC,OAAO,CAQjB,CAAC"}
1
+ {"version":3,"file":"git_operations.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/git_operations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD;;GAEG;AACH,eAAO,MAAM,OAAO,GACnB,OAAO,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,EAC7B,UAAU,YAAY,KACpB,OAAO,CAAC,IAAI,CAQd,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GACtB,SAAS,MAAM,EACf,OAAO,KAAK,CAAC,MAAM,CAAC,EACpB,UAAU,YAAY,KACpB,OAAO,CAAC,IAAI,CAcd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gCAAgC,GAAU,UAAU,YAAY,KAAG,OAAO,CAAC,MAAM,CAM7F,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { spawn_out, spawn_result_to_message } from '@fuzdev/fuz_util/process.ts';
2
- import { git_check_clean_workspace as gro_git_check_clean_workspace, git_checkout as gro_git_checkout, git_pull as gro_git_pull, git_current_branch_name as gro_git_current_branch_name, git_current_commit_hash as gro_git_current_commit_hash } from '@fuzdev/fuz_util/git.ts';
2
+ import { git_current_commit_hash as gro_git_current_commit_hash } from '@fuzdev/fuz_util/git.ts';
3
3
  /**
4
4
  * Adds files to git staging area and throws if anything goes wrong.
5
5
  */
@@ -11,139 +11,26 @@ export const git_add = async (files, options) => {
11
11
  }
12
12
  };
13
13
  /**
14
- * Commits staged changes with a message and throws if anything goes wrong.
14
+ * Commits `files` alone with a message, leaving anything else staged out of
15
+ * the commit, and throws if anything goes wrong. `files` must be non-empty:
16
+ * an empty list would commit the whole index.
15
17
  */
16
- export const git_commit = async (message, options) => {
17
- const { result, stderr } = await spawn_out('git', ['commit', '-m', message], options);
18
- if (!result.ok) {
19
- throw Error(`git_commit failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`);
18
+ export const git_commit = async (message, files, options) => {
19
+ if (files.length === 0) {
20
+ throw Error('git_commit needs at least one file: an empty list would commit the whole index');
20
21
  }
21
- };
22
- /**
23
- * Adds files and commits in one operation and throws if anything goes wrong.
24
- */
25
- export const git_add_and_commit = async (files, message, options) => {
26
- await git_add(files, options);
27
- await git_commit(message, options);
28
- };
29
- /**
30
- * Creates a git tag and throws if anything goes wrong.
31
- */
32
- export const git_tag = async (tag_name, message, options) => {
33
- const args = message ? ['tag', '-a', tag_name, '-m', message] : ['tag', tag_name];
34
- const { result, stderr } = await spawn_out('git', args, options);
22
+ const { result, stderr } = await spawn_out('git', ['commit', '-m', message, '--', ...files], options);
35
23
  if (!result.ok) {
36
- throw Error(`git_tag failed for tag '${tag_name}' with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`);
37
- }
38
- };
39
- /**
40
- * Pushes a tag to origin and throws if anything goes wrong.
41
- */
42
- export const git_push_tag = async (tag_name, origin = 'origin', options) => {
43
- const { result, stderr } = await spawn_out('git', ['push', origin, tag_name], options);
44
- if (!result.ok) {
45
- throw Error(`git_push_tag failed for tag '${tag_name}' with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`);
46
- }
47
- };
48
- /**
49
- * Returns `true` if the working tree has any changes — staged, unstaged, or
50
- * untracked (`git status --porcelain`). Broader than `git_list_uncommitted_files`,
51
- * which reports only tracked working-tree changes relative to HEAD.
52
- */
53
- export const git_has_changes = async (options) => {
54
- const { stdout } = await spawn_out('git', ['status', '--porcelain'], options);
55
- return stdout ? stdout.trim().length > 0 : false;
56
- };
57
- /**
58
- * Lists uncommitted files in the working tree (`git diff --name-only HEAD`).
59
- */
60
- export const git_list_uncommitted_files = async (options) => {
61
- const { stdout } = await spawn_out('git', ['diff', '--name-only', 'HEAD'], options);
62
- if (!stdout)
63
- return [];
64
- return stdout
65
- .split('\n')
66
- .map((f) => f.trim())
67
- .filter(Boolean);
68
- };
69
- export const git_has_file_changed = async (from_commit, to_commit, file_path, options) => {
70
- const { stdout } = await spawn_out('git', ['diff', '--name-only', from_commit, to_commit, '--', file_path], options);
71
- return stdout ? stdout.trim().length > 0 : false;
72
- };
73
- /**
74
- * Stashes current changes and throws if anything goes wrong.
75
- */
76
- export const git_stash = async (message, options) => {
77
- const args = message ? ['stash', 'push', '-m', message] : ['stash', 'push'];
78
- const { result, stderr } = await spawn_out('git', args, options);
79
- if (!result.ok) {
80
- throw Error(`git_stash failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`);
81
- }
82
- };
83
- /**
84
- * Applies stashed changes and throws if anything goes wrong.
85
- */
86
- export const git_stash_pop = async (options) => {
87
- const { result, stderr } = await spawn_out('git', ['stash', 'pop'], options);
88
- if (!result.ok) {
89
- throw Error(`git_stash_pop failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`);
90
- }
91
- };
92
- /**
93
- * Switches to a branch with safety checks and throws if workspace is not clean.
94
- */
95
- export const git_switch_branch = async (branch, pull = true, options) => {
96
- // Check if workspace is clean first
97
- const error = await gro_git_check_clean_workspace(options);
98
- if (error) {
99
- throw Error(`Cannot switch branch: ${error}`);
100
- }
101
- // Checkout the branch
102
- await gro_git_checkout(branch, options);
103
- // Pull latest changes if requested
104
- if (pull) {
105
- await gro_git_pull(undefined, undefined, options);
106
- }
107
- // Verify workspace is still clean
108
- const error_after = await gro_git_check_clean_workspace(options);
109
- if (error_after) {
110
- throw Error(`Workspace unclean after switching to ${branch}: ${error_after}`);
111
- }
112
- };
113
- /**
114
- * Wrapper for gro's `git_current_branch_name` that throws if null.
115
- */
116
- export const git_current_branch_name_required = async (options) => {
117
- const branch = await gro_git_current_branch_name(options);
118
- if (!branch) {
119
- throw new Error('Failed to get current branch name');
24
+ throw Error(`git_commit failed with ${spawn_result_to_message(result)}${stderr ? ': ' + stderr.trim() : ''}`);
120
25
  }
121
- return branch;
122
26
  };
123
27
  /**
124
- * Wrapper for gro's `git_current_commit_hash` that throws if null.
28
+ * Wrapper for gro's `git_current_commit_hash` that reads `HEAD` and throws if null.
125
29
  */
126
- export const git_current_commit_hash_required = async (branch, options) => {
127
- const hash = await gro_git_current_commit_hash(branch, options);
30
+ export const git_current_commit_hash_required = async (options) => {
31
+ const hash = await gro_git_current_commit_hash(undefined, options);
128
32
  if (!hash) {
129
- throw new Error(`Failed to get commit hash for branch: ${branch || 'current'}`);
33
+ throw new Error('Failed to get the current commit hash');
130
34
  }
131
35
  return hash;
132
36
  };
133
- /**
134
- * Wrapper for gro's `git_check_clean_workspace` that returns a boolean.
135
- */
136
- export const git_check_clean_workspace_as_boolean = async (options) => {
137
- const error = await gro_git_check_clean_workspace(options);
138
- return error === null;
139
- };
140
- export const git_has_remote = async (remote = 'origin', options) => {
141
- const { stdout } = await spawn_out('git', ['remote'], options);
142
- if (!stdout)
143
- return false;
144
- const remotes = stdout
145
- .split('\n')
146
- .map((r) => r.trim())
147
- .filter(Boolean);
148
- return remotes.includes(remote);
149
- };
package/dist/github.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type { Logger } from '@fuzdev/fuz_util/log.ts';
2
2
  import { z } from 'zod';
3
3
  import { type FetchValueCache } from '@fuzdev/fuz_util/fetch.ts';
4
+ import type { Result } from '@fuzdev/fuz_util/result.ts';
4
5
  /**
5
- * Minimal interface for GitHub API calls - works with both `Pkg` and `Repo`.
6
+ * Minimal interface for GitHub API calls, satisfied structurally by fuz_ui's `Library`.
6
7
  */
7
8
  export interface GithubRepoInfo {
8
9
  owner_name: string | null;
@@ -37,6 +38,7 @@ export declare const fetch_github_pull_requests: (repo_info: GithubRepoInfo, opt
37
38
  log?: Logger;
38
39
  token?: string;
39
40
  api_version?: string;
41
+ fetch?: typeof globalThis.fetch;
40
42
  }) => Promise<GithubPullRequests | null>;
41
43
  /**
42
44
  * @see https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#list-check-runs-for-a-git-reference
@@ -79,6 +81,13 @@ export declare const GithubCheckRuns: z.ZodObject<{
79
81
  }, z.core.$strip>;
80
82
  export type GithubCheckRuns = z.infer<typeof GithubCheckRuns>;
81
83
  /**
84
+ * Fetches the check runs on `ref` and reduces them to one overall status and
85
+ * conclusion.
86
+ *
87
+ * @param repo_info - the repo's GitHub owner and name
88
+ * @param options - the cache, logger, token, API version, ref (`main` by default), and `fetch`
89
+ * @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
90
+ * @throws {Error} on a 401 response (check `SECRET_GITHUB_API_TOKEN`) or a repo without an owner
82
91
  * @see https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#list-check-runs-for-a-git-reference
83
92
  */
84
93
  export declare const fetch_github_check_runs: (repo_info: GithubRepoInfo, options?: {
@@ -87,5 +96,11 @@ export declare const fetch_github_check_runs: (repo_info: GithubRepoInfo, option
87
96
  token?: string;
88
97
  api_version?: string;
89
98
  ref?: string;
90
- }) => Promise<GithubCheckRunsItem | null>;
99
+ fetch?: typeof globalThis.fetch;
100
+ }) => Promise<Result<{
101
+ value: GithubCheckRunsItem | null;
102
+ }, {
103
+ status: number;
104
+ message: string;
105
+ }>>;
91
106
  //# sourceMappingURL=github.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"github.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;iBAO5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAClE,eAAO,MAAM,kBAAkB;;;;;;;kBAA6B,CAAC;AAC7D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,0BAA0B,GACtC,WAAW,cAAc,EACzB,UAAS;IACR,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CAChB,KACJ,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAsBnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;iBAK9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACtE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;iBAG1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,uBAAuB,GACnC,WAAW,cAAc,EACzB,UAAS;IACR,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACR,KACJ,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAoBpC,CAAC"}
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;iBAO5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAClE,eAAO,MAAM,kBAAkB;;;;;;;kBAA6B,CAAC;AAC7D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,0BAA0B,GACtC,WAAW,cAAc,EACzB,UAAS;IACR,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAC3B,KACJ,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAuBnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;iBAK9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACtE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;iBAG1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GACnC,WAAW,cAAc,EACzB,UAAS;IACR,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAC3B,KACJ,OAAO,CAAC,MAAM,CAAC;IAAE,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAA;CAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB5F,CAAC"}
package/dist/github.js CHANGED
@@ -16,7 +16,7 @@ export const GithubPullRequests = z.array(GithubPullRequest);
16
16
  * @see https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests
17
17
  */
18
18
  export const fetch_github_pull_requests = async (repo_info, options = {}) => {
19
- const { cache, log, token, api_version } = options;
19
+ const { cache, log, token, api_version, fetch } = options;
20
20
  if (!repo_info.owner_name)
21
21
  throw Error('owner_name is required');
22
22
  const headers = api_version ? new Headers({ 'x-github-api-version': api_version }) : undefined;
@@ -26,7 +26,8 @@ export const fetch_github_pull_requests = async (repo_info, options = {}) => {
26
26
  parse: GithubPullRequests.parse,
27
27
  token,
28
28
  cache,
29
- log
29
+ log,
30
+ fetch
30
31
  });
31
32
  if (!fetched.ok) {
32
33
  // TODO @many this is messy but I think it's the main case we need to worry about?
@@ -51,10 +52,17 @@ export const GithubCheckRuns = z.object({
51
52
  check_runs: z.array(GithubCheckRunsItem)
52
53
  });
53
54
  /**
55
+ * Fetches the check runs on `ref` and reduces them to one overall status and
56
+ * conclusion.
57
+ *
58
+ * @param repo_info - the repo's GitHub owner and name
59
+ * @param options - the cache, logger, token, API version, ref (`main` by default), and `fetch`
60
+ * @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
61
+ * @throws {Error} on a 401 response (check `SECRET_GITHUB_API_TOKEN`) or a repo without an owner
54
62
  * @see https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#list-check-runs-for-a-git-reference
55
63
  */
56
64
  export const fetch_github_check_runs = async (repo_info, options = {}) => {
57
- const { cache, log, token, api_version, ref = 'main' } = options;
65
+ const { cache, log, token, api_version, ref = 'main', fetch } = options;
58
66
  if (!repo_info.owner_name)
59
67
  throw Error('owner_name is required');
60
68
  const headers = api_version ? new Headers({ 'x-github-api-version': api_version }) : undefined;
@@ -64,16 +72,17 @@ export const fetch_github_check_runs = async (repo_info, options = {}) => {
64
72
  parse: (v) => reduce_check_runs(GithubCheckRuns.parse(v).check_runs),
65
73
  token,
66
74
  cache,
67
- log
75
+ log,
76
+ fetch
68
77
  });
69
78
  if (!fetched.ok) {
70
79
  // TODO @many this is messy but I think it's the main case we need to worry about?
71
80
  if (fetched.status === 401) {
72
81
  throw Error('401 response fetching github CI status - check your SECRET_GITHUB_API_TOKEN');
73
82
  }
74
- return null;
83
+ return { ok: false, status: fetched.status, message: fetched.message };
75
84
  }
76
- return fetched.value;
85
+ return { ok: true, value: fetched.value };
77
86
  };
78
87
  const reduce_check_runs = (check_runs) => {
79
88
  if (!check_runs.length)
@@ -1 +1 @@
1
- {"version":3,"file":"github_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/github_helpers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,MAAM,iBAAiB,GAAG,CAAC,YAAY,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;AAEzF,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,EAAE,iBAAiB,CAAC;CAChC;AAED,eAAO,MAAM,gBAAgB,GAC5B,OAAO,KAAK,CAAC,IAAI,CAAC,EAClB,sBAAsB,iBAAiB,KACrC,KAAK,CAAC,eAAe,CAYI,CAAC;AAE7B,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,EAAE,MAAM,iBAAiB,KAAG,MACtB,CAAC"}
1
+ {"version":3,"file":"github_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/github_helpers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,MAAM,iBAAiB,GAAG,CAAC,YAAY,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;AAEzF,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,EAAE,iBAAiB,CAAC;CAChC;AAED,eAAO,MAAM,gBAAgB,GAC5B,OAAO,KAAK,CAAC,IAAI,CAAC,EAClB,sBAAsB,iBAAiB,KACrC,KAAK,CAAC,eAAe,CAKtB,CAAC;AAEH,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,EAAE,MAAM,iBAAiB,KAAG,MACtB,CAAC"}
@@ -1,13 +1,5 @@
1
1
  import { ensure_end } from '@fuzdev/fuz_util/string.ts';
2
- export const to_pull_requests = (repos, filter_pull_request) => repos
3
- .flatMap((repo) => {
4
- if (!repo.pull_requests)
5
- return null;
6
- // TODO hacky, figure out the data structure
7
- return repo.pull_requests.map((pull_request) => repo.package_json.homepage &&
8
- (!filter_pull_request || filter_pull_request(pull_request, repo))
9
- ? { repo, pull_request }
10
- : null);
11
- })
12
- .filter((v) => v !== null);
2
+ export const to_pull_requests = (repos, filter_pull_request) => repos.flatMap((repo) => (repo.pull_requests ?? [])
3
+ .filter((pull_request) => !filter_pull_request || filter_pull_request(pull_request, repo))
4
+ .map((pull_request) => ({ repo, pull_request })));
13
5
  export const to_pull_url = (repo_url, pull) => ensure_end(repo_url, '/') + 'pull/' + pull.number;
@@ -1,18 +1,40 @@
1
1
  import type { Task } from '@fuzdev/gro';
2
2
  import { z } from 'zod';
3
+ import type { Logger } from '@fuzdev/fuz_util/log.ts';
4
+ import { type ResolveGitopsReposOptions } from './gitops_task_helpers.js';
5
+ import { type LocalRepo } from './local_repo.js';
3
6
  /** @nodocs */
4
7
  export declare const Args: z.ZodObject<{
5
8
  config: z.ZodDefault<z.ZodString>;
6
- dir: z.ZodOptional<z.ZodString>;
9
+ registry: z.ZodOptional<z.ZodString>;
7
10
  format: z.ZodDefault<z.ZodEnum<{
8
11
  stdout: "stdout";
9
12
  json: "json";
10
13
  markdown: "markdown";
11
14
  }>>;
12
15
  outfile: z.ZodOptional<z.ZodString>;
13
- sync: z.ZodDefault<z.ZodBoolean>;
14
16
  }, z.core.$strict>;
15
17
  export type Args = z.infer<typeof Args>;
16
18
  /** @nodocs */
17
19
  export declare const task: Task<Args>;
20
+ /**
21
+ * The side effects `run_gitops_analyze` reaches through, injectable for tests.
22
+ *
23
+ * @nodocs
24
+ */
25
+ export interface GitopsAnalyzeDeps {
26
+ /** Loads the configured repos as they sit (`get_gitops_ready`). */
27
+ load_repos: (options: ResolveGitopsReposOptions) => Promise<{
28
+ local_repos: Array<LocalRepo>;
29
+ }>;
30
+ }
31
+ /**
32
+ * Runs `gro gitops_analyze`: loads the repos as they sit, logs the readiness
33
+ * block, and outputs the dependency analysis. Under `--format json` or
34
+ * `markdown` without `--outfile`, the log goes to stderr and stdout carries
35
+ * the document alone (`route_human_output`).
36
+ *
37
+ * @nodocs
38
+ */
39
+ export declare const run_gitops_analyze: (args: Args, log: Logger, deps?: Partial<GitopsAnalyzeDeps>) => Promise<void>;
18
40
  //# sourceMappingURL=gitops_analyze.task.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gitops_analyze.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_analyze.task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,cAAc;AACd,eAAO,MAAM,IAAI;;;;;;;;;;kBAqBf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC,cAAc;AACd,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CAkC3B,CAAC"}
1
+ {"version":3,"file":"gitops_analyze.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gitops_analyze.task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAGN,KAAK,yBAAyB,EAC9B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAe9D,cAAc;AACd,eAAO,MAAM,IAAI;;;;;;;;;kBAiBf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC,cAAc;AACd,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CAM3B,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,mEAAmE;IACnE,UAAU,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,OAAO,CAAC;QAAE,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/F;AAMD;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAC9B,MAAM,IAAI,EACV,KAAK,MAAM,EACX,OAAM,OAAO,CAAC,iBAAiB,CAAM,KACnC,OAAO,CAAC,IAAI,CAiCd,CAAC"}
@@ -1,10 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  import { styleText as st } from 'node:util';
3
- import { get_gitops_ready } from './gitops_task_helpers.js';
3
+ import { get_gitops_ready, log_readiness_block } from './gitops_task_helpers.js';
4
4
  import { repo_is_npm } from './local_repo.js';
5
5
  import { analyze_repos } from './graph_validation.js';
6
6
  import { format_wildcard_dependencies, format_dev_cycles, format_production_cycles } from './log_helpers.js';
7
- import { format_and_output } from './output_helpers.js';
7
+ import { format_and_output, output_is_machine, route_human_output } from './output_helpers.js';
8
8
  import { GITOPS_CONFIG_PATH_DEFAULT } from './gitops_constants.js';
9
9
  /** @nodocs */
10
10
  export const Args = z.strictObject({
@@ -12,48 +12,61 @@ export const Args = z.strictObject({
12
12
  .string()
13
13
  .meta({ description: 'path to the gitops config file, absolute or relative to the cwd' })
14
14
  .default(GITOPS_CONFIG_PATH_DEFAULT),
15
- dir: z
15
+ registry: z
16
16
  .string()
17
- .meta({ description: 'path containing the repos, defaults to the parent of the config dir' })
17
+ .meta({
18
+ description: 'path to the repos.toml registry, when `repos` would not find it walking up from the cwd'
19
+ })
18
20
  .optional(),
19
21
  format: z
20
22
  .enum(['stdout', 'json', 'markdown'])
21
23
  .meta({ description: 'output format' })
22
24
  .default('stdout'),
23
- outfile: z.string().meta({ description: 'write output to file instead of logging' }).optional(),
24
- sync: z
25
- .boolean()
26
- .meta({
27
- description: 'sync repos (switch branch, pull, install) before analyzing instead of reading the working tree as-is'
28
- })
29
- .default(false)
25
+ outfile: z.string().meta({ description: 'write output to file instead of logging' }).optional()
30
26
  });
31
27
  /** @nodocs */
32
28
  export const task = {
33
29
  Args,
34
30
  summary: 'analyze dependency structure and relationships across repos',
35
31
  run: async ({ args, log }) => {
36
- const { config, dir, format, outfile, sync } = args;
37
- // Get repos ready (without downloading); read the working tree as-is unless `--sync`
38
- const { local_repos } = await get_gitops_ready({ config, dir, download: false, sync, log });
39
- // Only npm packages form the dependency graph; note any non-npm repos (e.g. cargo)
40
- // that are excluded so the omission isn't silent.
41
- const non_npm_repos = local_repos.filter((r) => !repo_is_npm(r));
42
- if (non_npm_repos.length > 0) {
43
- log.info(st('dim', `excluding ${non_npm_repos.length} non-npm repo(s) from analysis (dashboard-only): ` +
44
- non_npm_repos.map((r) => r.library.name).join(', ')));
45
- }
46
- // Build the dependency graph and analyze cycles/wildcards (tolerating cycles)
47
- const { graph, analysis, publishing_order } = analyze_repos(local_repos);
48
- // Format and output using output_helpers
49
- const data = {
50
- graph,
51
- analysis,
52
- publishing_order
53
- };
54
- await format_and_output(data, create_formatters(), { format, outfile, log });
32
+ await run_gitops_analyze(args, log);
55
33
  }
56
34
  };
35
+ const default_gitops_analyze_deps = {
36
+ load_repos: get_gitops_ready
37
+ };
38
+ /**
39
+ * Runs `gro gitops_analyze`: loads the repos as they sit, logs the readiness
40
+ * block, and outputs the dependency analysis. Under `--format json` or
41
+ * `markdown` without `--outfile`, the log goes to stderr and stdout carries
42
+ * the document alone (`route_human_output`).
43
+ *
44
+ * @nodocs
45
+ */
46
+ export const run_gitops_analyze = async (args, log, deps = {}) => {
47
+ const { load_repos } = { ...default_gitops_analyze_deps, ...deps };
48
+ const { config, registry, format, outfile } = args;
49
+ const write_stdout = route_human_output(log, output_is_machine(format, outfile));
50
+ // Read the working trees as they sit, and say which aren't at rest
51
+ const { local_repos } = await load_repos({ config, registry, log });
52
+ log_readiness_block(local_repos, log);
53
+ // Only npm packages form the dependency graph; note any non-npm repos (e.g. cargo)
54
+ // that are excluded so the omission isn't silent.
55
+ const non_npm_repos = local_repos.filter((r) => !repo_is_npm(r));
56
+ if (non_npm_repos.length > 0) {
57
+ log.info(st('dim', `excluding ${non_npm_repos.length} non-npm repo(s) from analysis (dashboard-only): ` +
58
+ non_npm_repos.map((r) => r.library.name).join(', ')));
59
+ }
60
+ // Build the dependency graph and analyze cycles/wildcards (tolerating cycles)
61
+ const { graph, analysis, publishing_order } = analyze_repos(local_repos);
62
+ // Format and output using output_helpers
63
+ const data = {
64
+ graph,
65
+ analysis,
66
+ publishing_order
67
+ };
68
+ await format_and_output(data, create_formatters(), { format, outfile, log, write_stdout });
69
+ };
57
70
  // Create formatters for output_helpers
58
71
  const create_formatters = () => ({
59
72
  json: (data) => format_json(data.graph, data.analysis, data.publishing_order),
@@ -1,92 +1,47 @@
1
1
  /**
2
- * Configuration types and normalization for gitops multi-repo management.
2
+ * The gitops config: which repos a project's gitops tasks work over, as a flat
3
+ * list of `repos.toml` registry keys.
3
4
  *
4
- * Two-phase configuration system:
5
- * - `RawGitopsConfig` - User-friendly format with optional fields and flexible types
6
- * - `GitopsConfig` - Internal format with required fields and strict types
7
- *
8
- * This allows users to provide minimal configs (e.g., just URLs as strings) while
9
- * the system works with normalized configs internally for type safety.
5
+ * Every other fact about a repo — its dir, URL, branch, visibility, `ci`, and
6
+ * `archived` — comes from the registry, through `repos status --json`, so the
7
+ * config declares nothing that could drift from it.
10
8
  *
11
9
  * @module
12
10
  */
13
- import type { Url } from '@fuzdev/fuz_util/url.ts';
14
- import type { GitBranch } from '@fuzdev/fuz_util/git.ts';
15
- export interface GitopsConfig {
16
- repos: Array<GitopsRepoConfig>;
17
- repos_dir: string;
18
- }
19
- export type CreateGitopsConfig = (base_config: GitopsConfig) => RawGitopsConfig | Promise<RawGitopsConfig>;
20
- export interface RawGitopsConfig {
21
- repos?: Array<Url | RawGitopsRepoConfig>;
22
- repos_dir?: string;
11
+ import { z } from 'zod';
12
+ import type { ReposEntryStatus } from './repos_status.js';
13
+ /** A project's gitops config. */
14
+ export declare const GitopsConfig: z.ZodObject<{
15
+ repos: z.ZodArray<z.ZodString>;
16
+ }, z.core.$strict>;
17
+ export type GitopsConfig = z.infer<typeof GitopsConfig>;
18
+ /** A config module's default export in function form. */
19
+ export type CreateGitopsConfig = () => GitopsConfig | Promise<GitopsConfig>;
20
+ export interface GitopsConfigModule {
21
+ readonly default: GitopsConfig | CreateGitopsConfig;
23
22
  }
24
23
  /**
25
- * Visibility of a repo on its host, mirroring the host's own model
26
- * (e.g. GitHub's `visibility` field). Named to avoid confusion with the npm
27
- * `package.json` `private` flag, which is a separate publishing concern.
24
+ * The private repos a public host package must not publish. `gitops_sync` writes
25
+ * every configured repo's GitHub metadata into the host project's generated
26
+ * `repos.json` — a public site's data when the host package is public — so a
27
+ * private repo in that config would leak. Empty when the host is private.
28
+ *
29
+ * @param entries - the configured repos' registry entries
30
+ * @param host_is_private - whether the host `package.json` sets `private: true`
28
31
  */
29
- export type GitopsRepoVisibility = 'public' | 'private';
30
- export interface GitopsRepoConfig {
31
- /**
32
- * The HTTPS URL to the repo. Does not include a `.git` suffix.
33
- * @example
34
- * ```ts
35
- * 'https://github.com/fuzdev/fuz_ui'
36
- * ```
37
- */
38
- repo_url: Url;
39
- /**
40
- * Relative or absolute path to the repo's local directory.
41
- * If `null`, the directory is inferred from the URL and cwd.
42
- * @example
43
- * ```ts
44
- * 'relative/path/to/repo'
45
- * ```
46
- * @example
47
- * ```ts
48
- * '/absolute/path/to/repo'
49
- * ```
50
- */
51
- repo_dir: string | null;
52
- /**
53
- * The branch name to use when fetching the repo. Defaults to `main`.
54
- */
55
- branch: GitBranch;
56
- /**
57
- * Visibility of the repo on its host. Defaults to `'public'`.
58
- */
59
- visibility: GitopsRepoVisibility;
60
- /**
61
- * Whether the repo runs CI. Defaults to `true` for public repos and `false`
62
- * for private repos, unless set explicitly.
63
- */
64
- ci: boolean;
65
- /**
66
- * Whether the repo is archived (read-only) on its host. Defaults to `false`.
67
- */
68
- archived: boolean;
69
- }
70
- export interface RawGitopsRepoConfig {
71
- repo_url: Url;
72
- repo_dir?: string | null;
73
- branch?: GitBranch;
74
- /** Visibility of the repo on its host. Defaults to `'public'`. */
75
- visibility?: GitopsRepoVisibility;
76
- /** Whether the repo runs CI. Defaults to `true` for public, `false` for private. */
77
- ci?: boolean;
78
- /** Whether the repo is archived (read-only) on its host. Defaults to `false`. */
79
- archived?: boolean;
80
- }
81
- export declare const create_empty_gitops_config: () => GitopsConfig;
32
+ export declare const gitops_config_leaked_private_repos: (entries: ReadonlyArray<ReposEntryStatus>, host_is_private: boolean) => Array<ReposEntryStatus>;
82
33
  /**
83
- * Transforms a `RawGitopsConfig` to the more strict `GitopsConfig`.
84
- * This allows users to provide a more relaxed config.
34
+ * Loads a gitops config module and validates it.
35
+ *
36
+ * @returns the config, or `null` when no file exists at `config_path`
37
+ * @throws {Error} if the module's default export isn't a valid config
85
38
  */
86
- export declare const normalize_gitops_config: (raw_config: RawGitopsConfig) => GitopsConfig;
87
- export interface GitopsConfigModule {
88
- readonly default: RawGitopsConfig | CreateGitopsConfig;
89
- }
90
39
  export declare const load_gitops_config: (config_path: string) => Promise<GitopsConfig | null>;
40
+ /**
41
+ * Validates a loaded config value: registry keys, each listed once.
42
+ *
43
+ * @throws {Error} naming the config and what's wrong with it
44
+ */
45
+ export declare const parse_gitops_config: (raw: unknown, config_path: string) => GitopsConfig;
91
46
  export declare const validate_gitops_config_module: (config_module: any, config_path: string) => asserts config_module is GitopsConfigModule;
92
47
  //# sourceMappingURL=gitops_config.d.ts.map