@fuzdev/fuz_gitops 0.57.0 → 0.59.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 (93) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +15 -0
  3. package/dist/ModulesDetail.svelte +5 -4
  4. package/dist/ModulesDetail.svelte.d.ts +3 -3
  5. package/dist/ModulesDetail.svelte.d.ts.map +1 -1
  6. package/dist/ModulesNav.svelte +4 -4
  7. package/dist/ModulesNav.svelte.d.ts +3 -3
  8. package/dist/ModulesNav.svelte.d.ts.map +1 -1
  9. package/dist/ModulesPage.svelte +5 -4
  10. package/dist/ModulesPage.svelte.d.ts +3 -3
  11. package/dist/ModulesPage.svelte.d.ts.map +1 -1
  12. package/dist/PageHeader.svelte +8 -4
  13. package/dist/PageHeader.svelte.d.ts +3 -3
  14. package/dist/PageHeader.svelte.d.ts.map +1 -1
  15. package/dist/PullRequestsDetail.svelte +5 -4
  16. package/dist/PullRequestsDetail.svelte.d.ts +3 -3
  17. package/dist/PullRequestsDetail.svelte.d.ts.map +1 -1
  18. package/dist/PullRequestsPage.svelte +9 -10
  19. package/dist/PullRequestsPage.svelte.d.ts +5 -5
  20. package/dist/PullRequestsPage.svelte.d.ts.map +1 -1
  21. package/dist/ReposTable.svelte +5 -4
  22. package/dist/ReposTable.svelte.d.ts +3 -3
  23. package/dist/ReposTable.svelte.d.ts.map +1 -1
  24. package/dist/ReposTree.svelte +6 -4
  25. package/dist/ReposTree.svelte.d.ts +3 -3
  26. package/dist/ReposTree.svelte.d.ts.map +1 -1
  27. package/dist/ReposTreeNav.svelte +6 -4
  28. package/dist/ReposTreeNav.svelte.d.ts +3 -3
  29. package/dist/ReposTreeNav.svelte.d.ts.map +1 -1
  30. package/dist/TablePage.svelte +5 -4
  31. package/dist/TablePage.svelte.d.ts +3 -3
  32. package/dist/TablePage.svelte.d.ts.map +1 -1
  33. package/dist/TreeItemPage.svelte +6 -4
  34. package/dist/TreeItemPage.svelte.d.ts +3 -3
  35. package/dist/TreeItemPage.svelte.d.ts.map +1 -1
  36. package/dist/TreePage.svelte +5 -4
  37. package/dist/TreePage.svelte.d.ts +3 -3
  38. package/dist/TreePage.svelte.d.ts.map +1 -1
  39. package/dist/fetch_repo_data.d.ts +1 -1
  40. package/dist/fetch_repo_data.js +1 -1
  41. package/dist/fs_fetch_value_cache.d.ts +1 -1
  42. package/dist/fs_fetch_value_cache.js +1 -1
  43. package/dist/gitops_analyze.task.d.ts +1 -1
  44. package/dist/gitops_analyze.task.d.ts.map +1 -1
  45. package/dist/gitops_analyze.task.js +6 -5
  46. package/dist/gitops_constants.d.ts +27 -0
  47. package/dist/gitops_constants.d.ts.map +1 -0
  48. package/dist/gitops_constants.js +26 -0
  49. package/dist/gitops_plan.task.d.ts +2 -2
  50. package/dist/gitops_plan.task.d.ts.map +1 -1
  51. package/dist/gitops_plan.task.js +7 -6
  52. package/dist/gitops_publish.task.d.ts +1 -1
  53. package/dist/gitops_publish.task.d.ts.map +1 -1
  54. package/dist/gitops_publish.task.js +7 -6
  55. package/dist/gitops_run.task.d.ts +14 -0
  56. package/dist/gitops_run.task.d.ts.map +1 -0
  57. package/dist/gitops_run.task.js +173 -0
  58. package/dist/gitops_sync.task.d.ts +1 -1
  59. package/dist/gitops_sync.task.d.ts.map +1 -1
  60. package/dist/gitops_sync.task.js +6 -5
  61. package/dist/gitops_task_helpers.d.ts +7 -3
  62. package/dist/gitops_task_helpers.d.ts.map +1 -1
  63. package/dist/gitops_task_helpers.js +16 -7
  64. package/dist/gitops_validate.task.d.ts +1 -1
  65. package/dist/gitops_validate.task.d.ts.map +1 -1
  66. package/dist/gitops_validate.task.js +6 -5
  67. package/dist/local_repo.d.ts +3 -1
  68. package/dist/local_repo.d.ts.map +1 -1
  69. package/dist/local_repo.js +34 -3
  70. package/dist/multi_repo_publisher.d.ts.map +1 -1
  71. package/dist/multi_repo_publisher.js +6 -6
  72. package/dist/publishing_plan.js +4 -4
  73. package/dist/repo_ops.d.ts.map +1 -1
  74. package/dist/repo_ops.js +2 -1
  75. package/package.json +11 -11
  76. package/src/lib/fetch_repo_data.ts +1 -1
  77. package/src/lib/fs_fetch_value_cache.ts +1 -1
  78. package/src/lib/gitops_analyze.task.ts +6 -5
  79. package/src/lib/gitops_constants.ts +30 -0
  80. package/src/lib/gitops_plan.task.ts +7 -6
  81. package/src/lib/gitops_publish.task.ts +7 -6
  82. package/src/lib/gitops_run.task.ts +218 -0
  83. package/src/lib/gitops_sync.task.ts +6 -5
  84. package/src/lib/gitops_task_helpers.ts +20 -9
  85. package/src/lib/gitops_validate.task.ts +6 -5
  86. package/src/lib/local_repo.ts +45 -2
  87. package/src/lib/multi_repo_publisher.ts +11 -6
  88. package/src/lib/publishing_plan.ts +4 -4
  89. package/src/lib/repo_ops.ts +2 -1
  90. package/dist/constants.d.ts +0 -9
  91. package/dist/constants.d.ts.map +0 -1
  92. package/dist/constants.js +0 -8
  93. package/src/lib/constants.ts +0 -8
