fix(cloud): preserve scopes when switching workspaces

This commit is contained in:
bearsyankees 2026-08-27 17:30:16 -04:00
parent c59e36d353
commit 1b9f41d244
3 changed files with 37 additions and 5 deletions

View file

@ -27,7 +27,8 @@ _USAGE_HEADER = """[bold]Usage:[/] strix cloud <command> [arguments]
[bold]Resource commands:[/]"""
_USAGE_FOOTER = """
Run [bold]strix cloud <command>[/] without a verb to list its verbs.
Run [bold]strix cloud <command> help[/] to list its verbs. Common read-only
commands may also run their default verb when no verb is given.
Every command accepts [bold]--json[/] and [bold]--token[/]. Write commands
accept [bold]--data[/] with a JSON object of extra request fields.
API reference: https://docs.app.strix.ai"""
@ -57,7 +58,8 @@ def run_cloud(argv: list[str]) -> int:
console.print(f"[red]Unknown command:[/] {group}")
_print_usage(console)
return 2
resolved = resolve(group, rest)
group_help = bool(rest and rest[0] in ("-h", "--help", "help"))
resolved = None if group_help else resolve(group, rest)
if resolved is None:
_print_verbs(console, group)
return 0 if not rest or rest[0] in ("-h", "--help", "help") else 2

View file

@ -31,7 +31,10 @@ def run_workspace_use(argv: list[str]) -> int:
nargs="+",
metavar="SCOPE",
default=None,
help="API scopes for the new token. Without this option the server grants the defaults.",
help=(
"API scopes for the new token. Without this option, preserve the stored token's "
"scopes."
),
)
parser.add_argument("--json", action="store_true", help="Print the raw JSON response.")
parser.add_argument("--token", default=None, help="API token override.")
@ -58,9 +61,16 @@ def run_workspace_use(argv: list[str]) -> int:
def _use(console: Console, args: argparse.Namespace, *, as_json: bool) -> int:
workspace = _find_workspace(args.workspace, token=args.token)
record = read_record() or {}
body: dict[str, Any] = {}
if args.scopes:
body["scopes"] = args.scopes
elif args.token is None:
stored_scopes = record.get("scopes")
if isinstance(stored_scopes, list) and stored_scopes and all(
isinstance(scope, str) for scope in stored_scopes
):
body["scopes"] = stored_scopes
minted = http.check(
http.request(
"POST",
@ -73,7 +83,6 @@ def _use(console: Console, args: argparse.Namespace, *, as_json: bool) -> int:
raise http.CloudError("the platform did not return a token.")
minted_record = cast("dict[str, Any]", minted)
record = read_record() or {}
record.update(
{
"api_token": minted_record["api_token"],

View file

@ -571,17 +571,26 @@ def test_workspaces_use_switches_stored_token(
auth_path = tmp_path / "platform-auth.json"
monkeypatch.setattr(platform_cli, "AUTH_PATH", auth_path)
monkeypatch.setattr(workspaces, "AUTH_PATH", auth_path)
platform_cli.save_record({"api_token": "old", "email": "a@b.test"})
platform_cli.save_record(
{
"api_token": "old",
"email": "a@b.test",
"scopes": ["scans:read", "organizations:read", "tokens:write"],
}
)
calls: list[tuple[str, str]] = []
token_body: dict[str, Any] | None = None
def fake_request(method: str, path: str, **kwargs: Any) -> FakeResponse:
nonlocal token_body
calls.append((method, path))
if path == "/workspaces":
return FakeResponse(
status_code=200,
payload={"workspaces": [{"id": "org_1", "name": "Team One", "role": "admin"}]},
)
token_body = kwargs.get("body")
return FakeResponse(
status_code=201,
payload={
@ -596,6 +605,9 @@ def test_workspaces_use_switches_stored_token(
code = cloud.run_cloud(["workspaces", "use", "team one", "--json"])
assert code == 0
assert calls == [("GET", "/workspaces"), ("POST", "/workspaces/org_1/token")]
assert token_body == {
"scopes": ["scans:read", "organizations:read", "tokens:write"]
}
record = platform_cli.read_record()
assert record is not None
assert record["api_token"] == "new-token"
@ -613,3 +625,12 @@ def test_workspaces_use_reports_unknown_workspace(monkeypatch: pytest.MonkeyPatc
),
)
assert cloud.run_cloud(["workspaces", "use", "missing", "--json"]) == 1
def test_group_help_lists_all_verbs_instead_of_default_verb_help(capsys: Any) -> None:
assert cloud.run_cloud(["workspaces", "-h"]) == 0
output = capsys.readouterr().out
assert "workspaces verbs" in output
assert "list" in output
assert "create" in output
assert "use" in output