Skip to content

Commit

Permalink
feat(publish): upload files with api
Browse files Browse the repository at this point in the history
Remove dep @sentry/cli
  • Loading branch information
lgaticaq committed Dec 17, 2020
1 parent 84cb3f2 commit a0374c7
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 57 deletions.
81 changes: 47 additions & 34 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
"homepage": "https://github.com/eclass/semantic-release-sentry-releases#readme",
"dependencies": {
"@semantic-release/error": "^2.1.0",
"@sentry/cli": "^1.55.2",
"aggregate-error": "^3.1.0",
"form-data": "^3.0.0",
"git-diff-tree": "^1.1.0"
},
"devDependencies": {
Expand Down
52 changes: 52 additions & 0 deletions src/get-assets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { promises: fs } = require('fs')
const path = require('path')

/**
* @typedef {import('./types').SentryOrganizationReleaseFile} SentryOrganizationReleaseFile
*/
/**
* @param {string} dir - Path of source files.
* @param {string} urlPrefix - Path of source files.
* @param {boolean} [appendPath=false] -.
* @param {string[]} [extensions=['.js', '.map', '.jsbundle', '.bundle']] - Extension allowed.
* @returns {Promise<SentryOrganizationReleaseFile[]>} - Array of files.
* @example
* const sourcefiles = await getFiles(path.resolve(ctx.cwd, pluginConfig.sourcemaps))
*/
async function getFiles (
dir,
urlPrefix,
appendPath = false,
extensions = ['.js', '.map', '.jsbundle', '.bundle']
) {
// eslint-disable-next-line security/detect-non-literal-fs-filename
const entries = await fs.readdir(dir, { withFileTypes: true })

// Get files within the current directory and add a path key to the file objects
const files = entries
.filter(file => !file.isDirectory())
.filter(file => extensions.includes(path.extname(file.name)))
.map(file => {
const paths = [urlPrefix]
if (appendPath) paths.push(path.basename(dir))
paths.push(file.name)
return { name: path.join(...paths), file: path.resolve(dir, file.name) }
})

// Get folders within the current directory
const folders = entries.filter(folder => folder.isDirectory())

/*
Add the found files within the subdirectory to the files array by calling the
current function itself
*/
for (const folder of folders) {
files.push(
...(await getFiles(path.join(dir, folder.name), urlPrefix, true))
)
}

return files
}

module.exports = getFiles
23 changes: 15 additions & 8 deletions src/publish.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const path = require('path')
const SentryCli = require('@sentry/cli')
const getError = require('./get-error')
const parseCommits = require('./parse-commits')
const { createRelease, createDeploy } = require('./request')
const getAssets = require('./get-assets')
const { createRelease, createDeploy, uploadSourceFiles } = require('./request')

/**
* @typedef {import('./types').Context} Context
Expand Down Expand Up @@ -78,15 +78,22 @@ module.exports = async (pluginConfig, ctx) => {
process.env.SENTRY_ORG = org
process.env.SENTRY_URL = url
process.env.SENTRY_PROJECT = project
const cli = new SentryCli()
if (pluginConfig.sourcemaps && pluginConfig.urlPrefix) {
const sourcemaps = path.resolve(ctx.cwd, pluginConfig.sourcemaps)
ctx.logger.log('Searching sourcemaps in %s', sourcemaps)
const assets = await getAssets(
pluginConfig.sourcemaps,
pluginConfig.urlPrefix
)
ctx.logger.log('Uploading sourcemaps from %s', sourcemaps)
await cli.releases.uploadSourceMaps(sentryReleaseVersion, {
include: [sourcemaps],
urlPrefix: pluginConfig.urlPrefix,
rewrite: pluginConfig.rewrite || false
})
await uploadSourceFiles(
assets,
pluginConfig.rewrite || false,
ctx.env.SENTRY_AUTH_TOKEN,
org,
url,
sentryReleaseVersion
)
ctx.logger.log('Sourcemaps uploaded')
}
const deployData = getDeployData(pluginConfig, ctx)
Expand Down
78 changes: 77 additions & 1 deletion src/request.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const https = require('https')
const http = require('http')
const { URL } = require('url')
const { createReadStream } = require('fs')
const FormData = require('form-data')
const getError = require('./get-error')

/**
Expand All @@ -9,7 +11,9 @@ const getError = require('./get-error')
* @typedef {import('./types').SentryDeploySuccessResponse} SentryDeploySuccessResponse
* @typedef {import('./types').SentryReleaseParams} SentryReleaseParams
* @typedef {import('./types').SentryReleaseSuccessResponse} SentryReleaseSuccessResponse
* @typedef {import('./types').SentryOrganizationReleaseFile} SentryOrganizationReleaseFile
* @typedef {import('./types').PATCH_SET_TYPES} PATCH_SET_TYPES
* @typedef {import('http').RequestOptions} RequestOptions
*/

/**
Expand All @@ -25,6 +29,7 @@ const request = (path, data, token, url) =>
new Promise((resolve, reject) => {
const { hostname, protocol } = new URL(url)
const postData = JSON.stringify(data)
/** @type {RequestOptions} */
const options = {
hostname,
path,
Expand Down Expand Up @@ -60,6 +65,53 @@ const request = (path, data, token, url) =>
req.end()
})

