pgpm 5.24.4 → 5.25.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.
package/README.md CHANGED
@@ -376,12 +376,24 @@ pgpm kill --no-drop
376
376
 
377
377
  #### `pgpm test-packages`
378
378
 
379
- Run integration tests on all modules in a workspace. Creates a temporary database for each module, deploys, and optionally runs verify/revert/deploy cycles.
379
+ Run integration tests on the modules in a workspace. Creates a temporary database per tested module, deploys, and optionally runs verify/revert/deploy cycles.
380
+
381
+ By default only the **minimal covering set** is tested: the modules nothing else in the
382
+ workspace requires. Testing one of those deploys its whole dependency closure in
383
+ dependency order, so every module is still exercised — once per covering module instead
384
+ of once per module. Excluding a covering module promotes whatever it alone covered.
385
+
386
+ The property the default trades away is standalone deployability — that a module's own
387
+ `requires` is complete rather than satisfied by a sibling in the same database. Use
388
+ `--force-all` to get it back.
380
389
 
381
390
  ```bash
382
- # Test all modules in workspace (deploy only)
391
+ # Test the minimal covering set (deploy only)
383
392
  pgpm test-packages
384
393
 
394
+ # Test every module in its own database
395
+ pgpm test-packages --force-all
396
+
385
397
  # Run full deploy/verify/revert/deploy cycle
386
398
  pgpm test-packages --full-cycle
387
399
 
@@ -397,6 +409,7 @@ pgpm test-packages --full-cycle --continue-on-fail --exclude legacy-module
397
409
 
398
410
  **Options:**
399
411
 
412
+ - `--force-all` - Test every module in its own database instead of the minimal covering set
400
413
  - `--full-cycle` - Run full deploy/verify/revert/deploy cycle (default: deploy only)
401
414
  - `--continue-on-fail` - Continue testing all packages even after failures (default: stop on first failure)
402
415
  - `--exclude <modules>` - Comma-separated module names to exclude
@@ -1,3 +1,23 @@
1
1
  import { CLIOptions, Inquirerer, ParsedArgs } from 'inquirerer';
2
+ /**
3
+ * Reduce the module list to a minimal covering set: the modules that no other
4
+ * candidate requires, transitively.
5
+ *
6
+ * Testing a module deploys its whole dependency closure in dependency order, so
7
+ * a module that some other candidate requires is already deployed, verified,
8
+ * reverted and re-deployed inside that candidate's database. Selection runs over
9
+ * the post-exclude candidates, so excluding a covering module promotes whatever
10
+ * it alone covered rather than dropping it.
11
+ *
12
+ * The property this trades away is standalone deployability — that a module's own
13
+ * `requires` is complete rather than satisfied by a sibling in the same database.
14
+ * Use --force-all to get it back.
15
+ */
16
+ export declare function minimalCoveringSet(moduleMap: Record<string, {
17
+ requires: string[];
18
+ }>, candidateNames: string[]): {
19
+ selected: string[];
20
+ coveredBy: Map<string, string>;
21
+ };
2
22
  declare const _default: (argv: Partial<ParsedArgs>, _prompter: Inquirerer, _options: CLIOptions) => Promise<never>;
3
23
  export default _default;
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.minimalCoveringSet = minimalCoveringSet;
6
7
  const core_1 = require("@pgpmjs/core");
7
8
  const env_1 = require("@pgpmjs/env");
8
9
  const logger_1 = require("@pgpmjs/logger");
@@ -20,22 +21,85 @@ Test Packages Command:
20
21
 
21
22
  pgpm test-packages [OPTIONS]
22
23
 
23
- Run integration tests on all PGPM packages in a workspace.
24
+ Run integration tests on the PGPM packages in a workspace.
24
25
  Tests each package with a deploy/verify/revert/deploy cycle.
25
26
 
27
+ By default only the minimal covering set is tested: the modules nothing else
28
+ in the workspace requires. Deploying one of those deploys everything it
29
+ requires first, so every module in the workspace is still exercised — just
30
+ once per covering module instead of once per module.
31
+
26
32
  Options:
27
33
  --help, -h Show this help message
28
34
  --exclude <pkgs> Comma-separated module names to exclude
29
35
  --continue-on-fail Continue testing all packages even after failures
30
36
  --full-cycle Run full deploy/verify/revert/deploy cycle (default: deploy only)
37
+ --force-all Test every module in its own database (pre-minimal-set behavior)
31
38
  --cwd <directory> Working directory (default: current directory)
32
39
 
33
40
  Examples:
34
- pgpm test-packages Test all packages (stops on first failure)
41
+ pgpm test-packages Test the minimal covering set
42
+ pgpm test-packages --force-all Test every module in its own database
35
43
  pgpm test-packages --full-cycle Run full test cycle with verify/revert
36
44
  pgpm test-packages --continue-on-fail Test all packages, collect all failures
37
45
  pgpm test-packages --exclude my-module Exclude specific modules
38
46
  `;
