relacher 0.0.4-rc.2 → 0.0.4-rc.3

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.
@@ -1,4 +1,9 @@
1
1
  import { type DependencyList } from './shared';
2
+ declare module './shared' {
3
+ interface DependencyList {
4
+ couple(a: string, b: string): this;
5
+ }
6
+ }
2
7
  export interface CargoBuilderOptions {
3
8
  syncWorkspaceDeps?: boolean;
4
9
  }
@@ -9,6 +9,7 @@ export declare function matchBumpSize(message: string, sizes: SizePatterns): Bum
9
9
  export declare function evaluateCommitsBump(commits: Commit[], sizes: SizePatterns): BumpSize;
10
10
  export declare function topologicalSort(items: IntermediateReport[]): IntermediateReport[];
11
11
  export declare function propagateBumps(sorted: IntermediateReport[], rules?: CascadeRules): void;
12
+ export declare function propagateCoupledBumps(sorted: IntermediateReport[]): void;
12
13
  export declare function initReportItems(cargoDeps: DependencyConfig[], vcs: VcsProvider, sizes: SizePatterns, cwd: string): Promise<IntermediateReport[]>;
13
14
  export declare function prepare(cargoDeps: DependencyConfig[], vcs: VcsProvider, options?: PrepareOptions): Promise<DependencyUpdateReport[]>;
14
15
  export declare function finalizeReports(sorted: IntermediateReport[]): DependencyUpdateReport[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relacher",
3
- "version": "0.0.4-rc.2",
3
+ "version": "0.0.4-rc.3",
4
4
  "description": "Scriptable release orchestration library for monorepos",
5
5
  "type": "module",
6
6
  "private": false,
@@ -8,6 +8,12 @@ import { tomlUpdate, tomlFallback } from '../updater';
8
8
 
9
9
  import { decorateList, type DependencyList } from './shared';
10
10
 
11
+ declare module './shared' {
12
+ interface DependencyList {
13
+ couple(a: string, b: string): this;
14
+ }
15
+ }
16
+
11
17
  export interface CargoBuilderOptions {
12
18
  syncWorkspaceDeps?: boolean;
13
19
  }
@@ -237,22 +243,41 @@ function buildCrateConfig(
237
243
  };
238
244
  }
239
245
 
246
+ function coupleConfigs(configs: any[], a: string, b: string): boolean {
247
+ const itemA = configs.find((d) => d.name === a);
248
+ const itemB = configs.find((d) => d.name === b);
249
+ if (itemA && itemB) {
250
+ itemA.coupled = itemA.coupled || [];
251
+ itemB.coupled = itemB.coupled || [];
252
+ if (!itemA.coupled.includes(b)) itemA.coupled.push(b);
253
+ if (!itemB.coupled.includes(a)) itemB.coupled.push(a);
254
+ }
255
+ return true;
256
+ }
257
+
258
+ function scanWorkspace(workspaceRoot: string, rootDoc: any): CrateScanner {
259
+ const scanner = new CrateScanner(workspaceRoot, rootDoc.workspace?.dependencies || {});
260
+ if (rootDoc.workspace) registerWorkspaceMembers(scanner, rootDoc);
261
+ else if (rootDoc.package?.name) scanner.registerCrate(workspaceRoot);
262
+ runRecursiveDiscovery(scanner);
263
+ return scanner;
264
+ }
265
+
240
266
  export function cargoDeps(cwd: string, options?: CargoBuilderOptions): DependencyList {
241
267
  const workspaceRoot = findCargoWorkspaceRoot(cwd);
242
268
  const rootCargo = path.join(workspaceRoot, 'Cargo.toml');
243
269
  if (!fs.existsSync(rootCargo)) return decorateList([]);
244
270
  try {
245
271
  const rootDoc = parse(fs.readFileSync(rootCargo, 'utf8')) as any;
246
- const scanner = new CrateScanner(workspaceRoot, rootDoc.workspace?.dependencies || {});
247
- if (rootDoc.workspace) registerWorkspaceMembers(scanner, rootDoc);
248
- else if (rootDoc.package?.name) scanner.registerCrate(workspaceRoot);
249
- runRecursiveDiscovery(scanner);
272
+ const scanner = scanWorkspace(workspaceRoot, rootDoc);
250
273
  const hasLock = fs.existsSync(path.join(workspaceRoot, 'Cargo.lock'));
251
274
  const localNames = new Set(scanner.crateInfos.map((c) => c.name));
252
275
  const configs = scanner.crateInfos.map((info) =>
253
276
  buildCrateConfig(info, workspaceRoot, localNames, hasLock, scanner.workspaceDeps, options),
254
277
  );
255
- return decorateList(configs);
278
+ const list = decorateList(configs) as any;
279
+ list.couple = (a: string, b: string) => (coupleConfigs(configs, a, b) ? list : list);
280
+ return list;
256
281
  } catch {
257
282
  return decorateList([]);
258
283
  }
package/src/prepare.ts CHANGED
@@ -125,6 +125,39 @@ export function propagateBumps(sorted: IntermediateReport[], rules?: CascadeRule
125
125
  }
126
126
  }
127
127
 
128
+ export function propagateCoupledBumps(sorted: IntermediateReport[]): void {
129
+ const priority: Record<BumpSize, number> = { skip: 0, patch: 1, minor: 2, major: 3 };
130
+ const bumps: BumpSize[] = ['skip', 'patch', 'minor', 'major'];
131
+
132
+ let changed = true;
133
+ while (changed) {
134
+ changed = false;
135
+ for (const item of sorted) {
136
+ const coupledNames = (item as any).coupled || [];
137
+ for (const name of coupledNames) {
138
+ const other = sorted.find((r) => r.name === name);
139
+ if (other) {
140
+ const maxPriority = Math.max(priority[item.bump], priority[other.bump]);
141
+ const targetBump = bumps[maxPriority]!;
142
+ if (item.bump !== targetBump) {
143
+ item.bump = targetBump;
144
+ changed = true;
145
+ }
146
+ if (other.bump !== targetBump) {
147
+ other.bump = targetBump;
148
+ changed = true;
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ for (const item of sorted) {
156
+ item.newVersion =
157
+ item.bump !== 'skip' ? bumpVersion(item.currentVersion, item.bump) : item.currentVersion;
158
+ }
159
+ }
160
+
128
161
  export async function initReportItems(
129
162
  cargoDeps: DependencyConfig[],
130
163
  vcs: VcsProvider,
@@ -165,7 +198,8 @@ export async function initReportItems(
165
198
  depends: dep.depends || [],
166
199
  isErroneous,
167
200
  isFirstRelease: isFallback,
168
- });
201
+ coupled: (dep as any).coupled || [],
202
+ } as any);
169
203
  }
170
204
  return reports;
171
205
  }
@@ -180,6 +214,7 @@ export async function prepare(
180
214
  const items = await initReportItems(cargoDeps, vcs, sizes, cwd);
181
215
  const sorted = topologicalSort(items);
182
216
  propagateBumps(sorted, options.cascade);
217
+ propagateCoupledBumps(sorted);
183
218
  return finalizeReports(sorted);
184
219
  }
185
220
 
@@ -203,6 +238,7 @@ export function finalizeReports(sorted: IntermediateReport[]): DependencyUpdateR
203
238
  depends: item.depends,
204
239
  isErroneous: item.isErroneous,
205
240
  isFirstRelease: item.isFirstRelease,
241
+ coupled: (item as any).coupled || [],
206
242
  }));
207
243
  }
208
244