forked from getsentry/sentry-netlify-build-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
177 lines (154 loc) · 6.5 KB
/
index.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
/*
The Sentry Netlify build plugin:
- Notifies Sentry of new releases being deployed.
- Uploads source maps to Sentry.
- Sends Sentry the commit SHA of HEAD to enable commit features.
*/
const fs = require('fs')
const path = require('path')
const SentryCli = require('@sentry/cli')
const { promisify, inspect } = require('util')
const { version } = require('./package.json');
const writeFile = promisify(fs.writeFile)
const deleteFile = promisify(fs.unlink)
const CWD = path.resolve(process.cwd())
const SENTRY_CONFIG_PATH = path.resolve(CWD, '.sentryclirc')
const DEFAULT_SOURCE_MAP_URL_PREFIX = "~/"
const DEFAULT_DELETE_SOURCE_MAPS = false
module.exports = {
onPostBuild: async (pluginApi) => {
const { constants, inputs, utils } = pluginApi
const { PUBLISH_DIR, IS_LOCAL } = constants
const RUNNING_IN_NETLIFY = !IS_LOCAL
const IS_PREVIEW = process.env.CONTEXT == 'deploy-preview'
/* Set the user input settings */
const sentryUrl = process.env.SENTRY_URL || inputs.sentryUrl;
const sentryOrg = process.env.SENTRY_ORG || inputs.sentryOrg
const sentryProject = process.env.SENTRY_PROJECT || inputs.sentryProject
const sentryAuthToken = process.env.SENTRY_AUTH_TOKEN || inputs.sentryAuthToken
const sentryRelease = process.env.SENTRY_RELEASE || inputs.sentryRelease || process.env.COMMIT_REF
const releasePrefix = process.env.SENTRY_RELEASE_PREFIX || inputs.releasePrefix || ''
const sentryEnvironment = process.env.SENTRY_ENVIRONMENT || process.env.CONTEXT
const sentryRepository = process.env.SENTRY_REPOSITORY || inputs.sentryRepository
const sourceMapPath = inputs.sourceMapPath || PUBLISH_DIR
const sourceMapUrlPrefix = inputs.sourceMapUrlPrefix || DEFAULT_SOURCE_MAP_URL_PREFIX
const shouldDeleteSourceMaps = process.env.SENTRY_DELETE_SOURCE_MAPS || inputs.sentryDeleteSourceMaps || DEFAULT_DELETE_SOURCE_MAPS
if (RUNNING_IN_NETLIFY) {
if (IS_PREVIEW && !inputs.deployPreviews) {
console.log('Skipping Sentry release creation - Deploy Preview')
return
}
if (!sentryAuthToken) {
return utils.build.failBuild('SentryCLI needs an authentication token. Please set env variable SENTRY_AUTH_TOKEN')
} else if (!sentryOrg) {
return utils.build.failBuild('SentryCLI needs the organization slug. Please set env variable SENTRY_ORG or set sentryOrg as a plugin input')
} else if (!sentryProject) {
return utils.build.failBuild('SentryCLI needs the project slug. Please set env variable SENTRY_PROJECT or set sentryProject as a plugin input')
}
await createSentryConfig({ sentryOrg, sentryProject, sentryAuthToken, sentryUrl })
/* Apply release prefix */
const release = `${releasePrefix}${sentryRelease}`
/* Notify Sentry of release being deployed on Netlify */
await createSentryRelease({
pluginApi,
release,
sentryEnvironment,
sentryRepository,
sourceMapPath,
sourceMapUrlPrefix
})
console.log()
console.log('Successfully notified Sentry of deployment!')
console.log()
await deleteSentryConfig()
if (!shouldDeleteSourceMaps) {
console.log('Source map files retained.')
} else {
console.log('Removing source map files.')
await new Promise(resolve => {
const glob = require('glob');
glob(`${sourceMapPath}/**/*.map`, (er, files) => {
if (!er && files) {
Promise
.all(files
.map(file => deleteFile(file)))
.catch(e => console.log('error deleting source map files', e))
.finally(() => resolve(true));
} else {
console.log('could not find source map files', er);
resolve(false);
}
})
})
}
}
}
}
async function createSentryRelease({ pluginApi, release, sentryEnvironment, sentryRepository, sourceMapPath, sourceMapUrlPrefix }) {
// default config file is read from ~/.sentryclirc
const { constants, inputs, utils } = pluginApi
const cli = new SentryCli()
console.log('Creating new release with version: ', release)
// https://docs.sentry.io/cli/releases/#creating-releases
try {
await cli.releases.new(release)
} catch (error) {
if (error.message.includes("401")) {
return utils.build.failBuild("SentryCLI failed to create a new release. Invalid Sentry authentication token. (http status: 401)")
} else if (error.message.includes("403")) {
return utils.build.failBuild("SentryCLI failed to create a new release. You do not have permission to perform this action. (http status: 403)")
} else {
console.log(error)
return utils.build.failBuild("SentryCLI failed to create a new release.")
}
}
// https://docs.sentry.io/cli/releases/#managing-release-artifacts
if (!inputs.skipSourceMaps) {
await cli.releases.uploadSourceMaps(release, {
debug: false,
include: [sourceMapPath],
urlPrefix: sourceMapUrlPrefix,
rewrite: true,
ignore: ['node_modules']
})
}
// https://docs.sentry.io/cli/releases/#sentry-cli-commit-integration
if (!inputs.skipSetCommits) {
const repository = sentryRepository || process.env.REPOSITORY_URL.split(/[:/]/).slice(-2).join('/')
try {
await cli.releases.setCommits(release, {
repo: repository,
commit: process.env.COMMIT_REF
})
} catch (error) {
console.log(error)
return utils.build.failBuild(
`SentryCLI failed to set commits. You likely need to set up a repository or repository integration.
Read more: https://docs.sentry.io/workflow/releases/?platform=python#install-repo-integration`
)
}
}
// https://docs.sentry.io/cli/releases/#finalizing-releases
await cli.releases.finalize(release)
// https://docs.sentry.io/cli/releases/#creating-deploys
await cli.releases.execute(['releases', 'deploys', release, 'new', '-e', sentryEnvironment])
}
async function createSentryConfig({ sentryOrg, sentryProject, sentryAuthToken, sentryUrl }) {
const sentryConfigFile = `
[auth]
token=${sentryAuthToken}
[defaults]
${sentryUrl ? `url=${sentryUrl}` : ''}
project=${sentryProject}
org=${sentryOrg}
pipeline=netlify-build-plugin/${version}
`
await writeFile(SENTRY_CONFIG_PATH, sentryConfigFile, { flag: 'w+' })
}
async function deleteSentryConfig() {
await deleteFile(SENTRY_CONFIG_PATH)
}
function fileExists(s) {
// eslint-disable-next-line
return new Promise( r => fs.access(s, fs.F_OK, e => r(!e)))
}