diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index 1280e9c..f6bc680 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -135,6 +135,35 @@ jobs: cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-runner:latest cache-to: type=inline + # ── ui: source map upload ───────────────────────────────────────────────────── + # Builds the UI with source maps and uploads them to GlitchTip so that error + # stack traces resolve to original .svelte/.ts file names and line numbers. + # Runs in parallel with docker-ui (both need check-ui to pass first). + upload-sourcemaps: + name: Upload source maps + runs-on: ubuntu-latest + needs: [check-ui] + defaults: + run: + working-directory: ui + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + cache-dependency-path: ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build with source maps and upload to GlitchTip + env: + SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }} + BUILD_VERSION: ${{ gitea.ref_name }} + run: npm run build + # ── docker: ui ──────────────────────────────────────────────────────────────── docker-ui: name: Docker / ui @@ -213,7 +242,7 @@ jobs: release: name: Gitea Release runs-on: ubuntu-latest - needs: [docker-backend, docker-runner, docker-ui, docker-caddy] + needs: [docker-backend, docker-runner, docker-ui, docker-caddy, upload-sourcemaps] steps: - uses: actions/checkout@v4 with: diff --git a/ui/package-lock.json b/ui/package-lock.json index b431e22..f2bf992 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -17,6 +17,7 @@ "pocketbase": "^0.26.8" }, "devDependencies": { + "@sentry/vite-plugin": "^5.1.1", "@sveltejs/adapter-auto": "^7.0.0", "@sveltejs/adapter-node": "^5.5.4", "@sveltejs/kit": "^2.50.2", diff --git a/ui/package.json b/ui/package.json index 5a684b8..3dce99a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,6 +12,7 @@ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" }, "devDependencies": { + "@sentry/vite-plugin": "^5.1.1", "@sveltejs/adapter-auto": "^7.0.0", "@sveltejs/adapter-node": "^5.5.4", "@sveltejs/kit": "^2.50.2", diff --git a/ui/src/hooks.client.ts b/ui/src/hooks.client.ts index 00e3ff2..dd9d965 100644 --- a/ui/src/hooks.client.ts +++ b/ui/src/hooks.client.ts @@ -6,7 +6,10 @@ import { env } from '$env/dynamic/public'; if (env.PUBLIC_GLITCHTIP_DSN) { Sentry.init({ dsn: env.PUBLIC_GLITCHTIP_DSN, - tracesSampleRate: 0.1 + tracesSampleRate: 0.1, + // Must match the release name used when uploading source maps in CI + // (BUILD_VERSION injected by Dockerfile as PUBLIC_BUILD_VERSION). + release: env.PUBLIC_BUILD_VERSION || undefined }); } diff --git a/ui/src/hooks.server.ts b/ui/src/hooks.server.ts index 805bc73..0abdb36 100644 --- a/ui/src/hooks.server.ts +++ b/ui/src/hooks.server.ts @@ -13,7 +13,10 @@ import { drain as drainPresignCache } from '$lib/server/presignCache'; if (pubEnv.PUBLIC_GLITCHTIP_DSN) { Sentry.init({ dsn: pubEnv.PUBLIC_GLITCHTIP_DSN, - tracesSampleRate: 0.1 + tracesSampleRate: 0.1, + // Must match the release name used when uploading source maps in CI + // (BUILD_VERSION injected by Dockerfile as PUBLIC_BUILD_VERSION). + release: pubEnv.PUBLIC_BUILD_VERSION || undefined }); } diff --git a/ui/vite.config.ts b/ui/vite.config.ts index bb50a3d..1ade991 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -1,9 +1,36 @@ import { sveltekit } from '@sveltejs/kit/vite'; import tailwindcss from '@tailwindcss/vite'; import { defineConfig } from 'vite'; +import { sentryVitePlugin } from '@sentry/vite-plugin'; +// Source maps are always generated so that the CI pipeline can upload them to +// GlitchTip after a release build. The sentryVitePlugin upload step is only +// active when SENTRY_AUTH_TOKEN is present (i.e. in the release CI job). export default defineConfig({ - plugins: [tailwindcss(), sveltekit()], + build: { + sourcemap: true + }, + plugins: [ + tailwindcss(), + sveltekit(), + sentryVitePlugin({ + org: 'libnovel', + project: 'libnovel-ui', + url: 'https://errors.libnovel.cc/', + // Auth token injected by CI via SENTRY_AUTH_TOKEN env var. + // When the env var is absent the plugin is a no-op. + authToken: process.env.SENTRY_AUTH_TOKEN, + // Release name matches the Docker image tag (e.g. "1.2.3"). + release: { name: process.env.BUILD_VERSION }, + // Don't upload source maps in local dev or CI type-check jobs. + disable: !process.env.SENTRY_AUTH_TOKEN, + // Source maps are uploaded to GlitchTip; do not ship them in the + // production bundle served to browsers. + sourceMapsUploadOptions: { + filesToDeleteAfterUpload: ['./build/**/*.map'] + } + }) + ], ssr: { // Force these packages to be bundled into the server output rather than // treated as external requires. The production Docker image has no