/**
* @param {string} path -
* @param {{name: string, file: string}} data -
* @param {string} token -
* @param {string} url -
* @param {boolean} rewrite -
* @param {string} method -
* @returns {Promise<boolean>} -
* @example
* await uploadFile(path, data, token, url)
*/
const uploadFile = (path, data, token, url, rewrite, method = 'POST') =>
new Promise((resolve, reject) => {
const { hostname, protocol } = new URL(url)
const readStream = createReadStream(data.file)
const form = new FormData()
form.append('file', readStream)
form.append('name', data.name)
/** @type {RequestOptions} */
const options = {
hostname,
path,
method,
headers: {
Authorization: `Bearer ${token}`,
...form.getHeaders()
}
}
const client = protocol === 'http:' ? http : https
const req = client.request(options, res => {
// File already exist or created
if (res.statusCode === 201) {
return resolve(true)
} else if (res.statusCode === 409) {
if (rewrite) {
return uploadFile(path, data, token, url, rewrite, 'PUT')
} else {
return resolve(true)
}
} else {
return resolve(false)
}
})
req.on('error', () => resolve(false))
form.pipe(req)
})

/**
* @param {SentryReleaseParams} data -
* @param {string} token -
Expand Down Expand Up @@ -104,6 +156,7 @@ const createDeploy = (data, token, org, url, version) => {
const verify = (token, org, url, ctx) =>
new Promise((resolve, reject) => {
const { hostname, protocol } = new URL(url)
/** @type {RequestOptions} */
const options = {
hostname,
path: `/api/0/organizations/${org}/releases/`,
Expand Down Expand Up @@ -147,4 +200,27 @@ const verify = (token, org, url, ctx) =>
req.end()
})

module.exports = { createRelease, createDeploy, verify }
/**
* @param {SentryOrganizationReleaseFile[]} files -
* @param {boolean} rewrite -
* @param {string} token -
* @param {string} org -
* @param {string} url -
* @param {string} version -
* @returns {Promise<void>} -
* @example
* await uploadSourceFiles(files, token, org, url, version)
*/
const uploadSourceFiles = async (files, rewrite, token, org, url, version) => {
for (const file of files) {
await uploadFile(
`/api/0/organizations/${org}/releases/${version}/files/`,
file,
token,
url,
rewrite
)
}
}

module.exports = { createRelease, createDeploy, verify, uploadSourceFiles }
Loading

0 comments on commit a0374c7

Please sign in to comment.