runtime-sdk 0.4.18__tar.gz → 0.4.20__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/PKG-INFO +28 -1
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/README.md +27 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/pyproject.toml +1 -1
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk/cli.py +169 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk/client.py +22 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk.egg-info/PKG-INFO +28 -1
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk/__init__.py +0 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk/config.py +0 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk.egg-info/SOURCES.txt +0 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk.egg-info/dependency_links.txt +0 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk.egg-info/entry_points.txt +0 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk.egg-info/requires.txt +0 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/scripts/runtime_sdk.egg-info/top_level.txt +0 -0
- {runtime_sdk-0.4.18 → runtime_sdk-0.4.20}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runtime-sdk
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.20
|
|
4
4
|
Summary: Runtime Python SDK and CLI
|
|
5
5
|
Project-URL: Repository, https://github.com/The-Money-Company-Limited/runtimevm
|
|
6
6
|
Project-URL: Issues, https://github.com/The-Money-Company-Limited/runtimevm/issues
|
|
@@ -66,8 +66,13 @@ runtime login --api-key rt_live_...
|
|
|
66
66
|
runtime api-keys list
|
|
67
67
|
runtime api-keys create "my laptop"
|
|
68
68
|
runtime api-keys revoke 123
|
|
69
|
+
runtime integrations list
|
|
70
|
+
runtime integrations connect github
|
|
69
71
|
|
|
70
72
|
# Computers
|
|
73
|
+
runtime switch --repo owner/repo feature/login # open or create the branch workspace computer
|
|
74
|
+
runtime switch -c feature/login --repo owner/repo # create a new branch workspace from the repo default branch
|
|
75
|
+
runtime checkout --repo owner/repo feature/login # alias for switch
|
|
71
76
|
runtime create # creates a computer with the starter app already published
|
|
72
77
|
runtime create myapp --command "python3 app.py" --cwd /home/ubuntu --port 3000
|
|
73
78
|
runtime enter <name-or-id> # accepts slug/name like test, or the computer id
|
|
@@ -157,6 +162,28 @@ show|clear` expose that same durable app state directly. The low-level
|
|
|
157
162
|
`runtime run` stays one-shot: the filesystem is restored after going cold, but
|
|
158
163
|
that ad-hoc process is not.
|
|
159
164
|
|
|
165
|
+
## GitHub branch workspaces
|
|
166
|
+
|
|
167
|
+
`runtime switch` gives a repo branch its own Runtime computer so you can jump
|
|
168
|
+
between parallel tasks without local worktrees.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
runtime integrations connect github
|
|
172
|
+
runtime switch --repo owner/repo feature/login
|
|
173
|
+
runtime switch -c feature/search --repo owner/repo --from main
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Rules:
|
|
177
|
+
- GitHub must already be connected with `runtime integrations connect github`.
|
|
178
|
+
- `runtime switch` opens an existing branch workspace, or creates the computer if
|
|
179
|
+
the branch exists but the workspace does not yet.
|
|
180
|
+
- `runtime switch -c` creates a new branch first, then opens that branch's
|
|
181
|
+
workspace.
|
|
182
|
+
- Each `repo + branch` maps to one dedicated computer.
|
|
183
|
+
- The repo is cloned into `/home/ubuntu/<repo>` and new shells in that computer
|
|
184
|
+
start there automatically.
|
|
185
|
+
- `runtime checkout ...` is an alias for `runtime switch ...`.
|
|
186
|
+
|
|
160
187
|
Run the SDK unit tests through the backend project environment:
|
|
161
188
|
|
|
162
189
|
```bash
|
|
@@ -44,8 +44,13 @@ runtime login --api-key rt_live_...
|
|
|
44
44
|
runtime api-keys list
|
|
45
45
|
runtime api-keys create "my laptop"
|
|
46
46
|
runtime api-keys revoke 123
|
|
47
|
+
runtime integrations list
|
|
48
|
+
runtime integrations connect github
|
|
47
49
|
|
|
48
50
|
# Computers
|
|
51
|
+
runtime switch --repo owner/repo feature/login # open or create the branch workspace computer
|
|
52
|
+
runtime switch -c feature/login --repo owner/repo # create a new branch workspace from the repo default branch
|
|
53
|
+
runtime checkout --repo owner/repo feature/login # alias for switch
|
|
49
54
|
runtime create # creates a computer with the starter app already published
|
|
50
55
|
runtime create myapp --command "python3 app.py" --cwd /home/ubuntu --port 3000
|
|
51
56
|
runtime enter <name-or-id> # accepts slug/name like test, or the computer id
|
|
@@ -135,6 +140,28 @@ show|clear` expose that same durable app state directly. The low-level
|
|
|
135
140
|
`runtime run` stays one-shot: the filesystem is restored after going cold, but
|
|
136
141
|
that ad-hoc process is not.
|
|
137
142
|
|
|
143
|
+
## GitHub branch workspaces
|
|
144
|
+
|
|
145
|
+
`runtime switch` gives a repo branch its own Runtime computer so you can jump
|
|
146
|
+
between parallel tasks without local worktrees.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
runtime integrations connect github
|
|
150
|
+
runtime switch --repo owner/repo feature/login
|
|
151
|
+
runtime switch -c feature/search --repo owner/repo --from main
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Rules:
|
|
155
|
+
- GitHub must already be connected with `runtime integrations connect github`.
|
|
156
|
+
- `runtime switch` opens an existing branch workspace, or creates the computer if
|
|
157
|
+
the branch exists but the workspace does not yet.
|
|
158
|
+
- `runtime switch -c` creates a new branch first, then opens that branch's
|
|
159
|
+
workspace.
|
|
160
|
+
- Each `repo + branch` maps to one dedicated computer.
|
|
161
|
+
- The repo is cloned into `/home/ubuntu/<repo>` and new shells in that computer
|
|
162
|
+
start there automatically.
|
|
163
|
+
- `runtime checkout ...` is an alias for `runtime switch ...`.
|
|
164
|
+
|
|
138
165
|
Run the SDK unit tests through the backend project environment:
|
|
139
166
|
|
|
140
167
|
```bash
|
|
@@ -5,6 +5,7 @@ import contextlib
|
|
|
5
5
|
import importlib
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
|
+
import re
|
|
8
9
|
import select
|
|
9
10
|
import shlex
|
|
10
11
|
import shutil
|
|
@@ -189,12 +190,23 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
189
190
|
|
|
190
191
|
integrations_cmd = subparsers.add_parser("integrations", help="Connect account-level integrations")
|
|
191
192
|
integrations_subparsers = integrations_cmd.add_subparsers(dest="integrations_action", required=True)
|
|
193
|
+
integrations_subparsers.add_parser("list", aliases=["ls"], help="List integrations")
|
|
192
194
|
integrations_connect = integrations_subparsers.add_parser("connect", help="Connect an integration")
|
|
193
195
|
integrations_connect.add_argument("provider", choices=["github"])
|
|
194
196
|
integrations_connect.add_argument("--slug", default="github", help="Integration slug to create (default: github)")
|
|
195
197
|
integrations_connect.add_argument("--owner", default=None, help="GitHub username or org where the app is installed")
|
|
196
198
|
integrations_connect.add_argument("--no-open", action="store_true", help="Print the install URL instead of opening a browser")
|
|
197
199
|
|
|
200
|
+
switch_cmd = subparsers.add_parser(
|
|
201
|
+
"switch",
|
|
202
|
+
aliases=["checkout"],
|
|
203
|
+
help="Open a GitHub branch workspace in its own computer",
|
|
204
|
+
)
|
|
205
|
+
switch_cmd.add_argument("branch", nargs="?", default=None, help="Git branch to open")
|
|
206
|
+
switch_cmd.add_argument("--repo", default=None, help="GitHub repository in owner/name format")
|
|
207
|
+
switch_cmd.add_argument("-c", "--create", action="store_true", help="Create a new branch before opening it")
|
|
208
|
+
switch_cmd.add_argument("--from", dest="base_branch", default=None, help="Base branch for -c (defaults to the repo default branch)")
|
|
209
|
+
|
|
198
210
|
create_cmd = subparsers.add_parser("create", help="Create a new computer with the starter app published by default")
|
|
199
211
|
create_cmd.add_argument(
|
|
200
212
|
"name",
|
|
@@ -340,9 +352,19 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
340
352
|
return handle_secrets_delete(config, args.slug)
|
|
341
353
|
return report_error("unknown secrets command")
|
|
342
354
|
if args.command == "integrations":
|
|
355
|
+
if args.integrations_action in ("list", "ls"):
|
|
356
|
+
return handle_integrations_list(config)
|
|
343
357
|
if args.integrations_action == "connect":
|
|
344
358
|
return handle_integrations_connect(config, args.provider, args.slug, args.owner, no_open=args.no_open)
|
|
345
359
|
return report_error("unknown integrations command")
|
|
360
|
+
if args.command in ("switch", "checkout"):
|
|
361
|
+
return handle_switch(
|
|
362
|
+
config,
|
|
363
|
+
getattr(args, "repo", None),
|
|
364
|
+
getattr(args, "branch", None),
|
|
365
|
+
create_branch=getattr(args, "create", False),
|
|
366
|
+
base_branch=getattr(args, "base_branch", None),
|
|
367
|
+
)
|
|
346
368
|
if args.command == "create":
|
|
347
369
|
return handle_create(
|
|
348
370
|
config,
|
|
@@ -764,6 +786,48 @@ def _resolve_computer_ref(client: RuntimeClient, ref: str) -> str:
|
|
|
764
786
|
|
|
765
787
|
|
|
766
788
|
|
|
789
|
+
def _coerce_github_repo(repo: str | None) -> str | None:
|
|
790
|
+
text = str(repo or "").strip()
|
|
791
|
+
if not text:
|
|
792
|
+
return None
|
|
793
|
+
if not re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", text):
|
|
794
|
+
raise RuntimeAPIError("repo must be in owner/name format")
|
|
795
|
+
owner, name = text.split("/", 1)
|
|
796
|
+
return f"{owner}/{name}"
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
def _coerce_git_branch(branch: str | None) -> str | None:
|
|
801
|
+
text = str(branch or "").strip()
|
|
802
|
+
if not text:
|
|
803
|
+
return None
|
|
804
|
+
if text == "@":
|
|
805
|
+
raise RuntimeAPIError("branch name is invalid")
|
|
806
|
+
if text.startswith("-") or text.endswith("/") or text.endswith(".") or text.endswith(".lock"):
|
|
807
|
+
raise RuntimeAPIError("branch name is invalid")
|
|
808
|
+
if ".." in text or "//" in text or "@{" in text:
|
|
809
|
+
raise RuntimeAPIError("branch name is invalid")
|
|
810
|
+
if any(ch in text for ch in (" ", "~", "^", ":", "?", "*", "[", "\\")):
|
|
811
|
+
raise RuntimeAPIError("branch name is invalid")
|
|
812
|
+
if any(ord(ch) < 32 or ord(ch) == 127 for ch in text):
|
|
813
|
+
raise RuntimeAPIError("branch name is invalid")
|
|
814
|
+
return text
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def _render_branch_workspace(payload: dict[str, Any]) -> None:
|
|
819
|
+
computer = payload.get("computer") or {}
|
|
820
|
+
repo = str(payload.get("repo") or "")
|
|
821
|
+
branch = str(payload.get("branch") or "")
|
|
822
|
+
action = "created branch" if payload.get("create_branch") else "opened branch"
|
|
823
|
+
_render_computer_panel(computer, title="workspace")
|
|
824
|
+
_UI.console().print(f"[green]✓[/green] {action} [bold]{repo}#{branch}[/bold]")
|
|
825
|
+
url = computer.get("public_url")
|
|
826
|
+
if url:
|
|
827
|
+
_UI.console().print(f"[bold green]→[/bold green] [cyan]{url}[/cyan]")
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
|
|
767
831
|
def _computer_name(c: dict[str, Any]) -> str:
|
|
768
832
|
return str(c.get("slug") or c.get("id") or "?")
|
|
769
833
|
|
|
@@ -1492,6 +1556,35 @@ def handle_api_keys_revoke(config: RuntimeConfig, key_id: str | None) -> int:
|
|
|
1492
1556
|
|
|
1493
1557
|
|
|
1494
1558
|
|
|
1559
|
+
def _render_integrations_table(payload: dict[str, Any]) -> None:
|
|
1560
|
+
mods = _UI.rich()
|
|
1561
|
+
Table = mods["Table"]
|
|
1562
|
+
integrations = payload.get("integrations") or []
|
|
1563
|
+
if not isinstance(integrations, list) or not integrations:
|
|
1564
|
+
_UI.console().print("[yellow]no integrations found[/yellow]")
|
|
1565
|
+
return
|
|
1566
|
+
|
|
1567
|
+
table = Table(title="Integrations", expand=True, box=mods["box"].SIMPLE_HEAVY)
|
|
1568
|
+
table.add_column("Slug", style="bold white")
|
|
1569
|
+
table.add_column("Provider", style="white")
|
|
1570
|
+
table.add_column("Owner", style="white")
|
|
1571
|
+
table.add_column("Transport", style="white")
|
|
1572
|
+
table.add_column("Backend", style="white")
|
|
1573
|
+
|
|
1574
|
+
for entry in integrations:
|
|
1575
|
+
if not isinstance(entry, dict):
|
|
1576
|
+
continue
|
|
1577
|
+
table.add_row(
|
|
1578
|
+
str(entry.get("slug") or "—"),
|
|
1579
|
+
str(entry.get("provider") or "—"),
|
|
1580
|
+
str(entry.get("credential_ref") or "—"),
|
|
1581
|
+
str(entry.get("transport") or "—"),
|
|
1582
|
+
str(entry.get("backend") or "—"),
|
|
1583
|
+
)
|
|
1584
|
+
_UI.console().print(table)
|
|
1585
|
+
|
|
1586
|
+
|
|
1587
|
+
|
|
1495
1588
|
def _coerce_secret_set_slug(slug: str | None) -> str | None:
|
|
1496
1589
|
value = str(slug or "").strip()
|
|
1497
1590
|
return value or None
|
|
@@ -1624,6 +1717,14 @@ def handle_secrets_delete(config: RuntimeConfig, slug: str | None) -> int:
|
|
|
1624
1717
|
|
|
1625
1718
|
|
|
1626
1719
|
|
|
1720
|
+
def handle_integrations_list(config: RuntimeConfig) -> int:
|
|
1721
|
+
if (err := _require_api_key(config)) is not None:
|
|
1722
|
+
return err
|
|
1723
|
+
client = RuntimeClient(base_url=config.base_url, api_key=config.api_key)
|
|
1724
|
+
return report_success(client.list_integrations(), _render_integrations_table)
|
|
1725
|
+
|
|
1726
|
+
|
|
1727
|
+
|
|
1627
1728
|
def handle_integrations_connect(
|
|
1628
1729
|
config: RuntimeConfig,
|
|
1629
1730
|
provider: str,
|
|
@@ -1686,6 +1787,74 @@ def handle_integrations_connect(
|
|
|
1686
1787
|
|
|
1687
1788
|
|
|
1688
1789
|
|
|
1790
|
+
def handle_switch(
|
|
1791
|
+
config: RuntimeConfig,
|
|
1792
|
+
repo: str | None,
|
|
1793
|
+
branch: str | None,
|
|
1794
|
+
*,
|
|
1795
|
+
create_branch: bool = False,
|
|
1796
|
+
base_branch: str | None = None,
|
|
1797
|
+
) -> int:
|
|
1798
|
+
if (err := _require_api_key(config)) is not None:
|
|
1799
|
+
return err
|
|
1800
|
+
if base_branch is not None and not create_branch:
|
|
1801
|
+
return report_error("--from can only be used together with -c")
|
|
1802
|
+
|
|
1803
|
+
try:
|
|
1804
|
+
resolved_repo = _coerce_github_repo(repo)
|
|
1805
|
+
resolved_branch = _coerce_git_branch(branch)
|
|
1806
|
+
except RuntimeAPIError as exc:
|
|
1807
|
+
return report_error(str(exc), status_code=exc.status_code)
|
|
1808
|
+
|
|
1809
|
+
if resolved_repo is None:
|
|
1810
|
+
if not _interactive():
|
|
1811
|
+
return report_error("--repo is required")
|
|
1812
|
+
resolved_repo = _prompt_text("GitHub repo (owner/name)")
|
|
1813
|
+
try:
|
|
1814
|
+
resolved_repo = _coerce_github_repo(resolved_repo)
|
|
1815
|
+
except RuntimeAPIError as exc:
|
|
1816
|
+
return report_error(str(exc), status_code=exc.status_code)
|
|
1817
|
+
if resolved_repo is None:
|
|
1818
|
+
return report_error("repo is required")
|
|
1819
|
+
|
|
1820
|
+
if resolved_branch is None:
|
|
1821
|
+
if not _interactive():
|
|
1822
|
+
return report_error("branch is required")
|
|
1823
|
+
prompt = "New branch name" if create_branch else "Branch name"
|
|
1824
|
+
resolved_branch = _prompt_text(prompt)
|
|
1825
|
+
try:
|
|
1826
|
+
resolved_branch = _coerce_git_branch(resolved_branch)
|
|
1827
|
+
except RuntimeAPIError as exc:
|
|
1828
|
+
return report_error(str(exc), status_code=exc.status_code)
|
|
1829
|
+
if resolved_branch is None:
|
|
1830
|
+
return report_error("branch is required")
|
|
1831
|
+
|
|
1832
|
+
client = RuntimeClient(base_url=config.base_url, api_key=config.api_key)
|
|
1833
|
+
try:
|
|
1834
|
+
payload = _with_spinner(
|
|
1835
|
+
"opening branch workspace…",
|
|
1836
|
+
lambda: client.switch_workspace(
|
|
1837
|
+
repo=resolved_repo,
|
|
1838
|
+
branch=resolved_branch,
|
|
1839
|
+
create=create_branch,
|
|
1840
|
+
base_branch=base_branch,
|
|
1841
|
+
),
|
|
1842
|
+
)
|
|
1843
|
+
except RuntimeAPIError as exc:
|
|
1844
|
+
return report_error(str(exc), status_code=exc.status_code)
|
|
1845
|
+
|
|
1846
|
+
computer = payload.get("computer") or {}
|
|
1847
|
+
computer_id = str(computer.get("id") or "").strip()
|
|
1848
|
+
if not computer_id:
|
|
1849
|
+
return report_error("server did not return a computer id")
|
|
1850
|
+
|
|
1851
|
+
code = report_success(payload, _render_branch_workspace)
|
|
1852
|
+
if code != 0 or not _interactive():
|
|
1853
|
+
return code
|
|
1854
|
+
return _enter_computer(config, computer_id)
|
|
1855
|
+
|
|
1856
|
+
|
|
1857
|
+
|
|
1689
1858
|
def handle_onboarding(config: RuntimeConfig) -> int:
|
|
1690
1859
|
code = handle_login(config, None, None, None)
|
|
1691
1860
|
if code != 0:
|
|
@@ -54,6 +54,9 @@ class RuntimeClient:
|
|
|
54
54
|
|
|
55
55
|
# --- Integrations ---
|
|
56
56
|
|
|
57
|
+
def list_integrations(self) -> dict[str, Any]:
|
|
58
|
+
return self._request("GET", "/api/integrations", auth_required=True)
|
|
59
|
+
|
|
57
60
|
def start_github_connect(self, *, slug: str | None = None) -> dict[str, Any]:
|
|
58
61
|
body: dict[str, Any] = {}
|
|
59
62
|
if slug:
|
|
@@ -96,6 +99,25 @@ class RuntimeClient:
|
|
|
96
99
|
|
|
97
100
|
# --- Computers ---
|
|
98
101
|
|
|
102
|
+
def switch_workspace(
|
|
103
|
+
self,
|
|
104
|
+
*,
|
|
105
|
+
repo: str,
|
|
106
|
+
branch: str,
|
|
107
|
+
create: bool = False,
|
|
108
|
+
base_branch: str | None = None,
|
|
109
|
+
) -> dict[str, Any]:
|
|
110
|
+
body: dict[str, Any] = {"repo": repo, "branch": branch, "create": create}
|
|
111
|
+
if base_branch is not None:
|
|
112
|
+
body["from"] = base_branch
|
|
113
|
+
return self._request(
|
|
114
|
+
"POST",
|
|
115
|
+
"/api/workspaces/switch",
|
|
116
|
+
json=body,
|
|
117
|
+
auth_required=True,
|
|
118
|
+
timeout=max(self.timeout, DEFAULT_WARMUP_TIMEOUT),
|
|
119
|
+
)
|
|
120
|
+
|
|
99
121
|
def create_computer(
|
|
100
122
|
self,
|
|
101
123
|
*,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runtime-sdk
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.20
|
|
4
4
|
Summary: Runtime Python SDK and CLI
|
|
5
5
|
Project-URL: Repository, https://github.com/The-Money-Company-Limited/runtimevm
|
|
6
6
|
Project-URL: Issues, https://github.com/The-Money-Company-Limited/runtimevm/issues
|
|
@@ -66,8 +66,13 @@ runtime login --api-key rt_live_...
|
|
|
66
66
|
runtime api-keys list
|
|
67
67
|
runtime api-keys create "my laptop"
|
|
68
68
|
runtime api-keys revoke 123
|
|
69
|
+
runtime integrations list
|
|
70
|
+
runtime integrations connect github
|
|
69
71
|
|
|
70
72
|
# Computers
|
|
73
|
+
runtime switch --repo owner/repo feature/login # open or create the branch workspace computer
|
|
74
|
+
runtime switch -c feature/login --repo owner/repo # create a new branch workspace from the repo default branch
|
|
75
|
+
runtime checkout --repo owner/repo feature/login # alias for switch
|
|
71
76
|
runtime create # creates a computer with the starter app already published
|
|
72
77
|
runtime create myapp --command "python3 app.py" --cwd /home/ubuntu --port 3000
|
|
73
78
|
runtime enter <name-or-id> # accepts slug/name like test, or the computer id
|
|
@@ -157,6 +162,28 @@ show|clear` expose that same durable app state directly. The low-level
|
|
|
157
162
|
`runtime run` stays one-shot: the filesystem is restored after going cold, but
|
|
158
163
|
that ad-hoc process is not.
|
|
159
164
|
|
|
165
|
+
## GitHub branch workspaces
|
|
166
|
+
|
|
167
|
+
`runtime switch` gives a repo branch its own Runtime computer so you can jump
|
|
168
|
+
between parallel tasks without local worktrees.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
runtime integrations connect github
|
|
172
|
+
runtime switch --repo owner/repo feature/login
|
|
173
|
+
runtime switch -c feature/search --repo owner/repo --from main
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Rules:
|
|
177
|
+
- GitHub must already be connected with `runtime integrations connect github`.
|
|
178
|
+
- `runtime switch` opens an existing branch workspace, or creates the computer if
|
|
179
|
+
the branch exists but the workspace does not yet.
|
|
180
|
+
- `runtime switch -c` creates a new branch first, then opens that branch's
|
|
181
|
+
workspace.
|
|
182
|
+
- Each `repo + branch` maps to one dedicated computer.
|
|
183
|
+
- The repo is cloned into `/home/ubuntu/<repo>` and new shells in that computer
|
|
184
|
+
start there automatically.
|
|
185
|
+
- `runtime checkout ...` is an alias for `runtime switch ...`.
|
|
186
|
+
|
|
160
187
|
Run the SDK unit tests through the backend project environment:
|
|
161
188
|
|
|
162
189
|
```bash
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|