Skip to content

Commit 803ac98

Browse files
Merge pull request #137 from conveyal/dev
v3.5.0
2 parents 219f4e3 + fb43420 commit 803ac98

17 files changed

+1703
-1278
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ before_install:
1212
install:
1313
- yarn
1414
script:
15+
- yarn run lint
1516
- yarn run cover
1617
- codecov
1718
after_success:

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,15 @@ Options:
132132

133133
```
134134

135+
#### Slack Notifications
136+
137+
To enable Slack notifications during the deploy process create a [Slack Webhook](https://api.slack.com/incoming-webhooks) and add two entries `SLACK_WEBHOOK` and `SLACK_CHANNEL` to your `env.yml`.
138+
139+
```
140+
SLACK_CHANNEL: '#devops'
141+
SLACK_WEBHOOK: https://hooks.slack.com/services/fake-code
142+
```
143+
135144
### `lint`
136145

137146
Lint using [Standard](http://standardjs.com/). Everything is passed directly to [`standard-engine`](https://github.com/Flet/standard-engine).

__tests__/lib/__snapshots__/jest.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
13
exports[`test.js generateTestConfig should generate proper config 1`] = `
24
Array [
35
"-u",

__tests__/lib/__snapshots__/lint-messages.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
13
exports[`import { analysis, project as proj } from '../utils/messages' 1`] = `
24
Array [
35
undefined,

__tests__/lib/__snapshots__/load-config.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
13
exports[`load-config should work without any config files present 1`] = `
24
Object {
35
"env": Object {},

__tests__/lib/__snapshots__/util.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
13
exports[`util.js parseEntries should return inputted file paths 1`] = `
24
Array [
35
Array [

__tests__/lib/push-to-s3.js

+2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ const MOCK_DIR = '__tests__/test-utils/mocks'
66
describe('lib > push to s3', () => {
77
const configPush = require('../../lib/push-to-s3')
88
const loadConfig = require('../../lib/load-config')
9+
const createLogger = require('../../lib/logger')
910

1011
it('should compile JavaScript and CSS and send to s3 via aws-sdk', () => {
1112
const config = loadConfig(process.cwd(), '~/mastarm/configurations/default', 'test')
1213
const push = configPush({
1314
env: 'test',
1415
config,
16+
log: createLogger(),
1517
minify: false,
1618
s3bucket: 'test-bucket'
1719
})

bin/mastarm

+54-16
Original file line numberDiff line numberDiff line change
@@ -64,24 +64,53 @@ commander
6464
.option('--cloudfront', 'CloudFront Distribution ID to invalidate.')
6565
.option('--s3bucket', 'S3 Bucket to push to.')
6666
.action(function (entries, options) {
67+
const commit = require('this-commit')()
68+
const username = require('username')
69+
const createLogger = require('../lib/logger')
70+
const pkg = require('../lib/pkg')
6771
const pushToS3 = require('../lib/push-to-s3')
72+
73+
const url = pkg.repository.url.replace('.git', '')
74+
const tag = `<${url}/commit/${commit}|${pkg.name}@${commit.slice(0, 6)}>:`
75+
const user = username.sync()
6876
const config = loadConfig(process.cwd(), commander.config, commander.env)
6977
const get = util.makeGetFn([options, commander, config.settings])
78+
79+
const env = get('env')
80+
const s3bucket = get('s3bucket')
81+
7082
const files = util.parseEntries([...entries, ...(get('entries') || [])])
7183
util.assertEntriesExist(files)
72-
Promise.all(files.map(pushToS3({
73-
cloudfront: get('cloudfront'),
84+
85+
const log = createLogger({channel: config.env.SLACK_CHANNEL || '#devops', webhook: config.env.SLACK_WEBHOOK})
86+
const minify = get('minify')
87+
const cloudfront = get('cloudfront')
88+
const push = pushToS3({
89+
cloudfront,
7490
config,
75-
env: get('env'),
76-
minify: get('minify'),
77-
s3bucket: get('s3bucket')
78-
}))).then(() => {
79-
console.log('Finished deploying!')
80-
process.exit(0)
81-
}).catch((err) => {
82-
console.error('Error while deploying!')
83-
console.error(err.stack)
84-
process.exit(1)
91+
env,
92+
log,
93+
minify,
94+
s3bucket,
95+
tag
96+
})
97+
98+
log(
99+
`:construction: *${tag} deploy started by <@${user}>*
100+
:cloud: *cloudfront:* ${cloudfront}
101+
:hash: *commit:* ${commit}
102+
:seedling: *env:* ${env}
103+
:compression: *minify:* ${minify}
104+
:package: *s3bucket:* ${s3bucket}`
105+
).then(() => {
106+
Promise
107+
.all(files.map(push))
108+
.then(() =>
109+
log(`:rocket: ${tag} deploy finished!! :tada: :confetti_ball: :tada:`)
110+
.then(() => process.exit(0)))
111+
.catch((err) =>
112+
log(`:rotating_light: *error deploying ${tag} ${err.message || err}*`)
113+
.then(() => process.exit(1)))
85114
})
86115
})
87116

@@ -90,15 +119,24 @@ commander
90119
.allowUnknownOption()
91120
.description('Lint JavaScript [& CSS coming soon!]')
92121
.action(function () {
122+
const eslint = require('eslint')
93123
const engine = require('standard-engine')
94-
const standardOptions = require('standard/options')
95124
// get lint out of there
96125
process.argv = process.argv.filter((arg) => arg !== 'lint')
97126
// Force verbose
98127
process.argv.push('--verbose')
99-
// add babel-eslint parser
100-
standardOptions.parser = 'babel-eslint'
101-
engine.cli(standardOptions)
128+
129+
engine.cli({
130+
bugs: 'github.com/conveyal/mastarm/issues',
131+
cmd: 'mastarm lint',
132+
eslint,
133+
eslintConfig: {
134+
configFile: path.join(__dirname, '../lib/eslintrc.json')
135+
},
136+
homepage: 'github.com/conveyal/mastarm',
137+
tagline: 'never forget to floss...or lint!',
138+
version: require('../package.json').version
139+
})
102140
})
103141

104142
commander

lib/browserify.js

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = function ({
1414
basedir: process.cwd(),
1515
cache: {},
1616
debug: true,
17+
fullPaths: env === 'development',
1718
packageCache: {},
1819
paths: [
1920
path.join(__dirname, '/../node_modules'),

lib/eslintrc.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"extends": [
3+
"standard",
4+
"standard-jsx",
5+
"plugin:flowtype/recommended",
6+
"plugin:jsx-a11y/recommended"
7+
],
8+
"parser": "babel-eslint",
9+
"plugins": [
10+
"flowtype",
11+
"flowtype-errors",
12+
"jsx-a11y"
13+
],
14+
"rules": {
15+
"flowtype-errors/show-errors": 2,
16+
"prefer-const": ["warn", {
17+
"destructuring": "all",
18+
"ignoreReadBeforeAssign": false
19+
}]
20+
},
21+
"settings": {
22+
"flowtype": {
23+
"onlyFilesWithFlowAnnotation": true
24+
}
25+
}
26+
}

lib/lint-messages.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function lintFileContents (messages, js) {
4141
// what was messages imported as
4242
let importedAtRootAs = false
4343
let importedMembersAs
44-
let importedMembersLookup = new Map()
44+
const importedMembersLookup = new Map()
4545

4646
let rootMatcher, namedMatcher
4747

lib/logger.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const slack = require('./notify-slack')
2+
3+
module.exports = function ({channel, webhook} = {}) {
4+
if (webhook) console.log(`see slack@${channel} for notifications`)
5+
return function (text) {
6+
if (webhook) {
7+
return slack({channel, text, webhook})
8+
} else {
9+
return Promise.resolve(console.log(text))
10+
}
11+
}
12+
}

lib/notify-slack.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const Slack = require('slack-node')
2+
const slack = new Slack()
3+
4+
module.exports = function notify ({
5+
channel,
6+
text,
7+
webhook
8+
}) {
9+
return new Promise((resolve, reject) => {
10+
try {
11+
slack.setWebhook(webhook)
12+
slack.webhook({channel, text}, (err, response) => {
13+
if (err) {
14+
onError(err)
15+
} else if (response.statusCode >= 400) {
16+
onError(new Error(`${response.statusCode} ${response.response}`))
17+
} else {
18+
resolve(response)
19+
}
20+
})
21+
} catch (err) {
22+
onError(err)
23+
}
24+
25+
function onError (err) {
26+
console.log(text)
27+
console.error('Error posting to Slack webhook')
28+
console.error(err)
29+
resolve(err) // Always resolve to avoid logging to cause failure
30+
}
31+
})
32+
}

lib/pkg.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const pkgPath = path.resolve(process.cwd(), 'package.json')
4+
if (fs.existsSync(pkgPath)) {
5+
module.exports = require(pkgPath)
6+
} else {
7+
module.exports = {}
8+
}

lib/push-to-s3.js

+43-41
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,37 @@ module.exports = function ({
1010
cloudfront,
1111
config,
1212
env,
13+
log,
1314
minify,
14-
s3bucket
15+
s3bucket,
16+
tag
1517
}) {
16-
console.log('pushing to s3bucket:', s3bucket, '>cloudfront:', cloudfront, '>env:', env, '>minified:', Boolean(minify))
18+
const upload = createUpload({bucket: s3bucket, cloudfront, log, tag})
1719
return ([entry, outfile]) => (
1820
path.extname(entry) === '.js'
1921
? upload({
2022
body: browserify({config, entry, env, minify}).bundle(),
21-
bucket: s3bucket,
22-
cloudfront,
2323
outfile
2424
})
2525
: transformCss({config, entry, env, minify})
2626
.then((results) =>
27-
upload({
28-
body: results.css,
29-
bucket: s3bucket,
30-
cloudfront,
31-
outfile
32-
})
33-
)
27+
upload({body: results.css, outfile}))
3428
)
3529
}
3630

31+
const createUpload = ({bucket, cloudfront, log, tag}) =>
32+
({body, outfile}) =>
33+
upload({body, bucket, cloudfront, log, outfile, tag})
34+
3735
function upload ({
3836
body,
3937
bucket,
4038
cloudfront,
41-
outfile
39+
log,
40+
outfile,
41+
tag
4242
}) {
43+
const bucketUrl = `https://s3.amazonaws.com/${bucket}`
4344
return new Promise((resolve, reject) => {
4445
const s3object = new AWS.S3({
4546
params: {
@@ -51,39 +52,40 @@ function upload ({
5152
}
5253
})
5354

54-
console.log('building and pushing %s to s3 bucket %s', outfile, bucket)
55-
s3object
56-
.upload()
57-
.send(function (err) {
58-
if (err) return reject(err)
59-
console.log('finished pushing %s to s3', outfile)
60-
61-
if (cloudfront) {
62-
const cf = new AWS.CloudFront()
63-
console.log('creating invalidation for cf distribution %s at %s', cloudfront, outfile)
64-
cf.createInvalidation({
65-
DistributionId: cloudfront,
66-
InvalidationBatch: {
67-
CallerReference: uuid.v4(),
68-
Paths: {
69-
Quantity: 1,
70-
Items: [
71-
'/' + outfile
72-
]
73-
}
55+
log(`:hammer_and_wrench: ${tag} building ${outfile} and pushing to ${bucket}`).then(() => {
56+
s3object
57+
.upload()
58+
.send(function (err) {
59+
if (err) return reject(`s3 upload to ${bucket} rejected with ${err.message}`)
60+
log(`:ok_hand: ${tag} finished pushing to <${bucketUrl}/${outfile}|${bucket}/${outfile}>`).then(() => {
61+
if (cloudfront) {
62+
const cf = new AWS.CloudFront()
63+
log(`:earth_asia: ${tag} creating cloudfront invalidation at ${outfile}`).then(() => {
64+
cf.createInvalidation({
65+
DistributionId: cloudfront,
66+
InvalidationBatch: {
67+
CallerReference: uuid.v4(),
68+
Paths: {
69+
Quantity: 1,
70+
Items: [
71+
'/' + outfile
72+
]
73+
}
74+
}
75+
}, function (err) {
76+
if (err) return reject(`cf invalidation rejected with ${err.message}`)
77+
done()
78+
})
79+
})
80+
} else {
81+
done()
7482
}
75-
}, function (err) {
76-
if (err) return reject(err)
77-
done()
7883
})
79-
} else {
80-
done()
81-
}
82-
})
84+
})
85+
})
8386

8487
function done () {
85-
console.log('finished deploying %s', outfile)
86-
resolve()
88+
log(`:checkered_flag: ${tag} finished with ${outfile}`).then(resolve)
8789
}
8890
})
8991
}

0 commit comments

Comments
 (0)