feat(standalone): complete Terminal exact lifecycle (#7319)

* feat(standalone): bootstrap terminal exact lifecycle

* feat(standalone): complete terminal exact lifecycle

* fix(ci): build standalone during postinstall

* fix(standalone): repair exact lifecycle recovery

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(standalone): make exact updates replay-safe

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(terminal): enforce replay-safe exact carrier

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(standalone): align active update replay state

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(standalone): preserve exact channel lifecycle

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
This commit is contained in:
PerishFire
2026-08-25 03:56:31 +00:00
committed by GitHub
co-authored by Looper
parent 7bdf9978aa
commit d03bf69d58
45 changed files with 2582 additions and 1 deletions
+12
View File
@@ -35,6 +35,12 @@
"packages/sidecar/",
"packages/sidecar-proto/"
],
"terminal-scene": [
"suite://workspace-install",
"apps/closure/",
"packages/standalone/",
"shells/terminal/"
],
"daemon": [
"suite://workspace-install",
"apps/daemon/",
@@ -168,6 +174,12 @@
"runnerClass": "visual_hot",
"products": "manifest",
"reusable": false
},
"terminal_scene": {
"inputs": ["suite://terminal-scene"],
"runnerClass": "workspace_unit",
"products": "manifest",
"reusable": true
}
}
}
+9
View File
@@ -5,6 +5,7 @@
"web_tests_required",
"tools_dev_tests_required",
"tools_pack_tests_required",
"terminal_scene_required",
"windows_tools_pack_payload_tests_required",
"ui_critical_validation_required",
"ui_p0_validation_required",
@@ -156,6 +157,14 @@
},
"effects": ["tools_pack_tests_required"], "confidence": "medium"
},
{
"id": "terminal-scene-sources",
"match": {
"prefixes": ["apps/closure/", "packages/standalone/", "shells/terminal/"],
"exact": [".github/scripts/pack.py", ".github/scripts/release.py", ".github/workflows/release-exact.yml"]
},
"effects": ["terminal_scene_required", "workspace_validation_required"], "confidence": "certain"
},
{
"id": "workspace-manifests-and-ci",
"match": {
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env python3
"""Orchestrate Terminal scene build/reuse and owner-controlled promotion."""
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
import re
import subprocess
import tempfile
CHANNELS = {"betahyx", "previewhyx"}
TARGETS = {"darwin-arm64", "win32-x64"}
def read_json(path: Path) -> dict:
return json.loads(path.read_text(encoding="utf-8"))
def write_json(path: Path, value: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8")
def run(command: list[str]) -> None:
subprocess.run(command, check=True)
def run_terminal(request: Path, receipt: Path) -> None:
run(["pnpm", "--filter", "@open-design/terminal", "exact:pack", "--", "--request", str(request), "--receipt", str(receipt)])
def signing_keys() -> list[tuple[str, str]]:
keys = []
for suffix in ("", "_NEXT"):
key_id = os.environ.get(f"OD_EXACT_SIGNING_KEY_ID{suffix}")
key = os.environ.get(f"OD_EXACT_ED25519_PRIVATE_KEY{suffix}")
key_file = os.environ.get(f"OD_EXACT_ED25519_PRIVATE_KEY_FILE{suffix}")
if not key and key_file:
key = Path(key_file).read_text(encoding="utf-8")
if key_id or key:
if not key_id or not key:
raise SystemExit(f"incomplete signing key pair: {suffix or 'primary'}")
keys.append((key_id, key))
if not keys:
raise SystemExit("at least one exact signing key is required")
return keys
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--request", required=True, type=Path)
parser.add_argument("--receipt", required=True, type=Path)
args = parser.parse_args()
request = read_json(args.request.resolve())
if request.get("schemaVersion") != 1 or request.get("operation") != "exact.pack":
raise SystemExit("unsupported exact pack request")
channel = request.get("channel")
if channel not in CHANNELS:
raise SystemExit("channel must be betahyx or previewhyx")
release_version = request.get("releaseVersion", "")
if re.fullmatch(rf"\d+\.\d+\.\d+-{channel}\.\d+", release_version) is None:
raise SystemExit("releaseVersion does not belong to the requested channel")
source_commit = request.get("sourceCommit", "")
if re.fullmatch(r"[a-f0-9]{40}", source_commit) is None:
raise SystemExit("sourceCommit must be a full 40-character SHA")
targets = request.get("targets")
if not isinstance(targets, list) or {item.get("target") for item in targets} != TARGETS:
raise SystemExit("targets must contain exactly darwin-arm64 and win32-x64")
output = Path(request["outputDirectory"]).resolve()
scene = Path(request["sceneDirectory"]).resolve()
contracts = output / ".contracts"
contracts.mkdir(parents=True, exist_ok=True)
scene_reused = (scene / "scene.json").is_file()
if not scene_reused:
run(["pnpm", "--filter", "@open-design/closure", "build"])
closure_package = read_json(Path("apps/closure/package.json"))
scene_request = contracts / "terminal-scene-build-request.json"
scene_receipt = contracts / "terminal-scene-build-receipt.json"
write_json(scene_request, {
"schemaVersion": 1,
"operation": "terminal.scene.build",
"standaloneVersion": request["standaloneVersion"],
"closureVersion": closure_package["version"],
"closureArtifactFile": str(Path("apps/closure/dist/fixture.mjs").resolve()),
"targets": targets,
"sceneDirectory": str(scene),
})
run_terminal(scene_request, scene_receipt)
scene_manifest = read_json(scene / "scene.json")
if scene_manifest.get("standaloneVersion") != request.get("standaloneVersion"):
raise SystemExit("requested standaloneVersion differs from Terminal scene")
temporary_files: list[Path] = []
try:
signers = []
for key_id, key in signing_keys():
handle = tempfile.NamedTemporaryFile("w", encoding="utf-8", delete=False)
with handle:
handle.write(key)
path = Path(handle.name)
temporary_files.append(path)
signers.append({"keyId": key_id, "privateKeyFile": str(path)})
promote_request = contracts / "terminal-scene-promote-request.json"
promote_receipt = contracts / "terminal-scene-promote-receipt.json"
write_json(promote_request, {
"schemaVersion": 1,
"operation": "terminal.scene.promote",
"sceneDirectory": str(scene),
"sourceCommit": source_commit,
"channel": channel,
"releaseVersion": release_version,
"publishedAt": request["publishedAt"],
"artifactBaseUrl": request["artifactBaseUrl"].rstrip("/"),
"outputDirectory": str(output),
"signers": signers,
})
run_terminal(promote_request, promote_receipt)
finally:
for path in temporary_files:
path.unlink(missing_ok=True)
promoted = read_json(promote_receipt)
write_json(args.receipt.resolve(), {
"schemaVersion": 1,
"operation": "exact.pack",
"channel": channel,
"releaseVersion": release_version,
"sourceCommit": source_commit,
"sceneDirectory": str(scene),
"sceneDigest": promoted["sceneDigest"],
"sceneReused": scene_reused,
"artifacts": promoted["artifacts"],
"documents": promoted["documents"],
"closureMetadataFile": promoted["closureMetadataFile"],
"terminalMetadataFile": promoted["terminalMetadataFile"],
"channelHeadFile": promoted["channelHeadFile"],
})
if __name__ == "__main__":
main()
+132
View File
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
"""Publish exact receipts: immutable objects, readback, then monotonic latest CAS."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import urllib.error
import urllib.request
CHANNELS = {"betahyx", "previewhyx"}
def read_json(path: Path) -> dict:
return json.loads(path.read_text(encoding="utf-8"))
def write_json(path: Path, value: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8")
def request(url: str, method: str = "GET", body: bytes | None = None, headers: dict[str, str] | None = None):
request_headers = dict(headers or {})
token = os.environ.get("OD_EXACT_RELEASE_TOKEN")
if token:
request_headers["Authorization"] = f"Bearer {token}"
req = urllib.request.Request(url, data=body, headers=request_headers, method=method)
try:
return urllib.request.urlopen(req)
except urllib.error.HTTPError as error:
return error
def put_immutable(url: str, body: bytes, content_type: str) -> str:
response = request(url, "PUT", body, {"If-None-Match": "*", "Content-Type": content_type, "Cache-Control": "public, max-age=31536000, immutable"})
if response.status == 412:
current = request(url)
if current.status != 200 or current.read() != body:
raise SystemExit(f"immutable object collision: {url}")
return current.headers.get("ETag", "")
if response.status != 200:
raise SystemExit(f"immutable upload failed ({response.status}): {url}")
return response.headers.get("ETag", "")
def release_number(version: str, channel: str) -> tuple[tuple[int, int, int], int]:
match = re.fullmatch(rf"(\d+)\.(\d+)\.(\d+)-{channel}\.(\d+)", version)
if match is None:
raise SystemExit("invalid counted release version")
return ((int(match[1]), int(match[2]), int(match[3])), int(match[4]))
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--request", required=True, type=Path)
parser.add_argument("--receipt", required=True, type=Path)
args = parser.parse_args()
publish = read_json(args.request.resolve())
if publish.get("schemaVersion") != 1 or publish.get("operation") != "exact.release":
raise SystemExit("unsupported exact release request")
pack = read_json(Path(publish["packReceipt"]).resolve())
channel = pack.get("channel")
if channel not in CHANNELS:
raise SystemExit("release channel must be betahyx or previewhyx")
endpoint = publish["endpointUrl"].rstrip("/")
bucket = publish["bucket"].strip("/")
prefix = f"{endpoint}/{bucket}/{channel}"
version = pack["releaseVersion"]
uploaded: list[dict] = []
for artifact in pack["artifacts"]:
path = Path(artifact["file"]).resolve()
body = path.read_bytes()
if hashlib.sha256(body).hexdigest() != artifact["sha256"] or len(body) != artifact["size"]:
raise SystemExit(f"pack receipt verification failed: {path}")
url = f"{prefix}/{version}/{path.name}"
etag = put_immutable(url, body, "application/octet-stream")
uploaded.append({"url": url, "etag": etag, "sha256": artifact["sha256"]})
documents = []
for document in pack["documents"]:
path = Path(document["file"]).resolve()
body = path.read_bytes()
if hashlib.sha256(body).hexdigest() != document["sha256"] or len(body) != document["size"]:
raise SystemExit(f"document receipt verification failed: {path}")
url = f"{prefix}/{version}/{path.name}"
etag = put_immutable(url, body, "application/json; charset=utf-8")
readback = request(url)
if readback.status != 200 or readback.read() != body:
raise SystemExit(f"exact document readback failed: {url}")
documents.append({"url": url, "etag": etag, "sha256": document["sha256"]})
head_path = Path(pack["channelHeadFile"]).resolve()
head_body = head_path.read_bytes()
latest_url = f"{prefix}/latest/channel-head.json"
current = request(latest_url)
headers = {"Content-Type": "application/json; charset=utf-8", "Cache-Control": "public, max-age=60"}
if current.status == 404:
headers["If-None-Match"] = "*"
elif current.status == 200:
current_body = current.read()
if current_body == head_body:
latest_etag = current.headers.get("ETag", "")
write_json(args.receipt.resolve(), {"schemaVersion": 1, "operation": "exact.release", "channel": channel, "releaseVersion": version, "latestChannelHeadUrl": latest_url, "latestChannelHeadEtag": latest_etag, "documents": documents, "artifacts": uploaded, "replayed": True})
return
current_head = json.loads(current_body)["head"]
incoming_head = json.loads(head_body)["head"]
advanced = False
for lane in ("closure", "terminal"):
current_version = current_head["lanes"][lane]["releaseVersion"]
incoming_version = incoming_head["lanes"][lane]["releaseVersion"]
order = release_number(incoming_version, channel) > release_number(current_version, channel)
if release_number(incoming_version, channel) < release_number(current_version, channel):
raise SystemExit(f"{lane} lane would move backward: {current_version} -> {incoming_version}")
advanced = advanced or order
if not advanced:
raise SystemExit("channel head CAS would not advance either lane")
headers["If-Match"] = current.headers.get("ETag", "")
else:
raise SystemExit(f"latest inspection failed ({current.status})")
promoted = request(latest_url, "PUT", head_body, headers)
if promoted.status != 200:
raise SystemExit(f"latest CAS failed ({promoted.status})")
write_json(args.receipt.resolve(), {"schemaVersion": 1, "operation": "exact.release", "channel": channel, "releaseVersion": version, "latestChannelHeadUrl": latest_url, "latestChannelHeadEtag": promoted.headers.get("ETag", ""), "documents": documents, "artifacts": uploaded, "replayed": False})
if __name__ == "__main__":
main()
+2
View File
@@ -25,6 +25,7 @@ WORKLOADS = {
"playwright_critical",
"ui_p0",
"playwright_visual",
"terminal_scene",
}
@@ -195,6 +196,7 @@ def enabled_workloads(outputs, ci_mode, full_lanes):
"playwright_critical": outputs["ui_critical_validation_required"] and not ui_p0,
"ui_p0": ui_p0,
"playwright_visual": full_lanes or outputs["visual_validation_required"],
"terminal_scene": full_lanes or outputs["terminal_scene_required"],
}
if set(enabled) != WORKLOADS:
raise AssertionError("scope workload map drifted")
+99
View File
@@ -321,6 +321,11 @@ jobs:
pnpm --filter @open-design/platform test
pnpm --filter @open-design/sidecar test
pnpm --filter @open-design/sidecar-proto test
if [ "${{ fromJSON(needs.plan.outputs.scopes).terminal_scene_required }}" = "true" ]; then
pnpm --filter @open-design/standalone test
pnpm --filter @open-design/closure test
pnpm --filter @open-design/terminal test
fi
if [ "${{ fromJSON(needs.plan.outputs.scopes).tools_dev_tests_required }}" = "true" ]; then
pnpm --filter @open-design/tools-dev test
fi
@@ -657,6 +662,91 @@ jobs:
if: ${{ steps.visual.outcome != 'success' }}
run: exit 1
terminal_scene:
name: Terminal distribution scene
needs: [plan, runners]
if: ${{ fromJSON(needs.plan.outputs.run).terminal_scene }}
runs-on: ${{ fromJSON(needs.runners.outputs.runs_on).workspace_unit }}
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
- name: Setup workspace
uses: ./.github/actions/setup-workspace
with:
runner-labels: ${{ toJSON(fromJSON(needs.runners.outputs.runs_on).workspace_unit) }}
- name: Download convergence identity
uses: actions/download-artifact@v8
with:
name: ${{ needs.plan.outputs.convergence_plan_artifact }}
path: ${{ runner.temp }}/terminal-convergence
- name: Fetch locked official Node carriers
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/terminal-node"
python3 - <<'PY'
import hashlib, json, os, urllib.request
from pathlib import Path
lock = json.loads(Path('shells/terminal/node-lock.json').read_text())
root = Path(os.environ['RUNNER_TEMP']) / 'terminal-node'
for value in lock['targets'].values():
destination = root / value['archive']
urllib.request.urlretrieve(value['url'], destination)
if hashlib.sha256(destination.read_bytes()).hexdigest() != value['sha256']:
raise SystemExit(f"official Node digest mismatch: {destination.name}")
PY
- name: Build release-neutral Terminal scene
run: |
set -euo pipefail
pnpm --filter @open-design/closure build
pnpm --filter @open-design/terminal build
python3 - <<'PY'
import json, os
from pathlib import Path
lock = json.loads(Path('shells/terminal/node-lock.json').read_text())
closure = json.loads(Path('apps/closure/package.json').read_text())
standalone = json.loads(Path('packages/standalone/package.json').read_text())
root = Path(os.environ['RUNNER_TEMP'])
targets = [{'target': target, 'nodeArchiveFile': str((root / 'terminal-node' / value['archive']).resolve()), 'nodeArchiveSha256': value['sha256']} for target, value in lock['targets'].items()]
request = {'schemaVersion': 1, 'operation': 'terminal.scene.build', 'standaloneVersion': standalone['version'], 'closureVersion': closure['version'], 'closureArtifactFile': str(Path('apps/closure/dist/fixture.mjs').resolve()), 'targets': targets, 'sceneDirectory': str((root / 'terminal-scene').resolve())}
(root / 'terminal-scene-request.json').write_text(json.dumps(request, sort_keys=True, separators=(',', ':')) + '\n')
PY
node shells/terminal/dist/exact-pack.mjs --request "$RUNNER_TEMP/terminal-scene-request.json" --receipt "$RUNNER_TEMP/terminal-scene-receipt.json"
- name: Write generic convergence product manifest
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/convergence-manifest/terminal_scene"
python3 - <<'PY'
import json, os
from pathlib import Path
root = Path(os.environ['RUNNER_TEMP'])
pending = json.loads((root / 'terminal-convergence' / 'pending-convergence.json').read_text())
workload = pending['workloads']['terminal_scene']
manifest = {'workload': 'terminal_scene', 'digest': workload['digest'], 'executionClass': workload['executionClass'], 'products': {'scene': {'type': 'job', 'source': 'terminal_scene', 'data': {'contract': 'terminal-scene-v1', 'owner': 'shells/terminal'}}}}
(root / 'convergence-manifest' / 'terminal_scene' / 'product-manifest.json').write_text(json.dumps(manifest, sort_keys=True, separators=(',', ':')) + '\n')
PY
- name: Upload Terminal scene product
uses: actions/upload-artifact@v4
with:
name: terminal_scene
path: ${{ runner.temp }}/terminal-scene
if-no-files-found: error
retention-days: 1
- name: Upload Terminal scene convergence manifest
uses: actions/upload-artifact@v4
with:
name: terminal-scene-convergence-manifest
path: ${{ runner.temp }}/convergence-manifest
if-no-files-found: error
retention-days: 1
# This policy is a sibling of the validation workloads, not their prerequisite.
# Labeled merge groups still receive the same workload coverage before the
# required Validate workspace check rejects the converged result.
@@ -868,6 +958,7 @@ jobs:
- playwright_critical
- ui_p0
- playwright_visual
- terminal_scene
if: ${{ always() }}
runs-on: ${{ fromJSON(needs.runners.outputs.runs_on).control }}
timeout-minutes: 5
@@ -910,6 +1001,14 @@ jobs:
continue-on-error: true
uses: actions/checkout@v6.0.2
- name: Download Terminal scene convergence manifest
if: ${{ (github.event_name == 'pull_request' || github.event_name == 'merge_group') && fromJSON(needs.plan.outputs.run).terminal_scene && needs.terminal_scene.result == 'success' }}
continue-on-error: true
uses: actions/download-artifact@v8
with:
name: terminal-scene-convergence-manifest
path: ${{ runner.temp }}/convergence-products
- name: Download pending convergence plan
if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }}
continue-on-error: true
+261
View File
@@ -0,0 +1,261 @@
name: Release exact Terminal reference
on:
workflow_dispatch:
inputs:
source_sha:
description: Full 40-character source commit to build
required: true
type: string
channel:
description: Isolated exact channel
required: true
type: choice
options: [betahyx, previewhyx]
release_version:
description: Counted version, for example 0.1.0-betahyx.1
required: true
type: string
standalone_version:
description: Independent standalone protocol/runtime version
required: true
default: 0.1.0
type: string
permissions:
contents: read
concurrency:
group: release-exact-${{ inputs.channel }}
cancel-in-progress: false
jobs:
scene:
name: Resolve release-neutral Terminal scene
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- name: Validate immutable inputs
env:
SOURCE_SHA: ${{ inputs.source_sha }}
CHANNEL: ${{ inputs.channel }}
RELEASE_VERSION: ${{ inputs.release_version }}
run: |
[[ "$SOURCE_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$CHANNEL" == "betahyx" || "$CHANNEL" == "previewhyx" ]]
[[ "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+-${CHANNEL}\.[0-9]+$ ]]
- uses: actions/checkout@v6
with:
ref: ${{ inputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify checkout identity
env:
SOURCE_SHA: ${{ inputs.source_sha }}
run: |
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
git merge-base --is-ancestor "$SOURCE_SHA" origin/main
- uses: pnpm/action-setup@v5
with:
version: 10.33.2
- uses: actions/setup-node@v6
with:
node-version: 24.18.0
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Restore converged Terminal scene when available
id: convergence
env:
OD_WORKLOAD_RESULTS_BASE_URL: ${{ secrets.OD_WORKLOAD_RESULTS_BASE_URL }}
run: |
set -euo pipefail
python3 .github/scripts/runners.py
python3 - <<'PY'
import json
from pathlib import Path
workloads = ['static_gate', 'preflight', 'workspace_unit_tests', 'daemon_unit_tests', 'windows_tools_pack_payload_tests', 'web_workspace_tests', 'e2e_vitest', 'playwright_critical', 'ui_p0', 'playwright_visual', 'terminal_scene']
Path('.tmp/exact').mkdir(parents=True, exist_ok=True)
Path('.tmp/exact/scope-plan.json').write_text(json.dumps({'enabled': {name: name == 'terminal_scene' for name in workloads}}, sort_keys=True) + '\n')
PY
RUNNER_PLAN_JSON="$(python3 - <<'PY'
import os
from pathlib import Path
lines = Path(os.environ['GITHUB_OUTPUT']).read_text().splitlines()
print(next(line.split('=', 1)[1] for line in lines if line.startswith('runs_on=')))
PY
)"
python3 .github/scripts/convergence.py github-output --workflow ci --scope-plan .tmp/exact/scope-plan.json --runner-plan-json "$RUNNER_PLAN_JSON" --mode enforce --pending .tmp/exact/pending-convergence.json
python3 - <<'PY'
import hashlib, json, os, urllib.request, zipfile
from pathlib import Path, PurePosixPath
root = Path('.tmp/exact')
workload = json.loads((root / 'pending-convergence.json').read_text())['workloads']['terminal_scene']
result = workload.get('result')
if not workload.get('resultHit') or not result:
with Path(os.environ['GITHUB_OUTPUT']).open('a') as output:
output.write('hit=false\n')
raise SystemExit(0)
product = result['products']['scene']
archive = root / 'terminal-scene.zip'
urllib.request.urlretrieve(product['source'], archive)
if hashlib.sha256(archive.read_bytes()).hexdigest() != product['data']['sha256']:
raise SystemExit('converged Terminal scene digest mismatch')
destination = root / 'scene'
destination.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(archive) as source:
for entry in source.infolist():
path = PurePosixPath(entry.filename)
if entry.filename.startswith(('/', '\\')) or '\\' in entry.filename or '..' in path.parts:
raise SystemExit(f'unsafe converged scene entry: {entry.filename!r}')
source.extractall(destination)
with Path(os.environ['GITHUB_OUTPUT']).open('a') as output:
output.write('hit=true\n')
PY
- name: Fetch locked official Node carriers on convergence miss
if: steps.convergence.outputs.hit != 'true'
run: |
set -euo pipefail
mkdir -p .tmp/exact/node
python3 - <<'PY'
import hashlib, json, urllib.request
from pathlib import Path
lock = json.loads(Path('shells/terminal/node-lock.json').read_text())
root = Path('.tmp/exact/node')
for value in lock['targets'].values():
destination = root / value['archive']
urllib.request.urlretrieve(value['url'], destination)
if hashlib.sha256(destination.read_bytes()).hexdigest() != value['sha256']:
raise SystemExit(f"official Node digest mismatch: {destination.name}")
PY
- name: Build Terminal-owned scene on convergence miss
if: steps.convergence.outputs.hit != 'true'
run: |
set -euo pipefail
pnpm --filter @open-design/closure build
pnpm --filter @open-design/terminal build
python3 - <<'PY'
import json
from pathlib import Path
lock = json.loads(Path('shells/terminal/node-lock.json').read_text())
closure = json.loads(Path('apps/closure/package.json').read_text())
standalone = json.loads(Path('packages/standalone/package.json').read_text())
targets = [{'target': target, 'nodeArchiveFile': str((Path('.tmp/exact/node') / value['archive']).resolve()), 'nodeArchiveSha256': value['sha256']} for target, value in lock['targets'].items()]
request = {'schemaVersion': 1, 'operation': 'terminal.scene.build', 'standaloneVersion': standalone['version'], 'closureVersion': closure['version'], 'closureArtifactFile': str(Path('apps/closure/dist/fixture.mjs').resolve()), 'targets': targets, 'sceneDirectory': str(Path('.tmp/exact/scene').resolve())}
Path('.tmp/exact/scene-request.json').write_text(json.dumps(request, sort_keys=True, separators=(',', ':')) + '\n')
PY
node shells/terminal/dist/exact-pack.mjs --request .tmp/exact/scene-request.json --receipt .tmp/exact/scene-receipt.json
- name: Verify scene remains release-neutral
run: |
test -f .tmp/exact/scene/scene.json
! grep -Eq 'betahyx|previewhyx|publishedAt|artifactBaseUrl|signatures' .tmp/exact/scene/scene.json
- uses: actions/upload-artifact@v4
with:
name: exact-terminal-scene-${{ inputs.source_sha }}
path: .tmp/exact/scene
if-no-files-found: error
retention-days: 1
publish:
name: Promote, sign, and publish exact release
needs: [scene]
runs-on: ubuntu-latest
environment: standalone-exact-release
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify checkout identity
env:
SOURCE_SHA: ${{ inputs.source_sha }}
run: |
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
git merge-base --is-ancestor "$SOURCE_SHA" origin/main
- uses: pnpm/action-setup@v5
with:
version: 10.33.2
- uses: actions/setup-node@v6
with:
node-version: 24.18.0
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@v8
with:
name: exact-terminal-scene-${{ inputs.source_sha }}
path: .tmp/exact/scene
- name: Write promotion request
env:
SOURCE_SHA: ${{ inputs.source_sha }}
CHANNEL: ${{ inputs.channel }}
RELEASE_VERSION: ${{ inputs.release_version }}
STANDALONE_VERSION: ${{ inputs.standalone_version }}
STORAGE_ENDPOINT: ${{ secrets.EXACT_RELEASE_ENDPOINT }}
STORAGE_BUCKET: ${{ secrets.EXACT_RELEASE_BUCKET }}
run: |
python3 - <<'PY'
import json, os, subprocess
from pathlib import Path
root = Path('.tmp/exact')
output = root / 'output'
output.mkdir(parents=True, exist_ok=True)
channel = os.environ['CHANNEL']
version = os.environ['RELEASE_VERSION']
published_at = subprocess.check_output(['git', 'show', '--no-patch', '--format=%cI', os.environ['SOURCE_SHA']], text=True).strip()
prefix = f"{os.environ['STORAGE_ENDPOINT'].rstrip('/')}/{os.environ['STORAGE_BUCKET'].strip('/')}/{channel}/{version}"
lock = json.loads(Path('shells/terminal/node-lock.json').read_text())
targets = [{'target': target, 'nodeArchiveFile': f"/convergence-scene/{value['archive']}", 'nodeArchiveSha256': value['sha256']} for target, value in lock['targets'].items()]
request = {'schemaVersion': 1, 'operation': 'exact.pack', 'channel': channel, 'releaseVersion': version, 'standaloneVersion': os.environ['STANDALONE_VERSION'], 'sourceCommit': os.environ['SOURCE_SHA'], 'publishedAt': published_at, 'artifactBaseUrl': prefix, 'outputDirectory': str(output.resolve()), 'sceneDirectory': str((root / 'scene').resolve()), 'targets': targets}
(root / 'pack-request.json').write_text(json.dumps(request, sort_keys=True, separators=(',', ':')) + '\n')
PY
- name: Promote and sign Terminal scene
env:
OD_EXACT_SIGNING_KEY_ID: ${{ secrets.EXACT_SIGNING_KEY_ID }}
OD_EXACT_ED25519_PRIVATE_KEY: ${{ secrets.EXACT_ED25519_PRIVATE_KEY }}
OD_EXACT_SIGNING_KEY_ID_NEXT: ${{ secrets.EXACT_SIGNING_KEY_ID_NEXT }}
OD_EXACT_ED25519_PRIVATE_KEY_NEXT: ${{ secrets.EXACT_ED25519_PRIVATE_KEY_NEXT }}
run: python3 .github/scripts/pack.py --request .tmp/exact/pack-request.json --receipt .tmp/exact/pack-receipt.json
- name: Publish immutable release and channel-head CAS
env:
OD_EXACT_RELEASE_TOKEN: ${{ secrets.EXACT_RELEASE_TOKEN }}
STORAGE_ENDPOINT: ${{ secrets.EXACT_RELEASE_ENDPOINT }}
STORAGE_BUCKET: ${{ secrets.EXACT_RELEASE_BUCKET }}
run: |
python3 - <<'PY'
import json, os
from pathlib import Path
value = {'schemaVersion': 1, 'operation': 'exact.release', 'packReceipt': str(Path('.tmp/exact/pack-receipt.json').resolve()), 'endpointUrl': os.environ['STORAGE_ENDPOINT'], 'bucket': os.environ['STORAGE_BUCKET']}
Path('.tmp/exact/release-request.json').write_text(json.dumps(value, sort_keys=True, separators=(',', ':')) + '\n')
PY
python3 .github/scripts/release.py --request .tmp/exact/release-request.json --receipt .tmp/exact/release-receipt.json
- uses: actions/upload-artifact@v7
if: always()
with:
name: exact-${{ inputs.channel }}-${{ inputs.release_version }}-receipts
path: |
.tmp/exact/*-request.json
.tmp/exact/*-receipt.json
.tmp/exact/output/.contracts/*.json
if-no-files-found: error
retention-days: 30