Add Package.upgrade option
This commit is contained in:
parent
8ab2fbcccd
commit
b044dd015e
51
README.md
51
README.md
@ -187,12 +187,13 @@ class Docker(Target):
|
||||
return await self.parent.exec(*['docker', 'exec', self.name] + args)
|
||||
```
|
||||
|
||||
Don't worry about `self.parent` being set, it is enforced to `Localhost` if
|
||||
unset so that we always have something that actually spawns a process in the
|
||||
chain ;)
|
||||
This also means that you always need a parent with an exec implementation,
|
||||
there are two:
|
||||
|
||||
The result of that design is that the following use cases are open for
|
||||
business:
|
||||
- Localhost, executes on localhost
|
||||
- Stub, for testing
|
||||
|
||||
The result of that design is that the following use cases are available:
|
||||
|
||||
```python
|
||||
# This action installs my favorite package on any distro
|
||||
@ -215,14 +216,48 @@ Ssh(host='yourhost')(build)
|
||||
|
||||
# Or on a server behingh a bastion:
|
||||
# ssh yourbastion ssh yourhost build exec apt install python3
|
||||
Ssh(host='bastion')(Ssh(host='yourhost')(build))
|
||||
Localhost()(Ssh(host='bastion')(Ssh(host='yourhost')(build))
|
||||
|
||||
# That's going to do the same
|
||||
Ssh(
|
||||
Localhost(Ssh(
|
||||
Ssh(
|
||||
build,
|
||||
host='yourhost'
|
||||
),
|
||||
host='bastion'
|
||||
)()
|
||||
))()
|
||||
```
|
||||
|
||||
## CLI
|
||||
|
||||
You should build your CLI with your favorite CLI framework. Nonetheless, shlax
|
||||
provides a ConsoleScript built on cli2 (a personnal experiment, still pre-alpha
|
||||
stage) that will expose any callable you define in a script, for example:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env shlax
|
||||
|
||||
from shlax.shortcuts import *
|
||||
|
||||
webpack = Container(
|
||||
build=Buildah(
|
||||
Packages('npm')
|
||||
)
|
||||
)
|
||||
|
||||
django = Container(
|
||||
build=Buildah(
|
||||
Packages('python')
|
||||
)
|
||||
)
|
||||
|
||||
pod = Pod(
|
||||
django=django,
|
||||
webpack=webpack,
|
||||
)
|
||||
```
|
||||
|
||||
Running this file will output:
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
@ -52,8 +52,9 @@ class Packages:
|
||||
|
||||
installed = []
|
||||
|
||||
def __init__(self, *packages):
|
||||
def __init__(self, *packages, upgrade=True):
|
||||
self.packages = []
|
||||
self.upgrade = upgrade
|
||||
for package in packages:
|
||||
line = dedent(package).strip().replace('\n', ' ')
|
||||
self.packages += line.split(' ')
|
||||
@ -116,7 +117,8 @@ class Packages:
|
||||
|
||||
self.cmds = self.mgrs[self.mgr]
|
||||
await self.update(target)
|
||||
await target.rexec(self.cmds['upgrade'])
|
||||
if self.upgrade:
|
||||
await target.rexec(self.cmds['upgrade'])
|
||||
|
||||
packages = []
|
||||
for package in self.packages:
|
||||
@ -160,4 +162,4 @@ class Packages:
|
||||
return self.cache_root + '/pacman'
|
||||
|
||||
def __repr__(self):
|
||||
return f'Packages({self.packages})'
|
||||
return f'Packages({self.packages}, upgrade={self.upgrade})'
|
||||
|
||||
17
shlax/cli.py
17
shlax/cli.py
@ -5,6 +5,7 @@ Shlax executes mostly in 3 ways:
|
||||
- With the name of a module in shlax.repo: a community maintained shlaxfile
|
||||
"""
|
||||
import ast
|
||||
import asyncio
|
||||
import cli2
|
||||
import glob
|
||||
import inspect
|
||||
@ -49,7 +50,19 @@ class ConsoleScript(cli2.ConsoleScript):
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
for member in members:
|
||||
from shlax.targets.localhost import Localhost
|
||||
self[member] = cli2.Callable(member, Localhost(getattr(mod, member)))
|
||||
subject = getattr(mod, member)
|
||||
if callable(subject):
|
||||
self[member] = cli2.Callable(member, subject)
|
||||
else:
|
||||
importable = cli2.Importable(member, subject)
|
||||
self[member] = cli2.Group(member)
|
||||
for cb in importable.get_callables():
|
||||
self[member][cb.name] = cb
|
||||
|
||||
def call(self, command):
|
||||
if command.name == 'help':
|
||||
return super().call(command)
|
||||
from shlax.targets.localhost import Localhost
|
||||
asyncio.run(Localhost()(command.target))
|
||||
|
||||
cli = ConsoleScript(__doc__)
|
||||
|
||||
@ -25,15 +25,14 @@ class Output:
|
||||
def colorize(self, code, content):
|
||||
return self.color(code) + content + self.color()
|
||||
|
||||
def colorized(self):
|
||||
if hasattr(self.subject, 'colorized'):
|
||||
return self.subject.colorized(self.colors)
|
||||
def colorized(self, action):
|
||||
if hasattr(action, 'colorized'):
|
||||
return action.colorized(self.colors)
|
||||
else:
|
||||
return str(self.subject)
|
||||
return str(action)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
subject=None,
|
||||
prefix=None,
|
||||
regexps=None,
|
||||
debug='cmd,visit,out',
|
||||
@ -41,7 +40,6 @@ class Output:
|
||||
flush=None,
|
||||
**kwargs
|
||||
):
|
||||
self.subject = subject
|
||||
self.prefix = prefix
|
||||
self.debug = debug
|
||||
self.prefix_length = 0
|
||||
@ -114,59 +112,59 @@ class Output:
|
||||
|
||||
return line
|
||||
|
||||
def test(self):
|
||||
def test(self, action):
|
||||
self(''.join([
|
||||
self.colors['purplebold'],
|
||||
'! TEST ',
|
||||
self.colors['reset'],
|
||||
self.colorized(),
|
||||
self.colorized(action),
|
||||
'\n',
|
||||
]))
|
||||
|
||||
def clean(self):
|
||||
def clean(self, action):
|
||||
if self.debug:
|
||||
self(''.join([
|
||||
self.colors['bluebold'],
|
||||
'+ CLEAN ',
|
||||
self.colors['reset'],
|
||||
self.colorized(),
|
||||
self.colorized(action),
|
||||
'\n',
|
||||
]))
|
||||
|
||||
def start(self):
|
||||
def start(self, action):
|
||||
if self.debug is True or 'visit' in str(self.debug):
|
||||
self(''.join([
|
||||
self.colors['orangebold'],
|
||||
'⚠ START ',
|
||||
self.colors['reset'],
|
||||
self.colorized(),
|
||||
self.colorized(action),
|
||||
'\n',
|
||||
]))
|
||||
|
||||
def success(self):
|
||||
def success(self, action):
|
||||
if self.debug is True or 'visit' in str(self.debug):
|
||||
self(''.join([
|
||||
self.colors['greenbold'],
|
||||
'✔ SUCCESS ',
|
||||
self.colors['reset'],
|
||||
self.colorized(),
|
||||
self.colorized(action),
|
||||
'\n',
|
||||
]))
|
||||
|
||||
def fail(self, exception=None):
|
||||
def fail(self, action, exception=None):
|
||||
if self.debug is True or 'visit' in str(self.debug):
|
||||
self(''.join([
|
||||
self.colors['redbold'],
|
||||
'✘ FAIL ',
|
||||
self.colors['reset'],
|
||||
self.colorized(),
|
||||
self.colorized(action),
|
||||
'\n',
|
||||
]))
|
||||
|
||||
def results(self):
|
||||
def results(self, action):
|
||||
success = 0
|
||||
fail = 0
|
||||
for result in self.subject.results:
|
||||
for result in action.results:
|
||||
if result.status == 'success':
|
||||
success += 1
|
||||
if result.status == 'failure':
|
||||
|
||||
@ -5,3 +5,8 @@ from .targets.stub import Stub
|
||||
|
||||
from .actions.packages import Packages
|
||||
from .actions.run import Run
|
||||
from .actions.pip import Pip
|
||||
from .actions.parallel import Parallel
|
||||
|
||||
from .container import Container
|
||||
from .pod import Pod
|
||||
|
||||
@ -7,11 +7,10 @@ from ..result import Result, Results
|
||||
|
||||
|
||||
class Target:
|
||||
def __init__(self, *actions, **options):
|
||||
def __init__(self, *actions):
|
||||
self.actions = actions
|
||||
self.options = options
|
||||
self.results = []
|
||||
self.output = Output(self, **self.options)
|
||||
self.output = Output()
|
||||
self.parent = None
|
||||
|
||||
@property
|
||||
@ -32,13 +31,11 @@ class Target:
|
||||
|
||||
for action in actions or self.actions:
|
||||
result = Result(self, action)
|
||||
|
||||
self.output = Output(action, **self.options)
|
||||
self.output.start()
|
||||
self.output.start(action)
|
||||
try:
|
||||
await action(target=self)
|
||||
except Exception as e:
|
||||
self.output.fail(e)
|
||||
self.output.fail(action, e)
|
||||
result.status = 'failure'
|
||||
result.exception = e
|
||||
if actions:
|
||||
@ -47,7 +44,7 @@ class Target:
|
||||
else:
|
||||
break
|
||||
else:
|
||||
self.output.success()
|
||||
self.output.success(action)
|
||||
result.status = 'success'
|
||||
finally:
|
||||
self.caller.results.append(result)
|
||||
@ -55,7 +52,7 @@ class Target:
|
||||
clean = getattr(action, 'clean', None)
|
||||
if clean:
|
||||
action.result = result
|
||||
self.output.clean()
|
||||
self.output.clean(action)
|
||||
await clean(self)
|
||||
|
||||
async def rexec(self, *args, **kwargs):
|
||||
@ -77,4 +74,4 @@ class Target:
|
||||
return result
|
||||
|
||||
async def exec(self, *args, **kwargs):
|
||||
raise NotImplemented()
|
||||
raise Exception(f'{self} should run in Localhost() or Stub()')
|
||||
|
||||
@ -12,8 +12,7 @@ class Buildah(Target):
|
||||
def __init__(self,
|
||||
*actions,
|
||||
base=None, commit=None,
|
||||
cmd=None,
|
||||
**options):
|
||||
cmd=None):
|
||||
self.base = base or 'alpine'
|
||||
self.image = Image(commit) if commit else None
|
||||
|
||||
@ -28,7 +27,7 @@ class Buildah(Target):
|
||||
# Always consider localhost as parent for now
|
||||
self.parent = Target()
|
||||
|
||||
super().__init__(*actions, **options)
|
||||
super().__init__(*actions)
|
||||
|
||||
def is_runnable(self):
|
||||
return Proc.test or os.getuid() == 0
|
||||
@ -39,7 +38,9 @@ class Buildah(Target):
|
||||
return 'Buildah image builder'
|
||||
|
||||
async def __call__(self, *actions, target=None):
|
||||
self.parent = target
|
||||
if target:
|
||||
self.parent = target
|
||||
|
||||
if not self.is_runnable():
|
||||
os.execvp('buildah', ['buildah', 'unshare'] + sys.argv)
|
||||
# program has been replaced
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user