@@ -27,12 +27,14 @@ import {DEFAULT_REPOS_DIR} from './paths.js';
27
27
  import type {GitOperations, NpmOperations} from './operations.js';
28
28
 
29
29
  export interface GetGitopsReadyOptions {
30
- path: string;
30
+ config: string;
31
31
  dir?: string;
32
32
  download: boolean;
33
33
  log?: Logger;
34
34
  git_ops?: GitOperations;
35
35
  npm_ops?: NpmOperations;
36
+ parallel?: boolean;
37
+ concurrency?: number;
36
38
  }
37
39
 
38
40
  /**
@@ -41,7 +43,7 @@ export interface GetGitopsReadyOptions {
41
43
  * Initialization sequence:
42
44
  * 1. Loads and normalizes config from `gitops.config.ts`
43
45
  * 2. Resolves local repo paths (creates missing with `--download`)
44
- * 3. Switches branches and pulls latest changes
46
+ * 3. Switches branches and pulls latest changes (in parallel by default)
45
47
  * 4. Auto-installs deps if package.json changed during pull
46
48
  *
47
49
  * Priority for path resolution:
@@ -51,6 +53,8 @@ export interface GetGitopsReadyOptions {
51
53
  *
52
54
  * @param options.git_ops for testing (defaults to real git operations)
53
55
  * @param options.npm_ops for testing (defaults to real npm operations)
56
+ * @param options.parallel whether to load repos in parallel (default: true)
57
+ * @param options.concurrency max concurrent repo loads (default: 5)
54
58
  * @returns initialized config and fully loaded repos ready for operations
55
59
  * @throws {TaskError} if config loading or repo resolution fails
56
60
  */
