Created
July 17, 2019 15:19
-
-
Save rodneyrehm/8611730c8461befd0152da54b9e2de32 to your computer and use it in GitHub Desktop.
Sentry Cleanup: Debug Information Files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 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.
@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
Note to self: do not delete - linked to from https://forum.sentry.io/t/cleanup-of-old-dsyms/1939/7?u=rodneyrehm