mirror of https://github.com/sualko/cloud_bbb
build: use same code style for scripts
parent
92a06e3ff1
commit
33aad6367a
|
@ -1,12 +1,13 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
require('colors').setTheme({
|
||||
verbose: 'cyan',
|
||||
warn: 'yellow',
|
||||
error: 'red',
|
||||
verbose: 'cyan',
|
||||
warn: 'yellow',
|
||||
error: 'red',
|
||||
});
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const libxml = require("libxmljs");
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const libxml = require('libxmljs');
|
||||
const https = require('https');
|
||||
const archiver = require('archiver');
|
||||
const execa = require('execa');
|
||||
|
@ -17,243 +18,243 @@ const infoXmlPath = './appinfo/info.xml';
|
|||
const isStableRelease = process.argv.indexOf('--stable') > 1;
|
||||
|
||||
async function getVersion() {
|
||||
return package.version + (!isStableRelease ? '-git.' + (await git.raw(['rev-parse', '--short', 'HEAD'])).trim() : '');
|
||||
return package.version + (!isStableRelease ? '-git.' + (await git.raw(['rev-parse', '--short', 'HEAD'])).trim() : '');
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
async function run() {
|
||||
const appId = await prepareInfoXml();
|
||||
await createRelease(appId);
|
||||
const appId = await prepareInfoXml();
|
||||
await createRelease(appId);
|
||||
}
|
||||
|
||||
async function prepareInfoXml() {
|
||||
const infoFile = fs.readFileSync(infoXmlPath);
|
||||
const xmlDoc = libxml.parseXml(infoFile);
|
||||
const infoFile = fs.readFileSync(infoXmlPath);
|
||||
const xmlDoc = libxml.parseXml(infoFile);
|
||||
|
||||
updateVersion(xmlDoc, await getVersion());
|
||||
await validateXml(xmlDoc);
|
||||
updateVersion(xmlDoc, await getVersion());
|
||||
await validateXml(xmlDoc);
|
||||
|
||||
return xmlDoc.get('//id').text();
|
||||
return xmlDoc.get('//id').text();
|
||||
}
|
||||
|
||||
function updateVersion(xmlDoc, version) {
|
||||
let versionChild = xmlDoc.get('//version');
|
||||
let currentVersion = versionChild.text();
|
||||
const versionChild = xmlDoc.get('//version');
|
||||
const currentVersion = versionChild.text();
|
||||
|
||||
if (version !== currentVersion) {
|
||||
console.log(`✔ Update version in info.xml to ${version}.`.green);
|
||||
if (version !== currentVersion) {
|
||||
console.log(`✔ Update version in info.xml to ${version}.`.green);
|
||||
|
||||
versionChild.text(version);
|
||||
versionChild.text(version);
|
||||
|
||||
fs.writeFileSync(infoXmlPath, xmlDoc.toString());
|
||||
}
|
||||
fs.writeFileSync(infoXmlPath, xmlDoc.toString());
|
||||
}
|
||||
}
|
||||
|
||||
async function createRelease(appId) {
|
||||
const version = await getVersion();
|
||||
console.log(`I'm now building ${appId} in version ${version}.`.verbose);
|
||||
const version = await getVersion();
|
||||
console.log(`I'm now building ${appId} in version ${version}.`.verbose);
|
||||
|
||||
await execa('yarn', ['composer:install:dev']);
|
||||
console.log(`✔ composer dev dependencies installed`.green);
|
||||
await execa('yarn', ['composer:install:dev']);
|
||||
console.log('✔ composer dev dependencies installed'.green);
|
||||
|
||||
await execa('yarn', ['lint']);
|
||||
console.log(`✔ linters are happy`.green);
|
||||
await execa('yarn', ['lint']);
|
||||
console.log('✔ linters are happy'.green);
|
||||
|
||||
await execa('yarn', ['composer:install']);
|
||||
console.log(`✔ composer dependencies installed`.green);
|
||||
await execa('yarn', ['composer:install']);
|
||||
console.log('✔ composer dependencies installed'.green);
|
||||
|
||||
await execa('yarn', ['build']);
|
||||
console.log(`✔ scripts built`.green);
|
||||
await execa('yarn', ['build']);
|
||||
console.log('✔ scripts built'.green);
|
||||
|
||||
let filePath = await createArchive(appId, appId + '-v' + version);
|
||||
await createNextcloudSignature(appId, filePath);
|
||||
await createGPGSignature(filePath);
|
||||
await createGPGArmorSignature(filePath);
|
||||
const filePath = await createArchive(appId, appId + '-v' + version);
|
||||
await createNextcloudSignature(appId, filePath);
|
||||
await createGPGSignature(filePath);
|
||||
await createGPGArmorSignature(filePath);
|
||||
}
|
||||
|
||||
|
||||
function createArchive(appId, fileBaseName) {
|
||||
let fileName = `${fileBaseName}.tar.gz`;
|
||||
let filePath = path.normalize(__dirname + `/../archives/${fileName}`);
|
||||
let output = fs.createWriteStream(filePath);
|
||||
let archive = archiver('tar', {
|
||||
gzip: true,
|
||||
});
|
||||
const fileName = `${fileBaseName}.tar.gz`;
|
||||
const filePath = path.normalize(__dirname + `/../archives/${fileName}`);
|
||||
const output = fs.createWriteStream(filePath);
|
||||
const archive = archiver('tar', {
|
||||
gzip: true,
|
||||
});
|
||||
|
||||
archive.on('warning', function (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.warn('Archive warning: '.warn, err);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
archive.on('warning', function (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.warn('Archive warning: '.warn, err);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
archive.on('error', function (err) {
|
||||
throw err;
|
||||
});
|
||||
archive.on('error', function (err) {
|
||||
throw err;
|
||||
});
|
||||
|
||||
archive.pipe(output);
|
||||
archive.pipe(output);
|
||||
|
||||
function addDirectory(name) {
|
||||
archive.directory(name + '/', `${appId}/${name}/`);
|
||||
}
|
||||
function addDirectory(name) {
|
||||
archive.directory(name + '/', `${appId}/${name}/`);
|
||||
}
|
||||
|
||||
function addFile(name) {
|
||||
archive.file(name, {name: `${appId}/${name}`});
|
||||
}
|
||||
function addFile(name) {
|
||||
archive.file(name, { name: `${appId}/${name}` });
|
||||
}
|
||||
|
||||
addDirectory('appinfo');
|
||||
addDirectory('img');
|
||||
addDirectory('js'),
|
||||
addDirectory('lib');
|
||||
addDirectory('templates');
|
||||
addFile('COPYING');
|
||||
addFile('README.md')
|
||||
addDirectory('appinfo');
|
||||
addDirectory('img');
|
||||
addDirectory('js'),
|
||||
addDirectory('lib');
|
||||
addDirectory('templates');
|
||||
addFile('COPYING');
|
||||
addFile('README.md');
|
||||
|
||||
archive.glob('vendor/**/*', {
|
||||
ignore: ['.git']
|
||||
}, {
|
||||
prefix: appId
|
||||
});
|
||||
archive.glob('vendor/**/*', {
|
||||
ignore: ['.git'],
|
||||
}, {
|
||||
prefix: appId,
|
||||
});
|
||||
|
||||
return new Promise(resolve => {
|
||||
output.on('close', function () {
|
||||
console.log(`✔ Wrote ${archive.pointer()} bytes to ${fileName}`.green);
|
||||
return new Promise(resolve => {
|
||||
output.on('close', function () {
|
||||
console.log(`✔ Wrote ${archive.pointer()} bytes to ${fileName}`.green);
|
||||
|
||||
resolve(filePath);
|
||||
});
|
||||
resolve(filePath);
|
||||
});
|
||||
|
||||
archive.finalize();
|
||||
});
|
||||
archive.finalize();
|
||||
});
|
||||
}
|
||||
|
||||
function createNextcloudSignature(appId, filePath) {
|
||||
const {
|
||||
exec
|
||||
} = require('child_process');
|
||||
const {
|
||||
exec,
|
||||
} = require('child_process');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const sigPath = filePath + '.ncsig';
|
||||
exec(`openssl dgst -sha512 -sign ~/.nextcloud/certificates/${appId}.key ${filePath} | openssl base64 > ${sigPath}`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const sigPath = filePath + '.ncsig';
|
||||
exec(`openssl dgst -sha512 -sign ~/.nextcloud/certificates/${appId}.key ${filePath} | openssl base64 > ${sigPath}`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
console.log(`stdout: ${stdout}`);
|
||||
}
|
||||
if (stdout) {
|
||||
console.log(`stdout: ${stdout}`);
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
console.log(`stderr: ${stderr}`);
|
||||
}
|
||||
if (stderr) {
|
||||
console.log(`stderr: ${stderr}`);
|
||||
}
|
||||
|
||||
console.log(`✔ created Nextcloud signature: ${path.basename(sigPath)}`.green);
|
||||
console.log(`✔ created Nextcloud signature: ${path.basename(sigPath)}`.green);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createGPGSignature(filePath) {
|
||||
const {
|
||||
exec
|
||||
} = require('child_process');
|
||||
const {
|
||||
exec,
|
||||
} = require('child_process');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(`gpg --yes --detach-sign "${filePath}"`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(`gpg --yes --detach-sign "${filePath}"`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
console.log(`stdout: ${stdout}`);
|
||||
}
|
||||
if (stdout) {
|
||||
console.log(`stdout: ${stdout}`);
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
console.log(`stderr: ${stderr}`);
|
||||
}
|
||||
if (stderr) {
|
||||
console.log(`stderr: ${stderr}`);
|
||||
}
|
||||
|
||||
console.log(`✔ created detached signature: ${path.basename(filePath)}.sig`.green);
|
||||
console.log(`✔ created detached signature: ${path.basename(filePath)}.sig`.green);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createGPGArmorSignature(filePath) {
|
||||
const {
|
||||
exec
|
||||
} = require('child_process');
|
||||
const {
|
||||
exec,
|
||||
} = require('child_process');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(`gpg --yes --detach-sign --armor "${filePath}"`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(`gpg --yes --detach-sign --armor "${filePath}"`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
console.log(`stdout: ${stdout}`);
|
||||
}
|
||||
if (stdout) {
|
||||
console.log(`stdout: ${stdout}`);
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
console.log(`stderr: ${stderr}`);
|
||||
}
|
||||
if (stderr) {
|
||||
console.log(`stderr: ${stderr}`);
|
||||
}
|
||||
|
||||
console.log(`✔ created detached signature: ${path.basename(filePath)}.asc`.green);
|
||||
console.log(`✔ created detached signature: ${path.basename(filePath)}.asc`.green);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function validateXml(xmlDoc) {
|
||||
const schemaLocation = xmlDoc.root().attr('noNamespaceSchemaLocation').value();
|
||||
const schemaLocation = xmlDoc.root().attr('noNamespaceSchemaLocation').value();
|
||||
|
||||
if (!schemaLocation) {
|
||||
throw "Found no schema location";
|
||||
}
|
||||
if (!schemaLocation) {
|
||||
throw 'Found no schema location';
|
||||
}
|
||||
|
||||
|
||||
let schemaString;
|
||||
try {
|
||||
schemaString = await wget(schemaLocation);
|
||||
} catch (err) {
|
||||
console.log('Could not download schema. Skip validation.'.warn);
|
||||
let schemaString;
|
||||
try {
|
||||
schemaString = await wget(schemaLocation);
|
||||
} catch (err) {
|
||||
console.log('Could not download schema. Skip validation.'.warn);
|
||||
|
||||
return;
|
||||
}
|
||||
let xsdDoc = libxml.parseXml(schemaString);
|
||||
return;
|
||||
}
|
||||
const xsdDoc = libxml.parseXml(schemaString);
|
||||
|
||||
if (xmlDoc.validate(xsdDoc)) {
|
||||
console.log('✔ Document valid'.green);
|
||||
} else {
|
||||
console.log('✘ Document INVALID'.error);
|
||||
if (xmlDoc.validate(xsdDoc)) {
|
||||
console.log('✔ Document valid'.green);
|
||||
} else {
|
||||
console.log('✘ Document INVALID'.error);
|
||||
|
||||
xmlDoc.validationErrors.forEach((error, index) => {
|
||||
console.log(`#${index + 1}\t${error.toString().trim()}`.warn);
|
||||
console.log(`\tLine ${error.line}:${error.column} (level ${error.level})`.verbose);
|
||||
});
|
||||
xmlDoc.validationErrors.forEach((error, index) => {
|
||||
console.log(`#${index + 1}\t${error.toString().trim()}`.warn);
|
||||
console.log(`\tLine ${error.line}:${error.column} (level ${error.level})`.verbose);
|
||||
});
|
||||
|
||||
throw 'Abort';
|
||||
}
|
||||
throw 'Abort';
|
||||
}
|
||||
}
|
||||
|
||||
function wget(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get(url, (resp) => {
|
||||
let data = '';
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get(url, (resp) => {
|
||||
let data = '';
|
||||
|
||||
resp.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
resp.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
resp.on('end', () => {
|
||||
resolve(data);
|
||||
});
|
||||
resp.on('end', () => {
|
||||
resolve(data);
|
||||
});
|
||||
|
||||
}).on("error", (err) => {
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
}).on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
require('colors').setTheme({
|
||||
verbose: 'cyan',
|
||||
warn: 'yellow',
|
||||
error: 'red',
|
||||
verbose: 'cyan',
|
||||
warn: 'yellow',
|
||||
error: 'red',
|
||||
});
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const { Octokit } = require("@octokit/rest");
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
const execa = require('execa');
|
||||
const inquirer = require('inquirer');
|
||||
const git = require('simple-git/promise')();
|
||||
|
@ -19,405 +20,407 @@ const isDryRun = process.argv.indexOf('--dry-run') > 1;
|
|||
const commitMessage = `release: ${package.version} :tada:`;
|
||||
const tagName = `v${package.version}`;
|
||||
const files = [
|
||||
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz`),
|
||||
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz.asc`),
|
||||
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz.ncsig`),
|
||||
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz.sig`),
|
||||
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz`),
|
||||
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz.asc`),
|
||||
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz.ncsig`),
|
||||
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz.sig`),
|
||||
];
|
||||
|
||||
function pull() {
|
||||
return git.pull('origin', 'master');
|
||||
return git.pull('origin', 'master');
|
||||
}
|
||||
|
||||
async function notAlreadyTagged() {
|
||||
if ((await git.tags()).all.includes(tagName)) {
|
||||
throw 'version already tagged';
|
||||
}
|
||||
if ((await git.tags()).all.includes(tagName)) {
|
||||
throw 'version already tagged';
|
||||
}
|
||||
}
|
||||
|
||||
async function lastCommitNotBuild() {
|
||||
return (await git.log(['-1'])).latest.message !== commitMessage;
|
||||
return (await git.log(['-1'])).latest.message !== commitMessage;
|
||||
}
|
||||
|
||||
async function isMasterBranch() {
|
||||
return (await git.branch()) === 'master';
|
||||
return (await git.branch()) === 'master';
|
||||
}
|
||||
|
||||
async function generateChangelog() {
|
||||
const latestTag = (await git.tags()).latest;
|
||||
const title = `v${package.version}` === latestTag ? '[Unreleased]' : `${package.version} (${new Date().toISOString().split('T')[0]})`;
|
||||
const latestTag = (await git.tags()).latest;
|
||||
const title = `v${package.version}` === latestTag ? '[Unreleased]' : `${package.version} (${new Date().toISOString().split('T')[0]})`;
|
||||
|
||||
const logs = await git.log({
|
||||
from: latestTag,
|
||||
to: 'HEAD'
|
||||
});
|
||||
const logs = await git.log({
|
||||
from: latestTag,
|
||||
to: 'HEAD',
|
||||
});
|
||||
|
||||
const sections = [{
|
||||
type: 'feat',
|
||||
label: 'Added',
|
||||
}, {
|
||||
type: 'fix',
|
||||
label: 'Fixed',
|
||||
}];
|
||||
const sections = [{
|
||||
type: 'feat',
|
||||
label: 'Added',
|
||||
}, {
|
||||
type: 'fix',
|
||||
label: 'Fixed',
|
||||
}];
|
||||
|
||||
const entries = {};
|
||||
const entries = {};
|
||||
|
||||
logs.all.forEach(log => {
|
||||
let [, type, scope, description] = log.message.match(/^([a-z]+)(?:\((\w+)\))?: (.+)/);
|
||||
let entry = { type, scope, description, issues: [] };
|
||||
logs.all.forEach(log => {
|
||||
const [, type, scope, description] = log.message.match(/^([a-z]+)(?:\((\w+)\))?: (.+)/);
|
||||
const entry = { type, scope, description, issues: [] };
|
||||
|
||||
if(log.body) {
|
||||
const matches = log.body.match(/(?:fix|fixes|closes?|refs?) #(\d+)/g) || [];
|
||||
if(log.body) {
|
||||
const matches = log.body.match(/(?:fix|fixes|closes?|refs?) #(\d+)/g) || [];
|
||||
|
||||
for (let match of matches) {
|
||||
const [, number] = match.match(/(\d+)$/);
|
||||
for (const match of matches) {
|
||||
const [, number] = match.match(/(\d+)$/);
|
||||
|
||||
entry.issues.push(number);
|
||||
}
|
||||
}
|
||||
entry.issues.push(number);
|
||||
}
|
||||
}
|
||||
|
||||
if (!entries[type]) {
|
||||
entries[type] = [];
|
||||
}
|
||||
if (!entries[type]) {
|
||||
entries[type] = [];
|
||||
}
|
||||
|
||||
entries[type].push(entry);
|
||||
});
|
||||
entries[type].push(entry);
|
||||
});
|
||||
|
||||
let changeLog = `## ${title}\n`;
|
||||
let changeLog = `## ${title}\n`;
|
||||
|
||||
function stringifyEntry(entry) {
|
||||
let issues = entry.issues.map(issue => {
|
||||
return `[#${issue}](https://github.com/sualko/cloud_bbb/issues/${issue})`;
|
||||
}).join('');
|
||||
function stringifyEntry(entry) {
|
||||
const issues = entry.issues.map(issue => {
|
||||
return `[#${issue}](https://github.com/sualko/cloud_bbb/issues/${issue})`;
|
||||
}).join('');
|
||||
|
||||
return `- ${issues}${issues.length > 0 ? ' ' : ''}${entry.description}\n`;
|
||||
}
|
||||
return `- ${issues}${issues.length > 0 ? ' ' : ''}${entry.description}\n`;
|
||||
}
|
||||
|
||||
sections.forEach(section => {
|
||||
if (!entries[section.type]) {
|
||||
return;
|
||||
}
|
||||
sections.forEach(section => {
|
||||
if (!entries[section.type]) {
|
||||
return;
|
||||
}
|
||||
|
||||
changeLog += `### ${section.label}\n`;
|
||||
changeLog += `### ${section.label}\n`;
|
||||
|
||||
entries[section.type].forEach(entry => {
|
||||
changeLog += stringifyEntry(entry);
|
||||
});
|
||||
entries[section.type].forEach(entry => {
|
||||
changeLog += stringifyEntry(entry);
|
||||
});
|
||||
|
||||
delete entries[section.type];
|
||||
delete entries[section.type];
|
||||
|
||||
changeLog += `\n`
|
||||
});
|
||||
changeLog += '\n';
|
||||
});
|
||||
|
||||
const miscKeys = Object.keys(entries);
|
||||
const miscKeys = Object.keys(entries);
|
||||
|
||||
if (miscKeys && miscKeys.length > 0) {
|
||||
changeLog += `### Misc\n`;
|
||||
if (miscKeys && miscKeys.length > 0) {
|
||||
changeLog += '### Misc\n';
|
||||
|
||||
miscKeys.forEach(type => {
|
||||
entries[type].forEach(entry => {
|
||||
changeLog += stringifyEntry(entry);
|
||||
});
|
||||
})
|
||||
}
|
||||
miscKeys.forEach(type => {
|
||||
entries[type].forEach(entry => {
|
||||
changeLog += stringifyEntry(entry);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return changeLog;
|
||||
return changeLog;
|
||||
}
|
||||
|
||||
async function editChangeLog(changeLog) {
|
||||
let answers = await inquirer.prompt([{
|
||||
type: 'editor',
|
||||
name: 'changeLog',
|
||||
message: 'You have now the possibility to edit the change log',
|
||||
default: changeLog,
|
||||
}]);
|
||||
const answers = await inquirer.prompt([{
|
||||
type: 'editor',
|
||||
name: 'changeLog',
|
||||
message: 'You have now the possibility to edit the change log',
|
||||
default: changeLog,
|
||||
}]);
|
||||
|
||||
return answers.changeLog;
|
||||
return answers.changeLog;
|
||||
}
|
||||
|
||||
function hasChangeLogEntry() {
|
||||
return new Promise(resolve => {
|
||||
fs.readFile(path.join(__dirname, '..', 'CHANGELOG.md'), function (err, data) {
|
||||
if (err) throw err;
|
||||
return new Promise(resolve => {
|
||||
fs.readFile(path.join(__dirname, '..', 'CHANGELOG.md'), function (err, data) {
|
||||
if (err) throw err;
|
||||
|
||||
if (!data.includes(`## ${package.version}`)) {
|
||||
throw `Found no change log entry for ${package.version}`;
|
||||
}
|
||||
if (!data.includes(`## ${package.version}`)) {
|
||||
throw `Found no change log entry for ${package.version}`;
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function commitChangeLog() {
|
||||
let status = await git.status();
|
||||
const status = await git.status();
|
||||
|
||||
if (status.staged.length > 0) {
|
||||
throw 'Repo not clean. Found staged files.';
|
||||
}
|
||||
if (status.staged.length > 0) {
|
||||
throw 'Repo not clean. Found staged files.';
|
||||
}
|
||||
|
||||
if (!isDryRun) {
|
||||
await git.add('CHANGELOG.md');
|
||||
await git.commit('docs: update change log', ['-n']);
|
||||
}
|
||||
if (!isDryRun) {
|
||||
await git.add('CHANGELOG.md');
|
||||
await git.commit('docs: update change log', ['-n']);
|
||||
}
|
||||
}
|
||||
|
||||
async function hasArchiveAndSignatures() {
|
||||
return files.map(file => fs.existsSync(file)).indexOf(false) < 0;
|
||||
return files.map(file => fs.existsSync(file)).indexOf(false) < 0;
|
||||
}
|
||||
|
||||
async function stageAllFiles() {
|
||||
if (isDryRun) {
|
||||
return;
|
||||
}
|
||||
if (isDryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
let gitProcess = execa('git', ['add', '-u']);
|
||||
const gitProcess = execa('git', ['add', '-u']);
|
||||
|
||||
gitProcess.stdout.pipe(process.stdout);
|
||||
gitProcess.stdout.pipe(process.stdout);
|
||||
|
||||
return gitProcess;
|
||||
return gitProcess;
|
||||
}
|
||||
|
||||
function showStagedDiff() {
|
||||
let gitProcess = execa('git', ['diff', '--staged']);
|
||||
const gitProcess = execa('git', ['diff', '--staged']);
|
||||
|
||||
gitProcess.stdout.pipe(process.stdout);
|
||||
gitProcess.stdout.pipe(process.stdout);
|
||||
|
||||
return gitProcess;
|
||||
return gitProcess;
|
||||
}
|
||||
|
||||
async function keypress() {
|
||||
return inquirer.prompt([{
|
||||
type: 'input',
|
||||
name: 'keypress',
|
||||
message: 'Press any key to continue... (where is the any key?)',
|
||||
}]);
|
||||
return inquirer.prompt([{
|
||||
type: 'input',
|
||||
name: 'keypress',
|
||||
message: 'Press any key to continue... (where is the any key?)',
|
||||
}]);
|
||||
}
|
||||
|
||||
function commit() {
|
||||
if (isDryRun) {
|
||||
return;
|
||||
}
|
||||
if (isDryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
return git.commit(commitMessage, ['-S', '-n']);
|
||||
return git.commit(commitMessage, ['-S', '-n']);
|
||||
}
|
||||
|
||||
async function wantToContinue(message) {
|
||||
let answers = await inquirer.prompt([{
|
||||
type: 'confirm',
|
||||
name: 'continue',
|
||||
message,
|
||||
default: false,
|
||||
}]);
|
||||
const answers = await inquirer.prompt([{
|
||||
type: 'confirm',
|
||||
name: 'continue',
|
||||
message,
|
||||
default: false,
|
||||
}]);
|
||||
|
||||
if (!answers.continue) {
|
||||
process.exit(10);
|
||||
}
|
||||
if (!answers.continue) {
|
||||
process.exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
function push() {
|
||||
if (isDryRun) {
|
||||
return;
|
||||
}
|
||||
if (isDryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
return git.push('origin', 'master');
|
||||
return git.push('origin', 'master');
|
||||
}
|
||||
|
||||
async function createGithubRelease(changeLog) {
|
||||
if (!process.env.GITHUB_TOKEN) {
|
||||
throw 'Github token missing'
|
||||
}
|
||||
if (!process.env.GITHUB_TOKEN) {
|
||||
throw 'Github token missing';
|
||||
}
|
||||
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.GITHUB_TOKEN,
|
||||
userAgent: 'custom releaser for sualko/cloud_bbb',
|
||||
});
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.GITHUB_TOKEN,
|
||||
userAgent: 'custom releaser for sualko/cloud_bbb',
|
||||
});
|
||||
|
||||
let origin = (await git.remote(['get-url', 'origin'])).trim();
|
||||
let matches = origin.match(/^git@github\.com:(.+)\/(.+)\.git$/);
|
||||
const origin = (await git.remote(['get-url', 'origin'])).trim();
|
||||
const matches = origin.match(/^git@github\.com:(.+)\/(.+)\.git$/);
|
||||
|
||||
if (!matches) {
|
||||
throw 'Origin is not configured or no ssh url';
|
||||
}
|
||||
if (!matches) {
|
||||
throw 'Origin is not configured or no ssh url';
|
||||
}
|
||||
|
||||
const owner = matches[1];
|
||||
const repo = matches[2];
|
||||
const releaseOptions = {
|
||||
owner,
|
||||
repo,
|
||||
tag_name: tagName,
|
||||
name: `${package.name} ${tagName}`,
|
||||
body: changeLog.replace(/^## [^\n]+\n/, ''),
|
||||
prerelease: !/^\d+\.\d+\.\d+$/.test(package.version),
|
||||
};
|
||||
const owner = matches[1];
|
||||
const repo = matches[2];
|
||||
const releaseOptions = {
|
||||
owner,
|
||||
repo,
|
||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||
tag_name: tagName,
|
||||
name: `${package.name} ${tagName}`,
|
||||
body: changeLog.replace(/^## [^\n]+\n/, ''),
|
||||
prerelease: !/^\d+\.\d+\.\d+$/.test(package.version),
|
||||
};
|
||||
|
||||
if (isDryRun) {
|
||||
console.log('github release options', releaseOptions);
|
||||
return [];
|
||||
}
|
||||
if (isDryRun) {
|
||||
console.log('github release options', releaseOptions);
|
||||
return [];
|
||||
}
|
||||
|
||||
let releaseResponse = await octokit.repos.createRelease(releaseOptions);
|
||||
const releaseResponse = await octokit.repos.createRelease(releaseOptions);
|
||||
|
||||
console.log(`Draft created, see ${releaseResponse.data.html_url}`.verbose);
|
||||
console.log(`Draft created, see ${releaseResponse.data.html_url}`.verbose);
|
||||
|
||||
function getMimeType(filename) {
|
||||
if (filename.endsWith('.asc') || filename.endsWith('sig')) {
|
||||
return 'application/pgp-signature';
|
||||
}
|
||||
function getMimeType(filename) {
|
||||
if (filename.endsWith('.asc') || filename.endsWith('sig')) {
|
||||
return 'application/pgp-signature';
|
||||
}
|
||||
|
||||
if (filename.endsWith('.tar.gz')) {
|
||||
return 'application/gzip';
|
||||
}
|
||||
if (filename.endsWith('.tar.gz')) {
|
||||
return 'application/gzip';
|
||||
}
|
||||
|
||||
if (filename.endsWith('.ncsig')) {
|
||||
return 'text/plain';
|
||||
}
|
||||
if (filename.endsWith('.ncsig')) {
|
||||
return 'text/plain';
|
||||
}
|
||||
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
|
||||
let assetUrls = await Promise.all(files.map(async file => {
|
||||
const filename = path.basename(file);
|
||||
const uploadOptions = {
|
||||
owner,
|
||||
repo,
|
||||
release_id: releaseResponse.data.id,
|
||||
data: fs.createReadStream(file),
|
||||
headers: {
|
||||
'content-type': getMimeType(filename),
|
||||
'content-length': fs.statSync(file)['size'],
|
||||
},
|
||||
name: filename,
|
||||
};
|
||||
const assetUrls = await Promise.all(files.map(async file => {
|
||||
const filename = path.basename(file);
|
||||
const uploadOptions = {
|
||||
owner,
|
||||
repo,
|
||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||
release_id: releaseResponse.data.id,
|
||||
data: fs.createReadStream(file),
|
||||
headers: {
|
||||
'content-type': getMimeType(filename),
|
||||
'content-length': fs.statSync(file)['size'],
|
||||
},
|
||||
name: filename,
|
||||
};
|
||||
|
||||
let assetResponse = await octokit.repos.uploadReleaseAsset(uploadOptions);
|
||||
const assetResponse = await octokit.repos.uploadReleaseAsset(uploadOptions);
|
||||
|
||||
console.log(`Asset uploaded: ${assetResponse.data.name}`.verbose);
|
||||
console.log(`Asset uploaded: ${assetResponse.data.name}`.verbose);
|
||||
|
||||
return assetResponse.data.browser_download_url;
|
||||
}));
|
||||
return assetResponse.data.browser_download_url;
|
||||
}));
|
||||
|
||||
return assetUrls;
|
||||
return assetUrls;
|
||||
}
|
||||
|
||||
async function uploadToNextcloudStore(archiveUrl) {
|
||||
if(!process.env.NEXTCLOUD_TOKEN) {
|
||||
throw 'Nextcloud token missing';
|
||||
}
|
||||
if(!process.env.NEXTCLOUD_TOKEN) {
|
||||
throw 'Nextcloud token missing';
|
||||
}
|
||||
|
||||
const hostname = 'apps.nextcloud.com';
|
||||
const apiEndpoint = '/api/v1/apps/releases';
|
||||
const signatureFile = files.find(file => file.endsWith('.ncsig'));
|
||||
const data = JSON.stringify({
|
||||
download: archiveUrl,
|
||||
signature: fs.readFileSync(signatureFile, 'utf-8'),
|
||||
nightly: false,
|
||||
});
|
||||
const options = {
|
||||
hostname,
|
||||
port: 443,
|
||||
path: apiEndpoint,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': data.length,
|
||||
'Authorization': `Token ${process.env.NEXTCLOUD_TOKEN}`,
|
||||
}
|
||||
};
|
||||
const hostname = 'apps.nextcloud.com';
|
||||
const apiEndpoint = '/api/v1/apps/releases';
|
||||
const signatureFile = files.find(file => file.endsWith('.ncsig'));
|
||||
const data = JSON.stringify({
|
||||
download: archiveUrl,
|
||||
signature: fs.readFileSync(signatureFile, 'utf-8'),
|
||||
nightly: false,
|
||||
});
|
||||
const options = {
|
||||
hostname,
|
||||
port: 443,
|
||||
path: apiEndpoint,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': data.length,
|
||||
Authorization: `Token ${process.env.NEXTCLOUD_TOKEN}`,
|
||||
},
|
||||
};
|
||||
|
||||
if (isDryRun) {
|
||||
console.log('nextcloud app store request', options, data);
|
||||
return;
|
||||
}
|
||||
if (isDryRun) {
|
||||
console.log('nextcloud app store request', options, data);
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = https.request(options, res => {
|
||||
if (res.statusCode === 200) {
|
||||
console.log('App release was updated successfully'.verbose);
|
||||
resolve();
|
||||
} else if (res.statusCode === 201) {
|
||||
console.log('App release was created successfully'.verbose);
|
||||
resolve();
|
||||
} else if (res.statusCode === 400) {
|
||||
reject('App release was not accepted');
|
||||
} else {
|
||||
reject('App release rejected with status ' + res.statusCode);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = https.request(options, res => {
|
||||
if (res.statusCode === 200) {
|
||||
console.log('App release was updated successfully'.verbose);
|
||||
resolve();
|
||||
} else if (res.statusCode === 201) {
|
||||
console.log('App release was created successfully'.verbose);
|
||||
resolve();
|
||||
} else if (res.statusCode === 400) {
|
||||
reject('App release was not accepted');
|
||||
} else {
|
||||
reject('App release rejected with status ' + res.statusCode);
|
||||
}
|
||||
|
||||
res.on('data', d => {
|
||||
process.stdout.write(d)
|
||||
})
|
||||
})
|
||||
res.on('data', d => {
|
||||
process.stdout.write(d);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', error => {
|
||||
reject(error);
|
||||
});
|
||||
req.on('error', error => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
req.write(data);
|
||||
req.end();
|
||||
});
|
||||
req.write(data);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function run() {
|
||||
await pull();
|
||||
console.log(`✔ pulled latest changes`.green);
|
||||
await pull();
|
||||
console.log('✔ pulled latest changes'.green);
|
||||
|
||||
await notAlreadyTagged();
|
||||
console.log(`✔ not already tagged`.green);
|
||||
await notAlreadyTagged();
|
||||
console.log('✔ not already tagged'.green);
|
||||
|
||||
await lastCommitNotBuild();
|
||||
console.log(`✔ last commit is no build commit`.green);
|
||||
await lastCommitNotBuild();
|
||||
console.log('✔ last commit is no build commit'.green);
|
||||
|
||||
await isMasterBranch();
|
||||
console.log(`✔ this is the master branch`.green);
|
||||
await isMasterBranch();
|
||||
console.log('✔ this is the master branch'.green);
|
||||
|
||||
let changeLog = await generateChangelog();
|
||||
console.log(`✔ change log generated`.green);
|
||||
let changeLog = await generateChangelog();
|
||||
console.log('✔ change log generated'.green);
|
||||
|
||||
changeLog = await editChangeLog(changeLog);
|
||||
console.log(`✔ change log updated`.green);
|
||||
changeLog = await editChangeLog(changeLog);
|
||||
console.log('✔ change log updated'.green);
|
||||
|
||||
console.log('Press any key to continue...');
|
||||
await keypress();
|
||||
console.log('Press any key to continue...');
|
||||
await keypress();
|
||||
|
||||
await hasChangeLogEntry();
|
||||
console.log(`✔ there is a change log entry for this version`.green);
|
||||
await hasChangeLogEntry();
|
||||
console.log('✔ there is a change log entry for this version'.green);
|
||||
|
||||
await commitChangeLog();
|
||||
console.log(`✔ change log commited`.green);
|
||||
await commitChangeLog();
|
||||
console.log('✔ change log commited'.green);
|
||||
|
||||
await hasArchiveAndSignatures();
|
||||
console.log(`✔ found archive and signatures`.green);
|
||||
await hasArchiveAndSignatures();
|
||||
console.log('✔ found archive and signatures'.green);
|
||||
|
||||
await stageAllFiles();
|
||||
console.log(`✔ all files staged`.green);
|
||||
await stageAllFiles();
|
||||
console.log('✔ all files staged'.green);
|
||||
|
||||
await showStagedDiff();
|
||||
await showStagedDiff();
|
||||
|
||||
await wantToContinue('Should I commit those changes?');
|
||||
await wantToContinue('Should I commit those changes?');
|
||||
|
||||
await commit();
|
||||
console.log(`✔ All files commited`.green);
|
||||
await commit();
|
||||
console.log('✔ All files commited'.green);
|
||||
|
||||
await wantToContinue('Should I push all pending commits?');
|
||||
await wantToContinue('Should I push all pending commits?');
|
||||
|
||||
await push();
|
||||
console.log(`✔ All commits pushed`.green);
|
||||
await push();
|
||||
console.log('✔ All commits pushed'.green);
|
||||
|
||||
await wantToContinue('Should I continue to create a Github release?');
|
||||
await wantToContinue('Should I continue to create a Github release?');
|
||||
|
||||
const assetUrls = await createGithubRelease(changeLog);
|
||||
console.log(`✔ released on github`.green);
|
||||
const assetUrls = await createGithubRelease(changeLog);
|
||||
console.log('✔ released on github'.green);
|
||||
|
||||
const archiveAssetUrl = assetUrls.find(url => url.endsWith('.tar.gz'));
|
||||
console.log(`Asset url for Nextcloud store: ${archiveAssetUrl}`.verbose);
|
||||
const archiveAssetUrl = assetUrls.find(url => url.endsWith('.tar.gz'));
|
||||
console.log(`Asset url for Nextcloud store: ${archiveAssetUrl}`.verbose);
|
||||
|
||||
await wantToContinue('Should I continue to upload the release to the app store?');
|
||||
await wantToContinue('Should I continue to upload the release to the app store?');
|
||||
|
||||
await uploadToNextcloudStore(archiveAssetUrl);
|
||||
console.log(`✔ released in Nextcloud app store`.green);
|
||||
await uploadToNextcloudStore(archiveAssetUrl);
|
||||
console.log('✔ released in Nextcloud app store'.green);
|
||||
};
|
||||
|
||||
run().catch(err => {
|
||||
console.log(`✘ ${err.toString()}`.error);
|
||||
console.log(`✘ ${err.toString()}`.error);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue