-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
Copy pathdaily.js
242 lines (210 loc) · 6.87 KB
/
daily.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#!/usr/bin/env babel-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('./packages/client-app/CHANGELOG.md', 2)
await prependToFile('./packages/client-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 (
`# Nylas Mail 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('../packages/client-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')
const untrackedFiles = await exec('git ls-files --others --exclude-standard') || ''
if (untrackedFiles.length > 0) {
throw new Error('Working directory has untracked files')
}
} 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, ['./packages/client-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: 'packages/client-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', 'packages/client-app/build/resources/certs'])
await spawn('npm', ['install'], {env: {INSTALL_TARGET: 'client'}})
await spawn('npm', ['run', 'build-client'], {env: {SIGN_BUILD: true}})
await spawn('codesign', ['--verify', '--deep', '--verbose=2', '"packages/client-app/dist/Nylas Mail-darwin-x64/Nylas Mail.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)