Commit not found fix, SHAsums not used anymore, clean old versions
This commit is contained in:
Juan Ferrer 2022-04-30 02:06:56 +02:00
parent 112e346e2e
commit 33acdd8a86
9 changed files with 166 additions and 58 deletions

View File

@ -95,7 +95,7 @@ From now on, you can use the project as if it were a standard git repository
desired remote.
```text
$ myvc push [<remote>]
$ myvc push [<remote>] [--save-commit]
```
### Routines
@ -164,10 +164,10 @@ $ myvc init
Incorporates database routine changes into workspace.
```text
$ myvc pull [remote] [-f|--force] [-c|--checkout] [-u|--update]
$ myvc pull [remote] [-f|--force] [-c|--checkout] [-u|--update] [-s|save-sums]
```
When *checkout* option is provided, it does the following before export:
When *--checkout* option is provided, it does the following before export:
1. Get the last database push commit (saved in versioning tables).
2. Creates and checkout to a new branch based in database commit.
@ -177,16 +177,21 @@ When *checkout* option is provided, it does the following before export:
Applies versions and routine changes into database.
```text
$ myvc push [<remote>] [-f|--force]
$ myvc push [<remote>] [-f|--force] [-c|--save-commit] [-s|save-sums]
```
Commit is saved into database only if *--save-commit* option is provided, it
prevents from accidentally saving local commits into shared servers, causing
subsequent pushes from other clients to fail because they can't get that
commit from the git tree in order to get differences.
### version
Creates a new version folder, when name is not specified it generates a random
name mixing a color with a plant name.
```text
$ myvc version [<name>]
$ myvc version [<name>] [-c|--no-clean]
```
## Local server commands

74
lib.js
View File

