Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link
Owner Author

commented Jul 17, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.