Compare commits

...

7 Commits

Author SHA1 Message Date
Kevin Veen-Birkenbach
72fc69c2f8 Release version 1.0.0
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-container (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
2025-12-11 20:41:35 +01:00
Kevin Veen-Birkenbach
6d8c6deae8 **refactor(readme): rewrite README for multi-distro focus and Nix-based workflows**
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-container (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
Expanded and modernized the README to reflect PKGMGR's purpose as a
multi-distro development and packaging orchestrator. Added explanations for
Nix-based cross-distro workflows, clarified installation steps, documented the
full CLI capabilities, and embedded the architecture diagram.

Also replaced the verbose CLI DESCRIPTION_TEXT with a concise summary suitable
for `--help` output.

Included updated `assets/map.png`.

https://chatgpt.com/share/693b1d71-ca08-800f-a000-f3be49f7efb5
2025-12-11 20:37:05 +01:00
Kevin Veen-Birkenbach
6c116a029e Release version 0.10.2
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-container (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
2025-12-11 20:16:59 +01:00
Kevin Veen-Birkenbach
3eb7c81fa1 **Mark stable only on highest version tag**
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-container (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
Updated the `mark-stable` workflow so that the `stable` tag is only moved when:

* the current push is a version tag (`v*`)
* all tests have passed
* the pushed version tag is the highest semantic version among all existing tags

This ensures that `stable` always reflects the latest valid release and prevents older version tags from overwriting it.

https://chatgpt.com/share/693b163b-0c34-800f-adcb-12cf4744dbe2
2025-12-11 20:06:22 +01:00
Kevin Veen-Birkenbach
0334f477fd Release version 0.10.2
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-container (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
2025-12-11 20:01:29 +01:00
Kevin Veen-Birkenbach
8bb99c99b7 refactor(init-nix): unify installer logic and add robust retry handling
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-container (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
Refactored the Nix initialization script to reduce duplicated code and
centralize the installation workflow. The core functionality remains
unchanged, but all installer calls now use a unified function with retry
support to ensure resilient downloads in CI and container environments.

Key improvements:
- Added download retry logic (5 minutes total, 20-second intervals)
- Consolidated installer invocation into `install_nix_with_retry`
- Reduced code duplication across container/host install paths
- Preserved existing installation behavior for all environments
- Maintained `nixbld` group and build-user handling
- Improved consistency and readability without altering semantics

This prevents intermittent failures such as:
“curl: (6) Could not resolve host: nixos.org”
and ensures stable, deterministic Nix setup in CI pipelines.

https://chatgpt.com/share/693b13ce-fdcc-800f-a7bc-81c67478edff
2025-12-11 19:56:10 +01:00
Kevin Veen-Birkenbach
587cb2e516 Removed comments
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-container (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
2025-12-11 19:44:36 +01:00
9 changed files with 393 additions and 242 deletions

View File

@@ -3,7 +3,9 @@ name: Mark stable commit
on: on:
push: push:
branches: branches:
- main - main # still run tests for main
tags:
- 'v*' # run tests for version tags (e.g. v0.9.1)
jobs: jobs:
test-unit: test-unit:
@@ -34,31 +36,63 @@ jobs:
- test-virgin-root - test-virgin-root
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Only run this job if the push is for a version tag (v*)
if: startsWith(github.ref, 'refs/tags/v')
permissions: permissions:
contents: write # to move the tag contents: write # Required to move/update the tag
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
fetch-tags: true # We need all tags for version comparison
- name: Move 'stable' tag to this commit - name: Move 'stable' tag only if this version is the highest
run: | run: |
set -euo pipefail set -euo pipefail
git config user.name "github-actions[bot]" git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com" git config user.email "github-actions[bot]@users.noreply.github.com"
echo "Tagging commit $GITHUB_SHA as stable…" echo "Ref: $GITHUB_REF"
echo "SHA: $GITHUB_SHA"
# delete local tag if exists VERSION="${GITHUB_REF#refs/tags/}"
echo "Current version tag: ${VERSION}"
echo "Collecting all version tags..."
ALL_V_TAGS="$(git tag --list 'v*' || true)"
if [[ -z "${ALL_V_TAGS}" ]]; then
echo "No version tags found. Skipping stable update."
exit 0
fi
echo "All version tags:"
echo "${ALL_V_TAGS}"
# Determine highest version using natural version sorting
LATEST_TAG="$(printf '%s\n' ${ALL_V_TAGS} | sort -V | tail -n1)"
echo "Highest version tag: ${LATEST_TAG}"
if [[ "${VERSION}" != "${LATEST_TAG}" ]]; then
echo "Current version ${VERSION} is NOT the highest version."
echo "Stable tag will NOT be updated."
exit 0
fi
echo "Current version ${VERSION} IS the highest version."
echo "Updating 'stable' tag..."
# Delete existing stable tag (local + remote)
git tag -d stable 2>/dev/null || true git tag -d stable 2>/dev/null || true
# delete remote tag if exists
git push origin :refs/tags/stable || true git push origin :refs/tags/stable || true
# create new tag on this commit # Create new stable tag
git tag stable "$GITHUB_SHA" git tag stable "$GITHUB_SHA"
git push origin stable git push origin stable
echo "✅ Stable tag updated." echo "✅ Stable tag updated to ${VERSION}."

View File

@@ -1,3 +1,51 @@
## [1.0.0] - 2025-12-11
* **1.0.0 Official Stable Release 🎉**
*First stable release of PKGMGR, the multi-distro development and package workflow manager.*
---
**Key Features**
**Core Functionality**
* Manage many repositories with one CLI: `clone`, `update`, `install`, `list`, `path`, `config`
* Proxy wrappers for Git, Docker/Compose and Make
* Multi-repo execution with safe *preview mode*
* Mirror management: `mirror list/diff/merge/setup`
**Releases & Versioning**
* Automated SemVer bumps, tagging and changelog generation
* Supports PKGBUILD, Debian, RPM, pyproject.toml, flake.nix
**Developer Tools**
* Open repositories in VS Code, file manager or terminal
* Unified workflows across all major Linux distros
**Nix Integration**
* Cross-distro reproducible builds via Nix flakes
* CI-tested across all supported environments
---
**Summary**
PKGMGR 1.0.0 unifies repository management, build tooling, release automation and reproducible multi-distro workflows into one cohesive CLI tool.
*This is the first official stable release.*
## [0.10.2] - 2025-12-11
* * Stable tag now updates only when a new highest version is released.
* Debian package now includes sudo to ensure privilege escalation works reliably.
* Nix setup is significantly more resilient with retries, correct permissions, and better environment handling.
* AUR builder setup uses retries so yay installs succeed even under network instability.
* Nix flake installation now fails only on mandatory parts; optional outputs no longer block installation.
## [0.10.1] - 2025-12-11 ## [0.10.1] - 2025-12-11
* Fixed Debian\Ubuntu to pass container e2e tests * Fixed Debian\Ubuntu to pass container e2e tests
@@ -5,8 +53,6 @@
## [0.10.0] - 2025-12-11 ## [0.10.0] - 2025-12-11
* **Changes since v0.9.1**
**Mirror System** **Mirror System**
* Added SSH mirror support including multi-push and remote probing * Added SSH mirror support including multi-push and remote probing

166
README.md
View File

@@ -1,68 +1,184 @@
# Package Manager🤖📦 # Package Manager 🤖📦
[![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub%20Sponsors-blue?logo=github)](https://github.com/sponsors/kevinveenbirkenbach) [![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub%20Sponsors-blue?logo=github)](https://github.com/sponsors/kevinveenbirkenbach)
[![Patreon](https://img.shields.io/badge/Support-Patreon-orange?logo=patreon)](https://www.patreon.com/c/kevinveenbirkenbach) [![Patreon](https://img.shields.io/badge/Support-Patreon-orange?logo=patreon)](https://www.patreon.com/c/kevinveenbirkenbach)
[![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20Coffee-Funding-yellow?logo=buymeacoffee)](https://buymeacoffee.com/kevinveenbirkenbach) [![PayPal](https://img.shields.io/badge/Donate-PayPal-blue?logo=paypal)](https://s.veen.world/paypaldonate) [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20Coffee-Funding-yellow?logo=buymeacoffee)](https://buymeacoffee.com/kevinveenbirkenbach)
[![PayPal](https://img.shields.io/badge/Donate-PayPal-blue?logo=paypal)](https://s.veen.world/paypaldonate)
[![GitHub license](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![GitHub license](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![GitHub repo size](https://img.shields.io/github/repo-size/kevinveenbirkenbach/package-manager)](https://github.com/kevinveenbirkenbach/package-manager) [![GitHub repo size](https://img.shields.io/github/repo-size/kevinveenbirkenbach/package-manager)](https://github.com/kevinveenbirkenbach/package-manager)
*Kevins's* Package Manager is a configurable Python tool designed to manage multiple repositories via Bash. It automates common Git operations such as clone, pull, push, status, and more. Additionally, it handles the creation of executable wrappers and alias links for your repositories. **Kevin's Package Manager (PKGMGR)** is a *multi-distro* package manager and workflow orchestrator.
It helps you **develop, package, release and manage projects across multiple Linux-based
operating systems** (Arch, Debian, Ubuntu, Fedora, CentOS, …).
PKGMGR is implemented in **Python** and uses **Nix (flakes)** as a foundation for
distribution-independent builds and tooling. On top of that it provides a rich
CLI that proxies common developer tools (Git, Docker, Make, …) and glues them
together into repeatable development workflows.
---
## Why PKGMGR? 🧠
Traditional distro package managers like `apt`, `pacman` or `dnf` focus on a
single operating system. PKGMGR instead focuses on **your repositories and
development lifecycle**:
* one configuration for all your repos,
* one CLI to interact with them,
* one Nix-based layer to keep tooling reproducible across distros.
You keep using your native package manager where it makes sense PKGMGR
coordinates the *development and release flow* around it.
---
## Features 🚀 ## Features 🚀
- **Installation & Setup:** ### Multi-distro development & packaging
Create executable wrappers with auto-detected commands (e.g. `main.sh` or `main.py`).
- **Git Operations:** * Manage **many repositories at once** from a single `config/config.yaml`.
Easily perform `git pull`, `push`, `status`, `commit`, `diff`, `add`, `show`, and `checkout` with extra parameters passed through. * Drive full **release pipelines** across Linux distributions using:
- **Configuration Management:** * Nix flakes (`flake.nix`)
Manage repository configurations via a default file (`config/defaults.yaml`) and a user-specific file (`config/config.yaml`). Initialize, add, delete, or ignore entries using subcommands. * PyPI style builds (`pyproject.toml`)
* OS packages (PKGBUILD, Debian control/changelog, RPM spec)
* Ansible Galaxy metadata and more.
- **Path & Listing:** ### Rich CLI for daily work
Display repository paths or list all configured packages with their details.
- **Custom Aliases:** All commands are exposed via the `pkgmgr` CLI and are available on every distro:
Generate and manage custom aliases for easy command invocation.
* **Repository management**
* `clone`, `update`, `install`, `delete`, `deinstall`, `path`, `list`, `config`
* **Git proxies**
* `pull`, `push`, `status`, `diff`, `add`, `show`, `checkout`,
`reset`, `revert`, `rebase`, `commit`, `branch`
* **Docker & Compose orchestration**
* `build`, `up`, `down`, `exec`, `ps`, `start`, `stop`, `restart`
* **Release toolchain**
* `version`, `release`, `changelog`, `make`
* **Mirror & workflow helpers**
* `mirror` (list/diff/merge/setup), `shell`, `terminal`, `code`, `explore`
Many of these commands support `--preview` mode so you can inspect the
underlying Git or Docker calls without executing them.
### Full development workflows
PKGMGR is not just a helper around Git commands. Combined with its release and
versioning features it can drive **end-to-end workflows**:
1. Clone and mirror repositories.
2. Run tests and builds through `make` or Nix.
3. Bump versions, update changelogs and tags.
4. Build distro-specific packages.
5. Keep all mirrors and working copies in sync.
The extensive E2E tests (`tests/e2e/`) and GitHub Actions workflows (including
“virgin user” and “virgin root” Arch tests) validate these flows across
different Linux environments.
---
## Architecture & Setup Map 🗺️ ## Architecture & Setup Map 🗺️
The following diagram provides a full overview of PKGMGRs package structure, The following diagram gives a full overview of:
installation layers, and setup controller flow:
* PKGMGRs package structure,
* the layered installers (OS, foundation, Python, Makefile),
* and the setup controller that decides which layer to use on a given system.
![PKGMGR Architecture](assets/map.png) ![PKGMGR Architecture](assets/map.png)
**Diagram status:** *Stand: 11. Dezember 2025* **Diagram status:** 11 December 2025
**Always-up-to-date version:** https://s.veen.world/pkgmgrmp **Always-up-to-date version:** [https://s.veen.world/pkgmgrmp](https://s.veen.world/pkgmgrmp)
---
## Installation ⚙️ ## Installation ⚙️
Clone the repository and ensure your `~/.local/bin` is in your system PATH: ### 1. Get the latest stable version
For a stable setup, use the **latest tagged release** (the tag pointed to by
`latest`):
```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)"
``` ```
Install make and pip if not installed yet: ### 2. Install via Make
The project ships with a Makefile that encapsulates the typical installation
flow. On most systems you only need:
```bash ```bash
pacman -S make python-pip # 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
``` ```
Then, run the following command to set up the project: This will:
* 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
``` ```
The `make setup` command will: which prepares the environment and leaves you with a fully wired development
- Make `main.py` executable. workspace (including Nix, tests and scripts).
- Install required packages from `requirements.txt`.
- Execute `python main.py install` to complete the installation. ---
## Usage 🧰
After installation, the main entry point is:
```bash
pkgmgr --help
```
This prints a list of all available subcommands, for example:
* `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:
```bash
pkgmgr <command> --help
```
---
## License 📄 ## License 📄
This project is licensed under the MIT License. This project is licensed under the MIT License.
See the [LICENSE](LICENSE) file for details.
---
## Author 👤 ## Author 👤

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@@ -36,7 +36,7 @@
rec { rec {
pkgmgr = pyPkgs.buildPythonApplication { pkgmgr = pyPkgs.buildPythonApplication {
pname = "package-manager"; pname = "package-manager";
version = "0.10.1"; version = "1.0.0";
# Use the git repo as source # Use the git repo as source
src = ./.; src = ./.;

View File

@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "package-manager" name = "package-manager"
version = "0.10.1" version = "1.0.0"
description = "Kevin's package-manager tool (pkgmgr)" description = "Kevin's package-manager tool (pkgmgr)"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"

View File

@@ -3,21 +3,22 @@ set -euo pipefail
echo "[init-nix] Starting Nix initialization..." echo "[init-nix] Starting Nix initialization..."
NIX_INSTALL_URL="${NIX_INSTALL_URL:-https://nixos.org/nix/install}"
NIX_DOWNLOAD_MAX_TIME=300 # 5 minutes
NIX_DOWNLOAD_SLEEP_INTERVAL=20 # 20 seconds
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Helper: detect whether we are inside a container (Docker/Podman/etc.) # Detect whether we are inside a container (Docker/Podman/etc.)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
is_container() { is_container() {
# Docker / Podman markers
if [[ -f /.dockerenv ]] || [[ -f /run/.containerenv ]]; then if [[ -f /.dockerenv ]] || [[ -f /run/.containerenv ]]; then
return 0 return 0
fi fi
# cgroup hints
if grep -qiE 'docker|container|podman|lxc' /proc/1/cgroup 2>/dev/null; then if grep -qiE 'docker|container|podman|lxc' /proc/1/cgroup 2>/dev/null; then
return 0 return 0
fi fi
# Environment variable used by some runtimes
if [[ -n "${container:-}" ]]; then if [[ -n "${container:-}" ]]; then
return 0 return 0
fi fi
@@ -26,219 +27,206 @@ is_container() {
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Helper: ensure Nix binaries are on PATH (multi-user or single-user) # Ensure Nix binaries are on PATH (multi-user or single-user)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
ensure_nix_on_path() { ensure_nix_on_path() {
# Multi-user profile (daemon install)
if [[ -x /nix/var/nix/profiles/default/bin/nix ]]; then if [[ -x /nix/var/nix/profiles/default/bin/nix ]]; then
export PATH="/nix/var/nix/profiles/default/bin:${PATH}" export PATH="/nix/var/nix/profiles/default/bin:${PATH}"
fi fi
# Single-user profile (current user)
if [[ -x "${HOME}/.nix-profile/bin/nix" ]]; then if [[ -x "${HOME}/.nix-profile/bin/nix" ]]; then
export PATH="${HOME}/.nix-profile/bin:${PATH}" export PATH="${HOME}/.nix-profile/bin:${PATH}"
fi fi
# Single-user profile for dedicated "nix" user (container case)
if [[ -x /home/nix/.nix-profile/bin/nix ]]; then if [[ -x /home/nix/.nix-profile/bin/nix ]]; then
export PATH="/home/nix/.nix-profile/bin:${PATH}" export PATH="/home/nix/.nix-profile/bin:${PATH}"
fi fi
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Helper: ensure Nix build group and users exist (build-users-group = nixbld) # Ensure Nix build group and users exist (build-users-group = nixbld)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
ensure_nix_build_group() { ensure_nix_build_group() {
# Ensure nixbld group (build-users-group for Nix)
if ! getent group nixbld >/dev/null 2>&1; then if ! getent group nixbld >/dev/null 2>&1; then
echo "[init-nix] Creating group 'nixbld'..." echo "[init-nix] Creating group 'nixbld'..."
groupadd -r nixbld groupadd -r nixbld
fi fi
# Ensure Nix build users (nixbld1..nixbld10) as members of nixbld
for i in $(seq 1 10); do for i in $(seq 1 10); do
if ! id "nixbld$i" >/dev/null 2>&1; then if ! id "nixbld$i" >/dev/null 2>&1; then
echo "[init-nix] Creating build user nixbld$i..." echo "[init-nix] Creating build user nixbld$i..."
# -r: system account, -g: primary group, -G: supplementary (ensures membership is listed)
useradd -r -g nixbld -G nixbld -s /usr/sbin/nologin "nixbld$i" useradd -r -g nixbld -G nixbld -s /usr/sbin/nologin "nixbld$i"
fi fi
done done
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Fast path: Nix already available # Download and run Nix installer with retry
# Usage: install_nix_with_retry daemon|no-daemon [run_as_user]
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
if command -v nix >/dev/null 2>&1; then install_nix_with_retry() {
echo "[init-nix] Nix already available on PATH: $(command -v nix)" local mode="$1"
exit 0 local run_as="${2:-}"
fi local installer elapsed=0 mode_flag
ensure_nix_on_path 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
if command -v nix >/dev/null 2>&1; then installer="$(mktemp -t nix-installer.XXXXXX)"
echo "[init-nix] Nix found after adjusting PATH: $(command -v nix)"
exit 0
fi
echo "[init-nix] Nix not found, starting installation logic..." echo "[init-nix] Downloading Nix installer from ${NIX_INSTALL_URL} with retry (max ${NIX_DOWNLOAD_MAX_TIME}s)..."
IN_CONTAINER=0 while true; do
if is_container; then if curl -fL "${NIX_INSTALL_URL}" -o "${installer}"; then
IN_CONTAINER=1 echo "[init-nix] Successfully downloaded Nix installer to ${installer}"
echo "[init-nix] Detected container environment." break
else
echo "[init-nix] No container detected."
fi
# ---------------------------------------------------------------------------
# Container + root: install Nix as dedicated "nix" user (single-user)
# ---------------------------------------------------------------------------
if [[ "${IN_CONTAINER}" -eq 1 && "${EUID:-0}" -eq 0 ]]; then
echo "[init-nix] Running as root inside a container using dedicated 'nix' user."
# Ensure build group/users for Nix
ensure_nix_build_group
# Ensure "nix" user (home at /home/nix)
if ! id nix >/dev/null 2>&1; then
echo "[init-nix] Creating user 'nix'..."
# Resolve a valid shell path across distros:
# - Debian/Ubuntu: /bin/bash
# - Arch: /usr/bin/bash (often symlinked)
# Fall back to /bin/sh on ultra-minimal systems.
BASH_SHELL="$(command -v bash || true)"
if [[ -z "${BASH_SHELL}" ]]; then
BASH_SHELL="/bin/sh"
fi fi
useradd -m -r -g nixbld -s "${BASH_SHELL}" nix
fi
# Ensure /nix exists and is writable by the "nix" user. local curl_exit=$?
# echo "[init-nix] WARNING: Failed to download Nix installer (curl exit code ${curl_exit})."
# In some base images (or previous runs), /nix may already exist and be
# owned by root. In that case the Nix single-user installer will abort with: elapsed=$((elapsed + NIX_DOWNLOAD_SLEEP_INTERVAL))
# if (( elapsed >= NIX_DOWNLOAD_MAX_TIME )); then
# "directory /nix exists, but is not writable by you" echo "[init-nix] ERROR: Giving up after ${elapsed}s trying to download Nix installer."
# rm -f "${installer}"
# To keep container runs idempotent and robust, we always enforce exit 1
# ownership nix:nixbld here. fi
if [[ ! -d /nix ]]; then
echo "[init-nix] Creating /nix with owner nix:nixbld..." echo "[init-nix] Retrying in ${NIX_DOWNLOAD_SLEEP_INTERVAL}s (elapsed: ${elapsed}s/${NIX_DOWNLOAD_MAX_TIME}s)..."
mkdir -m 0755 /nix sleep "${NIX_DOWNLOAD_SLEEP_INTERVAL}"
chown nix:nixbld /nix done
else
current_owner="$(stat -c '%U' /nix 2>/dev/null || echo '?')" if [[ -n "${run_as}" ]]; then
current_group="$(stat -c '%G' /nix 2>/dev/null || echo '?')" echo "[init-nix] Running installer as user '${run_as}' with mode '${mode}'..."
if [[ "${current_owner}" != "nix" || "${current_group}" != "nixbld" ]]; then if command -v sudo >/dev/null 2>&1; then
echo "[init-nix] /nix already exists with owner ${current_owner}:${current_group} fixing to nix:nixbld..." sudo -u "${run_as}" bash -lc "sh '${installer}' ${mode_flag}"
chown -R nix:nixbld /nix
else else
echo "[init-nix] /nix already exists with correct owner nix:nixbld." su - "${run_as}" -c "sh '${installer}' ${mode_flag}"
fi fi
if [[ ! -w /nix ]]; then
echo "[init-nix] WARNING: /nix is still not writable after chown; Nix installer may fail."
fi
fi
# Run Nix single-user installer as "nix"
echo "[init-nix] Installing Nix as user 'nix' (single-user, --no-daemon)..."
if command -v sudo >/dev/null 2>&1; then
sudo -u nix bash -lc 'sh <(curl -L https://nixos.org/nix/install) --no-daemon'
else else
su - nix -c 'sh <(curl -L https://nixos.org/nix/install) --no-daemon' echo "[init-nix] Running installer as current user with mode '${mode}'..."
sh "${installer}" "${mode_flag}"
fi fi
# After installation, expose nix to root via PATH and symlink rm -f "${installer}"
ensure_nix_on_path }
if [[ -x /home/nix/.nix-profile/bin/nix ]]; then # ---------------------------------------------------------------------------
if [[ ! -e /usr/local/bin/nix ]]; then # Main
echo "[init-nix] Creating /usr/local/bin/nix symlink -> /home/nix/.nix-profile/bin/nix" # ---------------------------------------------------------------------------
ln -s /home/nix/.nix-profile/bin/nix /usr/local/bin/nix main() {
fi # Fast path: Nix already available
if command -v nix >/dev/null 2>&1; then
echo "[init-nix] Nix already available on PATH: $(command -v nix)"
return 0
fi fi
ensure_nix_on_path ensure_nix_on_path
if command -v nix >/dev/null 2>&1; then if command -v nix >/dev/null 2>&1; then
echo "[init-nix] Nix successfully installed (container mode) at: $(command -v nix)" echo "[init-nix] Nix found after adjusting PATH: $(command -v nix)"
else return 0
echo "[init-nix] WARNING: Nix installation finished in container, but 'nix' is still not on PATH."
fi fi
# Optionally add PATH hints to /etc/profile (best effort) echo "[init-nix] Nix not found, starting installation logic..."
if [[ -w /etc/profile ]]; then
if ! grep -q 'Nix profiles' /etc/profile 2>/dev/null; then
cat <<'EOF' >> /etc/profile
# Nix profiles (added by package-manager init-nix.sh) local IN_CONTAINER=0
if [ -d /nix/var/nix/profiles/default/bin ]; then if is_container; then
PATH="/nix/var/nix/profiles/default/bin:$PATH" IN_CONTAINER=1
fi echo "[init-nix] Detected container environment."
if [ -d "$HOME/.nix-profile/bin" ]; then else
PATH="$HOME/.nix-profile/bin:$PATH" echo "[init-nix] No container detected."
fi
EOF
echo "[init-nix] Appended Nix PATH setup to /etc/profile (container mode)."
fi
fi fi
echo "[init-nix] Nix initialization complete (container root mode)." # -------------------------------------------------------------------------
exit 0 # Container + root: dedicated "nix" user, single-user install
fi # -------------------------------------------------------------------------
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
# Non-container or non-root container: normal installer paths
# --------------------------------------------------------------------------- if ! id nix >/dev/null 2>&1; then
if [[ "${IN_CONTAINER}" -eq 0 ]]; then echo "[init-nix] Creating user 'nix'..."
# Real host local BASH_SHELL
if command -v systemctl >/dev/null 2>&1; then BASH_SHELL="$(command -v bash || true)"
echo "[init-nix] Host with systemd using multi-user install (--daemon)." [[ -z "${BASH_SHELL}" ]] && BASH_SHELL="/bin/sh"
if [[ "${EUID:-0}" -eq 0 ]]; then useradd -m -r -g nixbld -s "${BASH_SHELL}" nix
# Prepare build-users-group for Nix daemon installs
ensure_nix_build_group
fi fi
sh <(curl -L https://nixos.org/nix/install) --daemon
else
if [[ "${EUID:-0}" -eq 0 ]]; then
echo "[init-nix] WARNING: Running as root without systemd on host."
echo "[init-nix] Falling back to single-user install (--no-daemon), but this is not recommended."
# IMPORTANT: This is where Debian/Ubuntu inside your CI end up. if [[ ! -d /nix ]]; then
# We must ensure 'nixbld' exists before running the installer, echo "[init-nix] Creating /nix with owner nix:nixbld..."
# otherwise modern Nix fails with: "the group 'nixbld' ... does not exist". mkdir -m 0755 /nix
ensure_nix_build_group chown nix:nixbld /nix
sh <(curl -L https://nixos.org/nix/install) --no-daemon
else else
echo "[init-nix] Non-root host without systemd using single-user install (--no-daemon)." local current_owner current_group
# Non-root cannot create nixbld group; rely on upstream defaults current_owner="$(stat -c '%U' /nix 2>/dev/null || echo '?')"
sh <(curl -L https://nixos.org/nix/install) --no-daemon 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
if [[ ! -w /nix ]]; then
echo "[init-nix] WARNING: /nix is not writable after chown; Nix installer may fail."
fi
fi fi
fi
else install_nix_with_retry "no-daemon" "nix"
ensure_nix_on_path
if [[ -x /home/nix/.nix-profile/bin/nix && ! -e /usr/local/bin/nix ]]; then
echo "[init-nix] Creating /usr/local/bin/nix symlink -> /home/nix/.nix-profile/bin/nix"
ln -s /home/nix/.nix-profile/bin/nix /usr/local/bin/nix
fi
# -------------------------------------------------------------------------
# Host (no container)
# -------------------------------------------------------------------------
elif [[ "${IN_CONTAINER}" -eq 0 ]]; then
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
if [[ "${EUID:-0}" -eq 0 ]]; then
echo "[init-nix] Host without systemd as root using single-user install (--no-daemon)."
ensure_nix_build_group
else
echo "[init-nix] Host without systemd as non-root using single-user install (--no-daemon)."
fi
install_nix_with_retry "no-daemon"
fi
# -------------------------------------------------------------------------
# Container, but not root (rare) # Container, but not root (rare)
echo "[init-nix] Container as non-root user using single-user install (--no-daemon)." # -------------------------------------------------------------------------
sh <(curl -L https://nixos.org/nix/install) --no-daemon else
fi echo "[init-nix] Container as non-root using single-user install (--no-daemon)."
install_nix_with_retry "no-daemon"
fi
# --------------------------------------------------------------------------- # -------------------------------------------------------------------------
# After installation: fix PATH (runtime + shell profiles) # After installation: PATH + /etc/profile
# --------------------------------------------------------------------------- # -------------------------------------------------------------------------
ensure_nix_on_path ensure_nix_on_path
if ! command -v nix >/dev/null 2>&1; then if ! command -v nix >/dev/null 2>&1; then
echo "[init-nix] WARNING: Nix installation finished, but 'nix' is still not on PATH." echo "[init-nix] WARNING: Nix installation finished, but 'nix' is still not on PATH."
echo "[init-nix] You may need to source your shell profile manually." echo "[init-nix] You may need to source your shell profile manually."
exit 0 else
fi echo "[init-nix] Nix successfully installed at: $(command -v nix)"
fi
echo "[init-nix] Nix successfully installed at: $(command -v nix)" if [[ -w /etc/profile ]] && ! grep -q 'Nix profiles' /etc/profile 2>/dev/null; then
# Update global /etc/profile if writable (helps especially on minimal systems)
if [[ -w /etc/profile ]]; then
if ! grep -q 'Nix profiles' /etc/profile 2>/dev/null; then
cat <<'EOF' >> /etc/profile cat <<'EOF' >> /etc/profile
# Nix profiles (added by package-manager init-nix.sh) # Nix profiles (added by package-manager init-nix.sh)
@@ -251,6 +239,8 @@ fi
EOF EOF
echo "[init-nix] Appended Nix PATH setup to /etc/profile" echo "[init-nix] Appended Nix PATH setup to /etc/profile"
fi fi
fi
echo "[init-nix] Nix initialization complete." echo "[init-nix] Nix initialization complete."
}
main "$@"

View File

@@ -49,8 +49,8 @@ echo "[aur-builder-setup] Ensuring yay is installed for aur_builder..."
if ! "${RUN_AS_AUR[@]}" 'command -v yay >/dev/null 2>&1'; then if ! "${RUN_AS_AUR[@]}" 'command -v yay >/dev/null 2>&1'; then
echo "[aur-builder-setup] yay not found starting retry sequence for download..." echo "[aur-builder-setup] yay not found starting retry sequence for download..."
MAX_TIME=300 # 5 minutes MAX_TIME=300
SLEEP_INTERVAL=20 # 20 seconds SLEEP_INTERVAL=20
ELAPSED=0 ELAPSED=0
while true; do while true; do

View File

@@ -18,52 +18,17 @@ 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 Package Manager is a multi-repository, multi-package, and multi-format \033[3mKevin's multi-distro package and workflow manager.\033[0m
development tool crafted by and designed for:\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://www.veen.world/\033[0m
\033[1mOverview:\033[0m Built in \033[1;33mPython\033[0m on top of \033[1;33mNix flakes\033[0m to manage many
A powerful toolchain that unifies and automates workflows across heterogeneous repositories and packaging formats (pyproject.toml, flake.nix,
project ecosystems. pkgmgr is not only a package manager — it is a full PKGBUILD, debian, Ansible, …) with one CLI.
developer-oriented orchestration tool.
It automatically detects, merges, and processes metadata from multiple For details on any command, run:
dependency formats, including: \033[1mpkgmgr <command> --help\033[0m
\033[1;33mPython:\033[0m pyproject.toml, requirements.txt
\033[1;33mNix:\033[0m flake.nix
\033[1;33mArch Linux:\033[0m PKGBUILD
\033[1;33mAnsible:\033[0m requirements.yml
This allows pkgmgr to perform installation, updates, verification, dependency
resolution, and synchronization across complex multi-repo environments — with a
single unified command-line interface.
\033[1mDeveloper Tools:\033[0m
pkgmgr includes an integrated toolbox to enhance daily development workflows:
\033[1;33mVS Code integration:\033[0m Auto-generate and open multi-repo workspaces
\033[1;33mTerminal integration:\033[0m Open repositories in new GNOME Terminal tabs
\033[1;33mExplorer integration:\033[0m Open repositories in your file manager
\033[1;33mRelease automation:\033[0m Version bumping, changelog updates, and tagging
\033[1;33mBatch operations:\033[0m Execute shell commands across multiple repositories
\033[1;33mGit/Docker/Make wrappers:\033[0m Unified command proxying for many tools
\033[1mCapabilities:\033[0m
• Clone, pull, verify, update, and manage many repositories at once
• Resolve dependencies across languages and ecosystems
• Standardize install/update workflows
• Create symbolic executable wrappers for any project
• Merge configuration from default + user config layers
Use pkgmgr as both a robust package management framework and a versatile
development orchestration tool.
For detailed help on each command, use:
\033[1mpkgmgr <command> --help\033[0m
""" """
def main() -> None: def main() -> None:
""" """
Entry point for the pkgmgr CLI. Entry point for the pkgmgr CLI.