diff --git a/.github/workflows/ci-rust.yaml b/.github/workflows/ci.yaml
similarity index 70%
rename from .github/workflows/ci-rust.yaml
rename to .github/workflows/ci.yaml
index 2d9ab9f..904532b 100644
--- a/.github/workflows/ci-rust.yaml
+++ b/.github/workflows/ci.yaml
@@ -1,10 +1,6 @@
name: Rust CI
-on:
- push:
- paths:
- - "rozy/**"
- - ".github/workflows/ci-rust.yaml"
+on: push
jobs:
build:
@@ -26,10 +22,10 @@ jobs:
toolchain: stable
target: ${{ matrix.target }}
- name: Format
- run: cargo fmt --manifest-path rozy/Cargo.toml
+ run: cargo fmt
- name: Clippy
- run: cargo clippy --manifest-path rozy/Cargo.toml
+ run: cargo clippy
- if: ${{ contains(matrix.target, '-musl') }}
run: sudo apt-get install musl-tools
- name: Test
- run: cargo test --target=${{ matrix.target }} --manifest-path rozy/Cargo.toml
+ run: cargo test --target=${{ matrix.target }}
diff --git a/.github/workflows/release-rust.yaml b/.github/workflows/release.yaml
similarity index 97%
rename from .github/workflows/release-rust.yaml
rename to .github/workflows/release.yaml
index b33288b..d6b26ac 100644
--- a/.github/workflows/release-rust.yaml
+++ b/.github/workflows/release.yaml
@@ -30,7 +30,7 @@ jobs:
- if: ${{ contains(matrix.target, '-musl') }}
run: sudo apt-get install musl-tools
- name: Build
- run: cargo build --release --verbose --target=${{ matrix.target }} --manifest-path rozy/Cargo.toml
+ run: cargo build --release --verbose --target=${{ matrix.target }}
- name: Ensure version is correct
run: rozy/target/${{ matrix.target }}/release/ozy --version | grep "$(echo $GITHUB_REF_NAME | cut -c2-)"
- name: Rename
diff --git a/.gitignore b/.gitignore
index 05d3bb2..613ddba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -137,3 +137,7 @@ dmypy.json
# VSCode
.vscode/
+
+# ust
+/target
+/test_resource/integration/.*
diff --git a/rozy/.idea/.gitignore b/.idea/.gitignore
similarity index 100%
rename from rozy/.idea/.gitignore
rename to .idea/.gitignore
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index 49f7b87..0000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml
deleted file mode 100644
index dc8a6e9..0000000
--- a/.idea/dbnavigator.xml
+++ /dev/null
@@ -1,454 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
new file mode 100644
index 0000000..68ba0ac
--- /dev/null
+++ b/.idea/editor.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 912e521..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index c6d6b57..94eab37 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -5,4 +5,4 @@
-
\ No newline at end of file
+
diff --git a/.idea/ozy.iml b/.idea/ozy.iml
index 2c80e12..c254557 100644
--- a/.idea/ozy.iml
+++ b/.idea/ozy.iml
@@ -1,8 +1,9 @@
-
+
-
+
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 94a25f7..35eb1dd 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/rozy/Cargo.lock b/Cargo.lock
similarity index 100%
rename from rozy/Cargo.lock
rename to Cargo.lock
diff --git a/rozy/Cargo.toml b/Cargo.toml
similarity index 100%
rename from rozy/Cargo.toml
rename to Cargo.toml
diff --git a/README.md b/README.md
index 7d15589..cc82100 100644
--- a/README.md
+++ b/README.md
@@ -129,12 +129,20 @@ This will remove `~/.cache/ozy`!
TODO
+## Developing locally
+
+`ozy` is written in Rust. A quick start:
+1. Install [`rustup`](https://rustup.rs/)
+2. Run `install.sh`
+3. Prepend `$HOME/.ozy/bin` to your path to have access to managed apps
+
+Performance is really only a consideration on the common case of running a ozy-managed binary. This is why we prefer not to introduce asynchrony or even template caching in the `install-all` path.
+
## Making a release
To make a release of ozy:
-* Update the `__version__` in `ozy/__init__.py`
-* Ensure the `RELEASE_NOTES.md` are updated
+* Ensure the `RELEASE_NOTES.md` are updated (not required)
* Push the changes to GitHub
* Create a tag of the form `v1.2.3` and push it to GitHub
* GH actions will make the binaries automatically
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index b45890d..1ef4233 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,71 +1,13 @@
# ozy release notes
-## 0.0.14
-* Add support for ozy self-update
+All changes are in the [Releases](https://github.com/aquanauts/ozy/releases).
-## 0.0.13
-* changed CondaInstaller to work with micromamba
-* added empty `__init__.py` files in test directory tree to appease vscode
+### [0.1.11](https://github.com/aquanauts/ozy/releases/tag/v0.1.11)
+* Use `is_file` to avoid "is a directory error" (fixing #66)
+* Support symbolic link installations.
-## 0.0.12
-* Create a lockfile per installation to allow more concurrency
-* Fix concurrent conda installations
-* Add list command
+### [0.1.10](https://github.com/aquanauts/ozy/releases/tag/v0.1.10)
+* Support for passing environment variables
-## 0.0.11
-* Fix race condition related to concurrent installations.
-
-## 0.0.10
-* Fix for detecting `ozy`. Basically, 0.0.6-0.0.9 were broken once packaged.
-
-## 0.0.9
-* Build linux on Ubuntu 18.04 so we can run the release binaries on Ubuntu 18.04
-
-## 0.0.8
-* Add support for `pyinstaller`-compressed `conda` binaries. Set `pyinstaller: True` to have `ozy` install the conda binary in a temporary place, then squash it with pyinstaller, and use the squashed version instead.
-
-## 0.0.7
-* Add support for `zip` file releases
-
-## 0.0.6
-* Adds support for post installation commands
-* New command-line "install" to force install a subset of apps
-* Support for OSX
-
-## 0.0.5
-* Add support for `pip` installers (via `conda`).
-
-## 0.0.4
-* Fix logging crash on `ozy install-all`
-
-## 0.0.3
-* Fix for `ozy` running python programs: `PYTHONPATH` and `LD_LIBRARY_PATH` were left hijacked by
- `ozy`'s package system (pyinstaller).
-
-## 0.0.2
-* Bug fix in makefile-config
-
-
-## 0.0.1-pre (First test release)
-* Support makefile-config
-
-## 0.0.0 (Work in progress)
-* Init, info, update implemented
-* Apps! Support for `nomad`, `terraform`, and `vault` (general support for any Hashicorp thing)
-* Working [README.md](README.md)
-* One-line installs a la lake-client
-* Supports installing `conda` packages
-
-
----
-
-TODO
-* Check the shas and the signed security files
-* Support `rm`
-* Support `clean`
-* Open source!
-* "flock" for multiple ozy invocations at once?
-
-More Apps!
-* `docker`
-* `sshfs`
+### Older releases
+See the Releases page.
diff --git a/bin/ozy b/bin/ozy
deleted file mode 100755
index 6b4bf2f..0000000
--- a/bin/ozy
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env python
-
-import os
-import sys
-
-if getattr(sys, 'frozen', False):
- # Running under pyinstaller
- from ozy.__main__ import app_main
-
- # https://pyinstaller.readthedocs.io/en/stable/runtime-information.html#using-sys-executable-and-sys-argv-0
- # pyinstaller may override LD_LIBRARY_PATH, but if it does so, it saves it in LD_LIBRARY_PATH_ORIG
- app_main(sys.executable, sys.argv[0], sys.argv[1:], True)
-else:
- # Running in dev mode
- OZY_DIR = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
-
- OZY_PYTHON = os.path.join(OZY_DIR, '.venv', 'bin', 'python3')
- environment = os.environ.copy()
- # To ensure we work like pyinstaller, we preserve the "original" LD_LIBRARY_PATH here even though we don't modify
- # it. We _do_ modify PYTHONPATH so we save that here.
- for possibly_overridden in ('PYTHONPATH', 'LD_LIBRARY_PATH'):
- if possibly_overridden in environment:
- environment[possibly_overridden + '_ORIG'] = environment[possibly_overridden]
- environment['PYTHONPATH'] = OZY_DIR
- # We pass argv[0] again to the python executable: if we don't use OZY_PYTHON as argv[0], virtualenv flips out.
- # As the ozy script needs to know the name it was invoked as, we pass all of argv again.
- # i.e. if invoked as "nomad --version" we will "path/to/python path/to/ozy/__main__.py nomad --version
- os.execve(OZY_PYTHON, [OZY_PYTHON, os.path.join(OZY_DIR, 'ozy', '__main__.py')] + sys.argv, environment)
diff --git a/rozy/install.sh b/install.sh
similarity index 100%
rename from rozy/install.sh
rename to install.sh
diff --git a/ozy/__init__.py b/ozy/__init__.py
deleted file mode 100644
index e9ccf40..0000000
--- a/ozy/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-__version__ = '0.0.14'
-import logging
-
-_LOGGER = logging.getLogger(__name__)
-
-
-class OzyError(Exception):
- pass
diff --git a/ozy/__main__.py b/ozy/__main__.py
deleted file mode 100644
index 9f7eb9d..0000000
--- a/ozy/__main__.py
+++ /dev/null
@@ -1,323 +0,0 @@
-import logging
-import os
-from pathlib import Path
-import shutil
-import sys
-
-from packaging import version
-
-import click
-import coloredlogs
-
-from ozy import OzyError, __version__
-from ozy.app import App, find_app
-from ozy.config import load_ozy_user_conf, save_ozy_user_conf, parse_ozy_conf, load_config, safe_expand
-from ozy.files import ensure_ozy_dirs, get_ozy_bin_dir, softlink, get_ozy_dir
-from ozy.utils import download_to, restore_overridden_env_vars
-
-_LOGGER = logging.getLogger(__name__)
-
-PATH_TO_ME = None # TODO find a better way
-IS_SINGLE_FILE = False # TODO find a better way
-
-
-def _print_version(ctx, param, value):
- if not value or ctx.resilient_parsing:
- return
- click.echo(f'ozy v{__version__}')
- ctx.exit()
-
-
-@click.group()
-@click.option("--debug/--no-debug", default=False)
-@click.option('--version', is_flag=True, callback=_print_version, expose_value=False, is_eager=True)
-def main(debug):
- # TODO detect if redirected and don't do this, etc
- coloredlogs.install(
- fmt='%(message)s',
- level=debug and 'DEBUG' or 'INFO')
-
-
-@main.command()
-@click.argument("url", metavar="URL", type=str)
-def init(url):
- """Initialise and install ozy, with configuration from the given URL."""
- ensure_ozy_dirs()
- user_conf = load_ozy_user_conf()
- # TODO: make sure it isn't there already? upgrade/update instead?
- ozy_conf_filename = f"{get_ozy_dir()}/ozy.yaml"
- download_to(ozy_conf_filename, url)
- ozy_bin_dir = get_ozy_bin_dir()
- root_conf = parse_ozy_conf(ozy_conf_filename) ## TODO think how this interacts with local config files
-
- symlink_binaries(ozy_bin_dir, root_conf)
- user_conf['url'] = url
- save_ozy_user_conf(user_conf)
-
- if not check_path(ozy_bin_dir):
- _LOGGER.info("ozy is installed, but needs a little more setup work:")
- show_path_warning(ozy_bin_dir)
- else:
- _LOGGER.info("ozy is installed and is ready to run")
-
-
-def symlink_binaries(ozy_bin_dir, config):
- global PATH_TO_ME, IS_SINGLE_FILE
- if IS_SINGLE_FILE:
- dest_filename = os.path.join(ozy_bin_dir, 'ozy')
- if os.path.exists(dest_filename) and os.path.samefile(PATH_TO_ME, dest_filename):
- _LOGGER.debug("Not copying anything as we're already in the right place")
- else:
- _LOGGER.debug("Copying single-file ozy distribution")
- if os.path.exists(dest_filename):
- os.unlink(dest_filename)
- shutil.copyfile(PATH_TO_ME, dest_filename)
- shutil.copymode(PATH_TO_ME, dest_filename)
- else:
- _LOGGER.debug("Symlinking dev ozy")
- softlink(from_command=PATH_TO_ME, to_command='ozy', ozy_bin_dir=ozy_bin_dir)
- for app in config['apps']:
- if not softlink(from_command='ozy', to_command=app, ozy_bin_dir=ozy_bin_dir):
- _LOGGER.info("Supporting app '%s'", app)
-
-
-def show_path_warning(ozy_bin_dir):
- _LOGGER.warning("-" * 80)
- _LOGGER.warning("Please ensure '%s' is on your path", ozy_bin_dir)
- _LOGGER.info("bash shell users:")
- _LOGGER.info(" bash$ echo -e '# ozy support\\nexport PATH=%s:$PATH' >> ~/.bashrc", ozy_bin_dir)
- _LOGGER.info(" then restart your shell sessions")
- _LOGGER.info("zsh shell users:")
- _LOGGER.info(f" zsh$ # path+=({ozy_bin_dir})\\nexport PATH")
- _LOGGER.info("fish shell users: ")
- _LOGGER.info(" fish$ set --universal fish_user_paths %s $fish_user_paths", ozy_bin_dir)
- _LOGGER.warning("-" * 80)
-
-
-def check_path(ozy_bin_dir):
- real_paths = set(os.path.realpath(path) for path in os.getenv("PATH").split(":"))
- if os.path.realpath(ozy_bin_dir) in real_paths:
- return True
- return False
-
-
-@main.command()
-@click.option("--url", metavar="URL", type=str, help="configuration URL (default will use previously set)")
-@click.option("--dry-run/--no-dry-run", help="make no changes, just show what would happen", default=False)
-def update(dry_run, url):
- """Update base configuration from the remote URL."""
- user_conf = load_ozy_user_conf()
- if not url:
- if 'url' not in user_conf:
- raise OzyError('Missing url in configuration')
- url = user_conf['url']
- ozy_conf_filename = f"{get_ozy_dir()}/ozy.yaml"
- tmp_filename = ozy_conf_filename + ".tmp"
- download_to(tmp_filename, url)
- new_conf_root = parse_ozy_conf(tmp_filename)
- old_conf_root = parse_ozy_conf(ozy_conf_filename)
-
- new_ozy_version: str = new_conf_root['ozy_version']
- if version.parse(__version__) < version.parse(new_ozy_version):
- _LOGGER.info('Ozy update to %s is mandated by your team config', new_ozy_version)
- download_url = safe_expand(dict(version=new_ozy_version), new_conf_root['ozy_download'])
- _LOGGER.info('Downloading from %s', download_url)
- download_path = Path(get_ozy_dir()) / 'bin' / 'ozy.tmp'
- download_path.unlink(missing_ok=True)
- download_to(str(download_path), download_url)
- download_path.chmod(0o755)
- new_ozy_exe = str(download_path)
- environment = restore_overridden_env_vars(os.environ)
- os.execve(new_ozy_exe, [new_ozy_exe, 'update'], environment)
-
- changed = False
- for app, new_conf in new_conf_root['apps'].items():
- old_conf = old_conf_root['apps'].get(app, None)
- if not old_conf:
- _LOGGER.info('%s new app %s (%s)', "Would install" if dry_run else "Installing", app, new_conf['version'])
- changed = True
- elif old_conf['version'] != new_conf['version']:
- _LOGGER.info('%s %s from %s to %s', "Would upgrade" if dry_run else "Upgrading", app, old_conf['version'],
- new_conf['version'])
- changed = True
-
- if not dry_run:
- ozy_bin_dir = get_ozy_bin_dir()
- user_conf['url'] = url
- save_ozy_user_conf(user_conf)
- os.rename(tmp_filename, ozy_conf_filename)
- symlink_binaries(ozy_bin_dir, new_conf_root)
- if not changed:
- _LOGGER.info("No changes made")
- else:
- if changed:
- _LOGGER.info("Dry run only - no changes made")
- else:
- _LOGGER.info("Dry run only - no changes would be made, even without --dry-run")
- os.unlink(tmp_filename)
-
-
-@main.command()
-def info():
- """Print information about the installation and configuration."""
- _LOGGER.info(f"ozy v{__version__}")
- ozy_bin_dir = get_ozy_bin_dir()
- path_ok = check_path(ozy_bin_dir)
- if not path_ok:
- show_path_warning(ozy_bin_dir)
- user_config = load_ozy_user_conf()
- _LOGGER.info("Team URL: %s", user_config.get("url", "(unset)"))
- config = load_config()
- _LOGGER.info("Team config name: %s", config.get("name", "(unset)"))
- for app_name in config['apps']:
- app = App(app_name, config)
- _LOGGER.info(" %s: %s", app_name, app)
- if path_ok:
- found_app = shutil.which(app_name)
- if not found_app:
- _LOGGER.warning(" %s not found on path: perhaps an 'ozy sync' is needed?", app_name)
- else:
- if os.path.realpath(found_app) != os.path.realpath(os.path.join(ozy_bin_dir, app_name)):
- _LOGGER.warning(" %s is not under ozy control! It was found on your PATH earlier than ozy at %s",
- app_name, found_app)
-
-
-@main.command(name="list")
-def list_cmd():
- """List all known package namess."""
- config = load_config()
- for app_name in config['apps']:
- click.echo(app_name)
-
-
-@main.command()
-def install_all():
- """Ensures all applications are installed at their current prevailing versions."""
- config = load_config()
- for app_name in config['apps']:
- app = App(app_name, config)
- app.ensure_installed()
-
-
-@main.command()
-@click.argument("apps", metavar="APP...", nargs=-1, required=True, type=str)
-def install(apps):
- """Ensures the named applications are installed at their current prevailing versions."""
- config = load_config()
- for app_name in apps:
- if app_name not in config['apps']:
- raise OzyError(f"App '{app_name}' was not found")
- app = App(app_name, config)
- app.ensure_installed()
-
-
-@main.command()
-def sync():
- """
- Synchronise any local changes.
-
- If you're defining new applications in local user files, you can use this to ensure
- the relevant symlinks are created in your ozy bin directory.
- """
- symlink_binaries(get_ozy_bin_dir(), load_config())
-
-
-def _makefile_error(error):
- print(f'$(error "{error}")') # todo escape
- sys.exit(1)
-
-
-@main.command()
-@click.option("--all-apps/--no-all-apps", help="include all APPs", default=False)
-@click.argument("makefile_var", metavar="VAR", type=str)
-@click.argument("required_apps", metavar="APP", nargs=-1, type=str)
-def makefile_config(makefile_var, required_apps, all_apps):
- """
- Checks apps, and prints a single-line Makefile variable.
-
- Use as an argument to $(eval). Errors will are output as $(error) directives
- to report in make.
- The given variable is defined to be the ozy binary directory, so any app will be
- $(VAR)/app_name. If undefined, you know ozy isn't installed.
-
- Example:
-
- \b
- $ cat Makefile
- $(eval $(shell ozy makefile-config OZY_BIN_DIR terraform))
- ifndef OZY_BIN_DIR
- $(error please install ozy)
- endif
-
- \b
- install:
- $(OZY_BIN_DIR)/terraform apply
- """
- config = load_config()
- ozy_bin_dir = get_ozy_bin_dir()
- path_ok = check_path(ozy_bin_dir)
- if not path_ok:
- _makefile_error("ozy is not on the path")
- if all_apps:
- required_apps = config['apps'].keys()
- if not required_apps:
- _makefile_error("no ozy apps found to configure")
- for app_name in required_apps:
- if app_name not in config['apps']:
- _makefile_error(f"Missing ozy app '{app_name}'")
- app = App(app_name, config)
- found_app = shutil.which(app_name)
- app_in_bin = os.path.join(ozy_bin_dir, app_name)
- if os.path.realpath(found_app) != os.path.realpath(app_in_bin):
- _makefile_error(f"{found_app} found in PATH earlier than ozy: "
- f"results could be inconsistent (found at {found_app})")
- print(f"{makefile_var}:={ozy_bin_dir}")
-
-
-@main.command()
-@click.option("--version", metavar="VERSION", type=str)
-@click.argument("app", metavar="APP", type=str)
-@click.argument("arguments", metavar="ARG", nargs=-1, type=str)
-def run(app, arguments, version):
- """Runs the given application."""
- _run(app, arguments, version)
-
-
-def _run(app, arguments, version=None):
- tool = find_app(app, version)
- if not tool:
- raise OzyError(f"Unable to find ozy-controlled app '{app}'")
- tool.ensure_installed()
- try:
- # The child process shouldn't get any of our overridden variables; put the original ones back.
- environment = restore_overridden_env_vars(os.environ)
- os.execve(tool.executable, [tool.executable] + list(arguments), environment)
- except Exception as e:
- _LOGGER.error("Unable to execute %s: %s", tool.executable, e)
- raise
-
-
-def app_main(path_to_ozy, argv0, arguments, is_single_file):
- global PATH_TO_ME, IS_SINGLE_FILE
- PATH_TO_ME = os.path.realpath(path_to_ozy)
- IS_SINGLE_FILE = is_single_file
-
- invoked_as = os.path.basename(argv0)
- # If we were invoked as "ourself" (or something prefixed with ozy, to cover `ozy-Linux_x86_64` etc) then run as if
- # it was 'ozy'. We allow for anything called 'ozy' to be "us", rather than relying on the `argv0` being a symlink or
- # similar: under pyinstaller we lose the full path to the original executable, so can't check if it's "really" ozy
- # being called directly, or a symlink to ozy.
- if invoked_as.startswith('ozy'):
- main(prog_name='ozy', args=arguments)
- else:
- coloredlogs.install(fmt='%(message)s', level='INFO')
- _run(invoked_as, arguments)
-
-
-if __name__ == "__main__":
- try:
- app_main(sys.argv[1], sys.argv[1], sys.argv[2:], False)
- except OzyError as ozy_error:
- _LOGGER.error(ozy_error)
- _LOGGER.debug(ozy_error, exc_info=True)
- sys.exit(1)
diff --git a/ozy/app.py b/ozy/app.py
deleted file mode 100644
index b9c2654..0000000
--- a/ozy/app.py
+++ /dev/null
@@ -1,121 +0,0 @@
-import fcntl
-import logging
-import os
-import shutil
-import shlex
-from subprocess import check_call
-from typing import Union, List
-import uuid
-
-from ozy import OzyError
-from ozy.config import resolve, load_config
-from ozy.files import get_ozy_cache_dir
-from ozy.installers import SUPPORTED_INSTALLERS
-
-_LOGGER = logging.getLogger(__name__)
-
-
-def ensure_keys(name, config, *keys):
- values = []
- for required_key in keys:
- if required_key not in config:
- raise OzyError(f"Missing required key '{required_key}' in '{name}'")
- values.append(config[required_key])
- return values
-
-
-def _fixup_one_command(command):
- if isinstance(command, list):
- return command
- return shlex.split(command)
-
-
-def fixup_post_install(post_install: Union[list, str]) -> List[List[str]]:
- if not post_install:
- return []
- if isinstance(post_install, str):
- return [_fixup_one_command(post_install)]
- post_install = [_fixup_one_command(x) for x in post_install]
- return post_install
-
-
-class App:
- def __init__(self, name, root_config):
- self._name = name
- self._root_config = root_config
- self._config = resolve(root_config['apps'][name], self._root_config.get('templates', {}))
- self._executable_path = self._config.get('executable_path', self.name)
- self._relocatable = self._config.get('relocatable', True)
- self._post_install = fixup_post_install(self._config.get('post_install', []))
- self._version, install_type = ensure_keys(name, self._config, 'version', 'type')
- if install_type not in SUPPORTED_INSTALLERS:
- raise OzyError(f"Unsupported installation type '{install_type}'")
- self._installer = SUPPORTED_INSTALLERS[install_type](name, self._config)
-
- def __str__(self):
- return f'{self.name} {self._version} ({self._installer})'
-
- @property
- def name(self) -> str:
- return self._name
-
- @property
- def config(self) -> dict:
- return self._config
-
- @property
- def version(self) -> str:
- return self._version
-
- @property
- def install_path(self) -> str:
- return os.path.join(get_ozy_cache_dir(), self.name, self.version)
-
- @property
- def executable(self) -> str:
- return os.path.join(self.install_path, self._executable_path)
-
- def is_installed(self) -> bool:
- return os.path.isdir(self.install_path)
-
- def _install(self):
- _LOGGER.info("Installing %s %s", self.name, self.version)
- uniq_install_dir = f"{self.install_path}.{uuid.uuid4()}"
- try:
- self._installer.install(uniq_install_dir)
- if self._relocatable:
- os.rename(uniq_install_dir, self.install_path)
- else:
- os.symlink(uniq_install_dir, self.install_path)
- except Exception:
- shutil.rmtree(uniq_install_dir, ignore_errors=True)
- raise
-
- for install_step in self._post_install:
- _LOGGER.info("Running post install step '%s'", " ".join(install_step))
- check_call(install_step, cwd=self.install_path)
-
- def ensure_installed(self):
- if self.is_installed(): return
-
- lockfile_path = f"{self.install_path}.lock"
- os.makedirs(os.path.dirname(lockfile_path), exist_ok=True)
- with open(lockfile_path, 'w') as lockfile:
- try:
- fcntl.flock(lockfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
- except BlockingIOError:
- _LOGGER.info("Waiting for concurrent install to complete...")
- fcntl.flock(lockfile.fileno(), fcntl.LOCK_EX)
- if not self.is_installed():
- self._install()
-
-
-def find_app(tool, version=None):
- overrides = None
- if version:
- overrides = dict(apps=dict(tool=dict(version=version)))
- config = load_config(overrides)
- if tool in config['apps']:
- return App(tool, config)
- else:
- return None
diff --git a/ozy/config.py b/ozy/config.py
deleted file mode 100644
index ba99ede..0000000
--- a/ozy/config.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import logging
-import os
-from collections import ChainMap
-
-import yaml
-
-from ozy import OzyError
-from ozy.files import walk_up_dirs, get_ozy_dir
-
-_LOGGER = logging.getLogger(__name__)
-
-
-def load_config(overrides=None, config=None, current_working_directory=None):
- if config is None:
- config = parse_ozy_conf(f"{get_ozy_dir()}/ozy.yaml")
- if current_working_directory is None:
- current_working_directory = os.getcwd()
-
- # Annoyingly can't just use a chainmap here as nested maps don't work the way we'd like
- ozy_files = []
- for path in walk_up_dirs(current_working_directory):
- conf_file = os.path.join(path, '.ozy.yaml')
- if os.path.isfile(conf_file):
- ozy_files.append(conf_file)
- for path in reversed(ozy_files):
- apply_overrides(parse_ozy_conf(path), config)
- if overrides:
- apply_overrides(overrides, config)
- _LOGGER.debug(config)
- return config
-
-
-def apply_overrides(source, destination):
- for key, value in source.items():
- if isinstance(value, dict):
- node = destination.setdefault(key, {})
- apply_overrides(value, node)
- else:
- destination[key] = value
-
- return destination
-
-
-def resolve(config, templates):
- if 'template' in config:
- template_name = config['template']
- if template_name not in templates:
- raise OzyError(f"Unable to find template '{template_name}'")
- # TODO had these the wrong way round to start with. make a test
- config = ChainMap(config,
- templates[template_name])
- return {key: safe_expand(config, value) for key, value in config.items()}
-
-
-def safe_expand(format_params, to_expand):
- if isinstance(to_expand, list):
- return [safe_expand(format_params, x) for x in to_expand]
- elif not isinstance(to_expand, str):
- return to_expand
-
- params = get_system_variables()
- params.update(format_params)
- try:
- return to_expand.format(**params)
- except KeyError as ke:
- raise OzyError(f"Could not find key {ke} in expansion '{to_expand}' with params '{format_params}'")
-
-
-def load_ozy_user_conf():
- user_conf_file = get_user_conf_file()
- user_conf = dict()
- if os.path.exists(user_conf_file):
- user_conf = parse_ozy_conf(user_conf_file)
- return user_conf
-
-
-def save_ozy_user_conf(config):
- with open(get_user_conf_file(), 'w') as user_conf_file_obj:
- yaml.dump(config, user_conf_file_obj)
-
-
-def get_user_conf_file():
- user_conf_file = f"{get_ozy_dir()}/ozy.user.yaml"
- return user_conf_file
-
-
-def parse_ozy_conf(ozy_file_name):
- _LOGGER.debug("Parsing config %s", ozy_file_name)
- with open(ozy_file_name, "r") as ofnh:
- yaml_content = yaml.load(ofnh, Loader=yaml.UnsafeLoader)
- return yaml_content
-
-
-def get_system_variables():
- uname = os.uname()
- return {
- 'ozy_os': uname.sysname.lower(),
- 'ozy_machine': uname.machine,
- 'ozy_arch': 'amd64' if uname.machine == 'x86_64' else uname.machine
- }
diff --git a/ozy/files.py b/ozy/files.py
deleted file mode 100644
index 5dec8ab..0000000
--- a/ozy/files.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import logging
-import os
-
-from ozy import OzyError
-
-_LOGGER = logging.getLogger(__name__)
-
-
-def walk_up_dirs(path):
- path = os.path.realpath(path)
- previous_path = None
- while path != previous_path:
- yield path
- previous_path = path
- path = os.path.realpath(os.path.join(path, '..'))
-
-
-def ensure_ozy_dirs():
- [os.makedirs(p, exist_ok=True) for p in (get_ozy_dir(), get_ozy_bin_dir(), get_ozy_cache_dir())]
-
-
-def get_home_dir() -> str:
- if 'HOME' in os.environ:
- return os.environ['HOME']
- raise OzyError("HOME env variable not found")
-
-
-def get_ozy_dir() -> str:
- return f"{get_home_dir()}/.ozy"
-
-
-def get_ozy_cache_dir() -> str:
- return os.path.join(os.getenv('XDG_CACHE_HOME', f"{get_home_dir()}/.cache"), 'ozy')
-
-
-def get_ozy_bin_dir() -> str:
- return os.path.join(get_ozy_dir(), "bin")
-
-
-def softlink(from_command, to_command, ozy_bin_dir):
- # assume this linkage will ONLY be called by ozy
- path_to_app = os.path.join(ozy_bin_dir, to_command)
- was_there = os.path.exists(path_to_app)
- if was_there:
- _LOGGER.debug(f"Clobbering symlink path {path_to_app}")
- os.unlink(path_to_app)
- os.symlink(from_command, path_to_app)
- return was_there
diff --git a/ozy/installer.py b/ozy/installer.py
deleted file mode 100644
index 6ebb0af..0000000
--- a/ozy/installer.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from typing import Any
-
-from ozy import OzyError
-
-
-class Installer:
- def __init__(self, name: str, config: dict, *required_keys, **default_keys):
- self._name = name
- self._config = default_keys.copy()
- self._executable_path = config.get('executable_path', name)
-
- for required_key in required_keys:
- if required_key not in config:
- raise OzyError(f"Missing required key '{required_key}' in '{name}'")
- self._config[required_key] = config[required_key]
- for optional_key in default_keys.keys():
- if optional_key in config:
- self._config[optional_key] = config[optional_key]
-
- def config(self, name) -> Any:
- return self._config[name]
-
- def install(self, to_dir: str):
- raise RuntimeError("Must be overridden")
-
-# TODO tests for installers!
diff --git a/ozy/installers/__init__.py b/ozy/installers/__init__.py
deleted file mode 100644
index df26be3..0000000
--- a/ozy/installers/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from ozy.installers.conda import CondaInstaller
-from ozy.installers.file import SingleFileInstaller
-from ozy.installers.pip import PipInstaller
-from ozy.installers.shell import ShellInstaller
-from ozy.installers.tar import TarballInstaller
-from ozy.installers.zip import SingleBinaryZipInstaller, ZipInstaller
-
-SUPPORTED_INSTALLERS = dict(
- single_binary_zip=SingleBinaryZipInstaller,
- zip=ZipInstaller,
- tarball=TarballInstaller,
- single_file=SingleFileInstaller,
- shell_install=ShellInstaller,
- conda=CondaInstaller,
- pip=PipInstaller
-)
diff --git a/ozy/installers/conda.py b/ozy/installers/conda.py
deleted file mode 100644
index 4df93f7..0000000
--- a/ozy/installers/conda.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import logging
-import os
-from pathlib import Path
-from subprocess import check_call
-from tempfile import TemporaryDirectory
-from typing import List
-
-from ozy.installer import Installer
-
-_LOGGER = logging.getLogger(__name__)
-
-
-def do_conda_install(conda_bin, channels, to_dir, to_install):
- Path(to_dir).parent.mkdir(parents=True, exist_ok=True)
- channels_args = []
- for channel in channels:
- channels_args += ['-c', channel]
- with TemporaryDirectory(dir=Path(to_dir).parent) as conda_cache_dir:
- env = os.environ.copy()
- env['CONDA_PKGS_DIRS'] = conda_cache_dir
- args = [conda_bin, 'create', '-y'] + channels_args + ['-p', to_dir] + to_install
- _LOGGER.debug("Executing %s", " ".join(args))
- check_call(args, env=env)
-
-
-class CondaInstaller(Installer):
- def __init__(self, name, config):
- super().__init__(name, config, 'package', 'version', channels=[], conda_bin="conda", pyinstaller=False)
-
- def __str__(self):
- return f'conda app installer for {self.config("package")}'
-
- def _conda_install(self, to_dir: str, to_install: List[str]):
- do_conda_install(self.config("conda_bin"), self.config('channels'), to_dir, to_install)
-
- def install(self, to_dir):
- versioned_package = f'{self.config("package")}={self.config("version")}'
- if self.config('pyinstaller'):
- with TemporaryDirectory(prefix='ozy-conda-installer') as td:
- self._conda_install(td, [versioned_package, 'pyinstaller'])
- pyinst = Path(td) / 'bin' / 'pyinstaller'
- dest = (Path(to_dir) / self._executable_path).parent
- args = [
- str(pyinst),
- '--onefile',
- '--name', self._name,
- '--distpath', str(dest),
- str(Path(td) / self._executable_path)
- ]
- _LOGGER.debug("Executing %s", " ".join(args))
- check_call(args)
- else:
- self._conda_install(to_dir, [versioned_package])
diff --git a/ozy/installers/file.py b/ozy/installers/file.py
deleted file mode 100644
index 0fd5b83..0000000
--- a/ozy/installers/file.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import os
-
-from ozy.installer import Installer
-from ozy.utils import download_to_file_obj
-
-
-class SingleFileInstaller(Installer):
- def __init__(self, name, config):
- super().__init__(name, config, 'url', app_name=name)
-
- def __str__(self):
- return f'file installer from {self.config("url")}'
-
- def install(self, to_dir):
- os.makedirs(to_dir)
- url = self.config('url')
- app_path = os.path.join(to_dir, self.config('app_name'))
- with open(app_path, 'wb') as output_file:
- download_to_file_obj(output_file, url)
- os.chmod(app_path, 0o774)
diff --git a/ozy/installers/pip.py b/ozy/installers/pip.py
deleted file mode 100644
index 07bebcc..0000000
--- a/ozy/installers/pip.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import logging
-import os
-from subprocess import check_call
-
-from ozy.installer import Installer
-from ozy.installers.conda import do_conda_install
-
-_LOGGER = logging.getLogger(__name__)
-
-
-class PipInstaller(Installer):
- def __init__(self, name, config):
- super().__init__(name, config, 'package', 'version', channels=[])
-
- def __str__(self):
- return f'pip app installer for {self.config("package")}'
-
- def install(self, to_dir):
- # Create a conda environment with pip installed (which brings in python et al)
- do_conda_install('conda', self.config('channels'), to_dir, ['pip'])
- # Use that environment to pip install the user's package
- args = [os.path.join(to_dir, 'bin', 'pip'), 'install', f'{self.config("package")}=={self.config("version")}']
- _LOGGER.debug("Executing %s", " ".join(args))
- check_call(args)
diff --git a/ozy/installers/shell.py b/ozy/installers/shell.py
deleted file mode 100644
index 778effb..0000000
--- a/ozy/installers/shell.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import os
-from subprocess import check_call
-from tempfile import NamedTemporaryFile
-
-from ozy.installer import Installer
-from ozy.utils import download_to_file_obj
-
-
-class ShellInstaller(Installer):
- def __init__(self, name, config):
- super().__init__(name, config, 'url', 'shell_args', extra_path_during_install='')
-
- def __str__(self):
- return f'shell file installer from {self.config("url")}'
-
- def install(self, to_dir):
- os.makedirs(to_dir)
- url = self.config('url')
- with NamedTemporaryFile(delete=False) as temp_file:
- download_to_file_obj(temp_file, url)
- temp_file.close()
- env = os.environ.copy()
- extra_path_during_install = self.config('extra_path_during_install')
- if extra_path_during_install:
- extra_path_during_install = os.path.join(to_dir, extra_path_during_install)
- env['PATH'] = f"{extra_path_during_install}:{env['PATH']}"
- env['INSTALL_DIR'] = to_dir
- check_call(["/bin/bash", temp_file.name] + self.config("shell_args"), env=env)
- os.unlink(temp_file.name)
diff --git a/ozy/installers/tar.py b/ozy/installers/tar.py
deleted file mode 100644
index f0c10ad..0000000
--- a/ozy/installers/tar.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import os
-import tarfile
-from tempfile import NamedTemporaryFile
-
-from ozy.installer import Installer
-from ozy.utils import download_to_file_obj
-
-
-class TarballInstaller(Installer):
- def __init__(self, name, config):
- super().__init__(name, config, 'url')
-
- def __str__(self):
- return f'tar installer from {self.config("url")}'
-
- def install(self, to_dir):
- os.makedirs(to_dir)
- url = self.config('url')
- with NamedTemporaryFile() as temp_file:
- download_to_file_obj(temp_file, url)
- temp_file.flush()
- tf = tarfile.open(temp_file.name, 'r')
- tf.extractall(to_dir)
diff --git a/ozy/installers/zip.py b/ozy/installers/zip.py
deleted file mode 100644
index 6bb6dca..0000000
--- a/ozy/installers/zip.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import os
-from tempfile import NamedTemporaryFile
-from zipfile import ZipFile
-
-from ozy import OzyError
-# TODO support sha256, sha256_signature and sha256_gpg_key
-from ozy.installer import Installer
-from ozy.utils import download_to_file_obj
-
-
-class SingleBinaryZipInstaller(Installer):
- def __init__(self, name, config):
- super().__init__(name, config, 'url', app_name=name)
-
- def __str__(self):
- return f'single_binary_zip installer from {self.config("url")}'
-
- def install(self, to_dir):
- app_name = self.config('app_name')
- url = self.config('url')
- os.makedirs(to_dir)
- app_path = os.path.join(to_dir, app_name)
- with NamedTemporaryFile() as temp_file:
- download_to_file_obj(temp_file, url)
- temp_file.flush()
- zf = ZipFile(temp_file.name)
- contents = zf.namelist()
- if len(contents) != 1:
- raise OzyError(f"More than one file in the zipfile at {url}! ({contents})")
- with open(app_path, 'wb') as out_file:
- with zf.open(contents[0]) as in_file:
- out_file.write(in_file.read())
- os.chmod(app_path, 0o774)
-
-
-class ZipInstaller(Installer):
- def __init__(self, name, config):
- super().__init__(name, config, 'url')
-
- def __str__(self):
- return f'zip installer from {self.config("url")}'
-
- def install(self, to_dir):
- os.makedirs(to_dir)
- url = self.config('url')
- with NamedTemporaryFile() as temp_file:
- download_to_file_obj(temp_file, url)
- temp_file.flush()
- zf = ZipFile(temp_file.name)
- zf.extractall(to_dir)
-
- os.chmod(os.path.join(to_dir, self._executable_path), 0o755)
diff --git a/ozy/utils.py b/ozy/utils.py
deleted file mode 100644
index b19d82b..0000000
--- a/ozy/utils.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import logging
-import os
-from typing import BinaryIO
-
-import requests
-from tqdm import tqdm
-
-from ozy import OzyError
-
-_LOGGER = logging.getLogger(__name__)
-_DOWNLOAD_CHUNK_SIZE = 128 * 1024
-
-
-def download_to_file_obj(dest_file_obj: BinaryIO, url: str):
- response = requests.get(url, stream=True)
- if not response.ok:
- raise OzyError(f"Unable to fetch url '{url}' - {response}")
- total_size = int(response.headers.get('content-length', 0))
- with tqdm(total=total_size, unit='iB', unit_scale=True) as t:
- for data in response.iter_content(_DOWNLOAD_CHUNK_SIZE):
- t.update(len(data))
- dest_file_obj.write(data)
-
-
-def download_to(dest_file_name: str, url: str):
- _LOGGER.debug("Downloading %s to %s", url, dest_file_name)
- dest_file_temp = dest_file_name + ".tmp"
- try:
- with open(dest_file_temp, 'wb') as dest_file_obj:
- download_to_file_obj(dest_file_obj, url)
- os.rename(dest_file_temp, dest_file_name)
- except Exception:
- os.unlink(dest_file_temp)
- raise
-
-
-def restore_overridden_env_vars(environment):
- environment = environment.copy()
- for possibly_overridden in ['PYTHONPATH', 'LD_LIBRARY_PATH']:
- orig = possibly_overridden + '_ORIG'
- if orig in environment:
- environment[possibly_overridden] = environment[orig]
- del environment[orig]
- elif possibly_overridden in environment:
- del environment[possibly_overridden]
- return environment
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 853bd36..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-pytest
-pytest_watch
-
-click
-coloredlogs
-packaging
-pyinstaller
-pyyaml
-requests
-tqdm
diff --git a/rozy/.gitignore b/rozy/.gitignore
deleted file mode 100644
index 54f9ea1..0000000
--- a/rozy/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/target
-/test_resource/integration/.*
diff --git a/rozy/.idea/modules.xml b/rozy/.idea/modules.xml
deleted file mode 100644
index 4e398b4..0000000
--- a/rozy/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/rozy/.idea/rozy.iml b/rozy/.idea/rozy.iml
deleted file mode 100644
index c254557..0000000
--- a/rozy/.idea/rozy.iml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/rozy/.idea/vcs.xml b/rozy/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/rozy/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/rozy/README.md b/rozy/README.md
deleted file mode 100644
index 19e4d67..0000000
--- a/rozy/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a Rust implementation of Ozy.
-
-## Instructions
-1. Install [`rustup`](https://rustup.rs/)
-2. Run `install.sh`
-4. Prepend `$HOME/.ozy/bin` to your path to have access to managed apps
-
-Performance is really only a consideration on the common case of running a ozy-managed binary. This is why we prefer not to introduce asynchrony or even template caching in the `install-all` path.
-
-## Currently implemented commands
-* `ozy clean` deletes managed directories
-* `ozy init` sets up app symlinks from a base config from a provided URL
-* `ozy update` accepts an optional URL, but will use the init-configured one without one
-* `ozy install [app name]` installs a single app
-* `ozy install-all` installs them all
-* `ozy run [app name]` will run that app. Typically, you'll just run `[app name]` in the shell directly
-* `ozy makefile-config` output `Makefile` compatible configuration for a list of apps
diff --git a/rozy/src/app.rs b/src/app.rs
similarity index 100%
rename from rozy/src/app.rs
rename to src/app.rs
diff --git a/rozy/src/config.rs b/src/config.rs
similarity index 100%
rename from rozy/src/config.rs
rename to src/config.rs
diff --git a/rozy/src/files.rs b/src/files.rs
similarity index 100%
rename from rozy/src/files.rs
rename to src/files.rs
diff --git a/rozy/src/installers/conda.rs b/src/installers/conda.rs
similarity index 100%
rename from rozy/src/installers/conda.rs
rename to src/installers/conda.rs
diff --git a/rozy/src/installers/file.rs b/src/installers/file.rs
similarity index 100%
rename from rozy/src/installers/file.rs
rename to src/installers/file.rs
diff --git a/rozy/src/installers/installer.rs b/src/installers/installer.rs
similarity index 100%
rename from rozy/src/installers/installer.rs
rename to src/installers/installer.rs
diff --git a/rozy/src/installers/mod.rs b/src/installers/mod.rs
similarity index 100%
rename from rozy/src/installers/mod.rs
rename to src/installers/mod.rs
diff --git a/rozy/src/installers/pip.rs b/src/installers/pip.rs
similarity index 100%
rename from rozy/src/installers/pip.rs
rename to src/installers/pip.rs
diff --git a/rozy/src/installers/shell.rs b/src/installers/shell.rs
similarity index 100%
rename from rozy/src/installers/shell.rs
rename to src/installers/shell.rs
diff --git a/rozy/src/installers/single_binary_zip.rs b/src/installers/single_binary_zip.rs
similarity index 100%
rename from rozy/src/installers/single_binary_zip.rs
rename to src/installers/single_binary_zip.rs
diff --git a/rozy/src/installers/symlink.rs b/src/installers/symlink.rs
similarity index 100%
rename from rozy/src/installers/symlink.rs
rename to src/installers/symlink.rs
diff --git a/rozy/src/installers/tarball.rs b/src/installers/tarball.rs
similarity index 100%
rename from rozy/src/installers/tarball.rs
rename to src/installers/tarball.rs
diff --git a/rozy/src/installers/zip.rs b/src/installers/zip.rs
similarity index 100%
rename from rozy/src/installers/zip.rs
rename to src/installers/zip.rs
diff --git a/rozy/src/main.rs b/src/main.rs
similarity index 100%
rename from rozy/src/main.rs
rename to src/main.rs
diff --git a/rozy/src/utils.rs b/src/utils.rs
similarity index 100%
rename from rozy/src/utils.rs
rename to src/utils.rs
diff --git a/test/__init__.py b/test/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/test/smoke/__init__.py b/test/smoke/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/test/smoke/test_target_config.py b/test/smoke/test_target_config.py
deleted file mode 100644
index 77a3184..0000000
--- a/test/smoke/test_target_config.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import requests
-
-
-def is_valid_int(s):
- try:
- int(s)
- return True
- except ValueError:
- return False
-
-
-def test_can_validate_a_hashicorp_installer():
- config = {
- "version-string": "0.9.4",
- "download-url": "https://releases.hashicorp.com/nomad/0.9.4/nomad_0.9.4_linux_amd64.zip",
- "download-sha256sum-url": "https://releases.hashicorp.com/nomad/0.9.4/nomad_0.9.4_SHA256SUMS"
- }
-
- response = requests.get(config['download-url'], stream=True)
-
- assert response.status_code == 200
-
- headers = response.headers
- content_length = headers['Content-length']
- assert is_valid_int(content_length)
- content_length = int(content_length)
- assert content_length > 0
- print(content_length)
diff --git a/test/unit/__init__.py b/test/unit/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/test/unit/installers/__init__.py b/test/unit/installers/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/test/unit/installers/test_conda.py b/test/unit/installers/test_conda.py
deleted file mode 100644
index e43bfaa..0000000
--- a/test/unit/installers/test_conda.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import os
-
-from tempfile import TemporaryDirectory
-from unittest import mock
-
-from ozy.installers import CondaInstaller
-
-
-@mock.patch('ozy.installers.conda.check_call')
-def test_should_install_with_regular_installer(mock_check_call):
- with TemporaryDirectory() as root:
- installer = CondaInstaller('test', dict(package='package', version='1.0.0', channels=['chan1', 'chan2']))
- installer.install(root + '/some/directory')
- assert os.path.isdir(root + '/some')
- mock_check_call.assert_called_with([
- 'conda', 'create', '-y',
- '-c', 'chan1',
- '-c', 'chan2',
- '-p', root + '/some/directory',
- 'package=1.0.0'
- ], env=mock.ANY)
-
-@mock.patch('ozy.installers.conda.do_conda_install')
-@mock.patch('ozy.installers.conda.check_call')
-def test_should_install_with_pyinstaller_squish(mock_check_call, mock_do_conda_install):
- installer = CondaInstaller('test',
- dict(package='package', version='1.0.0', pyinstaller=True, channels=['chan1', 'chan2']))
- installer.install('/some/directory')
- mock_do_conda_install.assert_called_with('conda', ['chan1', 'chan2'], mock.ANY, ['package=1.0.0', 'pyinstaller'])
- mock_check_call.assert_called_with([
- mock.ANY, # the unknown temporary file reference to pyinstaller
- '--onefile', '--name', 'test', '--distpath', '/some/directory',
- mock.ANY # the unknown temporary file reference to the test installation
- ])
diff --git a/test/unit/installers/test_pip.py b/test/unit/installers/test_pip.py
deleted file mode 100644
index 03e1558..0000000
--- a/test/unit/installers/test_pip.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest import mock
-
-import pytest
-
-from ozy import OzyError
-from ozy.installers.pip import PipInstaller
-
-
-def test_raises_on_missing_keys():
- with pytest.raises(OzyError):
- PipInstaller('test', dict())
- with pytest.raises(OzyError):
- PipInstaller('test', dict(package='p'))
- with pytest.raises(OzyError):
- PipInstaller('test', dict(version='123'))
-
-
-def test_constructs_ok_with_correct_config():
- PipInstaller('test', dict(package='p', version='123'))
-
-
-@mock.patch('ozy.installers.pip.do_conda_install')
-@mock.patch('ozy.installers.pip.check_call')
-def test_installs(mock_check_call, mock_do_conda_install):
- installer = PipInstaller('test', dict(package='package', version='1.0.0'))
- installer.install('/some/directory')
- mock_do_conda_install.assert_called_with('conda', [], '/some/directory', ['pip'])
- mock_check_call.assert_has_calls([
- mock.call(['/some/directory/bin/pip', 'install', 'package==1.0.0'])
- ])
diff --git a/test/unit/test_app.py b/test/unit/test_app.py
deleted file mode 100644
index e53ac9f..0000000
--- a/test/unit/test_app.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import pytest
-
-from ozy import OzyError
-from ozy.app import App, ensure_keys, fixup_post_install
-from ozy.config import parse_ozy_conf, resolve
-
-
-def test_app():
- sample_config_file = "conf/sample-team-conf.yaml"
- ozy_conf = parse_ozy_conf(sample_config_file)
- app = App("nomad", ozy_conf)
- assert app.name == "nomad"
- assert app.config['template'] == 'hashicorp'
- assert app.config['url']
- assert app.config['type'] == 'single_binary_zip'
- assert app.config['sha256_gpg_key'] == ozy_conf['templates']['hashicorp']['sha256_gpg_key']
- assert app.config['sha256']
- assert app.config['sha256_signature']
-
-
-def test_unsupported_installer():
- sample_config_file = "conf/sample-team-conf.yaml"
- ozy_conf = parse_ozy_conf(sample_config_file)
- ozy_conf['templates']['hashicorp']['type'] = 'triple_binary_star' # not a supported installer
- with pytest.raises(OzyError):
- App("nomad", ozy_conf)
-
-
-def test_ensure_keys():
- sample_config_file = "conf/sample-team-conf.yaml"
- ozy_conf = parse_ozy_conf(sample_config_file)
-
- tmp_ozy_conf = ozy_conf.copy()
- del tmp_ozy_conf['templates']['hashicorp']['type']
- config = resolve(ozy_conf['apps']['nomad'], ozy_conf['templates'])
- assert config
- with pytest.raises(OzyError):
- ensure_keys('nomad', config, 'type')
-
-
-def test_fixup_post_install():
- assert fixup_post_install("") == []
- assert fixup_post_install("this is a test") == [["this", "is", "a", "test"]]
- assert fixup_post_install(["this is a test"]) == [["this", "is", "a", "test"]]
- assert fixup_post_install(["this is a test", "two"]) == [["this", "is", "a", "test"], ["two"]]
- assert fixup_post_install([["one", "two"], ["three", "four"]]) == [["one", "two"], ["three", "four"]]
- assert fixup_post_install("this is 'a test'") == [["this", "is", "a test"]]
diff --git a/test/unit/test_config.py b/test/unit/test_config.py
deleted file mode 100644
index ed84ce0..0000000
--- a/test/unit/test_config.py
+++ /dev/null
@@ -1,132 +0,0 @@
-import os
-from shutil import copyfile
-from tempfile import TemporaryDirectory
-
-import pytest
-import yaml
-
-from ozy import OzyError
-from ozy.config import safe_expand, parse_ozy_conf, resolve, apply_overrides, get_user_conf_file, load_config, get_system_variables
-
-
-def test_parse_ozy_conf():
- sample_config_file = "conf/sample-team-conf.yaml"
- ozy_conf = parse_ozy_conf(sample_config_file)
- assert ozy_conf is not None
- assert 'name' in ozy_conf
- assert 'ozy_version' in ozy_conf
- assert 'templates' in ozy_conf
- assert 'apps' in ozy_conf
-
- templates = ozy_conf['templates']
- assert 'hashicorp' in templates
-
-
-def test_safe_expand():
- sample_config = {
- "terraform": {
- "version": "0.12.10",
- "url": "https://releases.hashicorp.com/terraform/{version}/terraform_{version}_{hashicorp_os}_amd64.zip",
- "list": ['foo', '{version}', 'bar']
- },
- }
-
- tool_info = sample_config['terraform']
- expanded_config = safe_expand(dict(version="0.12.10", hashicorp_os="linux"), tool_info['url'])
- assert expanded_config == "https://releases.hashicorp.com/terraform/0.12.10/terraform_0.12.10_linux_amd64.zip"
- expanded_config = safe_expand(dict(version="0.12.10", hashicorp_os="linux"), tool_info['list'])
- assert expanded_config == ['foo', '0.12.10', 'bar']
-
-
-def test_bad_safe_expand():
- with pytest.raises(OzyError):
- safe_expand(dict(foo="bar"), "I am templated {baz}")
-
-
-def test_resolve():
- sample_config_file = "conf/sample-team-conf.yaml"
- ozy_conf = parse_ozy_conf(sample_config_file)
- templates = ozy_conf['templates']
- config = ozy_conf['apps']['nomad']
-
- with pytest.raises(OzyError):
- bad_templates = templates.copy()
- del bad_templates['hashicorp']
- resolve(config, bad_templates)
-
- # with a version check turned on, the below 3 LOC will verify that flipping config and template is caught.
- # try intentionally switching them
- # with pytest.raises(OzyError):
- # resolve(config=templates, templates=config)
-
- good_resolved = resolve(config, templates)
- assert 'url' in good_resolved
- assert 'template' in good_resolved
- assert 'type' in good_resolved
- assert 'app_name' in good_resolved
- assert 'version' in good_resolved
- assert 'sha256' in good_resolved
- assert 'sha256_gpg_key' in good_resolved
-
-
-def test_apply_overrides():
- # test the flat case
- source = {'baz': 'super-rab'}
- dest = {'baz': 'rab'}
- result = apply_overrides(source, dest)
- assert result['baz'] == 'super-rab'
-
- # test the nested dict case
- source = {'foo': {'bar': 'super-baz'}}
- dest = {'foo': {'bar': 'baz'}}
- result = apply_overrides(source, dest)
- assert result['foo']['bar'] == 'super-baz'
-
-
-def save_temp_ozy_conf(config, target_path):
- with open(target_path, "w") as fobj:
- yaml.dump(config, fobj)
-
-
-def test_load_config():
- with TemporaryDirectory() as td:
- subdirA = os.path.join(td, "A")
- subdirA1 = os.path.join(subdirA, "1")
-
- os.mkdir(subdirA)
- os.mkdir(subdirA1)
-
- sample_config = 'conf/sample-team-conf.yaml'
- subdirA_config = parse_ozy_conf(sample_config)
- subdirA_config['apps']['nomad']['version'] = '10.10.10.10'
- save_temp_ozy_conf(subdirA_config, os.path.join(subdirA, ".ozy.yaml"))
-
- loaded_config = load_config(config=parse_ozy_conf(sample_config),
- current_working_directory=subdirA)
-
- assert loaded_config['apps']['nomad']['version'] == '10.10.10.10'
-
- # lets try with three dirs!
- subdirA1_config = parse_ozy_conf(sample_config)
- subdirA1_config['apps']['nomad']['version'] = '11.11.11.11'
- save_temp_ozy_conf(subdirA1_config, os.path.join(subdirA1, ".ozy.yaml"))
-
- loaded_config = load_config(config=parse_ozy_conf(sample_config),
- current_working_directory=subdirA1)
-
- assert loaded_config['apps']['nomad']['version'] == '11.11.11.11'
-
-
-def test_get_user_conf_file():
- ucf = get_user_conf_file()
- assert ucf
-
-
-def test_system_variables():
- system_variables = get_system_variables()
- assert 'ozy_os' in system_variables
- assert 'ozy_machine' in system_variables
- assert 'ozy_arch' in system_variables
-
- expanded_config = safe_expand(dict(), "{ozy_os} {ozy_machine} {ozy_arch}")
- assert expanded_config == f"{os.uname().sysname.lower()} {os.uname().machine} {'amd64' if os.uname().machine == 'x86_64' else os.uname().machine}"
diff --git a/test/unit/test_installer.py b/test/unit/test_installer.py
deleted file mode 100644
index 2e1713e..0000000
--- a/test/unit/test_installer.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import pytest
-
-from ozy import OzyError
-from ozy.installer import Installer
-
-
-def test_empty_dict_is_ok():
- Installer('test', dict())
-
-
-def test_drops_non_specified_keys():
- installer = Installer('test', dict(not_mentioned='123'))
- with pytest.raises(KeyError):
- installer.config('not_mentioned')
-
-
-def test_accepts_non_required_keys():
- installer = Installer('test', dict(optional_key='opt'), optional_key='some default')
- assert installer.config('optional_key') == 'opt'
-
-
-def test_defaults_non_required_keys():
- installer = Installer('test', dict(), optional_key='some default')
- assert installer.config('optional_key') == 'some default'
-
-
-def test_accepts_required_keys():
- installer = Installer('test', dict(version='1.2.3'), 'version')
- assert installer.config('version') == '1.2.3'
-
-
-def test_throws_on_missing_required_keys():
- with pytest.raises(OzyError, match='Missing required key.*not_there.*in.*test.*'):
- Installer('test', dict(), 'not_there')
diff --git a/test/unit/test_ozy.py b/test/unit/test_ozy.py
deleted file mode 100644
index 9f50750..0000000
--- a/test/unit/test_ozy.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import os
-
-import pytest
-
-from ozy import OzyError
-from ozy.files import walk_up_dirs, get_ozy_dir
-
-
-def test_ozy_dirs():
- ozy_dir = get_ozy_dir()
- assert ozy_dir is not None
- home = os.environ['HOME']
- del os.environ['HOME']
- with pytest.raises(OzyError):
- get_ozy_dir()
- os.environ['HOME'] = home
-
-
-def test_walk_up_dirs():
- test_path = os.path.join(os.path.sep, 'one', 'two', 'three')
- assert [
- os.path.join(os.path.sep, 'one', 'two', 'three'),
- os.path.join(os.path.sep, 'one', 'two'),
- os.path.join(os.path.sep, 'one'),
- os.path.sep
- ] == [x for x in walk_up_dirs(test_path)]
diff --git a/test/unit/test_utils.py b/test/unit/test_utils.py
deleted file mode 100644
index d4b8d9e..0000000
--- a/test/unit/test_utils.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from ozy.utils import restore_overridden_env_vars
-
-
-def test_restore_original_env_vars_keeps_existing():
- orig = dict(a='a', b='b', c='c')
- assert restore_overridden_env_vars(orig) == orig
-
-
-def test_restore_original_env_vars_restores_pythonpath():
- orig = dict(PYTHONPATH='new', PYTHONPATH_ORIG='orig')
- assert restore_overridden_env_vars(orig) == dict(PYTHONPATH='orig')
-
-
-def test_restore_original_env_vars_restores_ld_library_path():
- orig = dict(LD_LIBRARY_PATH='new', LD_LIBRARY_PATH_ORIG='orig')
- assert restore_overridden_env_vars(orig) == dict(LD_LIBRARY_PATH='orig')
-
-
-def test_restore_original_env_vars_unsets_pythonpath_and_ld_path_if_not_originally_set():
- orig = dict(PYTHONPATH='new', LD_LIBRARY_PATH='new')
- assert restore_overridden_env_vars(orig) == dict()
diff --git a/rozy/test_resource/unittest.ozy.yaml b/test_resource/unittest.ozy.yaml
similarity index 100%
rename from rozy/test_resource/unittest.ozy.yaml
rename to test_resource/unittest.ozy.yaml