Skip to content

Commit c09041f

Browse files
committed
fix(publish): fix set repository in commit logs
Try get a valid repository name from sentry api. Default to repository name from package and truncated to 64 characters. It's necessary a token with scope org:read (optional) fix #355
1 parent 344c46b commit c09041f

File tree

6 files changed

+126
-15
lines changed

6 files changed

+126
-15
lines changed

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ The plugin can be configured in the [**semantic-release** configuration file](ht
4242

4343
### Environment variables
4444

45-
| Variable | Description |
46-
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47-
| `SENTRY_AUTH_TOKEN` | The authentication token with permission for releases created in [profile](https://docs.sentry.io/api/auth/#id1) or as [internal integration](https://docs.sentry.io/product/integrations/integration-platform/#internal-integrations) in developer settings of organization. |
48-
| `SENTRY_ORG` | The slug of the organization. |
49-
| `SENTRY_PROJECT` | The slug of the project. |
50-
| `SENTRY_URL` | The URL to use to connect to sentry. This defaults to https://sentry.io/. |
51-
| `SENTRY_ENVIRONMENT` | The environment to specify with this release. This defaults to production. |
52-
| `DEPLOY_START` | Sentry deploy start timestamp. Optional for deploy |
53-
| `DEPLOY_END` | Sentry deploy end timestamp. Optional for deploy |
45+
| Variable | Description |
46+
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47+
| `SENTRY_AUTH_TOKEN` | The authentication token with permission for releases created in [profile](https://docs.sentry.io/api/auth/#id1) or as [internal integration](https://docs.sentry.io/product/integrations/integration-platform/#internal-integrations) in developer settings of organization. Need scopes `release:admin`. Optional `org:read` to get valid repository name from sentry integration |
48+
| `SENTRY_ORG` | The slug of the organization. |
49+
| `SENTRY_PROJECT` | The slug of the project. |
50+
| `SENTRY_URL` | The URL to use to connect to sentry. This defaults to https://sentry.io/. |
51+
| `SENTRY_ENVIRONMENT` | The environment to specify with this release. This defaults to production. |
52+
| `DEPLOY_START` | Sentry deploy start timestamp. Optional for deploy |
53+
| `DEPLOY_END` | Sentry deploy end timestamp. Optional for deploy |
5454

5555
### Options
5656

@@ -59,7 +59,7 @@ The plugin can be configured in the [**semantic-release** configuration file](ht
5959
| `project` | The slug of the project. Optional. Required if not present in environment variables |
6060
| `org` | The slug of the organization. Optional. Required if not present in environment variables |
6161
| `url` | The URL to use to connect to sentry. Optional to set an on-premise instance |
62-
| `repositoryUrl` | A valid repository name for add link to commits of new release. Optional. Ex: 'myorg / myapp'. Default repository property in package.json, or git origin url. |
62+
| `repositoryUrl` | A valid repository name for add link to commits of new release in **sentry format**. Optional. Ex: 'myorg / myapp'. Default repository property in package.json, or git origin url. Try recovery repository name from sentry api https://docs.sentry.io/api/organizations/list-an-organizations-repositories/ |
6363
| `tagsUrl` | A valid url for add link to new release. Optional. Ex: https://github.com/owner/repo/releases/tag/vx.y.z |
6464
| `environment` | Sentry environment. Optional for deploy. Default production |
6565
| `deployName` | Deploy name. Optional for deploy |

src/parse-commits.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const parseCommits = async (pluginConfig, ctx) => {
2525
author_name: commit.author.name,
2626
author_email: commit.author.email,
2727
timestamp: commit.committerDate,
28-
repository: pluginConfig.repositoryUrl || ctx.options.repositoryUrl
28+
repository: pluginConfig.repositoryUrl
2929
}
3030
releaseCommit.patch_set = await getCommitPatchSet(
3131
pluginConfig.pathToGitFolder

src/publish.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ const path = require('path')
22
const getError = require('./get-error')
33
const parseCommits = require('./parse-commits')
44
const getAssets = require('./get-assets')
5-
const { createRelease, createDeploy, uploadSourceFiles } = require('./request')
5+
const {
6+
createRelease,
7+
createDeploy,
8+
uploadSourceFiles,
9+
getRepositoryName
10+
} = require('./request')
611

712
/**
813
* @typedef {import('./types').Context} Context
@@ -51,6 +56,15 @@ module.exports = async (pluginConfig, ctx) => {
5156
try {
5257
const tagsUrl = pluginConfig.tagsUrl || ''
5358
const project = ctx.env.SENTRY_PROJECT || pluginConfig.project
59+
const org = ctx.env.SENTRY_ORG || pluginConfig.org
60+
const url = ctx.env.SENTRY_URL || pluginConfig.url || 'https://sentry.io/'
61+
ctx.logger.log('Retrieving repository name')
62+
pluginConfig.repositoryUrl = await getRepositoryName(
63+
ctx.env.SENTRY_AUTH_TOKEN,
64+
org,
65+
url,
66+
pluginConfig.repositoryUrl || ctx.options.repositoryUrl
67+
)
5468
ctx.logger.log('Retrieving commits data')
5569
const commits = await parseCommits(pluginConfig, ctx)
5670
ctx.logger.log('Commit data retrieved')
@@ -66,8 +80,6 @@ module.exports = async (pluginConfig, ctx) => {
6680
if (tagsUrl !== '') {
6781
releaseDate.url = `${tagsUrl}/v${ctx.nextRelease.version}`
6882
}
69-
const org = ctx.env.SENTRY_ORG || pluginConfig.org
70-
const url = ctx.env.SENTRY_URL || pluginConfig.url || 'https://sentry.io/'
7183
ctx.logger.log('Creating release %s', sentryReleaseVersion)
7284
const release = await createRelease(
7385
releaseDate,

src/request.js

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const getError = require('./get-error')
1313
* @typedef {import('./types').SentryReleaseSuccessResponse} SentryReleaseSuccessResponse
1414
* @typedef {import('./types').SentryOrganizationReleaseFile} SentryOrganizationReleaseFile
1515
* @typedef {import('./types').PATCH_SET_TYPES} PATCH_SET_TYPES
16+
* @typedef {import('./types').SentryOrganizationRepository} SentryOrganizationRepository
1617
* @typedef {import('http').RequestOptions} RequestOptions
1718
*/
1819

@@ -36,6 +37,7 @@ const request = (path, data, token, url) =>
3637
method: 'POST',
3738
headers: {
3839
Authorization: `Bearer ${token}`,
40+
// eslint-disable-next-line sonarjs/no-duplicate-string
3941
'Content-Type': 'application/json',
4042
'Content-Length': Buffer.byteLength(postData)
4143
}
@@ -229,4 +231,68 @@ const uploadSourceFiles = async (files, rewrite, token, org, url, version) => {
229231
}
230232
}
231233

232-
module.exports = { createRelease, createDeploy, verify, uploadSourceFiles }
234+
/**
235+
* @param {string} token -
236+
* @param {string} org -
237+
* @param {string} url -
238+
* @param {string} repositoryUrl -
239+
* @returns {Promise<*>} -
240+
* @example
241+
* await getRepositoryName(token, org, url)
242+
*/
243+
const getRepositoryName = (token, org, url, repositoryUrl) =>
244+
new Promise(resolve => {
245+
const defaultUrl = repositoryUrl.substring(0, 64)
246+
const { hostname, protocol } = new URL(url)
247+
/** @type {RequestOptions} */
248+
const options = {
249+
hostname,
250+
path: `/api/0/organizations/${org}/repos/`,
251+
method: 'GET',
252+
headers: {
253+
Authorization: `Bearer ${token}`,
254+
'Content-Type': 'application/json'
255+
}
256+
}
257+
const client = protocol === 'http:' ? http : https
258+
const req = client.request(options, res => {
259+
/** @type {Array<Buffer>} */
260+
const chunks = []
261+
let totalLength = 0
262+
res.on('data', (/** @type {Buffer} */ chunk) => {
263+
chunks.push(chunk)
264+
totalLength += chunk.length
265+
})
266+
res.on('end', () => {
267+
try {
268+
const raw = Buffer.concat(chunks, totalLength).toString()
269+
/** @type {SentryOrganizationRepository[]} */
270+
const body = JSON.parse(raw || '[{}]')
271+
if (res.statusCode === 200) {
272+
const found = body.find(repository => {
273+
const name = repository.name.replace(/\s/g, '')
274+
// eslint-disable-next-line security/detect-non-literal-regexp
275+
const pattern = new RegExp(`${name}(.git)?$`)
276+
return pattern.test(repositoryUrl)
277+
})
278+
const name = found ? found.name : defaultUrl
279+
resolve(name)
280+
} else {
281+
resolve(defaultUrl)
282+
}
283+
} catch (err) {
284+
resolve(defaultUrl)
285+
}
286+
})
287+
})
288+
req.on('error', () => resolve(defaultUrl))
289+
req.end()
290+
})
291+
292+
module.exports = {
293+
createRelease,
294+
createDeploy,
295+
verify,
296+
uploadSourceFiles,
297+
getRepositoryName
298+
}

src/types.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,19 @@ export interface SentryOrganizationReleaseFile {
125125
name: string
126126
file: string
127127
}
128+
129+
export interface SentryOrganizationRepositoryProvider {
130+
id: string
131+
name: string
132+
}
133+
134+
export interface SentryOrganizationRepository {
135+
id: string
136+
name: string
137+
url: string
138+
provider: SentryOrganizationRepositoryProvider
139+
status: string
140+
dateCreated: string
141+
integrationId: string
142+
externalSlug: number
143+
}

test/publish.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,23 @@ describe('Publish', () => {
7474

7575
beforeEach(() => {
7676
nock.disableNetConnect()
77+
nock(SENTRY_HOST, NOCK_OPTIONS)
78+
.get('/api/0/organizations/valid/repos/')
79+
.reply(200, [
80+
{
81+
id: '1',
82+
name: 'eclass / semantic-release-sentry-releases',
83+
url: 'github.com/eclass/semantic-release-sentry-releases',
84+
provider: {
85+
id: 'integrations:github',
86+
name: 'Github'
87+
},
88+
status: 'active',
89+
dateCreated: '2020-10-30T20:05:57.053153Z',
90+
integrationId: '1',
91+
externalSlug: 1
92+
}
93+
])
7794
nock(SENTRY_HOST, NOCK_OPTIONS)
7895
.post(PATH_RELEASE, body => body.projects.includes('error'))
7996
.reply(400, {

0 commit comments

Comments
 (0)