myt/myt-pull.js

184 lines
5.8 KiB
JavaScript
Raw Permalink Normal View History

2022-12-21 13:17:50 +00:00
const Myt = require('./myt');
const Command = require('./lib/command');
2020-11-14 01:38:56 +00:00
const fs = require('fs-extra');
2021-10-14 08:34:40 +00:00
const nodegit = require('nodegit');
const ExporterEngine = require('./lib/exporter-engine');
const repoExt = require('./lib/repo');
2022-12-21 13:17:50 +00:00
class Pull extends Command {
static usage = {
description: 'Incorporate database routine changes into workspace',
params: {
force: 'Do it even if there are local changes',
checkout: 'Move to same database commit before pull',
update: 'Update all routines',
sums: 'Save SHA sums of all objects'
},
operand: 'remote'
};
static opts = {
alias: {
force: 'f',
checkout: 'c',
update: 'u',
sums: 's'
},
boolean: [
'force',
'checkout',
'update',
'sums'
]
};
2022-12-21 13:17:50 +00:00
async run(myt, opts) {
const conn = await myt.dbConnect();
const repo = await myt.openRepo();
if (!opts.force) {
async function hasChanges(diff) {
if (diff)
for (const patch of await diff.patches()) {
const match = patch
.newFile()
.path()
.match(/^routines\/(.+)\.sql$/);
if (match) return true;
}
return false;
}
// Check for unstaged changes
const unstagedDiff = await repoExt.getUnstaged(repo);
if (await hasChanges(unstagedDiff))
throw new Error('You have unstaged changes, save them before pull');
// Check for staged changes
const stagedDiff = await repoExt.getStaged(repo);
if (await hasChanges(stagedDiff))
throw new Error('You have staged changes, save them before pull');
}
// Checkout to remote commit
if (opts.checkout) {
2022-12-21 13:17:50 +00:00
const version = await myt.fetchDbVersion();
if (version && version.gitCommit) {
const now = parseInt(new Date().toJSON());
2022-12-21 13:17:50 +00:00
const branchName = `myt-pull_${now}`;
console.log(`Creating branch '${branchName}' from database commit.`);
const commit = await repo.getCommit(version.gitCommit);
const branch = await nodegit.Branch.create(repo,
2022-12-21 13:17:50 +00:00
`myt-pull_${now}`, commit, () => {});
await repo.checkoutBranch(branch);
}
2021-10-14 08:34:40 +00:00
}
2020-12-02 07:35:26 +00:00
// Export routines to SQL files
console.log(`Incorporating routine changes.`);
const engine = new ExporterEngine(conn, opts);
2022-02-02 04:05:31 +00:00
await engine.init();
const shaSums = engine.shaSums;
2020-12-02 07:35:26 +00:00
const routinesDir = opts.routinesDir;
if (!await fs.pathExists(routinesDir))
await fs.mkdir(routinesDir);
2020-12-19 01:06:06 +00:00
// Delete old schemas
const schemas = await fs.readdir(routinesDir);
2020-12-19 01:06:06 +00:00
for (const schema of schemas) {
if (opts.schemas.indexOf(schema) == -1)
await fs.remove(`${routinesDir}/${schema}`, {recursive: true});
2020-12-19 01:06:06 +00:00
}
2020-12-02 07:35:26 +00:00
2022-02-02 04:05:31 +00:00
for (const schema in shaSums) {
if (!await fs.pathExists(`${routinesDir}/${schema}`))
engine.deleteSchemaSums(schema);
2022-02-02 04:05:31 +00:00
}
// Export objects to SQL files
2020-12-02 07:35:26 +00:00
for (const schema of opts.schemas) {
let schemaDir = `${routinesDir}/${schema}`;
2020-12-02 07:35:26 +00:00
if (!await fs.pathExists(schemaDir))
await fs.mkdir(schemaDir);
for (const exporter of engine.exporters)
await this.export(conn, engine, exporter, schema);
2020-12-02 07:35:26 +00:00
}
await engine.refreshPullDate();
await engine.saveInfo();
2020-11-14 01:38:56 +00:00
}
async export(conn, engine, exporter, schema) {
const {opts} = this;
const res = await exporter.query(conn, schema);
if (!res.length) return;
const type = exporter.objectType;
const routineDir = `${opts.routinesDir}/${schema}/${type}s`;
if (!await fs.pathExists(routineDir))
await fs.mkdir(routineDir);
const routineSet = new Set();
for (const params of res)
routineSet.add(params.name);
const routines = await fs.readdir(routineDir);
for (const routineFile of routines) {
const match = routineFile.match(/^(.*)\.sql$/);
if (!match) continue;
const routine = match[1];
if (!routineSet.has(routine)) {
await fs.remove(`${routineDir}/${routine}.sql`);
engine.deleteShaSum(type, schema, routine)
}
}
for (const params of res) {
const routineName = params.name;
const sql = exporter.format(params);
const routineFile = `${routineDir}/${routineName}.sql`;
let update = opts.update;
const oldSum = engine.getShaSum(type, schema, routineName);
if (oldSum || opts.sums || (opts.sumViews && type === 'view')) {
const shaSum = engine.shaSum(sql);
if (oldSum !== shaSum) {
engine.setShaSum(type, schema, routineName, shaSum);
update = true;
}
} else if (params.modified && engine.lastPull) {
if (params.modified > engine.lastPull)
update = true;
} else if (await fs.pathExists(routineFile)) {
const currentSql = await fs.readFile(routineFile, 'utf8');
if (sql != currentSql)
update = true;
} else
update = true;
if (update)
await fs.writeFile(routineFile, sql);
}
}
2020-11-14 01:38:56 +00:00
}
2020-12-02 07:35:26 +00:00
module.exports = Pull;
2020-11-14 01:38:56 +00:00
2020-12-02 07:35:26 +00:00
if (require.main === module)
2022-12-21 13:17:50 +00:00
new Myt().run(Pull);