From 15a0fd56e05b4c8aeaebd9f44c8762056aa46aa6 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Wed, 8 Jul 2026 14:31:32 +0200 Subject: [PATCH 1/5] feat(playground): per-PR and per-version snapshot deploys Make the playground app-shell deploy-base aware so it can be served from any sub-path, and add pipeline paths to deploy an immutable playground snapshot per PR and per published version. Nightly on master continues to serve at the root (default base URL). - vite: parametrize `base` via PLAYGROUND_BASE - add playground/src/base.ts (BASE, withBase, stripBase, rebaseAssetReferences) and make runtime/SPA paths base-aware - runner rebases root-absolute asset paths before running user code - azure-pipelines.yml: PR PlaygroundSnapshot job (builds PR engine, deploys, comments URL) - azure-pipelines-npm-publish.yml: versioned snapshot after publish - upload-static-site.yml: optional postComment param Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- azure-pipelines-npm-publish.yml | 24 ++++++++++ azure-pipelines.yml | 60 ++++++++++++++++++++++++- config/templates/upload-static-site.yml | 31 ++++++++----- playground/public/runner.html | 5 ++- playground/src/base.ts | 55 +++++++++++++++++++++++ playground/src/download.ts | 2 +- playground/src/editor.ts | 3 +- playground/src/embed.ts | 3 +- playground/src/main.ts | 9 ++-- playground/src/runner.ts | 9 ++-- playground/src/snippets.ts | 10 +++-- playground/src/versions.ts | 5 ++- playground/vite.config.ts | 6 +++ 13 files changed, 192 insertions(+), 30 deletions(-) create mode 100644 playground/src/base.ts diff --git a/azure-pipelines-npm-publish.yml b/azure-pipelines-npm-publish.yml index 3953bc1c8c..f95fcda88c 100644 --- a/azure-pipelines-npm-publish.yml +++ b/azure-pipelines-npm-publish.yml @@ -51,6 +51,7 @@ parameters: default: false variables: + - group: BabylonJS-Deployment - name: NODE_VERSION value: "22.x" - name: PNPM_VERSION @@ -240,3 +241,26 @@ stages: displayName: "Publish compat package to npm" env: NPM_TOKEN: $(NPM_TOKEN) + + # --- Versioned playground snapshot ------------------------------ + # After a successful publish, build the Lite Playground from this + # tagged commit and deploy it to an immutable per-version sub-path, + # so every release gets a permanent, self-consistent playground URL + # (its self-hosted engine is built from — and stamped with — the + # published version via PACKAGE_VERSION). PLAYGROUND_BASE matches the + # deploy path so assets, the runner, the engine, and SPA snippet + # routes resolve under the sub-path. Compile-time gated on dryRun so + # dry runs never upload. + - ${{ if eq(parameters.dryRun, false) }}: + - script: pnpm build:playground + displayName: "Build versioned playground" + env: + PACKAGE_VERSION: $(PACKAGE_VERSION) + PLAYGROUND_BASE: /lite/v/$(PACKAGE_VERSION)/playground/ + + - template: config/templates/upload-static-site.yml + parameters: + siteDir: $(System.DefaultWorkingDirectory)/playground/dist + deployPath: lite/v/$(PACKAGE_VERSION)/playground + siteName: "Lite Playground v$(PACKAGE_VERSION)" + postComment: false diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 50bf274b78..8569941304 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,6 +1,6 @@ # Azure Pipelines — Babylon Lite CI # -# Runs on every PR targeting master. Seven parallel jobs: +# Runs on every PR targeting master. Eight parallel jobs: # 1. Release Markers — breaking-change marker validation # 2. API Report — public API diff comment + breaking-change validation # 3. Unit Tests — vitest + plumbing (Playwright) @@ -8,6 +8,7 @@ # 5. Perf Regression — Lite vs Stable on BrowserStack # 6. Parity (Cloud) — pixel-diff via cloud browser with real WebGPU # 7. Lint — ESLint + TypeScript type-check +# 8. Playground Snapshot — per-PR playground deploy + PR comment # trigger: none @@ -568,3 +569,60 @@ stages: mergeTestResults: true testRunTitle: "Compat Layer" continueOnError: true + + # ────────────────────────────────────────────────────────────── + # Job 8: PR Playground Snapshot + # ────────────────────────────────────────────────────────────── + # Builds the Lite Playground from THIS PR's source — the self-hosted + # engine is bundled from the same commit, so the runner executes the PR's + # engine code — and deploys it to a per-PR sub-path, then comments the URL + # on the PR. PLAYGROUND_BASE is set to match the deploy path so every + # asset, the runner iframe, the self-hosted engine, and the SPA snippet + # routes resolve under that sub-path. + # + # Fork PRs: Azure withholds secret variables (including the deploy token) + # from fork-triggered PRs by default, so on a fork the upload step simply + # no-ops (it is continueOnError) and the PR still passes. To publish + # snapshots for fork PRs, enable "Make secrets available to builds of + # forks" in the pipeline's trigger settings — note this exposes the + # deploy token to fork-authored code, a security trade-off to weigh. + - job: PlaygroundSnapshot + displayName: "PR Playground Snapshot" + pool: + vmImage: "ubuntu-latest" + variables: + # Immutable per-PR path; re-deployed in place on each push so the + # PR always has one stable snapshot URL. + - name: deployPath + value: lite/pr/$(System.PullRequest.PullRequestNumber)/playground + steps: + # fetchDepth: 0 + tags so the engine build can resolve the latest + # npm-lite-v* tag for its version stamp (see vite.engine.config.ts). + - checkout: self + fetchDepth: 0 + fetchTags: true + + - task: UseNode@1 + inputs: + version: $(NODE_VERSION) + + - script: | + corepack enable + corepack prepare pnpm@$(PNPM_VERSION) --activate + displayName: "Enable pnpm via corepack" + + - script: pnpm install --frozen-lockfile + displayName: "Install dependencies" + + # PLAYGROUND_BASE must be leading- and trailing-slashed and match + # the deploy path so the built app resolves assets under the sub-path. + - script: pnpm build:playground + displayName: "Build the playground (engine + app)" + env: + PLAYGROUND_BASE: /$(deployPath)/ + + - template: config/templates/upload-static-site.yml + parameters: + siteDir: $(System.DefaultWorkingDirectory)/playground/dist + deployPath: $(deployPath) + siteName: "Lite Playground" diff --git a/config/templates/upload-static-site.yml b/config/templates/upload-static-site.yml index 2c63ea84b2..50a71c54a9 100644 --- a/config/templates/upload-static-site.yml +++ b/config/templates/upload-static-site.yml @@ -18,6 +18,12 @@ parameters: type: string - name: siteName type: string + # When false, upload the site but skip the PR comment. Used by non-PR + # pipelines (e.g. the npm-publish versioned playground) where there is no + # System.PullRequest context to comment on. + - name: postComment + type: boolean + default: true steps: - script: | @@ -44,17 +50,18 @@ steps: continueOnError: true displayName: "Upload ${{ parameters.siteName }} static site" - - task: GitHubComment@0 - condition: succeeded() - continueOnError: true - displayName: "Post ${{ parameters.siteName }} site link to PR" - inputs: - gitHubConnection: "BabylonBotPAT" - repositoryName: "$(Build.Repository.Name)" - id: "$(System.PullRequest.PullRequestNumber)" - comment: | - ### ${{ parameters.siteName }} - Static Site + - ${{ if eq(parameters.postComment, true) }}: + - task: GitHubComment@0 + condition: succeeded() + continueOnError: true + displayName: "Post ${{ parameters.siteName }} site link to PR" + inputs: + gitHubConnection: "BabylonBotPAT" + repositoryName: "$(Build.Repository.Name)" + id: "$(System.PullRequest.PullRequestNumber)" + comment: | + ### ${{ parameters.siteName }} - Static Site - [Open deployed site]($(SERVE_DOMAIN)/${{ parameters.deployPath }}/index.html) + [Open deployed site]($(SERVE_DOMAIN)/${{ parameters.deployPath }}/index.html) - Build $(Build.BuildNumber) - $(Build.SourceBranchName) @ $(Build.SourceVersion) + Build $(Build.BuildNumber) - $(Build.SourceBranchName) @ $(Build.SourceVersion) diff --git a/playground/public/runner.html b/playground/public/runner.html index d7147d3d73..75ef3799cf 100644 --- a/playground/public/runner.html +++ b/playground/public/runner.html @@ -12,7 +12,10 @@ --> + diff --git a/playground/src/entry.ts b/playground/src/entry.ts new file mode 100644 index 0000000000..5d45e0bd3d --- /dev/null +++ b/playground/src/entry.ts @@ -0,0 +1,39 @@ +// Deploy coordinator / bootstrap — the stable entry every playground deploy ships. +// +// The playground is served in three shapes, all from the same origin: +// • the canonical nightly build at the root ("/"), +// • an immutable per-PR snapshot under "/pr//", +// • an immutable per-version snapshot under "/v//". +// Each is built once with its own PLAYGROUND_BASE baked into `BASE_URL`, and each +// ships THIS module as a stable, unhashed `assets/boot.js`. +// +// Why a coordinator is needed: a deep link like `/pr/387/snippet/XKIIYQ/v/3` has +// no matching file at the origin, so the CDN/Front Door SPA fallback rewrites it +// to the root `/index.html`. Front Door can only rewrite to a single static path +// (it can't reconstruct a per-base one), so that fallback serves the ROOT build's +// `boot.js` for a URL that actually belongs to a snapshot. This guard notices the +// mismatch between the URL's base and the build's baked base and hands off to the +// snapshot's own `boot.js`, which then matches and boots its app. When the base +// already matches — the root, or a snapshot served directly at `/pr//` — it +// boots straight away. +// +// Deployment stays a single per-target build + upload: the coordinator derives the +// hand-off target from the URL at runtime, so a build made today can bootstrap a +// snapshot published later without being rebuilt. + +/** The deploy base implied by the current URL, always trailing-slashed (e.g. `/`, `/pr/387/`). */ +function urlBase(): string { + // `[^/]+` captures the snapshot id (PR number or version) without the trailing + // slash, so this matches `/pr/387`, `/pr/387/`, and `/pr/387/snippet/...` alike. + const match = location.pathname.match(/^\/(?:pr|v)\/[^/]+/); + return match ? `${match[0]}/` : "/"; +} + +if (urlBase() === import.meta.env.BASE_URL) { + // This build is the right one for the URL — boot the app. + void import("./main"); +} else { + // A build reached via SPA fallback for a snapshot deep link. Hand off to the + // snapshot's own stable coordinator, which will match its base and boot. + void import(/* @vite-ignore */ `${urlBase()}assets/boot.js`); +} diff --git a/playground/vite.config.ts b/playground/vite.config.ts index 23b23546bb..c5473dd219 100644 --- a/playground/vite.config.ts +++ b/playground/vite.config.ts @@ -40,6 +40,20 @@ export default defineConfig({ build: { target: "esnext", sourcemap: true, + rollupOptions: { + output: { + // `index.html` is the sole entry, and Rollup only invokes + // `entryFileNames` for entry chunks — so the app's entry (the + // coordinator in entry.ts) is emitted as a stable, unhashed + // `assets/boot.js`. Any deploy can then hand off to another + // snapshot's coordinator via a predictable `assets/boot.js`. + // Every other chunk and asset stays content-hashed for long-term + // immutable caching. + entryFileNames: "assets/boot.js", + chunkFileNames: "assets/[name]-[hash].js", + assetFileNames: "assets/[name]-[hash][extname]", + }, + }, }, worker: { format: "es", From eeb409206f28c2b0e8cb9d468ef69667f62bb966 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Sat, 11 Jul 2026 15:00:16 +0200 Subject: [PATCH 4/5] feat(playground): serve PR & version snapshots same-origin as nightly Repoint the PR and versioned playground deploys to the tools storage account (liteplayground.babylonjs.com) under clean /pr// and /v// paths, so the stable assets/boot.js coordinator can hand off between snapshots on a single origin. Front Door can't do per-base SPA fallback, so a single rewrite to /index.html + the coordinator handles deep links. - upload-static-site.yml: add storageAccount + linkUrl params (masked auth untouched); PR comment now links the explicit public URL. - azure-pipelines.yml: PR snapshot -> TOOLS_STORAGE_ACCOUNT, base /pr//, upload litePlayground/pr/, comment liteplayground.babylonjs.com/pr//; lab caller passes its existing SERVE_DOMAIN link. Add CI-Infrastructure group. - azure-pipelines-npm-publish.yml: version snapshot -> TOOLS_STORAGE_ACCOUNT, base /v//, upload litePlayground/v/. Add CI-Infrastructure group. - Add config/frontdoor-playground-spa.md AFD SPA-fallback rule reference. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- azure-pipelines-npm-publish.yml | 22 +++-- azure-pipelines.yml | 35 ++++++-- config/frontdoor-playground-spa.md | 113 ++++++++++++++++++++++++ config/templates/upload-static-site.yml | 27 ++++-- 4 files changed, 175 insertions(+), 22 deletions(-) create mode 100644 config/frontdoor-playground-spa.md diff --git a/azure-pipelines-npm-publish.yml b/azure-pipelines-npm-publish.yml index d6c36cfbc4..44641abd64 100644 --- a/azure-pipelines-npm-publish.yml +++ b/azure-pipelines-npm-publish.yml @@ -52,6 +52,10 @@ parameters: variables: - group: BabylonJS-Deployment + # Provides TOOLS_STORAGE_ACCOUNT — the tools storage account backing + # liteplayground.babylonjs.com, so versioned playground snapshots serve + # same-origin as nightly (required by the client-side deploy coordinator). + - group: BabylonJS-CI-Infrastructure - name: NODE_VERSION value: "22.x" - name: PNPM_VERSION @@ -244,23 +248,25 @@ stages: # --- Versioned playground snapshot ------------------------------ # After a successful publish, build the Lite Playground from this - # tagged commit and deploy it to an immutable per-version sub-path, + # tagged commit and deploy it to an immutable per-version sub-path + # on the tools account (served at liteplayground.babylonjs.com/v//), # so every release gets a permanent, self-consistent playground URL - # (its self-hosted engine is built from — and stamped with — the - # published version via PACKAGE_VERSION). PLAYGROUND_BASE matches the - # deploy path so assets, the runner, the engine, and SPA snippet - # routes resolve under the sub-path. Compile-time gated on dryRun so - # dry runs never upload. + # served same-origin as nightly (its self-hosted engine is built + # from — and stamped with — the published version via PACKAGE_VERSION). + # PLAYGROUND_BASE matches the public sub-path so assets, the runner, + # the engine, and SPA snippet routes resolve under it. Compile-time + # gated on dryRun so dry runs never upload. - ${{ if eq(parameters.dryRun, false) }}: - script: pnpm build:playground displayName: "Build versioned playground" env: PACKAGE_VERSION: $(PACKAGE_VERSION) - PLAYGROUND_BASE: /lite/v/$(PACKAGE_VERSION)/playground/ + PLAYGROUND_BASE: /v/$(PACKAGE_VERSION)/ - template: config/templates/upload-static-site.yml parameters: siteDir: $(System.DefaultWorkingDirectory)/playground/dist - deployPath: lite/v/$(PACKAGE_VERSION)/playground + deployPath: litePlayground/v/$(PACKAGE_VERSION) + storageAccount: $(TOOLS_STORAGE_ACCOUNT) siteName: "Lite Playground v$(PACKAGE_VERSION)" postComment: false diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 675e57edb1..64ece9c814 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -21,6 +21,10 @@ pr: variables: - group: BabylonJS-BrowserStack - group: BabylonJS-Deployment + # Provides TOOLS_STORAGE_ACCOUNT — the tools storage account backing + # liteplayground.babylonjs.com, so PR playground snapshots serve same-origin + # as nightly (required by the client-side deploy coordinator). + - group: BabylonJS-CI-Infrastructure - name: NODE_VERSION value: "22.x" - name: PNPM_VERSION @@ -299,6 +303,7 @@ stages: siteDir: $(System.DefaultWorkingDirectory)/lab/dist deployPath: lite/$(Build.BuildNumber)/lab siteName: "Lab" + linkUrl: $(SERVE_DOMAIN)/lite/$(Build.BuildNumber)/lab/index.html # ────────────────────────────────────────────────────────────── # Job 4: Performance Regression @@ -610,10 +615,11 @@ stages: # ────────────────────────────────────────────────────────────── # Builds the Lite Playground from THIS PR's source — the self-hosted # engine is bundled from the same commit, so the runner executes the PR's - # engine code — and deploys it to a per-PR sub-path, then comments the URL - # on the PR. PLAYGROUND_BASE is set to match the deploy path so every - # asset, the runner iframe, the self-hosted engine, and the SPA snippet - # routes resolve under that sub-path. + # engine code — and deploys it to a per-PR sub-path on the tools account + # (served at liteplayground.babylonjs.com/pr//, same origin as nightly), + # then comments the URL on the PR. PLAYGROUND_BASE is set to match the + # public sub-path so every asset, the runner iframe, the self-hosted + # engine, and the SPA snippet routes resolve under it. # # Fork PRs: Azure withholds secret variables (including the deploy token) # from fork-triggered PRs by default, so on a fork the upload step simply @@ -626,10 +632,19 @@ stages: pool: vmImage: "ubuntu-latest" variables: - # Immutable per-PR path; re-deployed in place on each push so the - # PR always has one stable snapshot URL. + # Immutable per-PR snapshot, served same-origin as nightly on the + # tools account so the deploy coordinator can hand off to it. + # deployPath — upload path inside the tools storage account + # (the `litePlayground` container maps to the + # subdomain root, so this becomes /pr//). + # playgroundBase — public base path baked into the build. + # playgroundUrl — public URL posted in the PR comment. - name: deployPath - value: lite/pr/$(System.PullRequest.PullRequestNumber)/playground + value: litePlayground/pr/$(System.PullRequest.PullRequestNumber) + - name: playgroundBase + value: /pr/$(System.PullRequest.PullRequestNumber)/ + - name: playgroundUrl + value: https://liteplayground.babylonjs.com/pr/$(System.PullRequest.PullRequestNumber)/ steps: # fetchDepth: 0 + tags so the engine build can resolve the latest # npm-lite-v* tag for its version stamp (see vite.engine.config.ts). @@ -650,14 +665,16 @@ stages: displayName: "Install dependencies" # PLAYGROUND_BASE must be leading- and trailing-slashed and match - # the deploy path so the built app resolves assets under the sub-path. + # the public sub-path so the built app resolves assets under it. - script: pnpm build:playground displayName: "Build the playground (engine + app)" env: - PLAYGROUND_BASE: /$(deployPath)/ + PLAYGROUND_BASE: $(playgroundBase) - template: config/templates/upload-static-site.yml parameters: siteDir: $(System.DefaultWorkingDirectory)/playground/dist deployPath: $(deployPath) + storageAccount: $(TOOLS_STORAGE_ACCOUNT) siteName: "Lite Playground" + linkUrl: $(playgroundUrl) diff --git a/config/frontdoor-playground-spa.md b/config/frontdoor-playground-spa.md new file mode 100644 index 0000000000..75d8d70622 --- /dev/null +++ b/config/frontdoor-playground-spa.md @@ -0,0 +1,113 @@ +# Front Door rules for the Lite Playground snapshots + +The playground is served as a single origin with three shapes: + +| Shape | URL | Built by | +| ---------------- | ----------------------------------------------- | ------------------------------- | +| Nightly (master) | `https://liteplayground.babylonjs.com/` | `azure-pipelines-playground.yml`| +| Per-PR snapshot | `https://liteplayground.babylonjs.com/pr//` | `azure-pipelines.yml` | +| Per-version | `https://liteplayground.babylonjs.com/v//` | `azure-pipelines-npm-publish.yml`| + +Each is an immutable, independently built static site. The app is a History-API +SPA, so a deep link or hard refresh (e.g. `/pr/387/snippet/XKIIYQ/v/3`) requests a +path that has **no file** at the origin. Front Door must serve the app shell for +those paths instead of a 404. + +## Why one static rewrite is enough + +Azure Front Door Standard/Premium **URL Rewrite** only rewrites a prefix to a +single **static** destination — it has no regex capture groups or server +variables ([docs](https://learn.microsoft.com/azure/frontdoor/front-door-url-rewrite)). +So it cannot compute a *per-base* fallback like `/pr/387/index.html`. It can only +ever fall back to one path: the root `/index.html`. + +That is exactly enough, because every deploy ships the same stable coordinator at +`assets/boot.js` (loaded by every `index.html`). When the root `index.html` is +served for a snapshot deep link, its coordinator reads `location.pathname`, +derives the base (`/pr/387/`), and hands off to `/pr/387/assets/boot.js`, which +boots the PR build. See `playground/src/entry.ts`. + +## The rule set (one rule) + +Attach a rule set to the playground route with a single rule. + +**Conditions (AND):** + +1. **Request file extension** — `Operator: Equal`, `Negate: true`, values = the + asset allow-list below. (Requests that already point at a real file — with an + extension — pass through to blob storage untouched.) +2. **URL path** — `Operator: EndsWith`, `Negate: true`, value = `/`. (Directory + roots like `/`, `/pr/387/`, `/v/1.4.0/` are served natively by blob static + website's index document, so they skip the rewrite.) + +**Action:** **URL rewrite** — Source pattern `/`, Destination `/index.html`, +Preserve unmatched path `No`. + +### ARM / Bicep shape + +```json +{ + "name": "SpaFallback", + "order": 1, + "conditions": [ + { + "name": "RequestFileExtension", + "parameters": { + "typeName": "DeliveryRuleRequestFileExtensionConditionParameters", + "operator": "Equal", + "negateCondition": true, + "matchValues": [ + "js", "mjs", "css", "map", "json", "html", "wasm", "ts", "wgsl", + "txt", "xml", "csv", "ico", "svg", "png", "jpg", "jpeg", "gif", + "webp", "avif", "bmp", "woff", "woff2", "ttf", "otf", "eot", + "env", "dds", "ktx", "ktx2", "basis", "hdr", "exr", + "glb", "gltf", "obj", "stl", "bin", "mp3", "wav", "ogg", "m4a", + "mp4", "webm" + ], + "transforms": ["Lowercase"] + } + }, + { + "name": "UrlPath", + "parameters": { + "typeName": "DeliveryRuleUrlPathMatchConditionParameters", + "operator": "EndsWith", + "negateCondition": true, + "matchValues": ["/"], + "transforms": [] + } + } + ], + "actions": [ + { + "name": "UrlRewrite", + "parameters": { + "typeName": "DeliveryRuleUrlRewriteActionParameters", + "sourcePattern": "/", + "destination": "/index.html", + "preserveUnmatchedPath": false + } + } + ] +} +``` + +## Caching + +- `index.html` and `assets/boot.js` — **no-cache / must-revalidate**. They are the + stable coordinator surface; they must always reflect the latest deploy. The + nightly pipeline purges the CDN endpoint on every master deploy, which flushes + the root copies. +- Everything under `assets/*-[hash].*`, `/v//**`, and `/pr//**` other than + `boot.js`/`index.html` — content-hashed or immutable, safe to cache long-term. + +## Edge cases + +- **Bare snapshot root without a trailing slash** (`/pr/387`): condition 2 is not + met (doesn't end with `/`) and it has no extension, so it is rewritten to + `/index.html`; the coordinator still derives `/pr/387/` and hands off correctly. +- **The `runner.html` iframe and engine files** (`/pr/387/runner.html`, + `/pr/387/engine/dev/index.js`, `index.d.ts`) all carry extensions in the + allow-list, so they pass through to blob storage and are never rewritten. +- **Legacy hash links** (`/#XKIIYQ`) never reach the server and keep working; the + app also still parses the path form for backward compatibility. diff --git a/config/templates/upload-static-site.yml b/config/templates/upload-static-site.yml index 50a71c54a9..ce1048ed77 100644 --- a/config/templates/upload-static-site.yml +++ b/config/templates/upload-static-site.yml @@ -1,13 +1,17 @@ # Reusable step template: upload a static site directory and post its link to the PR # # Parameters: -# siteDir - path to the static site folder to zip and upload -# deployPath - storage path where the site should be served -# siteName - label used in display names and the PR comment +# siteDir - path to the static site folder to zip and upload +# deployPath - storage path where the site should be served +# siteName - label used in display names and the PR comment +# postComment - post a PR comment with the link (default true) +# storageAccount - storage account to upload to (default $(STORAGE_ACCOUNT)) +# linkUrl - public URL to link in the PR comment (when postComment) # # Required pipeline variables: # BabylonJS-Deployment variable group: DEPLOYMENT_SERVER, DEPLOY_TOKEN # Report upload settings: DEPLOY_ENDPOINT_UPLOAD, STORAGE_ACCOUNT, SERVE_DOMAIN +# Playground snapshots additionally pass TOOLS_STORAGE_ACCOUNT via storageAccount. # Required service connection: # BabylonBotPAT - GitHub PAT service connection for PR comments @@ -24,6 +28,19 @@ parameters: - name: postComment type: boolean default: true + # Storage account to upload to. Defaults to the report storage account; the + # playground snapshot jobs override this with the tools storage account so + # their sites serve from the playground subdomain. + - name: storageAccount + type: string + default: $(STORAGE_ACCOUNT) + # Public URL to link in the PR comment. Callers pass this explicitly because + # the served URL can't always be derived from the upload path (e.g. the tools + # account strips its container prefix from the public domain). Only used when + # postComment is true. + - name: linkUrl + type: string + default: "" steps: - script: | @@ -42,7 +59,7 @@ steps: -H "Content-Type: multipart/form-data" \ -H "Authorization: Bearer $(DEPLOY_TOKEN)" \ -F "path=${{ parameters.deployPath }}" \ - -F "storageAccount=$(STORAGE_ACCOUNT)" \ + -F "storageAccount=${{ parameters.storageAccount }}" \ -F "zip=@_static_site.zip" rm -f _static_site.zip @@ -62,6 +79,6 @@ steps: comment: | ### ${{ parameters.siteName }} - Static Site - [Open deployed site]($(SERVE_DOMAIN)/${{ parameters.deployPath }}/index.html) + [Open deployed site](${{ parameters.linkUrl }}) Build $(Build.BuildNumber) - $(Build.SourceBranchName) @ $(Build.SourceVersion) From 574b36b76e450301578e1c460347ce63656c9a73 Mon Sep 17 00:00:00 2001 From: Raanan Weber Date: Mon, 13 Jul 2026 13:38:01 +0200 Subject: [PATCH 5/5] chore: retrigger CI Empty commit to restart the stuck PR validation build. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>