Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rodneyrehm/8611730c8461befd0152da54b9e2de32 to your computer and use it in GitHub Desktop.
Save rodneyrehm/8611730c8461befd0152da54b9e2de32 to your computer and use it in GitHub Desktop.
Sentry Cleanup: Debug Information Files
const axios = require('axios')
const LinkHeader = require('http-link-header')
const nextPage = (header) => {
const link = LinkHeader.parse(header)
const [ next ] = link.get('rel', 'next')
return next && next.results === 'true' ? next.uri : null
}
function uri (strings, ...values) {
const escapeValue = (value, index) => strings[index] + encodeURIComponent(value)
return values.map(escapeValue).join('') + strings[strings.length - 1]
}
// https://docs.sentry.io/api/
class SentryClient {
constructor ( { url, timeout, name, token, org } ) {
if (url.slice(-1) === '/') {
url = url.slice(0, -1)
}
this.org = org
this.client = axios.create({
baseURL: `${url}/api/0`,
timeout: timeout || 30000,
headers: {
'Authorization': `Bearer ${token}`,
'User-Agent': name || 'sentry-client',
},
})
}
async request ({ method, url }) {
try {
const response = await this.client.request({ method, url })
// https://docs.sentry.io/api/pagination/
const next = nextPage(response.headers.link || '')
if (next) {
const _next = await this.request({ method, url: next })
return response.data.concat(_next)
}
return response.data
} catch (error) {
// TODO: properly unpack API errors
if (error.reponse) {
console.error('ERROR processing', method, url)
console.error('HTTP', error.response.status, error.response.statusText)
console.error('BODY', error.response.data)
throw new Error('Nope')
} else {
throw error
}
}
}
// https://docs.sentry.io/api/projects/get-debug-files/
async getDebugFiles({ project }) {
return this.request({
method: 'GET',
url: uri`/projects/${this.org}/${project}/files/dsyms/`,
})
}
// https://docs.sentry.io/api/projects/get-debug-files/
async deleteDebugFile({ project, id }) {
return this.request({
method: 'DELETE',
url: uri`/projects/${this.org}/${project}/files/dsyms/?id=${id}`,
})
}
async getAllDebugFiles({ projects }) {
const promises = projects.map(project => {
return this.getDebugFiles({ project })
})
const filesPerProject = await Promise.all(promises)
const result = []
const now = Date.now()
function addFile (project, item) {
const created = new Date(item.dateCreated)
const since = Math.floor((now - created.getTime()) / 1000 / 3600 / 24)
result.push({ ...item, project, since })
}
projects.forEach((project, index) => {
filesPerProject[index].forEach(item => {
addFile(project, item)
})
})
return result
}
}
// -------------------------------------------------------------
const URL = 'https://my-sentry-instance.example.org'
const ORG = 'my-organization-slug'
// create at https://my-sentry-instance.example.org/settings/account/api/auth-tokens/ requesting these scopes
// event:admin, event:read, member:read, org:read, project:read, project:releases, team:read, project:write, project:admin
// no idea if they're all necessary as API docs don't elaborate on that
const TOKEN = 'my-api-token'
const PROJECTS = [ 'my-project-one', 'my-project-two' ]
// anything older than that will be removed
const DAYS = 180 // 6 months
;(async () => {
const sentry = new SentryClient({
url: URL,
token: TOKEN,
org: ORG,
})
const files = await sentry.getAllDebugFiles(PROJECTS)
const removals = files.filter(item => {
// ignore debug information files that are not old enough
return item.since >= DAYS
}).map(item => {
return sentry.deleteDebugFile({ project: item.project, id: item.id })
})
await Promise.all(removals)
})()
@rodneyrehm
Copy link
Author

Note to self: do not delete - linked to from https://forum.sentry.io/t/cleanup-of-old-dsyms/1939/7?u=rodneyrehm

@yeleleo
Copy link

yeleleo commented Oct 6, 2020

@rodneyrehm seems like it does not work with current version of sentry, getting

(node:24578) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'map' of undefined
    at SentryClient.getAllDebugFiles (.../sentry-clean-debug-information-files.js:75:31)
    at .../sentry-clean-debug-information-files.js:118:30
    at Object.<anonymous> (.../sentry-clean-debug-information-files.js:128:3)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:643:32)
    at Function.Module._load (internal/modules/cjs/loader.js:556:12)
    at Function.Module.runMain (internal/modules/cjs/loader.js:839:10)
    at internal/main/run_main_module.js:17:11
(node:24578) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:24578) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

@Eeems
Copy link

Eeems commented May 6, 2022

@rodneyrehm seems like it does not work with current version of sentry, getting

(node:24578) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'map' of undefined
    at SentryClient.getAllDebugFiles (.../sentry-clean-debug-information-files.js:75:31)
    at .../sentry-clean-debug-information-files.js:118:30
    at Object.<anonymous> (.../sentry-clean-debug-information-files.js:128:3)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:643:32)
    at Function.Module._load (internal/modules/cjs/loader.js:556:12)
    at Function.Module.runMain (internal/modules/cjs/loader.js:839:10)
    at internal/main/run_main_module.js:17:11
(node:24578) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:24578) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Change line 75 to the following:

  async getAllDebugFiles(projects) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment