-
Notifications
You must be signed in to change notification settings - Fork 81
/
dangerfile.ts
157 lines (143 loc) · 5.28 KB
/
dangerfile.ts
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
import * as child from 'child_process'
import { danger, fail, schedule, warn } from 'danger'
const shouldRun =
!danger.github || (danger.github && danger.github.pr?.user.type !== 'Bot')
// Load all modified and new files
const allFiles = (danger.git.modified_files ?? []).concat(
danger.git.created_files
)
const checkYarnAudit: () => void = () => {
const result = child.spawnSync('yarn', [
'audit',
'--groups=dependencies',
'--level=high',
'--json',
])
const output = result.stdout.toString().split('\n')
const summary = JSON.parse(output[output.length - 2])
if (
'data' in summary &&
'vulnerabilities' in summary.data &&
'high' in summary.data.vulnerabilities &&
'critical' in summary.data.vulnerabilities
) {
if (
summary.data.vulnerabilities.high > 0 ||
summary.data.vulnerabilities.critical > 0
) {
let issuesFound = 'Yarn Audit Issues Found:\n'
output.forEach((rawAudit) => {
try {
const audit = JSON.parse(rawAudit)
if (audit.type === 'auditAdvisory') {
issuesFound +=
`${audit.data.advisory.severity} - ${audit.data.advisory.title}\n` +
`Package ${audit.data.advisory.module_name}\n` +
`Patched in ${audit.data.advisory.patched_versions}\n` +
`Dependency of ${audit.data.resolution.path.split('>')[0]}\n` +
`Path ${audit.data.resolution.path.replace(/>/g, ' > ')}\n` +
`More info ${audit.data.advisory.url}\n\n`
}
} catch {
// not all outputs maybe json and that's okay
}
})
fail(
`${issuesFound}${summary.data.vulnerabilities.high} high vulnerabilities and ` +
`${summary.data.vulnerabilities.critical} critical vulnerabilities found`
)
}
} else {
warn(`Couldn't find summary of vulnerabilities from yarn audit`)
}
}
const checkPrDescription: () => void = () => {
// No PR is too small to include a description of why you made a change
if (danger.github && danger.github.pr.body.length < 10) {
warn('Please include a description of your PR changes.')
}
}
const checkCodeChanges: () => void = () => {
// Request changes to package source code to also include changes to tests.
const hasCodeChanges = allFiles.some((p) => !!p.match(/^src\/.*\.[jt]sx?/))
const hasTestChanges = allFiles.some(
(p) => !!p.match(/^src\/.*\.test\.[jt]sx?/)
)
if (hasCodeChanges && !hasTestChanges) {
warn(
'This PR does not include changes to tests, even though it affects source code.'
)
}
// Make sure to export new components (src/components/*.[jt]sx)
const hasNewComponents = danger.git.created_files.some(
(p) => !!p.match(/^src\/components\/.*\.[jt]sx/)
)
const hasEntrypointChanges = allFiles.includes('src/index.ts')
if (hasNewComponents && !hasEntrypointChanges) {
const message = `It looks like there are new component (JSX/TSX) files, but the entrypoint (index.ts) has not changed.`
const idea = `Did you forget to export new components from the library entrypoint?`
warn(`${message} - <em>${idea}</em>`)
}
// Require new src/components files to include changes to storybook
const hasStorybookChanges = allFiles.some(
(p) => !!p.match(/^src\/.*\.stories\.[jt]sx?/)
)
if (hasCodeChanges && !hasStorybookChanges) {
warn(
'This PR does not include changes to storybook, even though it affects component code.'
)
}
}
const checkDependencyChanges: () => void = () => {
// Request update of yarn.lock if package.json changed but yarn.lock isn't
const packageChanged = allFiles.includes('package.json')
const lockfileChanged = allFiles.includes('yarn.lock')
if (packageChanged && !lockfileChanged) {
danger.git
.structuredDiffForFile('package.json')
.then((sdiff) => {
return sdiff?.chunks.every((chunk) => {
return chunk.changes
.filter((change) => {
// filter out changes that are context lines in the diff
return change.type !== 'normal'
})
.every((change) => {
// for every add/del, is the only change to the version?
return change.content.match(/"version":/)
})
})
})
.then((onlyVersionChanges) => {
// If the only thing that changed is the version, it is ok if
// yarn.lock didn't change
if (!onlyVersionChanges) {
const message =
'Changes were made to package.json, but not to yarn.lock'
const idea = 'Perhaps you need to run `yarn install`?'
warn(`${message} - <i>${idea}</i>`)
}
})
}
}
// Check for any changes to the contributors section of package.json
schedule(async () => {
if (!shouldRun) {
return
}
const pd = await danger.git.JSONDiffForFile('package.json')
if (pd.contributors) {
const message = 'Do not make changes to package.json around contributors.'
const idea =
'This project only uses .all-contributorsrc for tracking contributors.'
fail(`${message} - <i>${idea}</i>`)
}
})
// skip these checks if PR is by any bot (e.g. dependabot), if we
// don't have a github object let it run also since we are local
if (shouldRun) {
checkYarnAudit()
checkPrDescription()
checkCodeChanges()
checkDependencyChanges()
}