@pgpmjs/core 6.28.0 → 6.28.2
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/core/class/pgpm.d.ts +1 -1
- package/core/class/pgpm.js +7 -19
- package/esm/core/class/pgpm.js +9 -21
- package/esm/files/plan/generator.js +41 -8
- package/esm/files/plan/writer.js +31 -5
- package/files/plan/generator.d.ts +2 -1
- package/files/plan/generator.js +41 -8
- package/files/plan/writer.d.ts +2 -2
- package/files/plan/writer.js +31 -5
- package/package.json +9 -9
package/core/class/pgpm.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { PgpmOptions, PgpmWorkspaceConfig } from '@pgpmjs/types';
|
|
2
2
|
import { PgConfig } from 'pg-env';
|
|
3
3
|
import { ExtensionInfo } from '../../files';
|
|
4
|
-
import { ModuleMap } from '../../modules/modules';
|
|
5
4
|
import { PackageAnalysisResult, RenameOptions } from '../../files/types';
|
|
5
|
+
import { ModuleMap } from '../../modules/modules';
|
|
6
6
|
export declare enum PackageContext {
|
|
7
7
|
Outside = "outside",
|
|
8
8
|
Workspace = "workspace-root",
|
package/core/class/pgpm.js
CHANGED
|
@@ -40,7 +40,6 @@ exports.PgpmPackage = exports.PackageContext = void 0;
|
|
|
40
40
|
const env_1 = require("@pgpmjs/env");
|
|
41
41
|
const logger_1 = require("@pgpmjs/logger");
|
|
42
42
|
const types_1 = require("@pgpmjs/types");
|
|
43
|
-
const yanse_1 = __importDefault(require("yanse"));
|
|
44
43
|
const child_process_1 = require("child_process");
|
|
45
44
|
const fs_1 = __importDefault(require("fs"));
|
|
46
45
|
const glob = __importStar(require("glob"));
|
|
@@ -48,40 +47,29 @@ const os_1 = __importDefault(require("os"));
|
|
|
48
47
|
const parse_package_name_1 = require("parse-package-name");
|
|
49
48
|
const path_1 = __importStar(require("path"));
|
|
50
49
|
const pg_cache_1 = require("pg-cache");
|
|
51
|
-
const
|
|
50
|
+
const yanse_1 = __importDefault(require("yanse"));
|
|
52
51
|
const extensions_1 = require("../../extensions/extensions");
|
|
53
52
|
const files_1 = require("../../files");
|
|
54
|
-
const parser_1 = require("../../files/plan/parser");
|
|
55
|
-
const validators_1 = require("../../files/plan/validators");
|
|
56
|
-
const generator_1 = require("../../files/plan/generator");
|
|
57
53
|
const files_2 = require("../../files");
|
|
58
54
|
const writer_1 = require("../../files/extension/writer");
|
|
55
|
+
const generator_1 = require("../../files/plan/generator");
|
|
56
|
+
const parser_1 = require("../../files/plan/parser");
|
|
57
|
+
const validators_1 = require("../../files/plan/validators");
|
|
59
58
|
const client_1 = require("../../migrate/client");
|
|
60
59
|
const modules_1 = require("../../modules/modules");
|
|
61
60
|
const package_1 = require("../../packaging/package");
|
|
62
61
|
const deps_1 = require("../../resolution/deps");
|
|
63
62
|
const target_utils_1 = require("../../utils/target-utils");
|
|
63
|
+
const template_scaffold_1 = require("../template-scaffold");
|
|
64
64
|
const logger = new logger_1.Logger('pgpm');
|
|
65
65
|
/**
|
|
66
66
|
* Directory name for workspace extensions.
|
|
67
67
|
* Extensions are installed globally in the workspace's extensions/ directory.
|
|
68
68
|
*/
|
|
69
69
|
const EXTENSIONS_DIR = 'extensions';
|
|
70
|
-
function getUTCTimestamp(d = new Date()) {
|
|
71
|
-
return (d.getUTCFullYear() +
|
|
72
|
-
'-' + String(d.getUTCMonth() + 1).padStart(2, '0') +
|
|
73
|
-
'-' + String(d.getUTCDate()).padStart(2, '0') +
|
|
74
|
-
'T' + String(d.getUTCHours()).padStart(2, '0') +
|
|
75
|
-
':' + String(d.getUTCMinutes()).padStart(2, '0') +
|
|
76
|
-
':' + String(d.getUTCSeconds()).padStart(2, '0') +
|
|
77
|
-
'Z');
|
|
78
|
-
}
|
|
79
70
|
function sortObjectByKey(obj) {
|
|
80
71
|
return Object.fromEntries(Object.entries(obj).sort(([a], [b]) => a.localeCompare(b)));
|
|
81
72
|
}
|
|
82
|
-
const getNow = () => process.env.NODE_ENV === 'test'
|
|
83
|
-
? getUTCTimestamp(new Date('2017-08-11T08:11:51Z'))
|
|
84
|
-
: getUTCTimestamp(new Date());
|
|
85
73
|
/**
|
|
86
74
|
* Truncates workspace extensions to include only modules from the target onwards.
|
|
87
75
|
* This prevents processing unnecessary modules that come before the target in dependency order.
|
|
@@ -398,7 +386,7 @@ class PgpmPackage {
|
|
|
398
386
|
cacheTtlMs: options.cacheTtlMs ?? template_scaffold_1.DEFAULT_TEMPLATE_TTL_MS,
|
|
399
387
|
toolName: options.toolName ?? template_scaffold_1.DEFAULT_TEMPLATE_TOOL_NAME,
|
|
400
388
|
cwd: this.cwd,
|
|
401
|
-
prompter: options.prompter
|
|
389
|
+
prompter: options.prompter
|
|
402
390
|
});
|
|
403
391
|
// Only create pgpm files (pgpm.plan, .control, deploy/revert/verify dirs) for pgpm-managed modules
|
|
404
392
|
if (isPgpmModule) {
|
|
@@ -661,7 +649,7 @@ class PgpmPackage {
|
|
|
661
649
|
throw new Error('Change name is required');
|
|
662
650
|
}
|
|
663
651
|
if (!(0, validators_1.isValidChangeName)(changeName)) {
|
|
664
|
-
throw types_1.errors.INVALID_NAME({ name: changeName, type: 'change', rules:
|
|
652
|
+
throw types_1.errors.INVALID_NAME({ name: changeName, type: 'change', rules: 'Change names must follow Sqitch naming rules' });
|
|
665
653
|
}
|
|
666
654
|
if (!this.isInWorkspace() && !this.isInModule()) {
|
|
667
655
|
throw new Error('This command must be run inside a PGPM workspace or module.');
|
package/esm/core/class/pgpm.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { loadConfigSyncFromDir, resolvePgpmPath, walkUp } from '@pgpmjs/env';
|
|
2
2
|
import { Logger } from '@pgpmjs/logger';
|
|
3
3
|
import { errors } from '@pgpmjs/types';
|
|
4
|
-
import yanse from 'yanse';
|
|
5
4
|
import { execSync } from 'child_process';
|
|
6
5
|
import fs from 'fs';
|
|
7
6
|
import * as glob from 'glob';
|
|
@@ -9,40 +8,29 @@ import os from 'os';
|
|
|
9
8
|
import { parse } from 'parse-package-name';
|
|
10
9
|
import path, { dirname, resolve } from 'path';
|
|
11
10
|
import { getPgPool } from 'pg-cache';
|
|
12
|
-
import
|
|
11
|
+
import yanse from 'yanse';
|
|
13
12
|
import { getAvailableExtensions } from '../../extensions/extensions';
|
|
14
13
|
import { generatePlan, writePlan, writePlanFile } from '../../files';
|
|
15
|
-
import {
|
|
16
|
-
import { isValidTagName, isValidChangeName, parseReference } from '../../files/plan/validators';
|
|
17
|
-
import { getNow as getPlanTimestamp } from '../../files/plan/generator';
|
|
18
|
-
import { getExtensionInfo, getExtensionName, getInstalledExtensions, parseControlFile, writeExtensions, } from '../../files';
|
|
14
|
+
import { getExtensionInfo, getExtensionName, getInstalledExtensions, parseControlFile, writeExtensions } from '../../files';
|
|
19
15
|
import { generateControlFileContent, writeExtensionMakefile } from '../../files/extension/writer';
|
|
16
|
+
import { getNow as getPlanTimestamp } from '../../files/plan/generator';
|
|
17
|
+
import { parsePlanFile } from '../../files/plan/parser';
|
|
18
|
+
import { isValidChangeName, isValidTagName, parseReference } from '../../files/plan/validators';
|
|
20
19
|
import { PgpmMigrate } from '../../migrate/client';
|
|
21
20
|
import { getExtensionsAndModules, getExtensionsAndModulesChanges, latestChange, latestChangeAndVersion } from '../../modules/modules';
|
|
22
21
|
import { packageModule } from '../../packaging/package';
|
|
23
|
-
import {
|
|
22
|
+
import { resolveDependencies, resolveExtensionDependencies } from '../../resolution/deps';
|
|
24
23
|
import { parseTarget } from '../../utils/target-utils';
|
|
24
|
+
import { DEFAULT_TEMPLATE_REPO, DEFAULT_TEMPLATE_TOOL_NAME, DEFAULT_TEMPLATE_TTL_MS, scaffoldTemplate } from '../template-scaffold';
|
|
25
25
|
const logger = new Logger('pgpm');
|
|
26
26
|
/**
|
|
27
27
|
* Directory name for workspace extensions.
|
|
28
28
|
* Extensions are installed globally in the workspace's extensions/ directory.
|
|
29
29
|
*/
|
|
30
30
|
const EXTENSIONS_DIR = 'extensions';
|
|
31
|
-
function getUTCTimestamp(d = new Date()) {
|
|
32
|
-
return (d.getUTCFullYear() +
|
|
33
|
-
'-' + String(d.getUTCMonth() + 1).padStart(2, '0') +
|
|
34
|
-
'-' + String(d.getUTCDate()).padStart(2, '0') +
|
|
35
|
-
'T' + String(d.getUTCHours()).padStart(2, '0') +
|
|
36
|
-
':' + String(d.getUTCMinutes()).padStart(2, '0') +
|
|
37
|
-
':' + String(d.getUTCSeconds()).padStart(2, '0') +
|
|
38
|
-
'Z');
|
|
39
|
-
}
|
|
40
31
|
function sortObjectByKey(obj) {
|
|
41
32
|
return Object.fromEntries(Object.entries(obj).sort(([a], [b]) => a.localeCompare(b)));
|
|
42
33
|
}
|
|
43
|
-
const getNow = () => process.env.NODE_ENV === 'test'
|
|
44
|
-
? getUTCTimestamp(new Date('2017-08-11T08:11:51Z'))
|
|
45
|
-
: getUTCTimestamp(new Date());
|
|
46
34
|
/**
|
|
47
35
|
* Truncates workspace extensions to include only modules from the target onwards.
|
|
48
36
|
* This prevents processing unnecessary modules that come before the target in dependency order.
|
|
@@ -359,7 +347,7 @@ export class PgpmPackage {
|
|
|
359
347
|
cacheTtlMs: options.cacheTtlMs ?? DEFAULT_TEMPLATE_TTL_MS,
|
|
360
348
|
toolName: options.toolName ?? DEFAULT_TEMPLATE_TOOL_NAME,
|
|
361
349
|
cwd: this.cwd,
|
|
362
|
-
prompter: options.prompter
|
|
350
|
+
prompter: options.prompter
|
|
363
351
|
});
|
|
364
352
|
// Only create pgpm files (pgpm.plan, .control, deploy/revert/verify dirs) for pgpm-managed modules
|
|
365
353
|
if (isPgpmModule) {
|
|
@@ -622,7 +610,7 @@ export class PgpmPackage {
|
|
|
622
610
|
throw new Error('Change name is required');
|
|
623
611
|
}
|
|
624
612
|
if (!isValidChangeName(changeName)) {
|
|
625
|
-
throw errors.INVALID_NAME({ name: changeName, type: 'change', rules:
|
|
613
|
+
throw errors.INVALID_NAME({ name: changeName, type: 'change', rules: 'Change names must follow Sqitch naming rules' });
|
|
626
614
|
}
|
|
627
615
|
if (!this.isInWorkspace() && !this.isInModule()) {
|
|
628
616
|
throw new Error('This command must be run inside a PGPM workspace or module.');
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Fixed epoch timestamp used for deterministic plan generation.
|
|
4
|
+
* Can be overridden with the PGPM_FROZEN_TIME environment variable.
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
|
+
const DEFAULT_FROZEN_TIME = '2017-08-11T08:11:51Z';
|
|
7
|
+
/**
|
|
8
|
+
* Get a UTC timestamp string from a Date object.
|
|
9
|
+
*/
|
|
10
|
+
function getUTCTimestamp(d) {
|
|
6
11
|
return (d.getUTCFullYear() +
|
|
7
12
|
'-' + String(d.getUTCMonth() + 1).padStart(2, '0') +
|
|
8
13
|
'-' + String(d.getUTCDate()).padStart(2, '0') +
|
|
@@ -12,13 +17,38 @@ function getUTCTimestamp(d = new Date()) {
|
|
|
12
17
|
'Z');
|
|
13
18
|
}
|
|
14
19
|
/**
|
|
15
|
-
* Get a timestamp for the plan file
|
|
20
|
+
* Get a timestamp for the plan file.
|
|
21
|
+
* Uses the PGPM_FROZEN_TIME env var if present, otherwise the fixed epoch.
|
|
16
22
|
*/
|
|
17
23
|
export function getNow() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
const frozen = process.env.PGPM_FROZEN_TIME;
|
|
25
|
+
if (frozen) {
|
|
26
|
+
return getUTCTimestamp(new Date(frozen));
|
|
27
|
+
}
|
|
28
|
+
return DEFAULT_FROZEN_TIME;
|
|
21
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Sort dependencies by their order in the supplied plan, so prerequisites
|
|
32
|
+
* (parent migrations) appear before their dependents in the bracket.
|
|
33
|
+
* Internal dependencies are sorted by their plan index, and external
|
|
34
|
+
* dependencies (not in the plan) preserve their original order.
|
|
35
|
+
*/
|
|
36
|
+
const sortDependencies = (deps, order) => {
|
|
37
|
+
const originalOrder = new Map(deps.map((dep, index) => [dep, index]));
|
|
38
|
+
return [...deps].sort((a, b) => {
|
|
39
|
+
const aIndex = order.get(a);
|
|
40
|
+
const bIndex = order.get(b);
|
|
41
|
+
const aOriginal = originalOrder.get(a);
|
|
42
|
+
const bOriginal = originalOrder.get(b);
|
|
43
|
+
if (aIndex !== undefined && bIndex !== undefined)
|
|
44
|
+
return aIndex - bIndex || aOriginal - bOriginal;
|
|
45
|
+
if (aIndex !== undefined)
|
|
46
|
+
return -1;
|
|
47
|
+
if (bIndex !== undefined)
|
|
48
|
+
return 1;
|
|
49
|
+
return aOriginal - bOriginal;
|
|
50
|
+
});
|
|
51
|
+
};
|
|
22
52
|
/**
|
|
23
53
|
* Generate a plan file content from structured data
|
|
24
54
|
*/
|
|
@@ -30,10 +60,13 @@ export function generatePlan(options) {
|
|
|
30
60
|
`%project=${moduleName}`,
|
|
31
61
|
`%uri=${uri || moduleName}`
|
|
32
62
|
];
|
|
63
|
+
// Build an order map from the entry list so brackets can be sorted by plan order
|
|
64
|
+
const entryOrder = new Map(entries.map((entry, index) => [entry.change, index]));
|
|
33
65
|
// Generate the plan entries
|
|
34
66
|
entries.forEach(entry => {
|
|
35
|
-
|
|
36
|
-
|
|
67
|
+
const deps = sortDependencies(entry.dependencies || [], entryOrder);
|
|
68
|
+
if (deps.length > 0) {
|
|
69
|
+
planfile.push(`${entry.change} [${deps.join(' ')}] ${now} constructive <constructive@5b0c196eeb62>${entry.comment ? ` # ${entry.comment}` : ''}`);
|
|
37
70
|
}
|
|
38
71
|
else {
|
|
39
72
|
planfile.push(`${entry.change} ${now} constructive <constructive@5b0c196eeb62>${entry.comment ? ` # ${entry.comment}` : ''}`);
|
package/esm/files/plan/writer.js
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { parseAuthor } from '../../utils/author';
|
|
4
|
+
/**
|
|
5
|
+
* Sort dependencies by their order in the supplied plan, so prerequisites
|
|
6
|
+
* (parent migrations) appear before their dependents in the bracket.
|
|
7
|
+
* Internal dependencies are sorted by their plan index, and external
|
|
8
|
+
* dependencies (not in the plan) preserve their original order.
|
|
9
|
+
*/
|
|
10
|
+
const sortDependencies = (deps, order) => {
|
|
11
|
+
const originalOrder = new Map(deps.map((dep, index) => [dep, index]));
|
|
12
|
+
return [...deps].sort((a, b) => {
|
|
13
|
+
const aIndex = order.get(a);
|
|
14
|
+
const bIndex = order.get(b);
|
|
15
|
+
const aOriginal = originalOrder.get(a);
|
|
16
|
+
const bOriginal = originalOrder.get(b);
|
|
17
|
+
if (aIndex !== undefined && bIndex !== undefined)
|
|
18
|
+
return aIndex - bIndex || aOriginal - bOriginal;
|
|
19
|
+
if (aIndex !== undefined)
|
|
20
|
+
return -1;
|
|
21
|
+
if (bIndex !== undefined)
|
|
22
|
+
return 1;
|
|
23
|
+
return aOriginal - bOriginal;
|
|
24
|
+
});
|
|
25
|
+
};
|
|
4
26
|
/**
|
|
5
27
|
* Write a PGPM plan file based on the provided rows
|
|
6
28
|
*/
|
|
@@ -12,6 +34,8 @@ export function writePgpmPlan(rows, opts) {
|
|
|
12
34
|
const authorName = fullName;
|
|
13
35
|
const authorEmail = email || `${fullName}@5b0c196eeb62`;
|
|
14
36
|
const duplicates = {};
|
|
37
|
+
// Build an order map from the row list so brackets can be sorted by plan order
|
|
38
|
+
const rowOrder = new Map(rows.map((row, index) => [row.deploy, index]));
|
|
15
39
|
const plan = opts.replacer(`%syntax-version=1.0.0
|
|
16
40
|
%project=constructive-extension-name
|
|
17
41
|
%uri=constructive-extension-name
|
|
@@ -24,7 +48,7 @@ ${rows
|
|
|
24
48
|
}
|
|
25
49
|
duplicates[row.deploy] = true;
|
|
26
50
|
if (row.deps?.length) {
|
|
27
|
-
return `${row.deploy} [${row.deps.join(' ')}] ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
|
|
51
|
+
return `${row.deploy} [${sortDependencies(row.deps, rowOrder).join(' ')}] ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
|
|
28
52
|
}
|
|
29
53
|
return `${row.deploy} ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
|
|
30
54
|
})
|
|
@@ -50,9 +74,11 @@ export function generatePlanFileContent(plan) {
|
|
|
50
74
|
content += `%uri=${uri}\n`;
|
|
51
75
|
}
|
|
52
76
|
content += `\n`;
|
|
77
|
+
// Build an order map from the change list so brackets can be sorted by plan order
|
|
78
|
+
const changeOrder = new Map(changes.map((change, index) => [change.name, index]));
|
|
53
79
|
// Add changes and their associated tags
|
|
54
80
|
for (const change of changes) {
|
|
55
|
-
content += generateChangeLineContent(change);
|
|
81
|
+
content += generateChangeLineContent(change, changeOrder);
|
|
56
82
|
content += `\n`;
|
|
57
83
|
const associatedTags = tags.filter(tag => tag.change === change.name);
|
|
58
84
|
for (const tag of associatedTags) {
|
|
@@ -65,12 +91,12 @@ export function generatePlanFileContent(plan) {
|
|
|
65
91
|
/**
|
|
66
92
|
* Generate a line for a change in a plan file
|
|
67
93
|
*/
|
|
68
|
-
export function generateChangeLineContent(change) {
|
|
94
|
+
export function generateChangeLineContent(change, changeOrder) {
|
|
69
95
|
const { name, dependencies, timestamp, planner, email, comment } = change;
|
|
70
96
|
let line = name;
|
|
71
|
-
// Add dependencies if present
|
|
97
|
+
// Add dependencies if present, sorted by plan order
|
|
72
98
|
if (dependencies && dependencies.length > 0) {
|
|
73
|
-
line += ` [${dependencies.join(' ')}]`;
|
|
99
|
+
line += ` [${sortDependencies(dependencies, changeOrder || new Map()).join(' ')}]`;
|
|
74
100
|
}
|
|
75
101
|
// Add timestamp if present
|
|
76
102
|
if (timestamp) {
|
package/files/plan/generator.js
CHANGED
|
@@ -8,9 +8,14 @@ exports.generatePlan = generatePlan;
|
|
|
8
8
|
exports.writePlan = writePlan;
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Fixed epoch timestamp used for deterministic plan generation.
|
|
12
|
+
* Can be overridden with the PGPM_FROZEN_TIME environment variable.
|
|
12
13
|
*/
|
|
13
|
-
|
|
14
|
+
const DEFAULT_FROZEN_TIME = '2017-08-11T08:11:51Z';
|
|
15
|
+
/**
|
|
16
|
+
* Get a UTC timestamp string from a Date object.
|
|
17
|
+
*/
|
|
18
|
+
function getUTCTimestamp(d) {
|
|
14
19
|
return (d.getUTCFullYear() +
|
|
15
20
|
'-' + String(d.getUTCMonth() + 1).padStart(2, '0') +
|
|
16
21
|
'-' + String(d.getUTCDate()).padStart(2, '0') +
|
|
@@ -20,13 +25,38 @@ function getUTCTimestamp(d = new Date()) {
|
|
|
20
25
|
'Z');
|
|
21
26
|
}
|
|
22
27
|
/**
|
|
23
|
-
* Get a timestamp for the plan file
|
|
28
|
+
* Get a timestamp for the plan file.
|
|
29
|
+
* Uses the PGPM_FROZEN_TIME env var if present, otherwise the fixed epoch.
|
|
24
30
|
*/
|
|
25
31
|
function getNow() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
const frozen = process.env.PGPM_FROZEN_TIME;
|
|
33
|
+
if (frozen) {
|
|
34
|
+
return getUTCTimestamp(new Date(frozen));
|
|
35
|
+
}
|
|
36
|
+
return DEFAULT_FROZEN_TIME;
|
|
29
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Sort dependencies by their order in the supplied plan, so prerequisites
|
|
40
|
+
* (parent migrations) appear before their dependents in the bracket.
|
|
41
|
+
* Internal dependencies are sorted by their plan index, and external
|
|
42
|
+
* dependencies (not in the plan) preserve their original order.
|
|
43
|
+
*/
|
|
44
|
+
const sortDependencies = (deps, order) => {
|
|
45
|
+
const originalOrder = new Map(deps.map((dep, index) => [dep, index]));
|
|
46
|
+
return [...deps].sort((a, b) => {
|
|
47
|
+
const aIndex = order.get(a);
|
|
48
|
+
const bIndex = order.get(b);
|
|
49
|
+
const aOriginal = originalOrder.get(a);
|
|
50
|
+
const bOriginal = originalOrder.get(b);
|
|
51
|
+
if (aIndex !== undefined && bIndex !== undefined)
|
|
52
|
+
return aIndex - bIndex || aOriginal - bOriginal;
|
|
53
|
+
if (aIndex !== undefined)
|
|
54
|
+
return -1;
|
|
55
|
+
if (bIndex !== undefined)
|
|
56
|
+
return 1;
|
|
57
|
+
return aOriginal - bOriginal;
|
|
58
|
+
});
|
|
59
|
+
};
|
|
30
60
|
/**
|
|
31
61
|
* Generate a plan file content from structured data
|
|
32
62
|
*/
|
|
@@ -38,10 +68,13 @@ function generatePlan(options) {
|
|
|
38
68
|
`%project=${moduleName}`,
|
|
39
69
|
`%uri=${uri || moduleName}`
|
|
40
70
|
];
|
|
71
|
+
// Build an order map from the entry list so brackets can be sorted by plan order
|
|
72
|
+
const entryOrder = new Map(entries.map((entry, index) => [entry.change, index]));
|
|
41
73
|
// Generate the plan entries
|
|
42
74
|
entries.forEach(entry => {
|
|
43
|
-
|
|
44
|
-
|
|
75
|
+
const deps = sortDependencies(entry.dependencies || [], entryOrder);
|
|
76
|
+
if (deps.length > 0) {
|
|
77
|
+
planfile.push(`${entry.change} [${deps.join(' ')}] ${now} constructive <constructive@5b0c196eeb62>${entry.comment ? ` # ${entry.comment}` : ''}`);
|
|
45
78
|
}
|
|
46
79
|
else {
|
|
47
80
|
planfile.push(`${entry.change} ${now} constructive <constructive@5b0c196eeb62>${entry.comment ? ` # ${entry.comment}` : ''}`);
|
package/files/plan/writer.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Change, PgpmRow, Tag
|
|
1
|
+
import { Change, ExtendedPlanFile, PgpmRow, Tag } from '../types';
|
|
2
2
|
export interface PlanWriteOptions {
|
|
3
3
|
outdir: string;
|
|
4
4
|
name: string;
|
|
@@ -20,7 +20,7 @@ export declare function generatePlanFileContent(plan: ExtendedPlanFile): string;
|
|
|
20
20
|
/**
|
|
21
21
|
* Generate a line for a change in a plan file
|
|
22
22
|
*/
|
|
23
|
-
export declare function generateChangeLineContent(change: Change): string;
|
|
23
|
+
export declare function generateChangeLineContent(change: Change, changeOrder?: Map<string, number>): string;
|
|
24
24
|
/**
|
|
25
25
|
* Generate a line for a tag in a plan file
|
|
26
26
|
*/
|
package/files/plan/writer.js
CHANGED
|
@@ -11,6 +11,28 @@ exports.generateTagLineContent = generateTagLineContent;
|
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
13
|
const author_1 = require("../../utils/author");
|
|
14
|
+
/**
|
|
15
|
+
* Sort dependencies by their order in the supplied plan, so prerequisites
|
|
16
|
+
* (parent migrations) appear before their dependents in the bracket.
|
|
17
|
+
* Internal dependencies are sorted by their plan index, and external
|
|
18
|
+
* dependencies (not in the plan) preserve their original order.
|
|
19
|
+
*/
|
|
20
|
+
const sortDependencies = (deps, order) => {
|
|
21
|
+
const originalOrder = new Map(deps.map((dep, index) => [dep, index]));
|
|
22
|
+
return [...deps].sort((a, b) => {
|
|
23
|
+
const aIndex = order.get(a);
|
|
24
|
+
const bIndex = order.get(b);
|
|
25
|
+
const aOriginal = originalOrder.get(a);
|
|
26
|
+
const bOriginal = originalOrder.get(b);
|
|
27
|
+
if (aIndex !== undefined && bIndex !== undefined)
|
|
28
|
+
return aIndex - bIndex || aOriginal - bOriginal;
|
|
29
|
+
if (aIndex !== undefined)
|
|
30
|
+
return -1;
|
|
31
|
+
if (bIndex !== undefined)
|
|
32
|
+
return 1;
|
|
33
|
+
return aOriginal - bOriginal;
|
|
34
|
+
});
|
|
35
|
+
};
|
|
14
36
|
/**
|
|
15
37
|
* Write a PGPM plan file based on the provided rows
|
|
16
38
|
*/
|
|
@@ -22,6 +44,8 @@ function writePgpmPlan(rows, opts) {
|
|
|
22
44
|
const authorName = fullName;
|
|
23
45
|
const authorEmail = email || `${fullName}@5b0c196eeb62`;
|
|
24
46
|
const duplicates = {};
|
|
47
|
+
// Build an order map from the row list so brackets can be sorted by plan order
|
|
48
|
+
const rowOrder = new Map(rows.map((row, index) => [row.deploy, index]));
|
|
25
49
|
const plan = opts.replacer(`%syntax-version=1.0.0
|
|
26
50
|
%project=constructive-extension-name
|
|
27
51
|
%uri=constructive-extension-name
|
|
@@ -34,7 +58,7 @@ ${rows
|
|
|
34
58
|
}
|
|
35
59
|
duplicates[row.deploy] = true;
|
|
36
60
|
if (row.deps?.length) {
|
|
37
|
-
return `${row.deploy} [${row.deps.join(' ')}] ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
|
|
61
|
+
return `${row.deploy} [${sortDependencies(row.deps, rowOrder).join(' ')}] ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
|
|
38
62
|
}
|
|
39
63
|
return `${row.deploy} ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
|
|
40
64
|
})
|
|
@@ -60,9 +84,11 @@ function generatePlanFileContent(plan) {
|
|
|
60
84
|
content += `%uri=${uri}\n`;
|
|
61
85
|
}
|
|
62
86
|
content += `\n`;
|
|
87
|
+
// Build an order map from the change list so brackets can be sorted by plan order
|
|
88
|
+
const changeOrder = new Map(changes.map((change, index) => [change.name, index]));
|
|
63
89
|
// Add changes and their associated tags
|
|
64
90
|
for (const change of changes) {
|
|
65
|
-
content += generateChangeLineContent(change);
|
|
91
|
+
content += generateChangeLineContent(change, changeOrder);
|
|
66
92
|
content += `\n`;
|
|
67
93
|
const associatedTags = tags.filter(tag => tag.change === change.name);
|
|
68
94
|
for (const tag of associatedTags) {
|
|
@@ -75,12 +101,12 @@ function generatePlanFileContent(plan) {
|
|
|
75
101
|
/**
|
|
76
102
|
* Generate a line for a change in a plan file
|
|
77
103
|
*/
|
|
78
|
-
function generateChangeLineContent(change) {
|
|
104
|
+
function generateChangeLineContent(change, changeOrder) {
|
|
79
105
|
const { name, dependencies, timestamp, planner, email, comment } = change;
|
|
80
106
|
let line = name;
|
|
81
|
-
// Add dependencies if present
|
|
107
|
+
// Add dependencies if present, sorted by plan order
|
|
82
108
|
if (dependencies && dependencies.length > 0) {
|
|
83
|
-
line += ` [${dependencies.join(' ')}]`;
|
|
109
|
+
line += ` [${sortDependencies(dependencies, changeOrder || new Map()).join(' ')}]`;
|
|
84
110
|
}
|
|
85
111
|
// Add timestamp if present
|
|
86
112
|
if (timestamp) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgpmjs/core",
|
|
3
|
-
"version": "6.28.
|
|
3
|
+
"version": "6.28.2",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PGPM Package and Migration Tools",
|
|
6
6
|
"main": "index.js",
|
|
@@ -48,21 +48,21 @@
|
|
|
48
48
|
"makage": "^0.3.0"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@pgpmjs/env": "^2.26.
|
|
52
|
-
"@pgpmjs/logger": "^2.13.
|
|
53
|
-
"@pgpmjs/server-utils": "^3.14.
|
|
54
|
-
"@pgpmjs/types": "^2.33.
|
|
55
|
-
"csv-to-pg": "^3.20.
|
|
51
|
+
"@pgpmjs/env": "^2.26.4",
|
|
52
|
+
"@pgpmjs/logger": "^2.13.1",
|
|
53
|
+
"@pgpmjs/server-utils": "^3.14.4",
|
|
54
|
+
"@pgpmjs/types": "^2.33.1",
|
|
55
|
+
"csv-to-pg": "^3.20.2",
|
|
56
56
|
"genomic": "^5.6.2",
|
|
57
57
|
"glob": "^13.0.6",
|
|
58
58
|
"minimatch": "^10.2.5",
|
|
59
59
|
"parse-package-name": "^1.0.0",
|
|
60
60
|
"pg": "^8.21.0",
|
|
61
|
-
"pg-cache": "^3.14.
|
|
62
|
-
"pg-env": "^1.17.
|
|
61
|
+
"pg-cache": "^3.14.3",
|
|
62
|
+
"pg-env": "^1.17.1",
|
|
63
63
|
"pgsql-deparser": "^17.18.3",
|
|
64
64
|
"pgsql-parser": "^17.9.15",
|
|
65
65
|
"yanse": "^0.2.1"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "113036fa4fb581e4d8418a9309524495a423b624"
|
|
68
68
|
}
|