Fix release asset deployment
Package built frontend assets in wheels and force clean upgrades. Run API-triggered updates in a transient unit with a safe delay.
This commit is contained in:
@@ -49,18 +49,23 @@ if ! git rev-parse --verify --quiet "refs/tags/$RELEASE_TAG" >/dev/null; then
|
||||
fi
|
||||
git checkout --detach "$RELEASE_TAG"
|
||||
test "$(git describe --tags --exact-match HEAD)" = "$RELEASE_TAG"
|
||||
"$VENV/bin/pip" install --upgrade "$REPO_DIR"
|
||||
"$VENV/bin/pip" install --upgrade --force-reinstall --no-cache-dir "$REPO_DIR"
|
||||
install -m 0644 "$REPO_DIR/deploy/importarr.service" /etc/systemd/system/importarr.service
|
||||
install -m 0755 "$REPO_DIR/deploy/repo-upgrade.sh" "$PREFIX/repo-upgrade.sh"
|
||||
systemctl daemon-reload
|
||||
GIT_SHA="$(git rev-parse --short=12 HEAD 2>/dev/null || printf development)"
|
||||
BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
VERSION="$RELEASE_TAG"
|
||||
cat > "$PREFIX/build.env" <<EOF
|
||||
BUILD_ENV_TMP="$PREFIX/build.env.tmp.$$"
|
||||
trap 'rm -f "$BUILD_ENV_TMP"' EXIT HUP INT TERM
|
||||
cat > "$BUILD_ENV_TMP" <<EOF
|
||||
IMPORTARR_VERSION=$VERSION
|
||||
IMPORTARR_GIT_SHA=$GIT_SHA
|
||||
IMPORTARR_BUILD_DATE=$BUILD_DATE
|
||||
EOF
|
||||
chmod 0644 "$BUILD_ENV_TMP"
|
||||
mv -f "$BUILD_ENV_TMP" "$PREFIX/build.env"
|
||||
trap - EXIT HUP INT TERM
|
||||
systemctl restart "$SERVICE"
|
||||
systemctl restart manual-media-import.timer
|
||||
systemctl --no-pager --full status "$SERVICE"
|
||||
|
||||
@@ -11,7 +11,7 @@ install -d -m 0755 /opt/importarr
|
||||
REPO_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
|
||||
python3 -m venv /opt/importarr/venv
|
||||
/opt/importarr/venv/bin/pip install --upgrade pip
|
||||
/opt/importarr/venv/bin/pip install --upgrade "$REPO_DIR"
|
||||
/opt/importarr/venv/bin/pip install --upgrade --force-reinstall --no-cache-dir "$REPO_DIR"
|
||||
if [ ! -f /etc/importarr/importarr.env ]; then
|
||||
install -m 0600 "$REPO_DIR/deploy/importarr.env.example" /etc/importarr/importarr.env
|
||||
echo "Created /etc/importarr/importarr.env; edit it before starting the service."
|
||||
@@ -31,11 +31,16 @@ fi
|
||||
GIT_SHA="$(git -C "$REPO_DIR" rev-parse --short=12 HEAD 2>/dev/null || printf development)"
|
||||
BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
VERSION="$(git -C "$REPO_DIR" describe --tags --exact-match HEAD 2>/dev/null || /opt/importarr/venv/bin/python -c 'from importarr import __version__; print(__version__)')"
|
||||
cat > /opt/importarr/build.env <<EOF
|
||||
BUILD_ENV_TMP="/opt/importarr/build.env.tmp.$$"
|
||||
trap 'rm -f "$BUILD_ENV_TMP"' EXIT HUP INT TERM
|
||||
cat > "$BUILD_ENV_TMP" <<EOF
|
||||
IMPORTARR_VERSION=$VERSION
|
||||
IMPORTARR_GIT_SHA=$GIT_SHA
|
||||
IMPORTARR_BUILD_DATE=$BUILD_DATE
|
||||
EOF
|
||||
chmod 0644 "$BUILD_ENV_TMP"
|
||||
mv -f "$BUILD_ENV_TMP" /opt/importarr/build.env
|
||||
trap - EXIT HUP INT TERM
|
||||
systemctl daemon-reload
|
||||
systemctl enable importarr.service
|
||||
systemctl enable manual-media-import.timer
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "importarr-ui",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "importarr-ui",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.14",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "importarr-ui",
|
||||
"private": true,
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.1.2"
|
||||
__version__ = "0.1.3"
|
||||
|
||||
+10
-6
@@ -402,13 +402,17 @@ def _schedule_update(command: list[str]) -> None:
|
||||
if not command:
|
||||
raise HTTPException(status_code=500, detail="update command is not configured")
|
||||
try:
|
||||
subprocess.Popen(
|
||||
["/bin/sh", "-c", 'sleep 1; exec "$@"', "importarr-update", *command],
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
start_new_session=True,
|
||||
result = subprocess.run(
|
||||
["systemd-run", "--unit=importarr-update", "--collect", "--no-block", "--on-active=2s", "--", *command],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise HTTPException(status_code=500, detail=f"update command failed to schedule: {result.stderr[-1000:]}")
|
||||
except subprocess.TimeoutExpired as exc:
|
||||
raise HTTPException(status_code=504, detail="update command timed out while scheduling") from exc
|
||||
except OSError as exc:
|
||||
raise HTTPException(status_code=500, detail=f"update command failed to start: {exc.__class__.__name__}") from exc
|
||||
|
||||
|
||||
+4
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "importarr"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
description = "Arr-style manual SABnzbd import service"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
@@ -23,6 +23,9 @@ test = ["pytest>=8.2", "pytest-asyncio>=0.23"]
|
||||
importarr = "importarr.main:run"
|
||||
manual-media-import = "importarr.worker:main"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["importarr"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
pythonpath = ["."]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import fcntl
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -30,3 +32,28 @@ def test_repo_upgrade_refuses_concurrent_run(tmp_path):
|
||||
|
||||
assert result.returncode == 1
|
||||
assert "another Importarr upgrade is already running" in result.stderr
|
||||
|
||||
|
||||
def test_wheel_contains_built_frontend_assets(tmp_path):
|
||||
root = Path(__file__).parents[1]
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "pip", "wheel", "--no-deps", "--wheel-dir", str(tmp_path), str(root)],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
wheel = next(tmp_path.glob("importarr-*.whl"))
|
||||
with zipfile.ZipFile(wheel) as archive:
|
||||
packaged = set(archive.namelist())
|
||||
|
||||
assert "importarr/static/index.html" in packaged
|
||||
assert "importarr/static/assets/app.js" in packaged
|
||||
assert "importarr/static/assets/app.css" in packaged
|
||||
|
||||
|
||||
def test_repo_upgrade_forces_fresh_install_without_skipping_dependencies():
|
||||
script = (Path(__file__).parents[1] / "deploy/repo-upgrade.sh").read_text()
|
||||
|
||||
assert "pip\" install --upgrade --force-reinstall --no-cache-dir" in script
|
||||
assert "--no-deps" not in script
|
||||
|
||||
@@ -194,6 +194,32 @@ def test_update_schedules_exact_latest_release_tag(tmp_path, monkeypatch):
|
||||
assert response["status"] == "update_scheduled"
|
||||
|
||||
|
||||
def test_update_is_scheduled_outside_service_cgroup(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("IMPORTARR_STATE_PATH", str(tmp_path / "state.db"))
|
||||
import importarr.main as main
|
||||
|
||||
calls = []
|
||||
|
||||
def fake_run(command, **kwargs):
|
||||
calls.append((command, kwargs))
|
||||
return main.subprocess.CompletedProcess(command, 0, stdout="Running as unit", stderr="")
|
||||
|
||||
monkeypatch.setattr(main.subprocess, "run", fake_run)
|
||||
main._schedule_update(["/bin/sh", "/opt/importarr/repo-upgrade.sh", "v1.2.3"])
|
||||
|
||||
assert calls[0][0] == [
|
||||
"systemd-run",
|
||||
"--unit=importarr-update",
|
||||
"--collect",
|
||||
"--no-block",
|
||||
"--on-active=2s",
|
||||
"--",
|
||||
"/bin/sh",
|
||||
"/opt/importarr/repo-upgrade.sh",
|
||||
"v1.2.3",
|
||||
]
|
||||
|
||||
|
||||
def test_update_endpoint_requires_expected_tag(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("IMPORTARR_STATE_PATH", str(tmp_path / "state.db"))
|
||||
import importarr.main as main
|
||||
|
||||
Reference in New Issue
Block a user