Skip to content

Commit

Permalink
cli: Simplify conan argument passing
Browse files Browse the repository at this point in the history
BREAKING CHANGE: This fundamentally changes how cloe-launch
parses arguments. They now have the form:

    cloe-launch [OPTS] COMMAND [CMD_OPTS] CONANFILE [CONAN_INSTALL_ARGS] [-- ARGS]

For commands such as `shell` and `exec` it's now necessary to use `--`
to pass arguments to them. It is also no longer possible to provide
options to the cloe-launch command after specifying the conanfile.
These now go to conan.
  • Loading branch information
cassava committed Apr 22, 2024
1 parent 78bf138 commit 14e40a4
Show file tree
Hide file tree
Showing 29 changed files with 783 additions and 770 deletions.
4 changes: 2 additions & 2 deletions Makefile.package
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ smoketest:
@for conanfile in ${TEST_CONANFILES}; do \
test -f "$${conanfile}" || continue; \
printf "Running BATS tests with conanfile: $${conanfile}\n\n"; \
SHELL=/bin/bash ${CLOE_LAUNCH} shell -P "$${conanfile}" -- -c "source ${PROJECT_ROOT}/tests/setup_testname.bash; bats tests"; \
SHELL=/bin/bash ${CLOE_LAUNCH} shell "$${conanfile}" ${CONAN_OPTIONS} -- -c "source ${PROJECT_ROOT}/tests/setup_testname.bash; bats tests"; \
done

.PHONY: smoketest-deps
Expand All @@ -316,7 +316,7 @@ smoketest-deps:
@for conanfile in ${TEST_CONANFILES}; do \
test -f "$${conanfile}" || continue; \
echo "Building dependencies for conanfile: $${conanfile}"; \
${CLOE_LAUNCH} prepare -P "$${conanfile}" || exit 1; \
${CLOE_LAUNCH} prepare "$${conanfile}" ${CONAN_OPTIONS} || exit 1; \
done

# CONFIGURATION TARGETS -------------------------------------------------------
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ used.
Once the `cloe-launch` tool is available, you can do one of the following:
1. Launch a shell with the environment adjusted:
<!-- TODO: Update this example!! -->
```console
$ cloe-launch -v shell -P tests/conanfile_default.py
$ cloe-launch -v shell tests/conanfile_default.py
Source profile: tests/conanfile_default.py
Profile name: 7745ffb0e036192c8e29a8b8cc2b9571e7a72c8c
Configuration:
Expand All @@ -141,8 +142,9 @@ Once the `cloe-launch` tool is available, you can do one of the following:
...
```
2. Launch `cloe-engine` directly:
<!-- TODO: Update this example!! -->
```console
$ cloe-launch -v exec -P conanfile.py -- usage
$ cloe-launch -v exec conanfile.py -- usage
Source profile: tests/conanfile_default.py
Profile name: 7745ffb0e036192c8e29a8b8cc2b9571e7a72c8c
Configuration:
Expand Down
16 changes: 2 additions & 14 deletions cli/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,12 @@ help:
.PHONY: install
install: export
command -v ${PIP} >/dev/null 2>&1
# Work-around pip confused by pyproject.toml
-mv pyproject.toml pyproject.toml.bak
${PIP} install ${PIP_INSTALL_ARGS} . || ( \
mv pyproject.toml.bak pyproject.toml; \
exit 1; \
)
mv pyproject.toml.bak pyproject.toml
${PIP} install ${PIP_INSTALL_ARGS}

.PHONY: editable
editable: export
command -v ${PIP} >/dev/null 2>&1
# Work-around pip confused by pyproject.toml
-mv pyproject.toml pyproject.toml.bak
${PIP} install -e ${PIP_INSTALL_ARGS} . || ( \
mv pyproject.toml.bak pyproject.toml; \
exit 1; \
)
mv pyproject.toml.bak pyproject.toml
${PIP} install -e ${PIP_INSTALL_ARGS}

.PHONY: export
export:
Expand Down
21 changes: 21 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ This is easiest with the `Makefile`, which automates the whole process:
make editable
```

## Usage

<!-- TODO: Update this usage! -->
```
cloe-launch [-v] clean CONANFILE
cloe-launch [-v] prepare CONANFILE [CONAN_INSTALL_ARGS]
cloe-launch [-v] activate [-c] CONANFILE [CONAN_INSTALL_ARGS]
cloe-launch -v activate -c tests/conanfile.py -o cloe-engine:server=True
cloe-launch [-v] deploy [-c] [-f] [-r] [-D PATH] CONANFILE [CONAN_INSTALL_ARGS]
cloe-launch [-v] exec [-c] [-e VAR] [-E] [-d] CONANFILE [CONAN_INSTALL_ARGS] -- [ENGINE_ARGS]
cloe-launch exec tests/conanfile.py -o cloe-engine:server=False -- -l debug run
cloe-launch [-v] shell [-c] [-e VAR] [-E] CONANFILE [CONAN_INSTALL_ARGS] -- [SHELL_ARGS]
```

