diff --git a/.gitignore b/.gitignore index 7cd6bf8..73c6d11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ .env -src/.env .nvmrc node_modules yarn-error.log yarn.lock coverage *migrations +.upmigrc.js \ No newline at end of file diff --git a/README.md b/README.md index 58293d9..db0e3de 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,20 @@ or ```bash yarn add pg-upmig ``` + +## Configuration +Either use environment variables +```bash +UPMIG_PATH=./migrations +UPMIG_TABLE=pg_upmig +``` +or use `.upmigrc.js` to set global options: +```javascript +module.exports = { + migrations: "./migrations", // Where to store migrations files + table: "pg_upmig", // Table name where migrations history is stored +}; +``` ## Migrations folder tree view ``` . @@ -54,7 +68,6 @@ Usage: pg-upmig [options] [command] Options: -V, --version output the version number -h, --help display help for command - -e, --env specify environment file path -m, --migrations specify migrations path (default: "./migrations") -p, --pgtable specify migration table name (default: "pg_upmig") @@ -141,7 +154,6 @@ migration.up({ |options|[Global options](#global-options)| |connection|[Connection parameters](#connection-parameters) for default `node-postgres` client| |client|[Custom postgres client](#custom-postgres-client) instance| -|enfFile|Environment file path (.env)| ##### Global options |Key|Type|Description|Default| @@ -244,7 +256,6 @@ function customFunction () { *Promise* Creates timestamped migration files (sql file creation is avoided when `nosql` is `true`) using `template.stub` as a template. Filename collision is handled even if this case should not happen. -`name` is Returns the full name of created file : `1600775947530_create-table` @@ -282,3 +293,4 @@ Returns an array of objects representing migration file: |name|string|Migration name excluding timestamp.| ## Todo - [ ] Custom logger implementation +- [ ] Remove dispensable dependencies \ No newline at end of file diff --git a/jest.setup.js b/jest.setup.js index 5a97f11..de77b88 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -1,3 +1,14 @@ -require("dotenv").config({path: "src/.env"}); +const fs = require("fs"); -module.exports = () => {}; \ No newline at end of file +module.exports = async () => { + try { + (await fs.promises.readFile(".env")) // read config file + .toString("utf8") // buffer to string + .split(/\n|\r|\r/) // break by new line + .filter(item => /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/.test(item)) // keep key / val + .map(item => { + const match = item.match(/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/); + if (!Object.prototype.hasOwnProperty.call(process.env, match[1])) process.env[match[1]] = match[2].replace(/^("|')(.*)\1$/, "$2"); // set env + }); + } catch (error) {} +}; \ No newline at end of file diff --git a/package.json b/package.json index 0585dad..b4da723 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-upmig", - "version": "0.0.25", + "version": "0.0.26", "description": "Postgresql migration tool", "keywords": [ "database", @@ -25,7 +25,6 @@ "dependencies": { "chalk": "^4.1.0", "commander": "^6.1.0", - "dotenv": "^8.2.0", "pg": "^8.3.3" }, "devDependencies": { diff --git a/src/cli.js b/src/cli.js index f163ad9..68bf9a6 100644 --- a/src/cli.js +++ b/src/cli.js @@ -4,7 +4,16 @@ const migration = require("./index.js"); const pkg = require("../package.json"); const chalk = require("chalk"); const { program } = require("commander"); +const path = require("path"); +let migrationsPath = process.env.UPMIG_PATH||"./migrations"; +let pgTable = process.env.UPMIG_TABLE||"pg_upmig"; + +try { + const dotConfig = require(path.join(process.cwd(), ".upmigrc.js")); + migrationsPath = dotConfig.migrations?dotConfig.migrations:migrationsPath; + pgTable = dotConfig.table?dotConfig.table:pgTable; +} catch (error) {} function steps (s) { const parsed = parseInt(s.replace(/[^0-9]+/g, "")); @@ -26,16 +35,9 @@ function migTable (name) { exports.migTable = migTable; -let envFile = ".env"; -let migrationsPath = "./migrations"; -let pgTable = "pg_upmig"; - function setOpt (namespace) { return (option) => { switch (namespace) { - case "env": - envFile = option ? option:envFile; - break; case "migrations": migrationsPath = option ? option:migrationsPath; break; @@ -49,25 +51,22 @@ exports.setOpt = setOpt; exports._env = () => { return { - envFile, migrationsPath, pgTable }; } -program.on("option:env", setOpt("env")); program.on("option:migrations", setOpt("migrations")); program.on("option:pgtable", setOpt("pgtable")); program.version(pkg.version) .arguments(" [opt]") .usage(" [options]") -.option("-e, --env ", "specify environment file path") .option("-m, --migrations ", "specify migrations path", "./migrations") .option("-p, --pgtable
", "specify migration table name", "pg-upmig", migTable); async function up (cmd) { - const mig = new migration({envFile}); + const mig = new migration(); await mig.init({ table: pgTable, migrations: migrationsPath, @@ -98,9 +97,9 @@ program }); async function create (cmd, name){ - const mig = new migration({envFile}); + const mig = new migration(); await mig.init({migrations: migrationsPath, table: pgTable}); - const file = await mig.create(name.join("-"), cmd.nosql); + const file = await mig.create(name?name.join("-"):"", cmd.nosql); mig.release(); return file; } @@ -119,7 +118,7 @@ program }); async function pending (cmd) { - const mig = new migration({envFile}); + const mig = new migration(); await mig.init({migrations: migrationsPath, table: pgTable, history: cmd.history}); const list = await mig.pending(); mig.release(); diff --git a/src/index.js b/src/index.js index b50bda5..e1314bf 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,6 @@ /** @module pg-upmig */ const { Client, types } = require("pg"); -const dotenv = require("dotenv"); const path = require("path"); const fs = require("fs"); @@ -12,7 +11,6 @@ const fs = require("fs"); * @param {Object} params Constructor arguments * @param {Object} params.client Custom database client * @param {Object} params.connection PG connection params - * @param {String} params.envFile Environment file path * @param {Object} params.options Global options * @param {String} params.options.table Migrations table * @param {String} params.options.migrations Migrations path @@ -44,8 +42,8 @@ class migration { this.isConnected = false; this.options = { - table: "pg_upmig", - migrations: "./migrations", + table: process.env.UPMIG_TABLE||"pg_upmig", + migrations: process.env.UPMIG_PATH||"./migrations", transactionnal: true, queryMethod: "query", connectMethod: "connect", @@ -56,29 +54,24 @@ class migration { debug: false }; - if (params.options) { - Object.assign(this.options, params.options); - } - - // finds environment file path first if defined try { - const cfg = dotenv.config({path: params.envFile}); - if (cfg.error) { - throw new Error("Check for standard environment file (.env)"); - } - } catch (error) { - this._debug(error, 1); - dotenv.config(); + const dotConfig = require(path.join(process.cwd(),".upmigrc.js")); + this.options.migrations = dotConfig.migrations?dotConfig.migrations:this.options.migrations; + this.options.table = dotConfig.table?dotConfig.table:this.options.table; + } catch (error) { } + + if ((params||{}).options) { + Object.assign(this.options, params.options); } // Sets custom database client - if (params.client) { + if ((params||{}).client) { this.client = params.client; } // Sets default database client if (!this.client) { - if (params.connection) { + if ((params||{}).connection) { // Uses connection params this.client = new Client(params.connection); } else { diff --git a/tests/cli.test.js b/tests/cli.test.js index a6991c1..1de262f 100644 --- a/tests/cli.test.js +++ b/tests/cli.test.js @@ -49,16 +49,12 @@ describe("Core helpers logic", () => { }); test("setOpt function", () => { - cli.setOpt("env")(""); - expect(cli._env().envFile).toBe(".env"); - cli.setOpt("env")("null"); - expect(cli._env().envFile).toBe("null"); cli.setOpt("migrations")(""); - expect(cli._env().migrationsPath).toBe("./migrations"); + expect(cli._env().migrationsPath).toBe(fixtures.migrations); cli.setOpt("migrations")("null"); expect(cli._env().migrationsPath).toBe("null"); cli.setOpt("pgtable")(""); - expect(cli._env().pgTable).toBe("pg_upmig"); + expect(cli._env().pgTable).toBe(fixtures.pgTable); cli.setOpt("pgtable")("null"); expect(cli._env().pgTable).toBe("null"); }); @@ -251,7 +247,7 @@ describe("Create migration file", () => { }); test("Init and create new migration file", async () => { - const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); + const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); const line = response.trim(); expect(line).toEqual(expect.stringMatching(/Migration file created:\s+[0-9]+_.+$/i)); const filename = line.replace(/^.+\s([0-9]+_[0-9a-z_\-]+)$/i, "$1"); @@ -260,7 +256,7 @@ describe("Create migration file", () => { }); test("Create new migration file without sql placeholder file", async () => { - const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.new, fixtures.migrationFile, "-n"], [], {env}); + const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.new, fixtures.migrationFile, "-n"], [], {env}); const line = response.trim(); expect(line).toEqual(expect.stringMatching(/Migration file created:\s+[0-9]+_.+$/i)); const filename = line.replace(/^.+\s([0-9]+_[0-9a-z_\-]+)$/i, "$1"); @@ -283,7 +279,7 @@ describe("List pending migrations", () => { test("List pending migratons without history", async () => { const details = []; for (let i = 0; i <= Math.round(Math.random() * 5); i++) { - const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); + const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); details.push(created.trim().match(/\s([0-9]+)_([0-9a-z_\-]+)$/i)); } const containing = []; @@ -293,17 +289,17 @@ describe("List pending migrations", () => { } containing.push(expect.stringMatching(new RegExp(`Pending migrations:\\s+${details.length}$`, "i"))); - const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.list], [], {env}); + const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.list], [], {env}); expect(response.trim().split(EOL)).toEqual(containing); // Perform left pending migrations - await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.perform], [], {env}); + await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.perform], [], {env}); }); test("List pending migratons with history", async () => { const details = []; for (let i = 0; i <= Math.round(Math.random() * 5); i++) { - const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); + const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); details.push(created.trim().match(/\s([0-9]+)_([0-9a-z_\-]+)$/i)); } const containing = []; @@ -313,7 +309,7 @@ describe("List pending migrations", () => { } containing.push(expect.stringMatching(new RegExp(`Pending migrations:\\s+${details.length}/\[0\-9\]\+\\s\\(\[0\-9\]\+\\sdone\\)$`, "i"))); - const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.list, "-H"], [], {env}); + const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.list, "-H"], [], {env}); expect(response.trim().split(EOL)).toEqual(containing); }); }); @@ -330,9 +326,9 @@ describe("Perform pending migrations", () => { }); test("Perform all pending migrations", async () => { - const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); + const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); const details = created.trim().match(/\s([0-9]+)_([0-9a-z_\-]+)$/i); - const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.perform], [], {env}); + const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.perform], [], {env}); expect(response.trim().split(EOL)).toEqual(expect.arrayContaining([ expect.stringMatching(new RegExp(`${details[2]}\\s+${details[1]}$`, "i")), expect.stringMatching(/Migrations completed:\s+1$/i) @@ -342,10 +338,10 @@ describe("Perform pending migrations", () => { test("Perform specific number of pending migrations", async () => { const details = []; for (let i = 0; i < 3; i++) { - const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); + const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); details.push(created.trim().match(/\s([0-9]+)_([0-9a-z_\-]+)$/i)); } - const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.perform, "-s", "2"], [], {env}); + const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.perform, "-s", "2"], [], {env}); expect(response.trim().split(EOL)).toEqual(expect.arrayContaining([ expect.stringMatching(new RegExp(`${details[0][2]}\\s+${details[0][1]}$`, "i")), expect.stringMatching(new RegExp(`${details[1][2]}\\s+${details[1][1]}$`, "i")), @@ -353,17 +349,17 @@ describe("Perform pending migrations", () => { ])); // Perform left pending migrations - await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.perform], [], {env}); + await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.perform], [], {env}); }); test("Perform pending migrations till specific timestamp", async () => { const details = []; for (let i = 0; i < 3; i++) { - const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); + const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); details.push(created.trim().match(/\s([0-9]+)_([0-9a-z_\-]+)$/i)); } const ts = parseInt(details[1][1], 10) + 1; - const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.perform, "-t", ts], [], {env}); + const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.perform, "-t", ts], [], {env}); expect(response.trim().split(EOL)).toEqual(expect.arrayContaining([ expect.stringMatching(new RegExp(`${details[0][2]}\\s+${details[0][1]}$`, "i")), expect.stringMatching(new RegExp(`${details[1][2]}\\s+${details[1][1]}$`, "i")), @@ -371,13 +367,13 @@ describe("Perform pending migrations", () => { ])); // Perform left pending migrations - await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.perform], [], {env}); + await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.perform], [], {env}); }); test("Perform pending migrations first condition reached (steps & timestamp)", async () => { const details = []; for (let i = 0; i <= 4; i++) { - const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); + const created = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.new, fixtures.migrationFile], [], {env}); details.push(created.trim().match(/\s([0-9]+)_([0-9a-z_\-]+)$/i)); } const byTs = Math.round(Math.random() * 3); @@ -391,7 +387,7 @@ describe("Perform pending migrations", () => { } containing.push(expect.stringMatching(new RegExp(`Migrations completed:\\s+${min+1}$`, "i"))); - const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, "-e", fixtures.envFile, fixtures.cli._cmds.perform, "-t", ts, "-s", bySt+1], [], {env}); + const response = await proc.execute(["-m", fixtures.migrations, "-p", fixtures.pgTable, fixtures.cli._cmds.perform, "-t", ts, "-s", bySt+1], [], {env}); expect(response.trim().split(EOL)).toEqual(expect.arrayContaining(containing)); }); }); \ No newline at end of file diff --git a/tests/jest.fixtures.js b/tests/jest.fixtures.js index d0ea31d..84449ec 100644 --- a/tests/jest.fixtures.js +++ b/tests/jest.fixtures.js @@ -1,11 +1,10 @@ module.exports = { - envFile: "src/.env", migrations: "./jest-migrations", sql: "sql", templateStub: "template.stub", pgTable: "jest_pg_upmig", migrationFile: "jest-up", - jestmigTable: "jest_mig_test", + jestmigTable: "jest_upmig_test", cli: { _cmds: { "perform": "up", @@ -14,7 +13,6 @@ module.exports = { }, globalOptions: [ "-V, --version", - "-e, --env ", "-m, --migrations ", "-p, --pgtable
", "-h, --help" diff --git a/tests/jest.teardown.js b/tests/jest.teardown.js index 9f244ab..4b52312 100644 --- a/tests/jest.teardown.js +++ b/tests/jest.teardown.js @@ -1,7 +1,6 @@ const fs = require("fs"); const fixtures = require("./jest.fixtures.js"); const { Client } = require("pg"); -require("dotenv").config({path: "../src/.env"}); module.exports = async () => { try {