Skip to content
15 changes: 12 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ jobs:
printf "\n\nmodule help ============================================\n"
module help python/3.9.5-alpine

set -x
python-exec echo donuts >test_output
cat test_output
grep --quiet donuts test_output
Expand All @@ -109,13 +110,14 @@ jobs:
cat test_output
grep --quiet 'Python 3.9.5' test_output
rm test_output
shpc uninstall --force python:3.9.5-alpine

# Try creating views install
mkdir -p tmp-modules
shpc config set views_base:tmp-modules
shpc view create noodles
shpc install --view noodles python:3.9.5-alpine
shpc view install noodles python:3.9.5-alpine

shpc uninstall --force python:3.9.5-alpine
shpc view --force delete noodles

- name: Run python module tests (tcsh)
shell: tcsh -e {0}
Expand Down Expand Up @@ -164,4 +166,11 @@ jobs:
cat test_output
grep --quiet 'Python 3.9.5' test_output
rm test_output

mkdir -p tmp-modules
shpc config set views_base:tmp-modules
shpc view create noodles
shpc view install noodles python:3.9.5-alpine

shpc uninstall --force python:3.9.5-alpine
shpc view --force delete noodles
6 changes: 0 additions & 6 deletions shpc/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,6 @@ def get_parser():
"install_recipe",
help="recipe to install\nshpc install python\nshpc install python:3.9.5-alpine",
)
install.add_argument(
"--view",
dest="view",
help="install module to a named view (must be installed to shpc first).",
default=None,
)
install.add_argument(
"--no-view",
dest="no_view",
Expand Down
13 changes: 5 additions & 8 deletions shpc/client/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
__license__ = "MPL 2.0"

import shpc.utils
from shpc.logger import logger


def main(args, parser, extra, subparser):
Expand All @@ -25,11 +24,9 @@ def main(args, parser, extra, subparser):
# Update config settings on the fly
cli.settings.update_params(args.config_params)

# It doesn't make sense to define view and no view
if args.view and args.no_view:
logger.exit("Conflicting arguments --view and --no-view, choose one.")

# And do the install
cli.install(
args.install_recipe, view=args.view, disable_view=args.no_view, force=args.force
)
cli.install(args.install_recipe, force=args.force)
if cli.settings.default_view and not args.no_view:
cli.view_install(
cli.settings.default_view, args.install_recipe, force=args.force
)
6 changes: 4 additions & 2 deletions shpc/client/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def create_from_file(

# Extra modules to install
for install_module in install_modules:
cli.install(install_module, view=view_name, disable_view=False, force=force)
cli.install(install_module, force=force)
cli.view_install(view_name, install_module, force=force)


def main(args, parser, extra, subparser):
Expand Down Expand Up @@ -168,7 +169,8 @@ def main(args, parser, extra, subparser):
# We don't make it hard to require them to install to the root first
module_name = args.params.pop(0)
if command == "install":
cli.install(module_name, view=view_name, disable_view=False, force=args.force)
cli.install(module_name, force=args.force)
cli.view_install(view_name, module_name, force=args.force)

if command == "uninstall":
cli.uninstall(module_name, view=view_name, force=args.force)
68 changes: 44 additions & 24 deletions shpc/main/modules/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,7 @@ def check(self, module_name):
config = self._load_container(module_name.rsplit(":", 1)[0])
return self.container.check(module_name, config)

def install(
self, name, tag=None, view=None, disable_view=False, force=False, **kwargs
):
def install(self, name, tag=None, force=False, **kwargs):
"""
Given a unique resource identifier, install a recipe.

Expand Down Expand Up @@ -386,26 +384,9 @@ def install(
subfolder = os.path.join(uri, tag.name)
container_dir = self.container.container_dir(subfolder)

# Are we also installing to a named view?
if view is None and not disable_view:
view = self.settings.default_view

# We only want to load over-rides for a tag at install time
config.load_override_file(tag.name)

# A view is a symlink under views_base/$view/$module
if view:
if view not in self.views:
logger.exit(
"View %s does not exist, shpc view create %s." % (view, view)
)

# Update view from name to be View to interact with
view = self.views[view]

# Don't continue if it exists, unless force is True
view.confirm_install(module_dir, force=force)

# Create the module and container directory
utils.mkdirp([module_dir, container_dir])

Expand Down Expand Up @@ -475,8 +456,47 @@ def install(
name = "%s:%s" % (name, tag.name)
logger.info("Module %s was created." % name)

# Install the module (symlink) to the view and create version file
if view:
view.install(module_dir)

return container_path

def view_install(self, view_name, name, tag=None, force=False):
"""
Install a module in a view.
The module must already be installed.
"""
name = self.add_namespace(name)

# If the module has a version, overrides provided tag
if ":" in name:
name, tag = name.split(":", 1)
config = self._load_container(name, tag)

# The chosen tag is set for the config (or defaults to latest)
if not config.tag:
logger.exit(
"%s is not a known identifier. Choices are:\n%s"
% (name, "\n".join(config.tags.keys()))
)

# We currently support gh, docker, path, or oras
uri = config.get_uri()

# If we have a path, the URI comes from the name
if ".sif" in uri:
uri = name.split(":", 1)[0]

# This is a tag object with name and digest
tag = config.tag
module_dir = os.path.join(self.settings.module_base, uri, tag.name)

# A view is a symlink under views_base/$view/$module
if view_name not in self.views:
logger.exit(
"View %s does not exist, shpc view create %s." % (view_name, view_name)
)

# Update view from name to be View to interact with
view = self.views[view_name]

# Don't continue if it exists, unless force is True
view.confirm_install(module_dir, force=force)
view.install(module_dir)
3 changes: 2 additions & 1 deletion shpc/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ def test_views(tmp_path, module_sys, module_file, container_tech, remote):
assert not view._config["view"]["system_modules"]

# Now install to it via the client
client.install("ghcr.io/autamus/emacs:27.2", view=view_name)
client.install("ghcr.io/autamus/emacs:27.2")
client.view_install(view_name, "ghcr.io/autamus/emacs:27.2")

# Ensure it was created and as a symlink
assert view._config["view"]["modules"]
Expand Down