From 36d2fc4eba4fb5847ff7f130a99399799bff8069 Mon Sep 17 00:00:00 2001 From: David Pine <7679720+IEvangelist@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:39:07 -0500 Subject: [PATCH 1/2] Add SAMPLES_BRANCH env override to update-samples script Pulls the temporary edit used to generate #1190 (the Aspire 13.3 API refresh) into the script permanently so contributors can stage `samples.json` against a still-open upstream PR without a temp-edit-and-revert dance. Usage: SAMPLES_BRANCH= pnpm run update:samples Behavior: - `BRANCH` (used by `RAW_BASE` and the GitHub contents/tree API calls) reads `process.env.SAMPLES_BRANCH ?? 'main'`. - `TREE_BASE` (used for each sample's `href` field) stays pinned to `main` regardless of the override, so links remain valid after the PR branch is deleted. - Startup log shows the active branch and surfaces a note when the override is in effect. - With no env var set, behavior is byte-identical to today (verified with a scratch run of the constant block). Also documents the new env var in the `update-samples` contributor skill. Verified: `pnpm lint` clean (astro sync + verify-skill-digests + eslint --max-warnings 0). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .agents/skills/update-samples/SKILL.md | 1 + src/frontend/scripts/update-samples.ts | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.agents/skills/update-samples/SKILL.md b/.agents/skills/update-samples/SKILL.md index abdd440cb..78f58715e 100644 --- a/.agents/skills/update-samples/SKILL.md +++ b/.agents/skills/update-samples/SKILL.md @@ -141,6 +141,7 @@ After running the script: | Variable | Required | Description | |---|---|---| | `GITHUB_TOKEN` | No | GitHub personal access token for higher API rate limits (60 → 5000 requests/hour) | +| `SAMPLES_BRANCH` | No | Override the `microsoft/aspire-samples` ref to fetch from (default `main`). Useful for staging `samples.json` against a still-open upstream PR — set to the PR's head ref. The generated `href` fields stay anchored to `main` so links remain valid after the PR branch is deleted. | ### 7. Integration with build diff --git a/src/frontend/scripts/update-samples.ts b/src/frontend/scripts/update-samples.ts index b9bd458a1..e46cf5541 100644 --- a/src/frontend/scripts/update-samples.ts +++ b/src/frontend/scripts/update-samples.ts @@ -5,14 +5,24 @@ import { pipeline } from 'stream/promises'; import fetch from 'node-fetch'; const REPO = 'microsoft/aspire-samples'; -const BRANCH = 'main'; +const DEFAULT_BRANCH = 'main'; +// `BRANCH` controls which ref of `microsoft/aspire-samples` is fetched (README, +// AppHost code, raw assets, tree listing). The override exists so contributors +// can stage `samples.json` against a still-open upstream PR by running the +// script with `SAMPLES_BRANCH= pnpm run update:samples` — the output +// is identical to what the next normal run will produce after that PR merges, +// so the staged data PR is idempotent with the upstream merge. +const BRANCH = process.env.SAMPLES_BRANCH ?? DEFAULT_BRANCH; const SAMPLES_DIR = 'samples'; const OUTPUT_PATH = './src/data/samples.json'; const ASSETS_DIR = './src/assets/samples'; const ASSETS_IMPORT_PREFIX = '~/assets/samples'; const GITHUB_API = 'https://api.github.com'; const RAW_BASE = `https://raw.githubusercontent.com/${REPO}/${BRANCH}`; -const TREE_BASE = `https://github.com/${REPO}/tree/${BRANCH}`; +// `TREE_BASE` is intentionally pinned to `main` (never the override) so the +// generated `href` fields on each sample stay valid after a PR branch goes +// away. The branch override is for fetching content, not for cementing links. +const TREE_BASE = `https://github.com/${REPO}/tree/${DEFAULT_BRANCH}`; const LEGACY_DOCS_HOST = 'https://learn.microsoft.com'; const ASPIRE_DOC_URL_REWRITES = [ [ @@ -471,7 +481,10 @@ async function processSample( } async function main(): Promise { - console.log(`📦 Fetching sample directories from ${REPO}...`); + console.log(`📦 Fetching sample directories from ${REPO}@${BRANCH}...`); + if (BRANCH !== DEFAULT_BRANCH) { + console.log(` (branch override via SAMPLES_BRANCH; href links stay anchored to ${DEFAULT_BRANCH})`); + } const [dirs, pathsBySample] = await Promise.all([listSampleDirs(), fetchSamplePaths()]); console.log(`📂 Found ${dirs.length} sample directories`); From 1dfb8793668545d3bb70a0936b4912e103abfc4c Mon Sep 17 00:00:00 2001 From: David Pine <7679720+IEvangelist@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:52:17 -0500 Subject: [PATCH 2/2] Percent-encode SAMPLES_BRANCH ref in GitHub URLs PR head refs frequently contain `/` (e.g. `user/feature`, `release/13.4`). Other URL-significant characters (`#`, `?`, `%`, `&`) are rarer in refs but still legal per git, so the script should not be one bad branch name away from breaking. Introduce two encoded variants of `BRANCH`: - `BRANCH_PATH`: per-segment `encodeURIComponent`. Preserves `/` boundaries (which GitHub accepts in path positions for both `raw.githubusercontent.com` and `api.github.com/.../git/trees/...`) while escaping every other special char within each segment. - `BRANCH_QUERY`: full `encodeURIComponent`. Used for the `?ref=` query parameter on the contents API (where `/` must be `%2F`). Verified end-to-end against the live GitHub API with a real PR head ref containing `/`: both endpoints return the expected payloads. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/frontend/scripts/update-samples.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/frontend/scripts/update-samples.ts b/src/frontend/scripts/update-samples.ts index e46cf5541..bd1e9bf78 100644 --- a/src/frontend/scripts/update-samples.ts +++ b/src/frontend/scripts/update-samples.ts @@ -13,12 +13,20 @@ const DEFAULT_BRANCH = 'main'; // is identical to what the next normal run will produce after that PR merges, // so the staged data PR is idempotent with the upstream merge. const BRANCH = process.env.SAMPLES_BRANCH ?? DEFAULT_BRANCH; +// PR head refs frequently contain `/` (e.g. `feature/foo`, +// `user/dapine-aspire-api-updates`). GitHub accepts `/` in path positions for +// the raw, contents, and git/trees endpoints, but other URL-significant +// characters in a ref (`#`, `?`, `%`, `&`, etc.) need to be percent-encoded. +// Encoding per `/`-separated segment preserves the `/` boundaries while +// safely escaping every other special character within each segment. +const BRANCH_PATH = BRANCH.split('/').map(encodeURIComponent).join('/'); +const BRANCH_QUERY = encodeURIComponent(BRANCH); const SAMPLES_DIR = 'samples'; const OUTPUT_PATH = './src/data/samples.json'; const ASSETS_DIR = './src/assets/samples'; const ASSETS_IMPORT_PREFIX = '~/assets/samples'; const GITHUB_API = 'https://api.github.com'; -const RAW_BASE = `https://raw.githubusercontent.com/${REPO}/${BRANCH}`; +const RAW_BASE = `https://raw.githubusercontent.com/${REPO}/${BRANCH_PATH}`; // `TREE_BASE` is intentionally pinned to `main` (never the override) so the // generated `href` fields on each sample stay valid after a PR branch goes // away. The branch override is for fetching content, not for cementing links. @@ -374,7 +382,7 @@ async function fetchText(url: string): Promise { async function listSampleDirs(): Promise { const contents = await fetchJson( - `${GITHUB_API}/repos/${REPO}/contents/${SAMPLES_DIR}?ref=${BRANCH}` + `${GITHUB_API}/repos/${REPO}/contents/${SAMPLES_DIR}?ref=${BRANCH_QUERY}` ); return contents @@ -390,7 +398,7 @@ async function listSampleDirs(): Promise { */ async function fetchSamplePaths(): Promise> { const tree = await fetchJson( - `${GITHUB_API}/repos/${REPO}/git/trees/${BRANCH}?recursive=1` + `${GITHUB_API}/repos/${REPO}/git/trees/${BRANCH_PATH}?recursive=1` ); if (tree.truncated) {