diff --git a/.github/scripts/deployment-environment.sh b/.github/scripts/deployment-environment.sh index 79d173324..26d6f648d 100644 --- a/.github/scripts/deployment-environment.sh +++ b/.github/scripts/deployment-environment.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash set -euo pipefail -# Pages selects production by branch. Manual previews of master must use a -# preview alias, while a manual production deploy must explicitly target master. -if [[ "${GITHUB_EVENT_NAME:-}" == "push" && "$RAW_BRANCH" == "master" ]] || +# Keep the Pages production branch aligned with the repository's default branch. +# Manual previews of that branch still use an isolated preview alias. +if [[ "${GITHUB_EVENT_NAME:-}" == "push" && "$RAW_BRANCH" == "$PRODUCTION_BRANCH" ]] || [[ "${GITHUB_EVENT_NAME:-}" == "workflow_dispatch" && "${DEPLOY_ENVIRONMENT:-}" == "production" ]]; then { - echo "SAFE_BRANCH=master" + echo "SAFE_BRANCH=$PRODUCTION_BRANCH" echo "DOCS_DEPLOYMENT_ENVIRONMENT=production" echo "VITE_DEPLOYMENT_URL=" echo "DOCS_DEPLOYMENT_URL=https://rescript-lang.org" diff --git a/.github/scripts/lighthouse-environment.sh b/.github/scripts/lighthouse-environment.sh index 701c1d9c2..385b31527 100644 --- a/.github/scripts/lighthouse-environment.sh +++ b/.github/scripts/lighthouse-environment.sh @@ -13,8 +13,8 @@ else TARGET_ENVIRONMENT="${DOCS_DEPLOYMENT_ENVIRONMENT:-}" fi -ARTIFACT_NAME=$(node "$REPOSITORY_ROOT/apps/docs/scripts/lighthouse-report.mjs" artifact-name "$RAW_BRANCH" "${DOCS_DEPLOYMENT_ENVIRONMENT:-}") -TARGET_ARTIFACT_NAME=$(node "$REPOSITORY_ROOT/apps/docs/scripts/lighthouse-report.mjs" artifact-name "$RAW_TARGET_BRANCH" "$TARGET_ENVIRONMENT") +ARTIFACT_NAME=$(node "$REPOSITORY_ROOT/apps/docs/scripts/lighthouse-report.mjs" artifact-name "$RAW_BRANCH" "${DOCS_DEPLOYMENT_ENVIRONMENT:-}" "$PRODUCTION_BRANCH") +TARGET_ARTIFACT_NAME=$(node "$REPOSITORY_ROOT/apps/docs/scripts/lighthouse-report.mjs" artifact-name "$RAW_TARGET_BRANCH" "$TARGET_ENVIRONMENT" "$PRODUCTION_BRANCH") { echo "LIGHTHOUSE_ARTIFACT_NAME=$ARTIFACT_NAME" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7a647d212..95410d137 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,6 +3,7 @@ on: push: branches: - master + - v12 workflow_dispatch: inputs: environment: @@ -15,6 +16,9 @@ on: default: "preview" pull_request: +env: + PRODUCTION_BRANCH: ${{ github.event.repository.default_branch }} + jobs: deploy: runs-on: ubuntu-24.04 @@ -180,7 +184,7 @@ jobs: SAFE_BRANCH=$(echo "$RAW_BRANCH" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9-]+/-/g; s/^-+//; s/-+$//; s/-+/-/g') SAFE_BRANCH="${SAFE_BRANCH:0:28}" - if [[ "${{ github.event_name }}" == "push" && "$RAW_BRANCH" == "master" ]] || [[ "${{ inputs.environment }}" == "production" ]]; then + if [[ "${{ github.event_name }}" == "push" && "$RAW_BRANCH" == "$PRODUCTION_BRANCH" ]] || [[ "${{ inputs.environment }}" == "production" ]]; then echo "GUIDE_DEPLOY_COMMAND=deploy" >> "$GITHUB_ENV" echo "GUIDE_DEPLOYMENT_ENVIRONMENT=guide-production" >> "$GITHUB_ENV" echo "GUIDE_PRODUCTION_ENVIRONMENT=true" >> "$GITHUB_ENV" diff --git a/README.md b/README.md index bd4578c9e..34a8e1e32 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ yarn ci:test For a headless run of a single suite, use `yarn vitest --browser.headless --run` for docs or `yarn vitest:guide --browser.headless --run` for the guide. -Visual screenshot baselines are generated in CI's pinned Linux browser environment. Local snapshot updates are blocked. Push your feature branch, then open [Update Visual Regression Screenshots](https://github.com/rescript-lang/rescript-lang.org/actions/workflows/update-visual-screenshots.yml), select that branch, and choose **Run workflow**. The workflow refuses `master` and commits any changed PNGs back to the selected branch. +Visual screenshot baselines are generated in CI's pinned Linux browser environment. Local snapshot updates are blocked. Push your feature branch, then open [Update Visual Regression Screenshots](https://github.com/rescript-lang/rescript-lang.org/actions/workflows/update-visual-screenshots.yml), select that branch, and choose **Run workflow**. The workflow refuses the default branch and commits any changed PNGs back to the selected branch. You can also dispatch the workflow with the GitHub CLI, replacing `` with your pushed branch: diff --git a/apps/docs/scripts/__tests__/LighthouseFixtures.res b/apps/docs/scripts/__tests__/LighthouseFixtures.res index 4c2f194f4..6245d27d4 100644 --- a/apps/docs/scripts/__tests__/LighthouseFixtures.res +++ b/apps/docs/scripts/__tests__/LighthouseFixtures.res @@ -27,7 +27,8 @@ type commentInput = { } @module("../lighthouse-report.mjs") -external artifactName: (string, ~environment: string=?) => string = "lighthouseArtifactName" +external artifactName: (string, ~environment: string=?, ~productionBranch: string=?) => string = + "lighthouseArtifactName" @module("../lighthouse-report.mjs") external median: array => float = "median" @module("../lighthouse-report.mjs") external createBaseline: baselineInput => baseline = "createBaseline" diff --git a/apps/docs/scripts/__tests__/LighthouseReport_.test.res b/apps/docs/scripts/__tests__/LighthouseReport_.test.res index 1ef51a79b..3b30f450f 100644 --- a/apps/docs/scripts/__tests__/LighthouseReport_.test.res +++ b/apps/docs/scripts/__tests__/LighthouseReport_.test.res @@ -44,6 +44,18 @@ test("artifact-name CLI rejects an invalid environment", async () => { expect(result.stderr)->toContain("Invalid Lighthouse deployment environment: staging") }) +test("artifact-name CLI follows the renamed production branch", async () => { + let state = await workspace() + let result = run(state, ["artifact-name", "v12", "production", "v12"], ()) + expectSuccess(result) + let production = artifactName("v12", ~environment="production", ~productionBranch="v12") + expect(result.stdout)->toBe(`${production}\n`) + expect(production)->toBe(artifactName("v12", ~productionBranch="v12")) + expect(production != artifactName("v12", ~environment="preview", ~productionBranch="v12"))->toBe( + true, + ) +}) + for_([[], [""]])("artifact-name CLI rejects missing or empty branch %j", async arguments => { let state = await workspace() let result = run(state, ["artifact-name", ...arguments], ()) diff --git a/apps/docs/scripts/__tests__/LighthouseWorkflowFixtures.res b/apps/docs/scripts/__tests__/LighthouseWorkflowFixtures.res index 50dd575e0..5502cb955 100644 --- a/apps/docs/scripts/__tests__/LighthouseWorkflowFixtures.res +++ b/apps/docs/scripts/__tests__/LighthouseWorkflowFixtures.res @@ -62,6 +62,7 @@ let fixture = async (~overrides=[], ()) => { ("CALLS", join([directory, "calls.jsonl"])), ("GITHUB_ENV", join([directory, "environment"])), ("GITHUB_EVENT_NAME", "pull_request"), + ("PRODUCTION_BRANCH", "master"), ("DEPLOY_ENVIRONMENT", ""), ("DOCS_DEPLOYMENT_ENVIRONMENT", ""), ("GITHUB_REPOSITORY", "owner/site"), diff --git a/apps/docs/scripts/__tests__/LighthouseWorkflow_.test.res b/apps/docs/scripts/__tests__/LighthouseWorkflow_.test.res index 7c8ec1bbf..65ae8526a 100644 --- a/apps/docs/scripts/__tests__/LighthouseWorkflow_.test.res +++ b/apps/docs/scripts/__tests__/LighthouseWorkflow_.test.res @@ -41,6 +41,22 @@ test("preview PR audits compare against the target branch's default environment" expect(environment)->toContain("LIGHTHOUSE_TARGET_BRANCH=master\n") }) +test("PR audits use the renamed default branch's production baseline", async () => { + let state = await Workflow.fixture( + ~overrides=[ + ("PRODUCTION_BRANCH", "v12"), + ("GH_RESPONSE", "v12\n"), + ("DOCS_DEPLOYMENT_ENVIRONMENT", "preview"), + ], + (), + ) + expectSuccess(Workflow.run("lighthouse-environment.sh", state)) + let environment = await Workflow.environmentContents(state) + let artifact = LighthouseFixtures.artifactName("v12", ~productionBranch="v12") + expect(environment)->toContain(`LIGHTHOUSE_TARGET_ARTIFACT_NAME=${artifact}\n`) + expect(environment)->toContain("LIGHTHOUSE_TARGET_BRANCH=v12\n") +}) + let productionEnvironment = "SAFE_BRANCH=master\nDOCS_DEPLOYMENT_ENVIRONMENT=production\nVITE_DEPLOYMENT_URL=\nDOCS_DEPLOYMENT_URL=https://rescript-lang.org\n" let homepagePreviewEnvironment = "SAFE_BRANCH=perf-homepage-19ab1c0e\nDOCS_DEPLOYMENT_ENVIRONMENT=preview\nVITE_DEPLOYMENT_URL=https://perf-homepage-19ab1c0e.rescript-lang.pages.dev\nDOCS_DEPLOYMENT_URL=https://perf-homepage-19ab1c0e.rescript-lang.pages.dev\n" let masterPreviewEnvironment = "SAFE_BRANCH=master-fc613b4d\nDOCS_DEPLOYMENT_ENVIRONMENT=preview\nVITE_DEPLOYMENT_URL=https://master-fc613b4d.rescript-lang.pages.dev\nDOCS_DEPLOYMENT_URL=https://master-fc613b4d.rescript-lang.pages.dev\n" @@ -99,6 +115,37 @@ for_([ expect(exists(join([state.directory, "calls.jsonl"])))->toBe(false) }) +let v12ProductionEnvironment = "SAFE_BRANCH=v12\nDOCS_DEPLOYMENT_ENVIRONMENT=production\nVITE_DEPLOYMENT_URL=\nDOCS_DEPLOYMENT_URL=https://rescript-lang.org\n" +let v12PreviewEnvironment = "SAFE_BRANCH=v12-2ab8c63a\nDOCS_DEPLOYMENT_ENVIRONMENT=preview\nVITE_DEPLOYMENT_URL=https://v12-2ab8c63a.rescript-lang.pages.dev\nDOCS_DEPLOYMENT_URL=https://v12-2ab8c63a.rescript-lang.pages.dev\n" + +for_([ + ("push", "v12", "", v12ProductionEnvironment), + ("push", "master", "", masterPreviewEnvironment), + ("pull_request", "v12", "", v12PreviewEnvironment), + ("workflow_dispatch", "v12", "preview", v12PreviewEnvironment), + ("workflow_dispatch", "v12", "production", v12ProductionEnvironment), + ("workflow_dispatch", "Perf/Homepage", "production", v12ProductionEnvironment), +])("deployment after the rename for %s on %s with selection '%s'", async (( + event, + branch, + selection, + expectedEnvironment, +)) => { + let state = await Workflow.fixture( + ~overrides=[ + ("PRODUCTION_BRANCH", "v12"), + ("GITHUB_EVENT_NAME", event), + ("RAW_BRANCH", branch), + ("DEPLOY_ENVIRONMENT", selection), + ("GH_FAIL", "1"), + ], + (), + ) + expectSuccess(Workflow.run("deployment-environment.sh", state)) + expect(await Workflow.environmentContents(state))->toBe(expectedEnvironment) + expect(exists(join([state.directory, "calls.jsonl"])))->toBe(false) +}) + for_([ ("perf/fonts", "perf-fonts", "perf-fonts-a9ef7a11", "perf-fonts-a7ec124a"), ( diff --git a/apps/docs/scripts/lighthouse-report.mjs b/apps/docs/scripts/lighthouse-report.mjs index 15d5e2150..b088b6eaf 100644 --- a/apps/docs/scripts/lighthouse-report.mjs +++ b/apps/docs/scripts/lighthouse-report.mjs @@ -17,15 +17,20 @@ const reportCategoryKeys = { seo: "seo", }; -export function lighthouseArtifactName(branch, environment) { - const defaultEnvironment = branch === "master" ? "production" : "preview"; +export function lighthouseArtifactName( + branch, + environment, + productionBranch = "master", +) { + const defaultEnvironment = + branch === productionBranch ? "production" : "preview"; const effectiveEnvironment = environment || defaultEnvironment; if (!["production", "preview"].includes(effectiveEnvironment)) { throw new Error( `Invalid Lighthouse deployment environment: ${effectiveEnvironment}`, ); } - // Preserve existing production-master and branch-preview keys. Alternative + // Preserve existing production and branch-preview keys. Alternative // environments use a separate identity so they cannot overwrite those keys. const isDefaultEnvironment = effectiveEnvironment === defaultEnvironment; const identity = isDefaultEnvironment @@ -240,13 +245,13 @@ async function writeComment() { ); } -async function main(command, branch, environment) { +async function main(command, branch, environment, productionBranch) { if (command === "artifact-name") { if (!branch) { throw new Error("A branch name is required for artifact-name"); } - console.log(lighthouseArtifactName(branch, environment)); + console.log(lighthouseArtifactName(branch, environment, productionBranch)); return; } @@ -268,5 +273,10 @@ const entryPath = process.argv[1] : ""; if (import.meta.url === entryPath) { - await main(process.argv[2], process.argv[3], process.argv[4]); + await main( + process.argv[2], + process.argv[3], + process.argv[4], + process.argv[5], + ); } diff --git a/apps/guide/README.md b/apps/guide/README.md index 9504eec65..f257f320e 100644 --- a/apps/guide/README.md +++ b/apps/guide/README.md @@ -97,7 +97,7 @@ interactive compiler and runtime then run in the learner's browser. editor drafts, pane sizes, and theme in browser local storage. - Browser history support, light and dark themes, resizable instruction/output panes, Cloudflare preview deployments, and production deployment from - `master`. + the repository's default branch. The current interface is intentionally desktop-only: widths below `1024px` show a stop screen rather than a usable guide. @@ -134,6 +134,6 @@ Track outstanding guide launch work in this checklist: ## Deployment The `deploy-guide` job in `.github/workflows/deploy.yml` builds the guide and -deploys its `build/client/` assets with Wrangler. Pushes to `master` deploy the +deploys its `build/client/` assets with Wrangler. Pushes to the default branch deploy the production Worker; non-Dependabot pull requests from this repository receive a preview Worker version and a link in the pull request.