UNPKG

@clickup/pg-mig

Version:

PostgreSQL schema migration tool with microsharding and clustering support

187 lines 7.45 kB
"use strict"; 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