47
+ /**
48
+ * Reduce the module list to a minimal covering set: the modules that no other
49
+ * candidate requires, transitively.
50
+ *
51
+ * Testing a module deploys its whole dependency closure in dependency order, so
52
+ * a module that some other candidate requires is already deployed, verified,
53
+ * reverted and re-deployed inside that candidate's database. Selection runs over
54
+ * the post-exclude candidates, so excluding a covering module promotes whatever
55
+ * it alone covered rather than dropping it.
56
+ *
57
+ * The property this trades away is standalone deployability — that a module's own
58
+ * `requires` is complete rather than satisfied by a sibling in the same database.
59
+ * Use --force-all to get it back.
60
+ */
61
+ function minimalCoveringSet(moduleMap, candidateNames) {
62
+ const candidates = new Set(candidateNames);
63
+ // name -> its transitive requires, restricted to the candidates
64
+ const closures = new Map();
65
+ for (const name of candidates) {
66
+ const { resolved } = (0, core_1.resolveExtensionDependencies)(name, moduleMap);
67
+ closures.set(name, resolved.filter((dep) => dep !== name && candidates.has(dep)));
68
+ }
69
+ const coveredBy = new Map();
70
+ for (const [name, deps] of closures) {
71
+ for (const dep of deps) {
72
+ if (!coveredBy.has(dep)) {
73
+ coveredBy.set(dep, name);
74
+ }
75
+ }
76
+ }
77
+ const selected = candidateNames.filter((name) => !coveredBy.has(name));
78
+ // Nothing may fall out of the selection. Covering-set membership is derived
79
+ // from pairwise closures, so assert the union actually reaches every candidate
80
+ // rather than trusting that argument.
81
+ const covered = new Set();
82
+ for (const name of selected) {
83
+ covered.add(name);
84
+ for (const dep of closures.get(name) ?? []) {
85
+ covered.add(dep);
86
+ }
87
+ }
88
+ const uncovered = candidateNames.filter((name) => !covered.has(name));
89
+ if (uncovered.length > 0) {
90
+ throw new Error(`Minimal module selection left ${uncovered.length} module(s) untested: ${uncovered.join(', ')}. ` +
91
+ 'Re-run with --force-all and report this as a pgpm bug.');
92
+ }
93
+ return { selected, coveredBy };
94
+ }
95
+ function selectMinimalModules(workspacePkg, candidates) {
96
+ const { selected, coveredBy } = minimalCoveringSet(workspacePkg.getModuleMap(), candidates.map((mod) => mod.getModuleName()));
97
+ const selectedNames = new Set(selected);
98
+ return {
99
+ selected: candidates.filter((mod) => selectedNames.has(mod.getModuleName())),
100
+ coveredBy
101
+ };
102
+ }
39
103
  function dbSafeName(moduleName) {
40
104
  return `test_${moduleName}`.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase();
41
105
  }
