Refactor and fixes
This commit is contained in:
parent
0709670550
commit
b08617d77e
18
README.md
18
README.md
|
@ -12,7 +12,7 @@ Any help is welcomed! Feel free to contribute.
|
||||||
|
|
||||||
* Node.js <= 12.0
|
* Node.js <= 12.0
|
||||||
* Git
|
* Git
|
||||||
* Docker (Only to setup a local server)
|
* Docker (Local server)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -35,19 +35,19 @@ $ npx myvc [command]
|
||||||
Execute *myvc* with the desired command.
|
Execute *myvc* with the desired command.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ myvc [-w|--workspace] [-e|--env] [-h|--help] command
|
$ myvc [-w|--workspace] [-r|--remote] [-d|--debug] [-h|--help] command
|
||||||
```
|
```
|
||||||
|
|
||||||
The default workspace directory is the current working directory and unless
|
The default workspace directory is the current working directory and unless
|
||||||
otherwise indicated, the default environment is *local*.
|
otherwise indicated, the default remote is *local*.
|
||||||
|
|
||||||
Commands for database versioning:
|
Database versioning commands:
|
||||||
|
|
||||||
* **init**: Initialize an empty workspace.
|
* **init**: Initialize an empty workspace.
|
||||||
* **pull**: Export database routines into workspace.
|
* **pull**: Export database routines into workspace.
|
||||||
* **push**: Apply changes into database.
|
* **push**: Apply changes into database.
|
||||||
|
|
||||||
Commands for local server management:
|
Local server management commands:
|
||||||
|
|
||||||
* **dump**: Export database structure and fixtures from *production*.
|
* **dump**: Export database structure and fixtures from *production*.
|
||||||
* **run**: Build and starts local database server container.
|
* **run**: Build and starts local database server container.
|
||||||
|
@ -67,14 +67,14 @@ Now you can configure MyVC using *myvc.config.yml* file, located at the root of
|
||||||
your workspace. This file should include the project codename and schemas/tables
|
your workspace. This file should include the project codename and schemas/tables
|
||||||
wich are exported when you use *pull* or *dump* commands.
|
wich are exported when you use *pull* or *dump* commands.
|
||||||
|
|
||||||
### Environments
|
### Remotes
|
||||||
|
|
||||||
Create database connection configuration for each environment at *remotes*
|
Create database connection configuration for each environment at *remotes*
|
||||||
folder using standard MySQL *ini* configuration files. The predefined
|
folder using standard MySQL *ini* configuration files. The convention remote
|
||||||
environment names are *production* and *test*.
|
names are *production* and *test*.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
remotes/[environment].ini
|
remotes/[remote].ini
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dumps
|
### Dumps
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const MyVC = require('./myvc');
|
||||||
|
new MyVC().run();
|
253
index.js
253
index.js
|
@ -1,253 +0,0 @@
|
||||||
require('require-yaml');
|
|
||||||
require('colors');
|
|
||||||
const getopts = require('getopts');
|
|
||||||
const packageJson = require('./package.json');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const ini = require('ini');
|
|
||||||
const path = require('path');
|
|
||||||
const mysql = require('mysql2/promise');
|
|
||||||
const nodegit = require('nodegit');
|
|
||||||
|
|
||||||
class MyVC {
|
|
||||||
async run(command) {
|
|
||||||
console.log(
|
|
||||||
'MyVC (MySQL Version Control)'.green,
|
|
||||||
`v${packageJson.version}`.magenta
|
|
||||||
);
|
|
||||||
|
|
||||||
const opts = {};
|
|
||||||
const argv = process.argv.slice(2);
|
|
||||||
const cliOpts = getopts(argv, {
|
|
||||||
alias: {
|
|
||||||
env: 'e',
|
|
||||||
workspace: 'w',
|
|
||||||
socket: 's',
|
|
||||||
debug: 'd',
|
|
||||||
version: 'v',
|
|
||||||
help: 'h'
|
|
||||||
},
|
|
||||||
default: {
|
|
||||||
workspace: process.cwd()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (cliOpts.version)
|
|
||||||
process.exit(0);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!command) {
|
|
||||||
const commandName = cliOpts._[0];
|
|
||||||
if (!commandName) {
|
|
||||||
console.log(
|
|
||||||
'Usage:'.gray,
|
|
||||||
'[npx] myvc'
|
|
||||||
+ '[-w|--workspace]'
|
|
||||||
+ '[-e|--env]'
|
|
||||||
+ '[-d|--debug]'
|
|
||||||
+ '[-h|--help]'
|
|
||||||
+ '[-v|--version]'
|
|
||||||
+ 'command'.blue
|
|
||||||
);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const commands = [
|
|
||||||
'init',
|
|
||||||
'pull',
|
|
||||||
'push',
|
|
||||||
'dump',
|
|
||||||
'start',
|
|
||||||
'run'
|
|
||||||
];
|
|
||||||
|
|
||||||
if (commands.indexOf(commandName) == -1)
|
|
||||||
throw new Error (`Unknown command '${commandName}'`);
|
|
||||||
|
|
||||||
const Klass = require(`./myvc-${commandName}`);
|
|
||||||
command = new Klass();
|
|
||||||
}
|
|
||||||
|
|
||||||
const commandOpts = getopts(argv, command.myOpts);
|
|
||||||
Object.assign(cliOpts, commandOpts);
|
|
||||||
|
|
||||||
for (const opt in cliOpts) {
|
|
||||||
if (opt.length > 1 || opt == '_')
|
|
||||||
opts[opt] = cliOpts[opt];
|
|
||||||
}
|
|
||||||
|
|
||||||
parameter('Workspace:', opts.workspace);
|
|
||||||
parameter('Environment:', opts.env);
|
|
||||||
|
|
||||||
await this.load(opts);
|
|
||||||
command.opts = opts;
|
|
||||||
await command.run(this, opts);
|
|
||||||
await this.unload();
|
|
||||||
} catch (err) {
|
|
||||||
if (err.name == 'Error' && !opts.debug)
|
|
||||||
console.error('Error:'.gray, err.message.red);
|
|
||||||
else
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parameter(parameter, value) {
|
|
||||||
console.log(parameter.gray, (value || 'null').blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
async load(opts) {
|
|
||||||
// Configuration file
|
|
||||||
|
|
||||||
const config = require(`${__dirname}/myvc.default.yml`);
|
|
||||||
|
|
||||||
const configFile = 'myvc.config.yml';
|
|
||||||
const configPath = path.join(opts.workspace, configFile);
|
|
||||||
if (await fs.pathExists(configPath))
|
|
||||||
Object.assign(config, require(configPath));
|
|
||||||
|
|
||||||
Object.assign(opts, config);
|
|
||||||
opts.configFile = configFile;
|
|
||||||
|
|
||||||
// Database configuration
|
|
||||||
|
|
||||||
let iniFile = 'db.ini';
|
|
||||||
let iniDir = __dirname;
|
|
||||||
if (opts.env) {
|
|
||||||
iniFile = `remotes/${opts.env}.ini`;
|
|
||||||
iniDir = opts.workspace;
|
|
||||||
}
|
|
||||||
const iniPath = path.join(iniDir, iniFile);
|
|
||||||
|
|
||||||
if (!await fs.pathExists(iniPath))
|
|
||||||
throw new Error(`Database config file not found: ${iniFile}`);
|
|
||||||
|
|
||||||
const iniConfig = ini.parse(await fs.readFile(iniPath, 'utf8')).client;
|
|
||||||
const dbConfig = {
|
|
||||||
host: iniConfig.host,
|
|
||||||
port: iniConfig.port,
|
|
||||||
user: iniConfig.user,
|
|
||||||
password: iniConfig.password,
|
|
||||||
database: opts.versionSchema,
|
|
||||||
authPlugins: {
|
|
||||||
mysql_clear_password() {
|
|
||||||
return () => iniConfig.password + '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (iniConfig.ssl_ca) {
|
|
||||||
dbConfig.ssl = {
|
|
||||||
ca: await fs.readFile(`${opts.workspace}/${iniConfig.ssl_ca}`),
|
|
||||||
rejectUnauthorized: iniConfig.ssl_verify_server_cert != undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts.socket)
|
|
||||||
dbConfig.socketPath = '/var/run/mysqld/mysqld.sock';
|
|
||||||
|
|
||||||
Object.assign(opts, {
|
|
||||||
iniFile,
|
|
||||||
dbConfig
|
|
||||||
});
|
|
||||||
this.opts = opts;
|
|
||||||
}
|
|
||||||
|
|
||||||
async dbConnect() {
|
|
||||||
if (!this.conn)
|
|
||||||
this.conn = await this.createConnection();
|
|
||||||
return this.conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
async createConnection() {
|
|
||||||
return await mysql.createConnection(this.opts.dbConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
async unload() {
|
|
||||||
if (this.conn)
|
|
||||||
await this.conn.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchDbVersion() {
|
|
||||||
const {opts} = this;
|
|
||||||
|
|
||||||
const [[res]] = await this.conn.query(
|
|
||||||
`SELECT COUNT(*) > 0 tableExists
|
|
||||||
FROM information_schema.tables
|
|
||||||
WHERE TABLE_SCHEMA = ?
|
|
||||||
AND TABLE_NAME = 'version'`,
|
|
||||||
[opts.versionSchema]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.tableExists) {
|
|
||||||
const structure = await fs.readFile(`${__dirname}/structure.sql`, 'utf8');
|
|
||||||
await this.conn.query(structure);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [[version]] = await this.conn.query(
|
|
||||||
`SELECT number, gitCommit
|
|
||||||
FROM version WHERE code = ?`,
|
|
||||||
[opts.code]
|
|
||||||
);
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
async changedRoutines(commit) {
|
|
||||||
const repo = await nodegit.Repository.open(this.opts.workspace);
|
|
||||||
|
|
||||||
const from = await repo.getCommit(commit);
|
|
||||||
const fromTree = await from.getTree();
|
|
||||||
|
|
||||||
const to = await repo.getHeadCommit();
|
|
||||||
const toTree = await to.getTree();
|
|
||||||
|
|
||||||
const diff = await toTree.diff(fromTree);
|
|
||||||
const patches = await diff.patches();
|
|
||||||
|
|
||||||
const changes = [];
|
|
||||||
for (const patch of patches) {
|
|
||||||
const path = patch.newFile().path();
|
|
||||||
const match = path.match(/^routines\/(.+)\.sql$/);
|
|
||||||
if (!match) continue;
|
|
||||||
|
|
||||||
changes.push({
|
|
||||||
mark: patch.isDeleted() ? '-' : '+',
|
|
||||||
path: match[1]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return changes.sort(
|
|
||||||
(a, b) => b.mark == '-' && b.mark != a.mark ? 1 : -1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async cachedChanges() {
|
|
||||||
const changes = [];
|
|
||||||
const dumpDir = `${this.opts.workspace}/dump`;
|
|
||||||
const dumpChanges = `${dumpDir}/.changes`;
|
|
||||||
|
|
||||||
if (!await fs.pathExists(dumpChanges))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const readline = require('readline');
|
|
||||||
const rl = readline.createInterface({
|
|
||||||
input: fs.createReadStream(dumpChanges),
|
|
||||||
//output: process.stdout,
|
|
||||||
console: false
|
|
||||||
});
|
|
||||||
|
|
||||||
for await (const line of rl) {
|
|
||||||
changes.push({
|
|
||||||
mark: line.charAt(0),
|
|
||||||
path: line.substr(1)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return changes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = MyVC;
|
|
||||||
|
|
||||||
if (require.main === module)
|
|
||||||
new MyVC().run();
|
|
11
myvc-dump.js
11
myvc-dump.js
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
const MyVC = require('./index');
|
const MyVC = require('./myvc');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const docker = require('./docker');
|
const docker = require('./docker');
|
||||||
|
@ -11,10 +11,10 @@ class Dump {
|
||||||
get myOpts() {
|
get myOpts() {
|
||||||
return {
|
return {
|
||||||
alias: {
|
alias: {
|
||||||
env: 'e'
|
remote: 'r'
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
env: 'production'
|
remote: 'production'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class Dump {
|
||||||
|
|
||||||
await docker.build(__dirname, {
|
await docker.build(__dirname, {
|
||||||
tag: 'myvc/client',
|
tag: 'myvc/client',
|
||||||
file: path.join(__dirname, 'Dockerfile.client')
|
file: path.join(__dirname, 'server', 'Dockerfile')
|
||||||
}, opts.debug);
|
}, opts.debug);
|
||||||
|
|
||||||
let dumpArgs = [
|
let dumpArgs = [
|
||||||
|
@ -84,7 +84,8 @@ class Dump {
|
||||||
async dockerRun(command, args, execOptions) {
|
async dockerRun(command, args, execOptions) {
|
||||||
const commandArgs = [command].concat(args);
|
const commandArgs = [command].concat(args);
|
||||||
await docker.run('myvc/client', commandArgs, {
|
await docker.run('myvc/client', commandArgs, {
|
||||||
volume: `${this.opts.workspace}:/workspace`
|
volume: `${this.opts.workspace}:/workspace`,
|
||||||
|
rm: true
|
||||||
}, execOptions);
|
}, execOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
const MyVC = require('./index');
|
const MyVC = require('./myvc');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
class Init {
|
class Init {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
const MyVC = require('./index');
|
const MyVC = require('./myvc');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const ejs = require('ejs');
|
const ejs = require('ejs');
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
const MyVC = require('./index');
|
const MyVC = require('./myvc');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const nodegit = require('nodegit');
|
const nodegit = require('nodegit');
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class Push {
|
||||||
version = userVersion;
|
version = userVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.env == 'production') {
|
if (opts.remote == 'production') {
|
||||||
console.log(
|
console.log(
|
||||||
'\n ( ( ) ( ( ) ) '
|
'\n ( ( ) ( ( ) ) '
|
||||||
+ '\n )\\ ))\\ ) ( /( )\\ ) ( ))\\ ) ( /( ( /( '
|
+ '\n )\\ ))\\ ) ( /( )\\ ) ( ))\\ ) ( /( ( /( '
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
const MyVC = require('./index');
|
const MyVC = require('./myvc');
|
||||||
const docker = require('./docker');
|
const docker = require('./docker');
|
||||||
|
const Container = require('./docker').Container;
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Server = require('./server/server');
|
const Server = require('./server/server');
|
||||||
|
@ -37,7 +38,7 @@ class Run {
|
||||||
const changes = await myvc.changedRoutines(version.gitCommit);
|
const changes = await myvc.changedRoutines(version.gitCommit);
|
||||||
|
|
||||||
let isEqual = false;
|
let isEqual = false;
|
||||||
if (cache && changes && cache.length == changes.lenth)
|
if (cache && changes && cache.length == changes.length)
|
||||||
for (let i = 0; i < changes.length; i++) {
|
for (let i = 0; i < changes.length; i++) {
|
||||||
isEqual = cache[i].path == changes[i].path
|
isEqual = cache[i].path == changes[i].path
|
||||||
&& cache[i].mark == changes[i].mark;
|
&& cache[i].mark == changes[i].mark;
|
||||||
|
@ -45,6 +46,7 @@ class Run {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isEqual) {
|
if (!isEqual) {
|
||||||
|
console.log('not equal');
|
||||||
const fd = await fs.open(`${dumpDir}/.changes`, 'w+');
|
const fd = await fs.open(`${dumpDir}/.changes`, 'w+');
|
||||||
for (const change of changes)
|
for (const change of changes)
|
||||||
fs.write(fd, change.mark + change.path + '\n');
|
fs.write(fd, change.mark + change.path + '\n');
|
||||||
|
@ -85,7 +87,8 @@ class Run {
|
||||||
publish: `3306:${dbConfig.port}`
|
publish: `3306:${dbConfig.port}`
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
await this.rm();
|
const server = new Server(new Container(opts.code));
|
||||||
|
await server.rm();
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
const MyVC = require('./index');
|
const MyVC = require('./myvc');
|
||||||
const Container = require('./docker').Container;
|
const Container = require('./docker').Container;
|
||||||
const Server = require('./server/server');
|
const Server = require('./server/server');
|
||||||
const Run = require('./myvc-run');
|
const Run = require('./myvc-run');
|
||||||
|
|
255
myvc.js
255
myvc.js
|
@ -1,4 +1,255 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const MyVC = require('./');
|
require('require-yaml');
|
||||||
new MyVC().run();
|
require('colors');
|
||||||
|
const getopts = require('getopts');
|
||||||
|
const packageJson = require('./package.json');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const ini = require('ini');
|
||||||
|
const path = require('path');
|
||||||
|
const mysql = require('mysql2/promise');
|
||||||
|
const nodegit = require('nodegit');
|
||||||
|
|
||||||
|
class MyVC {
|
||||||
|
async run(command) {
|
||||||
|
console.log(
|
||||||
|
'MyVC (MySQL Version Control)'.green,
|
||||||
|
`v${packageJson.version}`.magenta
|
||||||
|
);
|
||||||
|
|
||||||
|
const opts = {};
|
||||||
|
const argv = process.argv.slice(2);
|
||||||
|
const cliOpts = getopts(argv, {
|
||||||
|
alias: {
|
||||||
|
remote: 'r',
|
||||||
|
workspace: 'w',
|
||||||
|
socket: 's',
|
||||||
|
debug: 'd',
|
||||||
|
version: 'v',
|
||||||
|
help: 'h'
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
workspace: process.cwd()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (cliOpts.version)
|
||||||
|
process.exit(0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!command) {
|
||||||
|
const commandName = cliOpts._[0];
|
||||||
|
if (!commandName) {
|
||||||
|
console.log(
|
||||||
|
'Usage:'.gray,
|
||||||
|
'[npx] myvc'
|
||||||
|
+ '[-w|--workspace]'
|
||||||
|
+ '[-r|--remote]'
|
||||||
|
+ '[-d|--debug]'
|
||||||
|
+ '[-h|--help]'
|
||||||
|
+ '[-v|--version]'
|
||||||
|
+ 'command'.blue
|
||||||
|
);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const commands = [
|
||||||
|
'init',
|
||||||
|
'pull',
|
||||||
|
'push',
|
||||||
|
'dump',
|
||||||
|
'start',
|
||||||
|
'run'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (commands.indexOf(commandName) == -1)
|
||||||
|
throw new Error (`Unknown command '${commandName}'`);
|
||||||
|
|
||||||
|
const Klass = require(`./myvc-${commandName}`);
|
||||||
|
command = new Klass();
|
||||||
|
}
|
||||||
|
|
||||||
|
const commandOpts = getopts(argv, command.myOpts);
|
||||||
|
Object.assign(cliOpts, commandOpts);
|
||||||
|
|
||||||
|
for (const opt in cliOpts) {
|
||||||
|
if (opt.length > 1 || opt == '_')
|
||||||
|
opts[opt] = cliOpts[opt];
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter('Workspace:', opts.workspace);
|
||||||
|
parameter('Remote:', opts.remote || 'local');
|
||||||
|
|
||||||
|
await this.load(opts);
|
||||||
|
command.opts = opts;
|
||||||
|
await command.run(this, opts);
|
||||||
|
await this.unload();
|
||||||
|
} catch (err) {
|
||||||
|
if (err.name == 'Error' && !opts.debug)
|
||||||
|
console.error('Error:'.gray, err.message.red);
|
||||||
|
else
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parameter(parameter, value) {
|
||||||
|
console.log(parameter.gray, (value || 'null').blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(opts) {
|
||||||
|
// Configuration file
|
||||||
|
|
||||||
|
const config = require(`${__dirname}/myvc.default.yml`);
|
||||||
|
|
||||||
|
const configFile = 'myvc.config.yml';
|
||||||
|
const configPath = path.join(opts.workspace, configFile);
|
||||||
|
if (await fs.pathExists(configPath))
|
||||||
|
Object.assign(config, require(configPath));
|
||||||
|
|
||||||
|
Object.assign(opts, config);
|
||||||
|
opts.configFile = configFile;
|
||||||
|
|
||||||
|
// Database configuration
|
||||||
|
|
||||||
|
let iniFile = 'db.ini';
|
||||||
|
let iniDir = __dirname;
|
||||||
|
if (opts.remote) {
|
||||||
|
iniFile = `remotes/${opts.remote}.ini`;
|
||||||
|
iniDir = opts.workspace;
|
||||||
|
}
|
||||||
|
const iniPath = path.join(iniDir, iniFile);
|
||||||
|
|
||||||
|
if (!await fs.pathExists(iniPath))
|
||||||
|
throw new Error(`Database config file not found: ${iniFile}`);
|
||||||
|
|
||||||
|
const iniConfig = ini.parse(await fs.readFile(iniPath, 'utf8')).client;
|
||||||
|
const dbConfig = {
|
||||||
|
host: iniConfig.host,
|
||||||
|
port: iniConfig.port,
|
||||||
|
user: iniConfig.user,
|
||||||
|
password: iniConfig.password,
|
||||||
|
database: opts.versionSchema,
|
||||||
|
authPlugins: {
|
||||||
|
mysql_clear_password() {
|
||||||
|
return () => iniConfig.password + '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (iniConfig.ssl_ca) {
|
||||||
|
dbConfig.ssl = {
|
||||||
|
ca: await fs.readFile(`${opts.workspace}/${iniConfig.ssl_ca}`),
|
||||||
|
rejectUnauthorized: iniConfig.ssl_verify_server_cert != undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opts.socket)
|
||||||
|
dbConfig.socketPath = '/var/run/mysqld/mysqld.sock';
|
||||||
|
|
||||||
|
Object.assign(opts, {
|
||||||
|
iniFile,
|
||||||
|
dbConfig
|
||||||
|
});
|
||||||
|
this.opts = opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
async dbConnect() {
|
||||||
|
if (!this.conn)
|
||||||
|
this.conn = await this.createConnection();
|
||||||
|
return this.conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createConnection() {
|
||||||
|
return await mysql.createConnection(this.opts.dbConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
async unload() {
|
||||||
|
if (this.conn)
|
||||||
|
await this.conn.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchDbVersion() {
|
||||||
|
const {opts} = this;
|
||||||
|
|
||||||
|
const [[res]] = await this.conn.query(
|
||||||
|
`SELECT COUNT(*) > 0 tableExists
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE TABLE_SCHEMA = ?
|
||||||
|
AND TABLE_NAME = 'version'`,
|
||||||
|
[opts.versionSchema]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.tableExists) {
|
||||||
|
const structure = await fs.readFile(`${__dirname}/structure.sql`, 'utf8');
|
||||||
|
await this.conn.query(structure);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [[version]] = await this.conn.query(
|
||||||
|
`SELECT number, gitCommit
|
||||||
|
FROM version WHERE code = ?`,
|
||||||
|
[opts.code]
|
||||||
|
);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
async changedRoutines(commit) {
|
||||||
|
const repo = await nodegit.Repository.open(this.opts.workspace);
|
||||||
|
|
||||||
|
const from = await repo.getCommit(commit);
|
||||||
|
const fromTree = await from.getTree();
|
||||||
|
|
||||||
|
const to = await repo.getHeadCommit();
|
||||||
|
const toTree = await to.getTree();
|
||||||
|
|
||||||
|
const diff = await toTree.diff(fromTree);
|
||||||
|
const patches = await diff.patches();
|
||||||
|
|
||||||
|
const changes = [];
|
||||||
|
for (const patch of patches) {
|
||||||
|
const path = patch.newFile().path();
|
||||||
|
const match = path.match(/^routines\/(.+)\.sql$/);
|
||||||
|
if (!match) continue;
|
||||||
|
|
||||||
|
changes.push({
|
||||||
|
mark: patch.isDeleted() ? '-' : '+',
|
||||||
|
path: match[1]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes.sort(
|
||||||
|
(a, b) => b.mark == '-' && b.mark != a.mark ? 1 : -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async cachedChanges() {
|
||||||
|
const changes = [];
|
||||||
|
const dumpDir = `${this.opts.workspace}/dump`;
|
||||||
|
const dumpChanges = `${dumpDir}/.changes`;
|
||||||
|
|
||||||
|
if (!await fs.pathExists(dumpChanges))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const readline = require('readline');
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: fs.createReadStream(dumpChanges),
|
||||||
|
//output: process.stdout,
|
||||||
|
console: false
|
||||||
|
});
|
||||||
|
|
||||||
|
for await (const line of rl) {
|
||||||
|
changes.push({
|
||||||
|
mark: line.charAt(0),
|
||||||
|
path: line.substr(1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MyVC;
|
||||||
|
|
||||||
|
if (require.main === module)
|
||||||
|
new MyVC().run();
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "myvc",
|
"name": "myvc",
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"author": "Verdnatura Levante SL",
|
"author": "Verdnatura Levante SL",
|
||||||
"description": "MySQL Version Control",
|
"description": "MySQL Version Control",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"bin": "myvc.js",
|
"bin": "cli.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/verdnatura/myvc.git"
|
"url": "https://github.com/verdnatura/myvc.git"
|
||||||
|
|
|
@ -32,7 +32,6 @@ RUN npm install --only=prod
|
||||||
|
|
||||||
COPY \
|
COPY \
|
||||||
structure.sql \
|
structure.sql \
|
||||||
index.js \
|
|
||||||
myvc.js \
|
myvc.js \
|
||||||
myvc-push.js \
|
myvc-push.js \
|
||||||
myvc.default.yml \
|
myvc.default.yml \
|
||||||
|
|
Loading…
Reference in New Issue