Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sualko/cloud_piwik.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsualko <klaus@jsxc.org>2020-08-14 11:33:15 +0300
committersualko <klaus@jsxc.org>2020-08-14 11:33:15 +0300
commit6bd662c02ef0f33829750de60be98b8fe13de01f (patch)
treea315d657fd1a813a2a32e6eadf74e4533fcea645 /scripts
parent489e1b473fffa35331cd9de1bf0c932a3ff2d3f1 (diff)
chore: add publish script
Diffstat (limited to 'scripts')
-rw-r--r--scripts/publish-release.js315
1 files changed, 315 insertions, 0 deletions
diff --git a/scripts/publish-release.js b/scripts/publish-release.js
new file mode 100644
index 0000000..59ee4f1
--- /dev/null
+++ b/scripts/publish-release.js
@@ -0,0 +1,315 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+require('colors').setTheme({
+ verbose: 'cyan',
+ warn: 'yellow',
+ error: 'red',
+});
+
+const fs = require('fs');
+const path = require('path');
+const https = require('https');
+const { Octokit } = require('@octokit/rest');
+const execa = require('execa');
+const inquirer = require('inquirer');
+const git = require('simple-git/promise')();
+const package = require('../package.json');
+
+require('dotenv').config();
+
+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', `cloud_piwik-${package.version}.tar.gz`),
+ path.join(__dirname, '..', 'archives', `cloud_piwik-${package.version}.tar.gz.asc`),
+ path.join(__dirname, '..', 'archives', `cloud_piwik-${package.version}.tar.gz.ncsig`),
+ path.join(__dirname, '..', 'archives', `cloud_piwik-${package.version}.tar.gz.sig`),
+];
+
+function pull() {
+ return git.pull('origin', 'master');
+}
+
+async function notAlreadyTagged() {
+ if ((await git.tags()).all.includes(tagName)) {
+ throw 'version already tagged';
+ }
+}
+
+async function lastCommitNotBuild() {
+ return (await git.log(['-1'])).latest.message !== commitMessage;
+}
+
+async function isMasterBranch() {
+ return (await git.branch()) === 'master';
+}
+
+async function editChangeLog(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;
+}
+
+async function hasArchiveAndSignatures() {
+ return files.map(file => fs.existsSync(file)).indexOf(false) < 0;
+}
+
+async function stageAllFiles() {
+ if (isDryRun) {
+ return;
+ }
+
+ const gitProcess = execa('git', ['add', '-u']);
+
+ gitProcess.stdout.pipe(process.stdout);
+
+ return gitProcess;
+}
+
+function showStagedDiff() {
+ const gitProcess = execa('git', ['diff', '--staged']);
+
+ gitProcess.stdout.pipe(process.stdout);
+
+ return gitProcess;
+}
+
+async function keypress() {
+ return inquirer.prompt([{
+ type: 'input',
+ name: 'keypress',
+ message: 'Press any key to continue... (where is the any key?)',
+ }]);
+}
+
+function commit() {
+ if (isDryRun) {
+ return;
+ }
+
+ return git.commit(commitMessage, ['-S', '-n']);
+}
+
+async function wantToContinue(message) {
+ const answers = await inquirer.prompt([{
+ type: 'confirm',
+ name: 'continue',
+ message,
+ default: false,
+ }]);
+
+ if (!answers.continue) {
+ process.exit(10);
+ }
+}
+
+function push() {
+ if (isDryRun) {
+ return;
+ }
+
+ return git.push('origin', 'master');
+}
+
+async function createGithubRelease(changeLog) {
+ if (!process.env.GITHUB_TOKEN) {
+ throw 'Github token missing';
+ }
+
+ const octokit = new Octokit({
+ auth: process.env.GITHUB_TOKEN,
+ userAgent: 'custom releaser for sualko/cloud_piwik',
+ });
+
+ 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';
+ }
+
+ const owner = matches[1];
+ const repo = matches[2];
+ const releaseOptions = {
+ owner,
+ repo,
+ // eslint-disable-next-line @typescript-eslint/camelcase
+ tag_name: tagName,
+ name: `Matomo Integration ${tagName}`,
+ body: changeLog.replace(/^## [^\n]+\n/, ''),
+ prerelease: !/^\d+\.\d+\.\d+$/.test(package.version),
+ };
+
+ if (isDryRun) {
+ console.log('github release options', releaseOptions);
+ return [];
+ }
+
+ const releaseResponse = await octokit.repos.createRelease(releaseOptions);
+
+ console.log(`Release 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,
+ // 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,
+ };
+
+ 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) {
+ 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}`,
+ },
+ };
+
+ 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);
+ }
+
+ res.on('data', d => {
+ process.stdout.write(d);
+ });
+ });
+
+ req.on('error', error => {
+ reject(error);
+ });
+
+ req.write(data);
+ req.end();
+ });
+}
+
+async function run() {
+ if (isDryRun) {
+ console.log('INFO: You executed this script in dry run mode.'.verbose);
+ }
+
+ await pull();
+ console.log('✔ pulled latest changes'.green);
+
+ await notAlreadyTagged();
+ console.log('✔ not already tagged'.green);
+
+ await lastCommitNotBuild();
+ console.log('✔ last commit is no build commit'.green);
+
+ await isMasterBranch();
+ console.log('✔ this is the master branch'.green);
+
+ let changeLog = await editChangeLog('');
+ console.log('✔ change log updated'.green);
+
+ console.log(changeLog);
+
+ console.log('Press any key to continue...');
+ await keypress();
+
+ await hasArchiveAndSignatures();
+ console.log('✔ found archive and signatures'.green);
+
+ await stageAllFiles();
+ console.log('✔ all files staged'.green);
+
+ await showStagedDiff();
+
+ await wantToContinue('Should I commit those changes?');
+
+ await commit();
+ console.log('✔ All files commited'.green);
+
+ await wantToContinue('Should I push all pending commits?');
+
+ await push();
+ console.log('✔ All commits pushed'.green);
+
+ await wantToContinue('Should I continue to create a Github release?');
+
+ 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);
+
+ await wantToContinue('Should I continue to upload the release to the app store?');
+
+ await uploadToNextcloudStore(archiveAssetUrl);
+ console.log('✔ released in Nextcloud app store'.green);
+};
+
+run().catch(err => {
+ console.log(`✘ ${err.toString()}`.error);
+});