cloud_bbb/scripts/publish-release.js

314 lines
7.5 KiB
JavaScript
Raw Normal View History

2021-02-01 10:57:41 +01:00
/* eslint-disable @typescript-eslint/no-var-requires */
const colors = require('colors');
const fs = require('fs');
const path = require('path');
const https = require('https');
const execa = require('execa');
const simpleGit = require('simple-git/promise');
const inquirer = require('inquirer');
const dotenv = require('dotenv');
const { Octokit } = require('@octokit/rest');
2021-02-24 15:45:55 +01:00
const { getChangelogEntry, hasChangeLogEntry } = require('./imports/changelog');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageInfo = require('../package.json');
colors.setTheme({
2020-04-29 14:16:57 +02:00
verbose: 'cyan',
warn: 'yellow',
error: 'red',
2020-04-28 01:08:00 +02:00
});
dotenv.config();
2020-04-28 01:08:00 +02:00
const git = simpleGit();
const isDryRun = process.argv.indexOf('--dry-run') > 1;
const commitMessage = `release: ${packageInfo.version} :tada:`;
const tagName = `v${packageInfo.version}`;
2020-04-28 01:08:00 +02:00
const files = [
path.join(__dirname, '..', 'archives', `bbb-v${packageInfo.version}.tar.gz`),
path.join(__dirname, '..', 'archives', `bbb-v${packageInfo.version}.tar.gz.asc`),
path.join(__dirname, '..', 'archives', `bbb-v${packageInfo.version}.tar.gz.ncsig`),
path.join(__dirname, '..', 'archives', `bbb-v${packageInfo.version}.tar.gz.sig`),
2020-04-28 01:08:00 +02:00
];
2021-02-01 10:57:41 +01:00
isDryRun && console.log('Script is executed in dry-run mode.'.verbose);
function pull() {
2020-04-29 14:16:57 +02:00
return git.pull('origin', 'master');
}
2020-04-28 01:08:00 +02:00
async function notAlreadyTagged() {
2020-04-29 14:16:57 +02:00
if ((await git.tags()).all.includes(tagName)) {
throw 'version already tagged';
}
2020-04-28 01:08:00 +02:00
}
async function lastCommitNotBuild() {
2021-02-24 15:45:55 +01:00
const latest = (await git.log(['-1'])).latest || {};
return latest.message !== commitMessage;
2020-04-28 01:08:00 +02:00
}
async function isMasterBranch() {
return (await git.branch()).current === 'master';
}
2020-04-28 01:08:00 +02:00
async function hasArchiveAndSignatures() {
2020-04-29 14:16:57 +02:00
return files.map(file => fs.existsSync(file)).indexOf(false) < 0;
2020-04-28 01:08:00 +02:00
}
async function stageAllFiles() {
2020-04-29 14:16:57 +02:00
if (isDryRun) {
return;
}
2020-04-29 14:16:57 +02:00
const gitProcess = execa('git', ['add', '-u']);
2020-04-28 01:08:00 +02:00
2021-02-24 15:45:55 +01:00
gitProcess.stdout.pipe(process.stdout);
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
return gitProcess;
2020-04-28 01:08:00 +02:00
}
function showStagedDiff() {
2020-04-29 14:16:57 +02:00
const gitProcess = execa('git', ['diff', '--staged']);
2020-04-28 01:08:00 +02:00
2021-02-24 15:45:55 +01:00
gitProcess.stdout.pipe(process.stdout);
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
return gitProcess;
2020-04-28 01:08:00 +02:00
}
async function keypress() {
2020-04-29 14:16:57 +02:00
return inquirer.prompt([{
type: 'input',
name: 'keypress',
message: 'Press any key to continue... (where is the any key?)',
}]);
2020-04-28 01:08:00 +02:00
}
function commit() {
2020-04-29 14:16:57 +02:00
if (isDryRun) {
return;
}
2020-04-29 14:16:57 +02:00
return git.commit(commitMessage, ['-S', '-n']);
2020-04-28 01:08:00 +02:00
}
async function wantToContinue(message) {
2020-04-29 14:16:57 +02:00
const answers = await inquirer.prompt([{
type: 'confirm',
name: 'continue',
message,
default: false,
}]);
if (!answers.continue) {
process.exit(10);
}
}
2020-04-28 01:08:00 +02:00
function push() {
2020-04-29 14:16:57 +02:00
if (isDryRun) {
return;
}
2020-04-29 14:16:57 +02:00
return git.push('origin', 'master');
2020-04-28 01:08:00 +02:00
}
async function createGithubRelease(changeLog) {
2020-04-29 14:16:57 +02:00
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 origin = (await git.remote(['get-url', 'origin'])) || '';
const matches = origin.trim().match(/^git@github\.com:(.+)\/(.+)\.git$/);
2020-04-29 14:16:57 +02:00
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,
2020-05-24 18:48:58 +02:00
name: `BigBlueButton Integration ${tagName}`,
2020-04-29 14:16:57 +02:00
body: changeLog.replace(/^## [^\n]+\n/, ''),
prerelease: !/^\d+\.\d+\.\d+$/.test(packageInfo.version),
2020-04-29 14:16:57 +02:00
};
if (isDryRun) {
console.log('github release options', releaseOptions);
return [];
}
const releaseResponse = await octokit.repos.createRelease(releaseOptions);
console.log(`Draft created, see ${releaseResponse.data.html_url}`.verbose);
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('.ncsig')) {
return 'text/plain';
}
return 'application/octet-stream';
}
const assetUrls = await Promise.all(files.map(async file => {
const filename = path.basename(file);
const uploadOptions = {
owner,
repo,
release_id: releaseResponse.data.id,
2021-02-24 15:45:55 +01:00
data: fs.createReadStream(file),
2020-04-29 14:16:57 +02:00
headers: {
'content-type': getMimeType(filename),
'content-length': fs.statSync(file)['size'],
},
name: filename,
};
const assetResponse = await octokit.repos.uploadReleaseAsset(uploadOptions);
console.log(`Asset uploaded: ${assetResponse.data.name}`.verbose);
return assetResponse.data.browser_download_url;
}));
return assetUrls;
}
async function uploadToNextcloudStore(archiveUrl) {
2020-04-29 14:16:57 +02:00
if(!process.env.NEXTCLOUD_TOKEN) {
throw 'Nextcloud token missing';
}
const hostname = 'apps.nextcloud.com';
const apiEndpoint = '/api/v1/apps/releases';
2021-02-24 15:45:55 +01:00
const signatureFile = files.find(file => file.endsWith('.ncsig'));
2020-04-29 14:16:57 +02:00
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;
}
2021-02-24 15:45:55 +01:00
return new Promise((resolve, reject) => {
2020-04-29 14:16:57 +02:00
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);
});
});
req.on('error', error => {
reject(error);
});
req.write(data);
req.end();
});
2020-04-28 01:08:00 +02:00
}
async function run() {
2020-04-29 14:16:57 +02:00
await pull();
console.log('✔ pulled latest changes'.green);
2020-06-15 13:27:57 +02:00
await execa('yarn', ['composer:install:dev']);
console.log('✔ composer dev dependencies installed'.green);
2020-04-29 14:16:57 +02:00
await notAlreadyTagged();
console.log('✔ not already tagged'.green);
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
await lastCommitNotBuild();
console.log('✔ last commit is no build commit'.green);
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
await isMasterBranch();
console.log('✔ this is the master branch'.green);
2020-04-28 01:08:00 +02:00
await hasChangeLogEntry(packageInfo.version);
console.log('✔ there is a change log entry for this version'.green);
const changeLog = await getChangelogEntry(packageInfo.version);
2020-04-29 14:14:30 +02:00
console.log(changeLog);
2020-04-29 14:16:57 +02:00
console.log('Press any key to continue...');
await keypress();
2020-04-29 14:16:57 +02:00
await hasArchiveAndSignatures();
console.log('✔ found archive and signatures'.green);
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
await stageAllFiles();
console.log('✔ all files staged'.green);
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
await showStagedDiff();
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
await wantToContinue('Should I commit those changes?');
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
await commit();
console.log('✔ All files commited'.green);
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
await wantToContinue('Should I push all pending commits?');
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
await push();
console.log('✔ All commits pushed'.green);
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
await wantToContinue('Should I continue to create a Github release?');
2020-04-29 14:16:57 +02:00
const assetUrls = await createGithubRelease(changeLog);
console.log('✔ released on github'.green);
2020-04-28 01:08:00 +02:00
2020-04-29 14:16:57 +02:00
const archiveAssetUrl = assetUrls.find(url => url.endsWith('.tar.gz'));
console.log(`Asset url for Nextcloud store: ${archiveAssetUrl}`.verbose);
2020-04-29 14:16:57 +02:00
await wantToContinue('Should I continue to upload the release to the app store?');
2020-04-29 14:16:57 +02:00
await uploadToNextcloudStore(archiveAssetUrl);
console.log('✔ released in Nextcloud app store'.green);
}
run().catch(err => {
2020-04-29 14:16:57 +02:00
console.log(`${err.toString()}`.error);
});