From 1a585106cdc29d0ade6cafa2216aa20c52f78edd Mon Sep 17 00:00:00 2001 From: Kevin Williams Date: Wed, 14 Oct 2026 16:49:31 -0400 Subject: [PATCH 1/4] fix: Stop shipping fallback copies of host modules The plugin bundled its own copy of every library it shares with the portal (react-router, react-query and the curated datum-ui subset). The host's copy always wins, yet the browser fetched and parsed each fallback on every load: about 300KB gzipped, 516KB of it unminified react-router source, for the project home column alone. Production builds now mark the host-provided modules import: false. Dev builds keep the fallbacks so the standalone preview runs without a host. react/jsx-runtime gets an explicit entry that keeps its fallback. Left implicit it inherits import: false from react, but the portal does not share that subpath, so the plugin would throw "must be provided by host" on load. Co-Authored-By: Claude --- ui/consumer/vite.config.ts | 172 ++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 78 deletions(-) diff --git a/ui/consumer/vite.config.ts b/ui/consumer/vite.config.ts index 322dceb7..73f22054 100644 --- a/ui/consumer/vite.config.ts +++ b/ui/consumer/vite.config.ts @@ -79,84 +79,100 @@ function consoleWasm(): Plugin { // browser never contacts this origin directly. MF's automatic publicPath makes // federated chunks resolve relative to remoteEntry.js, which is what lets them // load correctly through that same-origin proxy prefix. -export default defineConfig({ - server: { - port: 7778, - strictPort: true, - // Allow cross-origin fetches of the manifest/remote during Tier 0/standalone. - cors: true, - }, - preview: { - port: 7778, - // Bind IPv4 explicitly — "localhost"-only binding resolves to IPv6-only - // ([::1]) on some hosts, and the portal's server-side manifest fetch to - // http://localhost:7778 then gets ECONNREFUSED on IPv4, silently dropping - // the plugin out of the registry with no visible error. - host: '127.0.0.1', - strictPort: true, - cors: true, - }, - build: { - target: 'esnext', - // Keep the plugin readable when inspecting the built bundle. - minify: false, - }, - experimental: { - renderBuiltUrl(_filename, { hostType }) { - return hostType === 'js' ? { relative: true } : undefined; +export default defineConfig(({ command }) => { + const hostShared = (requiredVersion: string | false) => ({ + singleton: true, + requiredVersion, + ...(command === 'build' ? { import: false as const } : {}), + }); + + return { + server: { + port: 7778, + strictPort: true, + // Allow cross-origin fetches of the manifest/remote during Tier 0/standalone. + cors: true, }, - }, - plugins: [ - react(), - consoleWasm(), - federation({ - // MUST equal the manifest `name` — the host keys the remote by this id. - name: 'workload.compute.datumapis.com', - // The manifest's `remoteEntry` field points the host at this filename, - // requested through the asset proxy as /api/plugins/compute/remoteEntry.js. - filename: 'remoteEntry.js', - manifest: true, - // Exposed keys map 1:1 to the manifest's `exposedModules` keys / $codeRefs. - // The host loads e.g. loadRemote('workload.compute.datumapis.com/WorkloadList'). - exposes: { - './WorkloadList': './src/pages/workload-list.tsx', - './WorkloadDetail': './src/pages/workload-detail.tsx', - './InstanceDetail': './src/pages/instance-detail.tsx', - './InstanceShellWindow': './src/pages/instance-shell-window.tsx', - './WorkloadsHomeColumn': './src/cards/workloads-home-column.tsx', - }, - // Host-pinned singletons. requiredVersion tracks the host's majors - // (react 19, react-router 8, react-query 5) — cloud-portal moved - // react-router 7 -> 8 in fabef049 without bumping this, which silently - // broke every plugin page here (Module Federation rejects a singleton - // whose requiredVersion the host's actual version doesn't satisfy). - // Keep this in lockstep with cloud-portal's package.json majors. - // singleton:true guarantees one instance — the host provides all of - // these, so plugin queries share the host's QueryClient cache. - shared: { - react: { singleton: true, requiredVersion: '^19.0.0' }, - 'react-dom': { singleton: true, requiredVersion: '^19.0.0' }, - 'react-router': { singleton: true, requiredVersion: '^8.0.0' }, - '@tanstack/react-query': { singleton: true, requiredVersion: '^5.0.0' }, - // Curated datum-ui subset shared by the host (see the host's - // federation-host.ts DATUM_UI_SHARED). requiredVersion:false — the - // host's copy always wins, which is what keeps styling identical to - // built-in pages; the local install is types + standalone fallback. - '@datum-cloud/datum-ui/badge': { singleton: true, requiredVersion: false }, - '@datum-cloud/datum-ui/button': { singleton: true, requiredVersion: false }, - '@datum-cloud/datum-ui/card': { singleton: true, requiredVersion: false }, - '@datum-cloud/datum-ui/empty-content': { singleton: true, requiredVersion: false }, - '@datum-cloud/datum-ui/icons': { singleton: true, requiredVersion: false }, - // Do not share `logs`: MF colocates lucide-react / date-fns into the - // logs loadShare chunk, so a host-provided logs module replaces those - // exports and crashes WorkloadDetail / related pages. - // `select` is shared by the host (see cloud-portal `federation-host.ts`) so - // the overview range control renders the same Select as ALB. - '@datum-cloud/datum-ui/select': { singleton: true, requiredVersion: false }, - '@datum-cloud/datum-ui/separator': { singleton: true, requiredVersion: false }, - '@datum-cloud/datum-ui/skeleton': { singleton: true, requiredVersion: false }, - '@datum-cloud/datum-ui/table': { singleton: true, requiredVersion: false }, + preview: { + port: 7778, + // Bind IPv4 explicitly — "localhost"-only binding resolves to IPv6-only + // ([::1]) on some hosts, and the portal's server-side manifest fetch to + // http://localhost:7778 then gets ECONNREFUSED on IPv4, silently dropping + // the plugin out of the registry with no visible error. + host: '127.0.0.1', + strictPort: true, + cors: true, + }, + build: { + target: 'esnext', + // Keep the plugin readable when inspecting the built bundle. + minify: false, + }, + experimental: { + renderBuiltUrl(_filename, { hostType }) { + return hostType === 'js' ? { relative: true } : undefined; }, - }), - ], + }, + plugins: [ + react(), + consoleWasm(), + federation({ + // MUST equal the manifest `name` — the host keys the remote by this id. + name: 'workload.compute.datumapis.com', + // The manifest's `remoteEntry` field points the host at this filename, + // requested through the asset proxy as /api/plugins/compute/remoteEntry.js. + filename: 'remoteEntry.js', + manifest: true, + // Exposed keys map 1:1 to the manifest's `exposedModules` keys / $codeRefs. + // The host loads e.g. loadRemote('workload.compute.datumapis.com/WorkloadList'). + exposes: { + './WorkloadList': './src/pages/workload-list.tsx', + './WorkloadDetail': './src/pages/workload-detail.tsx', + './InstanceDetail': './src/pages/instance-detail.tsx', + './InstanceShellWindow': './src/pages/instance-shell-window.tsx', + './WorkloadsHomeColumn': './src/cards/workloads-home-column.tsx', + }, + // Host-pinned singletons. requiredVersion tracks the host's majors + // (react 19, react-router 8, react-query 5) — cloud-portal moved + // react-router 7 -> 8 in fabef049 without bumping this, which silently + // broke every plugin page here (Module Federation rejects a singleton + // whose requiredVersion the host's actual version doesn't satisfy). + // Keep this in lockstep with cloud-portal's package.json majors. + // singleton:true guarantees one instance — the host provides all of + // these, so plugin queries share the host's QueryClient cache. + // Builds rely on the host for these (import: false), so they bundle no + // fallback copy: each was its own chunk, about 300KB gzipped in all, + // fetched and parsed on every load though the host's copy always wins. + // Dev keeps the fallbacks so the standalone preview runs without a host. + shared: { + react: hostShared('^19.0.0'), + // The host does not share this subpath, so it keeps its (small) + // fallback; left implicit it would inherit import: false from + // `react` and throw "must be provided by host". + 'react/jsx-runtime': { singleton: true, requiredVersion: '^19.0.0' }, + 'react-dom': hostShared('^19.0.0'), + 'react-router': hostShared('^8.0.0'), + '@tanstack/react-query': hostShared('^5.0.0'), + // Curated datum-ui subset shared by the host (see the host's + // federation-host.ts DATUM_UI_SHARED). requiredVersion:false — the + // host's copy always wins, which is what keeps styling identical to + // built-in pages; the local install is types + standalone fallback. + '@datum-cloud/datum-ui/badge': hostShared(false), + '@datum-cloud/datum-ui/button': hostShared(false), + '@datum-cloud/datum-ui/card': hostShared(false), + '@datum-cloud/datum-ui/empty-content': hostShared(false), + '@datum-cloud/datum-ui/icons': hostShared(false), + // Do not share `logs`: MF colocates lucide-react / date-fns into the + // logs loadShare chunk, so a host-provided logs module replaces those + // exports and crashes WorkloadDetail / related pages. + // `select` is shared by the host (see cloud-portal `federation-host.ts`) so + // the overview range control renders the same Select as ALB. + '@datum-cloud/datum-ui/select': hostShared(false), + '@datum-cloud/datum-ui/separator': hostShared(false), + '@datum-cloud/datum-ui/skeleton': hostShared(false), + '@datum-cloud/datum-ui/table': hostShared(false), + }, + }), + ], + }; }); From af695ba60f3638ba1d7a020b76f1f5ebdeadf115 Mon Sep 17 00:00:00 2001 From: Kevin Williams Date: Wed, 14 Oct 2026 16:49:38 -0400 Subject: [PATCH 2/4] fix: Minify the consumer plugin bundle minify: false kept the built bundle readable, but every page load paid for it: the project home column shipped 576KB of unminified JavaScript (119KB gzipped). Minified it is 237KB (74KB gzipped), which cuts both transfer and parse time. Readable stack traces come back through source maps uploaded to Sentry in the next commit. Co-Authored-By: Claude --- ui/consumer/vite.config.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/consumer/vite.config.ts b/ui/consumer/vite.config.ts index 73f22054..a892da14 100644 --- a/ui/consumer/vite.config.ts +++ b/ui/consumer/vite.config.ts @@ -105,8 +105,6 @@ export default defineConfig(({ command }) => { }, build: { target: 'esnext', - // Keep the plugin readable when inspecting the built bundle. - minify: false, }, experimental: { renderBuiltUrl(_filename, { hostType }) { From bd6bb5f897979a4820a5a166a3e7b7365ace0c44 Mon Sep 17 00:00:00 2001 From: Kevin Williams Date: Wed, 14 Oct 2026 16:49:46 -0400 Subject: [PATCH 3/4] feat: Upload plugin source maps to Sentry The plugin runs inside cloud-portal, so its errors reach the portal's Sentry project with minified frames. Builds now emit hidden source maps and upload them to the cloud-portal project. Frames resolve through debug IDs injected into each chunk, not a release: the release on those events names the portal's build, not this plugin's. Release creation and finalizing are off for the same reason. The token comes from publish-docker's SENTRY_AUTH_TOKEN build arg and is only visible to the build stage. Builds without it (local, forks) skip the upload. The Dockerfile deletes the maps after the build, since serve.ts serves every file in dist/ and the portal's asset proxy would otherwise expose them. Co-Authored-By: Claude --- ui/consumer/Dockerfile | 6 ++- ui/consumer/bun.lock | 77 ++++++++++++++++++++++++++++++++++++++ ui/consumer/package.json | 1 + ui/consumer/vite.config.ts | 16 ++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/ui/consumer/Dockerfile b/ui/consumer/Dockerfile index ccfe1cbf..86e2bab8 100644 --- a/ui/consumer/Dockerfile +++ b/ui/consumer/Dockerfile @@ -41,7 +41,11 @@ RUN bun install --frozen-lockfile COPY ui/consumer/ . COPY --from=console-wasm /out/ /console-wasm/ ENV CONSOLE_WASM_DIR=/console-wasm -RUN bun run build +# Forwarded by publish-docker; the build uploads source maps to Sentry when set. +# Only this stage sees it: the final image copies dist/ alone. +ARG SENTRY_AUTH_TOKEN +# Maps go to Sentry, not the image: serve.ts serves every file in dist/. +RUN bun run build && find dist -name '*.map' -delete FROM oven/bun:1-slim WORKDIR /app diff --git a/ui/consumer/bun.lock b/ui/consumer/bun.lock index e423d091..e681160f 100644 --- a/ui/consumer/bun.lock +++ b/ui/consumer/bun.lock @@ -27,6 +27,7 @@ }, "devDependencies": { "@module-federation/vite": "^1.16.16", + "@sentry/vite-plugin": "5.4.0", "@types/node": "^26.6.3", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", @@ -329,6 +330,32 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA=="], + "@sentry/bundler-plugins": ["@sentry/bundler-plugins@10.76.2", "", { "dependencies": { "@babel/core": "^7.18.5", "@sentry/cli": "^2.58.6", "@sentry/core": "10.76.2", "dotenv": "^17.4.2", "find-up": "^5.0.0", "glob": "^13.0.6", "magic-string": "~0.30.8" }, "peerDependencies": { "rollup": ">=3.2.0", "webpack": ">=5.0.0" }, "optionalPeers": ["rollup", "webpack"] }, "sha512-5ovSYkHaTTDwe6p9vq0AzE+W9likkINBpzCv7VychGqYUfBGNzr+0YVkAt9//saJI9BKs+lWud5qX5iASf6wkw=="], + + "@sentry/cli": ["@sentry/cli@2.58.6", "", { "dependencies": { "https-proxy-agent": "^5.0.0", "node-fetch": "^2.6.7", "progress": "^2.0.3", "proxy-from-env": "^1.1.0", "which": "^2.0.2" }, "optionalDependencies": { "@sentry/cli-darwin": "2.58.6", "@sentry/cli-linux-arm": "2.58.6", "@sentry/cli-linux-arm64": "2.58.6", "@sentry/cli-linux-i686": "2.58.6", "@sentry/cli-linux-x64": "2.58.6", "@sentry/cli-win32-arm64": "2.58.6", "@sentry/cli-win32-i686": "2.58.6", "@sentry/cli-win32-x64": "2.58.6" }, "bin": { "sentry-cli": "bin/sentry-cli" } }, "sha512-baBcNPLLfUi9WuL+Tpri9BFaAdvugZIKelC5X0tt0Zdy+K0K+PCVSrnNmwMWU/HyaF/SEv6b6UHnXIdqanBlcg=="], + + "@sentry/cli-darwin": ["@sentry/cli-darwin@2.58.6", "", { "os": "darwin" }, "sha512-udAVvcyfNa0R+95GvPz/+43/N3TC0TYKdkQ7D7jhPSzbcMc7l2fxRNN5yB3UpCA5fWFnW4toeaqwDBhb/Wh3LA=="], + + "@sentry/cli-linux-arm": ["@sentry/cli-linux-arm@2.58.6", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "arm" }, "sha512-pD0LAt5PcUzAinBwvDqc66x9+2CabHEv486yP0gRjWO7SakbaxmfVq/EXd8VLq/Tzi39LAu422UYK1lpW3MILw=="], + + "@sentry/cli-linux-arm64": ["@sentry/cli-linux-arm64@2.58.6", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "arm64" }, "sha512-q8mEcNNmeXMy5i+jWT30TVpH7LcP4HD21CD5XRSPAd/a912HF6EpK0ybf/1USO14WOhoXbAGi9txwaWabSe33g=="], + + "@sentry/cli-linux-i686": ["@sentry/cli-linux-i686@2.58.6", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "ia32" }, "sha512-q8vNJi1eOV/4vxAFWBsEwLHoSYapaZHIf4j76KJGJXFKTkEbsjCOOsKbwUIBTQQhRgV4DFWh3ryfsPS/que4Kg=="], + + "@sentry/cli-linux-x64": ["@sentry/cli-linux-x64@2.58.6", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "x64" }, "sha512-DZu956Mhi3ZRjTBe1WdbGV46ldVbA8d2rgp/fh51GsI25zjBHah4wZnPTSzpc+YqxU6pJpg579B/r3jrIK530Q=="], + + "@sentry/cli-win32-arm64": ["@sentry/cli-win32-arm64@2.58.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-nj0Ff/kmAB73EPDhR8B4O9r+NUHK5GkPCkGWC+kXVemqAJWL5jcJ5KdxG0l/S0z6RoEoltID8/43/B+TaMlT7A=="], + + "@sentry/cli-win32-i686": ["@sentry/cli-win32-i686@2.58.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-WNZiDzPbgsEMQWq4avsQ391v/xWKJDIWWWo9GYl+N/w5qcYKkoDW7wQG7T9FasI6ENn68phChTOAPXXxbfAdOg=="], + + "@sentry/cli-win32-x64": ["@sentry/cli-win32-x64@2.58.6", "", { "os": "win32", "cpu": "x64" }, "sha512-R35WJ17oF4D2eqI1DR2sQQqr0fjRTt5xoP16WrTu91XM2lndRMFsnjh+/GttbxapLCBNlrjzia99MJ0PZHZpgA=="], + + "@sentry/conventions": ["@sentry/conventions@0.16.0", "", {}, "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ=="], + + "@sentry/core": ["@sentry/core@10.76.2", "", { "dependencies": { "@sentry/conventions": "^0.16.0" } }, "sha512-vndrQalRiwYatTkjlLWvyf69KocXIb9TKJ4A9oiyWguL8PJYtqIRTlRe4OpkpjLrxS18fvRdV0jEm29c/9p3hQ=="], + + "@sentry/vite-plugin": ["@sentry/vite-plugin@5.4.0", "", { "dependencies": { "@sentry/bundler-plugins": "^10.64.0" } }, "sha512-fFJgCxs5hDyAm9BbZJ+LbA+LK2tjX5OoD0v0ARU4StR6KQmGUduoPs69yJ9AfqZ0om3Rlp5JDliiwFcNkasORA=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], @@ -395,12 +422,18 @@ "adm-zip": ["adm-zip@0.5.10", "", {}, "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ=="], + "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.11.1", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A=="], "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], + "brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], + "browserslist": ["browserslist@4.28.7", "", { "dependencies": { "baseline-browser-mapping": "^2.10.44", "caniuse-lite": "^1.0.30001806", "electron-to-chromium": "^1.5.393", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw=="], "caniuse-lite": ["caniuse-lite@1.0.30001806", "", {}, "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw=="], @@ -457,6 +490,8 @@ "dompurify": ["dompurify@3.2.7", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw=="], + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], + "electron-to-chromium": ["electron-to-chromium@1.5.395", "", {}, "sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA=="], "entities": ["entities@8.0.0", "", {}, "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA=="], @@ -471,6 +506,8 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + "framer-motion": ["framer-motion@13.4.6", "", { "dependencies": { "motion-dom": "^13.4.5", "motion-utils": "^13.3.0", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-TwKWt2CC8rjhARzwV00TZIh04ipumSAqyKM/JZmS+urdncSHg6wTF/mfj/Fd6Oi2W01TZSWsiA3uxXDrXZtKyw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -479,14 +516,20 @@ "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + "glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], + "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="], + "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + "immer": ["immer@11.1.18", "", {}, "sha512-EQyQtLiYW029lyoczMl/Hh4Xu7cDecSc58JRYpHyL4tIAu3eqd1yJzQX04d2BZHDkzFFvm6qJEJWOtfDSWAXbQ=="], "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "isomorphic-dompurify": ["isomorphic-dompurify@3.22.0", "", { "dependencies": { "dompurify": "^3.4.12", "jsdom": "^30.0.0" } }, "sha512-cz/BkSODnQir4IV2quqbuEHhAGz4MAYGvhoemsxuwMpY/oZbRHlNjPLPyIQ0fdULiwDPUE+OHPwWb6XTEiNk1A=="], "isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="], @@ -499,14 +542,22 @@ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "lucide-react": ["lucide-react@1.26.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-raglYVR2+VkMfJL158krjVmE+rV5ST2lzA/KQm1FRSjMHT4MnWaegHxoVEpmc2So3nOEhp9oGejJwAPX8MoAjg=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "marked": ["marked@14.0.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ=="], "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], + "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], + + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + "monaco-editor": ["monaco-editor@0.55.1", "", { "dependencies": { "dompurify": "3.2.7", "marked": "14.0.0" } }, "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A=="], "motion": ["motion@13.4.6", "", { "dependencies": { "framer-motion": "^13.4.6", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-zDXi6lIdyhda6wZlTsv9QgT2ed6UExAfRegtl/akQmZb5iaeLlArXr9Gmo/hx3iTx2y4R7tmdb9k9koFQmV+NQ=="], @@ -519,18 +570,32 @@ "nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + "node-releases": ["node-releases@2.0.51", "", {}, "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ=="], "nuqs": ["nuqs@2.10.1", "", { "dependencies": { "@standard-schema/spec": "1.1.0" }, "peerDependencies": { "@remix-run/react": ">=2", "@tanstack/react-router": "^1", "next": ">=14.2.0", "react": ">=18.2.0 || ^19.0.0-0", "react-router": "^5 || ^6 || ^7 || ^8", "react-router-dom": "^5 || ^6 || ^7" }, "optionalPeers": ["@remix-run/react", "@tanstack/react-router", "next", "react-router", "react-router-dom"] }, "sha512-7lPZrPJVOsD0VvfQSKtodYBBPGPLsLzkFU8ueYIap9yMMJvSw6UC12p8luA5NW7aKe7AbPHKxcxb3OluH6iAJA=="], + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "parse5": ["parse5@8.0.1", "", { "dependencies": { "entities": "^8.0.0" } }, "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], "postcss": ["postcss@8.5.22", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ=="], + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "react": ["react@19.3.0", "", {}, "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog=="], @@ -623,6 +688,8 @@ "whatwg-url": ["whatwg-url@17.1.0", "", { "dependencies": { "@exodus/bytes": "^1.15.1", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], @@ -631,6 +698,8 @@ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@asamuzakjp/css-color/lru-cache": ["lru-cache@11.5.2", "", {}, "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g=="], @@ -655,6 +724,10 @@ "jsdom/undici": ["undici@8.10.0", "", {}, "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ=="], + "node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + + "path-scurry/lru-cache": ["lru-cache@11.5.2", "", {}, "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g=="], + "cmdk/@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.2.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EraVbFjiIjibpLr6EjvEDmSCYJU2SlKDMiO+qEK/D9GOWnQoAQlpQo2occGYC1UM9MBeEx5Bek3UtW/Qi57vAg=="], "cmdk/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.4", "@radix-ui/react-primitive": "2.1.8", "@radix-ui/react-use-callback-ref": "1.1.3", "@radix-ui/react-use-effect-event": "0.0.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QAXwa38pG0xNAYh1pjdSaf86NrkqsMoDNmget/Y7X8O8E/C3Iqlj9GAPE4DfX9BPLXc7WH2TWSzMRnIoCdcjzQ=="], @@ -677,6 +750,10 @@ "cmdk/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.1", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Bu/aAQHFFh6/QAvXAeUMurJ9fbW0JUIqlojU/yBXZ7cAVqy75Y7JYYyuCr9zLNF0p4WWoJYV54CTUIf4l7FzTw=="], + "node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + + "node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + "cmdk/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-AUS7HoBBAncIsGMLNG+CcpLuJ+JIBbZzmyM8Qdb1eIThX0AlhSSC6wn40xfBlPE+ypx/vSSiRWnklUAjy3U3UA=="], "cmdk/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.4", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-XYcfa6wlXDCwQtePuEiPmXLSAhGL4DWtedSyRgGbG3y10mw+OnrLp6SyeY1gJFMiYF0Dx0nMAX9InylKbLEFQQ=="], diff --git a/ui/consumer/package.json b/ui/consumer/package.json index 2902b60b..536fcf7c 100644 --- a/ui/consumer/package.json +++ b/ui/consumer/package.json @@ -34,6 +34,7 @@ }, "devDependencies": { "@module-federation/vite": "^1.16.16", + "@sentry/vite-plugin": "5.4.0", "@types/node": "^26.6.3", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", diff --git a/ui/consumer/vite.config.ts b/ui/consumer/vite.config.ts index a892da14..b68de959 100644 --- a/ui/consumer/vite.config.ts +++ b/ui/consumer/vite.config.ts @@ -1,5 +1,6 @@ /// import { federation } from '@module-federation/vite'; +import { sentryVitePlugin } from '@sentry/vite-plugin'; import react from '@vitejs/plugin-react'; import { existsSync, readFileSync } from 'node:fs'; import { resolve } from 'node:path'; @@ -105,6 +106,9 @@ export default defineConfig(({ command }) => { }, build: { target: 'esnext', + // Maps for Sentry (below); no sourceMappingURL comment, so browsers + // never request them. The Dockerfile drops them from the image. + sourcemap: 'hidden', }, experimental: { renderBuiltUrl(_filename, { hostType }) { @@ -171,6 +175,18 @@ export default defineConfig(({ command }) => { '@datum-cloud/datum-ui/table': hostShared(false), }, }), + // The plugin runs inside cloud-portal, so its errors reach the portal's + // Sentry project. Debug IDs tie frames to these maps without a release, + // since the portal's release names its own build, not this plugin's. + // No token (local builds, forks) skips the upload. + sentryVitePlugin({ + org: 'sentry', + project: 'cloud-portal', + authToken: process.env.SENTRY_AUTH_TOKEN, + disable: command !== 'build' || !process.env.SENTRY_AUTH_TOKEN, + release: { create: false, finalize: false, inject: false }, + telemetry: false, + }), ], }; }); From 6e629ee1e2a3c76a257ebdc82ebcc592038a76e7 Mon Sep 17 00:00:00 2001 From: Kevin Williams Date: Wed, 14 Oct 2026 17:01:20 -0400 Subject: [PATCH 4/4] fix: Pass the Sentry token as a BuildKit secret A build-arg is recorded in the history of the stage that uses it, and publish-docker exports every stage to the Actions cache (mode=max), so the token would have been readable by anyone who can restore that cache. The build now reads the token from a secret mount, which never lands in a layer, history or cache. The consumer image job passes it through publish-docker's build-secrets instead of secrets: inherit, which would also have forwarded it as the SENTRY_AUTH_TOKEN build-arg. Co-Authored-By: Claude --- .github/workflows/publish.yaml | 7 ++++++- ui/consumer/Dockerfile | 10 ++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 7add4ce5..f58d3568 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -30,7 +30,12 @@ jobs: image-name: compute-consumer-ui-plugin context: . dockerfile-path: ui/consumer/Dockerfile - secrets: inherit + # The Sentry token goes in as a BuildKit secret only. `secrets: inherit` + # would also pass it to publish-docker's SENTRY_AUTH_TOKEN, which it + # forwards as a build-arg, and build-args persist in the exported cache. + secrets: + build-secrets: | + "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" # Deployed as a Component (config/components/provider-ui-plugin) folded # into compute-manager's own kustomize bundle/Flux Kustomization — same diff --git a/ui/consumer/Dockerfile b/ui/consumer/Dockerfile index 86e2bab8..2ac59ada 100644 --- a/ui/consumer/Dockerfile +++ b/ui/consumer/Dockerfile @@ -41,11 +41,13 @@ RUN bun install --frozen-lockfile COPY ui/consumer/ . COPY --from=console-wasm /out/ /console-wasm/ ENV CONSOLE_WASM_DIR=/console-wasm -# Forwarded by publish-docker; the build uploads source maps to Sentry when set. -# Only this stage sees it: the final image copies dist/ alone. -ARG SENTRY_AUTH_TOKEN +# The Sentry token is a BuildKit secret (publish-docker's build-secrets), so it +# never lands in a layer, the image history or the exported build cache. When +# absent the file is missing, the variable is empty and the upload is skipped. # Maps go to Sentry, not the image: serve.ts serves every file in dist/. -RUN bun run build && find dist -name '*.map' -delete +RUN --mount=type=secret,id=SENTRY_AUTH_TOKEN,required=false \ + SENTRY_AUTH_TOKEN="$(cat /run/secrets/SENTRY_AUTH_TOKEN 2>/dev/null)" bun run build && \ + find dist -name '*.map' -delete FROM oven/bun:1-slim WORKDIR /app