Templates: migrate clan templates to flake identifiers
This commit is contained in:
@@ -4,36 +4,17 @@ import logging
|
||||
from pathlib import Path
|
||||
|
||||
from clan_lib.clan.create import CreateOptions, create_clan
|
||||
from clan_lib.templates import (
|
||||
InputPrio,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def register_create_parser(parser: argparse.ArgumentParser) -> None:
|
||||
parser.add_argument(
|
||||
"--input",
|
||||
type=str,
|
||||
help="""Flake input name to use as template source
|
||||
can be specified multiple times, inputs are tried in order of definition
|
||||
Example: --input clan --input clan-core
|
||||
""",
|
||||
action="append",
|
||||
default=[],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--no-self",
|
||||
help="Do not look into own flake for templates",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--template",
|
||||
type=str,
|
||||
help="Clan template name",
|
||||
help="""Reference to the template to use for the clan. default="default". In the format '<flake_ref>#template_name' Where <flake_ref> is a flake reference (e.g. github:org/repo) or a local path (e.g. '.' ).
|
||||
Omitting '<flake_ref>#' will use the builtin templates (e.g. just 'default' from clan-core ).
|
||||
""",
|
||||
default="default",
|
||||
)
|
||||
|
||||
@@ -59,19 +40,10 @@ def register_create_parser(parser: argparse.ArgumentParser) -> None:
|
||||
)
|
||||
|
||||
def create_flake_command(args: argparse.Namespace) -> None:
|
||||
if len(args.input) == 0:
|
||||
args.input = ["clan", "clan-core"]
|
||||
|
||||
if args.no_self:
|
||||
input_prio = InputPrio.try_inputs(tuple(args.input))
|
||||
else:
|
||||
input_prio = InputPrio.try_self_then_inputs(tuple(args.input))
|
||||
|
||||
create_clan(
|
||||
CreateOptions(
|
||||
input_prio=input_prio,
|
||||
dest=args.path,
|
||||
template_name=args.template,
|
||||
template=args.template,
|
||||
setup_git=not args.no_git,
|
||||
src_flake=args.flake,
|
||||
update_clan=not args.no_update,
|
||||
|
||||
@@ -4,16 +4,12 @@ from pathlib import Path
|
||||
|
||||
from clan_lib.api import API
|
||||
from clan_lib.cmd import RunOpts, run
|
||||
from clan_lib.dirs import clan_templates
|
||||
from clan_lib.errors import ClanError
|
||||
from clan_lib.flake import Flake
|
||||
from clan_lib.nix import nix_command, nix_metadata, nix_shell
|
||||
from clan_lib.persist.inventory_store import InventorySnapshot, InventoryStore
|
||||
from clan_lib.templates import (
|
||||
InputPrio,
|
||||
TemplateName,
|
||||
get_template,
|
||||
)
|
||||
from clan_lib.templates.filesystem import copy_from_nixstore
|
||||
from clan_lib.templates.handler import clan_template
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -21,9 +17,9 @@ log = logging.getLogger(__name__)
|
||||
@dataclass
|
||||
class CreateOptions:
|
||||
dest: Path
|
||||
template_name: str
|
||||
template: str
|
||||
|
||||
src_flake: Flake | None = None
|
||||
input_prio: InputPrio | None = None
|
||||
setup_git: bool = True
|
||||
initial: InventorySnapshot | None = None
|
||||
update_clan: bool = True
|
||||
@@ -47,44 +43,31 @@ def create_clan(opts: CreateOptions) -> None:
|
||||
log.warning("Setting src_flake to None")
|
||||
opts.src_flake = None
|
||||
|
||||
template = get_template(
|
||||
TemplateName(opts.template_name),
|
||||
"clan",
|
||||
input_prio=opts.input_prio,
|
||||
clan_dir=opts.src_flake,
|
||||
)
|
||||
log.info(f"Found template '{template.name}' in '{template.input_variant}'")
|
||||
if opts.src_flake is None:
|
||||
opts.src_flake = Flake(str(clan_templates()))
|
||||
|
||||
if dest.exists():
|
||||
dest /= template.name
|
||||
with clan_template(
|
||||
opts.src_flake, template_ident=opts.template, dst_dir=opts.dest
|
||||
) as _clan_dir:
|
||||
if opts.setup_git:
|
||||
run(git_command(dest, "init"))
|
||||
run(git_command(dest, "add", "."))
|
||||
|
||||
if dest.exists():
|
||||
msg = f"Destination directory {dest} already exists"
|
||||
raise ClanError(msg)
|
||||
# check if username is set
|
||||
has_username = run(
|
||||
git_command(dest, "config", "user.name"), RunOpts(check=False)
|
||||
)
|
||||
if has_username.returncode != 0:
|
||||
run(git_command(dest, "config", "user.name", "clan-tool"))
|
||||
|
||||
src = Path(template.src["path"])
|
||||
has_username = run(
|
||||
git_command(dest, "config", "user.email"), RunOpts(check=False)
|
||||
)
|
||||
if has_username.returncode != 0:
|
||||
run(git_command(dest, "config", "user.email", "clan@example.com"))
|
||||
|
||||
copy_from_nixstore(src, dest)
|
||||
|
||||
if opts.setup_git:
|
||||
run(git_command(dest, "init"))
|
||||
run(git_command(dest, "add", "."))
|
||||
|
||||
# check if username is set
|
||||
has_username = run(
|
||||
git_command(dest, "config", "user.name"), RunOpts(check=False)
|
||||
)
|
||||
if has_username.returncode != 0:
|
||||
run(git_command(dest, "config", "user.name", "clan-tool"))
|
||||
|
||||
has_username = run(
|
||||
git_command(dest, "config", "user.email"), RunOpts(check=False)
|
||||
)
|
||||
if has_username.returncode != 0:
|
||||
run(git_command(dest, "config", "user.email", "clan@example.com"))
|
||||
|
||||
if opts.update_clan:
|
||||
run(nix_command(["flake", "update"]), RunOpts(cwd=dest))
|
||||
if opts.update_clan:
|
||||
run(nix_command(["flake", "update"]), RunOpts(cwd=dest))
|
||||
|
||||
if opts.initial:
|
||||
inventory_store = InventoryStore(flake=Flake(str(opts.dest)))
|
||||
|
||||
@@ -99,3 +99,73 @@ def machine_template(
|
||||
finally:
|
||||
# If no error occurred, the machine directory is kept
|
||||
pass
|
||||
|
||||
|
||||
@contextmanager
|
||||
def clan_template(flake: Flake, template_ident: str, dst_dir: Path) -> Iterator[Path]:
|
||||
"""
|
||||
Create a clan from a template.
|
||||
This function will copy the template files to a new clan directory
|
||||
|
||||
:param flake: The flake to create the machine in.
|
||||
:param template_ident: The identifier of the template to use. Example ".#template_name"
|
||||
:param dst: The name of the directory to create.
|
||||
|
||||
|
||||
Example usage:
|
||||
|
||||
>>> with clan_template(
|
||||
... Flake("/home/johannes/git/clan-core"), ".#new-machine", "my-machine"
|
||||
... ) as clan_dir:
|
||||
... # Use `clan_dir` here if you want to access the created directory
|
||||
|
||||
... The directory is removed if the context raised any errors.
|
||||
... Only if the context is exited without errors, it is kept.
|
||||
"""
|
||||
|
||||
# Get the clan template from the specifier
|
||||
[flake_ref, template_selector] = transform_url("clan", template_ident, flake=flake)
|
||||
# For pretty error messages
|
||||
printable_template_ref = f"{flake_ref}#{template_selector}"
|
||||
|
||||
template_flake = Flake(flake_ref)
|
||||
|
||||
try:
|
||||
template = template_flake.select(template_selector)
|
||||
except ClanError as e:
|
||||
msg = f"Failed to select template '{template_ident}' from flake '{flake_ref}' (via attribute path: {printable_template_ref})"
|
||||
raise ClanError(msg) from e
|
||||
|
||||
src = template.get("path")
|
||||
if not src:
|
||||
msg = f"Malformed template: {printable_template_ref} does not have a 'path' attribute"
|
||||
raise ClanError(msg)
|
||||
|
||||
src_path = Path(src).resolve()
|
||||
|
||||
realize_nix_path(template_flake, str(src_path))
|
||||
|
||||
if not src_path.exists():
|
||||
msg = f"Template {printable_template_ref} does not exist at {src_path}"
|
||||
raise ClanError(msg)
|
||||
|
||||
if not src_path.is_dir():
|
||||
msg = f"Template {printable_template_ref} is not a directory at {src_path}"
|
||||
raise ClanError(msg)
|
||||
|
||||
if dst_dir.exists():
|
||||
msg = f"Destination directory {dst_dir} already exists"
|
||||
raise ClanError(msg)
|
||||
|
||||
copy_from_nixstore(src_path, dst_dir)
|
||||
|
||||
try:
|
||||
yield dst_dir
|
||||
except Exception as e:
|
||||
log.error(f"An error occurred inside the 'clan_template' context: {e}")
|
||||
log.info(f"Removing left-over directory: {dst_dir}")
|
||||
shutil.rmtree(dst_dir, ignore_errors=True)
|
||||
raise
|
||||
finally:
|
||||
# If no error occurred, the directory is kept
|
||||
pass
|
||||
|
||||
@@ -137,7 +137,7 @@ def test_clan_create_api(
|
||||
# TODO: We need to generate a lock file for the templates
|
||||
clan_cli.clan.create.create_clan(
|
||||
clan_cli.clan.create.CreateOptions(
|
||||
template_name="minimal", dest=dest_clan_dir, update_clan=False
|
||||
template="minimal", dest=dest_clan_dir, update_clan=False
|
||||
)
|
||||
)
|
||||
assert dest_clan_dir.is_dir()
|
||||
|
||||
Reference in New Issue
Block a user