diff --git a/.woodpecker/job.yaml b/.woodpecker/job.yaml index d934026..b9c484a 100644 --- a/.woodpecker/job.yaml +++ b/.woodpecker/job.yaml @@ -15,4 +15,4 @@ steps: image: bash commands: - '[ "${CI_COMMIT_BRANCH:-}" = "master" ] || { echo "skipping deploy on branch ${CI_COMMIT_BRANCH:-unknown}"; exit 0; }' - - /usr/local/libexec/deploy-kotsukotsu + - bash ./scripts/deploy-app.sh diff --git a/nix/module.nix b/nix/module.nix index 90e2bb1..82bcdee 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -2,6 +2,14 @@ let cfg = config.services.kotsukotsu; + launcher = pkgs.writeShellScript "kotsukotsu-launch" '' + exec ${cfg.currentPath}/bin/kotsukotsu + ''; + ensureCurrent = pkgs.writeShellScript "kotsukotsu-ensure-current" '' + if [ ! -e ${cfg.currentPath} ]; then + ln -sfn ${cfg.package} ${cfg.currentPath} + fi + ''; in { options.services.kotsukotsu = { @@ -38,6 +46,12 @@ in description = "Directory for the persistent SQLite database."; }; + currentPath = lib.mkOption { + type = lib.types.str; + default = "/var/lib/kotsukotsu/current"; + description = "Stable symlink path that points at the active app package."; + }; + user = lib.mkOption { type = lib.types.str; default = "kotsukotsu"; @@ -71,6 +85,10 @@ in assertion = lib.hasPrefix "/" cfg.dataDir; message = "services.kotsukotsu.dataDir must be an absolute path."; } + { + assertion = lib.hasPrefix "/" cfg.currentPath; + message = "services.kotsukotsu.currentPath must be an absolute path."; + } ]; users.groups = lib.mkIf (cfg.group == "kotsukotsu") { @@ -104,7 +122,8 @@ in }; serviceConfig = { - ExecStart = lib.getExe cfg.package; + ExecStart = launcher; + ExecStartPre = ensureCurrent; Group = cfg.group; Restart = "on-failure"; User = cfg.user; diff --git a/scripts/deploy-app.sh b/scripts/deploy-app.sh new file mode 100644 index 0000000..eae4f9c --- /dev/null +++ b/scripts/deploy-app.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +target_host="${1:-root@sortug.com}" +service_name="${SERVICE_NAME:-kotsukotsu}" +current_path="${CURRENT_PATH:-/var/lib/kotsukotsu/current}" +health_url="${HEALTH_URL:-http://127.0.0.1:5174/healthz}" + +store_path="$(nix build --accept-flake-config --print-out-paths .#default | tail -n 1)" + +echo "Built ${store_path}" +echo "Copying package to ${target_host}" +nix copy --to "ssh://${target_host}" "${store_path}" + +echo "Activating release on ${target_host}" +ssh "${target_host}" " + set -euo pipefail + ln -sfn '${store_path}' '${current_path}' + systemctl restart '${service_name}' + for _ in 1 2 3 4 5 6 7 8 9 10; do + if curl -fsS '${health_url}' >/dev/null; then + exit 0 + fi + sleep 1 + done + echo 'health check failed for ${service_name}' >&2 + exit 1 +"