Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f950bb493c | ||
|
|
fb0b81954d | ||
|
|
b9b4c3fa59 |
@@ -1,3 +1,8 @@
|
|||||||
|
## [1.3.1] - 2025-12-12
|
||||||
|
|
||||||
|
* Updated documentation with better run and installation instructions
|
||||||
|
|
||||||
|
|
||||||
## [1.3.0] - 2025-12-12
|
## [1.3.0] - 2025-12-12
|
||||||
|
|
||||||
* **Minor release – Stability & CI hardening**
|
* **Minor release – Stability & CI hardening**
|
||||||
|
|||||||
100
README.md
100
README.md
@@ -8,8 +8,9 @@
|
|||||||
[](https://s.veen.world/paypaldonate)
|
[](https://s.veen.world/paypaldonate)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://github.com/kevinveenbirkenbach/package-manager)
|
[](https://github.com/kevinveenbirkenbach/package-manager)
|
||||||
|
[](https://github.com/kevinveenbirkenbach/package-manager/actions/workflows/mark-stable.yml)
|
||||||
|
|
||||||
**Kevin's Package Manager (PKGMGR)** is a *multi-distro* package manager and workflow orchestrator.
|
[**Kevin's Package Manager (PKGMGR)**](https://s.veen.world/pkgmgr) is a *multi-distro* package manager and workflow orchestrator.
|
||||||
It helps you **develop, package, release and manage projects across multiple Linux-based
|
It helps you **develop, package, release and manage projects across multiple Linux-based
|
||||||
operating systems** (Arch, Debian, Ubuntu, Fedora, CentOS, …).
|
operating systems** (Arch, Debian, Ubuntu, Fedora, CentOS, …).
|
||||||
|
|
||||||
@@ -103,50 +104,75 @@ The following diagram gives a full overview of:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
Perfekt, dann hier die **noch kompaktere und korrekt differenzierte Version**, die **nur** zwischen
|
||||||
|
**`make setup`** und **`make setup-venv`** unterscheidet und exakt deinem Verhalten entspricht.
|
||||||
|
|
||||||
|
README-ready, ohne Over-Engineering.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Installation ⚙️
|
## Installation ⚙️
|
||||||
|
|
||||||
### 1. Get the latest stable version
|
PKGMGR can be installed using `make`.
|
||||||
|
The setup mode defines **which runtime layers are prepared**.
|
||||||
|
|
||||||
For a stable setup, use the **latest tagged release** (the tag pointed to by
|
---
|
||||||
`latest`):
|
|
||||||
|
### Setup modes
|
||||||
|
|
||||||
|
| Command | Prepares | Use case |
|
||||||
|
| ------------------- | ----------------------- | --------------------- |
|
||||||
|
| **make setup** | Python venv **and** Nix | Full development & CI |
|
||||||
|
| **make setup-venv** | Python venv only | Local user setup |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Install & setup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/kevinveenbirkenbach/package-manager.git
|
git clone https://github.com/kevinveenbirkenbach/package-manager.git
|
||||||
cd package-manager
|
cd package-manager
|
||||||
|
|
||||||
# Optional but recommended: checkout the latest stable tag
|
|
||||||
git fetch --tags
|
|
||||||
git checkout "$(git describe --tags --abbrev=0)"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Install via Make
|
|
||||||
|
|
||||||
The project ships with a Makefile that encapsulates the typical installation
|
|
||||||
flow. On most systems you only need:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Ensure make, Python and pip are installed via your distro package manager
|
|
||||||
# (e.g. pacman -S make python python-pip, apt install make python3-pip, ...)
|
|
||||||
|
|
||||||
make install
|
make install
|
||||||
```
|
```
|
||||||
|
|
||||||
This will:
|
#### Full setup (venv + Nix)
|
||||||
|
|
||||||
* create or reuse a Python virtual environment,
|
|
||||||
* install PKGMGR (and its Python dependencies) into that environment,
|
|
||||||
* expose the `pkgmgr` executable on your PATH (usually via `~/.local/bin`),
|
|
||||||
* prepare Nix-based integration where available so PKGMGR can build and manage
|
|
||||||
packages distribution-independently.
|
|
||||||
|
|
||||||
For development use, you can also run:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make setup
|
make setup
|
||||||
```
|
```
|
||||||
|
|
||||||
which prepares the environment and leaves you with a fully wired development
|
Use this for CI, servers, containers and full development workflows.
|
||||||
workspace (including Nix, tests and scripts).
|
|
||||||
|
#### Venv-only setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make setup-venv
|
||||||
|
source ~/.venvs/pkgmgr/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
Use this if you want PKGMGR isolated without Nix integration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Run without installation (Nix)
|
||||||
|
|
||||||
|
Run PKGMGR directly via Nix Flakes.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix run github:kevinveenbirkenbach/package-manager#pkgmgr -- --help
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix run github:kevinveenbirkenbach/package-manager#pkgmgr -- version pkgmgr
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
* full flake URL required
|
||||||
|
* `--` separates Nix and PKGMGR arguments
|
||||||
|
* can be used alongside any setup mode
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -158,21 +184,9 @@ After installation, the main entry point is:
|
|||||||
pkgmgr --help
|
pkgmgr --help
|
||||||
```
|
```
|
||||||
|
|
||||||
This prints a list of all available subcommands, for example:
|
This prints a list of all available subcommands.
|
||||||
|
|
||||||
* `pkgmgr list --all` – show all repositories in the config
|
|
||||||
* `pkgmgr update --all --clone-mode https` – update every repository
|
|
||||||
* `pkgmgr release patch --preview` – simulate a patch release
|
|
||||||
* `pkgmgr version --all` – show version information for all repositories
|
|
||||||
* `pkgmgr mirror setup --preview --all` – prepare Git mirrors (no changes in preview)
|
|
||||||
* `pkgmgr make install --preview pkgmgr` – preview make install for the pkgmgr repo
|
|
||||||
|
|
||||||
The help for each command is available via:
|
The help for each command is available via:
|
||||||
|
|
||||||
```bash
|
|
||||||
pkgmgr <command> --help
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## License 📄
|
## License 📄
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
rec {
|
rec {
|
||||||
pkgmgr = pyPkgs.buildPythonApplication {
|
pkgmgr = pyPkgs.buildPythonApplication {
|
||||||
pname = "package-manager";
|
pname = "package-manager";
|
||||||
version = "1.3.0";
|
version = "1.3.1";
|
||||||
|
|
||||||
# Use the git repo as source
|
# Use the git repo as source
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
|||||||
@@ -50,9 +50,10 @@ package() {
|
|||||||
install -Dm0755 "scripts/pkgmgr-wrapper.sh" \
|
install -Dm0755 "scripts/pkgmgr-wrapper.sh" \
|
||||||
"$pkgdir/usr/bin/pkgmgr"
|
"$pkgdir/usr/bin/pkgmgr"
|
||||||
|
|
||||||
# Install Nix init helper
|
# Install Nix bootstrap (init + lib)
|
||||||
install -Dm0755 "scripts/init-nix.sh" \
|
install -d "$pkgdir/usr/lib/package-manager/nix"
|
||||||
"$pkgdir/usr/lib/package-manager/init-nix.sh"
|
cp -a scripts/nix/* "$pkgdir/usr/lib/package-manager/nix/"
|
||||||
|
chmod 0755 "$pkgdir/usr/lib/package-manager/nix/init.sh"
|
||||||
|
|
||||||
# Install the full repository into /usr/lib/package-manager
|
# Install the full repository into /usr/lib/package-manager
|
||||||
mkdir -p "$pkgdir/usr/lib/package-manager"
|
mkdir -p "$pkgdir/usr/lib/package-manager"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
post_install() {
|
post_install() {
|
||||||
/usr/lib/package-manager/init-nix.sh || echo ">>> ERROR: /usr/lib/package-manager/init-nix.sh not found or not executable."
|
/usr/lib/package-manager/nix/init.sh || echo ">>> ERROR: /usr/lib/package-manager/nix/init.sh not found or not executable."
|
||||||
}
|
}
|
||||||
|
|
||||||
post_upgrade() {
|
post_upgrade() {
|
||||||
/usr/lib/package-manager/init-nix.sh || echo ">>> ERROR: /usr/lib/package-manager/init-nix.sh not found or not executable."
|
/usr/lib/package-manager/nix/init.sh || echo ">>> ERROR: /usr/lib/package-manager/nix/init.sh not found or not executable."
|
||||||
}
|
}
|
||||||
|
|
||||||
post_remove() {
|
post_remove() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ set -e
|
|||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
configure)
|
configure)
|
||||||
/usr/lib/package-manager/init-nix.sh || echo ">>> ERROR: /usr/lib/package-manager/init-nix.sh not found or not executable."
|
/usr/lib/package-manager/nix/init.sh || echo ">>> ERROR: /usr/lib/package-manager/nix/init.sh not found or not executable."
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ override_dh_auto_test:
|
|||||||
:
|
:
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Install phase: copy wrapper + init script + full project source
|
# Install phase: copy wrapper + Nix bootstrap (init + lib) + full project source
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
override_dh_auto_install:
|
override_dh_auto_install:
|
||||||
# Create target directories
|
# Create target directories
|
||||||
@@ -31,9 +31,11 @@ override_dh_auto_install:
|
|||||||
install -m0755 scripts/pkgmgr-wrapper.sh \
|
install -m0755 scripts/pkgmgr-wrapper.sh \
|
||||||
debian/package-manager/usr/bin/pkgmgr
|
debian/package-manager/usr/bin/pkgmgr
|
||||||
|
|
||||||
# Install shared Nix init script
|
# Install Nix bootstrap (init + lib)
|
||||||
install -m0755 scripts/init-nix.sh \
|
install -d debian/package-manager/usr/lib/package-manager/nix
|
||||||
debian/package-manager/usr/lib/package-manager/init-nix.sh
|
cp -a scripts/nix/* \
|
||||||
|
debian/package-manager/usr/lib/package-manager/nix/
|
||||||
|
chmod 0755 debian/package-manager/usr/lib/package-manager/nix/init.sh
|
||||||
|
|
||||||
# Copy full project source into /usr/lib/package-manager,
|
# Copy full project source into /usr/lib/package-manager,
|
||||||
# but do not include the debian/ directory itself.
|
# but do not include the debian/ directory itself.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ BuildArch: noarch
|
|||||||
# NOTE:
|
# NOTE:
|
||||||
# Nix is a runtime requirement, but it is *not* declared here as a hard
|
# Nix is a runtime requirement, but it is *not* declared here as a hard
|
||||||
# RPM dependency, because many distributions do not ship a "nix" RPM.
|
# RPM dependency, because many distributions do not ship a "nix" RPM.
|
||||||
# Instead, Nix is installed and initialized by init-nix.sh, which is
|
# Instead, Nix is installed and initialized by nix/init.sh, which is
|
||||||
# called in the %post scriptlet below.
|
# called in the %post scriptlet below.
|
||||||
|
|
||||||
%description
|
%description
|
||||||
@@ -22,7 +22,7 @@ manager via a local Nix flake:
|
|||||||
nix run /usr/lib/package-manager#pkgmgr -- ...
|
nix run /usr/lib/package-manager#pkgmgr -- ...
|
||||||
|
|
||||||
Nix is a runtime requirement and is installed/initialized by the
|
Nix is a runtime requirement and is installed/initialized by the
|
||||||
init-nix.sh helper during package installation if it is not yet
|
nix/init.sh helper during package installation if it is not yet
|
||||||
available on the system.
|
available on the system.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
@@ -34,8 +34,8 @@ available on the system.
|
|||||||
|
|
||||||
%install
|
%install
|
||||||
rm -rf %{buildroot}
|
rm -rf %{buildroot}
|
||||||
|
|
||||||
install -d %{buildroot}%{_bindir}
|
install -d %{buildroot}%{_bindir}
|
||||||
# Install project tree into a fixed, architecture-independent location.
|
|
||||||
install -d %{buildroot}/usr/lib/package-manager
|
install -d %{buildroot}/usr/lib/package-manager
|
||||||
|
|
||||||
# Copy full project source into /usr/lib/package-manager
|
# Copy full project source into /usr/lib/package-manager
|
||||||
@@ -44,8 +44,10 @@ cp -a . %{buildroot}/usr/lib/package-manager/
|
|||||||
# Wrapper
|
# Wrapper
|
||||||
install -m0755 scripts/pkgmgr-wrapper.sh %{buildroot}%{_bindir}/pkgmgr
|
install -m0755 scripts/pkgmgr-wrapper.sh %{buildroot}%{_bindir}/pkgmgr
|
||||||
|
|
||||||
# Shared Nix init script (ensure it is executable in the installed tree)
|
# Nix bootstrap (init + lib)
|
||||||
install -m0755 scripts/init-nix.sh %{buildroot}/usr/lib/package-manager/init-nix.sh
|
install -d %{buildroot}/usr/lib/package-manager/nix
|
||||||
|
cp -a scripts/nix/* %{buildroot}/usr/lib/package-manager/nix/
|
||||||
|
chmod 0755 %{buildroot}/usr/lib/package-manager/nix/init.sh
|
||||||
|
|
||||||
# Remove packaging-only and development artefacts from the installed tree
|
# Remove packaging-only and development artefacts from the installed tree
|
||||||
rm -rf \
|
rm -rf \
|
||||||
@@ -60,7 +62,7 @@ rm -rf \
|
|||||||
%{buildroot}/usr/lib/package-manager/.gitkeep || true
|
%{buildroot}/usr/lib/package-manager/.gitkeep || true
|
||||||
|
|
||||||
%post
|
%post
|
||||||
/usr/lib/package-manager/init-nix.sh || echo ">>> ERROR: /usr/lib/package-manager/init-nix.sh not found or not executable."
|
/usr/lib/package-manager/nix/init.sh || echo ">>> ERROR: /usr/lib/package-manager/nix/init.sh not found or not executable."
|
||||||
|
|
||||||
%postun
|
%postun
|
||||||
echo ">>> package-manager removed. Nix itself was not removed."
|
echo ">>> package-manager removed. Nix itself was not removed."
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "package-manager"
|
name = "package-manager"
|
||||||
version = "1.3.0"
|
version = "1.3.1"
|
||||||
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"
|
||||||
|
|||||||
@@ -1,385 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
echo "[init-nix] Starting Nix initialization..."
|
|
||||||
|
|
||||||
NIX_INSTALL_URL="${NIX_INSTALL_URL:-https://nixos.org/nix/install}"
|
|
||||||
NIX_DOWNLOAD_MAX_TIME="${NIX_DOWNLOAD_MAX_TIME:-300}"
|
|
||||||
NIX_DOWNLOAD_SLEEP_INTERVAL="${NIX_DOWNLOAD_SLEEP_INTERVAL:-20}"
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Detect whether we are inside a container (Docker/Podman/etc.)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
is_container() {
|
|
||||||
[[ -f /.dockerenv || -f /run/.containerenv ]] && return 0
|
|
||||||
grep -qiE 'docker|container|podman|lxc' /proc/1/cgroup 2>/dev/null && return 0
|
|
||||||
[[ -n "${container:-}" ]] && return 0
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Ensure Nix binaries are on PATH (additive, never destructive)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
ensure_nix_on_path() {
|
|
||||||
if [[ -x /nix/var/nix/profiles/default/bin/nix ]]; then
|
|
||||||
PATH="/nix/var/nix/profiles/default/bin:$PATH"
|
|
||||||
fi
|
|
||||||
if [[ -x "$HOME/.nix-profile/bin/nix" ]]; then
|
|
||||||
PATH="$HOME/.nix-profile/bin:$PATH"
|
|
||||||
fi
|
|
||||||
if [[ -x /home/nix/.nix-profile/bin/nix ]]; then
|
|
||||||
PATH="/home/nix/.nix-profile/bin:$PATH"
|
|
||||||
fi
|
|
||||||
if [[ -d "$HOME/.local/bin" ]]; then
|
|
||||||
PATH="$HOME/.local/bin:$PATH"
|
|
||||||
fi
|
|
||||||
export PATH
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Resolve a path to a real executable (follows symlinks)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
real_exe() {
|
|
||||||
local p="${1:-}"
|
|
||||||
[[ -z "$p" ]] && return 1
|
|
||||||
|
|
||||||
local r
|
|
||||||
r="$(readlink -f "$p" 2>/dev/null || echo "$p")"
|
|
||||||
|
|
||||||
[[ -x "$r" ]] && { echo "$r"; return 0; }
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Resolve nix binary path robustly (works across distros + Arch /usr/sbin)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
resolve_nix_bin() {
|
|
||||||
local nix_cmd=""
|
|
||||||
nix_cmd="$(command -v nix 2>/dev/null || true)"
|
|
||||||
[[ -n "$nix_cmd" ]] && real_exe "$nix_cmd" && return 0
|
|
||||||
|
|
||||||
# IMPORTANT: prefer system locations before /usr/local to avoid self-symlink traps
|
|
||||||
[[ -x /usr/sbin/nix ]] && { echo "/usr/sbin/nix"; return 0; } # Arch package can land here
|
|
||||||
[[ -x /usr/bin/nix ]] && { echo "/usr/bin/nix"; return 0; }
|
|
||||||
[[ -x /bin/nix ]] && { echo "/bin/nix"; return 0; }
|
|
||||||
|
|
||||||
# /usr/local last, and only if it resolves to a real executable
|
|
||||||
[[ -e /usr/local/bin/nix ]] && real_exe "/usr/local/bin/nix" && return 0
|
|
||||||
|
|
||||||
[[ -x /nix/var/nix/profiles/default/bin/nix ]] && {
|
|
||||||
echo "/nix/var/nix/profiles/default/bin/nix"; return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[ -x "$HOME/.nix-profile/bin/nix" ]] && {
|
|
||||||
echo "$HOME/.nix-profile/bin/nix"; return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[ -x "$HOME/.local/bin/nix" ]] && {
|
|
||||||
echo "$HOME/.local/bin/nix"; return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[ -x /home/nix/.nix-profile/bin/nix ]] && {
|
|
||||||
echo "/home/nix/.nix-profile/bin/nix"; return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Ensure globally reachable nix symlink(s) (CI / non-login shells) - root only
|
|
||||||
#
|
|
||||||
# Key rule:
|
|
||||||
# - Never overwrite distro-managed nix locations (Arch may ship nix in /usr/sbin).
|
|
||||||
# - But for sudo secure_path (CentOS), /usr/local/bin is often NOT included.
|
|
||||||
# Therefore: also create /usr/bin/nix (and /usr/sbin/nix) ONLY if they do not exist.
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
ensure_global_nix_symlinks() {
|
|
||||||
local nix_bin="${1:-}"
|
|
||||||
|
|
||||||
[[ -z "$nix_bin" ]] && nix_bin="$(resolve_nix_bin 2>/dev/null || true)"
|
|
||||||
|
|
||||||
if [[ -z "$nix_bin" || ! -x "$nix_bin" ]]; then
|
|
||||||
echo "[init-nix] WARNING: nix binary not found, cannot create global symlink(s)."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Always link to the real executable to avoid /usr/local/bin/nix -> /usr/local/bin/nix
|
|
||||||
nix_bin="$(real_exe "$nix_bin" 2>/dev/null || echo "$nix_bin")"
|
|
||||||
|
|
||||||
local targets=()
|
|
||||||
|
|
||||||
# Always provide /usr/local/bin/nix for CI shells
|
|
||||||
mkdir -p /usr/local/bin 2>/dev/null || true
|
|
||||||
targets+=("/usr/local/bin/nix")
|
|
||||||
|
|
||||||
# Provide sudo-friendly locations only if they are NOT present (do not override distro paths)
|
|
||||||
if [[ ! -e /usr/bin/nix ]]; then
|
|
||||||
targets+=("/usr/bin/nix")
|
|
||||||
fi
|
|
||||||
if [[ ! -e /usr/sbin/nix ]]; then
|
|
||||||
targets+=("/usr/sbin/nix")
|
|
||||||
fi
|
|
||||||
|
|
||||||
local target current_real
|
|
||||||
for target in "${targets[@]}"; do
|
|
||||||
current_real=""
|
|
||||||
if [[ -e "$target" ]]; then
|
|
||||||
current_real="$(real_exe "$target" 2>/dev/null || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "$current_real" && "$current_real" == "$nix_bin" ]]; then
|
|
||||||
echo "[init-nix] $target already points to: $nix_bin"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If something exists but is not the same (and we promised not to override), skip.
|
|
||||||
if [[ -e "$target" && "$target" != "/usr/local/bin/nix" ]]; then
|
|
||||||
echo "[init-nix] WARNING: $target exists; not overwriting."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ln -sf "$nix_bin" "$target" 2>/dev/null; then
|
|
||||||
echo "[init-nix] Ensured $target -> $nix_bin"
|
|
||||||
else
|
|
||||||
echo "[init-nix] WARNING: Failed to ensure $target symlink."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Ensure user-level nix symlink (works without root; CI-safe)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
ensure_user_nix_symlink() {
|
|
||||||
local nix_bin="${1:-}"
|
|
||||||
|
|
||||||
[[ -z "$nix_bin" ]] && nix_bin="$(resolve_nix_bin 2>/dev/null || true)"
|
|
||||||
|
|
||||||
if [[ -z "$nix_bin" || ! -x "$nix_bin" ]]; then
|
|
||||||
echo "[init-nix] WARNING: nix binary not found, cannot create user symlink."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
nix_bin="$(real_exe "$nix_bin" 2>/dev/null || echo "$nix_bin")"
|
|
||||||
|
|
||||||
mkdir -p "$HOME/.local/bin" 2>/dev/null || true
|
|
||||||
ln -sf "$nix_bin" "$HOME/.local/bin/nix"
|
|
||||||
|
|
||||||
echo "[init-nix] Ensured $HOME/.local/bin/nix -> $nix_bin"
|
|
||||||
|
|
||||||
PATH="$HOME/.local/bin:$PATH"
|
|
||||||
export PATH
|
|
||||||
|
|
||||||
if [[ -w "$HOME/.profile" ]] && ! grep -q 'init-nix.sh' "$HOME/.profile" 2>/dev/null; then
|
|
||||||
cat >>"$HOME/.profile" <<'EOF'
|
|
||||||
|
|
||||||
# PATH for nix (added by package-manager init-nix.sh)
|
|
||||||
if [ -d "$HOME/.local/bin" ]; then
|
|
||||||
PATH="$HOME/.local/bin:$PATH"
|
|
||||||
fi
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Ensure Nix build group and users exist (build-users-group = nixbld) - root only
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
ensure_nix_build_group() {
|
|
||||||
if ! getent group nixbld >/dev/null 2>&1; then
|
|
||||||
echo "[init-nix] Creating group 'nixbld'..."
|
|
||||||
groupadd -r nixbld
|
|
||||||
fi
|
|
||||||
|
|
||||||
for i in $(seq 1 10); do
|
|
||||||
if ! id "nixbld$i" >/dev/null 2>&1; then
|
|
||||||
echo "[init-nix] Creating build user nixbld$i..."
|
|
||||||
useradd -r -g nixbld -G nixbld -s /usr/sbin/nologin "nixbld$i"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Download and run Nix installer with retry
|
|
||||||
# Usage: install_nix_with_retry daemon|no-daemon [run_as_user]
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
install_nix_with_retry() {
|
|
||||||
local mode="$1"
|
|
||||||
local run_as="${2:-}"
|
|
||||||
local installer elapsed=0 mode_flag
|
|
||||||
|
|
||||||
case "$mode" in
|
|
||||||
daemon) mode_flag="--daemon" ;;
|
|
||||||
no-daemon) mode_flag="--no-daemon" ;;
|
|
||||||
*)
|
|
||||||
echo "[init-nix] ERROR: Invalid mode '$mode' (expected 'daemon' or 'no-daemon')."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
installer="$(mktemp -t nix-installer.XXXXXX)"
|
|
||||||
chmod 0644 "$installer"
|
|
||||||
|
|
||||||
echo "[init-nix] Downloading Nix installer from $NIX_INSTALL_URL (max ${NIX_DOWNLOAD_MAX_TIME}s)..."
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
if curl -fL "$NIX_INSTALL_URL" -o "$installer"; then
|
|
||||||
echo "[init-nix] Successfully downloaded installer to $installer"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
elapsed=$((elapsed + NIX_DOWNLOAD_SLEEP_INTERVAL))
|
|
||||||
echo "[init-nix] WARNING: Download failed. Retrying in ${NIX_DOWNLOAD_SLEEP_INTERVAL}s (elapsed ${elapsed}s)..."
|
|
||||||
|
|
||||||
if (( elapsed >= NIX_DOWNLOAD_MAX_TIME )); then
|
|
||||||
echo "[init-nix] ERROR: Giving up after ${elapsed}s trying to download Nix installer."
|
|
||||||
rm -f "$installer"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep "$NIX_DOWNLOAD_SLEEP_INTERVAL"
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -n "$run_as" ]]; then
|
|
||||||
chown "$run_as:$run_as" "$installer" 2>/dev/null || true
|
|
||||||
echo "[init-nix] Running installer as user '$run_as' ($mode_flag)..."
|
|
||||||
if command -v sudo >/dev/null 2>&1; then
|
|
||||||
sudo -u "$run_as" bash -lc "sh '$installer' $mode_flag"
|
|
||||||
else
|
|
||||||
su - "$run_as" -c "sh '$installer' $mode_flag"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "[init-nix] Running installer as current user ($mode_flag)..."
|
|
||||||
sh "$installer" "$mode_flag"
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f "$installer"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Main
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
main() {
|
|
||||||
# Fast path: already available
|
|
||||||
if command -v nix >/dev/null 2>&1; then
|
|
||||||
echo "[init-nix] Nix already available on PATH: $(command -v nix)"
|
|
||||||
ensure_nix_on_path
|
|
||||||
|
|
||||||
if [[ "${EUID:-0}" -eq 0 ]]; then
|
|
||||||
ensure_global_nix_symlinks "$(resolve_nix_bin 2>/dev/null || true)"
|
|
||||||
else
|
|
||||||
ensure_user_nix_symlink "$(resolve_nix_bin 2>/dev/null || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
ensure_nix_on_path
|
|
||||||
|
|
||||||
if command -v nix >/dev/null 2>&1; then
|
|
||||||
echo "[init-nix] Nix found after PATH adjustment: $(command -v nix)"
|
|
||||||
if [[ "${EUID:-0}" -eq 0 ]]; then
|
|
||||||
ensure_global_nix_symlinks "$(resolve_nix_bin 2>/dev/null || true)"
|
|
||||||
else
|
|
||||||
ensure_user_nix_symlink "$(resolve_nix_bin 2>/dev/null || true)"
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
local IN_CONTAINER=0
|
|
||||||
if is_container; then
|
|
||||||
IN_CONTAINER=1
|
|
||||||
echo "[init-nix] Detected container environment."
|
|
||||||
else
|
|
||||||
echo "[init-nix] No container detected."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
|
||||||
# Container + root: dedicated "nix" user, single-user install
|
|
||||||
# -------------------------------------------------------------------------
|
|
||||||
if [[ "$IN_CONTAINER" -eq 1 && "${EUID:-0}" -eq 0 ]]; then
|
|
||||||
echo "[init-nix] Container + root: installing as 'nix' user (single-user)."
|
|
||||||
|
|
||||||
ensure_nix_build_group
|
|
||||||
|
|
||||||
if ! id nix >/dev/null 2>&1; then
|
|
||||||
echo "[init-nix] Creating user 'nix'..."
|
|
||||||
local BASH_SHELL
|
|
||||||
BASH_SHELL="$(command -v bash || true)"
|
|
||||||
[[ -z "$BASH_SHELL" ]] && BASH_SHELL="/bin/sh"
|
|
||||||
useradd -m -r -g nixbld -s "$BASH_SHELL" nix
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -d /nix ]]; then
|
|
||||||
echo "[init-nix] Creating /nix with owner nix:nixbld..."
|
|
||||||
mkdir -m 0755 /nix
|
|
||||||
chown nix:nixbld /nix
|
|
||||||
else
|
|
||||||
local current_owner current_group
|
|
||||||
current_owner="$(stat -c '%U' /nix 2>/dev/null || echo '?')"
|
|
||||||
current_group="$(stat -c '%G' /nix 2>/dev/null || echo '?')"
|
|
||||||
if [[ "$current_owner" != "nix" || "$current_group" != "nixbld" ]]; then
|
|
||||||
echo "[init-nix] Fixing /nix ownership from $current_owner:$current_group to nix:nixbld..."
|
|
||||||
chown -R nix:nixbld /nix
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
install_nix_with_retry "no-daemon" "nix"
|
|
||||||
|
|
||||||
ensure_nix_on_path
|
|
||||||
|
|
||||||
# Ensure stable global symlink(s) (sudo secure_path friendly)
|
|
||||||
ensure_global_nix_symlinks "/home/nix/.nix-profile/bin/nix"
|
|
||||||
|
|
||||||
# Ensure non-root users can traverse and execute nix user profile
|
|
||||||
if [[ -d /home/nix ]]; then
|
|
||||||
chmod o+rx /home/nix 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
if [[ -d /home/nix/.nix-profile ]]; then
|
|
||||||
chmod -R o+rx /home/nix/.nix-profile 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
|
||||||
# Host (no container)
|
|
||||||
# -------------------------------------------------------------------------
|
|
||||||
else
|
|
||||||
if command -v systemctl >/dev/null 2>&1; then
|
|
||||||
echo "[init-nix] Host with systemd: using multi-user install (--daemon)."
|
|
||||||
if [[ "${EUID:-0}" -eq 0 ]]; then
|
|
||||||
ensure_nix_build_group
|
|
||||||
fi
|
|
||||||
install_nix_with_retry "daemon"
|
|
||||||
else
|
|
||||||
echo "[init-nix] No systemd detected: using single-user install (--no-daemon)."
|
|
||||||
if [[ "${EUID:-0}" -eq 0 ]]; then
|
|
||||||
ensure_nix_build_group
|
|
||||||
fi
|
|
||||||
install_nix_with_retry "no-daemon"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
|
||||||
# After install: PATH + symlink(s)
|
|
||||||
# -------------------------------------------------------------------------
|
|
||||||
ensure_nix_on_path
|
|
||||||
|
|
||||||
local nix_bin_post
|
|
||||||
nix_bin_post="$(resolve_nix_bin 2>/dev/null || true)"
|
|
||||||
|
|
||||||
if [[ "${EUID:-0}" -eq 0 ]]; then
|
|
||||||
ensure_global_nix_symlinks "$nix_bin_post"
|
|
||||||
else
|
|
||||||
ensure_user_nix_symlink "$nix_bin_post"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Final verification (must succeed for CI)
|
|
||||||
if ! command -v nix >/dev/null 2>&1; then
|
|
||||||
echo "[init-nix] ERROR: nix not found after installation."
|
|
||||||
echo "[init-nix] DEBUG: resolved nix path = ${nix_bin_post:-<empty>}"
|
|
||||||
echo "[init-nix] DEBUG: PATH = $PATH"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[init-nix] Nix successfully available at: $(command -v nix)"
|
|
||||||
echo "[init-nix] Nix initialization complete."
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
|
||||||
53
scripts/nix/README.md
Normal file
53
scripts/nix/README.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Nix Bootstrap (package-manager)
|
||||||
|
|
||||||
|
This directory contains the **Nix initialization and bootstrap logic** used by *package-manager* to ensure the `nix` command is available on supported systems (host machines and CI containers).
|
||||||
|
|
||||||
|
It is invoked during package installation (Arch/Debian/Fedora scriptlets) and can also be called manually.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Entry Point
|
||||||
|
|
||||||
|
- *scripts/nix/init.sh*
|
||||||
|
Main bootstrap script. It:
|
||||||
|
- checks whether `nix` is already available
|
||||||
|
- adjusts `PATH` for common Nix locations
|
||||||
|
- installs Nix when missing (daemon install on systemd hosts, single-user in containers)
|
||||||
|
- ensures predictable `nix` availability via symlinks (without overwriting distro-managed paths)
|
||||||
|
- validates that `nix` is usable at the end (CI-safe)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Library Layout
|
||||||
|
|
||||||
|
The entry point sources small, focused modules from *scripts/nix/lib/*:
|
||||||
|
|
||||||
|
- *config.sh* — configuration defaults (installer URL, retry timing)
|
||||||
|
- *detect.sh* — container detection helpers
|
||||||
|
- *path.sh* — PATH adjustments and `nix` binary resolution helpers
|
||||||
|
- *symlinks.sh* — user/global symlink helpers for stable `nix` discovery
|
||||||
|
- *users.sh* — build group/users and container ownership/perms helpers
|
||||||
|
- *install.sh* — installer download + retry logic and execution helpers
|
||||||
|
|
||||||
|
Each library file includes a simple guard to prevent double-sourcing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When It Runs
|
||||||
|
|
||||||
|
This bootstrap is typically executed automatically:
|
||||||
|
|
||||||
|
- Arch: post-install / post-upgrade hook
|
||||||
|
- Debian: `postinst`
|
||||||
|
- Fedora/RPM: `%post`
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes / Design Goals
|
||||||
|
|
||||||
|
- **Cross-distro compatibility:** supports common Linux layouts (including Arch placing `nix` in */usr/sbin*).
|
||||||
|
- **Non-destructive behavior:** avoids overwriting distro-managed `nix` binaries.
|
||||||
|
- **CI robustness:** retry logic for downloads and a final `nix` availability check.
|
||||||
|
- **Container-safe defaults:** single-user install as a dedicated `nix` user when running as root in containers.
|
||||||
|
|
||||||
130
scripts/nix/init.sh
Executable file
130
scripts/nix/init.sh
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# shellcheck source=lib/config.sh
|
||||||
|
# shellcheck source=lib/detect.sh
|
||||||
|
# shellcheck source=lib/path.sh
|
||||||
|
# shellcheck source=lib/symlinks.sh
|
||||||
|
# shellcheck source=lib/users.sh
|
||||||
|
# shellcheck source=lib/install.sh
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
source "${SCRIPT_DIR}/lib/config.sh"
|
||||||
|
source "${SCRIPT_DIR}/lib/detect.sh"
|
||||||
|
source "${SCRIPT_DIR}/lib/path.sh"
|
||||||
|
source "${SCRIPT_DIR}/lib/symlinks.sh"
|
||||||
|
source "${SCRIPT_DIR}/lib/users.sh"
|
||||||
|
source "${SCRIPT_DIR}/lib/install.sh"
|
||||||
|
|
||||||
|
echo "[init-nix] Starting Nix initialization..."
|
||||||
|
|
||||||
|
main() {
|
||||||
|
# Fast path: already available
|
||||||
|
if command -v nix >/dev/null 2>&1; then
|
||||||
|
echo "[init-nix] Nix already available on PATH: $(command -v nix)"
|
||||||
|
ensure_nix_on_path
|
||||||
|
|
||||||
|
if [[ "${EUID:-0}" -eq 0 ]]; then
|
||||||
|
ensure_global_nix_symlinks "$(resolve_nix_bin 2>/dev/null || true)"
|
||||||
|
else
|
||||||
|
ensure_user_nix_symlink "$(resolve_nix_bin 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_nix_on_path
|
||||||
|
|
||||||
|
if command -v nix >/dev/null 2>&1; then
|
||||||
|
echo "[init-nix] Nix found after PATH adjustment: $(command -v nix)"
|
||||||
|
if [[ "${EUID:-0}" -eq 0 ]]; then
|
||||||
|
ensure_global_nix_symlinks "$(resolve_nix_bin 2>/dev/null || true)"
|
||||||
|
else
|
||||||
|
ensure_user_nix_symlink "$(resolve_nix_bin 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local IN_CONTAINER=0
|
||||||
|
if is_container; then
|
||||||
|
IN_CONTAINER=1
|
||||||
|
echo "[init-nix] Detected container environment."
|
||||||
|
else
|
||||||
|
echo "[init-nix] No container detected."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Container + root: dedicated "nix" user, single-user install
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
if [[ "$IN_CONTAINER" -eq 1 && "${EUID:-0}" -eq 0 ]]; then
|
||||||
|
echo "[init-nix] Container + root: installing as 'nix' user (single-user)."
|
||||||
|
|
||||||
|
ensure_nix_build_group
|
||||||
|
|
||||||
|
if ! id nix >/dev/null 2>&1; then
|
||||||
|
echo "[init-nix] Creating user 'nix'..."
|
||||||
|
local BASH_SHELL
|
||||||
|
BASH_SHELL="$(command -v bash || true)"
|
||||||
|
[[ -z "$BASH_SHELL" ]] && BASH_SHELL="/bin/sh"
|
||||||
|
useradd -m -r -g nixbld -s "$BASH_SHELL" nix
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_nix_store_dir_for_container_user
|
||||||
|
|
||||||
|
install_nix_with_retry "no-daemon" "nix"
|
||||||
|
|
||||||
|
ensure_nix_on_path
|
||||||
|
|
||||||
|
# Ensure stable global symlink(s) (sudo secure_path friendly)
|
||||||
|
ensure_global_nix_symlinks "/home/nix/.nix-profile/bin/nix"
|
||||||
|
|
||||||
|
# Ensure non-root users can traverse and execute nix user profile
|
||||||
|
ensure_container_profile_perms
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Host (no container)
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
echo "[init-nix] Host with systemd: using multi-user install (--daemon)."
|
||||||
|
if [[ "${EUID:-0}" -eq 0 ]]; then
|
||||||
|
ensure_nix_build_group
|
||||||
|
fi
|
||||||
|
install_nix_with_retry "daemon"
|
||||||
|
else
|
||||||
|
echo "[init-nix] No systemd detected: using single-user install (--no-daemon)."
|
||||||
|
if [[ "${EUID:-0}" -eq 0 ]]; then
|
||||||
|
ensure_nix_build_group
|
||||||
|
fi
|
||||||
|
install_nix_with_retry "no-daemon"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# After install: PATH + symlink(s)
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
ensure_nix_on_path
|
||||||
|
|
||||||
|
local nix_bin_post
|
||||||
|
nix_bin_post="$(resolve_nix_bin 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ "${EUID:-0}" -eq 0 ]]; then
|
||||||
|
ensure_global_nix_symlinks "$nix_bin_post"
|
||||||
|
else
|
||||||
|
ensure_user_nix_symlink "$nix_bin_post"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Final verification (must succeed for CI)
|
||||||
|
if ! command -v nix >/dev/null 2>&1; then
|
||||||
|
echo "[init-nix] ERROR: nix not found after installation."
|
||||||
|
echo "[init-nix] DEBUG: resolved nix path = ${nix_bin_post:-<empty>}"
|
||||||
|
echo "[init-nix] DEBUG: PATH = $PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[init-nix] Nix successfully available at: $(command -v nix)"
|
||||||
|
echo "[init-nix] Nix initialization complete."
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
11
scripts/nix/lib/config.sh
Normal file
11
scripts/nix/lib/config.sh
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Prevent double-sourcing
|
||||||
|
if [[ -n "${PKGMGR_NIX_CONFIG_SH:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
PKGMGR_NIX_CONFIG_SH=1
|
||||||
|
|
||||||
|
NIX_INSTALL_URL="${NIX_INSTALL_URL:-https://nixos.org/nix/install}"
|
||||||
|
NIX_DOWNLOAD_MAX_TIME="${NIX_DOWNLOAD_MAX_TIME:-300}"
|
||||||
|
NIX_DOWNLOAD_SLEEP_INTERVAL="${NIX_DOWNLOAD_SLEEP_INTERVAL:-20}"
|
||||||
14
scripts/nix/lib/detect.sh
Normal file
14
scripts/nix/lib/detect.sh
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ -n "${PKGMGR_NIX_DETECT_SH:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
PKGMGR_NIX_DETECT_SH=1
|
||||||
|
|
||||||
|
# Detect whether we are inside a container (Docker/Podman/etc.)
|
||||||
|
is_container() {
|
||||||
|
[[ -f /.dockerenv || -f /run/.containerenv ]] && return 0
|
||||||
|
grep -qiE 'docker|container|podman|lxc' /proc/1/cgroup 2>/dev/null && return 0
|
||||||
|
[[ -n "${container:-}" ]] && return 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
63
scripts/nix/lib/install.sh
Normal file
63
scripts/nix/lib/install.sh
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ -n "${PKGMGR_NIX_INSTALL_SH:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
PKGMGR_NIX_INSTALL_SH=1
|
||||||
|
|
||||||
|
# Requires: NIX_INSTALL_URL, NIX_DOWNLOAD_MAX_TIME, NIX_DOWNLOAD_SLEEP_INTERVAL
|
||||||
|
|
||||||
|
# Download and run Nix installer with retry
|
||||||
|
# Usage: install_nix_with_retry daemon|no-daemon [run_as_user]
|
||||||
|
install_nix_with_retry() {
|
||||||
|
local mode="$1"
|
||||||
|
local run_as="${2:-}"
|
||||||
|
local installer elapsed=0 mode_flag
|
||||||
|
|
||||||
|
case "$mode" in
|
||||||
|
daemon) mode_flag="--daemon" ;;
|
||||||
|
no-daemon) mode_flag="--no-daemon" ;;
|
||||||
|
*)
|
||||||
|
echo "[init-nix] ERROR: Invalid mode '$mode' (expected 'daemon' or 'no-daemon')."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
installer="$(mktemp -t nix-installer.XXXXXX)"
|
||||||
|
chmod 0644 "$installer"
|
||||||
|
|
||||||
|
echo "[init-nix] Downloading Nix installer from $NIX_INSTALL_URL (max ${NIX_DOWNLOAD_MAX_TIME}s)..."
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if curl -fL "$NIX_INSTALL_URL" -o "$installer"; then
|
||||||
|
echo "[init-nix] Successfully downloaded installer to $installer"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
elapsed=$((elapsed + NIX_DOWNLOAD_SLEEP_INTERVAL))
|
||||||
|
echo "[init-nix] WARNING: Download failed. Retrying in ${NIX_DOWNLOAD_SLEEP_INTERVAL}s (elapsed ${elapsed}s)..."
|
||||||
|
|
||||||
|
if (( elapsed >= NIX_DOWNLOAD_MAX_TIME )); then
|
||||||
|
echo "[init-nix] ERROR: Giving up after ${elapsed}s trying to download Nix installer."
|
||||||
|
rm -f "$installer"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "$NIX_DOWNLOAD_SLEEP_INTERVAL"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "$run_as" ]]; then
|
||||||
|
chown "$run_as:$run_as" "$installer" 2>/dev/null || true
|
||||||
|
echo "[init-nix] Running installer as user '$run_as' ($mode_flag)..."
|
||||||
|
if command -v sudo >/dev/null 2>&1; then
|
||||||
|
sudo -u "$run_as" bash -lc "sh '$installer' $mode_flag"
|
||||||
|
else
|
||||||
|
su - "$run_as" -c "sh '$installer' $mode_flag"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "[init-nix] Running installer as current user ($mode_flag)..."
|
||||||
|
sh "$installer" "$mode_flag"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$installer"
|
||||||
|
}
|
||||||
68
scripts/nix/lib/path.sh
Normal file
68
scripts/nix/lib/path.sh
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ -n "${PKGMGR_NIX_PATH_SH:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
PKGMGR_NIX_PATH_SH=1
|
||||||
|
|
||||||
|
# Ensure Nix binaries are on PATH (additive, never destructive)
|
||||||
|
ensure_nix_on_path() {
|
||||||
|
if [[ -x /nix/var/nix/profiles/default/bin/nix ]]; then
|
||||||
|
PATH="/nix/var/nix/profiles/default/bin:$PATH"
|
||||||
|
fi
|
||||||
|
if [[ -x "$HOME/.nix-profile/bin/nix" ]]; then
|
||||||
|
PATH="$HOME/.nix-profile/bin:$PATH"
|
||||||
|
fi
|
||||||
|
if [[ -x /home/nix/.nix-profile/bin/nix ]]; then
|
||||||
|
PATH="/home/nix/.nix-profile/bin:$PATH"
|
||||||
|
fi
|
||||||
|
if [[ -d "$HOME/.local/bin" ]]; then
|
||||||
|
PATH="$HOME/.local/bin:$PATH"
|
||||||
|
fi
|
||||||
|
export PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve a path to a real executable (follows symlinks)
|
||||||
|
real_exe() {
|
||||||
|
local p="${1:-}"
|
||||||
|
[[ -z "$p" ]] && return 1
|
||||||
|
|
||||||
|
local r
|
||||||
|
r="$(readlink -f "$p" 2>/dev/null || echo "$p")"
|
||||||
|
|
||||||
|
[[ -x "$r" ]] && { echo "$r"; return 0; }
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve nix binary path robustly (works across distros + Arch /usr/sbin)
|
||||||
|
resolve_nix_bin() {
|
||||||
|
local nix_cmd=""
|
||||||
|
nix_cmd="$(command -v nix 2>/dev/null || true)"
|
||||||
|
[[ -n "$nix_cmd" ]] && real_exe "$nix_cmd" && return 0
|
||||||
|
|
||||||
|
# IMPORTANT: prefer system locations before /usr/local to avoid self-symlink traps
|
||||||
|
[[ -x /usr/sbin/nix ]] && { echo "/usr/sbin/nix"; return 0; } # Arch package can land here
|
||||||
|
[[ -x /usr/bin/nix ]] && { echo "/usr/bin/nix"; return 0; }
|
||||||
|
[[ -x /bin/nix ]] && { echo "/bin/nix"; return 0; }
|
||||||
|
|
||||||
|
# /usr/local last, and only if it resolves to a real executable
|
||||||
|
[[ -e /usr/local/bin/nix ]] && real_exe "/usr/local/bin/nix" && return 0
|
||||||
|
|
||||||
|
[[ -x /nix/var/nix/profiles/default/bin/nix ]] && {
|
||||||
|
echo "/nix/var/nix/profiles/default/bin/nix"; return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ -x "$HOME/.nix-profile/bin/nix" ]] && {
|
||||||
|
echo "$HOME/.nix-profile/bin/nix"; return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ -x "$HOME/.local/bin/nix" ]] && {
|
||||||
|
echo "$HOME/.local/bin/nix"; return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ -x /home/nix/.nix-profile/bin/nix ]] && {
|
||||||
|
echo "/home/nix/.nix-profile/bin/nix"; return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
95
scripts/nix/lib/symlinks.sh
Normal file
95
scripts/nix/lib/symlinks.sh
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ -n "${PKGMGR_NIX_SYMLINKS_SH:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
PKGMGR_NIX_SYMLINKS_SH=1
|
||||||
|
|
||||||
|
# Requires: real_exe, resolve_nix_bin
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
|
||||||
|
# Ensure globally reachable nix symlink(s) (CI / non-login shells) - root only
|
||||||
|
ensure_global_nix_symlinks() {
|
||||||
|
local nix_bin="${1:-}"
|
||||||
|
|
||||||
|
[[ -z "$nix_bin" ]] && nix_bin="$(resolve_nix_bin 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ -z "$nix_bin" || ! -x "$nix_bin" ]]; then
|
||||||
|
echo "[init-nix] WARNING: nix binary not found, cannot create global symlink(s)."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Always link to the real executable to avoid /usr/local/bin/nix -> /usr/local/bin/nix
|
||||||
|
nix_bin="$(real_exe "$nix_bin" 2>/dev/null || echo "$nix_bin")"
|
||||||
|
|
||||||
|
local targets=()
|
||||||
|
|
||||||
|
# Always provide /usr/local/bin/nix for CI shells
|
||||||
|
mkdir -p /usr/local/bin 2>/dev/null || true
|
||||||
|
targets+=("/usr/local/bin/nix")
|
||||||
|
|
||||||
|
# Provide sudo-friendly locations only if they are NOT present (do not override distro paths)
|
||||||
|
if [[ ! -e /usr/bin/nix ]]; then
|
||||||
|
targets+=("/usr/bin/nix")
|
||||||
|
fi
|
||||||
|
if [[ ! -e /usr/sbin/nix ]]; then
|
||||||
|
targets+=("/usr/sbin/nix")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local target current_real
|
||||||
|
for target in "${targets[@]}"; do
|
||||||
|
current_real=""
|
||||||
|
if [[ -e "$target" ]]; then
|
||||||
|
current_real="$(real_exe "$target" 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$current_real" && "$current_real" == "$nix_bin" ]]; then
|
||||||
|
echo "[init-nix] $target already points to: $nix_bin"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If something exists but is not the same (and we promised not to override), skip.
|
||||||
|
if [[ -e "$target" && "$target" != "/usr/local/bin/nix" ]]; then
|
||||||
|
echo "[init-nix] WARNING: $target exists; not overwriting."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ln -sf "$nix_bin" "$target" 2>/dev/null; then
|
||||||
|
echo "[init-nix] Ensured $target -> $nix_bin"
|
||||||
|
else
|
||||||
|
echo "[init-nix] WARNING: Failed to ensure $target symlink."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure user-level nix symlink (works without root; CI-safe)
|
||||||
|
ensure_user_nix_symlink() {
|
||||||
|
local nix_bin="${1:-}"
|
||||||
|
|
||||||
|
[[ -z "$nix_bin" ]] && nix_bin="$(resolve_nix_bin 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ -z "$nix_bin" || ! -x "$nix_bin" ]]; then
|
||||||
|
echo "[init-nix] WARNING: nix binary not found, cannot create user symlink."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
nix_bin="$(real_exe "$nix_bin" 2>/dev/null || echo "$nix_bin")"
|
||||||
|
|
||||||
|
mkdir -p "$HOME/.local/bin" 2>/dev/null || true
|
||||||
|
ln -sf "$nix_bin" "$HOME/.local/bin/nix"
|
||||||
|
|
||||||
|
echo "[init-nix] Ensured $HOME/.local/bin/nix -> $nix_bin"
|
||||||
|
|
||||||
|
PATH="$HOME/.local/bin:$PATH"
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
if [[ -w "$HOME/.profile" ]] && ! grep -q 'nix/init.sh' "$HOME/.profile" 2>/dev/null; then
|
||||||
|
cat >>"$HOME/.profile" <<'EOF'
|
||||||
|
|
||||||
|
# PATH for nix (added by package-manager nix/init.sh)
|
||||||
|
if [ -d "$HOME/.local/bin" ]; then
|
||||||
|
PATH="$HOME/.local/bin:$PATH"
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
}
|
||||||
49
scripts/nix/lib/users.sh
Normal file
49
scripts/nix/lib/users.sh
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ -n "${PKGMGR_NIX_USERS_SH:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
PKGMGR_NIX_USERS_SH=1
|
||||||
|
|
||||||
|
# Ensure Nix build group and users exist (build-users-group = nixbld) - root only
|
||||||
|
ensure_nix_build_group() {
|
||||||
|
if ! getent group nixbld >/dev/null 2>&1; then
|
||||||
|
echo "[init-nix] Creating group 'nixbld'..."
|
||||||
|
groupadd -r nixbld
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in $(seq 1 10); do
|
||||||
|
if ! id "nixbld$i" >/dev/null 2>&1; then
|
||||||
|
echo "[init-nix] Creating build user nixbld$i..."
|
||||||
|
useradd -r -g nixbld -G nixbld -s /usr/sbin/nologin "nixbld$i"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Container-only helper: /nix ownership + perms for single-user install as 'nix'
|
||||||
|
ensure_nix_store_dir_for_container_user() {
|
||||||
|
if [[ ! -d /nix ]]; then
|
||||||
|
echo "[init-nix] Creating /nix with owner nix:nixbld..."
|
||||||
|
mkdir -m 0755 /nix
|
||||||
|
chown nix:nixbld /nix
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local current_owner current_group
|
||||||
|
current_owner="$(stat -c '%U' /nix 2>/dev/null || echo '?')"
|
||||||
|
current_group="$(stat -c '%G' /nix 2>/dev/null || echo '?')"
|
||||||
|
if [[ "$current_owner" != "nix" || "$current_group" != "nixbld" ]]; then
|
||||||
|
echo "[init-nix] Fixing /nix ownership from $current_owner:$current_group to nix:nixbld..."
|
||||||
|
chown -R nix:nixbld /nix
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Container-only helper: make nix profile executable/traversable for non-root
|
||||||
|
ensure_container_profile_perms() {
|
||||||
|
if [[ -d /home/nix ]]; then
|
||||||
|
chmod o+rx /home/nix 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ -d /home/nix/.nix-profile ]]; then
|
||||||
|
chmod -R o+rx /home/nix/.nix-profile 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -28,11 +28,11 @@ if ! command -v nix >/dev/null 2>&1; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# If nix is still missing, try to run init-nix.sh once
|
# If nix is still missing, try to run nix/init.sh once
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
if ! command -v nix >/dev/null 2>&1; then
|
if ! command -v nix >/dev/null 2>&1; then
|
||||||
if [[ -x "${FLAKE_DIR}/init-nix.sh" ]]; then
|
if [[ -x "${FLAKE_DIR}/nix/init.sh" ]]; then
|
||||||
"${FLAKE_DIR}/init-nix.sh" || true
|
"${FLAKE_DIR}/nix/init.sh" || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ USER_CONFIG_PATH = os.path.expanduser("~/.config/pkgmgr/config.yaml")
|
|||||||
DESCRIPTION_TEXT = """\
|
DESCRIPTION_TEXT = """\
|
||||||
\033[1;32mPackage Manager 🤖📦\033[0m
|
\033[1;32mPackage Manager 🤖📦\033[0m
|
||||||
\033[3mKevin's multi-distro package and workflow manager.\033[0m
|
\033[3mKevin's multi-distro package and workflow manager.\033[0m
|
||||||
\033[1;34mKevin Veen-Birkenbach\033[0m – \033[4mhttps://www.veen.world/\033[0m
|
\033[1;34mKevin Veen-Birkenbach\033[0m – \033[4mhttps://s.veen.world/pkgmgr\033[0m
|
||||||
|
|
||||||
Built in \033[1;33mPython\033[0m on top of \033[1;33mNix flakes\033[0m to manage many
|
Built in \033[1;33mPython\033[0m on top of \033[1;33mNix flakes\033[0m to manage many
|
||||||
repositories and packaging formats (pyproject.toml, flake.nix,
|
repositories and packaging formats (pyproject.toml, flake.nix,
|
||||||
|
|||||||
Reference in New Issue
Block a user