@@ -209,6 +273,7 @@ exports.default = async (argv, _prompter, _options) => {
209
273
  const continueOnFail = argv['continue-on-fail'] === true || argv.continueOnFail === true;
210
274
  const stopOnFail = !continueOnFail;
211
275
  const fullCycle = argv['full-cycle'] === true || argv.fullCycle === true;
276
+ const forceAll = argv['force-all'] === true || argv.forceAll === true;
212
277
  const cwd = argv.cwd || process.cwd();
213
278
  // Parse excludes
214
279
  let excludes = [];
@@ -216,7 +281,7 @@ exports.default = async (argv, _prompter, _options) => {
216
281
  excludes = argv.exclude.split(',').map(e => e.trim());
217
282
  }
218
283
  console.log('=== PGPM Package Integration Test ===');
219
- console.log(`Testing all packages with ${fullCycle ? 'deploy/verify/revert/deploy cycle' : 'deploy only'}`);
284
+ console.log(`Testing ${forceAll ? 'all packages' : 'the minimal covering set'} with ${fullCycle ? 'deploy/verify/revert/deploy cycle' : 'deploy only'}`);
220
285
  if (!stopOnFail) {
221
286
  console.log('Mode: Test all packages (collect all failures)');
222
287
  }
@@ -255,14 +320,21 @@ exports.default = async (argv, _prompter, _options) => {
255
320
  });
256
321
  console.log(`Excluding: ${excludes.join(', ')}`);
257
322
  }
