diff --git a/packages/playground/build-webpack4.js b/packages/playground/build-webpack4.js index 3114a1f8..a1adb0df 100644 --- a/packages/playground/build-webpack4.js +++ b/packages/playground/build-webpack4.js @@ -18,12 +18,7 @@ webpack4( }, plugins: [ sentryWebpackPlugin({ - org: "sentry-sdks", - project: "someProj", - authToken: "1234", - include: "*", - debugLogging: true, - debug: true, + ...placeHolderOptions, }), ], devtool: "source-map", diff --git a/packages/playground/package.json b/packages/playground/package.json index 88e5265e..07713bb8 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -11,15 +11,19 @@ "build:vite": "vite build --config vite.config.js", "build:webpack4": "node build-webpack4.js", "build:webpack5": "node build-webpack5.js", - "build:esbuild": "node build-esbuild.js" + "build:esbuild": "node build-esbuild.js", + "build:smallNodeApp": "vite build --config vite.config.smallNodeApp.js" }, "dependencies": { "@sentry/unplugin": "*", + "@sentry/node": "^7.11.1", + "@sentry/integrations": "^7.11.1", "esbuild": "0.14.49", "rollup": "2.77.0", "vite": "3.0.0", "webpack4": "npm:webpack@4.46.0", "webpack": "5.74.0", - "npm-run-all": "4.1.5" + "npm-run-all": "4.1.5", + "@sentry/cli": "1.74.5" } } diff --git a/packages/playground/src/smallNodeApp.js b/packages/playground/src/smallNodeApp.js new file mode 100644 index 00000000..88ee728f --- /dev/null +++ b/packages/playground/src/smallNodeApp.js @@ -0,0 +1,24 @@ +const Sentry = require("@sentry/node"); +const { RewriteFrames } = require("@sentry/integrations"); + +Sentry.init({ + dsn: "https://8fa8ac58d94740a69f74934665aa0770@o1151230.ingest.sentry.io/6680403", + debug: true, + enabled: true, + sampleRate: 1.0, + integrations: [new RewriteFrames()], +}); + +const fibonacci = (n) => { + if (n === 3) { + Sentry.captureException(new Error("Test error")); + } + if (n <= 1) { + return n; + } + return fibonacci(n - 1) + fibonacci(n - 2); +}; + +console.log("Hi, I'm a small sample node app"); + +fibonacci(10); diff --git a/packages/playground/vite.config.smallNodeApp.js b/packages/playground/vite.config.smallNodeApp.js new file mode 100644 index 00000000..13e40f6b --- /dev/null +++ b/packages/playground/vite.config.smallNodeApp.js @@ -0,0 +1,30 @@ +// @ts-check +import { sentryVitePlugin } from "@sentry/unplugin"; +import { defineConfig } from "vite"; +import * as path from "path"; + +export default defineConfig({ + build: { + outDir: "./out/vite-smallNodeApp", + lib: { + entry: path.resolve(__dirname, "./src/smallNodeApp.js"), + name: "ExampleBundle", + fileName: "index", + formats: ["cjs"], + }, + sourcemap: true, + minify: true, + }, + plugins: [ + sentryVitePlugin({ + authToken: process.env.SENTRY_AUTH_TOKEN, + org: "lms-testorg-9m", + project: "hackweek-node-sample-app", + debug: true, + debugLogging: true, + release: "0.0.1", + include: "out/vite-smallNodeApp", + cleanArtifacts: true, + }), + ], +}); diff --git a/packages/unplugin/rollup.config.js b/packages/unplugin/rollup.config.js index 4b84dba1..a8da0590 100644 --- a/packages/unplugin/rollup.config.js +++ b/packages/unplugin/rollup.config.js @@ -11,7 +11,7 @@ const extensions = [".js", ".ts"]; export default { input, // external: [...Object.keys(packageJson.dependencies)], - external: ["path", "unplugin"], + external: ["path", "unplugin", "@sentry/cli"], plugins: [ resolve({ extensions, preferBuiltins: true }), commonjs(), diff --git a/packages/unplugin/src/cli.ts b/packages/unplugin/src/cli.ts index e1a5123e..153c1479 100644 --- a/packages/unplugin/src/cli.ts +++ b/packages/unplugin/src/cli.ts @@ -4,7 +4,7 @@ import { Options } from "./types"; /** Creates a new Sentry CLI instance. */ export function makeSentryCli(options: Options) { //TODO: pass config file instead of null - const cli = new SentryCli(undefined, { + const cli = new SentryCli(options.configFile, { silent: false, //TODO read from options org: options.org, project: options.project, diff --git a/packages/unplugin/src/facade.ts b/packages/unplugin/src/facade.ts index 5308ff05..896a7b80 100644 --- a/packages/unplugin/src/facade.ts +++ b/packages/unplugin/src/facade.ts @@ -1,7 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable no-console */ -//TODO: remove eslint rules - // Build a facade that exposes necessary sentry functionality // Idea: We start out with Sentry-CLI and replace the cli-commands one by one afterwards. // Goal: eventually replace everything sentry-cli does with "native" code here @@ -12,57 +8,90 @@ import { makeSentryCli } from "./cli"; import { Options } from "./types"; +import SentryCli from "@sentry/cli"; export type SentryFacade = { - createNewRelease: () => any; - cleanArtifacts: () => any; - uploadSourceMaps: () => any; - setCommits: () => any; - finalizeRelease: () => any; - addDeploy: () => any; + createNewRelease: () => Promise; + cleanArtifacts: () => Promise; + uploadSourceMaps: () => Promise; + setCommits: () => Promise; + finalizeRelease: () => Promise; + addDeploy: () => Promise; }; /** * Factory function that provides all necessary Sentry functionality for creating * a release on Sentry. This includes uploading source maps and finalizing the release */ -export function makeSentryFacade(version: string, options: Options): SentryFacade { - makeSentryCli(options); - //TODO: remove - // void cli.execute(["--version"], true); +export function makeSentryFacade(release: string, options: Options): SentryFacade { + const cli = makeSentryCli(options); return { - createNewRelease: () => createNewRelease(version), - cleanArtifacts: () => cleanArtifacts(), - uploadSourceMaps: () => uploadSourceMaps(version), - setCommits: () => setCommits(version), - finalizeRelease: () => finalizeRelease(version), - addDeploy: () => addDeploy(version), + createNewRelease: () => createNewRelease(cli, release), + cleanArtifacts: () => cleanArtifacts(cli, release, options), + uploadSourceMaps: () => uploadSourceMaps(cli, release, options), + setCommits: () => setCommits(/* release */), + finalizeRelease: () => finalizeRelease(cli, release, options), + addDeploy: () => addDeploy(/* release */), }; } -function createNewRelease(version: string) { - //TODO(must have): implement release creation logic here +async function createNewRelease(cli: SentryCli, release: string): Promise { + return cli.releases.new(release); } -function uploadSourceMaps(version: string) { - //TODO(must have): implement source maps upload logic here -} +async function uploadSourceMaps( + cli: SentryCli, + release: string, + options: Options +): Promise { + /** + * One or more paths to ignore during upload. Overrides entries in ignoreFile file. + */ + + const { + include, + // ignore, + // ignoreFile, + // rewrite, + // sourceMapReference, + // stripPrefix, + // stripCommonPrefix, + // validate, + // urlPrefix, + // urlSuffix, + // ext, + } = options; + + //TODO: sort out mess between Sentry CLI options and WebPack plugin options (ideally, + // we normalize everything before and don't diverge with options between our + // own CLI implementation and the plugin. + // I don't want to do too much for this right now b/c we'll eventually get rid of the CLI anyway + const uploadSourceMapsOptions = { include: typeof include === "string" ? [include] : include }; -function finalizeRelease(version: string) { - //TODO(must have): implement release finalization logic here + return cli.releases.uploadSourceMaps(release, uploadSourceMapsOptions); } -// TODO: Stuff we worry about later: +async function finalizeRelease(cli: SentryCli, release: string, options: Options): Promise { + if (options.finalize) { + return cli.releases.finalize(release); + } + return Promise.resolve("nothing to do here"); +} -function cleanArtifacts() { - // NOOP for now +async function cleanArtifacts(cli: SentryCli, release: string, options: Options): Promise { + if (options.cleanArtifacts) { + return cli.releases.execute(["releases", "files", release, "delete", "--all"], true); + } + return Promise.resolve("nothing to do here"); } -function setCommits(version: string) { - // NOOP for now +// TODO: Stuff we worry about later: + +async function setCommits(/* version: string */): Promise { + return Promise.resolve("Noop"); } -function addDeploy(version: string) { - // NOOP for now +async function addDeploy(/* version: string */): Promise { + return Promise.resolve("Noop"); } diff --git a/packages/unplugin/src/index.ts b/packages/unplugin/src/index.ts index f521e313..95e8237d 100644 --- a/packages/unplugin/src/index.ts +++ b/packages/unplugin/src/index.ts @@ -4,6 +4,15 @@ import { getReleaseName } from "./getReleaseName"; import { Options } from "./types"; import { makeSentryFacade } from "./facade"; +const defaultOptions: Omit = { + //TODO: add default options here as we port over options from the webpack plugin + // validate: false + configFile: "~/.sentryclirc", + debug: false, + cleanArtifacts: false, + finalize: true, +}; + /** * The sentry-unplugin concerns itself with two things: * - Release injection @@ -67,7 +76,9 @@ import { makeSentryFacade } from "./facade"; * The sentry-unplugin will also take care of uploading source maps to Sentry. This is all done in the `buildEnd` hook. * TODO: elaborate a bit on how sourcemaps upload works */ -const unplugin = createUnplugin((options, unpluginMetaContext) => { +const unplugin = createUnplugin((originalOptions, unpluginMetaContext) => { + const options = { ...defaultOptions, ...originalOptions }; + function debugLog(...args: unknown[]) { if (options?.debugLogging) { // eslint-disable-next-line no-console @@ -162,9 +173,27 @@ const unplugin = createUnplugin((options, unpluginMetaContext) => { } }, buildEnd() { - const sentryFacade = makeSentryFacade(getReleaseName(options.release), options); - //TODO: do stuff with the facade here lol - debugLog("this is my facade:", sentryFacade); + const release = getReleaseName(options.release); + //TODO: + // 1. validate options to see if we get a valid include property, release name, etc. + // 2. normalize the include property: Users can pass string | string [] | IncludeEntry[]. + // That's good for them but a hassle for us. Let's try to normalize this into one data type + // (I vote IncludeEntry[]) and continue with that down the line + + const sentryFacade = makeSentryFacade(release, options); + + sentryFacade + .createNewRelease() + .then(() => sentryFacade.cleanArtifacts()) + .then(() => sentryFacade.uploadSourceMaps()) + .then(() => sentryFacade.setCommits()) // this is a noop for now + .then(() => sentryFacade.finalizeRelease()) + .then(() => sentryFacade.addDeploy()) // this is a noop for now + .catch((e) => { + //TODO: invoke error handler here + // https://github.com/getsentry/sentry-webpack-plugin/blob/137503f3ac6fe423b16c5c50379859c86e689017/src/index.js#L540-L547 + debugLog(e); + }); }, }; }); diff --git a/packages/unplugin/src/types.ts b/packages/unplugin/src/types.ts index d4041e28..99ba7d60 100644 --- a/packages/unplugin/src/types.ts +++ b/packages/unplugin/src/types.ts @@ -1,4 +1,5 @@ //TODO: JsDoc for all properties +//TODO: compare types w/ webpack plugin (and sentry-cli?) export type Options = { debugLogging?: boolean; @@ -7,16 +8,16 @@ export type Options = { project?: string; authToken?: string; url?: string; - // configFile: string + configFile?: string; /* --- release properties: */ release?: string; // dist: string, // entries: string[] | RegExp | ((key: string) => boolean); + finalize?: boolean; /* --- source maps properties: */ - //TODO: make this required - include?: string; //| string[] | IncludeEntry[]; + include: string; // | Array; // ignoreFile: string // ignore: string | string[] // ext: string[] @@ -32,11 +33,10 @@ export type Options = { // vcsRemote: string, // customHeader: string, - // finalize?: boolean, // dryRun?: boolean, debug?: boolean; // silent?: boolean, - // cleanArtifacts?: boolean, + cleanArtifacts?: boolean; // errorHandler?: (err: Error, invokeErr: function(): void, compilation: unknown) => void, // setCommits?: { // repo?: string, diff --git a/yarn.lock b/yarn.lock index 0927cbd4..2e7ec4d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2073,6 +2073,62 @@ proxy-from-env "^1.1.0" which "^2.0.2" +"@sentry/core@7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.11.1.tgz#d68e796f3b6428aefd6086a1db00118df7a9a9e4" + integrity sha512-kaDSZ6VNuO4ZZdqUOOX6XM6x+kjo2bMnDQ3IJG51FPvVjr8lXYhXj1Ccxcot3pBYAIWPPby2+vNDOXllmXqoBA== + dependencies: + "@sentry/hub" "7.11.1" + "@sentry/types" "7.11.1" + "@sentry/utils" "7.11.1" + tslib "^1.9.3" + +"@sentry/hub@7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.11.1.tgz#1749b2b102ea1892ff388d65d66d3b402b393958" + integrity sha512-M6ClgdXdptS0lUBKB5KpXXe2qMQhsoiEN2pEGRI6+auqhfHCUQB1ZXsfjiOYexKC9fwx7TyFyZ9Jcaf2DTxEhw== + dependencies: + "@sentry/types" "7.11.1" + "@sentry/utils" "7.11.1" + tslib "^1.9.3" + +"@sentry/integrations@^7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.11.1.tgz#fa7b648fd479b9b8b8558f5b23c8c51a066737f5" + integrity sha512-G4aw9X2WdRGwLk/2pAj+5LuZnLM4u1GG3o8bOWNASR9E7IiQQ9ERYlnfW7jas+08B1Q61WLwJPXZhJxvQfxLQw== + dependencies: + "@sentry/types" "7.11.1" + "@sentry/utils" "7.11.1" + localforage "^1.8.1" + tslib "^1.9.3" + +"@sentry/node@^7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.11.1.tgz#97fd26de26e8203a3c34e26b38f3c2a5ba46828b" + integrity sha512-EAAHou/eHSzwRK0Z5qnQiwXNbkpnjWjloaG979gftA+MS/kM0AxQHdOrSJQbOEaqRf3F7/eC4Hj+1tfglAuaLQ== + dependencies: + "@sentry/core" "7.11.1" + "@sentry/hub" "7.11.1" + "@sentry/types" "7.11.1" + "@sentry/utils" "7.11.1" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/types@7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.11.1.tgz#06e2827f6ba37159c33644208a0453b86d25e232" + integrity sha512-gIEhOPxC2cjrxQ0+K2SFJ1P6e/an5osSxVc9OOtekN28eHtVsXFCLB8XVWeNQnS7N2VkrVrkqORMBz1kvIcvVQ== + +"@sentry/utils@7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.11.1.tgz#1635c5b223369d9428bc83c9b8908c9c3287ee10" + integrity sha512-tRVXNT5O9ilkV31pyHeTqA1PcPQfMV/2OR6yUYM4ah+QVISovC0f0ybhByuH5nYg6x/Gsnx1o7pc8L1GE3+O7A== + dependencies: + "@sentry/types" "7.11.1" + tslib "^1.9.3" + "@sinclair/typebox@^0.23.3": version "0.23.5" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d" @@ -3929,6 +3985,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +cookie@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -5799,6 +5860,11 @@ ignore@^5.1.4, ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -6902,6 +6968,13 @@ libnpmpublish@^1.1.1: semver "^5.5.1" ssri "^6.0.1" +lie@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw== + dependencies: + immediate "~3.0.5" + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -6947,6 +7020,13 @@ loader-utils@^1.2.3: emojis-list "^3.0.0" json5 "^1.0.1" +localforage@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" + integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== + dependencies: + lie "3.1.1" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -7064,6 +7144,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== + macos-release@^2.2.0: version "2.5.0" resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2" @@ -9889,7 +9974,7 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==