Mailspring/scripts/unused/daily.js
2017-09-05 13:40:25 -07:00

238 lines
6.5 KiB
JavaScript

#!/usr/bin/env node
const childProcess = require('child_process')
const path = require('path')
const mkdirp = require('mkdirp')
const semver = require('semver')
const program = require('commander')
const TMP_DIR = path.join(__dirname, '..', 'tmp')
async function spawn(cmd, args, opts = {}) {
return new Promise((resolve, reject) => {
const env = Object.assign({}, process.env, opts.env || {})
delete opts.env
const options = Object.assign({stdio: 'inherit', env}, opts);
const proc = childProcess.spawn(cmd, args, options)
proc.on("error", reject)
proc.on("exit", resolve)
})
}
function exec(cmd, opts = {}) {
return new Promise((resolve, reject) => {
childProcess.exec(cmd, opts, (err, stdout) => {
if (err) {
return reject(err)
}
return resolve(stdout)
})
})
}
function git(subCmd, opts = {}) {
const optsString = Object.keys(opts).reduce((prev, key) => {
const optVal = opts[key]
if (optVal == null) {
return key.length > 1 ? `${prev} --${key}` : `${prev} -${key}`
}
return key.length > 1 ? `${prev} --${key}=${optVal}` : `${prev} -${key} ${optVal}`
}, '')
return exec(`git ${subCmd} ${optsString}`, {cwd: './'})
}
async function prependToFile(filepath, string) {
await exec(`echo "${string}" > ${TMP_DIR}/tmpfile`)
await exec(`cat ${filepath} >> ${TMP_DIR}/tmpfile`)
await exec(`mv ${TMP_DIR}/tmpfile ${filepath}`)
}
async function sliceFileLines(filepath, idx) {
await exec(`tail -n +${1 + idx} ${filepath} > ${TMP_DIR}/tmpfile`)
await exec(`mv ${TMP_DIR}/tmpfile ${filepath}`)
}
async function updateChangelogFile(changelogString) {
mkdirp.sync(TMP_DIR)
await sliceFileLines('./app/CHANGELOG.md', 2)
await prependToFile('./app/CHANGELOG.md', changelogString)
}
function getFormattedLogs(mainLog) {
const formattedMainLog = (
mainLog
.filter(line => line.length > 0)
.filter(line => !/^bump/i.test(line) && !/changelog/i.test(line))
.map(line => ` + ${line.replace('*', '\\*')}`)
.join('\n')
)
return `${formattedMainLog}\n`
}
function getChangelogHeader(nextVersion) {
const date = new Date().toLocaleDateString()
return (
`# Mailspring Changelog
### ${nextVersion} (${date})
`
)
}
function validateArgs(args) {
if (args.editChangelog && !process.env.EDITOR) {
throw new Error(`You can't edit the changelog without a default EDITOR in your env`)
}
}
// TODO add progress indicators with ora
// TODO add options
// --update-daily-channel
// --notify
// --quiet
async function main(args) {
validateArgs(args)
// Pull latest changes
try {
await git(`checkout master`)
await git(`pull --rebase`)
} catch (err) {
console.error(err)
process.exit(1)
}
const pkg = require('../app/package.json') //eslint-disable-line
const currentVersion = pkg.version
const nextVersion = semver.inc(currentVersion, 'patch')
// Make sure working directory is clean
try {
await exec('git diff --exit-code && git diff --cached --exit-code')
} catch (err) {
console.error('Git working directory is not clean!')
process.exit(1)
}
// Make sure there is a diff to build
let mainLog = '';
try {
mainLog = (await git(`log ${currentVersion}..master --format='%s'`)).split('\n')
if (mainLog.length <= 1) {
console.error(`There are no changes to build since ${currentVersion}`)
process.exit(1)
}
} catch (err) {
console.error(err)
process.exit(1)
}
// Update CHANGELOG
try {
const commitLogSinceLatestVersion = await getFormattedLogs(mainLog)
const changelogHeader = getChangelogHeader(nextVersion)
const changelogString = `${changelogHeader}${commitLogSinceLatestVersion}`
await updateChangelogFile(changelogString)
console.log(changelogString)
} catch (err) {
console.error('Could not update changelog file')
console.error(err)
process.exit(1)
}
// Allow editing
if (args.editChangelog) {
try {
await spawn(process.env.EDITOR, ['./app/CHANGELOG.md'], {stdio: 'inherit'})
} catch (err) {
console.error('Error editing CHANGELOG.md')
console.error(err)
process.exit(1)
}
}
// Bump patch version in package.json
try {
await exec('npm --no-git-tag-version version patch', {cwd: './app'})
} catch (err) {
console.error('Could not bump version in package.json')
console.error(err)
process.exit(1)
}
if (args.noCommit) {
return
}
// Commit changes
try {
await git('add .')
await git(`commit -m 'bump(version): ${nextVersion}'`)
} catch (err) {
console.error('Could not commit changes')
console.error(err)
process.exit(1)
}
if (args.noTag) {
return
}
// Tag commit
try {
await git(`tag ${nextVersion}`)
} catch (err) {
console.error('Could not tag commit')
console.error(err)
process.exit(1)
}
if (args.noPush) {
return
}
// Push changes
try {
await git(`push origin master --tags`)
} catch (err) {
console.error('Could not tag commit')
console.error(err)
process.exit(1)
}
// Build locally. This should only be used when building from our in-office
// coffee machine mac mini
if (args.build) {
try {
await spawn('git', ['clean', '-xdf'])
await spawn('cp', ['-r', '../n1-keys-and-certificates', 'app/build/resources/certs'])
await spawn('npm', ['install'], {env: {}})
await spawn('npm', ['run', 'build-client'], {env: {SIGN_BUILD: true}})
await spawn('codesign', ['--verify', '--deep', '--verbose=2', '"app/dist/Mailspring-darwin-x64/Mailspring.app"'])
// await spawn('npm', ['run', 'upload-client'])
} catch (err) {
console.error('Errored while running build')
console.error(err)
process.exit(1)
}
// TODO Update `daily` channel
// TODO send out notification email
}
console.log('Done!')
}
program
.version('0.0.1')
.usage('[options]')
.description('This script will bump the version in package.json, edit the changelog with the latest\n git log (for easier editing), commit and tag the changes, and push to Github to trigger\n a build')
.option('--edit-changelog', 'Open your $EDITOR to edit CHANGELOG before commiting version bump.')
.option('--no-commit', 'Wether to commit changes to CHANGELOG.md and package.json')
.option('--no-tag', 'Wether to tag the version bump commit (no-op if --no-commit is used)')
.option('--no-push', 'Wether to push changes to the Github remote')
.option('--build', 'Wether to build the app locally. This should only be used when building from our in-office Mac Mini by the coffee machine')
.parse(process.argv)
main(program)