- Light theme uses Papirus-Light: the panel icons of Papirus are #dfdfdf, made for dark panels (xsettings, theme index, login screen). - das-panel-icon help/-h/--help print usage instead of setting an icon; unknown options are rejected. - New packages publisher (publish) and deployer (deploy) on the desktop image: config in ~/.config/<name>/ (neutral example copied on first run), history in ~/.local/state/<name>/. - das-theme 0.3.0, das-settings 0.6.0. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
69 lines
2.7 KiB
Python
69 lines
2.7 KiB
Python
"""docker: compose project in ~/app/docker/<app>, .env linked from configs/docker/<app>.env."""
|
|
from __future__ import annotations
|
|
|
|
import shlex
|
|
from pathlib import Path, PurePosixPath
|
|
|
|
from .. import remote
|
|
from ..config import App, Config
|
|
from .base import Kind, Plan, health_extra, register
|
|
|
|
_UP = r'''
|
|
cd "$DEST" || exit 1
|
|
if [ ! -e Makefile ] && [ ! -L Makefile ] && [ -f "$APPROOT/docker/Makefile" ] && [ "$(dirname "$DEST")" = "$APPROOT/docker" ]; then
|
|
ln -s "$APPROOT/docker/Makefile" Makefile && echo "linked Makefile"
|
|
fi
|
|
docker compose config -q || exit 1
|
|
if [ "$RESTART" = 1 ]; then docker compose up -d --build --remove-orphans; else docker compose build; fi
|
|
'''
|
|
|
|
_HEALTH = r'''
|
|
cd "$DEST" || exit 1
|
|
start=$SECONDS; end=$((SECONDS + TIMEOUT))
|
|
while :; do
|
|
ps=$(docker compose ps -a --format '{{.Name}} {{.State}} {{.Health}}')
|
|
bad=$(echo "$ps" | awk '$2=="exited" || $2=="dead" || $3=="unhealthy"')
|
|
if [ -n "$bad" ]; then echo "$ps"; docker compose logs --tail 30; exit 1; fi
|
|
wait=$(echo "$ps" | awk 'NF && ($2!="running" || ($3!="" && $3!="healthy"))')
|
|
if [ -z "$wait" ] && [ $SECONDS -ge $((start + 6)) ]; then echo "$ps"; exit 0; fi
|
|
[ $SECONDS -lt $end ] || { echo "$ps"; echo "not ready after ${TIMEOUT}s"; exit 1; }
|
|
sleep 3
|
|
done
|
|
'''
|
|
|
|
|
|
@register
|
|
class Docker(Kind):
|
|
name = "docker"
|
|
protected = (".env", "data", "Makefile")
|
|
|
|
def config_items(self, plan: Plan) -> list[tuple[str, str]]:
|
|
app = PurePosixPath(plan.dest).name
|
|
root = f"{plan.cfg.server.configs_root}/docker"
|
|
return [(".env", f"{root}/{app}.env")] + [(n, f"{root}/{app}/{n}") for n in plan.app.configs]
|
|
|
|
def local_config(self, plan: Plan, name: str) -> Path:
|
|
p = plan.app.src_path / name
|
|
if name == ".env" and not p.exists():
|
|
return plan.app.src_path / ".env.example"
|
|
return p
|
|
|
|
def install(self, plan: Plan, log) -> None:
|
|
remote.run(plan.server, _UP, log, DEST=plan.dest, APPROOT=plan.cfg.server.app_root,
|
|
RESTART=int(plan.opts.restart))
|
|
|
|
def health(self, plan: Plan, log) -> bool:
|
|
try:
|
|
remote.run(plan.server, _HEALTH, log, DEST=plan.dest, TIMEOUT=plan.app.health_timeout)
|
|
except remote.RemoteError:
|
|
return False
|
|
return health_extra(plan, log)
|
|
|
|
def status_script(self) -> str:
|
|
return ('cd "$DEST" 2>/dev/null || { echo missing; exit; }; '
|
|
't=$(docker compose ps -a -q 2>/dev/null | wc -l); r=$(docker compose ps -q --status running 2>/dev/null | wc -l); '
|
|
'[ "$t" -gt 0 ] && echo "running $r/$t" || echo stopped')
|
|
|
|
def logs_command(self, cfg: Config, app: App, dest: str) -> str:
|
|
return f"cd {shlex.quote(dest)} && docker compose logs -f --tail=100"
|