fix(release): bake the workspace-team gate into stable and preview builds (#6468)

release-stable.yml and release-preview.yml never set OPEN_DESIGN_AMR_PROFILE
or OD_VELA_WEB_URL. Neither has a default in tools-pack, so velaWebUrl reached
the packaged config as undefined, workspaceTeamTransportEnv returned an empty
record, and the four vela transports were never injected into the daemon spawn
env. Both channels therefore shipped clients with Workspace Team silently
dormant — the build succeeds, the app installs and starts, and the absence only
surfaces once a package is in a user's hands.

beta and prerelease already inject the pair from their amr_profile input, which
is why prerelease validation looked correct: the artifact under test had the
feature and the stable artifact promoted from it would not have.

Stable and preview are production channels, so the pair is pinned instead of
dispatched — there is no legitimate stable build aimed at the test backend.
Naming prod explicitly is a no-op for AMR itself (the daemon already defaults to
prod in integrations/vela-profile.ts); it exists to satisfy the profile half of
the gate. A fork or PR build without the secret leaves the origin empty, so the
transports stay dormant rather than half-enabled.
This commit is contained in:
lefarcen
2026-08-05 11:58:49 +00:00
committed by GitHub
parent 8e3853f0bc
commit 3371ce4b2a
3 changed files with 66 additions and 0 deletions
+16
View File
@@ -100,6 +100,22 @@ concurrency:
cancel-in-progress: false
env:
# Workspace Team's four vela transports are gated in the packaged runtime on
# BOTH a known AMR profile and a non-empty vela web origin
# (workspaceTeamTransportEnv in apps/packaged/src/workspace-team.ts). Neither
# has a default in tools-pack, so a lane that omits them ships a client whose
# team features are silently dormant: the build succeeds, the app starts, and
# the gap only shows up in a user's hands.
#
# Unlike beta/prerelease these are production channels, so the profile is
# pinned rather than taken from an input — there is no legitimate stable or
# preview build aimed at the test backend. Naming prod explicitly is a no-op
# for AMR itself (apps/daemon/src/integrations/vela-profile.ts already
# defaults to prod); it exists to satisfy the first half of the gate. A fork
# or PR build without the secret leaves the origin empty, which keeps the
# transports dormant rather than half-enabled.
OPEN_DESIGN_AMR_PROFILE: prod
OD_VELA_WEB_URL: ${{ secrets.VELA_WEB_URL_PROD }}
OPEN_DESIGN_TELEMETRY_RELAY_URL: ${{ vars.OPEN_DESIGN_TELEMETRY_RELAY_URL }}
# PostHog product-analytics ingest. Defined as repository secret + var
# so official builds ship with analytics enabled; PR builds and forks
+16
View File
@@ -119,6 +119,22 @@ concurrency:
cancel-in-progress: false
env:
# Workspace Team's four vela transports are gated in the packaged runtime on
# BOTH a known AMR profile and a non-empty vela web origin
# (workspaceTeamTransportEnv in apps/packaged/src/workspace-team.ts). Neither
# has a default in tools-pack, so a lane that omits them ships a client whose
# team features are silently dormant: the build succeeds, the app starts, and
# the gap only shows up in a user's hands.
#
# Unlike beta/prerelease these are production channels, so the profile is
# pinned rather than taken from an input — there is no legitimate stable or
# preview build aimed at the test backend. Naming prod explicitly is a no-op
# for AMR itself (apps/daemon/src/integrations/vela-profile.ts already
# defaults to prod); it exists to satisfy the first half of the gate. A fork
# or PR build without the secret leaves the origin empty, which keeps the
# transports dormant rather than half-enabled.
OPEN_DESIGN_AMR_PROFILE: prod
OD_VELA_WEB_URL: ${{ secrets.VELA_WEB_URL_PROD }}
OPEN_DESIGN_TELEMETRY_RELAY_URL: ${{ vars.OPEN_DESIGN_TELEMETRY_RELAY_URL }}
# PostHog product-analytics ingest. Defined as repository secret + var
# so official builds ship with analytics enabled; PR builds and forks
@@ -394,6 +394,40 @@ describe("release workflows", () => {
expect(stablePrepare).toContain('setOutput("publish_side_effects_enabled"');
});
it("bakes both halves of the workspace-team gate into every shipping lane", async () => {
const [beta, preview, prerelease, stable] = await Promise.all([
readFile(new URL("../../../.github/workflows/release-beta.yml", import.meta.url), "utf8"),
readFile(new URL("../../../.github/workflows/release-preview.yml", import.meta.url), "utf8"),
readFile(new URL("../../../.github/workflows/release-prerelease.yml", import.meta.url), "utf8"),
readFile(new URL("../../../.github/workflows/release-stable.yml", import.meta.url), "utf8"),
]);
// workspaceTeamTransportEnv (apps/packaged/src/workspace-team.ts) enables the
// four vela transports only when a known AMR profile AND a non-empty vela web
// origin are both baked in. A lane that bakes neither still builds, still
// installs, and still starts — the gap only surfaces as "Workspace Team does
// nothing" once a package reaches a user. So the presence of both halves is
// asserted per lane rather than left to the packaging step to notice.
for (const workflow of [beta, preview, prerelease, stable]) {
expect(workflow).toContain("OPEN_DESIGN_AMR_PROFILE:");
expect(workflow).toContain("OD_VELA_WEB_URL:");
}
// beta and prerelease are validation lanes and stay dispatch-driven, so an
// operator can aim a build at feature-test or test.
expect(beta).toContain("OPEN_DESIGN_AMR_PROFILE: ${{ inputs.amr_profile }}");
expect(prerelease).toContain("OPEN_DESIGN_AMR_PROFILE: ${{ inputs.amr_profile }}");
// preview and stable are production channels by definition. Pinning the pair
// instead of accepting an input removes the footgun of publishing a stable
// build wired to the test backend — there is no legitimate reason for one.
for (const workflow of [preview, stable]) {
expect(workflow).toContain("OPEN_DESIGN_AMR_PROFILE: prod");
expect(workflow).toContain("OD_VELA_WEB_URL: ${{ secrets.VELA_WEB_URL_PROD }}");
expect(workflow).not.toContain("inputs.amr_profile");
}
});
it("passes launcher version floor repo vars through to metadata publish and verify verbatim", async () => {
const [beta, betaSelfHosted, preview, prerelease, stable] = await Promise.all([
readFile(new URL("../../../.github/workflows/release-beta.yml", import.meta.url), "utf8"),