feat(templates_urls): short circuit input names

This commit is contained in:
Johannes Kirschbauer
2025-06-11 20:06:31 +02:00
parent 32b5b7dc93
commit 1724f127d5
3 changed files with 60 additions and 37 deletions

View File

@@ -47,7 +47,7 @@ def machine_template(
# Get the clan template from the specifier
[flake_ref, template_selector] = transform_url(
"machine", template_ident, local_path=flake
"machine", template_ident, flake=flake
)
template_flake = Flake(flake_ref)

View File

@@ -16,10 +16,10 @@ class Flake(Protocol):
@property
def path(self) -> Path: ...
def get_input_names(self) -> list[str]: ...
def transform_url(
template_type: str, identifier: str, local_path: Flake
) -> tuple[str, str]:
def transform_url(template_type: str, identifier: str, flake: Flake) -> tuple[str, str]:
"""
Transform a template flake ref by injecting the context (clan|machine|disko) into the url.
We do this for shorthand notation of URLs.
@@ -56,6 +56,11 @@ def transform_url(
clan machines create --template github:/org/repo#new.machine
-> clanInternals.templates.machine."new.machine"
6. Templates locked via inputs:
clan machines create --template clan-core#new-machine
path: clan-core matches one of the input attributes.
-> <local_flake>#inputs.clan-core.clan.templates.machine."new-machine"
As of URL specification (RFC 3986).
scheme:[//[user:password@]host[:port]][/path][?query][#fragment]
@@ -76,22 +81,28 @@ def transform_url(
# Substitute the flake reference with the local flake path if it is empty or just a dot.
# This is required if the command will be executed from a different place, than the local flake root.
if not flake_ref or flake_ref == ".":
flake_ref = str(local_path.path)
flake_ref = str(flake.path)
if "#" not in identifier:
# No fragment, so we assume its a builtin template
return (flake_ref, f'clanInternals.templates.{template_type}."{selector}"')
input_prefix = ""
if flake_ref in flake.get_input_names():
# Interpret the flake reference as an input of the local flake.
input_prefix = f"inputs.{flake_ref}."
flake_ref = str(flake.path)
# TODO: implement support for quotes in the tail "a.b".c
# If the tail contains a dot, or is quoted we assume its a path and don't transform it.
if '"' in selector or "'" in selector:
log.warning(
"Quotes in template paths are not yet supported. Please use unquoted paths."
)
return (flake_ref, selector)
return (flake_ref, input_prefix + selector)
if "." in selector:
return (flake_ref, selector)
return (flake_ref, input_prefix + selector)
# Tail doesn't contain a dot at this point, so we can inject the context.
return (flake_ref, f'clan.templates.{template_type}."{selector}"')
return (flake_ref, input_prefix + f'clan.templates.{template_type}."{selector}"')

View File

@@ -12,6 +12,9 @@ class DummyFlake:
def __init__(self, path: str) -> None:
self.path: Path = Path(path)
def get_input_names(self) -> list[str]:
return ["locked-input"]
local_path = DummyFlake(".")
@@ -20,9 +23,7 @@ def test_transform_url_self_explizit_dot() -> None:
user_input = ".#new-machine"
expected_selector = 'clan.templates.machine."new-machine"'
flake_ref, selector = transform_url(
template_type, user_input, local_path=local_path
)
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == str(local_path.path)
assert selector == expected_selector
@@ -31,9 +32,7 @@ def test_transform_url_self_no_dot() -> None:
user_input = "#new-machine"
expected_selector = 'clan.templates.machine."new-machine"'
flake_ref, selector = transform_url(
template_type, user_input, local_path=local_path
)
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == str(local_path.path)
assert selector == expected_selector
@@ -42,9 +41,7 @@ def test_transform_url_builtin_template() -> None:
user_input = "new-machine"
expected_selector = 'clanInternals.templates.machine."new-machine"'
flake_ref, selector = transform_url(
template_type, user_input, local_path=local_path
)
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == str(local_path.path)
assert selector == expected_selector
@@ -53,9 +50,7 @@ def test_transform_url_remote_template() -> None:
user_input = "github:/org/repo#new-machine"
expected_selector = 'clan.templates.machine."new-machine"'
flake_ref, selector = transform_url(
template_type, user_input, local_path=local_path
)
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == "github:/org/repo"
assert selector == expected_selector
@@ -65,9 +60,7 @@ def test_transform_url_explicit_path() -> None:
user_input = ".#clan.templates.machine.new-machine"
expected_selector = "clan.templates.machine.new-machine"
flake_ref, selector = transform_url(
template_type, user_input, local_path=local_path
)
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == str(local_path.path)
assert selector == expected_selector
@@ -76,9 +69,7 @@ def test_transform_url_explicit_path() -> None:
def test_transform_url_quoted_selector() -> None:
user_input = '.#"new.machine"'
expected_selector = '"new.machine"'
flake_ref, selector = transform_url(
template_type, user_input, local_path=local_path
)
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == str(local_path.path)
assert selector == expected_selector
@@ -86,9 +77,7 @@ def test_transform_url_quoted_selector() -> None:
def test_single_quote_selector() -> None:
user_input = ".#'new.machine'"
expected_selector = "'new.machine'"
flake_ref, selector = transform_url(
template_type, user_input, local_path=local_path
)
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == str(local_path.path)
assert selector == expected_selector
@@ -97,9 +86,7 @@ def test_custom_template_path() -> None:
user_input = "github:/org/repo#my.templates.custom.machine"
expected_selector = "my.templates.custom.machine"
flake_ref, selector = transform_url(
template_type, user_input, local_path=local_path
)
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == "github:/org/repo"
assert selector == expected_selector
@@ -109,9 +96,7 @@ def test_full_url_query_and_fragment() -> None:
expected_flake_ref = "github:/org/repo?query=param"
expected_selector = "my.templates.custom.machine"
flake_ref, selector = transform_url(
template_type, user_input, local_path=local_path
)
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == expected_flake_ref
assert selector == expected_selector
@@ -120,7 +105,7 @@ def test_custom_template_type() -> None:
user_input = "#my.templates.custom.machine"
expected_selector = "my.templates.custom.machine"
flake_ref, selector = transform_url("custom", user_input, local_path=local_path)
flake_ref, selector = transform_url("custom", user_input, flake=local_path)
assert flake_ref == str(local_path.path)
assert selector == expected_selector
@@ -129,7 +114,7 @@ def test_malformed_identifier() -> None:
user_input = "github:/org/repo#my.templates.custom.machine#extra"
with pytest.raises(ClanError) as exc_info:
_flake_ref, _selector = transform_url(
template_type, user_input, local_path=local_path
template_type, user_input, flake=local_path
)
assert isinstance(exc_info.value, ClanError)
@@ -137,3 +122,30 @@ def test_malformed_identifier() -> None:
str(exc_info.value)
== "Invalid template identifier: More than one '#' found. Please use a single '#'"
)
def test_locked_input_template() -> None:
user_input = "locked-input#new-machine"
expected_selector = 'inputs.locked-input.clan.templates.machine."new-machine"'
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert flake_ref == str(local_path.path)
assert selector == expected_selector
def test_locked_input_template_no_quotes() -> None:
user_input = 'locked-input#"new.machine"'
expected_selector = 'inputs.locked-input."new.machine"'
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert selector == expected_selector
assert flake_ref == str(local_path.path)
def test_locked_input_template_no_dot() -> None:
user_input = "locked-input#new.machine"
expected_selector = "inputs.locked-input.new.machine"
flake_ref, selector = transform_url(template_type, user_input, flake=local_path)
assert selector == expected_selector
assert flake_ref == str(local_path.path)