@@ -62,13 +66,13 @@ export const get_gitops_ready = async (
62
66
  gitops_config: GitopsConfig;
63
67
  local_repos: Array<LocalRepo>;
64
68
  }> => {
65
- const {path, dir, download, log, git_ops, npm_ops} = options;
66
- const config_path = resolve(path);
69
+ const {config, dir, download, log, git_ops, npm_ops, parallel, concurrency} = options;
70
+ const config_path = resolve(config);
67
71
  const gitops_config = await import_gitops_config(config_path);
68
72
 
69
73
  // Priority: explicit dir arg → config repos_dir → default (two dirs up from config)
70
74
  const repos_dir = resolve_gitops_paths({
71
- path,
75
+ config,
72
76
  dir,
73
77
  config_repos_dir: gitops_config.repos_dir,
74
78
  }).repos_dir;
@@ -88,13 +92,20 @@ export const get_gitops_ready = async (
88
92
  npm_ops,
89
93
  });
90
94
 
91
- const local_repos = await local_repos_load({local_repo_paths, log, git_ops, npm_ops});
95
+ const local_repos = await local_repos_load({
96
+ local_repo_paths,
97
+ log,
98
+ git_ops,
99
+ npm_ops,
100
+ parallel,
101
+ concurrency,
102
+ });
92
103
 
93
104
  return {config_path, repos_dir, gitops_config, local_repos};
94
105
  };
95
106
 
96
107
  export interface ResolveGitopsPathsOptions {
97
- path: string;
108
+ config: string;
98
109
  dir?: string;
99
110
  config_repos_dir?: string;
100
111
  }
