2022-12-21 13:17:50 +00:00
|
|
|
const Myt = require('./myt');
|
2022-12-21 12:34:17 +00:00
|
|
|
const Command = require('./lib/command');
|
2021-10-23 13:20:35 +00:00
|
|
|
const fs = require('fs-extra');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new version.
|
|
|
|
*/
|
2022-12-21 12:34:17 +00:00
|
|
|
class Version extends Command {
|
|
|
|
static usage = {
|
|
|
|
description: 'Creates a new version',
|
|
|
|
params: {
|
2024-09-04 12:50:27 +00:00
|
|
|
name: 'Name for the new version',
|
|
|
|
deprecate: 'Whether to generate sql to delete deprecated objects'
|
2022-12-21 12:34:17 +00:00
|
|
|
},
|
|
|
|
operand: 'name'
|
|
|
|
};
|
|
|
|
|
2022-12-29 09:15:02 +00:00
|
|
|
static opts = {
|
2022-12-21 12:34:17 +00:00
|
|
|
alias: {
|
2024-09-04 12:50:27 +00:00
|
|
|
name: 'n',
|
|
|
|
deprecate: 'kk'
|
2022-12-21 12:34:17 +00:00
|
|
|
},
|
|
|
|
string: [
|
|
|
|
'name'
|
|
|
|
],
|
2024-09-04 12:50:27 +00:00
|
|
|
boolean: [
|
|
|
|
'deprecate'
|
|
|
|
],
|
2022-12-21 12:34:17 +00:00
|
|
|
default: {
|
|
|
|
remote: 'production'
|
|
|
|
}
|
|
|
|
};
|
2021-10-23 13:20:35 +00:00
|
|
|
|
2024-01-04 12:02:40 +00:00
|
|
|
static reporter = {
|
|
|
|
dbInfo: function(number, lastNumber) {
|
|
|
|
console.log(
|
|
|
|
`Database information:`
|
|
|
|
+ `\n -> Version: ${number}`
|
|
|
|
+ `\n -> Last version: ${lastNumber}`
|
|
|
|
);
|
|
|
|
},
|
|
|
|
versionCreated: function(versionName) {
|
|
|
|
console.log(`New version created: ${versionName}`);
|
2024-09-04 12:50:27 +00:00
|
|
|
},
|
|
|
|
deprecate: 'Generating sql.'
|
2024-01-04 12:02:40 +00:00
|
|
|
};
|
|
|
|
|
2022-12-21 13:17:50 +00:00
|
|
|
async run(myt, opts) {
|
2022-04-30 00:06:56 +00:00
|
|
|
let newVersionDir;
|
2021-10-23 13:20:35 +00:00
|
|
|
|
|
|
|
// Fetch last version number
|
|
|
|
|
2022-12-21 13:17:50 +00:00
|
|
|
const conn = await myt.dbConnect();
|
2021-10-23 13:20:35 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
await conn.query('START TRANSACTION');
|
|
|
|
|
|
|
|
const [[row]] = await conn.query(
|
2021-10-25 13:38:07 +00:00
|
|
|
`SELECT number, lastNumber
|
|
|
|
FROM version
|
|
|
|
WHERE code = ?
|
|
|
|
FOR UPDATE`,
|
2021-10-23 13:20:35 +00:00
|
|
|
[opts.code]
|
|
|
|
);
|
2021-10-25 13:38:07 +00:00
|
|
|
const number = row && row.number;
|
|
|
|
const lastNumber = row && row.lastNumber;
|
2024-01-04 12:02:40 +00:00
|
|
|
this.emit('dbInfo', number, lastNumber);
|
2021-10-23 13:20:35 +00:00
|
|
|
|
2021-10-25 13:38:07 +00:00
|
|
|
let newVersion;
|
|
|
|
if (lastNumber)
|
|
|
|
newVersion = Math.max(
|
2022-04-04 18:53:18 +00:00
|
|
|
parseInt(number) || 0,
|
|
|
|
parseInt(lastNumber) || 0
|
2021-10-25 13:38:07 +00:00
|
|
|
) + 1;
|
|
|
|
else
|
|
|
|
newVersion = 1;
|
|
|
|
|
|
|
|
const versionDigits = number
|
|
|
|
? number.length
|
|
|
|
: opts.versionDigits;
|
|
|
|
|
|
|
|
newVersion = String(newVersion).padStart(versionDigits, '0');
|
2021-10-23 13:20:35 +00:00
|
|
|
|
|
|
|
// Get version name
|
|
|
|
|
2021-10-25 13:38:07 +00:00
|
|
|
let versionName = opts.name;
|
|
|
|
|
2021-10-23 13:20:35 +00:00
|
|
|
const versionNames = new Set();
|
2022-12-21 12:34:17 +00:00
|
|
|
const versionDirs = await fs.readdir(opts.versionsDir);
|
2022-04-30 00:06:56 +00:00
|
|
|
for (const versionDir of versionDirs) {
|
2024-01-14 14:49:49 +00:00
|
|
|
const version = myt.parseVersionDir(versionDir);
|
|
|
|
if (!version) continue;
|
|
|
|
versionNames.add(version.name);
|
2021-10-23 13:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!versionName) {
|
|
|
|
let attempts;
|
|
|
|
const maxAttempts = 1000;
|
|
|
|
|
|
|
|
for (attempts = 0; attempts < maxAttempts; attempts++) {
|
|
|
|
versionName = randomName();
|
|
|
|
if (!versionNames.has(versionName)) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attempts === maxAttempts)
|
|
|
|
throw new Error(`Cannot create a unique version name after ${attempts} attempts`);
|
|
|
|
} else {
|
|
|
|
const isNameValid = typeof versionName === 'string'
|
|
|
|
&& /^[a-zA-Z0-9]+$/.test(versionName);
|
|
|
|
if (!isNameValid)
|
|
|
|
throw new Error('Version name can only contain letters or numbers');
|
|
|
|
if (versionNames.has(versionName))
|
|
|
|
throw new Error('Version with same name already exists');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create version
|
|
|
|
|
|
|
|
const versionFolder = `${newVersion}-${versionName}`;
|
2022-12-21 12:34:17 +00:00
|
|
|
newVersionDir = `${opts.versionsDir}/${versionFolder}`;
|
2021-10-23 13:20:35 +00:00
|
|
|
|
|
|
|
await conn.query(
|
2021-10-25 13:38:07 +00:00
|
|
|
`INSERT INTO version
|
|
|
|
SET code = ?,
|
|
|
|
lastNumber = ?
|
|
|
|
ON DUPLICATE KEY UPDATE
|
|
|
|
lastNumber = VALUES(lastNumber)`,
|
|
|
|
[opts.code, newVersion]
|
2021-10-23 13:20:35 +00:00
|
|
|
);
|
2022-04-30 00:06:56 +00:00
|
|
|
await fs.mkdir(newVersionDir);
|
2021-10-25 13:38:07 +00:00
|
|
|
await fs.writeFile(
|
2022-04-30 00:06:56 +00:00
|
|
|
`${newVersionDir}/00-firstScript.sql`,
|
2024-09-04 12:50:27 +00:00
|
|
|
opts.deprecate
|
|
|
|
? this.emit('deprecate') && await deprecate()
|
|
|
|
: '-- Place your SQL code here\n'
|
2021-10-25 13:38:07 +00:00
|
|
|
);
|
2024-01-04 12:02:40 +00:00
|
|
|
this.emit('versionCreated', versionFolder);
|
2021-10-23 13:20:35 +00:00
|
|
|
|
|
|
|
await conn.query('COMMIT');
|
|
|
|
} catch (err) {
|
|
|
|
await conn.query('ROLLBACK');
|
2022-04-30 00:06:56 +00:00
|
|
|
if (newVersionDir && await fs.pathExists(newVersionDir))
|
2023-08-11 13:41:03 +00:00
|
|
|
await fs.remove(newVersionDir);
|
2021-10-23 13:20:35 +00:00
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-04 12:50:27 +00:00
|
|
|
async function deprecate() {
|
|
|
|
const [[config]] = await conn.query(`
|
|
|
|
SELECT dateRegex,
|
|
|
|
deprecatedMarkRegex,
|
|
|
|
VN_CURDATE() - INTERVAL daysKeepDeprecatedObjects DAY dated
|
|
|
|
FROM config
|
|
|
|
`);
|
|
|
|
|
|
|
|
const sql = await conn.query(`
|
|
|
|
WITH variables AS (
|
|
|
|
SELECT ? markRegex, ? dateRegex, ? dated
|
|
|
|
)
|
|
|
|
SELECT CONCAT('ALTER TABLE ', c.TABLE_SCHEMA, '.', c.TABLE_NAME, ' DROP PRIMARY KEY;') sql
|
|
|
|
FROM information_schema.COLUMNS c
|
|
|
|
LEFT JOIN information_schema.VIEWS v ON v.TABLE_SCHEMA = c.TABLE_SCHEMA
|
|
|
|
AND v.TABLE_NAME = c.TABLE_NAME
|
|
|
|
JOIN information_schema.STATISTICS s ON s.TABLE_SCHEMA = c.TABLE_SCHEMA
|
|
|
|
AND s.TABLE_NAME = c.TABLE_NAME
|
|
|
|
AND s.COLUMN_NAME = c.COLUMN_NAME
|
|
|
|
JOIN variables var
|
|
|
|
WHERE c.COLUMN_NAME REGEXP var.markRegex COLLATE utf8mb4_unicode_ci
|
|
|
|
AND REGEXP_SUBSTR(c.COLUMN_COMMENT, var.dateRegex COLLATE utf8mb4_unicode_ci) < var.dated
|
|
|
|
AND v.TABLE_NAME IS NULL
|
|
|
|
AND s.INDEX_NAME = 'PRIMARY'
|
|
|
|
UNION
|
|
|
|
SELECT CONCAT('ALTER TABLE ', c.TABLE_SCHEMA, '.', c.TABLE_NAME, ' DROP FOREIGN KEY ', kcu.CONSTRAINT_NAME, ';')
|
|
|
|
FROM information_schema.COLUMNS c
|
|
|
|
LEFT JOIN information_schema.VIEWS v ON v.TABLE_SCHEMA = c.TABLE_SCHEMA
|
|
|
|
AND v.TABLE_NAME = c.TABLE_NAME
|
|
|
|
JOIN information_schema.KEY_COLUMN_USAGE kcu ON kcu.TABLE_SCHEMA = c.TABLE_SCHEMA
|
|
|
|
AND kcu.TABLE_NAME = c.TABLE_NAME
|
|
|
|
AND kcu.COLUMN_NAME = c.COLUMN_NAME
|
|
|
|
JOIN variables var
|
|
|
|
WHERE c.COLUMN_NAME REGEXP var.markRegex COLLATE utf8mb4_unicode_ci
|
|
|
|
AND REGEXP_SUBSTR(c.COLUMN_COMMENT, var.dateRegex COLLATE utf8mb4_unicode_ci) < var.dated
|
|
|
|
AND v.TABLE_NAME IS NULL
|
|
|
|
AND kcu.REFERENCED_COLUMN_NAME IS NOT NULL
|
|
|
|
UNION
|
|
|
|
SELECT CONCAT('ALTER TABLE ', c.TABLE_SCHEMA, '.', c.TABLE_NAME, ' DROP COLUMN ', c.COLUMN_NAME, ';')
|
|
|
|
FROM information_schema.COLUMNS c
|
|
|
|
LEFT JOIN information_schema.VIEWS v ON v.TABLE_SCHEMA = c.TABLE_SCHEMA
|
|
|
|
AND v.TABLE_NAME = c.TABLE_NAME
|
|
|
|
LEFT JOIN information_schema.KEY_COLUMN_USAGE kcu ON kcu.TABLE_SCHEMA = c.TABLE_SCHEMA
|
|
|
|
AND kcu.TABLE_NAME = c.TABLE_NAME
|
|
|
|
AND kcu.COLUMN_NAME = c.COLUMN_NAME
|
|
|
|
JOIN variables var
|
|
|
|
WHERE c.COLUMN_NAME REGEXP var.markRegex COLLATE utf8mb4_unicode_ci
|
|
|
|
AND REGEXP_SUBSTR(c.COLUMN_COMMENT, var.dateRegex COLLATE utf8mb4_unicode_ci) < var.dated
|
|
|
|
AND v.TABLE_NAME IS NULL
|
|
|
|
UNION
|
|
|
|
SELECT CONCAT('DROP TABLE ', t.TABLE_SCHEMA, '.', t.TABLE_NAME, ';')
|
|
|
|
FROM information_schema.TABLES t
|
|
|
|
JOIN variables var
|
|
|
|
WHERE t.TABLE_NAME REGEXP var.markRegex COLLATE utf8mb4_unicode_ci
|
|
|
|
AND REGEXP_SUBSTR(t.TABLE_COMMENT, var.dateRegex COLLATE utf8mb4_unicode_ci) < var.dated
|
|
|
|
`, [config.deprecatedMarkRegex, config.dateRegex, config.dated]);
|
|
|
|
|
|
|
|
return sql.map(row => row.sql).join('\n');
|
|
|
|
}
|
|
|
|
|
2021-10-23 13:20:35 +00:00
|
|
|
function randomName() {
|
|
|
|
const color = random(colors);
|
|
|
|
let plant = random(plants);
|
|
|
|
plant = plant.charAt(0).toUpperCase() + plant.slice(1);
|
|
|
|
return color + plant;
|
|
|
|
}
|
|
|
|
|
|
|
|
function random(array) {
|
|
|
|
return array[Math.floor(Math.random() * array.length)];
|
|
|
|
}
|
|
|
|
|
|
|
|
const colors = [
|
|
|
|
'aqua',
|
|
|
|
'azure',
|
|
|
|
'black',
|
|
|
|
'blue',
|
|
|
|
'bronze',
|
|
|
|
'brown',
|
|
|
|
'chocolate',
|
|
|
|
'crimson',
|
|
|
|
'golden',
|
|
|
|
'gray',
|
|
|
|
'green',
|
|
|
|
'lime',
|
|
|
|
'maroon',
|
|
|
|
'navy',
|
|
|
|
'orange',
|
|
|
|
'pink',
|
|
|
|
'purple',
|
|
|
|
'red',
|
|
|
|
'salmon',
|
|
|
|
'silver',
|
|
|
|
'teal',
|
|
|
|
'turquoise',
|
|
|
|
'yellow',
|
|
|
|
'wheat',
|
|
|
|
'white'
|
|
|
|
];
|
|
|
|
|
|
|
|
const plants = [
|
|
|
|
'anthurium',
|
|
|
|
'aralia',
|
|
|
|
'arborvitae',
|
|
|
|
'asparagus',
|
|
|
|
'aspidistra',
|
|
|
|
'bamboo',
|
|
|
|
'birch',
|
|
|
|
'carnation',
|
|
|
|
'camellia',
|
|
|
|
'cataractarum',
|
|
|
|
'chico',
|
|
|
|
'chrysanthemum',
|
|
|
|
'cordyline',
|
|
|
|
'cyca',
|
|
|
|
'cymbidium',
|
|
|
|
'dendro',
|
|
|
|
'dracena',
|
|
|
|
'erica',
|
|
|
|
'eucalyptus',
|
|
|
|
'fern',
|
|
|
|
'galax',
|
|
|
|
'gerbera',
|
|
|
|
'hydrangea',
|
|
|
|
'ivy',
|
|
|
|
'laurel',
|
|
|
|
'lilium',
|
|
|
|
'mastic',
|
|
|
|
'medeola',
|
|
|
|
'monstera',
|
|
|
|
'moss',
|
|
|
|
'oak',
|
|
|
|
'orchid',
|
|
|
|
'palmetto',
|
|
|
|
'paniculata',
|
|
|
|
'phormium',
|
|
|
|
'raphis',
|
|
|
|
'roebelini',
|
|
|
|
'rose',
|
|
|
|
'ruscus',
|
|
|
|
'salal',
|
|
|
|
'tulip'
|
|
|
|
];
|
|
|
|
|
|
|
|
module.exports = Version;
|
|
|
|
|
|
|
|
if (require.main === module)
|
2024-01-04 12:02:40 +00:00
|
|
|
new Myt().cli(Version);
|