Compare commits
12 Commits
v1.9.1
...
ac6981ad4d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac6981ad4d | ||
|
|
f3a7b69bac | ||
|
|
5bcad7f5f3 | ||
|
|
d39582d1da | ||
|
|
043d389a76 | ||
|
|
cc1e543ebc | ||
|
|
25a0579809 | ||
|
|
d4e461bb63 | ||
|
|
1864d0700e | ||
|
|
a9bd8d202f | ||
|
|
28df54503e | ||
|
|
aa489811e3 |
24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,3 +1,27 @@
|
|||||||
|
## [1.10.0] - 2026-01-20
|
||||||
|
|
||||||
|
* Introduce safe verbose image cleanup to reduce Docker image size and build artifacts
|
||||||
|
|
||||||
|
## [1.9.5] - 2026-01-16
|
||||||
|
|
||||||
|
* Release patch: improve git pull error diagnostics
|
||||||
|
|
||||||
|
|
||||||
|
## [1.9.4] - 2026-01-13
|
||||||
|
|
||||||
|
* fix(ci): replace sudo with su for user switching to avoid PAM failures in minimal container images
|
||||||
|
|
||||||
|
|
||||||
|
## [1.9.3] - 2026-01-07
|
||||||
|
|
||||||
|
* Made the Nix dependency optional on non-x86_64 architectures to avoid broken Arch Linux ARM repository packages.
|
||||||
|
|
||||||
|
|
||||||
|
## [1.9.2] - 2025-12-21
|
||||||
|
|
||||||
|
* Default configuration files are now packaged and loaded correctly when no user config exists, while fully preserving custom user configurations.
|
||||||
|
|
||||||
|
|
||||||
## [1.9.1] - 2025-12-21
|
## [1.9.1] - 2025-12-21
|
||||||
|
|
||||||
* Fixed installation issues and improved loading of default configuration files.
|
* Fixed installation issues and improved loading of default configuration files.
|
||||||
|
|||||||
13
Dockerfile
13
Dockerfile
@@ -33,6 +33,7 @@ CMD ["bash"]
|
|||||||
# - inherits from virgin
|
# - inherits from virgin
|
||||||
# - builds + installs pkgmgr
|
# - builds + installs pkgmgr
|
||||||
# - sets entrypoint + default cmd
|
# - sets entrypoint + default cmd
|
||||||
|
# - NOTE: does NOT run slim.sh (that is done in slim stage)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
FROM virgin AS full
|
FROM virgin AS full
|
||||||
|
|
||||||
@@ -53,3 +54,15 @@ COPY scripts/docker/entry.sh /usr/local/bin/docker-entry.sh
|
|||||||
WORKDIR /opt/src/pkgmgr
|
WORKDIR /opt/src/pkgmgr
|
||||||
ENTRYPOINT ["/usr/local/bin/docker-entry.sh"]
|
ENTRYPOINT ["/usr/local/bin/docker-entry.sh"]
|
||||||
CMD ["pkgmgr", "--help"]
|
CMD ["pkgmgr", "--help"]
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Target: slim
|
||||||
|
# - based on full
|
||||||
|
# - runs slim.sh
|
||||||
|
# ============================================================
|
||||||
|
FROM full AS slim
|
||||||
|
|
||||||
|
COPY scripts/docker/slim.sh /usr/local/bin/slim.sh
|
||||||
|
RUN chmod +x /usr/local/bin/slim.sh
|
||||||
|
RUN /usr/local/bin/slim.sh
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
rec {
|
rec {
|
||||||
pkgmgr = pyPkgs.buildPythonApplication {
|
pkgmgr = pyPkgs.buildPythonApplication {
|
||||||
pname = "package-manager";
|
pname = "package-manager";
|
||||||
version = "1.9.1";
|
version = "1.10.0";
|
||||||
|
|
||||||
# Use the git repo as source
|
# Use the git repo as source
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
|||||||
@@ -1,15 +1,25 @@
|
|||||||
# Maintainer: Kevin Veen-Birkenbach <info@veen.world>
|
# Maintainer: Kevin Veen-Birkenbach <info@veen.world>
|
||||||
|
|
||||||
pkgname=package-manager
|
pkgname=package-manager
|
||||||
pkgver=1.9.1
|
pkgver=1.10.0
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Local-flake wrapper for Kevin's package-manager (Nix-based)."
|
pkgdesc="Local-flake wrapper for Kevin's package-manager (Nix-based)."
|
||||||
arch=('any')
|
arch=('any')
|
||||||
url="https://github.com/kevinveenbirkenbach/package-manager"
|
url="https://github.com/kevinveenbirkenbach/package-manager"
|
||||||
license=('MIT')
|
license=('MIT')
|
||||||
|
|
||||||
# Nix is the only runtime dependency; Python is provided by the Nix closure.
|
# Nix is required at runtime to run pkgmgr via the flake.
|
||||||
depends=('nix')
|
# On Arch x86_64 we can depend on the distro package.
|
||||||
|
# On other arches (e.g. ARM) we only declare it as optional because the
|
||||||
|
# repo package may be broken/out-of-sync; installation can be done via the official installer.
|
||||||
|
depends=()
|
||||||
|
optdepends=('nix: required to run pkgmgr via flake')
|
||||||
|
|
||||||
|
if [[ "${CARCH}" == "x86_64" ]]; then
|
||||||
|
depends=('nix')
|
||||||
|
optdepends=()
|
||||||
|
fi
|
||||||
|
|
||||||
makedepends=('rsync')
|
makedepends=('rsync')
|
||||||
|
|
||||||
install=${pkgname}.install
|
install=${pkgname}.install
|
||||||
|
|||||||
@@ -1,3 +1,33 @@
|
|||||||
|
package-manager (1.10.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Automated release.
|
||||||
|
|
||||||
|
-- Kevin Veen-Birkenbach <kevin@veen.world> Tue, 20 Jan 2026 10:44:58 +0100
|
||||||
|
|
||||||
|
package-manager (1.9.5-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Release patch: improve git pull error diagnostics
|
||||||
|
|
||||||
|
-- Kevin Veen-Birkenbach <kevin@veen.world> Fri, 16 Jan 2026 10:09:43 +0100
|
||||||
|
|
||||||
|
package-manager (1.9.4-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* fix(ci): replace sudo with su for user switching to avoid PAM failures in minimal container images
|
||||||
|
|
||||||
|
-- Kevin Veen-Birkenbach <kevin@veen.world> Tue, 13 Jan 2026 14:48:50 +0100
|
||||||
|
|
||||||
|
package-manager (1.9.3-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Made the Nix dependency optional on non-x86_64 architectures to avoid broken Arch Linux ARM repository packages.
|
||||||
|
|
||||||
|
-- Kevin Veen-Birkenbach <kevin@veen.world> Wed, 07 Jan 2026 13:44:40 +0100
|
||||||
|
|
||||||
|
package-manager (1.9.2-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Default configuration files are now packaged and loaded correctly when no user config exists, while fully preserving custom user configurations.
|
||||||
|
|
||||||
|
-- Kevin Veen-Birkenbach <kevin@veen.world> Sun, 21 Dec 2025 15:30:22 +0100
|
||||||
|
|
||||||
package-manager (1.9.1-1) unstable; urgency=medium
|
package-manager (1.9.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
* Fixed installation issues and improved loading of default configuration files.
|
* Fixed installation issues and improved loading of default configuration files.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name: package-manager
|
Name: package-manager
|
||||||
Version: 1.9.1
|
Version: 1.10.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Wrapper that runs Kevin's package-manager via Nix flake
|
Summary: Wrapper that runs Kevin's package-manager via Nix flake
|
||||||
|
|
||||||
@@ -74,6 +74,21 @@ echo ">>> package-manager removed. Nix itself was not removed."
|
|||||||
/usr/lib/package-manager/
|
/usr/lib/package-manager/
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Tue Jan 20 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.10.0-1
|
||||||
|
- Automated release.
|
||||||
|
|
||||||
|
* Fri Jan 16 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.9.5-1
|
||||||
|
- Release patch: improve git pull error diagnostics
|
||||||
|
|
||||||
|
* Tue Jan 13 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.9.4-1
|
||||||
|
- fix(ci): replace sudo with su for user switching to avoid PAM failures in minimal container images
|
||||||
|
|
||||||
|
* Wed Jan 07 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.9.3-1
|
||||||
|
- Made the Nix dependency optional on non-x86_64 architectures to avoid broken Arch Linux ARM repository packages.
|
||||||
|
|
||||||
|
* Sun Dec 21 2025 Kevin Veen-Birkenbach <kevin@veen.world> - 1.9.2-1
|
||||||
|
- Default configuration files are now packaged and loaded correctly when no user config exists, while fully preserving custom user configurations.
|
||||||
|
|
||||||
* Sun Dec 21 2025 Kevin Veen-Birkenbach <kevin@veen.world> - 1.9.1-1
|
* Sun Dec 21 2025 Kevin Veen-Birkenbach <kevin@veen.world> - 1.9.1-1
|
||||||
- Fixed installation issues and improved loading of default configuration files.
|
- Fixed installation issues and improved loading of default configuration files.
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "kpmx"
|
name = "kpmx"
|
||||||
version = "1.9.1"
|
version = "1.10.0"
|
||||||
description = "Kevin's package-manager tool (pkgmgr)"
|
description = "Kevin's package-manager tool (pkgmgr)"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
@@ -44,10 +44,11 @@ pkgmgr = "pkgmgr.cli:main"
|
|||||||
# Source layout: all packages live under "src/"
|
# Source layout: all packages live under "src/"
|
||||||
[tool.setuptools]
|
[tool.setuptools]
|
||||||
package-dir = { "" = "src" }
|
package-dir = { "" = "src" }
|
||||||
|
include-package-data = true
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["src"]
|
where = ["src"]
|
||||||
include = ["pkgmgr*"]
|
include = ["pkgmgr*"]
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
"config" = ["defaults.yaml"]
|
"pkgmgr.config" = ["*.yml", "*.yaml"]
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ Usage: PKGMGR_DISTRO=<distro> $0 [options]
|
|||||||
Build options:
|
Build options:
|
||||||
--missing Build only if the image does not already exist (local build only)
|
--missing Build only if the image does not already exist (local build only)
|
||||||
--no-cache Build with --no-cache
|
--no-cache Build with --no-cache
|
||||||
--target <name> Build a specific Dockerfile target (e.g. virgin)
|
--target <name> Build a specific Dockerfile target (e.g. virgin, slim)
|
||||||
--tag <image> Override the output image tag (default: ${default_tag})
|
--tag <image> Override the output image tag (default: ${default_tag})
|
||||||
|
|
||||||
Publish options:
|
Publish options:
|
||||||
@@ -47,7 +47,7 @@ Publish options:
|
|||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- --publish implies --push and requires --registry, --owner, and --version.
|
- --publish implies --push and requires --registry, --owner, and --version.
|
||||||
- Local build (no --push) uses "docker build" and creates local images like "pkgmgr-arch" / "pkgmgr-arch-virgin".
|
- Local build (no --push) uses "docker build" and creates local images like "pkgmgr-arch" / "pkgmgr-arch-virgin" / "pkgmgr-arch-slim".
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--missing) MISSING_ONLY=1; shift ;;
|
--missing) MISSING_ONLY=1; shift ;;
|
||||||
--target)
|
--target)
|
||||||
TARGET="${2:-}"
|
TARGET="${2:-}"
|
||||||
[[ -n "${TARGET}" ]] || { echo "ERROR: --target requires a value (e.g. virgin)"; exit 2; }
|
[[ -n "${TARGET}" ]] || { echo "ERROR: --target requires a value (e.g. virgin|slim)"; exit 2; }
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
--tag)
|
--tag)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Publish all distro images (full + virgin) to a registry via image.sh --publish
|
# Publish all distro images (full + virgin + slim) to a registry via image.sh --publish
|
||||||
#
|
#
|
||||||
# Required env:
|
# Required env:
|
||||||
# OWNER (e.g. GITHUB_REPOSITORY_OWNER)
|
# OWNER (e.g. GITHUB_REPOSITORY_OWNER)
|
||||||
@@ -11,6 +11,9 @@ set -euo pipefail
|
|||||||
# REGISTRY (default: ghcr.io)
|
# REGISTRY (default: ghcr.io)
|
||||||
# IS_STABLE (default: false)
|
# IS_STABLE (default: false)
|
||||||
# DISTROS (default: "arch debian ubuntu fedora centos")
|
# DISTROS (default: "arch debian ubuntu fedora centos")
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# - This expects Dockerfile targets: virgin, full (default), slim
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
@@ -33,7 +36,10 @@ for d in ${DISTROS}; do
|
|||||||
echo "[publish] PKGMGR_DISTRO=${d}"
|
echo "[publish] PKGMGR_DISTRO=${d}"
|
||||||
echo "============================================================"
|
echo "============================================================"
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
# virgin
|
# virgin
|
||||||
|
# -> ghcr.io/<owner>/pkgmgr-<distro>-virgin:{latest,<version>,stable?}
|
||||||
|
# ----------------------------------------------------------
|
||||||
PKGMGR_DISTRO="${d}" bash "${SCRIPT_DIR}/image.sh" \
|
PKGMGR_DISTRO="${d}" bash "${SCRIPT_DIR}/image.sh" \
|
||||||
--publish \
|
--publish \
|
||||||
--registry "${REGISTRY}" \
|
--registry "${REGISTRY}" \
|
||||||
@@ -42,13 +48,29 @@ for d in ${DISTROS}; do
|
|||||||
--stable "${IS_STABLE}" \
|
--stable "${IS_STABLE}" \
|
||||||
--target virgin
|
--target virgin
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
# full (default target)
|
# full (default target)
|
||||||
|
# -> ghcr.io/<owner>/pkgmgr-<distro>:{latest,<version>,stable?}
|
||||||
|
# ----------------------------------------------------------
|
||||||
PKGMGR_DISTRO="${d}" bash "${SCRIPT_DIR}/image.sh" \
|
PKGMGR_DISTRO="${d}" bash "${SCRIPT_DIR}/image.sh" \
|
||||||
--publish \
|
--publish \
|
||||||
--registry "${REGISTRY}" \
|
--registry "${REGISTRY}" \
|
||||||
--owner "${OWNER}" \
|
--owner "${OWNER}" \
|
||||||
--version "${VERSION}" \
|
--version "${VERSION}" \
|
||||||
--stable "${IS_STABLE}"
|
--stable "${IS_STABLE}"
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# slim
|
||||||
|
# -> ghcr.io/<owner>/pkgmgr-<distro>-slim:{latest,<version>,stable?}
|
||||||
|
# + alias for default distro: ghcr.io/<owner>/pkgmgr-slim:{...}
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
PKGMGR_DISTRO="${d}" bash "${SCRIPT_DIR}/image.sh" \
|
||||||
|
--publish \
|
||||||
|
--registry "${REGISTRY}" \
|
||||||
|
--owner "${OWNER}" \
|
||||||
|
--version "${VERSION}" \
|
||||||
|
--stable "${IS_STABLE}" \
|
||||||
|
--target slim
|
||||||
done
|
done
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
130
scripts/docker/slim.sh
Normal file
130
scripts/docker/slim.sh
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
log() { echo "[cleanup] $*"; }
|
||||||
|
warn() { echo "[cleanup][WARN] $*" >&2; }
|
||||||
|
|
||||||
|
MODE="${MODE:-safe}" # safe | aggressive
|
||||||
|
# safe: caches/logs/tmp only
|
||||||
|
# aggressive: safe + docs/man/info (optional)
|
||||||
|
|
||||||
|
ID="unknown"
|
||||||
|
if [ -f /etc/os-release ]; then
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. /etc/os-release
|
||||||
|
ID="${ID:-unknown}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Starting image cleanup"
|
||||||
|
log "Mode: ${MODE}"
|
||||||
|
log "Detected OS: ${ID}"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Package manager caches (SAFE)
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
case "${ID}" in
|
||||||
|
alpine)
|
||||||
|
log "Cleaning apk cache"
|
||||||
|
if [ -d /var/cache/apk ]; then
|
||||||
|
du -sh /var/cache/apk || true
|
||||||
|
rm -rvf /var/cache/apk/* || true
|
||||||
|
else
|
||||||
|
log "apk cache directory not present (already clean)"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
arch)
|
||||||
|
log "Cleaning pacman cache"
|
||||||
|
du -sh /var/cache/pacman/pkg 2>/dev/null || true
|
||||||
|
pacman -Scc --noconfirm || true
|
||||||
|
rm -rvf /var/cache/pacman/pkg/* || true
|
||||||
|
;;
|
||||||
|
debian|ubuntu)
|
||||||
|
log "Cleaning apt cache"
|
||||||
|
du -sh /var/lib/apt/lists 2>/dev/null || true
|
||||||
|
apt-get clean || true
|
||||||
|
rm -rvf /var/lib/apt/lists/* || true
|
||||||
|
;;
|
||||||
|
fedora)
|
||||||
|
log "Cleaning dnf cache"
|
||||||
|
du -sh /var/cache/dnf 2>/dev/null || true
|
||||||
|
dnf clean all || true
|
||||||
|
rm -rvf /var/cache/dnf/* || true
|
||||||
|
;;
|
||||||
|
centos|rhel)
|
||||||
|
log "Cleaning yum/dnf cache"
|
||||||
|
du -sh /var/cache/yum /var/cache/dnf 2>/dev/null || true
|
||||||
|
(command -v dnf >/dev/null 2>&1 && dnf clean all) || true
|
||||||
|
(command -v yum >/dev/null 2>&1 && yum clean all) || true
|
||||||
|
rm -rvf /var/cache/yum/* /var/cache/dnf/* || true
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
warn "Unknown distro '${ID}' — skipping package manager cleanup"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Python caches (SAFE)
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
log "Cleaning pip cache"
|
||||||
|
du -sh /root/.cache/pip 2>/dev/null || true
|
||||||
|
rm -rvf /root/.cache/pip 2>/dev/null || true
|
||||||
|
rm -rvf /home/*/.cache/pip 2>/dev/null || true
|
||||||
|
|
||||||
|
log "Cleaning __pycache__ directories"
|
||||||
|
find /opt /usr /root /home -type d -name "__pycache__" -print -prune 2>/dev/null || true
|
||||||
|
find /opt /usr /root /home -type d -name "__pycache__" -prune -exec rm -rvf {} + 2>/dev/null || true
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Logs (SAFE)
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
log "Truncating log files (keeping paths intact)"
|
||||||
|
if [ -d /var/log ]; then
|
||||||
|
find /var/log -type f -name "*.log" -print 2>/dev/null || true
|
||||||
|
find /var/log -type f -name "*.log" -exec sh -lc ': > "$1" 2>/dev/null || true' _ {} \; 2>/dev/null || true
|
||||||
|
|
||||||
|
find /var/log -type f -name "*.out" -print 2>/dev/null || true
|
||||||
|
find /var/log -type f -name "*.out" -exec sh -lc ': > "$1" 2>/dev/null || true' _ {} \; 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v journalctl >/dev/null 2>&1; then
|
||||||
|
log "Vacuuming journald logs"
|
||||||
|
journalctl --disk-usage || true
|
||||||
|
journalctl --vacuum-size=10M || true
|
||||||
|
journalctl --vacuum-time=1s || true
|
||||||
|
journalctl --disk-usage || true
|
||||||
|
else
|
||||||
|
log "journald not present (skipping)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Temporary files (SAFE)
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
log "Cleaning temporary directories"
|
||||||
|
if [ -d /tmp ]; then
|
||||||
|
du -sh /tmp 2>/dev/null || true
|
||||||
|
rm -rvf /tmp/* || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d /var/tmp ]; then
|
||||||
|
du -sh /var/tmp 2>/dev/null || true
|
||||||
|
rm -rvf /var/tmp/* || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Generic caches (SAFE)
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
log "Cleaning generic caches"
|
||||||
|
du -sh /root/.cache 2>/dev/null || true
|
||||||
|
rm -rvf /root/.cache/* 2>/dev/null || true
|
||||||
|
rm -rvf /home/*/.cache/* 2>/dev/null || true
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Optional aggressive extras (still safe for runtime)
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
if [[ "${MODE}" == "aggressive" ]]; then
|
||||||
|
log "Aggressive mode enabled: removing docs/man/info"
|
||||||
|
du -sh /usr/share/doc /usr/share/man /usr/share/info 2>/dev/null || true
|
||||||
|
rm -rvf /usr/share/doc/* /usr/share/man/* /usr/share/info/* 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Cleanup finished successfully"
|
||||||
@@ -38,11 +38,7 @@ echo "[aur-builder-setup] Configuring sudoers for aur_builder..."
|
|||||||
${ROOT_CMD} bash -c "echo '%aur_builder ALL=(ALL) NOPASSWD: /usr/bin/pacman' > /etc/sudoers.d/aur_builder"
|
${ROOT_CMD} bash -c "echo '%aur_builder ALL=(ALL) NOPASSWD: /usr/bin/pacman' > /etc/sudoers.d/aur_builder"
|
||||||
${ROOT_CMD} chmod 0440 /etc/sudoers.d/aur_builder
|
${ROOT_CMD} chmod 0440 /etc/sudoers.d/aur_builder
|
||||||
|
|
||||||
if command -v sudo >/dev/null 2>&1; then
|
RUN_AS_AUR=(su - aur_builder -s /bin/bash -c)
|
||||||
RUN_AS_AUR=(sudo -u aur_builder bash -lc)
|
|
||||||
else
|
|
||||||
RUN_AS_AUR=(su - aur_builder -c)
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[aur-builder-setup] Ensuring yay is installed for aur_builder..."
|
echo "[aur-builder-setup] Ensuring yay is installed for aur_builder..."
|
||||||
|
|
||||||
|
|||||||
@@ -49,11 +49,7 @@ install_nix_with_retry() {
|
|||||||
if [[ -n "$run_as" ]]; then
|
if [[ -n "$run_as" ]]; then
|
||||||
chown "$run_as:$run_as" "$installer" 2>/dev/null || true
|
chown "$run_as:$run_as" "$installer" 2>/dev/null || true
|
||||||
echo "[init-nix] Running installer as user '$run_as' ($mode_flag)..."
|
echo "[init-nix] Running installer as user '$run_as' ($mode_flag)..."
|
||||||
if command -v sudo >/dev/null 2>&1; then
|
su - "$run_as" -s /bin/bash -c "bash -lc \"sh '$installer' $mode_flag\""
|
||||||
sudo -u "$run_as" bash -lc "sh '$installer' $mode_flag"
|
|
||||||
else
|
|
||||||
su - "$run_as" -c "sh '$installer' $mode_flag"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo "[init-nix] Running installer as current user ($mode_flag)..."
|
echo "[init-nix] Running installer as current user ($mode_flag)..."
|
||||||
sh "$installer" "$mode_flag"
|
sh "$installer" "$mode_flag"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# src/pkgmgr/cli/commands/config.py
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
@@ -38,25 +39,18 @@ def _load_user_config(user_config_path: str) -> Dict[str, Any]:
|
|||||||
|
|
||||||
def _find_defaults_source_dir() -> Optional[str]:
|
def _find_defaults_source_dir() -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Find the directory inside the installed pkgmgr package OR the
|
Find the directory inside the installed pkgmgr package that contains
|
||||||
project root that contains default config files.
|
the default config files.
|
||||||
|
|
||||||
Preferred locations (in dieser Reihenfolge):
|
Preferred location:
|
||||||
- <pkg_root>/config
|
- <pkg_root>/config
|
||||||
- <project_root>/config
|
|
||||||
"""
|
"""
|
||||||
import pkgmgr # local import to avoid circular deps
|
import pkgmgr # local import to avoid circular deps
|
||||||
|
|
||||||
pkg_root = Path(pkgmgr.__file__).resolve().parent
|
pkg_root = Path(pkgmgr.__file__).resolve().parent
|
||||||
project_root = pkg_root.parent
|
cand = pkg_root / "config"
|
||||||
|
if cand.is_dir():
|
||||||
candidates = [
|
return str(cand)
|
||||||
pkg_root / "config",
|
|
||||||
project_root / "config",
|
|
||||||
]
|
|
||||||
for cand in candidates:
|
|
||||||
if cand.is_dir():
|
|
||||||
return str(cand)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -84,7 +78,6 @@ def _update_default_configs(user_config_path: str) -> None:
|
|||||||
if not (lower.endswith(".yml") or lower.endswith(".yaml")):
|
if not (lower.endswith(".yml") or lower.endswith(".yaml")):
|
||||||
continue
|
continue
|
||||||
if name == "config.yaml":
|
if name == "config.yaml":
|
||||||
# Never overwrite the user config template / live config
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
src = os.path.join(source_dir, name)
|
src = os.path.join(source_dir, name)
|
||||||
@@ -98,48 +91,28 @@ def handle_config(args, ctx: CLIContext) -> None:
|
|||||||
"""
|
"""
|
||||||
Handle 'pkgmgr config' subcommands.
|
Handle 'pkgmgr config' subcommands.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user_config_path = ctx.user_config_path
|
user_config_path = ctx.user_config_path
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# config show
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
if args.subcommand == "show":
|
if args.subcommand == "show":
|
||||||
if args.all or (not args.identifiers):
|
if args.all or (not args.identifiers):
|
||||||
# Full merged config view
|
|
||||||
show_config([], user_config_path, full_config=True)
|
show_config([], user_config_path, full_config=True)
|
||||||
else:
|
else:
|
||||||
# Show only matching entries from user config
|
|
||||||
user_config = _load_user_config(user_config_path)
|
user_config = _load_user_config(user_config_path)
|
||||||
selected = resolve_repos(
|
selected = resolve_repos(
|
||||||
args.identifiers,
|
args.identifiers, user_config.get("repositories", [])
|
||||||
user_config.get("repositories", []),
|
|
||||||
)
|
)
|
||||||
if selected:
|
if selected:
|
||||||
show_config(
|
show_config(selected, user_config_path, full_config=False)
|
||||||
selected,
|
|
||||||
user_config_path,
|
|
||||||
full_config=False,
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# config add
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
if args.subcommand == "add":
|
if args.subcommand == "add":
|
||||||
interactive_add(ctx.config_merged, user_config_path)
|
interactive_add(ctx.config_merged, user_config_path)
|
||||||
return
|
return
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# config edit
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
if args.subcommand == "edit":
|
if args.subcommand == "edit":
|
||||||
run_command(f"nano {user_config_path}")
|
run_command(f"nano {user_config_path}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# config init
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
if args.subcommand == "init":
|
if args.subcommand == "init":
|
||||||
user_config = _load_user_config(user_config_path)
|
user_config = _load_user_config(user_config_path)
|
||||||
config_init(
|
config_init(
|
||||||
@@ -150,9 +123,6 @@ def handle_config(args, ctx: CLIContext) -> None:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# config delete
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
if args.subcommand == "delete":
|
if args.subcommand == "delete":
|
||||||
user_config = _load_user_config(user_config_path)
|
user_config = _load_user_config(user_config_path)
|
||||||
|
|
||||||
@@ -163,10 +133,7 @@ def handle_config(args, ctx: CLIContext) -> None:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
to_delete = resolve_repos(
|
to_delete = resolve_repos(args.identifiers, user_config.get("repositories", []))
|
||||||
args.identifiers,
|
|
||||||
user_config.get("repositories", []),
|
|
||||||
)
|
|
||||||
new_repos = [
|
new_repos = [
|
||||||
entry
|
entry
|
||||||
for entry in user_config.get("repositories", [])
|
for entry in user_config.get("repositories", [])
|
||||||
@@ -177,9 +144,6 @@ def handle_config(args, ctx: CLIContext) -> None:
|
|||||||
print(f"Deleted {len(to_delete)} entries from user config.")
|
print(f"Deleted {len(to_delete)} entries from user config.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# config ignore
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
if args.subcommand == "ignore":
|
if args.subcommand == "ignore":
|
||||||
user_config = _load_user_config(user_config_path)
|
user_config = _load_user_config(user_config_path)
|
||||||
|
|
||||||
@@ -190,17 +154,10 @@ def handle_config(args, ctx: CLIContext) -> None:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
to_modify = resolve_repos(
|
to_modify = resolve_repos(args.identifiers, user_config.get("repositories", []))
|
||||||
args.identifiers,
|
|
||||||
user_config.get("repositories", []),
|
|
||||||
)
|
|
||||||
|
|
||||||
for entry in user_config["repositories"]:
|
for entry in user_config["repositories"]:
|
||||||
key = (
|
key = (entry.get("provider"), entry.get("account"), entry.get("repository"))
|
||||||
entry.get("provider"),
|
|
||||||
entry.get("account"),
|
|
||||||
entry.get("repository"),
|
|
||||||
)
|
|
||||||
for mod in to_modify:
|
for mod in to_modify:
|
||||||
mod_key = (
|
mod_key = (
|
||||||
mod.get("provider"),
|
mod.get("provider"),
|
||||||
@@ -214,21 +171,9 @@ def handle_config(args, ctx: CLIContext) -> None:
|
|||||||
save_user_config(user_config, user_config_path)
|
save_user_config(user_config, user_config_path)
|
||||||
return
|
return
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# config update
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
if args.subcommand == "update":
|
if args.subcommand == "update":
|
||||||
"""
|
|
||||||
Copy default YAML configs from the installed package into the
|
|
||||||
user's ~/.config/pkgmgr directory.
|
|
||||||
|
|
||||||
This will overwrite files with the same name (except config.yaml).
|
|
||||||
"""
|
|
||||||
_update_default_configs(user_config_path)
|
_update_default_configs(user_config_path)
|
||||||
return
|
return
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# Unknown subcommand
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
print(f"Unknown config subcommand: {args.subcommand}")
|
print(f"Unknown config subcommand: {args.subcommand}")
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# src/pkgmgr/core/config/load.py
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
@@ -7,29 +8,28 @@ Load and merge pkgmgr configuration.
|
|||||||
Layering rules:
|
Layering rules:
|
||||||
|
|
||||||
1. Defaults / category files:
|
1. Defaults / category files:
|
||||||
- Zuerst werden alle *.yml/*.yaml (außer config.yaml) im
|
- First load all *.yml/*.yaml (except config.yaml) from the user directory:
|
||||||
Benutzerverzeichnis geladen:
|
|
||||||
~/.config/pkgmgr/
|
~/.config/pkgmgr/
|
||||||
|
|
||||||
- Falls dort keine passenden Dateien existieren, wird auf die im
|
- If no matching files exist there, fall back to defaults shipped with pkgmgr:
|
||||||
Paket / Projekt mitgelieferten Config-Verzeichnisse zurückgegriffen:
|
|
||||||
|
|
||||||
<pkg_root>/config
|
<pkg_root>/config
|
||||||
<project_root>/config
|
|
||||||
|
|
||||||
Dabei werden ebenfalls alle *.yml/*.yaml als Layer geladen.
|
During development (src-layout), we optionally also check:
|
||||||
|
<repo_root>/config
|
||||||
|
|
||||||
- Der Dateiname ohne Endung (stem) wird als Kategorie-Name
|
All *.yml/*.yaml files are loaded as layers.
|
||||||
verwendet und in repo["category_files"] eingetragen.
|
|
||||||
|
- The filename stem is used as category name and stored in repo["category_files"].
|
||||||
|
|
||||||
2. User config:
|
2. User config:
|
||||||
- ~/.config/pkgmgr/config.yaml (oder der übergebene Pfad)
|
- ~/.config/pkgmgr/config.yaml (or the provided path)
|
||||||
wird geladen und PER LISTEN-MERGE über die Defaults gelegt:
|
is loaded and merged over defaults:
|
||||||
- directories: dict deep-merge
|
- directories: dict deep-merge
|
||||||
- repositories: per _merge_repo_lists (kein Löschen!)
|
- repositories: per _merge_repo_lists (no deletions!)
|
||||||
|
|
||||||
3. Ergebnis:
|
3. Result:
|
||||||
- Ein dict mit mindestens:
|
- A dict with at least:
|
||||||
config["directories"] (dict)
|
config["directories"] (dict)
|
||||||
config["repositories"] (list[dict])
|
config["repositories"] (list[dict])
|
||||||
"""
|
"""
|
||||||
@@ -38,7 +38,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Tuple, Optional
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ Repo = Dict[str, Any]
|
|||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Hilfsfunktionen
|
# Helper functions
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -83,17 +83,16 @@ def _merge_repo_lists(
|
|||||||
"""
|
"""
|
||||||
Merge two repository lists, matching by (provider, account, repository).
|
Merge two repository lists, matching by (provider, account, repository).
|
||||||
|
|
||||||
- Wenn ein Repo aus new_list noch nicht existiert, wird es hinzugefügt.
|
- If a repo from new_list does not exist, it is added.
|
||||||
- Wenn es existiert, werden seine Felder per Deep-Merge überschrieben.
|
- If it exists, its fields are deep-merged (override wins).
|
||||||
- Wenn category_name gesetzt ist, wird dieser in
|
- If category_name is set, it is appended to repo["category_files"].
|
||||||
repo["category_files"] eingetragen.
|
|
||||||
"""
|
"""
|
||||||
index: Dict[Tuple[str, str, str], Repo] = {_repo_key(r): r for r in base_list}
|
index: Dict[Tuple[str, str, str], Repo] = {_repo_key(r): r for r in base_list}
|
||||||
|
|
||||||
for src in new_list:
|
for src in new_list:
|
||||||
key = _repo_key(src)
|
key = _repo_key(src)
|
||||||
if key == ("", "", ""):
|
if key == ("", "", ""):
|
||||||
# Unvollständiger Schlüssel -> einfach anhängen
|
# Incomplete key -> append as-is
|
||||||
dst = dict(src)
|
dst = dict(src)
|
||||||
if category_name:
|
if category_name:
|
||||||
dst.setdefault("category_files", [])
|
dst.setdefault("category_files", [])
|
||||||
@@ -141,10 +140,9 @@ def _load_layer_dir(
|
|||||||
"""
|
"""
|
||||||
Load all *.yml/*.yaml from a directory as layered defaults.
|
Load all *.yml/*.yaml from a directory as layered defaults.
|
||||||
|
|
||||||
- skip_filename: Dateiname (z.B. "config.yaml"), der ignoriert
|
- skip_filename: filename (e.g. "config.yaml") to ignore.
|
||||||
werden soll (z.B. User-Config).
|
|
||||||
|
|
||||||
Rückgabe:
|
Returns:
|
||||||
{
|
{
|
||||||
"directories": {...},
|
"directories": {...},
|
||||||
"repositories": [...],
|
"repositories": [...],
|
||||||
@@ -169,7 +167,7 @@ def _load_layer_dir(
|
|||||||
|
|
||||||
for path in yaml_files:
|
for path in yaml_files:
|
||||||
data = _load_yaml_file(path)
|
data = _load_yaml_file(path)
|
||||||
category_name = path.stem # Dateiname ohne .yml/.yaml
|
category_name = path.stem
|
||||||
|
|
||||||
dirs = data.get("directories")
|
dirs = data.get("directories")
|
||||||
if isinstance(dirs, dict):
|
if isinstance(dirs, dict):
|
||||||
@@ -190,8 +188,11 @@ def _load_layer_dir(
|
|||||||
|
|
||||||
def _load_defaults_from_package_or_project() -> Dict[str, Any]:
|
def _load_defaults_from_package_or_project() -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Fallback: load default configs from various possible install or development
|
Fallback: load default configs from possible install or dev layouts.
|
||||||
layouts (pip-installed, editable install, source repo with src/ layout).
|
|
||||||
|
Supported locations:
|
||||||
|
- <pkg_root>/config (installed wheel / editable)
|
||||||
|
- <repo_root>/config (optional dev fallback when pkg_root is src/pkgmgr)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
import pkgmgr # type: ignore
|
import pkgmgr # type: ignore
|
||||||
@@ -199,24 +200,16 @@ def _load_defaults_from_package_or_project() -> Dict[str, Any]:
|
|||||||
return {"directories": {}, "repositories": []}
|
return {"directories": {}, "repositories": []}
|
||||||
|
|
||||||
pkg_root = Path(pkgmgr.__file__).resolve().parent
|
pkg_root = Path(pkgmgr.__file__).resolve().parent
|
||||||
roots = set()
|
candidates: List[Path] = []
|
||||||
|
|
||||||
# Case 1: installed package (site-packages/pkgmgr)
|
# Always prefer package-internal config dir
|
||||||
roots.add(pkg_root)
|
candidates.append(pkg_root / "config")
|
||||||
|
|
||||||
# Case 2: parent directory (site-packages/, src/)
|
# Dev fallback: repo_root/src/pkgmgr -> repo_root/config
|
||||||
roots.add(pkg_root.parent)
|
|
||||||
|
|
||||||
# Case 3: src-layout during development:
|
|
||||||
# repo_root/src/pkgmgr -> repo_root
|
|
||||||
parent = pkg_root.parent
|
parent = pkg_root.parent
|
||||||
if parent.name == "src":
|
if parent.name == "src":
|
||||||
roots.add(parent.parent)
|
repo_root = parent.parent
|
||||||
|
candidates.append(repo_root / "config")
|
||||||
# Candidate config dirs
|
|
||||||
candidates = []
|
|
||||||
for root in roots:
|
|
||||||
candidates.append(root / "config")
|
|
||||||
|
|
||||||
for cand in candidates:
|
for cand in candidates:
|
||||||
defaults = _load_layer_dir(cand, skip_filename=None)
|
defaults = _load_layer_dir(cand, skip_filename=None)
|
||||||
@@ -227,7 +220,7 @@ def _load_defaults_from_package_or_project() -> Dict[str, Any]:
|
|||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Hauptfunktion
|
# Public API
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -235,53 +228,49 @@ def load_config(user_config_path: str) -> Dict[str, Any]:
|
|||||||
"""
|
"""
|
||||||
Load and merge configuration for pkgmgr.
|
Load and merge configuration for pkgmgr.
|
||||||
|
|
||||||
Schritte:
|
Steps:
|
||||||
1. Ermittle ~/.config/pkgmgr/ (oder das Verzeichnis von user_config_path).
|
1. Determine ~/.config/pkgmgr/ (or dir of user_config_path).
|
||||||
2. Lade alle *.yml/*.yaml dort (außer der User-Config selbst) als
|
2. Load all *.yml/*.yaml in that dir (except the user config file) as defaults.
|
||||||
Defaults / Kategorie-Layer.
|
3. If nothing found, fall back to package defaults.
|
||||||
3. Wenn dort nichts gefunden wurde, Fallback auf Paket/Projekt.
|
4. Load the user config file (if present).
|
||||||
4. Lade die User-Config-Datei selbst (falls vorhanden).
|
|
||||||
5. Merge:
|
5. Merge:
|
||||||
- directories: deep-merge (Defaults <- User)
|
- directories: deep-merge (defaults <- user)
|
||||||
- repositories: _merge_repo_lists (Defaults <- User)
|
- repositories: _merge_repo_lists (defaults <- user)
|
||||||
"""
|
"""
|
||||||
user_config_path_expanded = os.path.expanduser(user_config_path)
|
user_config_path_expanded = os.path.expanduser(user_config_path)
|
||||||
user_cfg_path = Path(user_config_path_expanded)
|
user_cfg_path = Path(user_config_path_expanded)
|
||||||
|
|
||||||
config_dir = user_cfg_path.parent
|
config_dir = user_cfg_path.parent
|
||||||
if not str(config_dir):
|
if not str(config_dir):
|
||||||
# Fallback, falls jemand nur "config.yaml" übergibt
|
|
||||||
config_dir = Path(os.path.expanduser("~/.config/pkgmgr"))
|
config_dir = Path(os.path.expanduser("~/.config/pkgmgr"))
|
||||||
config_dir.mkdir(parents=True, exist_ok=True)
|
config_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
user_cfg_name = user_cfg_path.name
|
user_cfg_name = user_cfg_path.name
|
||||||
|
|
||||||
# 1+2) Defaults / Kategorie-Layer aus dem User-Verzeichnis
|
# 1+2) Defaults from user directory
|
||||||
defaults = _load_layer_dir(config_dir, skip_filename=user_cfg_name)
|
defaults = _load_layer_dir(config_dir, skip_filename=user_cfg_name)
|
||||||
|
|
||||||
# 3) Falls dort nichts gefunden wurde, Fallback auf Paket/Projekt
|
# 3) Fallback to package defaults
|
||||||
if not defaults["directories"] and not defaults["repositories"]:
|
if not defaults["directories"] and not defaults["repositories"]:
|
||||||
defaults = _load_defaults_from_package_or_project()
|
defaults = _load_defaults_from_package_or_project()
|
||||||
|
|
||||||
defaults.setdefault("directories", {})
|
defaults.setdefault("directories", {})
|
||||||
defaults.setdefault("repositories", [])
|
defaults.setdefault("repositories", [])
|
||||||
|
|
||||||
# 4) User-Config
|
# 4) User config
|
||||||
user_cfg: Dict[str, Any] = {}
|
user_cfg: Dict[str, Any] = {}
|
||||||
if user_cfg_path.is_file():
|
if user_cfg_path.is_file():
|
||||||
user_cfg = _load_yaml_file(user_cfg_path)
|
user_cfg = _load_yaml_file(user_cfg_path)
|
||||||
user_cfg.setdefault("directories", {})
|
user_cfg.setdefault("directories", {})
|
||||||
user_cfg.setdefault("repositories", [])
|
user_cfg.setdefault("repositories", [])
|
||||||
|
|
||||||
# 5) Merge: directories deep-merge, repositories listen-merge
|
# 5) Merge
|
||||||
merged: Dict[str, Any] = {}
|
merged: Dict[str, Any] = {}
|
||||||
|
|
||||||
# directories
|
|
||||||
merged["directories"] = {}
|
merged["directories"] = {}
|
||||||
_deep_merge(merged["directories"], defaults["directories"])
|
_deep_merge(merged["directories"], defaults["directories"])
|
||||||
_deep_merge(merged["directories"], user_cfg["directories"])
|
_deep_merge(merged["directories"], user_cfg["directories"])
|
||||||
|
|
||||||
# repositories
|
|
||||||
merged["repositories"] = []
|
merged["repositories"] = []
|
||||||
_merge_repo_lists(
|
_merge_repo_lists(
|
||||||
merged["repositories"], defaults["repositories"], category_name=None
|
merged["repositories"], defaults["repositories"], category_name=None
|
||||||
@@ -290,7 +279,7 @@ def load_config(user_config_path: str) -> Dict[str, Any]:
|
|||||||
merged["repositories"], user_cfg["repositories"], category_name=None
|
merged["repositories"], user_cfg["repositories"], category_name=None
|
||||||
)
|
)
|
||||||
|
|
||||||
# andere Top-Level-Keys (falls vorhanden)
|
# Merge other top-level keys
|
||||||
other_keys = (set(defaults.keys()) | set(user_cfg.keys())) - {
|
other_keys = (set(defaults.keys()) | set(user_cfg.keys())) - {
|
||||||
"directories",
|
"directories",
|
||||||
"repositories",
|
"repositories",
|
||||||
|
|||||||
@@ -29,7 +29,11 @@ def pull_args(
|
|||||||
try:
|
try:
|
||||||
run(["pull", *extra], cwd=cwd, preview=preview)
|
run(["pull", *extra], cwd=cwd, preview=preview)
|
||||||
except GitRunError as exc:
|
except GitRunError as exc:
|
||||||
|
details = getattr(exc, "output", None) or getattr(exc, "stderr", None) or ""
|
||||||
raise GitPullArgsError(
|
raise GitPullArgsError(
|
||||||
f"Failed to run `git pull` with args={extra!r}.",
|
(
|
||||||
|
f"Failed to run `git pull` with args={extra!r} "
|
||||||
|
f"in cwd={cwd!r}.\n{details}"
|
||||||
|
).rstrip(),
|
||||||
cwd=cwd,
|
cwd=cwd,
|
||||||
) from exc
|
) from exc
|
||||||
|
|||||||
@@ -20,13 +20,11 @@ class ConfigDefaultsIntegrationTest(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
Integration test:
|
Integration test:
|
||||||
- Create a temp "site-packages/pkgmgr" fake install root
|
- Create a temp "site-packages/pkgmgr" fake install root
|
||||||
- Put defaults under "<project_root>/config/defaults.yaml"
|
- Put defaults under "<pkg_root>/config/defaults.yaml"
|
||||||
where project_root == pkg_root.parent (as per your current logic)
|
|
||||||
- Verify:
|
- Verify:
|
||||||
A) load_config() picks up defaults from that config folder when user dir has no defaults
|
A) load_config() picks up defaults from that config folder when user dir has no defaults
|
||||||
B) _update_default_configs() copies defaults.yaml into ~/.config/pkgmgr/
|
B) _update_default_configs() copies defaults.yaml into ~/.config/pkgmgr/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as td:
|
with tempfile.TemporaryDirectory() as td:
|
||||||
root = Path(td)
|
root = Path(td)
|
||||||
|
|
||||||
@@ -44,15 +42,12 @@ class ConfigDefaultsIntegrationTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Fake pkg install layout:
|
# Fake pkg install layout:
|
||||||
# pkg_root = <root>/site-packages/pkgmgr
|
# pkg_root = <root>/site-packages/pkgmgr
|
||||||
# project_root = pkg_root.parent = <root>/site-packages
|
|
||||||
site_packages = root / "site-packages"
|
site_packages = root / "site-packages"
|
||||||
pkg_root = site_packages / "pkgmgr"
|
pkg_root = site_packages / "pkgmgr"
|
||||||
pkg_root.mkdir(parents=True)
|
pkg_root.mkdir(parents=True)
|
||||||
|
|
||||||
# This is the "project_root/config" candidate for both:
|
# defaults live inside the package now: <pkg_root>/config/defaults.yaml
|
||||||
# - load.py: roots include pkg_root.parent -> site-packages, so it checks site-packages/config
|
config_dir = pkg_root / "config"
|
||||||
# - cli/config.py: project_root == pkg_root.parent -> site-packages, so it checks site-packages/config
|
|
||||||
config_dir = site_packages / "config"
|
|
||||||
config_dir.mkdir(parents=True)
|
config_dir.mkdir(parents=True)
|
||||||
|
|
||||||
defaults_payload = {
|
defaults_payload = {
|
||||||
@@ -74,7 +69,7 @@ class ConfigDefaultsIntegrationTest(unittest.TestCase):
|
|||||||
|
|
||||||
with patch.dict(sys.modules, {"pkgmgr": fake_pkgmgr}):
|
with patch.dict(sys.modules, {"pkgmgr": fake_pkgmgr}):
|
||||||
with patch.dict(os.environ, {"HOME": str(home)}):
|
with patch.dict(os.environ, {"HOME": str(home)}):
|
||||||
# A) load_config should fall back to site-packages/config/defaults.yaml
|
# A) load_config should fall back to <pkg_root>/config/defaults.yaml
|
||||||
merged = load_config(user_config_path)
|
merged = load_config(user_config_path)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -98,7 +93,6 @@ class ConfigDefaultsIntegrationTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# B) update_default_configs should copy defaults.yaml to ~/.config/pkgmgr/
|
# B) update_default_configs should copy defaults.yaml to ~/.config/pkgmgr/
|
||||||
# (and should not overwrite config.yaml)
|
|
||||||
before_config_yaml = (user_cfg_dir / "config.yaml").read_text(
|
before_config_yaml = (user_cfg_dir / "config.yaml").read_text(
|
||||||
encoding="utf-8"
|
encoding="utf-8"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user