-
-
Notifications
You must be signed in to change notification settings - Fork 10
feat(init): add init command for guided Sentry project setup #283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 21 commits
4d6ddde
8146d8b
57f902f
0c5e440
d60e3b2
3d39f61
11cdf6c
049fc95
1e76a55
077119a
350530f
4e1269f
476bcbc
193a467
bcb10f2
129e7b7
b6c10b7
a8156c0
1d29ebd
ce9614f
3227619
04ae63d
4d7787c
102baa6
51f8968
d5d0b22
1308035
00417d6
d41cbb8
2dd7f47
a8a10a4
b8cddd5
b20089e
441ea88
184dff6
9fb739d
20a4431
068663d
7488e86
2124e99
0f0f61a
07905aa
864fef6
8cfa8f5
241eb0c
a1a1629
b5cce8e
6ca341e
b6b4525
dee9c53
b5bc811
c1461d3
037382b
89e98ca
cae38bb
b5a8c8f
39e4b5a
bc4ca1d
6ffca44
aa04148
d1b5fc8
5313a92
be2e9ad
c69da8b
914995f
af67578
ba6bd73
30eb4e8
4803bbf
1313877
6bdb9ff
32cb8bd
4ab3d5f
61bdb09
7abb381
08a8689
cda7aef
4d4f650
2328b0f
320cf38
b5b8cec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| name: Init Eval | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| platform: | ||
| description: "Platform to evaluate (or 'all')" | ||
| required: true | ||
| default: all | ||
| type: choice | ||
| options: | ||
| - all | ||
| - express | ||
| - nextjs | ||
| - python-fastapi | ||
| - python-flask | ||
| - react-vite | ||
| - sveltekit | ||
|
|
||
| jobs: | ||
| eval: | ||
| name: Eval ${{ matrix.platform }} | ||
| runs-on: ubuntu-latest | ||
| environment: init-eval | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| platform: ${{ inputs.platform == 'all' | ||
| && fromJson('["express","nextjs","python-fastapi","python-flask","react-vite","sveltekit"]') | ||
| || fromJson(format('["{0}"]', inputs.platform)) }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: oven-sh/setup-bun@v2 | ||
| - uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: "3.12" | ||
| - uses: actions/cache@v4 | ||
| id: cache | ||
| with: | ||
| path: node_modules | ||
| key: node-modules-${{ hashFiles('bun.lock', 'patches/**') }} | ||
| - if: steps.cache.outputs.cache-hit != 'true' | ||
| run: bun install --frozen-lockfile | ||
| - name: Run eval | ||
| env: | ||
| MASTRA_API_URL: ${{ secrets.MASTRA_API_URL }} | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| run: bun test ./test/init-eval/${{ matrix.platform }}.eval.test.ts --timeout 600000 | ||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,12 +21,15 @@ | |
| "test:unit": "bun test test/lib test/commands test/types --coverage --coverage-reporter=lcov", | ||
| "test:isolated": "bun test test/isolated", | ||
| "test:e2e": "bun test test/e2e", | ||
| "test:init-eval": "bun test test/init-eval --timeout 600000", | ||
| "generate:skill": "bun run script/generate-skill.ts", | ||
| "check:skill": "bun run script/check-skill.ts", | ||
| "check:deps": "bun run script/check-no-deps.ts" | ||
| }, | ||
| "devDependencies": { | ||
| "@anthropic-ai/sdk": "^0.39.0", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You know I'm not a fan but why not Vercel AI SDK as I assume that's more established? |
||
| "@biomejs/biome": "2.3.8", | ||
| "@mastra/client-js": "^1.4.0", | ||
| "@sentry/api": "^0.1.0", | ||
| "@sentry/bun": "10.39.0", | ||
| "@sentry/esbuild-plugin": "^2.23.0", | ||
|
|
@@ -42,6 +45,7 @@ | |
| "esbuild": "^0.25.0", | ||
| "fast-check": "^4.5.3", | ||
| "ignore": "^7.0.5", | ||
| "openai": "^6.22.0", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this? |
||
| "p-limit": "^7.2.0", | ||
| "pretty-ms": "^9.3.0", | ||
| "qrcode-terminal": "^0.12.0", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| /** | ||
| * sentry init | ||
| * | ||
| * Initialize Sentry in a project using the remote wizard workflow. | ||
| * Communicates with the Mastra API via suspend/resume to perform | ||
| * local filesystem operations and interactive prompts. | ||
| */ | ||
|
|
||
| import path from "node:path"; | ||
| import type { SentryContext } from "../context.js"; | ||
| import { buildCommand } from "../lib/command.js"; | ||
| import { runWizard } from "../lib/init/wizard-runner.js"; | ||
|
|
||
| type InitFlags = { | ||
| readonly force: boolean; | ||
| readonly yes: boolean; | ||
| readonly "dry-run": boolean; | ||
| readonly features?: string; | ||
| }; | ||
|
|
||
| export const initCommand = buildCommand<InitFlags, [string?], SentryContext>({ | ||
| docs: { | ||
| brief: "Initialize Sentry in your project", | ||
| fullDescription: | ||
| "Runs the Sentry setup wizard to detect your project's framework, " + | ||
| "install the SDK, and configure error monitoring. Uses a remote " + | ||
| "workflow that coordinates local file operations through the CLI.", | ||
|
betegon marked this conversation as resolved.
|
||
| }, | ||
| parameters: { | ||
| positional: { | ||
| kind: "tuple", | ||
| parameters: [ | ||
| { | ||
| placeholder: "directory", | ||
| brief: "Project directory (default: current directory)", | ||
| parse: String, | ||
| optional: true, | ||
| }, | ||
| ], | ||
| }, | ||
| flags: { | ||
| force: { | ||
| kind: "boolean", | ||
| brief: "Continue even if Sentry is already installed", | ||
| default: false, | ||
| }, | ||
|
betegon marked this conversation as resolved.
|
||
| yes: { | ||
| kind: "boolean", | ||
| brief: "Non-interactive mode (accept defaults)", | ||
| default: false, | ||
| }, | ||
| "dry-run": { | ||
| kind: "boolean", | ||
| brief: "Preview changes without applying them", | ||
| default: false, | ||
| }, | ||
| features: { | ||
| kind: "parsed", | ||
| parse: String, | ||
| brief: "Comma-separated features: errors,tracing,logs,replay,metrics", | ||
|
betegon marked this conversation as resolved.
|
||
| optional: true, | ||
| placeholder: "list", | ||
| }, | ||
| }, | ||
| aliases: { | ||
| y: "yes", | ||
| }, | ||
| }, | ||
| async func(this: SentryContext, flags: InitFlags, directory?: string) { | ||
| const targetDir = directory ? path.resolve(this.cwd, directory) : this.cwd; | ||
| const featuresList = flags.features | ||
| ?.split(",") | ||
| .map((f) => f.trim()) | ||
| .filter(Boolean); | ||
|
|
||
| await runWizard({ | ||
| directory: targetDir, | ||
| force: flags.force, | ||
| yes: flags.yes, | ||
| dryRun: flags["dry-run"], | ||
| features: featuresList, | ||
| stdout: this.stdout, | ||
| stderr: this.stderr, | ||
| stdin: this.stdin, | ||
| }); | ||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /** | ||
| * Clack Utilities | ||
| * | ||
| * Shared helpers for the clack-based init wizard UI. | ||
| */ | ||
|
|
||
| import { cancel, isCancel } from "@clack/prompts"; | ||
| import { SENTRY_DOCS_URL } from "./constants.js"; | ||
|
|
||
| export class WizardCancelledError extends Error { | ||
| constructor() { | ||
| super("Setup cancelled."); | ||
| this.name = "WizardCancelledError"; | ||
| } | ||
| } | ||
|
|
||
| export function abortIfCancelled<T>(value: T | symbol): T { | ||
| if (isCancel(value)) { | ||
| cancel(`Setup cancelled. Visit ${SENTRY_DOCS_URL} to set up manually.`); | ||
|
betegon marked this conversation as resolved.
|
||
| throw new WizardCancelledError(); | ||
| } | ||
| return value as T; | ||
| } | ||
|
|
||
| export const FEATURE_INFO: Record<string, { label: string; hint: string }> = { | ||
| errorMonitoring: { | ||
| label: "Error Monitoring", | ||
| hint: "Automatic error and crash reporting", | ||
|
betegon marked this conversation as resolved.
|
||
| }, | ||
| performanceMonitoring: { | ||
| label: "Performance Monitoring", | ||
|
betegon marked this conversation as resolved.
|
||
| hint: "Transaction and span tracing", | ||
| }, | ||
| sessionReplay: { | ||
| label: "Session Replay", | ||
| hint: "Visual replay of user sessions", | ||
| }, | ||
| profiling: { | ||
| label: "Profiling", | ||
| hint: "Code-level performance insights", | ||
| }, | ||
| logs: { label: "Logging", hint: "Structured log ingestion" }, | ||
|
betegon marked this conversation as resolved.
|
||
| metrics: { label: "Custom Metrics", hint: "Track custom business metrics" }, | ||
| sourceMaps: { | ||
| label: "Source Maps", | ||
| hint: "See original source code in production errors", | ||
| }, | ||
| }; | ||
|
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| export function featureLabel(id: string): string { | ||
| return FEATURE_INFO[id]?.label ?? id; | ||
| } | ||
|
|
||
| export function featureHint(id: string): string | undefined { | ||
| return FEATURE_INFO[id]?.hint; | ||
| } | ||
|
|
||
| export const STEP_LABELS: Record<string, string> = { | ||
| "discover-context": "Analyzing project structure", | ||
| "select-target-app": "Selecting target application", | ||
| "resolve-dir": "Resolving project directory", | ||
| "check-existing-sentry": "Checking for existing Sentry installation", | ||
| "detect-platform": "Detecting platform and framework", | ||
| "ensure-sentry-project": "Setting up Sentry project", | ||
| "select-features": "Selecting features", | ||
| "determine-pm": "Detecting package manager", | ||
| "install-deps": "Installing dependencies", | ||
| "plan-codemods": "Planning code modifications", | ||
| "apply-codemods": "Applying code modifications", | ||
| "verify-changes": "Verifying changes", | ||
| "add-example-trigger": "Example error trigger", | ||
| "open-sentry-ui": "Finishing up", | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| export const MASTRA_API_URL = | ||
| process.env.SENTRY_WIZARD_API_URL ?? | ||
| "http://sentry-init-agent.getsentry.workers.dev"; | ||
|
|
||
| export const WORKFLOW_ID = "sentry-wizard"; | ||
|
|
||
| export const SENTRY_DOCS_URL = "https://docs.sentry.io/platforms/"; | ||
|
|
||
| export const MAX_FILE_BYTES = 262_144; // 256KB per file | ||
| export const MAX_STDOUT_BYTES = 65_536; // 64KB stdout/stderr truncation | ||
| export const DEFAULT_COMMAND_TIMEOUT_MS = 120_000; // 2 minutes |
Uh oh!
There was an error while loading. Please reload this page.