mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
26c7856d5d
# v2.4.1 - [FIX] app: make subroots more robust - [IMP] devtools: show objects class name and add special extension cases - [IMP] devtools: allow expansion of empty class - [IMP] Bump support for Node 20+
250 lines
8.1 KiB
JavaScript
250 lines
8.1 KiB
JavaScript
const package = require("../package.json");
|
|
const packageLock = require("../package-lock.json");
|
|
const readline = require("readline");
|
|
const fs = require("fs");
|
|
const exec = require("child_process").exec;
|
|
const chalk = require("chalk");
|
|
const branchName = require('current-git-branch');
|
|
|
|
const REL_NOTES_FILE = `release-notes.md`;
|
|
const branch = "master";
|
|
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout
|
|
});
|
|
|
|
startRelease().then(() => {
|
|
rl.close();
|
|
});
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Relase Script
|
|
// -----------------------------------------------------------------------------
|
|
|
|
async function startRelease() {
|
|
// First check we are on master
|
|
if (branchName() !== branch) {
|
|
logError(`You shall not pass! You are not on the ${branch} branch!`)
|
|
return;
|
|
}
|
|
|
|
log(`*** Owl release script ***`);
|
|
log(`Current Version: ${package.version}`);
|
|
log(`Warning: this script will push to the master branch!`);
|
|
log(`Make sure that github is configured to allow it:`);
|
|
log(` settings => branches => edit master => uncheck Do not allow bypassing the above settings`);
|
|
log(` (and probably a good idea to readd the protection after)`)
|
|
|
|
const STEPS = 11;
|
|
let step = 1;
|
|
// ---------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: Checking if code formatting is right...`)
|
|
const checkFormatting = await execCommand("npm run check-formatting");
|
|
if (checkFormatting !== 0) {
|
|
logError("Prettier format validation failed. Aborting.");
|
|
return;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: collecting info...`);
|
|
let next = await ask("Next version: ");
|
|
if (next[0] === 'v') next = next.substring(1);
|
|
let file = await ask(`Release notes (${REL_NOTES_FILE}): `);
|
|
file = file || REL_NOTES_FILE;
|
|
if (!fs.existsSync(`./${file}`)) {
|
|
const lastRelease = await getOutput("git log --grep='\\[REL\\]' -n 1 --pretty=%H");
|
|
const commitsSinceLastRelease = await getOutput(`git log ${lastRelease.trim()}..HEAD --pretty=%s`);
|
|
const commitsAsMdList = commitsSinceLastRelease.trim().split("\n").map(l => " - " + l).join("\n");
|
|
log(`${file} did not exist, created a template containing all commits since last release.`)
|
|
fs.writeFileSync(file, `# v${next}\n\n${commitsAsMdList}`);
|
|
const shouldContinue = await ask(`Check that the contents of ${file} is correct, then press y to continue: `);
|
|
if (shouldContinue.toLowerCase() !== "y") {
|
|
log("aborted");
|
|
return;
|
|
}
|
|
}
|
|
let content;
|
|
try {
|
|
content = await readFile("./" + file);
|
|
} catch (e) {
|
|
logSubContent(e.message);
|
|
logError("Cannot find release notes... Aborting");
|
|
return;
|
|
}
|
|
let shouldBeDraft = await ask(`Should be a draft [y/n] ? (n)`);
|
|
let draft = ""
|
|
if (shouldBeDraft.toLowerCase() === 'y') {
|
|
draft = "--draft";
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: running tests...`);
|
|
const testsResult = await execCommand("npm run test");
|
|
if (testsResult !== 0) {
|
|
logError("Test suite does not pass. Aborting.");
|
|
return;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: updating package.json...`);
|
|
await writeFile("package.json", JSON.stringify({...package, version: next}, null, 2) + "\n");
|
|
await writeFile("package-lock.json", JSON.stringify({...packageLock, version: next}, null, 2) + "\n");
|
|
await writeFile("./src/version.ts", `// do not modify manually. This file is generated by the release script.\nexport const version = "${next}";\n`);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: building owl...`);
|
|
await execCommand("rm -rf dist/");
|
|
const buildResult = await execCommand("npm run build");
|
|
if (buildResult !== 0) {
|
|
logError("Build failed. Aborting.");
|
|
return;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: building devtools...`);
|
|
const chromeResult = await execCommand("npm run build:devtools-chrome");
|
|
if (chromeResult !== 0) {
|
|
logError("Build devtools chrome failed. Aborting.");
|
|
return;
|
|
}
|
|
await execCommand("mv dist/devtools dist/devtools-chrome");
|
|
const firefoxResult = await execCommand("npm run build:devtools-firefox");
|
|
if (firefoxResult !== 0) {
|
|
logError("Build devtools firefox failed. Aborting.");
|
|
return;
|
|
}
|
|
await execCommand("mv dist/devtools dist/devtools-firefox");
|
|
await execCommand("cd dist && zip -r owl-devtools.zip devtools-chrome devtools-firefox && cd ..");
|
|
await execCommand("rm -r dist/devtools-chrome dist/devtools-firefox && rm dist/compiler.js");
|
|
|
|
// ---------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: updating owl on github page...`);
|
|
await fs.copyFileSync("dist/owl.es.js", "docs/owl.js");
|
|
|
|
// ---------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: creating git commit...`);
|
|
const escapedContent = content.replace(/\"/g, '\\\"').replace(/\`/g, '\\\`');
|
|
const gitResult = await execCommand(`git commit -am "[REL] v${next}\n\n${escapedContent}"`);
|
|
if (gitResult !== 0) {
|
|
logError("Git commit failed. Aborting.");
|
|
return;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: pushing on github...`);
|
|
const pushResult = await execCommand("git push origin " + branch);
|
|
if (pushResult !== 0) {
|
|
logError("git push failed. Aborting.");
|
|
return;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
log(`Step ${step++}/${STEPS}: Creating the release...`);
|
|
const relaseResult = await execCommand(`gh release create v${next} dist/*.js dist/*.zip ${draft} -F ${file}`);
|
|
if (relaseResult !== 0) {
|
|
logError("github release failed. Aborting.");
|
|
return;
|
|
}
|
|
|
|
log(`Step ${step++}/${STEPS}: publishing module on npm...`);
|
|
await execCommand("npm publish");
|
|
|
|
log("Owl Release process completed! Thank you for your patience");
|
|
await execCommand(`gh release view`);
|
|
await execCommand(`gh release view -w`);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Helpers
|
|
// -----------------------------------------------------------------------------
|
|
|
|
function log(text) {
|
|
console.log(chalk.yellow(formatLog(text)));
|
|
}
|
|
|
|
function logError(text) {
|
|
console.log(chalk.red(formatLog(text)));
|
|
}
|
|
|
|
function formatLog(text) {
|
|
return `[REL] ${text}`;
|
|
}
|
|
|
|
function logSubContent(text) {
|
|
for (let line of text.split("\n")) {
|
|
if (line.trim()) {
|
|
console.log(" " + line);
|
|
}
|
|
}
|
|
}
|
|
|
|
function ask(question) {
|
|
return new Promise(resolve => {
|
|
rl.question(question, result => {
|
|
resolve(result);
|
|
});
|
|
});
|
|
}
|
|
|
|
function logStream(stream) {
|
|
stream.on("data", data => {
|
|
logSubContent(data);
|
|
});
|
|
}
|
|
|
|
function execCommand(command) {
|
|
return new Promise(resolve => {
|
|
const childProcess = exec(command, (err, stdout, stderr) => {
|
|
if (err) {
|
|
resolve(err.code);
|
|
}
|
|
});
|
|
childProcess.on("exit", code => {
|
|
resolve(code);
|
|
});
|
|
logStream(childProcess.stdout);
|
|
logStream(childProcess.stderr);
|
|
});
|
|
}
|
|
|
|
function readFile(file) {
|
|
return new Promise((resolve, reject) => {
|
|
fs.readFile(file, "utf8", function(err, content) {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
resolve(content);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function writeFile(file, content) {
|
|
return new Promise((resolve, reject) => {
|
|
fs.writeFile(file, content, "utf8", err => {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
async function getOutput(command) {
|
|
return new Promise((resolve, reject) => {
|
|
const childProcess = exec(command, (err, stdout, stderr) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
resolve(stdout);
|
|
});
|
|
childProcess.on("exit", code => {
|
|
if (code !== 0) {
|
|
reject(code);
|
|
}
|
|
});
|
|
});
|
|
}
|