Skip to content

Commit 3cdb912

Browse files
andhusclaude
andauthored
Fix misleading slug display in stardag modal CLI (v0.7.3) (#143)
## Summary `stardag modal deploy` and `stardag modal stardag-api-key create` printed the workspace/environment slug by reading the active CLI profile's TOML (`prof["environment"]`). When env vars or a custom `config_provider` default-factory override the resolved IDs, the slug pulled from the active profile may not correspond to the resolved UUID — producing hypothetical output like: ``` Environment: <env-a-uuid> (env-b-slug) ``` …where the UUID resolves to environment A but the slug is read from a profile pointing at environment B. Deployed bytes were always correct; only the printed line was misleading. ### Fix - Added `get_cached_workspace_slug` / `get_cached_environment_slug` reverse lookups in `stardag/config/cache.py` (UUID → slug, against `~/.stardag/id-cache.json`). - Replaced `_get_profile_slugs` with `_resolve_display_slugs` in `stardag/_cli/modal.py`. The slug shown is now always the one that actually maps to the resolved UUID, or omitted when there is no cache entry — instead of guessing from the active profile. Includes minimal `CHANGELOG.md` + `RELEASE_NOTES.md` entries for **v0.7.3**. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e5dabf7 commit 3cdb912

4 files changed

Lines changed: 113 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,28 @@ For detailed SDK migration guides, see [RELEASE_NOTES.md](RELEASE_NOTES.md).
66

77
## [Unreleased]
88

9+
## [0.7.3] — 2026-05-08
10+
11+
`stardag modal deploy` and `stardag modal stardag-api-key create` now
12+
display the slug that actually corresponds to the resolved
13+
workspace/environment UUID. Previously the slug was read from the active
14+
CLI profile's TOML, which could be unrelated to the resolved UUID when
15+
env vars or a custom `config_provider` override the IDs — producing
16+
misleading lines pairing the resolved UUID with a slug from an unrelated
17+
profile. No client-code changes — `pip install -U stardag` is
18+
sufficient. See
19+
[RELEASE_NOTES.md](RELEASE_NOTES.md#v073--correct-slug-display-in-stardag-modal-cli)
20+
for details.
21+
22+
### SDK
23+
24+
- **`stardag/_cli/modal.py`**: replaced `_get_profile_slugs` with
25+
`_resolve_display_slugs`, which reverse-looks up the slug from the
26+
resolved UUID via the id-cache. Slug is omitted when no cache hit
27+
rather than guessing from the active profile.
28+
- **`stardag/config/cache.py`**: added `get_cached_workspace_slug` and
29+
`get_cached_environment_slug` (UUID → slug reverse lookups).
30+
931
## [0.7.2] — 2026-05-08
1032

1133
`sd.build(resume_build_id=...)` now fires a `BUILD_RESUMED` event so

RELEASE_NOTES.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,28 @@ For changes to the Registry API, UI, and other components, see [CHANGELOG.md](CH
66

77
---
88

9+
## v0.7.3 — Correct slug display in `stardag modal` CLI
10+
11+
Fixes a misleading display in `stardag modal deploy` and
12+
`stardag modal stardag-api-key create` where the workspace/environment
13+
slug printed alongside the resolved UUID could come from a different
14+
profile than the one whose IDs were actually used. The CLI now
15+
reverse-looks up the slug from the resolved UUID via the id-cache, or
16+
omits it when no cache entry exists.
17+
18+
This typically affected setups that override `STARDAG_WORKSPACE_ID` /
19+
`STARDAG_ENVIRONMENT_ID` via env vars or a custom `config_provider`
20+
default factory while leaving an unrelated profile active — for example
21+
hypothetical output like
22+
`Environment: <env-a-uuid> (env-b-slug)`, where the UUID resolves to
23+
environment A but the slug is read from a profile pointing at
24+
environment B. The deployed bytes were already correct; only the
25+
printed slug was wrong.
26+
27+
**No client-code changes**`pip install -U stardag` is sufficient.
28+
29+
---
30+
931
## v0.7.2 — Build resume status fix and SKIPPED UI polish
1032

1133
Fixes a UX bug where `sd.build(resume_build_id=...)` silently reused the

lib/stardag/src/stardag/_cli/modal.py

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030

3131
from stardag._cli._helpers import get_authenticated_client
3232
from stardag._cli.credentials import (
33-
get_active_profile,
34-
list_profiles,
33+
list_registries,
3534
resolve_environment_slug_to_id,
3635
resolve_workspace_slug_to_id,
3736
)
37+
from stardag.config.cache import get_cached_slugs
3838
from stardag.config.loader import clear_config_cache, get_config
3939
from stardag.integration.modal import StardagApp
4040

@@ -61,45 +61,57 @@
6161
app.add_typer(stardag_api_key_app, name="stardag-api-key")
6262

6363

64-
def _get_profile_slugs(
65-
profile_name: str | None,
66-
workspace_id: str | None = None,
67-
environment_id: str | None = None,
68-
) -> tuple[str | None, str | None]:
69-
"""Get workspace and environment slugs from a profile.
64+
def _registry_name_for_url(api_url: str | None) -> str | None:
65+
"""Look up the configured registry name for a given URL.
7066
71-
Returns (workspace_slug, environment_slug) where slug is None if
72-
the profile value matches the resolved ID (i.e. it's already a UUID).
67+
The id-cache is keyed by registry *name* (the TOML key under ``[registry.<name>]``)
68+
rather than URL, so we reverse-lookup the name from the URL the resolved IDs were
69+
fetched against. Returns ``None`` if no registry with a matching URL is configured
70+
(e.g. STARDAG_API_URL is set without a corresponding TOML entry).
7371
"""
74-
if not profile_name:
75-
profile_name_resolved, _ = get_active_profile()
76-
profile_name = profile_name_resolved
77-
78-
if not profile_name:
79-
return None, None
80-
81-
profiles = list_profiles()
82-
prof = profiles.get(profile_name)
83-
if not prof:
72+
if not api_url:
73+
return None
74+
target = api_url.rstrip("/")
75+
for name, url in list_registries().items():
76+
if url.rstrip("/") == target:
77+
return name
78+
return None
79+
80+
81+
def _resolve_display_slugs(
82+
api_url: str | None,
83+
workspace_id: str | None,
84+
environment_id: str | None,
85+
) -> tuple[str | None, str | None]:
86+
"""Reverse-lookup workspace/environment slugs for the resolved UUIDs.
87+
88+
Why: the resolved UUIDs may come from env vars or a custom config_provider
89+
override and have no relation to the active profile's TOML slugs. Reading
90+
`prof["environment"]` could display a slug for a totally different env
91+
than the resolved UUID. We also can't trust ``get_config().context.registry_name``
92+
because callers like ``deploy(--profile foo)`` and ``stardag_api_key_create``
93+
resolve IDs under a temporarily-overridden profile, then restore the env
94+
before this function runs — leaving ``get_config()`` pointing at the
95+
*active* profile, which may use a different registry. Pin the registry to
96+
the URL the IDs were actually resolved against.
97+
"""
98+
registry_name = _registry_name_for_url(api_url)
99+
if not registry_name:
84100
return None, None
85-
86-
ws_slug = prof["workspace"] if prof["workspace"] != workspace_id else None
87-
env_slug = prof["environment"] if prof["environment"] != environment_id else None
88-
return ws_slug, env_slug
101+
return get_cached_slugs(registry_name, workspace_id, environment_id)
89102

90103

91-
def _print_stardag_context(
92-
env_vars: dict[str, str],
93-
profile_name: str | None = None,
94-
) -> None:
104+
def _print_stardag_context(env_vars: dict[str, str]) -> None:
95105
"""Print stardag context info (registry, workspace, environment, target roots)."""
96106
import json
97107

98108
registry = env_vars.get("STARDAG_API_URL", "none (local mode)")
99109
ws_id = env_vars.get("STARDAG_WORKSPACE_ID", "N/A")
100110
env_id = env_vars.get("STARDAG_ENVIRONMENT_ID", "N/A")
101111

102-
ws_slug, env_slug = _get_profile_slugs(profile_name, ws_id, env_id)
112+
ws_slug, env_slug = _resolve_display_slugs(
113+
env_vars.get("STARDAG_API_URL"), ws_id, env_id
114+
)
103115

104116
console.print(f"[dim] Registry: {registry}[/dim]")
105117

@@ -297,7 +309,7 @@ def stardag_api_key_create(
297309
if api_key_name is None:
298310
api_key_name = f"modal-{modal_env or 'default'}"
299311

300-
ws_slug, env_slug = _get_profile_slugs(stardag_profile, ws_id, env_id)
312+
ws_slug, env_slug = _resolve_display_slugs(api_url, ws_id, env_id)
301313
ws_display = f"{ws_id} ({ws_slug})" if ws_slug else ws_id
302314
env_display = f"{env_id} ({env_slug})" if env_slug else env_id
303315

@@ -561,7 +573,7 @@ def deploy(
561573
console.print(f"[cyan]Using stardag profile: {profile}[/cyan]")
562574
env_vars = get_profile_env_vars(profile)
563575
if env_vars:
564-
_print_stardag_context(env_vars, profile)
576+
_print_stardag_context(env_vars)
565577
extra_secrets.append(
566578
modal.Secret.from_dict(dict(env_vars)) # type: ignore[arg-type]
567579
)

lib/stardag/src/stardag/config/cache.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,32 @@ def cache_environment_id(
188188
save_id_cache(cache)
189189

190190

191+
def get_cached_slugs(
192+
registry: str,
193+
workspace_id: str | None,
194+
environment_id: str | None,
195+
) -> tuple[str | None, str | None]:
196+
"""Reverse lookup: cached (workspace_slug, environment_slug) for the given UUIDs.
197+
198+
Loads the id-cache once. Returns ``(None, None)`` for inputs without a cache hit.
199+
"""
200+
cache = load_id_cache()
201+
ws_slug: str | None = None
202+
if workspace_id:
203+
for slug, ws_id in cache.workspaces.get(registry, {}).items():
204+
if ws_id == workspace_id:
205+
ws_slug = slug
206+
break
207+
env_slug: str | None = None
208+
if workspace_id and environment_id:
209+
envs = cache.environments.get(registry, {}).get(workspace_id, {})
210+
for slug, env_id in envs.items():
211+
if env_id == environment_id:
212+
env_slug = slug
213+
break
214+
return ws_slug, env_slug
215+
216+
191217
def _looks_like_uuid(value: str) -> bool:
192218
"""Check if a string looks like a UUID."""
193219
uuid_pattern = r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"

0 commit comments

Comments
 (0)