build: use same code style for scripts

pull/29/head
sualko 2020-04-29 14:16:57 +02:00
parent 92a06e3ff1
commit 33aad6367a
2 changed files with 445 additions and 441 deletions

View File

@ -1,12 +1,13 @@
/* eslint-disable @typescript-eslint/no-var-requires */
require('colors').setTheme({ require('colors').setTheme({
verbose: 'cyan', verbose: 'cyan',
warn: 'yellow', warn: 'yellow',
error: 'red', error: 'red',
}); });
const fs = require("fs"); const fs = require('fs');
const path = require("path"); const path = require('path');
const libxml = require("libxmljs"); const libxml = require('libxmljs');
const https = require('https'); const https = require('https');
const archiver = require('archiver'); const archiver = require('archiver');
const execa = require('execa'); const execa = require('execa');
@ -17,243 +18,243 @@ const infoXmlPath = './appinfo/info.xml';
const isStableRelease = process.argv.indexOf('--stable') > 1; const isStableRelease = process.argv.indexOf('--stable') > 1;
async function getVersion() { 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(); run();
async function run() { async function run() {
const appId = await prepareInfoXml(); const appId = await prepareInfoXml();
await createRelease(appId); await createRelease(appId);
} }
async function prepareInfoXml() { async function prepareInfoXml() {
const infoFile = fs.readFileSync(infoXmlPath); const infoFile = fs.readFileSync(infoXmlPath);
const xmlDoc = libxml.parseXml(infoFile); const xmlDoc = libxml.parseXml(infoFile);
updateVersion(xmlDoc, await getVersion()); updateVersion(xmlDoc, await getVersion());
await validateXml(xmlDoc); await validateXml(xmlDoc);
return xmlDoc.get('//id').text(); return xmlDoc.get('//id').text();
} }
function updateVersion(xmlDoc, version) { function updateVersion(xmlDoc, version) {
let versionChild = xmlDoc.get('//version'); const versionChild = xmlDoc.get('//version');
let currentVersion = versionChild.text(); const currentVersion = versionChild.text();
if (version !== currentVersion) { if (version !== currentVersion) {
console.log(`✔ Update version in info.xml to ${version}.`.green); 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) { async function createRelease(appId) {
const version = await getVersion(); const version = await getVersion();
console.log(`I'm now building ${appId} in version ${version}.`.verbose); console.log(`I'm now building ${appId} in version ${version}.`.verbose);
await execa('yarn', ['composer:install:dev']); await execa('yarn', ['composer:install:dev']);
console.log(`✔ composer dev dependencies installed`.green); console.log('✔ composer dev dependencies installed'.green);
await execa('yarn', ['lint']); await execa('yarn', ['lint']);
console.log(`✔ linters are happy`.green); console.log('✔ linters are happy'.green);
await execa('yarn', ['composer:install']); await execa('yarn', ['composer:install']);
console.log(`✔ composer dependencies installed`.green); console.log('✔ composer dependencies installed'.green);
await execa('yarn', ['build']); await execa('yarn', ['build']);
console.log(`✔ scripts built`.green); console.log('✔ scripts built'.green);
let filePath = await createArchive(appId, appId + '-v' + version); const filePath = await createArchive(appId, appId + '-v' + version);
await createNextcloudSignature(appId, filePath); await createNextcloudSignature(appId, filePath);
await createGPGSignature(filePath); await createGPGSignature(filePath);
await createGPGArmorSignature(filePath); await createGPGArmorSignature(filePath);
} }
function createArchive(appId, fileBaseName) { function createArchive(appId, fileBaseName) {
let fileName = `${fileBaseName}.tar.gz`; const fileName = `${fileBaseName}.tar.gz`;
let filePath = path.normalize(__dirname + `/../archives/${fileName}`); const filePath = path.normalize(__dirname + `/../archives/${fileName}`);
let output = fs.createWriteStream(filePath); const output = fs.createWriteStream(filePath);
let archive = archiver('tar', { const archive = archiver('tar', {
gzip: true, gzip: true,
}); });
archive.on('warning', function (err) { archive.on('warning', function (err) {
if (err.code === 'ENOENT') { if (err.code === 'ENOENT') {
console.warn('Archive warning: '.warn, err); console.warn('Archive warning: '.warn, err);
} else { } else {
throw err; throw err;
} }
}); });
archive.on('error', function (err) { archive.on('error', function (err) {
throw err; throw err;
}); });
archive.pipe(output); archive.pipe(output);
function addDirectory(name) { function addDirectory(name) {
archive.directory(name + '/', `${appId}/${name}/`); archive.directory(name + '/', `${appId}/${name}/`);
} }
function addFile(name) { function addFile(name) {
archive.file(name, {name: `${appId}/${name}`}); archive.file(name, { name: `${appId}/${name}` });
} }
addDirectory('appinfo'); addDirectory('appinfo');
addDirectory('img'); addDirectory('img');
addDirectory('js'), addDirectory('js'),
addDirectory('lib'); addDirectory('lib');
addDirectory('templates'); addDirectory('templates');
addFile('COPYING'); addFile('COPYING');
addFile('README.md') addFile('README.md');
archive.glob('vendor/**/*', { archive.glob('vendor/**/*', {
ignore: ['.git'] ignore: ['.git'],
}, { }, {
prefix: appId prefix: appId,
}); });
return new Promise(resolve => { return new Promise(resolve => {
output.on('close', function () { output.on('close', function () {
console.log(`✔ Wrote ${archive.pointer()} bytes to ${fileName}`.green); console.log(`✔ Wrote ${archive.pointer()} bytes to ${fileName}`.green);
resolve(filePath); resolve(filePath);
}); });
archive.finalize(); archive.finalize();
}); });
} }
function createNextcloudSignature(appId, filePath) { function createNextcloudSignature(appId, filePath) {
const { const {
exec exec,
} = require('child_process'); } = require('child_process');
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const sigPath = filePath + '.ncsig'; const sigPath = filePath + '.ncsig';
exec(`openssl dgst -sha512 -sign ~/.nextcloud/certificates/${appId}.key ${filePath} | openssl base64 > ${sigPath}`, (error, stdout, stderr) => { exec(`openssl dgst -sha512 -sign ~/.nextcloud/certificates/${appId}.key ${filePath} | openssl base64 > ${sigPath}`, (error, stdout, stderr) => {
if (error) { if (error) {
throw error; throw error;
} }
if (stdout) { if (stdout) {
console.log(`stdout: ${stdout}`); console.log(`stdout: ${stdout}`);
} }
if (stderr) { if (stderr) {
console.log(`stderr: ${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) { function createGPGSignature(filePath) {
const { const {
exec exec,
} = require('child_process'); } = require('child_process');
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
exec(`gpg --yes --detach-sign "${filePath}"`, (error, stdout, stderr) => { exec(`gpg --yes --detach-sign "${filePath}"`, (error, stdout, stderr) => {
if (error) { if (error) {
throw error; throw error;
} }
if (stdout) { if (stdout) {
console.log(`stdout: ${stdout}`); console.log(`stdout: ${stdout}`);
} }
if (stderr) { if (stderr) {
console.log(`stderr: ${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) { function createGPGArmorSignature(filePath) {
const { const {
exec exec,
} = require('child_process'); } = require('child_process');
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
exec(`gpg --yes --detach-sign --armor "${filePath}"`, (error, stdout, stderr) => { exec(`gpg --yes --detach-sign --armor "${filePath}"`, (error, stdout, stderr) => {
if (error) { if (error) {
throw error; throw error;
} }
if (stdout) { if (stdout) {
console.log(`stdout: ${stdout}`); console.log(`stdout: ${stdout}`);
} }
if (stderr) { if (stderr) {
console.log(`stderr: ${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) { async function validateXml(xmlDoc) {
const schemaLocation = xmlDoc.root().attr('noNamespaceSchemaLocation').value(); const schemaLocation = xmlDoc.root().attr('noNamespaceSchemaLocation').value();
if (!schemaLocation) { if (!schemaLocation) {
throw "Found no schema location"; throw 'Found no schema location';
} }
let schemaString; let schemaString;
try { try {
schemaString = await wget(schemaLocation); schemaString = await wget(schemaLocation);
} catch (err) { } catch (err) {
console.log('Could not download schema. Skip validation.'.warn); console.log('Could not download schema. Skip validation.'.warn);
return; return;
} }
let xsdDoc = libxml.parseXml(schemaString); const xsdDoc = libxml.parseXml(schemaString);
if (xmlDoc.validate(xsdDoc)) { if (xmlDoc.validate(xsdDoc)) {
console.log('✔ Document valid'.green); console.log('✔ Document valid'.green);
} else { } else {
console.log('✘ Document INVALID'.error); console.log('✘ Document INVALID'.error);
xmlDoc.validationErrors.forEach((error, index) => { xmlDoc.validationErrors.forEach((error, index) => {
console.log(`#${index + 1}\t${error.toString().trim()}`.warn); console.log(`#${index + 1}\t${error.toString().trim()}`.warn);
console.log(`\tLine ${error.line}:${error.column} (level ${error.level})`.verbose); console.log(`\tLine ${error.line}:${error.column} (level ${error.level})`.verbose);
}); });
throw 'Abort'; throw 'Abort';
} }
} }
function wget(url) { function wget(url) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
https.get(url, (resp) => { https.get(url, (resp) => {
let data = ''; let data = '';
resp.on('data', (chunk) => { resp.on('data', (chunk) => {
data += chunk; data += chunk;
}); });
resp.on('end', () => { resp.on('end', () => {
resolve(data); resolve(data);
}); });
}).on("error", (err) => { }).on('error', (err) => {
reject(err); reject(err);
}); });
}) });
} }

View File

@ -1,13 +1,14 @@
/* eslint-disable @typescript-eslint/no-var-requires */
require('colors').setTheme({ require('colors').setTheme({
verbose: 'cyan', verbose: 'cyan',
warn: 'yellow', warn: 'yellow',
error: 'red', error: 'red',
}); });
const fs = require("fs"); const fs = require('fs');
const path = require("path"); const path = require('path');
const https = require('https'); const https = require('https');
const { Octokit } = require("@octokit/rest"); const { Octokit } = require('@octokit/rest');
const execa = require('execa'); const execa = require('execa');
const inquirer = require('inquirer'); const inquirer = require('inquirer');
const git = require('simple-git/promise')(); 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 commitMessage = `release: ${package.version} :tada:`;
const tagName = `v${package.version}`; const tagName = `v${package.version}`;
const files = [ const files = [
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz`), 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.asc`),
path.join(__dirname, '..', 'archives', `bbb-v${package.version}.tar.gz.ncsig`), 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.sig`),
]; ];
function pull() { function pull() {
return git.pull('origin', 'master'); return git.pull('origin', 'master');
} }
async function notAlreadyTagged() { async function notAlreadyTagged() {
if ((await git.tags()).all.includes(tagName)) { if ((await git.tags()).all.includes(tagName)) {
throw 'version already tagged'; throw 'version already tagged';
} }
} }
async function lastCommitNotBuild() { async function lastCommitNotBuild() {
return (await git.log(['-1'])).latest.message !== commitMessage; return (await git.log(['-1'])).latest.message !== commitMessage;
} }
async function isMasterBranch() { async function isMasterBranch() {
return (await git.branch()) === 'master'; return (await git.branch()) === 'master';
} }
async function generateChangelog() { async function generateChangelog() {
const latestTag = (await git.tags()).latest; const latestTag = (await git.tags()).latest;
const title = `v${package.version}` === latestTag ? '[Unreleased]' : `${package.version} (${new Date().toISOString().split('T')[0]})`; const title = `v${package.version}` === latestTag ? '[Unreleased]' : `${package.version} (${new Date().toISOString().split('T')[0]})`;
const logs = await git.log({ const logs = await git.log({
from: latestTag, from: latestTag,
to: 'HEAD' to: 'HEAD',
}); });
const sections = [{ const sections = [{
type: 'feat', type: 'feat',
label: 'Added', label: 'Added',
}, { }, {
type: 'fix', type: 'fix',
label: 'Fixed', label: 'Fixed',
}]; }];
const entries = {}; const entries = {};
logs.all.forEach(log => { logs.all.forEach(log => {
let [, type, scope, description] = log.message.match(/^([a-z]+)(?:\((\w+)\))?: (.+)/); const [, type, scope, description] = log.message.match(/^([a-z]+)(?:\((\w+)\))?: (.+)/);
let entry = { type, scope, description, issues: [] }; const entry = { type, scope, description, issues: [] };
if(log.body) { if(log.body) {
const matches = log.body.match(/(?:fix|fixes|closes?|refs?) #(\d+)/g) || []; const matches = log.body.match(/(?:fix|fixes|closes?|refs?) #(\d+)/g) || [];
for (let match of matches) { for (const match of matches) {
const [, number] = match.match(/(\d+)$/); const [, number] = match.match(/(\d+)$/);
entry.issues.push(number); entry.issues.push(number);
} }
} }
if (!entries[type]) { if (!entries[type]) {
entries[type] = []; entries[type] = [];
} }
entries[type].push(entry); entries[type].push(entry);
}); });
let changeLog = `## ${title}\n`; let changeLog = `## ${title}\n`;
function stringifyEntry(entry) { function stringifyEntry(entry) {
let issues = entry.issues.map(issue => { const issues = entry.issues.map(issue => {
return `[#${issue}](https://github.com/sualko/cloud_bbb/issues/${issue})`; return `[#${issue}](https://github.com/sualko/cloud_bbb/issues/${issue})`;
}).join(''); }).join('');
return `- ${issues}${issues.length > 0 ? ' ' : ''}${entry.description}\n`; return `- ${issues}${issues.length > 0 ? ' ' : ''}${entry.description}\n`;
} }
sections.forEach(section => { sections.forEach(section => {
if (!entries[section.type]) { if (!entries[section.type]) {
return; return;
} }
changeLog += `### ${section.label}\n`; changeLog += `### ${section.label}\n`;
entries[section.type].forEach(entry => { entries[section.type].forEach(entry => {
changeLog += stringifyEntry(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) { if (miscKeys && miscKeys.length > 0) {
changeLog += `### Misc\n`; changeLog += '### Misc\n';
miscKeys.forEach(type => { miscKeys.forEach(type => {
entries[type].forEach(entry => { entries[type].forEach(entry => {
changeLog += stringifyEntry(entry); changeLog += stringifyEntry(entry);
}); });
}) });
} }
return changeLog; return changeLog;
} }
async function editChangeLog(changeLog) { async function editChangeLog(changeLog) {
let answers = await inquirer.prompt([{ const answers = await inquirer.prompt([{
type: 'editor', type: 'editor',
name: 'changeLog', name: 'changeLog',
message: 'You have now the possibility to edit the change log', message: 'You have now the possibility to edit the change log',
default: changeLog, default: changeLog,
}]); }]);
return answers.changeLog; return answers.changeLog;
} }
function hasChangeLogEntry() { function hasChangeLogEntry() {
return new Promise(resolve => { return new Promise(resolve => {
fs.readFile(path.join(__dirname, '..', 'CHANGELOG.md'), function (err, data) { fs.readFile(path.join(__dirname, '..', 'CHANGELOG.md'), function (err, data) {
if (err) throw err; if (err) throw err;
if (!data.includes(`## ${package.version}`)) { if (!data.includes(`## ${package.version}`)) {
throw `Found no change log entry for ${package.version}`; throw `Found no change log entry for ${package.version}`;
} }
resolve(); resolve();
}); });
}); });
} }
async function commitChangeLog() { async function commitChangeLog() {
let status = await git.status(); const status = await git.status();
if (status.staged.length > 0) { if (status.staged.length > 0) {
throw 'Repo not clean. Found staged files.'; throw 'Repo not clean. Found staged files.';
} }
if (!isDryRun) { if (!isDryRun) {
await git.add('CHANGELOG.md'); await git.add('CHANGELOG.md');
await git.commit('docs: update change log', ['-n']); await git.commit('docs: update change log', ['-n']);
} }
} }
async function hasArchiveAndSignatures() { 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() { async function stageAllFiles() {
if (isDryRun) { if (isDryRun) {
return; 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() { 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() { async function keypress() {
return inquirer.prompt([{ return inquirer.prompt([{
type: 'input', type: 'input',
name: 'keypress', name: 'keypress',
message: 'Press any key to continue... (where is the any key?)', message: 'Press any key to continue... (where is the any key?)',
}]); }]);
} }
function commit() { function commit() {
if (isDryRun) { if (isDryRun) {
return; return;
} }
return git.commit(commitMessage, ['-S', '-n']); return git.commit(commitMessage, ['-S', '-n']);
} }
async function wantToContinue(message) { async function wantToContinue(message) {
let answers = await inquirer.prompt([{ const answers = await inquirer.prompt([{
type: 'confirm', type: 'confirm',
name: 'continue', name: 'continue',
message, message,
default: false, default: false,
}]); }]);
if (!answers.continue) { if (!answers.continue) {
process.exit(10); process.exit(10);
} }
} }
function push() { function push() {
if (isDryRun) { if (isDryRun) {
return; return;
} }
return git.push('origin', 'master'); return git.push('origin', 'master');
} }
async function createGithubRelease(changeLog) { async function createGithubRelease(changeLog) {
if (!process.env.GITHUB_TOKEN) { if (!process.env.GITHUB_TOKEN) {
throw 'Github token missing' throw 'Github token missing';
} }
const octokit = new Octokit({ const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN, auth: process.env.GITHUB_TOKEN,
userAgent: 'custom releaser for sualko/cloud_bbb', userAgent: 'custom releaser for sualko/cloud_bbb',
}); });
let origin = (await git.remote(['get-url', 'origin'])).trim(); const origin = (await git.remote(['get-url', 'origin'])).trim();
let matches = origin.match(/^git@github\.com:(.+)\/(.+)\.git$/); const matches = origin.match(/^git@github\.com:(.+)\/(.+)\.git$/);
if (!matches) { if (!matches) {
throw 'Origin is not configured or no ssh url'; throw 'Origin is not configured or no ssh url';
} }
const owner = matches[1]; const owner = matches[1];
const repo = matches[2]; const repo = matches[2];
const releaseOptions = { const releaseOptions = {
owner, owner,
repo, repo,
tag_name: tagName, // eslint-disable-next-line @typescript-eslint/camelcase
name: `${package.name} ${tagName}`, tag_name: tagName,
body: changeLog.replace(/^## [^\n]+\n/, ''), name: `${package.name} ${tagName}`,
prerelease: !/^\d+\.\d+\.\d+$/.test(package.version), body: changeLog.replace(/^## [^\n]+\n/, ''),
}; prerelease: !/^\d+\.\d+\.\d+$/.test(package.version),
};
if (isDryRun) { if (isDryRun) {
console.log('github release options', releaseOptions); console.log('github release options', releaseOptions);
return []; 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) { function getMimeType(filename) {
if (filename.endsWith('.asc') || filename.endsWith('sig')) { if (filename.endsWith('.asc') || filename.endsWith('sig')) {
return 'application/pgp-signature'; return 'application/pgp-signature';
} }
if (filename.endsWith('.tar.gz')) { if (filename.endsWith('.tar.gz')) {
return 'application/gzip'; return 'application/gzip';
} }
if (filename.endsWith('.ncsig')) { if (filename.endsWith('.ncsig')) {
return 'text/plain'; return 'text/plain';
} }
return 'application/octet-stream'; return 'application/octet-stream';
} }
let assetUrls = await Promise.all(files.map(async file => { const assetUrls = await Promise.all(files.map(async file => {
const filename = path.basename(file); const filename = path.basename(file);
const uploadOptions = { const uploadOptions = {
owner, owner,
repo, repo,
release_id: releaseResponse.data.id, // eslint-disable-next-line @typescript-eslint/camelcase
data: fs.createReadStream(file), release_id: releaseResponse.data.id,
headers: { data: fs.createReadStream(file),
'content-type': getMimeType(filename), headers: {
'content-length': fs.statSync(file)['size'], 'content-type': getMimeType(filename),
}, 'content-length': fs.statSync(file)['size'],
name: filename, },
}; 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) { async function uploadToNextcloudStore(archiveUrl) {
if(!process.env.NEXTCLOUD_TOKEN) { if(!process.env.NEXTCLOUD_TOKEN) {
throw 'Nextcloud token missing'; throw 'Nextcloud token missing';
} }
const hostname = 'apps.nextcloud.com'; const hostname = 'apps.nextcloud.com';
const apiEndpoint = '/api/v1/apps/releases'; const apiEndpoint = '/api/v1/apps/releases';
const signatureFile = files.find(file => file.endsWith('.ncsig')); const signatureFile = files.find(file => file.endsWith('.ncsig'));
const data = JSON.stringify({ const data = JSON.stringify({
download: archiveUrl, download: archiveUrl,
signature: fs.readFileSync(signatureFile, 'utf-8'), signature: fs.readFileSync(signatureFile, 'utf-8'),
nightly: false, nightly: false,
}); });
const options = { const options = {
hostname, hostname,
port: 443, port: 443,
path: apiEndpoint, path: apiEndpoint,
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Content-Length': data.length, 'Content-Length': data.length,
'Authorization': `Token ${process.env.NEXTCLOUD_TOKEN}`, Authorization: `Token ${process.env.NEXTCLOUD_TOKEN}`,
} },
}; };
if (isDryRun) { if (isDryRun) {
console.log('nextcloud app store request', options, data); console.log('nextcloud app store request', options, data);
return; return;
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const req = https.request(options, res => { const req = https.request(options, res => {
if (res.statusCode === 200) { if (res.statusCode === 200) {
console.log('App release was updated successfully'.verbose); console.log('App release was updated successfully'.verbose);
resolve(); resolve();
} else if (res.statusCode === 201) { } else if (res.statusCode === 201) {
console.log('App release was created successfully'.verbose); console.log('App release was created successfully'.verbose);
resolve(); resolve();
} else if (res.statusCode === 400) { } else if (res.statusCode === 400) {
reject('App release was not accepted'); reject('App release was not accepted');
} else { } else {
reject('App release rejected with status ' + res.statusCode); reject('App release rejected with status ' + res.statusCode);
} }
res.on('data', d => { res.on('data', d => {
process.stdout.write(d) process.stdout.write(d);
}) });
}) });
req.on('error', error => { req.on('error', error => {
reject(error); reject(error);
}); });
req.write(data); req.write(data);
req.end(); req.end();
}); });
} }
async function run() { async function run() {
await pull(); await pull();
console.log(`✔ pulled latest changes`.green); console.log('✔ pulled latest changes'.green);
await notAlreadyTagged(); await notAlreadyTagged();
console.log(`✔ not already tagged`.green); console.log('✔ not already tagged'.green);
await lastCommitNotBuild(); await lastCommitNotBuild();
console.log(`✔ last commit is no build commit`.green); console.log('✔ last commit is no build commit'.green);
await isMasterBranch(); await isMasterBranch();
console.log(`✔ this is the master branch`.green); console.log('✔ this is the master branch'.green);
let changeLog = await generateChangelog(); let changeLog = await generateChangelog();
console.log(`✔ change log generated`.green); console.log('✔ change log generated'.green);
changeLog = await editChangeLog(changeLog); changeLog = await editChangeLog(changeLog);
console.log(`✔ change log updated`.green); console.log('✔ change log updated'.green);
console.log('Press any key to continue...'); console.log('Press any key to continue...');
await keypress(); await keypress();
await hasChangeLogEntry(); await hasChangeLogEntry();
console.log(`✔ there is a change log entry for this version`.green); console.log('✔ there is a change log entry for this version'.green);
await commitChangeLog(); await commitChangeLog();
console.log(`✔ change log commited`.green); console.log('✔ change log commited'.green);
await hasArchiveAndSignatures(); await hasArchiveAndSignatures();
console.log(`✔ found archive and signatures`.green); console.log('✔ found archive and signatures'.green);
await stageAllFiles(); await stageAllFiles();
console.log(`✔ all files staged`.green); 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(); await commit();
console.log(`✔ All files commited`.green); console.log('✔ All files commited'.green);
await wantToContinue('Should I push all pending commits?'); await wantToContinue('Should I push all pending commits?');
await push(); await push();
console.log(`✔ All commits pushed`.green); 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); const assetUrls = await createGithubRelease(changeLog);
console.log(`✔ released on github`.green); console.log('✔ released on github'.green);
const archiveAssetUrl = assetUrls.find(url => url.endsWith('.tar.gz')); const archiveAssetUrl = assetUrls.find(url => url.endsWith('.tar.gz'));
console.log(`Asset url for Nextcloud store: ${archiveAssetUrl}`.verbose); 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); await uploadToNextcloudStore(archiveAssetUrl);
console.log(`✔ released in Nextcloud app store`.green); console.log('✔ released in Nextcloud app store'.green);
}; };
run().catch(err => { run().catch(err => {
console.log(`${err.toString()}`.error); console.log(`${err.toString()}`.error);
}); });