fix(packaging): address review findings for #44
- nfpm.yaml: type:config → config_noreplace (RPM noreplace semantics) - nfpm.yaml: type:ghost → type:dir for /var/lib/fenris (deb compatibility) - postinst.sh/rpm/post.sh: fix timer restart — capture running unit before daemon-reload so the diff actually detects changes - README: fix Python floor to ≥3.10 (was ≥3.9, inconsistent with Makefile) - signing-key-ceremony.md: fix stale claim about nfpm signing RPMs (actual path is post-build rpmsign) - tests/conftest.py: extract shared _get_version() and _read() helpers - tests: wire up to shared conftest helpers - release.yml: extract VERSION once via GITHUB_OUTPUT step Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
This commit is contained in:
co-authored by
CommandCodeBot
parent
120d80b28c
commit
1e2ddfb928
@@ -35,11 +35,15 @@ jobs:
|
||||
- name: Generate and clearsign SHA256SUMS
|
||||
run: make clearsign
|
||||
|
||||
- name: Determine version
|
||||
id: version
|
||||
run: echo "version=$(sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload deb packages to registry
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
run: |
|
||||
VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml)
|
||||
VERSION=${{ steps.version.outputs.version }}
|
||||
DEB="fenris_${VERSION}_amd64.deb"
|
||||
for CODENAME in bookworm jammy noble; do
|
||||
curl --fail -X PUT \
|
||||
@@ -52,7 +56,7 @@ jobs:
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
run: |
|
||||
VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml)
|
||||
VERSION=${{ steps.version.outputs.version }}
|
||||
RPM="fenris-${VERSION}-1.x86_64.rpm"
|
||||
curl --fail -X PUT \
|
||||
-u "xavierk:${GITEA_TOKEN}" \
|
||||
@@ -63,7 +67,7 @@ jobs:
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
run: |
|
||||
VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml)
|
||||
VERSION=${{ steps.version.outputs.version }}
|
||||
# Check if release already exists (idempotent re-runs)
|
||||
EXISTING=$(curl -s -o /dev/null -w '%{http_code}' \
|
||||
-u "xavierk:${GITEA_TOKEN}" \
|
||||
@@ -82,7 +86,7 @@ jobs:
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
run: |
|
||||
VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml)
|
||||
VERSION=${{ steps.version.outputs.version }}
|
||||
# Get release ID for this tag
|
||||
RELEASE_ID=$(curl -s \
|
||||
-u "xavierk:${GITEA_TOKEN}" \
|
||||
|
||||
@@ -8,7 +8,7 @@ Fenris is a persistent TUI monitor backed by a short-lived privileged collector
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Python ≥ 3.9** (verified at install time; ≥ 3.10 for packages)
|
||||
- **Python ≥ 3.10** (verified at install time)
|
||||
- **smartmontools** (`smartctl` — verified at install time)
|
||||
- **systemd** with a polkit agent (the collector runs as root oneshot; elevation is exclusively polkit)
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ make release # builds, signs RPM, clearsigns SHA256SUMS, prints upload steps
|
||||
|
||||
Under the hood:
|
||||
|
||||
1. `nfpm pkg -p rpm` signs the RPM payload via `rpm.signature.key_file` and
|
||||
`rpm.signature.key_id` in `packaging/nfpm.yaml`.
|
||||
1. `rpmsign --addsign` signs the RPM payload with the packaging key
|
||||
(invoked by `make sign-rpm`).
|
||||
2. `sha256sum` generates the checksum manifest.
|
||||
3. `gpg --clearsign` produces `SHA256SUMS.asc` with the packaging key.
|
||||
|
||||
|
||||
+5
-5
@@ -26,17 +26,17 @@ contents:
|
||||
file_info:
|
||||
mode: 0755
|
||||
|
||||
# Default placeholder-commented config (conffile for deb)
|
||||
# Default placeholder-commented config (deb conffile / rpm %config(noreplace))
|
||||
- src: packaging/fenris.conf
|
||||
dst: /etc/fenris/fenris.conf
|
||||
type: config
|
||||
type: config_noreplace
|
||||
file_info:
|
||||
mode: 0644
|
||||
|
||||
# Observation store directory — owned by package, never packed
|
||||
# deb: created in postinst; rpm: %ghost
|
||||
# Observation store directory — owned by package, never packed.
|
||||
# Store files (observations.db, WAL sidecars, .bak) are never owned.
|
||||
- dst: /var/lib/fenris
|
||||
type: ghost
|
||||
type: dir
|
||||
file_info:
|
||||
mode: 2750
|
||||
group: fenris
|
||||
|
||||
+14
-9
@@ -26,19 +26,24 @@ n = migrate_to_latest(Path('${STORE_DB}'))
|
||||
print(f'Fenris migration: {n} step(s) applied') if n else None
|
||||
" 2>&1 || echo "Fenris: migration skipped (store not yet initialized)"
|
||||
fi
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
# Restart timer only if unit contents changed AND active (spec §7)
|
||||
# Capture running unit content BEFORE daemon-reload (spec §7)
|
||||
RUNNING_UNITS=""
|
||||
for unit in fenris-collect.timer; do
|
||||
if systemctl is-active --quiet "${unit}" 2>/dev/null; then
|
||||
TMPFILE="$(mktemp)"
|
||||
systemctl cat "${unit}" > "${TMPFILE}" 2>/dev/null || true
|
||||
UNIT_PATH="/usr/lib/systemd/system/${unit}"
|
||||
if ! diff -q "${TMPFILE}" "${UNIT_PATH}" > /dev/null 2>&1; then
|
||||
systemctl restart "${unit}" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "${TMPFILE}"
|
||||
RUNNING_UNITS="${RUNNING_UNITS} ${unit}"
|
||||
fi
|
||||
done
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
# Restart timer only if unit contents changed AND active
|
||||
for unit in ${RUNNING_UNITS}; do
|
||||
OLD_CONTENT="$(mktemp)"
|
||||
NEW_PATH="/usr/lib/systemd/system/${unit}"
|
||||
systemctl cat "${unit}" > "${OLD_CONTENT}" 2>/dev/null || true
|
||||
if ! diff -q "${OLD_CONTENT}" "${NEW_PATH}" > /dev/null 2>&1; then
|
||||
systemctl restart "${unit}" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "${OLD_CONTENT}"
|
||||
done
|
||||
fi
|
||||
# sysusers, tmpfiles, daemon-reload (both fresh install and upgrade)
|
||||
systemd-sysusers || true
|
||||
|
||||
+14
-8
@@ -25,16 +25,22 @@ n = migrate_to_latest(Path('${STORE_DB}'))
|
||||
print(f'Fenris migration: {n} step(s) applied') if n else None
|
||||
" 2>&1 || echo "Fenris: migration skipped (store not yet initialized)"
|
||||
fi
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
# Capture running unit content BEFORE daemon-reload (spec §7)
|
||||
RUNNING_UNITS=""
|
||||
for unit in fenris-collect.timer; do
|
||||
if systemctl is-active --quiet "${unit}" 2>/dev/null; then
|
||||
TMPFILE="$(mktemp)"
|
||||
systemctl cat "${unit}" > "${TMPFILE}" 2>/dev/null || true
|
||||
UNIT_PATH="/usr/lib/systemd/system/${unit}"
|
||||
if ! diff -q "${TMPFILE}" "${UNIT_PATH}" > /dev/null 2>&1; then
|
||||
systemctl restart "${unit}" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "${TMPFILE}"
|
||||
RUNNING_UNITS="${RUNNING_UNITS} ${unit}"
|
||||
fi
|
||||
done
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
# Restart timer only if unit contents changed AND active
|
||||
for unit in ${RUNNING_UNITS}; do
|
||||
OLD_CONTENT="$(mktemp)"
|
||||
NEW_PATH="/usr/lib/systemd/system/${unit}"
|
||||
systemctl cat "${unit}" > "${OLD_CONTENT}" 2>/dev/null || true
|
||||
if ! diff -q "${OLD_CONTENT}" "${NEW_PATH}" > /dev/null 2>&1; then
|
||||
systemctl restart "${unit}" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "${OLD_CONTENT}"
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Shared test helpers for Fenris test suite."""
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
VERSION_FILE = REPO_ROOT / "pyproject.toml"
|
||||
|
||||
|
||||
def get_version() -> str:
|
||||
"""Extract version from pyproject.toml."""
|
||||
for line in VERSION_FILE.read_text().splitlines():
|
||||
if line.startswith("version"):
|
||||
return line.split("=")[1].strip().strip('"')
|
||||
raise RuntimeError("Could not determine version from pyproject.toml")
|
||||
|
||||
|
||||
def read(path: str | Path) -> str:
|
||||
"""Read a file relative to the repository root."""
|
||||
return (REPO_ROOT / path).read_text()
|
||||
@@ -21,7 +21,6 @@ import pytest
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
DIST_DIR = REPO_ROOT / "dist"
|
||||
VERSION_FILE = REPO_ROOT / "pyproject.toml"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
@@ -29,10 +28,8 @@ VERSION_FILE = REPO_ROOT / "pyproject.toml"
|
||||
|
||||
def _get_version() -> str:
|
||||
"""Extract version from pyproject.toml."""
|
||||
for line in VERSION_FILE.read_text().splitlines():
|
||||
if line.startswith("version"):
|
||||
return line.split("=")[1].strip().strip('"')
|
||||
raise RuntimeError("Could not determine version from pyproject.toml")
|
||||
from tests.conftest import get_version
|
||||
return get_version()
|
||||
|
||||
|
||||
def _docker_available() -> bool:
|
||||
|
||||
@@ -26,15 +26,14 @@ RELEASE_SCRIPT = REPO_ROOT / "scripts" / "release.sh"
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _read(path: str | Path) -> str:
|
||||
return (REPO_ROOT / path).read_text()
|
||||
from tests.conftest import read
|
||||
return read(path)
|
||||
|
||||
|
||||
def _get_version() -> str:
|
||||
"""Extract version from pyproject.toml."""
|
||||
for line in (REPO_ROOT / "pyproject.toml").read_text().splitlines():
|
||||
if line.startswith("version"):
|
||||
return line.split("=")[1].strip().strip('"')
|
||||
raise RuntimeError("Could not determine version from pyproject.toml")
|
||||
from tests.conftest import get_version
|
||||
return get_version()
|
||||
|
||||
|
||||
def _run_dry_run(*args: str) -> tuple[int, str]:
|
||||
|
||||
@@ -22,7 +22,8 @@ REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _read(path: str | Path) -> str:
|
||||
return (REPO_ROOT / path).read_text()
|
||||
from tests.conftest import read
|
||||
return read(path)
|
||||
|
||||
|
||||
def _gpg_available() -> bool:
|
||||
|
||||
Reference in New Issue
Block a user