Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
6db0cab
docs(sandbox): add a how-to for running Minecraft in a Windows sandbo…
r33drichards Aug 13, 2026
1803e52
fix(cua-driver): preserve named CLI sessions (#3144)
f-trycua Aug 13, 2026
54d62e7
feat(cyclops): add browser bash chat (#6769)
r33drichards Aug 12, 2026
a90e683
fix(cyclops): keep serving available through PostgreSQL cutover (#6804)
r33drichards Aug 13, 2026
8b71b3c
fix(cyclops): match cua.ai favicon (#6806)
r33drichards Aug 13, 2026
873ecc6
fix(cyclops): allow RDS role ownership semantics (#6807)
r33drichards Aug 13, 2026
69ed111
feat(cyclops): require card or admin for CR creation (#6796)
r33drichards Aug 13, 2026
3eecd0b
docs(cyclops): use public ECR in Fleets example (#6813)
r33drichards Aug 13, 2026
5a77587
feat(cyclops): add browser bash chat tools (#6808)
r33drichards Aug 13, 2026
6fbe843
fix(cyclops): use public default pool image (#6817)
r33drichards Aug 13, 2026
73c1d78
feat(cyclops): break down latency alerts by path and user (#6832)
r33drichards Aug 13, 2026
b762f25
feat(cyclops): grandfather pre-cutoff billing users (#6833)
r33drichards Aug 13, 2026
5f403c9
fix(cyclops): explain billing policy denials (#6842)
r33drichards Aug 13, 2026
2eb5e82
docs(sandbox): publish the installed sandbox as a containerDisk (#3155)
r33drichards Aug 13, 2026
1927960
fix(sandbox): let from_registry name the guest OS (#3154)
r33drichards Aug 13, 2026
cb1051c
Make install.ps1 survive the piped invocation it documents (#3153)
r33drichards Aug 13, 2026
fa093ca
Bump cua-sandbox to v0.3.3
github-actions[bot] Aug 13, 2026
a294d24
feat: nested KVM support for KubeVirt VMs (CUA-952) (#5968)
r33drichards Aug 13, 2026
4f86b42
docs(sandbox): drop the from_registry workaround and fix the retry ad…
r33drichards Aug 13, 2026
792a791
feat(sandbox): support warm pool autoscaling through Pool.apply (#3168)
r33drichards Aug 13, 2026
d97f0af
Bump cua-sandbox to v0.3.4 (#3169)
r33drichards Aug 13, 2026
45de825
test: rehearse nightly release transactions (#3173)
f-trycua Aug 14, 2026
51107f4
fix: isolate nightly release staging (#3175)
f-trycua Aug 14, 2026
0d7c011
feat(cua-driver)!: remove browser approval tokens (#3185)
f-trycua Aug 15, 2026
bb8c860
chore(main): release cua-driver-rs 0.20.0 (#3075)
cua-release-bot[bot] Aug 15, 2026
6562006
chore(cua-driver): advance published installer version to 0.20.0 [ski…
Aug 15, 2026
80b0501
docs(cua-driver): add integration chooser (#3190)
f-trycua Aug 15, 2026
c9f754c
Make a degraded database startup visible on the cyclops-cs backend (#…
r33drichards Aug 15, 2026
61e51cd
feat(cua-driver): Project Centennial preview (#3188)
f-trycua Aug 15, 2026
e3d822e
Report the state query executor's startup health separately (#6887)
r33drichards Aug 16, 2026
154b6b7
Recover degraded database features without restarting the pod (#6901)
r33drichards Aug 16, 2026
a744308
feat(cua-driver): extend Project Centennial across desktop platforms …
f-trycua Aug 16, 2026
e54a420
ci: accept Apple Developer ID profile allowlists (#3194)
f-trycua Aug 16, 2026
0d0ab33
ci: accept empty helper entitlement output (#3195)
f-trycua Aug 16, 2026
2f5a2a6
Gate the run.cua.ai SPA rollout on a blackbox probe of the canary (st…
r33drichards Aug 16, 2026
c78c1d8
fix(cua-driver): refuse unproven Wayland window capture (#3200)
f-trycua Aug 16, 2026
e14891a
fix(cua-driver): drop the stale 0.17 version from element_index messages
f-trycua Aug 16, 2026
c4f8bac
fix(docs): resolve Cua Driver tags cross-platform (#3205)
f-trycua Aug 16, 2026
ab957bd
feat(lume): support running without a VNC listener (#3209)
f-trycua Aug 16, 2026
eaf5a34
fix(cua-driver): launch isolated browser without pid (#3208)
f-trycua Aug 16, 2026
0225d36
docs(cua-driver): publish Computer History preview docs (#3212)
f-trycua Aug 16, 2026
7ecb58f
docs(cua-driver): lower Computer History guide priority
f-trycua Aug 16, 2026
6711ab2
fix(cyclops-sdk): state the absent pull secret in the template merge …
r33drichards Aug 17, 2026
315d02f
fix(sandbox): support Fleet request timeouts (#3214)
r33drichards Aug 17, 2026
192c536
Bump cua-sandbox to v0.3.5
github-actions[bot] Aug 17, 2026
13b21db
fix(cyclops-sdk): make HttpRequest.timeout_secs optional and add Http…
r33drichards Aug 17, 2026
efc8ba7
feat(sandbox): construct Fleet requests through HttpRequestBuilder (#…
r33drichards Aug 17, 2026
2fe1c32
ci(fleet): promote the cua-fleet 0.1.11 wheel set to PyPI (#3217)
r33drichards Aug 17, 2026
da44420
fix(cua-driver): strip UTF-8 BOM from uninstall.ps1
f-trycua Aug 17, 2026
745c43b
docs(cua-driver): accept every supported platform in the history even…
f-trycua Aug 17, 2026
62ede45
fix(cua-driver): tell operators to reconnect agent sessions after his…
f-trycua Aug 17, 2026
30d9130
fix(cua-driver): attribute hosted Windows apps by window (#3227)
f-trycua Aug 17, 2026
f913244
fix(cua-driver): tolerate missing local autostart task (#3229)
f-trycua Aug 17, 2026
3f791b2
fix(cua-driver): harden consented existing-profile attachment (#3211)
f-trycua Aug 17, 2026
605a742
feat(cua-sandbox)!: require explicit pool names for Fleet images (#3215)
r33drichards Aug 17, 2026
feb236c
chore(cua-sandbox): bump version to 0.4.0 (#3232)
r33drichards Aug 17, 2026
7e1a8fa
feat(web): unify Cua identity and dashboard design (#6941)
f-trycua Aug 17, 2026
ef5912c
feat(cyclops-sdk): raise PoolAccessDenied for 403s on pool-namespace …
r33drichards Aug 17, 2026
a383ed7
ci(fleet): promote the cua-fleet 0.1.12 wheel set to PyPI (#3233)
r33drichards Aug 17, 2026
9b625d7
test(cua-sandbox): exercise persistent pre-provisioned pools in perio…
r33drichards Aug 17, 2026
ab02082
fix(cua-sandbox): adopt upstream PoolAccessDenied via cua-fleet 0.1.1…
r33drichards Aug 17, 2026
4bcf5dc
Remove API keys section from Settings page (#7031)
r33drichards Aug 17, 2026
74ea024
test(cua-sandbox): tolerate Fleet 403 for not-yet-created pool namesp…
r33drichards Aug 17, 2026
4c12f5e
fix(auth): restore Keycloak device authorization (#7035)
r33drichards Aug 17, 2026
0661938
fix(fleet): default claim bind deadline to 900s (#7039)
r33drichards Aug 17, 2026
a7ba403
test(fleet): expect default bind deadline in SDK requests (#7040)
r33drichards Aug 17, 2026
3af8db7
fix(cyclops-cs): fail closed on flag type mismatch (#6858)
r33drichards Aug 17, 2026
76bad7a
fix(agent): reject unsafe UITARS coordinate expressions (#1392)
RinZ27 Aug 18, 2026
9045b0c
fix(cua-driver): preserve history admission on macOS relaunch (#3245)
f-trycua Aug 18, 2026
c43f102
Update k8s.go (#6753)
r33drichards Aug 19, 2026
ced70e3
ci: promote the cua-fleet 0.1.14 wheel set to PyPI (#3255)
r33drichards Aug 19, 2026
d503700
feat(cua-sandbox): expose creation TTL for pools and claims (0.4.2) (…
r33drichards Aug 19, 2026
78673aa
fix(computer-server): fail closed for host tools in VNC mode
f-trycua Aug 19, 2026
22f9371
docs(sandbox): split pool/claim TTL into its own how-to guide (#3260)
r33drichards Aug 19, 2026
f60085a
test: synchronize release wiring versions (#3261)
f-trycua Aug 19, 2026
4864ccd
fix(cua-driver): admit stable local history signatures (#3262)
f-trycua Aug 19, 2026
6aa6438
ci(cua-driver): bound Ubuntu package downloads (#3268)
f-trycua Aug 19, 2026
70db98d
chore(main): release cua-driver-rs 0.21.0 (#3192)
cua-release-bot[bot] Aug 19, 2026
291ccdd
chore(cua-driver): advance published installer version to 0.21.0 [ski…
Aug 19, 2026
9bf47f6
fix(cua-driver): make Windows browser prepare language independent (#…
loonghao Aug 19, 2026
56f13c3
fix(cua-driver): block uinput pointer hotplug on KDE X11 (#2888)
Berzerker5653 Aug 19, 2026
f8ce064
docs: require canonical repository branches (#3273)
injaneity Aug 20, 2026
2c8fa2a
fix(cua-driver): preserve embedded telemetry preference (#3277)
injaneity Aug 20, 2026
b25f829
docs: recommend Hermes built-in computer use (#3274)
injaneity Aug 20, 2026
7db1eb0
fix(docs): scope validation to affected generators (#3293)
injaneity Aug 20, 2026
ee09e86
fix(cua-driver): reset stale local TCC rows after ad-hoc rebuilds (#2…
f-trycua Aug 20, 2026
3ae080d
feat(cua-driver): configure embedded daemon overlay (#3280)
injaneity Aug 20, 2026
9a61050
fix(cua-driver): honor HERMES_HOME for skill links (#3291)
injaneity Aug 20, 2026
d138baa
fix(cua-driver): normalize legacy dispatch before authorization (#3037)
injaneity Aug 20, 2026
1542a71
fix(cua-driver): preserve TCC across release updates (#3297)
f-trycua Aug 21, 2026
58dfd87
chore: restore injaneity as Cua Driver code owner (#3308)
f-trycua Aug 21, 2026
a682895
fix(cua-driver): match canonical Windows executable paths (#3299)
HsiangNianian Aug 21, 2026
0213cd8
fix(cua-driver): sign Windows release binaries (#3320)
f-trycua Aug 21, 2026
e4b0a03
fix(cua-sandbox): support local Linux on ARM64 (#3257)
r33drichards Aug 22, 2026
b296ec9
chore(release): cua-sandbox 0.4.3, cua-cli 0.1.15 (#3325)
r33drichards Aug 22, 2026
737dc2a
docs(sandbox): drop incorrect Cua cloud registry-image restriction (#…
r33drichards Aug 22, 2026
4d52465
chore(cua-sandbox): synchronize bumpversion release metadata (#3352)
f-trycua Aug 24, 2026
ac9b164
chore(main): release cua-driver-rs 0.22.0
cua-release-bot[bot] Aug 24, 2026
76bea14
fix(cua-driver): capture identified Hyprland toplevels
RodriMora Aug 10, 2026
e65a101
test(cua-driver): add Hyprland desktop harness
RodriMora Aug 10, 2026
ec1870b
fix(cua-driver): normalize Hyprland desktop coordinates
RodriMora Aug 10, 2026
2f01070
fix(cua-driver): complete Hyprland desktop contracts
RodriMora Aug 10, 2026
d06a34d
fix(cua-driver): preserve Hyprland worker and evidence scope
RodriMora Aug 10, 2026
79f42a3
fix(cua-driver): render browser cursor on Linux
injaneity Aug 10, 2026
d60f71f
fix(cua-driver): isolate Wayland cursors across outputs
jacob-vincent-mink Aug 13, 2026
3e84ef9
docs(cua-driver): clarify persistent GUI sessions
jacob-vincent-mink Aug 13, 2026
a303d3a
fix(cua-driver): render cursors at negative display coordinates
injaneity Aug 10, 2026
c0b02d0
fix(cua-driver): align Wayland desktop capture and input pixels
jacob-vincent-mink Aug 13, 2026
0bde8d3
fix(cua-driver): verify Wayland window activation
jacob-vincent-mink Aug 13, 2026
4ddc9ca
Merge remote-tracking branch 'upstream/pr-3052' into test/hyprland-in…
jacob-vincent-mink Aug 13, 2026
4fbcf93
Merge remote-tracking branch 'upstream/pr-3031' into test/hyprland-in…
jacob-vincent-mink Aug 13, 2026
f2532b2
Merge remote-tracking branch 'upstream/pr-3152' into test/hyprland-in…
jacob-vincent-mink Aug 13, 2026
0a908f0
fix(cua-driver): avoid duplicate X11 overlay on Wayland
jacob-vincent-mink Aug 13, 2026
2920b82
Merge remote-tracking branch 'upstream/pr-3019' into test/hyprland-in…
jacob-vincent-mink Aug 13, 2026
6f7ad6b
Merge updated Wayland overlay companion for live validation
jacob-vincent-mink Aug 13, 2026
0f10825
Merge branch 'fix/wayland-overlay-companion' into test/hyprland-integ…
jacob-vincent-mink Aug 13, 2026
f34cea7
fix(cua-driver): synchronize Hyprland virtual-pointer motion
jacob-vincent-mink Aug 13, 2026
ba65966
Merge branch 'fix/wayland-overlay-companion' into test/hyprland-integ…
jacob-vincent-mink Aug 13, 2026
4bf5d22
fix(cua-driver): suppress X11 overlay on Wayland displays
jacob-vincent-mink Aug 14, 2026
71bb9f1
fix(cua-driver): keep session cursor visible during Linux input
spencerbull Aug 12, 2026
34f2286
fix(cua-driver): preserve user pointer in agent mode
spencerbull Aug 17, 2026
1be6b39
fix(cua-driver): reconcile Wayland overlay state on v0.20
spencerbull Aug 17, 2026
ea707a5
fix(cua-driver): release held buttons during cleanup
spencerbull Aug 25, 2026
639e859
Merge Jacob's Hyprland integration base for v0.22 review
spencerbull Aug 25, 2026
c8a951c
fix(cua-driver): address Hyprland review findings
spencerbull Aug 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
46 changes: 23 additions & 23 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@
/release-please-config.json @f-trycua

# Core products and SDKs.
/libs/cua-driver/ @f-trycua
/libs/cua-driver/ @f-trycua @injaneity
/libs/lume/ @f-trycua
/libs/python/ @ddupont808
/libs/cua-bench/ @ddupont808

# Cua Driver documentation and driver-only release/CI automation.
/docs/content/docs/reference/cua-driver/ @f-trycua
/scripts/docs-generators/cua-driver.ts @f-trycua
/.github/release-notes/cua-driver-rs.md @f-trycua
/.github/scripts/sync_driver_release_docs.py @f-trycua
/.github/scripts/verify_cua_driver_release_archives.py @f-trycua
/.github/scripts/tests/test_cua_driver_release_wiring.py @f-trycua
/.github/scripts/tests/test_sync_driver_release_docs.py @f-trycua
/.github/scripts/tests/test_verify_cua_driver_release_archives.py @f-trycua
/.github/workflows/cd-cua-driver-docs.yml @f-trycua
/.github/workflows/cd-py-cua-driver.yml @f-trycua
/.github/workflows/cd-rust-cua-driver.yml @f-trycua
/.github/workflows/ci-cua-driver-contract-clients.yml @f-trycua
/.github/workflows/ci-cua-driver-installer-compat.yml @f-trycua
/.github/workflows/ci-distro-compat-cua-driver.yml @f-trycua
/.github/workflows/ci-rust-format.yml @f-trycua
/.github/workflows/ci-rust-linux.yml @f-trycua
/.github/workflows/ci-rust-windows.yml @f-trycua
/.github/workflows/clawhub-cua-driver.yml @f-trycua
/.github/workflows/e2e-rust-linux-wayland.yml @f-trycua
/.github/workflows/e2e-rust-linux.yml @f-trycua
/.github/workflows/e2e-rust-standalone-browsers.yml @f-trycua
/.github/workflows/e2e-rust-windows.yml @f-trycua
/docs/content/docs/reference/cua-driver/ @f-trycua @injaneity
/scripts/docs-generators/cua-driver.ts @f-trycua @injaneity
/.github/release-notes/cua-driver-rs.md @f-trycua @injaneity
/.github/scripts/sync_driver_release_docs.py @f-trycua @injaneity
/.github/scripts/verify_cua_driver_release_archives.py @f-trycua @injaneity
/.github/scripts/tests/test_cua_driver_release_wiring.py @f-trycua @injaneity
/.github/scripts/tests/test_sync_driver_release_docs.py @f-trycua @injaneity
/.github/scripts/tests/test_verify_cua_driver_release_archives.py @f-trycua @injaneity
/.github/workflows/cd-cua-driver-docs.yml @f-trycua @injaneity
/.github/workflows/cd-py-cua-driver.yml @f-trycua @injaneity
/.github/workflows/cd-rust-cua-driver.yml @f-trycua @injaneity
/.github/workflows/ci-cua-driver-contract-clients.yml @f-trycua @injaneity
/.github/workflows/ci-cua-driver-installer-compat.yml @f-trycua @injaneity
/.github/workflows/ci-distro-compat-cua-driver.yml @f-trycua @injaneity
/.github/workflows/ci-rust-format.yml @f-trycua @injaneity
/.github/workflows/ci-rust-linux.yml @f-trycua @injaneity
/.github/workflows/ci-rust-windows.yml @f-trycua @injaneity
/.github/workflows/clawhub-cua-driver.yml @f-trycua @injaneity
/.github/workflows/e2e-rust-linux-wayland.yml @f-trycua @injaneity
/.github/workflows/e2e-rust-linux.yml @f-trycua @injaneity
/.github/workflows/e2e-rust-standalone-browsers.yml @f-trycua @injaneity
/.github/workflows/e2e-rust-windows.yml @f-trycua @injaneity

# Platform infrastructure.
/libs/fleet/ @r33drichards
Expand Down
1 change: 1 addition & 0 deletions .github/release-attribution-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"klymenko@adobe.com": "pavelklymenko",
"m.fuechtenkoetter@posteo.de": "ai-ag2026",
"manfred@ubuntu26.04-vm": "ai-ag2026",
"marko.civonafets@gmail.com": "civonafets",
"me@madhavajay.com": "madhavajay",
"robert@trycua.com": "enchanted-koala",
"rwendt1337@gmail.com": "r33drichards",
Expand Down
2 changes: 1 addition & 1 deletion .github/release-state/cua-driver-rs-published-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.19.3
0.21.0
5 changes: 1 addition & 4 deletions .github/scripts/github_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,7 @@ def unique_release(api: GitHubApi, repository: str, tag: str) -> dict[str, Any]:


def validate_registered_nightly_tag(tag: str) -> None:
# Driver publication runs after its exact nightly version has been staged
# into the checkout. Tag validation needs registry shape and namespaces,
# not the stable source-state invariant enforced by planning and builds.
registry = release_channels.load_registry(require_stable_state=False)
registry = release_channels.load_registry()
matches = []
for name, component in registry["components"].items():
try:
Expand Down
193 changes: 141 additions & 52 deletions .github/scripts/release_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import os
from pathlib import Path, PurePosixPath
import re
import shutil
import subprocess
import sys
import tempfile
from typing import Any, Mapping, Sequence

import release_attribution
Expand Down Expand Up @@ -54,7 +56,6 @@ def load_registry(
path: Path = DEFAULT_REGISTRY,
*,
root: Path = ROOT,
require_stable_state: bool = True,
) -> dict[str, Any]:
registry = read_json(path)
if registry.get("schemaVersion") != 1:
Expand Down Expand Up @@ -127,21 +128,18 @@ def load_registry(
raise ChannelError(
f"component {name} stable prefix must match Release Please: {expected_prefix}"
)
if require_stable_state:
authority = (
repository_path(root, component["versionAuthorityFile"])
.read_text(encoding="utf-8")
.strip()
authority = (
repository_path(root, component["versionAuthorityFile"])
.read_text(encoding="utf-8")
.strip()
)
if not SEMVER_RE.fullmatch(authority):
raise ChannelError(f"component {name} authority is not a stable version: {authority!r}")
if release_manifest.get(package_path) != authority:
raise ChannelError(
f"component {name} authority {authority} differs from Release Please manifest "
f"{release_manifest.get(package_path)!r}"
)
if not SEMVER_RE.fullmatch(authority):
raise ChannelError(
f"component {name} authority is not a stable version: {authority!r}"
)
if release_manifest.get(package_path) != authority:
raise ChannelError(
f"component {name} authority {authority} differs from Release Please manifest "
f"{release_manifest.get(package_path)!r}"
)
return registry


Expand All @@ -150,13 +148,8 @@ def component_descriptor(
path: Path = DEFAULT_REGISTRY,
*,
root: Path = ROOT,
require_stable_state: bool = True,
) -> dict[str, Any]:
registry = load_registry(
path,
root=root,
require_stable_state=require_stable_state,
)
registry = load_registry(path, root=root)
try:
return dict(registry["components"][name])
except KeyError as error:
Expand Down Expand Up @@ -278,6 +271,39 @@ def replace_block(match: re.Match[str]) -> str:
return len(seen)


def _rewrite_version_site(
site: Mapping[str, Any],
path: Path,
old_version: str,
new_version: str,
*,
manifest_path: Path | None = None,
) -> None:
kind = site["kind"]
if kind == "plain":
if path.read_text(encoding="utf-8").strip() != old_version:
raise ChannelError(f"plain version site {site['path']} differs from {old_version}")
path.write_text(f"{new_version}\n", encoding="utf-8")
elif kind == "regex":
original = path.read_text(encoding="utf-8")
replacement = str(site["replacement"]).replace("{version}", new_version)
rewritten, count = re.subn(str(site["pattern"]), replacement, original)
if count != int(site["expectedMatches"]):
raise ChannelError(
f"version site {site['path']} matched {count} times, "
f"expected {site['expectedMatches']}"
)
path.write_text(rewritten, encoding="utf-8")
elif kind == "cargo-workspace-lock":
if manifest_path is None:
raise ChannelError(
f"cargo lock version site {site['path']} requires its manifest in the staged tree"
)
_rewrite_cargo_lock(path, manifest_path, old_version, new_version)
else:
raise ChannelError(f"unsupported version site kind: {kind!r}")


def apply_version(
name: str,
version: str,
Expand All @@ -293,30 +319,86 @@ def apply_version(
changed: list[str] = []
for site in component["buildVersionSites"]:
path = repository_path(root, site["path"])
kind = site["kind"]
if kind == "plain":
if path.read_text(encoding="utf-8").strip() != old_version:
raise ChannelError(f"plain version site {site['path']} differs from {old_version}")
path.write_text(f"{version}\n", encoding="utf-8")
elif kind == "regex":
original = path.read_text(encoding="utf-8")
replacement = str(site["replacement"]).replace("{version}", version)
rewritten, count = re.subn(str(site["pattern"]), replacement, original)
if count != int(site["expectedMatches"]):
raise ChannelError(
f"version site {site['path']} matched {count} times, "
f"expected {site['expectedMatches']}"
)
path.write_text(rewritten, encoding="utf-8")
elif kind == "cargo-workspace-lock":
manifest = repository_path(root, site["manifestPath"])
_rewrite_cargo_lock(path, manifest, old_version, version)
else:
raise ChannelError(f"unsupported version site kind: {kind!r}")
manifest = (
repository_path(root, site["manifestPath"])
if site["kind"] == "cargo-workspace-lock"
else None
)
_rewrite_version_site(site, path, old_version, version, manifest_path=manifest)
changed.append(str(site["path"]))
return changed


def stage_versioned_tree(
name: str,
version: str,
source: str,
destination: str,
*,
registry_path: Path = DEFAULT_REGISTRY,
root: Path = ROOT,
) -> list[str]:
"""Copy one source tree and rewrite only its declared version sites."""
nightly_version(version)
component = component_descriptor(name, registry_path, root=root)
authority = repository_path(root, component["versionAuthorityFile"])
old_version = authority.read_text(encoding="utf-8").strip()
stable_version(old_version)
source_path = repository_path(root, source)
destination_path = repository_path(root, destination)
if not source_path.is_dir():
raise ChannelError(f"versioned tree source is not a directory: {source}")
if destination_path.exists():
raise ChannelError(f"versioned tree destination already exists: {destination}")
if destination_path.resolve().is_relative_to(source_path.resolve()):
raise ChannelError("versioned tree destination cannot be inside its source")

source_pure = PurePosixPath(source)
selected: list[tuple[Mapping[str, Any], PurePosixPath]] = []
for site in component["buildVersionSites"]:
try:
relative = PurePosixPath(str(site["path"])).relative_to(source_pure)
except ValueError:
continue
selected.append((site, relative))
if not selected:
raise ChannelError(
f"versioned tree {source!r} contains no declared buildVersionSites for {name}"
)

destination_path.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(
dir=destination_path.parent,
prefix=f".{destination_path.name}.stage-",
) as temporary:
staged = Path(temporary) / destination_path.name
shutil.copytree(source_path, staged)
changed: list[str] = []
for site, relative in selected:
manifest = None
if site["kind"] == "cargo-workspace-lock":
try:
manifest_relative = PurePosixPath(str(site["manifestPath"])).relative_to(
source_pure
)
except ValueError as error:
raise ChannelError(
f"cargo lock version site {site['path']} cannot be staged without "
f"manifest {site['manifestPath']}"
) from error
manifest = staged / manifest_relative
_rewrite_version_site(
site,
staged / relative,
old_version,
version,
manifest_path=manifest,
)
changed.append(str(PurePosixPath(destination) / relative))
staged.rename(destination_path)
return changed


def _git(root: Path, *args: str) -> str:
result = subprocess.run(["git", *args], cwd=root, check=False, text=True, capture_output=True)
if result.returncode != 0:
Expand Down Expand Up @@ -407,12 +489,7 @@ def build_manifest(
attribution_config_path: Path | None = None,
github: release_attribution.GitHubClient | None = None,
) -> dict[str, Any]:
component = component_descriptor(
name,
registry_path,
root=root,
require_stable_state=False,
)
component = component_descriptor(name, registry_path, root=root)
parsed = parse_tag(component, "nightly", tag)
if parsed != version:
raise ChannelError(f"tag version {parsed} differs from requested version {version}")
Expand All @@ -426,11 +503,7 @@ def build_manifest(
if not base_sha:
raise ChannelError(f"nightly attribution base has no commit: {previous_tag}")
_git(root, "merge-base", "--is-ancestor", base_sha, source_sha)
registry = load_registry(
registry_path,
root=root,
require_stable_state=False,
)
registry = load_registry(registry_path, root=root)
paths = sorted(
{
*(str(path) for path in component["changeDetectionPaths"]),
Expand Down Expand Up @@ -576,6 +649,12 @@ def parser() -> argparse.ArgumentParser:
apply.add_argument("--component", required=True)
apply.add_argument("--version", required=True)

stage = subparsers.add_parser("stage-versioned-tree")
stage.add_argument("--component", required=True)
stage.add_argument("--version", required=True)
stage.add_argument("--source", required=True)
stage.add_argument("--destination", required=True)

plan = subparsers.add_parser("plan")
plan.add_argument("--component", required=True)
plan.add_argument("--source-sha", required=True)
Expand Down Expand Up @@ -627,6 +706,16 @@ def main(argv: Sequence[str] | None = None) -> int:
args.component, args.version, registry_path=args.registry, root=root
):
print(changed)
elif args.command == "stage-versioned-tree":
for changed in stage_versioned_tree(
args.component,
args.version,
args.source,
args.destination,
registry_path=args.registry,
root=root,
):
print(changed)
elif args.command == "plan":
releases = read_json(args.releases)
if not isinstance(releases, list):
Expand Down
Loading