Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved manifest #11

Merged
merged 39 commits into from
Jun 13, 2020
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7a94ecd
Getting started on the improved manifest.
haz May 24, 2020
8a197d6
Initial draft of a package template.
haz May 25, 2020
5320d8a
Finished the initial config for the new package format.
haz May 26, 2020
284e0a5
Added some generic planutils directory variable.
haz May 27, 2020
00c015c
Updating the fast-downward shortcode.
haz May 27, 2020
ba451db
Redirect for the run scripts.
haz May 27, 2020
6527346
Changing image extension and simplifying singularity example.
haz May 27, 2020
4f61025
Renaming fd -> downward
haz May 27, 2020
26b6331
Minor bug.
haz May 27, 2020
b3efa88
Shift to packages.
haz May 28, 2020
bfa7c70
Manifest fix.
haz May 28, 2020
f996c1f
Some bug fixes and filename change.
haz May 28, 2020
4ddefc0
Put in the settings, and recording what is installed.
haz May 29, 2020
9b87f4c
Version bump and docker fix.
haz May 30, 2020
ac44d53
Added uninstall functionality.
haz May 30, 2020
ac84c2e
Improved parsing with subcommands.
haz Jun 4, 2020
03fe6de
Improved redirect script for installable packages.
haz Jun 4, 2020
b1b82e7
Small bug fix.
haz Jun 4, 2020
46dc0e5
Only create new command-line utilities for runnable packages.
haz Jun 4, 2020
c8b367b
Improved handling of the dependency mapping.
haz Jun 4, 2020
f041490
Making lama lama
haz Jun 4, 2020
a0b4509
Cleanup.
haz Jun 4, 2020
58115c5
Confirm installation of all (recursively computed) dependencies.
haz Jun 5, 2020
6a15d1d
Rollback failed installation process.
haz Jun 5, 2020
3890887
Collect & delete (after confirmation) all of the dependencies requested
haz Jun 5, 2020
c676dcb
Minor output touchup and allow for setup to be forced.
haz Jun 5, 2020
1595111
Fixing bug with main installation call.
haz Jun 5, 2020
a930f8e
Adding upgrade functionality.
haz Jun 5, 2020
c40829b
Improved display of installed/available packages.
haz Jun 5, 2020
c629d07
Better return codes and function naming.
haz Jun 6, 2020
54a6f27
Fixing bug with bash scripts.
haz Jun 6, 2020
6533802
Simplify the installation check using return codes.
haz Jun 6, 2020
eb5bd2a
Allow for installing/uninstalling multiple packages.
haz Jun 6, 2020
87f659d
Make sure packages are set up properly.
haz Jun 6, 2020
be1c2b9
Requiring an estimated size, and outputting a predicted size on install
haz Jun 6, 2020
ffb00f9
Refactored the size config name.
haz Jun 6, 2020
660260f
Minor fixes to the default behaviour of uninstalled package scripts.
haz Jun 7, 2020
6f90676
Optionally iterate through packages that may be no longer required.
haz Jun 7, 2020
150b7e3
Fixing a couple of bugs.
haz Jun 13, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/*

RUN pip3 install --upgrade pip
RUN pip3 install setuptools

# Install & setup the planutils
RUN pip3 install planutils --trusted-host pypi.org --trusted-host files.pythonhosted.org
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include planutils/planner_configs/*.json
recursive-include planutils/packages *
127 changes: 94 additions & 33 deletions planutils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,119 @@

import argparse, os

from planutils import settings
from planutils.package_installation import PACKAGES


def setup():

assert not_setup_yet(), "Error: planutils is already setup. Remove ~/.planutils to reset (warning: all cached planners will be lost)."
if not not_setup_yet():
haz marked this conversation as resolved.
Show resolved Hide resolved
print("\nError: planutils is already setup. Setting up again will wipe all cached packages and settings.")
if input(" Proceed? [y/N] ").lower() in ['y', 'yes']:
os.system("rm -rf %s" % os.path.join(os.path.expanduser('~'), '.planutils'))
else:
return

CUR_DIR = os.path.dirname(os.path.abspath(__file__))

print("\nCreating ~/.planutils...")
os.mkdir(os.path.join(os.path.expanduser('~'), '.planutils'))
os.mkdir(os.path.join(os.path.expanduser('~'), '.planutils', 'bin'))
os.mkdir(os.path.join(os.path.expanduser('~'), '.planutils', 'bin', 'images'))

settings.save({
'installed': []
})

os.symlink(os.path.join(CUR_DIR, 'packages'),
os.path.join(os.path.expanduser('~'), '.planutils', 'packages'))

print("Adding bin folder to path (assuming ~/.bashrc exists)...")
os.system('echo \'export PATH="$HOME/.planutils/bin:$PATH"\' >> ~/.bashrc')

print("Installing planner scripts...")
from planutils.planner_installation import PLANNERS
for p in PLANNERS:
script = "#!/bin/bash\n"
script += "echo\n"
script += "echo 'Planner not installed!'\n"
script += "read -p \"Download & install? [y/n] \" varchoice\n"
script += "if [ $varchoice == \"y\" ]\n"
script += "then\n"
script += " planutils --install " + p + "\n"
script += "fi\n"
script += "echo"
with open(os.path.join(os.path.expanduser('~'), '.planutils', 'bin', p), 'w') as f:
f.write(script)
os.chmod(os.path.join(os.path.expanduser('~'), '.planutils', 'bin', p), 0o0755)
os.system("echo 'export PLANUTILS_PREFIX=\"~/.planutils\"' >> ~/.bashrc")
os.system("echo 'export PATH=\"$PLANUTILS_PREFIX/bin:$PATH\"' >> ~/.bashrc")
haz marked this conversation as resolved.
Show resolved Hide resolved

print("Installing package scripts...")
for p in PACKAGES:
if PACKAGES[p]['runnable']:
script = "#!/bin/bash\n"
script += "if [ \"$(planutils check-installed %s)\" == \"True\" ]\n" % p
haz marked this conversation as resolved.
Show resolved Hide resolved
script += "then\n"
script += " ~/.planutils/packages/%s/run $@\n" % p
script += "else\n"
script += " echo\n"
script += " echo 'Package not installed!'\n"
script += " read -r -p \" Download & install? [y/N] \" varchoice\n"
script += " varchoice=${varchoice,,}\n" # tolower
haz marked this conversation as resolved.
Show resolved Hide resolved
script += " if [[ \"$varchoice\" =~ ^(yes|y)$ ]]\n"
haz marked this conversation as resolved.
Show resolved Hide resolved
script += " then\n"
script += " planutils install " + p + "\n"
script += " if [ \"$(planutils check-installed %s)\" == \"True\" ]\n" % p
haz marked this conversation as resolved.
Show resolved Hide resolved
script += " then\n"
script += " echo 'Successfully installed %s!'\n" % p
script += " echo \"Original command: %s $@\"\n" % p
script += " read -r -p \" Re-run command? [Y/n] \" varchoice\n"
script += " varchoice=${varchoice,,}\n" # tolower
script += " if ! [[ \"$varchoice\" =~ ^(no|n)$ ]]\n"
script += " then\n"
script += " ~/.planutils/packages/%s/run $@\n" % p
script += " fi\n"
script += " fi\n"
script += " fi\n"
script += " echo\n"
script += "fi\n"
with open(os.path.join(os.path.expanduser('~'), '.planutils', 'bin', p), 'w') as f:
haz marked this conversation as resolved.
Show resolved Hide resolved
f.write(script)
os.chmod(os.path.join(os.path.expanduser('~'), '.planutils', 'bin', p), 0o0755)


print("\nAll set! Be sure to start a new bash session or update your PATH variable to include ~/.planutils/bin\n")

def not_setup_yet():
return not os.path.exists(os.path.join(os.path.expanduser('~'), '.planutils'))


def main():
parser = argparse.ArgumentParser()

parser.add_argument("-i", "--install",
help="install an individual or collection of planners ('list' shows the options)",
metavar="{planner or collection or list}")

parser.add_argument("-s", "--setup", help="setup planutils for current user", action="store_true")

parser = argparse.ArgumentParser(prog="planutils")
subparsers = parser.add_subparsers(help='sub-command help', dest='command')

parser_install = subparsers.add_parser('install', help='install an individual package such as a planner')
haz marked this conversation as resolved.
Show resolved Hide resolved
parser_install.add_argument('package', help='package name')

parser_uninstall = subparsers.add_parser('uninstall', help='uninstall an individual package')
parser_uninstall.add_argument('package', help='package name')

parser_checkinstalled = subparsers.add_parser('check-installed', help='check if a package is installed')
parser_checkinstalled.add_argument('package', help='package name')

parser_list = subparsers.add_parser('list', help='list the available packages')
parser_setup = subparsers.add_parser('setup', help='setup planutils for current user')
parser_upgrade = subparsers.add_parser('upgrade', help='upgrade all of the installed packages')

args = parser.parse_args()

if args.setup:
if 'setup' == args.command:
setup()
elif not_setup_yet():
print("\nPlease run 'planutils --setup' before using utility.\n")
exit()
print("\nPlease run 'planutils setup' before using utility.\n")

elif 'check-installed' == args.command:
from planutils.package_installation import check_installed
print(check_installed(args.package))

elif 'install' == args.command:
from planutils.package_installation import install
install(args.package)

elif 'uninstall' == args.command:
from planutils.package_installation import uninstall
uninstall(args.package)

elif 'list' == args.command:
from planutils.package_installation import package_list
package_list()

elif 'upgrade' == args.command:
from planutils.package_installation import upgrade
upgrade()

if args.install:
from planutils.planner_installation import install
install(args.install)
else:
parser.print_help()
118 changes: 118 additions & 0 deletions planutils/package_installation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

import json, os, glob, subprocess
from collections import defaultdict

from planutils import settings

PACKAGES = {}

CUR_DIR = os.path.dirname(os.path.abspath(__file__))

for conf_file in glob.glob(os.path.join(CUR_DIR, 'packages', '*')):
base = os.path.basename(conf_file)
if base not in ['README.md', 'TEMPLATE']:
with open(os.path.join(conf_file, 'manifest.json'), 'r') as f:
FlorianPommerening marked this conversation as resolved.
Show resolved Hide resolved
config = json.load(f)
assert base not in PACKAGES, "Error: Duplicate package config -- %s" % base
PACKAGES[base] = config
PACKAGES[base]['runnable'] = os.path.exists(os.path.join(conf_file, 'run'))


def check_installed(target):
return target in settings.load()['installed']


def uninstall(target):

if target not in PACKAGES:
print("Error: Package not found -- %s" % target)
return

if not check_installed(target):
print("%s isn't installed." % target)
else:
s = settings.load()
# map a package to all those that depend on it
dependency_mapping = defaultdict(set)
for p in s['installed']:
for dep in PACKAGES[p]['dependencies']:
dependency_mapping[dep].add(p)

# compute all the packages that will be removed
to_check = [target]
to_remove = set()
while to_check:
check = to_check.pop(0)
to_remove.add(check)
to_check.extend(list(dependency_mapping[check]))

print("\nAbout to remove the following packages: %s" % ', '.join(to_remove))
if input(" Proceed? [y/N] ").lower() in ['y', 'yes']:
for package in to_remove:
print ("Uninstalling %s..." % package)
subprocess.call('./uninstall', cwd=os.path.join(CUR_DIR, 'packages', package))
haz marked this conversation as resolved.
Show resolved Hide resolved
s['installed'].remove(package)
settings.save(s)

def package_list():
print("\nInstalled:")
installed = set(settings.load()['installed'])
for p in installed:
print(" %s: %s" % (p, PACKAGES[p]['name']))

print("\nAvailable:")
for p in PACKAGES:
if p not in installed:
print(" %s: %s" % (p, PACKAGES[p]['name']))
print()

def upgrade():
s = settings.load()
for package in s['installed']:
haz marked this conversation as resolved.
Show resolved Hide resolved
print("Upgrading %s..." % package)
subprocess.call('./uninstall', cwd=os.path.join(CUR_DIR, 'packages', package))
subprocess.call('./install', cwd=os.path.join(CUR_DIR, 'packages', package))

def install(target):

if target not in PACKAGES:
print("Error: Package not found -- %s" % target)
return

# Compute all those that will need to be installed
done = set()
to_check = [target]
to_install = []
while to_check:
check = to_check.pop(0)
if check not in done:
done.add(check)
if not check_installed(check):
to_install.append(check)
to_check.extend(PACKAGES[check]['dependencies'])

to_install.reverse()

if to_install:
print("\nAbout to install the following packages: %s" % ', '.join(to_install))
if input(" Proceed? [Y/n] ").lower() in ['', 'y', 'yes']:
installed = []
for package in to_install:
print("Installing %s..." % package)
try:
installed.append(package)
subprocess.check_call('./install', cwd=os.path.join(CUR_DIR, 'packages', package))
except subprocess.CalledProcessError:
print("Error installing %s. Rolling back changes..." % package)
for p in installed:
subprocess.call('./uninstall', cwd=os.path.join(CUR_DIR, 'packages', p))
return

s = settings.load()
s['installed'].extend(installed)
settings.save(s)
else:
print("Aborting installation.")
else:
print("%s is already installed." % target)

24 changes: 24 additions & 0 deletions planutils/packages/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# planutils packages

Each package must contain the files `manifest.json`, `install`, and `uninstall`. Optionally, `run` can be included if the package installs a command-line executable (e.g., planners). An example of all four files can be found in the `TEMPLATE` directory, and a description of each follows.

## manifest.json

Details on the package. Must include:

1. **name**: Long-form name of the package.
2. **shortname**: Short-form name that will be used for installation, running, etc.
3. **description**: General description of the package.
4. **dependencies**: List of shortnames for other `planutils` packages that are required.

## install

Script to install the package along with any dependencies not part of `planutils`.

## uninstall

Cleanup script to remove the package installation.

## run

(optional) Script to run the installed package. `shortname` specified in the `manifest.json` file will be used for the command-line invocation of this script.
17 changes: 17 additions & 0 deletions planutils/packages/TEMPLATE/install
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

# No need to install planutils dependencies
# No need to check if already installed
# The install script will be run from the package's directory

# To use if root is required
#[ "$UID" -eq 0 ] || (echo "installation requires root access"; exec sudo "$0" "$@")

# Install general linux dependencies

# General setup / configuration


# Recipe for singularity images
## Fetch the image
#singularity pull --name <image name> <singularity shub url>
5 changes: 5 additions & 0 deletions planutils/packages/TEMPLATE/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Full name of package",
"description": "General description of the package",
"dependencies": ["list", "of", "dependencies"]
}
haz marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions planutils/packages/TEMPLATE/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

# whatever command-line method needs to be used to run this package
14 changes: 14 additions & 0 deletions planutils/packages/TEMPLATE/uninstall
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash

# No need to uninstall planutils dependencies
# The uninstall script will be run from the package's directory

# To use if root is required
#[ "$UID" -eq 0 ] || (echo "installation requires root access"; exec sudo "$0" "$@")

# Only uninstall general linux dependencies in rare circumstances -- if another package requires it, there is risk of breaking their dependency.

# Remove general setup / configuration

# Recipe for singularity images
#rm <image name>
3 changes: 3 additions & 0 deletions planutils/packages/downward/install
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

singularity pull --name downward.sif shub://aibasel/downward
5 changes: 5 additions & 0 deletions planutils/packages/downward/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Fast Downward",
"description": "http://fast-downward.org/",
"dependencies": []
}
3 changes: 3 additions & 0 deletions planutils/packages/downward/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

singularity run $(dirname $0)/downward.sif $@
3 changes: 3 additions & 0 deletions planutils/packages/downward/uninstall
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

rm downward.sif
3 changes: 3 additions & 0 deletions planutils/packages/lama/install
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

# Nothing to do!
5 changes: 5 additions & 0 deletions planutils/packages/lama/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "LAMA",
"description": "http://fast-downward.org/",
"dependencies": ["downward"]
}
3 changes: 3 additions & 0 deletions planutils/packages/lama/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

downward --alias lama $@
3 changes: 3 additions & 0 deletions planutils/packages/lama/uninstall
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

# Nothing to do!
Loading