diff --git a/README.md b/README.md index ed75e95..a37c911 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,7 @@ custom: authToken: my-sentry-api-key release: version: + sourcemaps: true refs: - repository: commit: @@ -244,6 +245,9 @@ custom: - any fixed string - Use a fixed string for the release. Serverless variables are allowed. +* `sourcemaps` - if set to true, upload of sources and sourcemaps to sentry.io based + on files in `.serverless/.zip` + * `refs` - If you have set up Sentry to collect commit data, you can use commit refs to associate your commits with your Sentry releases. Refer to the [Sentry Documentation](https://docs.sentry.io/learn/releases/) for details diff --git a/package-lock.json b/package-lock.json index e10d69b..a959a17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,19 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@sentry/cli": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.41.1.tgz", + "integrity": "sha512-bcsud8ZMNIskmm8COPdyco4qy8VI5YxknRuidqoecyjZ+76IU9dCd8NwJlrYoPNTcM/7gfbRQQFkY2mU/LLrGQ==", + "requires": { + "fs-copy-file-sync": "^1.1.1", + "https-proxy-agent": "^2.2.1", + "mkdirp": "^0.5.1", + "node-fetch": "^2.1.2", + "progress": "2.0.0", + "proxy-from-env": "^1.0.0" + } + }, "acorn": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", @@ -19,6 +32,19 @@ "acorn": "^5.0.3" } }, + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==" + }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, "ajv": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", @@ -128,8 +154,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "bluebird": { "version": "3.5.1", @@ -140,7 +165,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -250,8 +274,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "cookiejar": { "version": "2.1.2", @@ -353,6 +376,19 @@ "is-symbol": "^1.0.1" } }, + "es6-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", + "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -481,6 +517,17 @@ "chardet": "^0.4.0", "iconv-lite": "^0.4.17", "tmp": "^0.0.33" + }, + "dependencies": { + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + } } }, "fast-deep-equal": { @@ -553,11 +600,15 @@ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" }, + "fs-copy-file-sync": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fs-copy-file-sync/-/fs-copy-file-sync-1.1.1.tgz", + "integrity": "sha512-2QY5eeqVv4m2PfyMiEuy9adxNP+ajf+8AR05cEi+OAzPcOj90hvFImeZhTmKLBgSd9EvG33jsD7ZRxsx9dThkQ==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "function-bind": { "version": "1.1.1", @@ -575,7 +626,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -641,6 +691,15 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + } + }, "iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", @@ -666,7 +725,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -854,7 +912,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -862,14 +919,12 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" } @@ -897,6 +952,11 @@ "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", "dev": true }, + "node-fetch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", + "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -913,7 +973,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -950,8 +1009,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -1006,8 +1064,12 @@ "progress": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", - "dev": true + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=" + }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=" }, "punycode": { "version": "2.1.1", @@ -1079,11 +1141,18 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, "requires": { "glob": "^7.0.5" } }, + "rmrf-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/rmrf-promise/-/rmrf-promise-1.1.0.tgz", + "integrity": "sha512-UBhWeBcQWuqCs8YqVahxAPSSSqB/m9bfKSnOjJZVZtmz3AA2gPDGxrGAMyfjBl5uCcYSYa/RpOcIC7MXLKrRSQ==", + "requires": { + "rimraf": "^2.6.1" + } + }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", @@ -1264,12 +1333,34 @@ "dev": true }, "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", "requires": { - "os-tmpdir": "~1.0.2" + "rimraf": "^2.6.3" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + } } }, "type-check": { @@ -1318,8 +1409,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "0.2.1", diff --git a/package.json b/package.json index 3e39cb5..7cdac54 100644 --- a/package.json +++ b/package.json @@ -32,10 +32,14 @@ "eslint": "^5.0.1" }, "dependencies": { + "@sentry/cli": "^1.41.1", + "adm-zip": "^0.4.13", "bluebird": "^3.5.1", "lodash": "^4.17.10", + "rmrf-promise": "^1.1.0", "semver": "^5.5.0", "superagent": "^3.8.3", + "tmp": "^0.1.0", "uuid": "^3.3.2" }, "peerDependencies": { diff --git a/src/index.js b/src/index.js index 4a78eb1..bce4b5e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,16 @@ "use strict"; -const _ = require("lodash") - , BbPromise = require("bluebird") - , SemVer = require("semver") - , uuid = require("uuid/v4") - , request = require("superagent") - , GitRev = require("./git-rev"); +const _ = require("lodash"), + BbPromise = require("bluebird"), + SemVer = require("semver"), + uuid = require("uuid/v4"), + request = require("superagent"), + GitRev = require("./git-rev"), + SentryCli = require("@sentry/cli"), + tmp = require("tmp"), + path = require("path"), + AdmZip = require("adm-zip"), + rmrf = require("rmrf-promise"); /** * Serverless Plugin forward Lambda exceptions to Sentry (https://sentry.io) @@ -30,7 +35,8 @@ class Sentry { "after:deploy:deploy": () => BbPromise.bind(this) .then(this.createSentryRelease) - .then(this.deploySentryRelease), + .then(this.deploySentryRelease) + .then(this.uploadSentryArtifacts), "before:invoke:local:invoke": () => BbPromise.bind(this) .then(this.validate) @@ -150,6 +156,9 @@ class Sentry { if (_.has(sentryConfig, "captureTimeoutWarnings")) { _.set(functionObject, "environment.SENTRY_CAPTURE_TIMEOUTS", String(sentryConfig.captureTimeoutWarnings)); } + if (_.has(sentryConfig, "release.sourcemaps")) { + _.set(functionObject, "environment.SENTRY_SOURCEMAPS", String(sentryConfig.release.sourcemaps)); + } return BbPromise.resolve(functionObject); } @@ -288,6 +297,48 @@ class Sentry { }); } + async uploadSentryArtifacts() { + if ( + !this.sentry.authToken || + !this.sentry.release || + !this.sentry.release.sourcemaps + ) { + // Nothing to do + return BbPromise.resolve(); + } + const tmpdir = tmp.dirSync({ keep: false }); + const zipfile = `${this._serverless.service.service}.zip`; + const zip = new AdmZip( + path.join(this._serverless.config.servicePath, ".serverless", zipfile) + ); + this._serverless.cli.log(`Sentry: extract ${zipfile} to TEMP`); + zip.extractAllTo(tmpdir.name); + const cli = new SentryCli(); + const args = [ + "releases", + "files", + this.sentry.release.version, + "upload-sourcemaps", + `${tmpdir.name}/src`, + "--validate", + "--ignore-file", + path.join(this._serverless.config.servicePath, ".sentryignore"), + "--rewrite", + "--url-prefix", + "~/" + ]; + + try { + await cli.execute(args, true); + this._serverless.cli.log(`Sentry: done`); + } catch (err) { + this._serverless.cli.log(`Sentry: Error: ${err}`); + this._serverless.cli.log(`Sentry: sentry-cli ${args}`); + } + await rmrf(tmpdir.name); + return BbPromise.resolve(); + } + getRandomVersion() { return _.replace(uuid(), /-/g, ""); }