@@ -102,8 +113,8 @@ export interface ResolveGitopsPathsOptions {
102
113
  export const resolve_gitops_paths = (
103
114
  options: ResolveGitopsPathsOptions,
104
115
  ): {config_path: string; repos_dir: string} => {
105
- const {path, dir, config_repos_dir} = options;
106
- const config_path = resolve(path);
116
+ const {config, dir, config_repos_dir} = options;
117
+ const config_path = resolve(config);
107
118
  const config_dir = dirname(config_path);
108
119
 
109
120
  // Priority: explicit dir arg → config repos_dir → default (parent of config dir)
@@ -8,16 +8,17 @@ import {DependencyGraphBuilder} from './dependency_graph.js';
8
8
  import {generate_publishing_plan, log_publishing_plan} from './publishing_plan.js';
9
9
  import {publish_repos, type PublishingOptions} from './multi_repo_publisher.js';
10
10
  import {log_dependency_analysis} from './log_helpers.js';
11
+ import {GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.js';
11
12
 
12
13
  /** @nodocs */
13
14
  export const Args = z.strictObject({
14
- path: z
15
+ config: z
15
16
  .string()
16
17
  .meta({description: 'path to the gitops config file, absolute or relative to the cwd'})
17
- .default('gitops.config.ts'),
18
+ .default(GITOPS_CONFIG_PATH_DEFAULT),
18
19
  dir: z
19
20
  .string()
20
- .meta({description: 'path containing the repos, defaults to the parent of the `path` dir'})
21
+ .meta({description: 'path containing the repos, defaults to the parent of the config dir'})
21
22
  .optional(),
22
23
  verbose: z.boolean().meta({description: 'show additional details'}).default(false),
23
24
  });
@@ -29,7 +30,7 @@ export const task: Task<Args> = {
29
30
  summary:
30
31
  'validate gitops configuration by running all read-only commands and checking for issues',
31
32
  run: async ({args, log}) => {
32
- const {path, dir, verbose} = args;
33
+ const {config, dir, verbose} = args;
33
34
 
34
35
  log.info(st('cyan', 'Running Gitops Validation Suite'));
35
36
  log.info(st('dim', 'This runs all read-only commands and checks for consistency.'));
@@ -49,7 +50,7 @@ export const task: Task<Args> = {
49
50
 
50
51
  // Load repos once (shared by all commands)
51
52
  log.info(st('dim', 'Loading repositories...'));
52
- const {local_repos} = await get_gitops_ready({path, dir, download: false, log});
53
+ const {local_repos} = await get_gitops_ready({config, dir, download: false, log});
53
54
  log.info(st('dim', ` Found ${local_repos.length} local repos`));
54
55
 
55
56
  // 1. Run gitops_analyze
@@ -6,11 +6,13 @@ import {join} from 'node:path';
6
6
  import {TaskError} from '@ryanatkn/gro';
7
7
  import type {Logger} from '@fuzdev/fuz_util/log.js';
8
8
  import {spawn} from '@fuzdev/fuz_util/process.js';
9
+ import {map_concurrent_settled} from '@fuzdev/fuz_util/async.js';
9
10
  import type {GitOperations, NpmOperations} from './operations.js';
10
11
  import {default_git_operations, default_npm_operations} from './operations_defaults.js';
11
12
 
12
13
  import type {GitopsConfig, GitopsRepoConfig} from './gitops_config.js';
13
14
  import type {ResolvedGitopsConfig} from './resolved_gitops_config.js';
15
+ import {GITOPS_CONCURRENCY_DEFAULT} from './gitops_constants.js';
14
16
 
15
17
  /**
16
18
  * Fully loaded local repo with Library and extracted dependency data.
@@ -280,16 +282,57 @@ export const local_repos_load = async ({
280
282
  log,
281
283
  git_ops = default_git_operations,
282
284
  npm_ops = default_npm_operations,
285
+ parallel = true,
286
+ concurrency = GITOPS_CONCURRENCY_DEFAULT,
283
287
  }: {
284
288
  local_repo_paths: Array<LocalRepoPath>;
285
289
  log?: Logger;
286
290
  git_ops?: GitOperations;
287
291
  npm_ops?: NpmOperations;
292
+ parallel?: boolean;
293
+ concurrency?: number;
288
294
  }): Promise<Array<LocalRepo>> => {
295
+ if (!parallel) {
296
+ // Sequential loading (original behavior)
297
+ const loaded: Array<LocalRepo> = [];
298
+ for (const local_repo_path of local_repo_paths) {
299
+ loaded.push(await local_repo_load({local_repo_path, log, git_ops, npm_ops})); // eslint-disable-line no-await-in-loop
300
+ }
301
+ return loaded;
302
+ }
303
+
304
+ // Parallel loading with concurrency limit
305
+ const results = await map_concurrent_settled(
306
+ local_repo_paths,
307
+ async (local_repo_path) => {
308
+ return local_repo_load({local_repo_path, log, git_ops, npm_ops});
309
+ },
310
+ concurrency,
311
+ );
312
+
313
+ // Check for failures and collect successes
289
314
  const loaded: Array<LocalRepo> = [];
290
- for (const local_repo_path of local_repo_paths) {
291
- loaded.push(await local_repo_load({local_repo_path, log, git_ops, npm_ops})); // eslint-disable-line no-await-in-loop
315
+ const errors: Array<{repo_name: string; error: string}> = [];
316
+
317
+ for (let i = 0; i < results.length; i++) {
318
+ const result = results[i]!;
319
+ if (result.status === 'fulfilled') {
320
+ loaded.push(result.value);
321
+ } else {
322
+ const repo_path = local_repo_paths[i]!;
323
+ errors.push({
324
+ repo_name: repo_path.repo_name,
325
+ error: String(result.reason),
326
+ });
327
+ }
292
328
  }
329
+
330
+ // If any repos failed to load, throw with details
331
+ if (errors.length > 0) {
332
+ const error_details = errors.map((e) => ` ${e.repo_name}: ${e.error}`).join('\n');
333
+ throw new TaskError(`Failed to load ${errors.length} repos:\n${error_details}`);
334
+ }
335
+
293
336
  return loaded;
294
337
  };
295
338
 
@@ -10,7 +10,10 @@ import {type PreflightOptions} from './preflight_checks.js';
10
10
  import {needs_update, is_breaking_change, detect_bump_type} from './version_utils.js';
11
11
  import type {GitopsOperations} from './operations.js';
12
12
  import {default_gitops_operations} from './operations_defaults.js';
13
- import {MAX_ITERATIONS} from './constants.js';
13
+ import {
14
+ GITOPS_MAX_ITERATIONS_DEFAULT,
15
+ GITOPS_NPM_WAIT_TIMEOUT_DEFAULT,
16
+ } from './gitops_constants.js';
14
17
  import {install_with_cache_healing} from './npm_install_helpers.js';
15
18
 
16
19
  /* eslint-disable no-await-in-loop */
@@ -90,9 +93,11 @@ export const publish_repos = async (
90
93
  let iteration = 0;
91
94
  let converged = false;
92
95
 
93
- while (!converged && iteration < MAX_ITERATIONS) {
96
+ while (!converged && iteration < GITOPS_MAX_ITERATIONS_DEFAULT) {
94
97
  iteration++;
95
- log?.info(st('cyan', `\n🚀 Publishing iteration ${iteration}/${MAX_ITERATIONS}...\n`));
98
+ log?.info(
99
+ st('cyan', `\n🚀 Publishing iteration ${iteration}/${GITOPS_MAX_ITERATIONS_DEFAULT}...\n`),
100
+ );
96
101
 
97
102
  // Track if any packages were published in this iteration
98
103
  let published_in_iteration = false;
@@ -157,7 +162,7 @@ export const publish_repos = async (
157
162
  max_attempts: 30,
158
163
  initial_delay: 1000,
159
164
  max_delay: 60000,
160
- timeout: options.max_wait || 600000, // 10 minutes default
165
+ timeout: options.max_wait ?? GITOPS_NPM_WAIT_TIMEOUT_DEFAULT,
161
166
  },
162
167
  log,
163
168
  });
@@ -245,7 +250,7 @@ export const publish_repos = async (
245
250
  if (!published_in_iteration) {
246
251
  converged = true;
247
252
  log?.info(st('green', `\n✓ Converged after ${iteration} iteration(s) - no new changesets\n`));
248
- } else if (iteration === MAX_ITERATIONS) {
253
+ } else if (iteration === GITOPS_MAX_ITERATIONS_DEFAULT) {
249
254
  // Count packages that still have changesets (not yet published)
250
255
  const pending_count = order.length - published.size;
251
256
  const estimated_iterations = Math.ceil(pending_count / 2); // Rough estimate
@@ -253,7 +258,7 @@ export const publish_repos = async (
253
258
  log?.warn(
254
259
  st(
255
260
  'yellow',
256
- `\n⚠️ Reached maximum iterations (${MAX_ITERATIONS}) without full convergence\n` +
261
+ `\n⚠️ Reached maximum iterations (${GITOPS_MAX_ITERATIONS_DEFAULT}) without full convergence\n` +
257
262
  ` ${pending_count} package(s) may still have changesets to process\n` +
258
263
  ` Estimated ${estimated_iterations} more iteration(s) needed - run 'gro gitops_publish' again\n`,
259
264
  ),
@@ -7,7 +7,7 @@ import {validate_dependency_graph} from './graph_validation.js';
7
7
  import {is_breaking_change, compare_bump_types, calculate_next_version} from './version_utils.js';
8
8
  import type {ChangesetOperations} from './operations.js';
9
9
  import {default_changeset_operations} from './operations_defaults.js';
10
- import {MAX_ITERATIONS} from './constants.js';
10
+ import {GITOPS_MAX_ITERATIONS_DEFAULT} from './gitops_constants.js';
11
11
  import type {DependencyGraph} from './dependency_graph.ts';
12
12
  import {
13
13
  calculate_dependency_updates,
@@ -247,7 +247,7 @@ export const generate_publishing_plan = async (
247
247
  let iteration = 0;
248
248
  let changed = true;
249
249
 
250
- while (changed && iteration < MAX_ITERATIONS) {
250
+ while (changed && iteration < GITOPS_MAX_ITERATIONS_DEFAULT) {
251
251
  changed = false;
252
252
  iteration++;
253
253
 
@@ -392,7 +392,7 @@ export const generate_publishing_plan = async (
392
392
  }
393
393
 
394
394
  // Check if we hit iteration limit without convergence
395
- if (iteration === MAX_ITERATIONS && changed) {
395
+ if (iteration === GITOPS_MAX_ITERATIONS_DEFAULT && changed) {
396
396
  // Calculate how many packages still need processing
397
397
  const pending_packages: Array<string> = [];
398
398
 
@@ -428,7 +428,7 @@ export const generate_publishing_plan = async (
428
428
  const pending_count = pending_packages.length;
429
429
  const estimated_iterations = Math.ceil(pending_count / 2); // Rough estimate
430
430
  warnings.push(
431
- `Reached maximum iterations (${MAX_ITERATIONS}) without full convergence - ` +
431
+ `Reached maximum iterations (${GITOPS_MAX_ITERATIONS_DEFAULT}) without full convergence - ` +
432
432
  `${pending_count} package(s) may still need processing: ${pending_packages.join(', ')}. ` +
433
433
  `Estimated ${estimated_iterations} more iteration(s) needed.`,
434
434
  );
@@ -15,6 +15,7 @@ import {join, resolve, dirname} from 'node:path';
15
15
 
16
16
  import {load_gitops_config} from './gitops_config.js';
17
17
  import {DEFAULT_REPOS_DIR} from './paths.js';
18
+ import {GITOPS_CONFIG_PATH_DEFAULT} from './gitops_constants.js';
18
19
 
19
20
  /** Default directories to exclude from file walking */
20
21
  export const DEFAULT_EXCLUDE_DIRS = [
@@ -84,7 +85,7 @@ export interface RepoPath {
84
85
  * @returns Array of repo info with name, path, and url
85
86
  */
86
87
  export const get_repo_paths = async (config_path?: string): Promise<Array<RepoPath>> => {
87
- const resolved_config_path = resolve(config_path ?? 'gitops.config.ts');
88
+ const resolved_config_path = resolve(config_path ?? GITOPS_CONFIG_PATH_DEFAULT);
88
89
  const config = await load_gitops_config(resolved_config_path);
89
90
 
90
91
  if (!config) {
@@ -1,9 +0,0 @@
1
- /**
2
- * Maximum number of iterations for fixed-point iteration during publishing.
3
- * Used in both plan generation and actual publishing to resolve transitive dependency cascades.
4
- *
5
- * In practice, most repos converge in 2-3 iterations.
6
- * Deep dependency chains may require more iterations.
7
- */
8
- export declare const MAX_ITERATIONS = 10;
9
- //# sourceMappingURL=constants.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/constants.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC"}
package/dist/constants.js DELETED
@@ -1,8 +0,0 @@
1
- /**
2
- * Maximum number of iterations for fixed-point iteration during publishing.
3
- * Used in both plan generation and actual publishing to resolve transitive dependency cascades.
4
- *
5
- * In practice, most repos converge in 2-3 iterations.
6
- * Deep dependency chains may require more iterations.
7
- */
8
- export const MAX_ITERATIONS = 10;
@@ -1,8 +0,0 @@
1
- /**
2
- * Maximum number of iterations for fixed-point iteration during publishing.
3
- * Used in both plan generation and actual publishing to resolve transitive dependency cascades.
4
- *
5
- * In practice, most repos converge in 2-3 iterations.
6
- * Deep dependency chains may require more iterations.
7
- */
8
- export const MAX_ITERATIONS = 10;