diff --git a/packages/playground/vite.config.smallNodeApp.js b/packages/playground/vite.config.smallNodeApp.js index 13e40f6b..95aa4e9e 100644 --- a/packages/playground/vite.config.smallNodeApp.js +++ b/packages/playground/vite.config.smallNodeApp.js @@ -22,7 +22,7 @@ export default defineConfig({ project: "hackweek-node-sample-app", debug: true, debugLogging: true, - release: "0.0.1", + release: "0.0.6", include: "out/vite-smallNodeApp", cleanArtifacts: true, }), diff --git a/packages/unplugin/package.json b/packages/unplugin/package.json index ca0057ef..a50e9517 100644 --- a/packages/unplugin/package.json +++ b/packages/unplugin/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@sentry/cli": "1.74.5", + "axios": "^0.27.2", "magic-string": "0.26.2", "unplugin": "0.9.4" }, diff --git a/packages/unplugin/src/index.ts b/packages/unplugin/src/index.ts index 95e8237d..a52f811b 100644 --- a/packages/unplugin/src/index.ts +++ b/packages/unplugin/src/index.ts @@ -2,7 +2,7 @@ import { createUnplugin } from "unplugin"; import MagicString from "magic-string"; import { getReleaseName } from "./getReleaseName"; import { Options } from "./types"; -import { makeSentryFacade } from "./facade"; +import { makeSentryFacade } from "./sentry/facade"; const defaultOptions: Omit = { //TODO: add default options here as we port over options from the webpack plugin @@ -11,6 +11,7 @@ const defaultOptions: Omit = { debug: false, cleanArtifacts: false, finalize: true, + url: "https://sentry.io", }; /** diff --git a/packages/unplugin/src/sentry/api.ts b/packages/unplugin/src/sentry/api.ts new file mode 100644 index 00000000..02577bc5 --- /dev/null +++ b/packages/unplugin/src/sentry/api.ts @@ -0,0 +1,68 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ + +import axios from "axios"; +import { AxiosError } from "axios"; +import { Options } from "../types"; + +const API_PATH = "api/0"; + +type SentryRequestOptions = Pick & { + method: string; + endpoint: string; + payload: unknown; +}; + +/** + * Generic function to call to make the actual request. + * Currently takes care of adding headers. + * Happy to change this to something more sophisticated. + */ +async function makeRequest(requestOptions: SentryRequestOptions) { + const { authToken, url, endpoint, method, payload } = requestOptions; + + if (!authToken || !url || !endpoint) { + return Promise.reject(); + } + + const response = await axios({ + method, + url: `${url}/${API_PATH}/${endpoint}`, + data: payload, + headers: { Authorization: `Bearer ${authToken}`, "User-Agent": "sentry-unplugin" }, + }).catch((error: AxiosError) => { + const msg = `Error: ${error.message}`; + throw new Error(msg); + }); + + return response; +} + +/* Just a wrapper to make POST requests */ +async function makePostRequest(requestOptions: Omit) { + return makeRequest({ ...requestOptions, method: "POST" }); +} + +export async function makeNewReleaseRequest(release: string, options: Options): Promise { + const releasePayload = { + version: release, + projects: [options.project], + dateStarted: new Date(), + dateReleased: new Date(), //TODO: figure out if these dates are set correctly + }; + + const orgSlug = options.org || ""; + const projectSlug = options.project || ""; + + // using the legacy endpoint here because the sentry webpack plugin only associates one project + // with the release. If we ever wanna support multiple projects in the unplugin, + // take a look at how sentry/cli calls the new endpoint: + // https://github.com/getsentry/sentry-cli/blob/4fa813549cd249e77ae6ba974d76e606a19f48de/src/api.rs#L769-L773 + const response = await makePostRequest({ + authToken: options.authToken, + url: options.url, + endpoint: `projects/${orgSlug}/${projectSlug}/releases/`, + payload: releasePayload, + }); + + return response.status.toString(); +} diff --git a/packages/unplugin/src/cli.ts b/packages/unplugin/src/sentry/cli.ts similarity index 98% rename from packages/unplugin/src/cli.ts rename to packages/unplugin/src/sentry/cli.ts index 153c1479..480adb78 100644 --- a/packages/unplugin/src/cli.ts +++ b/packages/unplugin/src/sentry/cli.ts @@ -1,5 +1,5 @@ import SentryCli from "@sentry/cli"; -import { Options } from "./types"; +import { Options } from "../types"; /** Creates a new Sentry CLI instance. */ export function makeSentryCli(options: Options) { diff --git a/packages/unplugin/src/facade.ts b/packages/unplugin/src/sentry/facade.ts similarity index 91% rename from packages/unplugin/src/facade.ts rename to packages/unplugin/src/sentry/facade.ts index 896a7b80..ef0f27f7 100644 --- a/packages/unplugin/src/facade.ts +++ b/packages/unplugin/src/sentry/facade.ts @@ -7,8 +7,9 @@ // - unnecessary functionality import { makeSentryCli } from "./cli"; -import { Options } from "./types"; +import { Options } from "../types"; import SentryCli from "@sentry/cli"; +import { makeNewReleaseRequest } from "./api"; export type SentryFacade = { createNewRelease: () => Promise; @@ -27,7 +28,7 @@ export function makeSentryFacade(release: string, options: Options): SentryFacad const cli = makeSentryCli(options); return { - createNewRelease: () => createNewRelease(cli, release), + createNewRelease: () => createNewRelease(release, options), cleanArtifacts: () => cleanArtifacts(cli, release, options), uploadSourceMaps: () => uploadSourceMaps(cli, release, options), setCommits: () => setCommits(/* release */), @@ -36,8 +37,8 @@ export function makeSentryFacade(release: string, options: Options): SentryFacad }; } -async function createNewRelease(cli: SentryCli, release: string): Promise { - return cli.releases.new(release); +async function createNewRelease(release: string, options: Options): Promise { + return makeNewReleaseRequest(release, options); } async function uploadSourceMaps( diff --git a/yarn.lock b/yarn.lock index 075ab4bd..8d276baf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3250,6 +3250,14 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + babel-jest@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" @@ -3935,7 +3943,7 @@ columnify@^1.5.4: strip-ansi "^6.0.1" wcwidth "^1.0.0" -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -5393,6 +5401,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +follow-redirects@^1.14.9: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -5403,6 +5416,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"