@clickup/pg-mig
Version:
PostgreSQL schema migration tool with microsharding and clustering support
187 lines • 7.45 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = main;
exports.migrate = migrate;
exports.loadDBDigest = loadDBDigest;
exports.cli = cli;
const compact_1 = __importDefault(require("lodash/compact"));
const mapValues_1 = __importDefault(require("lodash/mapValues"));
const pickBy_1 = __importDefault(require("lodash/pickBy"));
const actionChain_1 = require("./actions/actionChain");
const actionDigest_1 = require("./actions/actionDigest");
const actionList_1 = require("./actions/actionList");
const actionListDB_1 = require("./actions/actionListDB");
const actionMake_1 = require("./actions/actionMake");
const actionUndoOrApply_1 = require("./actions/actionUndoOrApply");
const Dest_1 = require("./internal/Dest");
const Args_1 = require("./internal/helpers/Args");
const readConfigs_1 = require("./internal/helpers/readConfigs");
const Registry_1 = require("./internal/Registry");
const render_1 = require("./internal/render");
/**
* CLI tool entry point. This function is run when `pg-mig` is called from the
* command line. Accepts parameters from process.argv. See `migrate()` for
* option names.
*
* If no options are passed, uses `PGHOST`, `PGPORT`, `PGUSER`, `PGPASSWORD`,
* `PGDATABASE` environment variables which are standard for e.g. `psql`.
*
* You can pass multiple hosts separated by comma or semicolon.
*
* Examples:
* ```
* pg-mig --make=my-migration-name@sh
* pg-mig --make=other-migration-name@sh0000
* pg-mig --undo=20191107201239.my-migration-name.sh
* pg-mig --list
* pg-mig --list=db
* pg-mig --list=digest
* pg-mig
* ```
*/
async function main(argsIn) {
const args = new Args_1.Args(argsIn, [
// We use --migdir and not --dir, because @mapbox/node-pre-gyp used by
// bcrypt conflicts with --dir option.
"migdir",
"hosts",
"port",
"user",
"pass",
"db",
"undo",
"make",
"chain",
"list",
"parallelism",
"valid-shard-schemas-sql",
], ["dry", "createdb", "force", "skip-config"]);
const action = args.getOptional("make") !== undefined
? { type: "make", name: args.get("make") }
: args.getOptional("chain") !== undefined
? { type: "chain" }
: args.getOptional("list") === ""
? { type: "list" }
: args.getOptional("list") === "db"
? { type: "list-db" }
: args.getOptional("list") === "digest"
? { type: "digest" }
: args.getOptional("undo") !== undefined
? { type: "undo", version: args.get("undo") }
: { type: "apply", after: [] };
if (!args.getFlag("skip-config")) {
for (const config of await (0, readConfigs_1.readConfigs)("pg-mig.config", action.type, {
Dest: Dest_1.Dest,
})) {
Object.assign(process.env, (0, mapValues_1.default)((0, pickBy_1.default)(config, (v) => typeof v === "string" ||
typeof v === "number" ||
typeof v === "boolean"), String));
if (action.type === "apply") {
if ("after" in config && typeof config.after === "function") {
action.after.push(config.after);
}
}
}
}
return migrate({
migDir: args.get("migdir", process.env["PGMIGDIR"]),
hosts: (0, compact_1.default)(args
.get("hosts", process.env["PGHOST"] || "127.0.0.1")
.split(/[\s,;]+/)
.map((host) => host.trim())),
port: parseInt(args.get("port", process.env["PGPORT"] || "")) || undefined,
user: args.get("user", process.env["PGUSER"] || "") || undefined,
pass: args.get("pass", process.env["PGPASSWORD"] || "") || undefined,
db: args.get("db", process.env["PGDATABASE"] || "") || undefined,
createDB: args.getFlag("createdb") ||
![undefined, null, "", "0", "false", "undefined", "null", "no"].includes(process.env["PGCREATEDB"]),
parallelism: parseInt(args.get("parallelism", "0")) || undefined,
dry: args.getFlag("dry"),
force: args.getFlag("force"),
validShardSchemasSql: args.getOptional("valid-shard-schemas-sql") ||
process.env["PGVALIDSHARDSCHEMASSQL"] ||
undefined,
action,
});
}
/**
* Similar to main(), but accepts options explicitly, not from process.argv.
* This function is meant to be called from other tools.
*/
async function migrate(options) {
var _a;
const registry = new Registry_1.Registry(options.migDir);
if (options.action.type === "digest") {
return (0, actionDigest_1.actionDigest)(options, registry);
}
if (options.hosts.length === 0) {
throw "No hosts provided.";
}
const hostDests = options.hosts.map((host) => Dest_1.Dest.create(host, options));
// Available in *.sql migration version files.
process.env["PG_MIG_HOSTS"] = hostDests
.map((dest) => dest.getHostSpec())
.join(",");
const portIsSignificant = hostDests.some((dest) => dest.port !== hostDests[0].port);
const dbIsSignificant = hostDests.some((dest) => dest.db !== hostDests[0].db);
for (const dest of hostDests) {
dest.setSignificance({ portIsSignificant, dbIsSignificant });
}
(0, render_1.printText)((0, compact_1.default)([
"Running on " + hostDests.map((dest) => dest.getName()).join(","),
!portIsSignificant && `port ${hostDests[0].port}`,
!dbIsSignificant && `db ${hostDests[0].db}`,
]).join(", "));
if (options.action.type === "make") {
return (0, actionMake_1.actionMake)(options, registry, options.action.name);
}
if (options.action.type === "chain") {
return (0, actionChain_1.actionChain)(options, registry);
}
if (options.action.type === "list") {
return (0, actionList_1.actionList)(options, registry);
}
if (options.action.type === "list-db") {
return (0, actionListDB_1.actionListDB)(options, hostDests);
}
while (true) {
const { success, hasMoreWork } = await (0, actionUndoOrApply_1.actionUndoOrApply)(options, hostDests, registry);
if (!options.dry &&
options.action.type === "apply" &&
success &&
!hasMoreWork) {
for (const after of (_a = options.action.after) !== null && _a !== void 0 ? _a : []) {
await after();
}
}
if (!success || !hasMoreWork) {
return success;
}
}
}
/**
* Loads the digest strings from the provided databases and chooses the one
* which reflects the database schema status the best.
*/
async function loadDBDigest(dests, sqlRunner) {
const digests = await Dest_1.Dest.loadDigests(dests, sqlRunner);
return Registry_1.Registry.chooseBestDigest(digests);
}
/**
* A wrapper around main() to call it from a bin script.
*/
function cli() {
main(process.argv.slice(2))
.then((success) => process.exit(success ? 0 : 1))
.catch((e) => {
(0, render_1.printError)(e);
process.exit(1);
});
}
if (require.main === module) {
cli();
}
//# sourceMappingURL=cli.js.map