258
- console.log(`Found ${filteredModules.length} modules to test:`);
259
- for (const mod of filteredModules) {
323
+ let selectedModules = filteredModules;
324
+ if (!forceAll) {
325
+ const { selected, coveredBy } = selectMinimalModules(workspacePkg, filteredModules);
326
+ selectedModules = selected;
327
+ console.log(`Minimal covering set: ${selected.length} of ${filteredModules.length} modules ` +
328
+ `(${coveredBy.size} covered transitively; --force-all to test each on its own)`);
329
+ }
330
+ console.log(`Found ${selectedModules.length} modules to test:`);
331
+ for (const mod of selectedModules) {
260
332
  console.log(` - ${mod.getModuleName()}`);
261
333
  }
262
334
  console.log('');
263
335
  const failedPackages = [];
264
336
  const successfulPackages = [];
265
- for (const modulePkg of filteredModules) {
337
+ for (const modulePkg of selectedModules) {
266
338
  const result = await testModule(workspacePkg, modulePkg, fullCycle);
267
339
  if (result.success) {
268
340
  successfulPackages.push(result);
@@ -1,4 +1,4 @@
1
- import { PgpmPackage } from '@pgpmjs/core';
1
+ import { PgpmPackage, resolveExtensionDependencies } from '@pgpmjs/core';
2
2
  import { getEnvOptions } from '@pgpmjs/env';
3
3
  import { Logger } from '@pgpmjs/logger';
4
4
  import path from 'path';
@@ -15,22 +15,85 @@ Test Packages Command:
15
15
 
16
16
  pgpm test-packages [OPTIONS]
17
17
 
18
- Run integration tests on all PGPM packages in a workspace.
18
+ Run integration tests on the PGPM packages in a workspace.
19
19
  Tests each package with a deploy/verify/revert/deploy cycle.
20
20
 
21
+ By default only the minimal covering set is tested: the modules nothing else
22
+ in the workspace requires. Deploying one of those deploys everything it
23
+ requires first, so every module in the workspace is still exercised — just
24
+ once per covering module instead of once per module.
25
+
21
26
  Options:
22
27
  --help, -h Show this help message
23
28
  --exclude <pkgs> Comma-separated module names to exclude
24
29
  --continue-on-fail Continue testing all packages even after failures
25
30
  --full-cycle Run full deploy/verify/revert/deploy cycle (default: deploy only)
31
+ --force-all Test every module in its own database (pre-minimal-set behavior)
26
32
  --cwd <directory> Working directory (default: current directory)
27
33
 
28
34
  Examples:
29
- pgpm test-packages Test all packages (stops on first failure)
35
+ pgpm test-packages Test the minimal covering set
36
+ pgpm test-packages --force-all Test every module in its own database
30
37
  pgpm test-packages --full-cycle Run full test cycle with verify/revert
31
38
  pgpm test-packages --continue-on-fail Test all packages, collect all failures
32
39
  pgpm test-packages --exclude my-module Exclude specific modules
33
40
  `;
41
+ /**
42
+ * Reduce the module list to a minimal covering set: the modules that no other
43
+ * candidate requires, transitively.
44
+ *
45
+ * Testing a module deploys its whole dependency closure in dependency order, so
46
+ * a module that some other candidate requires is already deployed, verified,
47
+ * reverted and re-deployed inside that candidate's database. Selection runs over
48
+ * the post-exclude candidates, so excluding a covering module promotes whatever
49
+ * it alone covered rather than dropping it.
50
+ *
51
+ * The property this trades away is standalone deployability — that a module's own
52
+ * `requires` is complete rather than satisfied by a sibling in the same database.
53
+ * Use --force-all to get it back.
54
+ */
55
+ export function minimalCoveringSet(moduleMap, candidateNames) {
56
+ const candidates = new Set(candidateNames);
57
+ // name -> its transitive requires, restricted to the candidates
58
+ const closures = new Map();
59
+ for (const name of candidates) {
60
+ const { resolved } = resolveExtensionDependencies(name, moduleMap);
61
+ closures.set(name, resolved.filter((dep) => dep !== name && candidates.has(dep)));
62
+ }
63
+ const coveredBy = new Map();
64
+ for (const [name, deps] of closures) {
65
+ for (const dep of deps) {
66
+ if (!coveredBy.has(dep)) {
67
+ coveredBy.set(dep, name);
68
+ }
69
+ }
70
+ }
71
+ const selected = candidateNames.filter((name) => !coveredBy.has(name));
72
+ // Nothing may fall out of the selection. Covering-set membership is derived
73
+ // from pairwise closures, so assert the union actually reaches every candidate
74
+ // rather than trusting that argument.
75
+ const covered = new Set();
76
+ for (const name of selected) {
77
+ covered.add(name);
78
+ for (const dep of closures.get(name) ?? []) {
79
+ covered.add(dep);
80
+ }
81
+ }
82
+ const uncovered = candidateNames.filter((name) => !covered.has(name));
83
+ if (uncovered.length > 0) {
84
+ throw new Error(`Minimal module selection left ${uncovered.length} module(s) untested: ${uncovered.join(', ')}. ` +
85
+ 'Re-run with --force-all and report this as a pgpm bug.');
86
+ }
87
+ return { selected, coveredBy };
88
+ }
89
+ function selectMinimalModules(workspacePkg, candidates) {
90
+ const { selected, coveredBy } = minimalCoveringSet(workspacePkg.getModuleMap(), candidates.map((mod) => mod.getModuleName()));
91
+ const selectedNames = new Set(selected);
92
+ return {
93
+ selected: candidates.filter((mod) => selectedNames.has(mod.getModuleName())),
94
+ coveredBy
95
+ };
96
+ }
34
97
  function dbSafeName(moduleName) {
35
98
  return `test_${moduleName}`.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase();
36
99
  }
@@ -204,6 +267,7 @@ export default async (argv, _prompter, _options) => {
204
267
  const continueOnFail = argv['continue-on-fail'] === true || argv.continueOnFail === true;
205
268
  const stopOnFail = !continueOnFail;
206
269
  const fullCycle = argv['full-cycle'] === true || argv.fullCycle === true;
270
+ const forceAll = argv['force-all'] === true || argv.forceAll === true;
207
271
  const cwd = argv.cwd || process.cwd();
208
272
  // Parse excludes
209
273
  let excludes = [];
@@ -211,7 +275,7 @@ export default async (argv, _prompter, _options) => {
211
275
  excludes = argv.exclude.split(',').map(e => e.trim());
212
276
  }
213
277
  console.log('=== PGPM Package Integration Test ===');
214
- console.log(`Testing all packages with ${fullCycle ? 'deploy/verify/revert/deploy cycle' : 'deploy only'}`);
278
+ console.log(`Testing ${forceAll ? 'all packages' : 'the minimal covering set'} with ${fullCycle ? 'deploy/verify/revert/deploy cycle' : 'deploy only'}`);
215
279
  if (!stopOnFail) {
216
280
  console.log('Mode: Test all packages (collect all failures)');
217
281
  }
@@ -250,14 +314,21 @@ export default async (argv, _prompter, _options) => {
250
314
  });
251
315
  console.log(`Excluding: ${excludes.join(', ')}`);
252
316
  }
253
- console.log(`Found ${filteredModules.length} modules to test:`);
254
- for (const mod of filteredModules) {
317
+ let selectedModules = filteredModules;
318
+ if (!forceAll) {
319
+ const { selected, coveredBy } = selectMinimalModules(workspacePkg, filteredModules);
320
+ selectedModules = selected;
321
+ console.log(`Minimal covering set: ${selected.length} of ${filteredModules.length} modules ` +
322
+ `(${coveredBy.size} covered transitively; --force-all to test each on its own)`);
323
+ }
324
+ console.log(`Found ${selectedModules.length} modules to test:`);
325
+ for (const mod of selectedModules) {
255
326
  console.log(` - ${mod.getModuleName()}`);
256
327
  }
257
328
  console.log('');
258
329
  const failedPackages = [];
259
330
  const successfulPackages = [];
260
- for (const modulePkg of filteredModules) {
331
+ for (const modulePkg of selectedModules) {
261
332
  const result = await testModule(workspacePkg, modulePkg, fullCycle);
262
333
  if (result.success) {
263
334
  successfulPackages.push(result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgpm",
3
- "version": "5.24.4",
3
+ "version": "5.25.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "PostgreSQL Package Manager - Database migration and package management CLI",
6
6
  "main": "index.js",
@@ -46,16 +46,16 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@inquirerer/utils": "^3.3.12",
49
- "@pgpmjs/core": "^7.25.3",
50
- "@pgpmjs/diff": "^0.3.4",
49
+ "@pgpmjs/core": "^7.25.4",
50
+ "@pgpmjs/diff": "^0.3.5",
51
51
  "@pgpmjs/env": "^2.41.3",
52
- "@pgpmjs/export": "^1.16.7",
53
- "@pgpmjs/import": "^0.3.5",
52
+ "@pgpmjs/export": "^1.16.9",
53
+ "@pgpmjs/import": "^0.3.6",
54
54
  "@pgpmjs/logger": "^2.24.1",
55
- "@pgpmjs/slice": "^0.14.5",
56
- "@pgpmjs/transform": "^0.23.3",
55
+ "@pgpmjs/slice": "^0.14.6",
56
+ "@pgpmjs/transform": "^0.23.4",
57
57
  "@pgpmjs/types": "^2.50.3",
58
- "@pgsql/quotes": "^18.2.1",
58
+ "@pgsql/quotes": "^18.2.4",
59
59
  "appstash": "^0.8.0",
60
60
  "find-and-require-package-json": "^0.9.1",
61
61
  "genomic": "^5.6.5",
@@ -63,7 +63,7 @@
63
63
  "js-yaml": "^4.1.0",
64
64
  "pg-cache": "^3.25.4",
65
65
  "pg-env": "^1.29.1",
66
- "pgsql-deparser": "^18.3.3",
66
+ "pgsql-deparser": "^18.3.6",
67
67
  "semver": "^7.8.5",
68
68
  "shelljs": "^0.10.0",
69
69
  "yanse": "^0.2.2"
@@ -80,5 +80,5 @@
80
80
  "pg",
81
81
  "pgsql"
82
82
  ],
83
- "gitHead": "a7ba21122bd39663b40fb8fc3afe6eb9b69e0041"
83
+ "gitHead": "44f56ee80a348f649a22e5bd32b35f2822726509"
84
84
  }