## Design Considerations

The CLI interface to `cloe-engine` should do as little as possible and as much
Expand Down
94 changes: 18 additions & 76 deletions cli/cloe_launch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import shutil
import subprocess

from typing import List
from typing import Optional
from typing import (
List,
Optional,
)

import toml

Expand All @@ -23,7 +25,6 @@ class Configuration:
config_dir = os.path.expanduser("~/.config/cloe/launcher/")

config_file = os.path.expanduser("~/.config/cloe/launcher/conf.toml")
profiles_dir = os.path.join(config_dir, "profiles")
runtime_dir = os.path.expanduser("~/.cache/cloe/launcher")

conf_version = "1"
Expand All @@ -38,20 +39,13 @@ class Configuration:
},
}

all_profiles: List[str] = []
default_profile: Optional[str] = None
current_profile = None

def __init__(self, profile: str = None):
def __init__(self):
# Make configuration and runtime directories if needed:
if not os.path.exists(self.config_dir):
logging.info("Create configuration directory:", self.config_dir)
logging.info("Create configuration directory: %s", self.config_dir)
os.makedirs(self.config_dir)
if not os.path.exists(self.profiles_dir):
logging.info("Create profile directory:", self.profiles_dir)
os.makedirs(self.profiles_dir)
if not os.path.exists(self.runtime_dir):
logging.info("Create runtime directory:", self.runtime_dir)
logging.info("Create runtime directory: %s", self.runtime_dir)
os.makedirs(self.runtime_dir)

# Load configuration file:
Expand All @@ -63,76 +57,24 @@ def __init__(self, profile: str = None):
)
for k in conf.keys():
self._conf[k] = conf[k]
self.default_profile = self._conf["default_profile"]

# Read all profile names from profile directory
self.all_profiles = [
f
for f in os.listdir(self.profiles_dir)
if os.path.isfile(self.profile_path(f))
]

# Set current profile
if profile is not None:
self.set_current(profile)
elif self.default_profile is not None:
self.set_current(self.default_profile)

def profile_path(self, profile: str) -> str:
"""Return the path to named profile."""
return os.path.join(self.profiles_dir, profile)

def profile_runtime(self, profile: str) -> str:
def profile_runtime(self, hash: str) -> str:
"""Return the path to the runtime directory of the profile."""
return os.path.join(self.runtime_dir, profile)
return os.path.join(self.runtime_dir, hash)

def set_current(self, profile: str) -> None:
"""Set the current profile and make sure it exists."""
self.current_profile = profile

def set_default(self, profile: str) -> None:
"""Set the default profile and write it to the configuration."""
if profile is not None and profile not in self.all_profiles:
raise ConfigurationError(f"profile {profile} does not exist")
self.default_profile = profile
self._conf["default_profile"] = profile
def write(self) -> None:
"""Write current configuration to the disk."""
logging.info(f"Write configuration to {self.config_file}:\n {self._conf}")
with open(self.config_file, "w") as file:
with open(self.config_file, "w", encoding="utf-8") as file:
toml.dump(self._conf, file)

def read(self, profile: str) -> str:
"""Read the specified profile."""
logging.info("Open:", self.profile_path(profile))
with open(self.profile_path(profile)) as file:
return file.read()

def edit(self, profile: str, create: bool = False) -> None:
"""Open the specified profile in the user's editor."""
def edit(self, create: bool = False) -> None:
"""Open the configuration in the user's editor."""
editor = os.getenv("EDITOR")
if editor is None:
raise ConfigurationError("environment variable EDITOR is unset")
if not create and not os.path.exists(self.profile_path(profile)):
raise ConfigurationError(f"profile {profile} does not exist")
cmd = [editor, self.profile_path(profile)]
logging.info("Exec:", " ".join(cmd))
if not create and not os.path.exists(self.config_file):
raise ConfigurationError(f"configuration {self.config_file} does not exist")
cmd = [editor, self.config_file]
logging.info("Exec: %s", " ".join(cmd))
subprocess.call(cmd)

def add(self, profile: str, file: str, force: bool = False) -> None:
"""Add the file as a profile."""
if profile in self.all_profiles and not force:
raise ConfigurationError(
f"cannot overwrite profile {profile} unless forced"
)
logging.debug("Copy: {} -> {}".format(file, self.profile_path(profile)))
shutil.copyfile(file, self.profile_path(profile))
if profile not in self.all_profiles:
self.all_profiles.append(profile)

def remove(self, profile: str) -> None:
"""Remove the profile, if it exists."""
file = os.path.join(self.profiles_dir, profile)
if os.path.exists(file):
logging.info("Remove:", file)
os.remove(file)
if self.default_profile == profile:
self.set_default(None)
Loading

0 comments on commit 14e40a4

Please sign in to comment.