@ -24,7 +24,7 @@ class Exporter {
this.attrs = require(`${templateDir}.js`);
}
async export(exportDir, schema, newSums, oldSums, update) {
async export(exportDir, schema, update, saveSum) {
const res = await this.query(schema);
if (!res.length) return;
@ -45,15 +45,32 @@ class Exporter {
await fs.remove(`${routineDir}/${routine}.sql`);
}
const engine = this.engine;
for (const params of res) {
const routineName = params.name;
const sql = this.format(params);
const routineFile = `${routineDir}/${routineName}.sql`;
const shaSum = this.engine.shaSum(sql);
newSums[routineName] = shaSum;
const oldSum = engine.getShaSum(routineName);
if (oldSum || saveSum) {
const shaSum = engine.shaSum(sql);
if (oldSum !== shaSum) {
engine.setShaSum(
this.objectType, 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 (oldSums[routineName] !== shaSum || update)
if (update)
await fs.writeFile(routineFile, sql);
}
}
@ -104,16 +121,26 @@ class Exporter {
class ExporterEngine {
constructor(conn, myvcDir) {
this.conn = conn;
this.shaFile = `${myvcDir}/.shasums.json`;
this.pullFile = `${myvcDir}/.pullinfo.json`;
this.exporters = [];
this.exporterMap = {};
}
async init () {
if (await fs.pathExists(this.shaFile))
this.shaSums = JSON.parse(await fs.readFile(this.shaFile, 'utf8'));
else
this.shaSums = {};
if (await fs.pathExists(this.pullFile)) {
this.pullInfo = JSON.parse(await fs.readFile(this.pullFile, 'utf8'));
const lastPull = this.pullInfo.lastPull;
if (lastPull)
this.pullInfo.lastPull = new Date(lastPull);
} else
this.pullInfo = {
lastPull: null,
shaSums: {}
};
this.shaSums = this.pullInfo.shaSums;
this.lastPull = this.pullInfo.lastPull;
this.infoChanged = false;
const types = [
'function',
@ -150,6 +177,14 @@ class ExporterEngine {
.digest('hex');
}
getShaSum(type, schema, name) {
try {
return this.shaSums[schema][type][name];
} catch (e) {};
return null;
}
setShaSum(type, schema, name, shaSum) {
if (!shaSum) {
this.deleteShaSum(type, schema, name);
@ -162,17 +197,32 @@ class ExporterEngine {
if (!shaSums[schema][type])
shaSums[schema][type] = {};
shaSums[schema][type][name] = shaSum;
this.infoChanged = true;
}
deleteShaSum(type, schema, name) {
try {
delete this.shaSums[schema][type][name];
this.infoChanged = true;
} catch (e) {};
}
async saveShaSums() {
await fs.writeFile(this.shaFile,
JSON.stringify(this.shaSums, null, ' '));
deleteSchemaSums(schema) {
delete this.shaSums[schema];
this.infoChanged = true;
}
async refreshPullDate() {
const [[row]] = await this.conn.query(`SELECT NOW() now`);
this.pullInfo.lastPull = row.now;
this.infoChanged = true;
}
async saveInfo() {
if (!this.infoChanged) return;
await fs.writeFile(this.pullFile,
JSON.stringify(this.pullInfo, null, ' '));
this.infoChanged = false;
}
}

View File

@ -10,7 +10,8 @@ class Pull {
params: {
force: 'Do it even if there are local changes',
checkout: 'Move to same database commit before pull',
update: 'Update routine file even is shasum is the same'
updateAll: 'Update all routines',
saveSums: 'Save SHA sums of all objects'
},
operand: 'remote'
};
@ -21,12 +22,14 @@ class Pull {
alias: {
force: 'f',
checkout: 'c',
update: 'u'
updateAll: 'u',
saveSums: 's'
},
boolean: [
'force',
'checkout',
'update'
'updateAll',
'saveSums'
]
};
}
@ -102,7 +105,7 @@ class Pull {
for (const schema in shaSums) {
if (!await fs.pathExists(`${exportDir}/${schema}`))
delete shaSums[schema];
engine.deleteSchemaSums(schema);
}
// Export objects to SQL files
@ -111,19 +114,14 @@ class Pull {
let schemaDir = `${exportDir}/${schema}`;
if (!await fs.pathExists(schemaDir))
await fs.mkdir(schemaDir);
if (!shaSums[schema])
shaSums[schema] = {};
const sums = shaSums[schema];
for (const exporter of engine.exporters) {
const type = exporter.objectType;
const oldSums = sums[type] || {};
sums[type] = {};
await exporter.export(exportDir, schema, sums[type], oldSums, opts.update);
}
for (const exporter of engine.exporters)
await exporter.export(exportDir,
schema, opts.update, opts.saveSums);
}
await engine.saveShaSums();
await engine.refreshPullDate();
await engine.saveInfo();
}
}

View File

@ -12,7 +12,9 @@ class Push {
return {
description: 'Apply changes into database',
params: {
force: 'Answer yes to all questions'
force: 'Answer yes to all questions',
saveCommit: 'Wether to save the commit SHA into database',
saveSums: 'Save SHA sums of pushed objects'
},
operand: 'remote'
};
@ -21,10 +23,14 @@ class Push {
get localOpts() {
return {
alias: {
force: 'f'
force: 'f',
saveCommit: 'c',
saveSums: 's'
},
boolean: [
'force'
'force',
'saveCommit',
'saveSums'
]
};
}
@ -33,6 +39,9 @@ class Push {
const conn = await myvc.dbConnect();
this.conn = conn;
if (opts.saveCommit == null && opts.remote == 'local')
opts.saveCommit = true;
// Obtain exclusive lock
const [[row]] = await conn.query(
@ -142,16 +151,16 @@ class Push {
if (versionDir == 'README.md')
continue;
const match = versionDir.match(/^([0-9]+)-([a-zA-Z0-9]+)?$/);
if (!match) {
const dirVersion = myvc.parseVersionDir(versionDir);
if (!dirVersion) {
logVersion('[?????]'.yellow, versionDir,
`Wrong directory name.`
);
continue;
}
const versionNumber = match[1];
const versionName = match[2];
const versionNumber = dirVersion.number;
const versionName = dirVersion.name;
if (versionNumber.length != version.number.length) {
logVersion('[*****]'.gray, versionDir,
@ -283,7 +292,7 @@ class Push {
await engine.init();
async function finalize() {
await engine.saveShaSums();
await engine.saveInfo();
if (routines.length) {
await conn.query('FLUSH PRIVILEGES');
@ -303,6 +312,7 @@ class Push {
if (exists)
newSql = await fs.readFile(fullPath, 'utf8');
const oldSql = await engine.fetchRoutine(type, schema, name);
const oldSum = engine.getShaSum(type, schema, name);
const isEqual = newSql == oldSql;
let actionMsg;
@ -336,7 +346,8 @@ class Push {
);
}
await engine.fetchShaSum(type, schema, name);
if (opts.saveSums || oldSum)
await engine.fetchShaSum(type, schema, name);
} else {
const escapedName =
scapedSchema + '.' +
@ -366,7 +377,7 @@ class Push {
} else
console.log(` -> No routines changed.`);
if (gitExists) {
if (gitExists && opts.saveCommit) {
const repo = await nodegit.Repository.open(this.opts.workspace);
const head = await repo.getHeadCommit();

View File

@ -10,7 +10,8 @@ class Version {
return {
description: 'Creates a new version',
params: {
name: 'Name for the new version'
name: 'Name for the new version',
noClean: 'Do not clean old versions'
},
operand: 'name'
};
@ -19,11 +20,15 @@ class Version {
get localOpts() {
return {
alias: {
name: 'n'
name: 'n',
noClean: 'c'
},
string: [
'name'
],
boolean: [
'noClean'
],
default: {
remote: 'production'
}
@ -31,7 +36,9 @@ class Version {
}
async run(myvc, opts) {
let versionDir;
let newVersionDir;
const verionsDir =`${opts.myvcDir}/versions`;
const oldVersions = [];
// Fetch last version number
@ -74,14 +81,16 @@ class Version {
// Get version name
let versionName = opts.name;
const verionsDir =`${opts.myvcDir}/versions`;
const versionNames = new Set();
const versionDirs = await fs.readdir(verionsDir);
for (const versionNameDir of versionDirs) {
const split = versionNameDir.split('-');
const versionName = split[1];
if (versionName) versionNames.add(versionName);
for (const versionDir of versionDirs) {
const dirVersion = myvc.parseVersionDir(versionDir);
if (!dirVersion) continue;
versionNames.add(dirVersion.name);
if (parseInt(dirVersion.number) < parseInt(number))
oldVersions.push(versionDir);
}
if (!versionName) {
@ -107,7 +116,7 @@ class Version {
// Create version
const versionFolder = `${newVersion}-${versionName}`;
versionDir = `${verionsDir}/${versionFolder}`;
newVersionDir = `${verionsDir}/${versionFolder}`;
await conn.query(
`INSERT INTO version
@ -117,9 +126,9 @@ class Version {
lastNumber = VALUES(lastNumber)`,
[opts.code, newVersion]
);
await fs.mkdir(versionDir);
await fs.mkdir(newVersionDir);
await fs.writeFile(
`${versionDir}/00-firstScript.sql`,
`${newVersionDir}/00-firstScript.sql`,
'-- Place your SQL code here\n'
);
console.log(`New version created: ${versionFolder}`);
@ -127,10 +136,23 @@ class Version {
await conn.query('COMMIT');
} catch (err) {
await conn.query('ROLLBACK');
if (versionDir && await fs.pathExists(versionDir))
await fs.remove(versionDir, {recursive: true});
if (newVersionDir && await fs.pathExists(newVersionDir))
await fs.remove(newVersionDir, {recursive: true});
throw err;
}
// Remove old versions
if (opts.maxOldVersions && !opts.noClean
&& oldVersions.length > opts.maxOldVersions) {
oldVersions.splice(-opts.maxOldVersions);
for (const oldVersion of oldVersions)
await fs.remove(`${verionsDir}/${oldVersion}`,
{recursive: true});
console.log(`Old versions deleted: ${oldVersions.length}`);
}
}
}

View File

@ -1,5 +1,6 @@
versionSchema: myvc
versionDigits: 5
maxOldVersions: 30
schemas:
- myvc
fixtures:

26
myvc.js
View File

@ -33,7 +33,7 @@ class MyVC {
alias: {
remote: 'r',
workspace: 'w',
socket: 's',
socket: 'k',
debug: 'd',
version: 'v',
help: 'h'
@ -275,6 +275,15 @@ class MyVC {
return version;
}
parseVersionDir(versionDir) {
const match = versionDir.match(/^([0-9]+)-([a-zA-Z0-9]+)?$/);
if (!match) return null;
return {
number: match[1],
name: match[2]
};
}
async changedRoutines(commitSha) {
const repo = await this.openRepo();
const changes = [];
@ -302,7 +311,20 @@ class MyVC {
const head = await repo.getHeadCommit();
if (head && commitSha) {
const commit = await repo.getCommit(commitSha);
let commit;
try {
await repo.fetchAll();
} catch(err) {
console.warn(err.message.yellow);
}
try {
commit = await repo.getCommit(commitSha);
} catch (err) {
if (err.errorFunction == 'Commit.lookup')
throw new Error(`Commit id (${commitSha}) not found, you may have to run 'git fetch' first`);
else
throw err;
}
const commitTree = await commit.getTree();
const headTree = await head.getTree();

View File

@ -1,6 +1,6 @@
{
"name": "myvc",
"version": "1.3.13",
"version": "1.4.0",
"author": "Verdnatura Levante SL",
"description": "MySQL Version Control",
"license": "GPL-3.0",

View File

@ -26,7 +26,6 @@ WORKDIR /myvc
COPY \
package.json \
package-lock.json \
./
RUN npm install --only=prod