Compare commits

..

27 Commits

Author SHA1 Message Date
Kevin Veen-Birkenbach
eeda944b73 ci: migrate tests to reusable workflows and introduce stable-tag pipeline
- convert all test workflows to reusable workflow_call
- add central CI workflow for branches and PRs
- add mark-stable workflow triggered on main pushes
- ensure stable tag updates only after all tests succeed
- remove duplicated triggers from test workflows
`

https://chatgpt.com/share/693aa4a6-7460-800f-ba47-cfc15b1b2236
2025-12-11 13:04:44 +01:00
Kevin Veen-Birkenbach
52cfbebba4 ci: make mark-stable robust for workflow_run
- fetch workflow_run runs without head_sha filter
- match by workflow name and head_sha in jq
- keep tagging logic and permissions unchanged

https://chatgpt.com/share/693aa4a6-7460-800f-ba47-cfc15b1b2236
2025-12-11 12:46:42 +01:00
Kevin Veen-Birkenbach
f4385807f1 e2e: disable Nix sandbox for cross-distro flake build test
- Update test_nix_build_pkgmgr.py to invoke
    nix --option sandbox false build .#pkgmgr -L
  to avoid sandbox/permission issues in Debian and Ubuntu containers.
- Keeps the test logic identical across all distros while ensuring
  consistent flake build behaviour during E2E runs.

https://chatgpt.com/share/693aa33f-4e3c-800f-86ec-99c38a07eacb
2025-12-11 12:45:04 +01:00
Kevin Veen-Birkenbach
e9e083c9dd ci: finalize mark-stable workflow fixes
- use correct GitHub API path (/repos/.../actions/runs)
- resolve repository via workflow_run.repository.full_name
- improve logging and safe no-tag exits
- ensure correct token handling and tag update logic

https://chatgpt.com/share/693aa4a6-7460-800f-ba47-cfc15b1b2236
2025-12-11 12:38:12 +01:00
Kevin Veen-Birkenbach
3218b2b39f ci: fix mark-stable workflow for workflow_run events
- use workflow_run.repository.full_name for gh API queries
- expose GITHUB_TOKEN as GH_TOKEN for the GitHub CLI
- improve log messages and keep tag skipped when checks are missing or failing
2025-12-11 12:26:29 +01:00
Kevin Veen-Birkenbach
ba296a79c9 ci: fix mark-stable permissions and ignore Nix result symlink
https://chatgpt.com/share/693aa4a6-7460-800f-ba47-cfc15b1b2236
2025-12-11 12:16:34 +01:00
Kevin Veen-Birkenbach
62e05e2f5b ci: tag commit as stable after full test matrix
- add mark-stable workflow that runs on workflow_run for all test pipelines
- use GitHub API to ensure all required workflows succeeded before moving the 'stable' tag
- add Nix flake.lock to pin nixpkgs for reproducible builds

https://chatgpt.com/share/693aa4a6-7460-800f-ba47-cfc15b1b2236
2025-12-11 12:01:21 +01:00
Kevin Veen-Birkenbach
77d8b68ba5 Add E2E Nix flake build test across all distro containers
- Introduce tests/e2e/test_nix_build_pkgmgr.py to inspect the Nix environment
  and build the pkgmgr flake inside the container started by test-e2e.sh
- Run the same commands in every distro container: nix --version, sandbox
  config, id, and nix build .#pkgmgr -L
- Print stdout/stderr and assert the flake build succeeds for easier
  cross-distro Nix debugging

https://chatgpt.com/share/693aa33f-4e3c-800f-86ec-99c38a07eacb
2025-12-11 11:55:43 +01:00
Kevin Veen-Birkenbach
bb0a801396 Fix Git safe.directory handling in E2E containers
- Mark /src and /src/.git as safe to satisfy newer Git ownership checks
- Add '*' as safe.directory for ephemeral test containers to avoid Nix flake failures

https://chatgpt.com/share/693a9e1f-1cc8-800f-9df4-90813cbb6bd5
2025-12-11 11:33:51 +01:00
Kevin Veen-Birkenbach
ee968efc4b Harden E2E test runner and fix Git safe.directory in containers
- Quote Nix store/cache volumes and distro image name in docker run
- Use strict bash flags (set -euo pipefail) inside test container
- Print distro ID robustly with fallback
- Configure /src as Git safe.directory when git is available

https://chatgpt.com/share/693a9c0e-59ec-800f-83a1-eec31bd76962
2025-12-11 11:25:11 +01:00
Kevin Veen-Birkenbach
644b2b8fa0 Align Nix Python environment and add lazy CLI import
- Switch flake package and dev shell to Python 3.11 to match pyproject
- Ensure the python-with-deps environment is preferred on PATH in nix develop
- Introduce a lightweight pkgmgr __init__ with lazy loading of pkgmgr.cli
- Avoid pulling in CLI/config dependencies on plain `import pkgmgr`, fixing
  unit test imports and PyYAML availability in the Nix test containers

https://chatgpt.com/share/693a9723-27ac-800f-a6c2-c1bcc91b7dff
2025-12-11 11:04:12 +01:00
Kevin Veen-Birkenbach
0f74907f82 flake.nix: switch to generic python3 and remove side-effects from pkgmgr package root
- Replace hardcoded python311 references with generic python3 to avoid minor
  version pinning and ensure consistent interpreter selection across systems.
- Use python.pkgs instead of python311Packages in the build pipeline.
- Update devShell to use python3.withPackages, including pip and pyyaml.
- Add Python version echo in shellHook for improved debugging.
- Remove cli re-export from src/pkgmgr/__init__.py to eliminate heavy
  side-effects during import and prevent premature config loading in tests.
2025-12-11 10:30:19 +01:00
Kevin Veen-Birkenbach
5a8b1b11de arch packaging: exclude assets from PKGBUILD rsync
Exclude the assets/ directory from the PKGBUILD rsync step to avoid
permission issues (e.g. map.png) when building the Arch package in
Docker as aur_builder.

https://chatgpt.com/share/693a8c25-4464-800f-8d5e-5c4579d78b52
2025-12-11 10:17:14 +01:00
Kevin Veen-Birkenbach
389ec40163 Refine Nix dev shell, ensure PyYAML availability, fix Python invocation, and
expose pkgmgr.cli for Python 3.13 compatibility

- Add `.nix-dev-installed` to .gitignore
- Improve flake.nix:
  * unify pkgs/pyPkgs definitions
  * provide python311.withPackages including pip + PyYAML
  * remove unused pkgmgrPkg reference from devShell
  * fix PYTHONPATH export and devShell help message
- Update unit/integration test scripts to use `python3 -m unittest`
- Add top-level pkgmgr.__init__ exposing `cli` attribute for
  pkgutil.resolve_name compatibility under Python 3.13+
2025-12-11 09:33:55 +01:00
Kevin Veen-Birkenbach
1d03055491 Removed ignore files 2025-12-11 09:07:18 +01:00
Kevin Veen-Birkenbach
7775c6d974 Refine packaging layout and Arch build paths
* Move Arch-specific ignore rules into `packaging/arch/.gitignore` and simplify top-level `.gitignore`/`.dockerignore`.
* Update Arch `PKGBUILD` to sync from the project root and drop `packaging/` from the installed tree.
* Fix OS-specific `package.sh` helpers to resolve the new `packaging/*` locations correctly for Arch, Debian/Ubuntu, Fedora, and CentOS.
2025-12-11 09:04:17 +01:00
Kevin Veen-Birkenbach
a24a819511 Restructure repo layout, wiring src/ and packaging for local and distro builds
- Add dev runner main.py that prefers local src/ over installed pkgmgr
- Move Arch/Debian/Fedora packaging files under packaging/* and update build scripts
- Adjust .gitignore/.dockerignore for new packaging paths and src/source/
- Improve config defaults discovery to support src/ layout and installed packages
- Update architecture diagram and add TODO overview for TAGS/MIRROR/SIGNING_KEY

https://chatgpt.com/share/693a76a0-e408-800f-9939-868524cbef4d
2025-12-11 08:45:07 +01:00
Kevin Veen-Birkenbach
0a6c2f2988 Release version 0.9.1 2025-12-10 22:56:04 +01:00
Kevin Veen-Birkenbach
0c90e984ad Refine setup workflows and add architecture map
- Split virgin tests into separate root and user GitHub Actions workflows
  (test-virgin-root, test-virgin-user) and adjust Arch container flows
- Introduce scripts/installation/venv-create.sh and reuse it from
  scripts/installation/main.sh with separate root/system and user/dev paths
- Add PKGMGR architecture & setup map (assets/map.png) and section in README
  with link to the up-to-date master page
- Simplify README by removing outdated Docker quickstart, usage examples,
  and AI footer
- Extend .gitignore to exclude src/source artifacts

https://chatgpt.com/share/6939bbfe-5cb0-800f-8ea8-95628dc911f5
2025-12-10 22:51:40 +01:00
Kevin Veen-Birkenbach
0a0cbbfe6d fix(init-nix): create 'nix' user with a valid shell across all distros
The init-nix.sh script previously hardcoded /usr/bin/bash as the login shell
for the 'nix' user, which exists on Arch but not on Debian. This caused the
Nix single-user installer (run via `su - nix`) to fail silently or break in
unpredictable ways on Debian-based images.

We now resolve the shell dynamically via `command -v bash` and fall back to
/bin/sh on minimal systems. This makes Nix installation deterministic across
Arch, Debian, Ubuntu, Fedora, CentOS and CI containers.

https://chatgpt.com/share/6939e97f-c93c-800f-887b-27c7e67ec46d
2025-12-10 22:43:20 +01:00
Kevin Veen-Birkenbach
15c44cd484 Removed deprecated pkgmgr.yml 2025-12-10 21:34:33 +01:00
Kevin Veen-Birkenbach
6d7ee6fc04 Fix test scripts: ensure default distro and always run via bash
- Remove Makefile inline variable export (distro=arch) and invoke scripts via bash
- Add robust default in test-unit.sh and test-integration.sh:
    : "${distro:=arch}"
- Prevent "unbound variable" errors under `set -u` when no distro is provided
2025-12-10 21:09:18 +01:00
Kevin Veen-Birkenbach
5a022db0db Use dynamic distro selection for UNIT and INTEGRATION tests
- Pass `distro=arch` from Makefile into test scripts
- Replace hardcoded "arch" references with "${distro}"
- Update test-unit.sh and test-integration.sh to use dynamic image names
- Improve log output to reflect selected distro

https://chatgpt.com/share/6939c98a-d428-800f-8bb8-cf72e80ba80c
2025-12-10 20:27:03 +01:00
Kevin Veen-Birkenbach
37ac22e0b4 test: isolate Nix store/cache per distro to fix cross-distro manifest conflicts
- Replace shared Nix volumes with distro-specific volumes
  (pkgmgr_nix_store_<distro>, pkgmgr_nix_cache_<distro>)
- Prevent incompatible profile manifest versions between Ubuntu and Debian
- Update all test scripts (unit, integration, container, e2e)
- Remove unused global Nix volume variables from Makefile
- Improve consistency of test-e2e.sh formatting and environment handling
- Add Git safe.directory configuration for mounted /src to avoid ownership warnings
2025-12-10 20:07:41 +01:00
Kevin Veen-Birkenbach
bcea440e40 Fix path and shell repo directory resolution + add unit/E2E tests
- Introduce `_resolve_repository_directory()` to unify directory lookup
  (explicit `directory` key → fallback to `get_repo_dir()` using base dir)
- Fix `pkgmgr path` to avoid KeyError and behave consistently with
  other commands using lazy directory resolution
- Fix `pkgmgr shell` to use resolved directory and correctly emit cwd
- Add full E2E tests for `pkgmgr path --all` and `pkgmgr path pkgmgr`
- Add unit tests covering:
    * explicit directory usage
    * fallback resolution via get_repo_dir()
    * empty selection behavior
    * shell command cwd resolution
    * missing shell command error handling
2025-12-10 19:47:26 +01:00
Kevin Veen-Birkenbach
6edde2d65b Release version 0.9.0 2025-12-10 18:38:10 +01:00
Kevin Veen-Birkenbach
74189c1e14 Add virgin Nix flake E2E workflow and update .gitignore
- Introduce `test-nix-flake-e2e.yml` workflow to run a full Arch-based virgin
  environment test with Nix flakes enabled and shared Docker caches
- Ensure pkgmgr self-installation and flake-based installer path are exercised
- Update .gitignore with additional build artifacts, Debian packaging files,
  and pkgmgr output directories
2025-12-10 18:37:29 +01:00
114 changed files with 1099 additions and 212 deletions

View File

@@ -25,7 +25,5 @@ venv/
.DS_Store .DS_Store
Thumbs.db Thumbs.db
# Arch pkg artifacts # Logs
*.pkg.tar.* *.log
*.log
package-manager-*

26
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: CI
on:
push:
branches-ignore:
- main
pull_request:
jobs:
test-unit:
uses: ./.github/workflows/test-unit.yml
test-integration:
uses: ./.github/workflows/test-integration.yml
test-container:
uses: ./.github/workflows/test-container.yml
test-e2e:
uses: ./.github/workflows/test-e2e.yml
test-virgin-user:
uses: ./.github/workflows/test-virgin-user.yml
test-virgin-root:
uses: ./.github/workflows/test-virgin-root.yml

64
.github/workflows/mark-stable.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Mark stable commit
on:
push:
branches:
- main
jobs:
test-unit:
uses: ./.github/workflows/test-unit.yml
test-integration:
uses: ./.github/workflows/test-integration.yml
test-container:
uses: ./.github/workflows/test-container.yml
test-e2e:
uses: ./.github/workflows/test-e2e.yml
test-virgin-user:
uses: ./.github/workflows/test-virgin-user.yml
test-virgin-root:
uses: ./.github/workflows/test-virgin-root.yml
mark-stable:
needs:
- test-unit
- test-integration
- test-container
- test-e2e
- test-virgin-user
- test-virgin-root
runs-on: ubuntu-latest
permissions:
contents: write # to move the tag
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Move 'stable' tag to this commit
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
echo "Tagging commit $GITHUB_SHA as stable…"
# delete local tag if exists
git tag -d stable 2>/dev/null || true
# delete remote tag if exists
git push origin :refs/tags/stable || true
# create new tag on this commit
git tag stable "$GITHUB_SHA"
git push origin stable
echo "✅ Stable tag updated."

View File

@@ -1,13 +1,7 @@
name: Test OS Containers name: Test OS Containers
on: on:
push: workflow_call:
branches:
- main
- master
- develop
- "*"
pull_request:
jobs: jobs:
test-container: test-container:

View File

@@ -1,13 +1,7 @@
name: Test End-To-End name: Test End-To-End
on: on:
push: workflow_call:
branches:
- main
- master
- develop
- "*"
pull_request:
jobs: jobs:
test-e2e: test-e2e:

View File

@@ -1,13 +1,7 @@
name: Test Code Integration name: Test Code Integration
on: on:
push: workflow_call:
branches:
- main
- master
- develop
- "*"
pull_request:
jobs: jobs:
test-integration: test-integration:

View File

@@ -1,13 +1,7 @@
name: Test Units name: Test Units
on: on:
push: workflow_call:
branches:
- main
- master
- develop
- "*"
pull_request:
jobs: jobs:
test-unit: test-unit:

58
.github/workflows/test-virgin-root.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Test Virgin Root
on:
workflow_call:
jobs:
test-virgin-root:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Show Docker version
run: docker version
- name: Virgin Arch pkgmgr flake test (root)
run: |
set -euo pipefail
echo ">>> Starting virgin ArchLinux container test (root, with shared caches)..."
docker run --rm \
-v "$PWD":/src \
-v pkgmgr_repos:/root/Repositories \
-v pkgmgr_pip_cache:/root/.cache/pip \
-w /src \
archlinux:latest \
bash -lc '
set -euo pipefail
echo ">>> Updating and upgrading Arch system..."
pacman -Syu --noconfirm git python python-pip nix >/dev/null
echo ">>> Creating isolated virtual environment for pkgmgr..."
python -m venv /tmp/pkgmgr-venv
echo ">>> Activating virtual environment..."
source /tmp/pkgmgr-venv/bin/activate
echo ">>> Upgrading pip (cached)..."
python -m pip install --upgrade pip >/dev/null
echo ">>> Installing pkgmgr from current source tree (cached pip)..."
python -m pip install /src >/dev/null
echo ">>> Enabling Nix experimental features..."
export NIX_CONFIG="experimental-features = nix-command flakes"
echo ">>> Running: pkgmgr update pkgmgr --clone-mode shallow --no-verification"
pkgmgr update pkgmgr --clone-mode shallow --no-verification
echo ">>> Running: pkgmgr version pkgmgr"
pkgmgr version pkgmgr
echo ">>> Virgin Arch (root) test completed successfully."
'

73
.github/workflows/test-virgin-user.yml vendored Normal file
View File

@@ -0,0 +1,73 @@
name: Test Virgin User
on:
workflow_call:
jobs:
test-virgin-user:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Show Docker version
run: docker version
- name: Virgin Arch pkgmgr user test (non-root with sudo)
run: |
set -euo pipefail
echo ">>> Starting virgin ArchLinux container test (non-root user with sudo)..."
docker run --rm \
-v "$PWD":/src \
archlinux:latest \
bash -lc '
set -euo pipefail
echo ">>> [root] Updating and upgrading Arch system..."
pacman -Syu --noconfirm git python python-pip sudo base-devel debugedit
echo ">>> [root] Creating non-root user dev..."
useradd -m dev
echo ">>> [root] Allowing passwordless sudo for dev..."
echo "dev ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/dev
chmod 0440 /etc/sudoers.d/dev
echo ">>> [root] Adjusting ownership of /src for dev..."
chown -R dev:dev /src
echo ">>> [root] Running pkgmgr flow as non-root user dev..."
sudo -u dev env PKGMGR_DISABLE_NIX_FLAKE_INSTALLER=1 bash -lc "
set -euo pipefail
cd /src
echo \">>> [dev] Using user: \$(whoami)\"
echo \">>> [dev] Running scripts/installation/main.sh...\"
bash scripts/installation/main.sh
echo \">>> [dev] Activating venv...\"
. \"\$HOME/.venvs/pkgmgr/bin/activate\"
echo \">>> [dev] Installing pkgmgr into venv via pip...\"
python -m pip install /src >/dev/null
echo \">>> [dev] PKGMGR_DISABLE_NIX_FLAKE_INSTALLER=\$PKGMGR_DISABLE_NIX_FLAKE_INSTALLER\"
echo \">>> [dev] Updating managed repo package-manager via pkgmgr...\"
pkgmgr update pkgmgr --clone-mode shallow --no-verification
echo \">>> [dev] PATH:\"
echo \"\$PATH\"
echo \">>> [dev] which pkgmgr:\"
which pkgmgr || echo \">>> [dev] pkgmgr not found in PATH\"
echo \">>> [dev] Running: pkgmgr version pkgmgr\"
pkgmgr version pkgmgr
"
echo ">>> [root] Container flow finished."
'

13
.gitignore vendored
View File

@@ -1,9 +1,6 @@
# Prevents unwanted files from being committed to version control. # Prevents unwanted files from being committed to version control.
# Custom Config file
config/config.yaml
# Python bytecode # Python bytecode
__pycache__/ __pycache__/
*.pyc *.pyc
@@ -17,6 +14,7 @@ venv/
dist/ dist/
build/* build/*
*.egg-info/ *.egg-info/
package-manager-*
# Editor files # Editor files
.vscode/ .vscode/
@@ -28,14 +26,9 @@ Thumbs.db
# Nix Cache to speed up tests # Nix Cache to speed up tests
.nix/ .nix/
.nix-dev-installed
# Ignore logs # Ignore logs
*.log *.log
package-manager-*
# debian result
debian/package-manager/
debian/debhelper-build-stamp
debian/files
debian/.debhelper/
debian/package-manager.substvars

View File

@@ -1,3 +1,16 @@
## [0.9.1] - 2025-12-10
* * Refactored installer: new `venv-create.sh`, cleaner root/user setup flow, updated README with architecture map.
* Split virgin tests into root/user workflows; stabilized Nix installer across distros; improved test scripts with dynamic distro selection and isolated Nix stores.
* Fixed repository directory resolution; improved `pkgmgr path` and `pkgmgr shell`; added full unit/E2E coverage.
* Removed deprecated files and updated `.gitignore`.
## [0.9.0] - 2025-12-10
* Introduce a virgin Arch-based Nix flake E2E workflow that validates pkgmgrs full flake installation path using shared caches for faster and reproducible CI runs.
## [0.8.0] - 2025-12-10 ## [0.8.0] - 2025-12-10
* **v0.7.15 — Installer & Command Resolution Improvements** * **v0.7.15 — Installer & Command Resolution Improvements**

View File

@@ -2,12 +2,6 @@
test build build-no-cache test-unit test-e2e test-integration \ test build build-no-cache test-unit test-e2e test-integration \
test-container test-container
# ------------------------------------------------------------
# Local Nix cache directories in the repo
# ------------------------------------------------------------
NIX_STORE_VOLUME := pkgmgr_nix_store
NIX_CACHE_VOLUME := pkgmgr_nix_cache
# ------------------------------------------------------------ # ------------------------------------------------------------
# Distro list and base images # Distro list and base images
# (kept for documentation/reference; actual build logic is in scripts/build) # (kept for documentation/reference; actual build logic is in scripts/build)

View File

@@ -24,6 +24,15 @@
- **Custom Aliases:** - **Custom Aliases:**
Generate and manage custom aliases for easy command invocation. Generate and manage custom aliases for easy command invocation.
## Architecture & Setup Map 🗺️
The following diagram provides a full overview of PKGMGRs package structure,
installation layers, and setup controller flow:
![PKGMGR Architecture](assets/map.png)
**Diagram status:** *Stand: 11. Dezember 2025*
**Always-up-to-date version:** https://s.veen.world/pkgmgrmp
## Installation ⚙️ ## Installation ⚙️
@@ -51,55 +60,6 @@ The `make setup` command will:
- Install required packages from `requirements.txt`. - Install required packages from `requirements.txt`.
- Execute `python main.py install` to complete the installation. - Execute `python main.py install` to complete the installation.
## Docker Quickstart 🐳
Alternatively to installing locally, you can use Docker: build the image with
```bash
docker build --no-cache -t pkgmgr .
```
or alternativ pull it via
```bash
docker pull kevinveenbirkenbach/pkgmgr:latest
```
and then run
```bash
docker run --rm pkgmgr --help
```
## Usage 📖
Run the script with different commands. For example:
- **Install all packages:**
```bash
pkgmgr install --all
```
- **Pull updates for a specific repository:**
```bash
pkgmgr pull pkgmgr
```
- **Commit changes with extra Git parameters:**
```bash
pkgmgr commit pkgmgr -- -m "Your commit message"
```
- **List all configured packages:**
```bash
pkgmgr config show
```
- **Manage configuration:**
```bash
pkgmgr config init
pkgmgr config add
pkgmgr config edit
pkgmgr config delete <identifier>
pkgmgr config ignore <identifier> --set true
```
## License 📄 ## License 📄
This project is licensed under the MIT License. This project is licensed under the MIT License.
@@ -108,9 +68,3 @@ This project is licensed under the MIT License.
Kevin Veen-Birkenbach Kevin Veen-Birkenbach
[https://www.veen.world](https://www.veen.world) [https://www.veen.world](https://www.veen.world)
---
**Repository:** [github.com/kevinveenbirkenbach/package-manager](https://github.com/kevinveenbirkenbach/package-manager)
*Created with AI 🤖 - [View conversation](https://chatgpt.com/share/67c728c4-92d0-800f-8945-003fa9bf27c6)*

7
TODO.md Normal file
View File

@@ -0,0 +1,7 @@
# to-dos
For the following checkout the implementation map:
- Implement TAGS
- Implement MIRROR
- Implement SIGNING_KEY

BIN
assets/map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

27
flake.lock generated Normal file
View File

@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1765186076,
"narHash": "sha256-hM20uyap1a0M9d344I692r+ik4gTMyj60cQWO+hAYP8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "addf7cf5f383a3101ecfba091b98d0a1263dc9b8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -26,12 +26,17 @@
packages = forAllSystems (system: packages = forAllSystems (system:
let let
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
# Single source of truth for pkgmgr: Python 3.11
# - Matches pyproject.toml: requires-python = ">=3.11"
# - Uses python311Packages so that PyYAML etc. are available
python = pkgs.python311;
pyPkgs = pkgs.python311Packages; pyPkgs = pkgs.python311Packages;
in in
rec { rec {
pkgmgr = pyPkgs.buildPythonApplication { pkgmgr = pyPkgs.buildPythonApplication {
pname = "package-manager"; pname = "package-manager";
version = "0.8.0"; version = "0.9.1";
# Use the git repo as source # Use the git repo as source
src = ./.; src = ./.;
@@ -45,7 +50,7 @@
pyPkgs.wheel pyPkgs.wheel
]; ];
# Runtime dependencies (matches [project.dependencies]) # Runtime dependencies (matches [project.dependencies] in pyproject.toml)
propagatedBuildInputs = [ propagatedBuildInputs = [
pyPkgs.pyyaml pyPkgs.pyyaml
pyPkgs.pip pyPkgs.pip
@@ -55,6 +60,7 @@
pythonImportsCheck = [ "pkgmgr" ]; pythonImportsCheck = [ "pkgmgr" ];
}; };
default = pkgmgr; default = pkgmgr;
} }
); );
@@ -65,29 +71,42 @@
devShells = forAllSystems (system: devShells = forAllSystems (system:
let let
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
pkgmgrPkg = self.packages.${system}.pkgmgr;
ansiblePkg = ansiblePkg =
if pkgs ? ansible-core then pkgs.ansible-core if pkgs ? ansible-core then pkgs.ansible-core
else pkgs.ansible; else pkgs.ansible;
# Python 3 + pip für alles, was "python3 -m pip" macht # Use the same Python version as the package (3.11)
pythonWithPip = pkgs.python3.withPackages (ps: [ python = pkgs.python311;
pythonWithDeps = python.withPackages (ps: [
ps.pip ps.pip
ps.pyyaml
]); ]);
in in
{ {
default = pkgs.mkShell { default = pkgs.mkShell {
buildInputs = [ buildInputs = [
pythonWithPip pythonWithDeps
pkgmgrPkg
pkgs.git pkgs.git
ansiblePkg ansiblePkg
]; ];
shellHook = '' shellHook = ''
# Ensure our Python with dependencies is preferred on PATH
export PATH=${pythonWithDeps}/bin:$PATH
# Ensure src/ layout is importable:
# pkgmgr lives in ./src/pkgmgr
export PYTHONPATH="$PWD/src:${PYTHONPATH:-}"
# Also add repo root in case tools/tests rely on it
export PYTHONPATH="$PWD:$PYTHONPATH"
echo "Entered pkgmgr development shell for ${system}" echo "Entered pkgmgr development shell for ${system}"
echo "pkgmgr CLI is available via the flake build" echo "Python used in this shell:"
python --version
echo "pkgmgr CLI (from source) is available via:"
echo " python -m pkgmgr.cli --help"
''; '';
}; };
} }

10
main.py
View File

@@ -1,6 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys
from pathlib import Path
# Ensure local src/ overrides installed package
ROOT = Path(__file__).resolve().parent
SRC = ROOT / "src"
if SRC.is_dir():
sys.path.insert(0, str(SRC))
from pkgmgr.cli import main from pkgmgr.cli import main
if __name__ == "__main__": if __name__ == "__main__":
main() main()

6
packaging/arch/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
# Arch pkg artifacts
*.pkg.tar.*
*.log
package-manager-*
src/
pkg/

View File

@@ -1,7 +1,7 @@
# Maintainer: Kevin Veen-Birkenbach <info@veen.world> # Maintainer: Kevin Veen-Birkenbach <info@veen.world>
pkgname=package-manager pkgname=package-manager
pkgver=0.8.0 pkgver=0.9.1
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')
@@ -15,7 +15,7 @@ makedepends=('rsync')
install=${pkgname}.install install=${pkgname}.install
# Local source checkout — avoids the tarball requirement. # Local source checkout — avoids the tarball requirement.
# This assumes you build the package from inside the main project repository. # We build from the project root (two levels above packaging/arch/).
source=() source=()
sha256sums=() sha256sums=()
@@ -24,12 +24,18 @@ _srcdir_name="source"
prepare() { prepare() {
mkdir -p "$srcdir/$_srcdir_name" mkdir -p "$srcdir/$_srcdir_name"
local project_root
project_root="$(cd "$startdir/../.." && pwd)"
rsync -a \ rsync -a \
--exclude=".git" \ --exclude=".git" \
--exclude=".github" \ --exclude=".github" \
--exclude="pkg" \ --exclude="pkg" \
--exclude="srcpkg" \ --exclude="srcpkg" \
"$startdir/" "$srcdir/$_srcdir_name/" --exclude="packaging" \
--exclude="assets" \
"$project_root/" "$srcdir/$_srcdir_name/"
} }
build() { build() {
@@ -62,7 +68,8 @@ package() {
"$pkgdir/usr/lib/package-manager/PKGBUILD" \ "$pkgdir/usr/lib/package-manager/PKGBUILD" \
"$pkgdir/usr/lib/package-manager/Dockerfile" \ "$pkgdir/usr/lib/package-manager/Dockerfile" \
"$pkgdir/usr/lib/package-manager/debian" \ "$pkgdir/usr/lib/package-manager/debian" \
"$pkgdir/usr/lib/package-manager/packaging" \
"$pkgdir/usr/lib/package-manager/.gitignore" \ "$pkgdir/usr/lib/package-manager/.gitignore" \
"$pkgdir/usr/lib/package-manager/__pycache__" \ "$pkgdir/usr/lib/package-manager/__pycache__" \
"$pkgdir/usr/lib/package-manager/.gitkeep" "$pkgdir/usr/lib/package-manager/.gitkeep" || true
} }

6
packaging/debian/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
# debian
package-manager/
debhelper-build-stamp
files
.debhelper/
package-manager.substvars

View File

@@ -1,3 +1,18 @@
package-manager (0.9.1-1) unstable; urgency=medium
* * Refactored installer: new `venv-create.sh`, cleaner root/user setup flow, updated README with architecture map.
* Split virgin tests into root/user workflows; stabilized Nix installer across distros; improved test scripts with dynamic distro selection and isolated Nix stores.
* Fixed repository directory resolution; improved `pkgmgr path` and `pkgmgr shell`; added full unit/E2E coverage.
* Removed deprecated files and updated `.gitignore`.
-- Kevin Veen-Birkenbach <kevin@veen.world> Wed, 10 Dec 2025 22:56:01 +0100
package-manager (0.9.0-1) unstable; urgency=medium
* Introduce a virgin Arch-based Nix flake E2E workflow that validates pkgmgrs full flake installation path using shared caches for faster and reproducible CI runs.
-- Kevin Veen-Birkenbach <kevin@veen.world> Wed, 10 Dec 2025 18:38:07 +0100
package-manager (0.8.0-1) unstable; urgency=medium package-manager (0.8.0-1) unstable; urgency=medium
* **v0.7.15 — Installer & Command Resolution Improvements** * **v0.7.15 — Installer & Command Resolution Improvements**

View File

@@ -1,5 +1,5 @@
Name: package-manager Name: package-manager
Version: 0.8.0 Version: 0.9.1
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
@@ -77,6 +77,15 @@ echo ">>> package-manager removed. Nix itself was not removed."
/usr/lib/package-manager/ /usr/lib/package-manager/
%changelog %changelog
* Wed Dec 10 2025 Kevin Veen-Birkenbach <kevin@veen.world> - 0.9.1-1
- * Refactored installer: new `venv-create.sh`, cleaner root/user setup flow, updated README with architecture map.
* Split virgin tests into root/user workflows; stabilized Nix installer across distros; improved test scripts with dynamic distro selection and isolated Nix stores.
* Fixed repository directory resolution; improved `pkgmgr path` and `pkgmgr shell`; added full unit/E2E coverage.
* Removed deprecated files and updated `.gitignore`.
* Wed Dec 10 2025 Kevin Veen-Birkenbach <kevin@veen.world> - 0.9.0-1
- Introduce a virgin Arch-based Nix flake E2E workflow that validates pkgmgrs full flake installation path using shared caches for faster and reproducible CI runs.
* Wed Dec 10 2025 Kevin Veen-Birkenbach <kevin@veen.world> - 0.8.0-1 * Wed Dec 10 2025 Kevin Veen-Birkenbach <kevin@veen.world> - 0.8.0-1
- **v0.7.15 Installer & Command Resolution Improvements** - **v0.7.15 Installer & Command Resolution Improvements**

View File

@@ -1,7 +0,0 @@
version: 1
author: "Kevin Veen-Birkenbach"
url: "https://github.com/kevinveenbirkenbach/package-manager"
description: "A configurable Python-based package manager for managing multiple repositories via Bash."
dependencies: []

View File

@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "package-manager" name = "package-manager"
version = "0.8.0" version = "0.9.1"
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"
@@ -39,13 +39,13 @@ pkgmgr = "pkgmgr.cli:main"
# ----------------------------- # -----------------------------
# setuptools configuration # setuptools configuration
# ----------------------------- # -----------------------------
# We use find_packages(), not a fixed list, # Source layout: all packages live under "src/"
# and explicitly include pkgmgr* and config* [tool.setuptools]
package-dir = { "" = "src", "config" = "config" }
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
where = ["."] where = ["src", "."]
include = ["pkgmgr*", "config*"] include = ["pkgmgr*", "config*"]
# Ensure defaults.yaml is shipped inside wheels & nix builds
[tool.setuptools.package-data] [tool.setuptools.package-data]
"config" = ["defaults.yaml"] "config" = ["defaults.yaml"]

View File

@@ -94,7 +94,15 @@ if [[ "${IN_CONTAINER}" -eq 1 && "${EUID:-0}" -eq 0 ]]; then
# Ensure "nix" user (home at /home/nix) # Ensure "nix" user (home at /home/nix)
if ! id nix >/dev/null 2>&1; then if ! id nix >/dev/null 2>&1; then
echo "[init-nix] Creating user 'nix'..." echo "[init-nix] Creating user 'nix'..."
useradd -m -r -g nixbld -s /usr/bin/bash 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
useradd -m -r -g nixbld -s "${BASH_SHELL}" nix
fi fi
# Ensure /nix exists and is writable by the "nix" user. # Ensure /nix exists and is writable by the "nix" user.

View File

@@ -3,10 +3,21 @@ set -euo pipefail
echo "[arch/package] Building Arch package (makepkg --nodeps)..." echo "[arch/package] Building Arch package (makepkg --nodeps)..."
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
PKG_DIR="${PROJECT_ROOT}/packaging/arch"
if [[ ! -f "${PKG_DIR}/PKGBUILD" ]]; then
echo "[arch/package] ERROR: PKGBUILD not found in ${PKG_DIR}"
exit 1
fi
cd "${PKG_DIR}"
if id aur_builder >/dev/null 2>&1; then if id aur_builder >/dev/null 2>&1; then
echo "[arch/package] Using 'aur_builder' user for makepkg..." echo "[arch/package] Using 'aur_builder' user for makepkg..."
chown -R aur_builder:aur_builder "$(pwd)" chown -R aur_builder:aur_builder "${PKG_DIR}"
su aur_builder -c "cd '$(pwd)' && rm -f package-manager-*.pkg.tar.* && makepkg --noconfirm --clean --nodeps" su aur_builder -c "cd '${PKG_DIR}' && rm -f package-manager-*.pkg.tar.* && makepkg --noconfirm --clean --nodeps"
else else
echo "[arch/package] WARNING: user 'aur_builder' not found, running makepkg as current user..." echo "[arch/package] WARNING: user 'aur_builder' not found, running makepkg as current user..."
rm -f package-manager-*.pkg.tar.* rm -f package-manager-*.pkg.tar.*

View File

@@ -4,8 +4,17 @@ set -euo pipefail
echo "[centos/package] Setting up rpmbuild directories..." echo "[centos/package] Setting up rpmbuild directories..."
mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
SPEC_PATH="${PROJECT_ROOT}/packaging/fedora/package-manager.spec"
if [[ ! -f "${SPEC_PATH}" ]]; then
echo "[centos/package] ERROR: SPEC file not found: ${SPEC_PATH}"
exit 1
fi
echo "[centos/package] Extracting version from package-manager.spec..." echo "[centos/package] Extracting version from package-manager.spec..."
version="$(grep -E '^Version:' package-manager.spec | awk '{print $2}')" version="$(grep -E '^Version:' "${SPEC_PATH}" | awk '{print $2}')"
if [[ -z "${version}" ]]; then if [[ -z "${version}" ]]; then
echo "ERROR: Version missing!" echo "ERROR: Version missing!"
exit 1 exit 1
@@ -15,13 +24,13 @@ srcdir="package-manager-${version}"
echo "[centos/package] Preparing source tree: ${srcdir}" echo "[centos/package] Preparing source tree: ${srcdir}"
rm -rf "/tmp/${srcdir}" rm -rf "/tmp/${srcdir}"
mkdir -p "/tmp/${srcdir}" mkdir -p "/tmp/${srcdir}"
cp -a . "/tmp/${srcdir}/" cp -a "${PROJECT_ROOT}/." "/tmp/${srcdir}/"
echo "[centos/package] Creating source tarball..." echo "[centos/package] Creating source tarball..."
tar czf "/root/rpmbuild/SOURCES/${srcdir}.tar.gz" -C /tmp "${srcdir}" tar czf "/root/rpmbuild/SOURCES/${srcdir}.tar.gz" -C /tmp "${srcdir}"
echo "[centos/package] Copying SPEC..." echo "[centos/package] Copying SPEC..."
cp package-manager.spec /root/rpmbuild/SPECS/ cp "${SPEC_PATH}" /root/rpmbuild/SPECS/
echo "[centos/package] Running rpmbuild..." echo "[centos/package] Running rpmbuild..."
cd /root/rpmbuild/SPECS cd /root/rpmbuild/SPECS

View File

@@ -3,6 +3,25 @@ set -euo pipefail
echo "[debian/package] Building Debian package..." echo "[debian/package] Building Debian package..."
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
BUILD_ROOT="/tmp/package-manager-debian-build"
rm -rf "${BUILD_ROOT}"
mkdir -p "${BUILD_ROOT}"
echo "[debian/package] Syncing project sources to ${BUILD_ROOT}..."
rsync -a \
--exclude 'packaging/debian' \
"${PROJECT_ROOT}/" "${BUILD_ROOT}/"
echo "[debian/package] Overlaying debian/ metadata from packaging/debian..."
mkdir -p "${BUILD_ROOT}/debian"
cp -a "${PROJECT_ROOT}/packaging/debian/." "${BUILD_ROOT}/debian/"
cd "${BUILD_ROOT}"
echo "[debian/package] Running dpkg-buildpackage..."
dpkg-buildpackage -us -uc -b dpkg-buildpackage -us -uc -b
echo "[debian/package] Installing generated DEB package..." echo "[debian/package] Installing generated DEB package..."

View File

@@ -4,8 +4,17 @@ set -euo pipefail
echo "[fedora/package] Setting up rpmbuild directories..." echo "[fedora/package] Setting up rpmbuild directories..."
mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
SPEC_PATH="${PROJECT_ROOT}/packaging/fedora/package-manager.spec"
if [[ ! -f "${SPEC_PATH}" ]]; then
echo "[fedora/package] ERROR: SPEC file not found: ${SPEC_PATH}"
exit 1
fi
echo "[fedora/package] Extracting version from package-manager.spec..." echo "[fedora/package] Extracting version from package-manager.spec..."
version="$(grep -E '^Version:' package-manager.spec | awk '{print $2}')" version="$(grep -E '^Version:' "${SPEC_PATH}" | awk '{print $2}')"
if [[ -z "${version}" ]]; then if [[ -z "${version}" ]]; then
echo "ERROR: Version missing!" echo "ERROR: Version missing!"
exit 1 exit 1
@@ -15,13 +24,13 @@ srcdir="package-manager-${version}"
echo "[fedora/package] Preparing source tree: ${srcdir}" echo "[fedora/package] Preparing source tree: ${srcdir}"
rm -rf "/tmp/${srcdir}" rm -rf "/tmp/${srcdir}"
mkdir -p "/tmp/${srcdir}" mkdir -p "/tmp/${srcdir}"
cp -a . "/tmp/${srcdir}/" cp -a "${PROJECT_ROOT}/." "/tmp/${srcdir}/"
echo "[fedora/package] Creating source tarball..." echo "[fedora/package] Creating source tarball..."
tar czf "/root/rpmbuild/SOURCES/${srcdir}.tar.gz" -C /tmp "${srcdir}" tar czf "/root/rpmbuild/SOURCES/${srcdir}.tar.gz" -C /tmp "${srcdir}"
echo "[fedora/package] Copying SPEC..." echo "[fedora/package] Copying SPEC..."
cp package-manager.spec /root/rpmbuild/SPECS/ cp "${SPEC_PATH}" /root/rpmbuild/SPECS/
echo "[fedora/package] Running rpmbuild..." echo "[fedora/package] Running rpmbuild..."
cd /root/rpmbuild/SPECS cd /root/rpmbuild/SPECS

View File

@@ -4,20 +4,22 @@ set -euo pipefail
# ------------------------------------------------------------ # ------------------------------------------------------------
# main.sh # main.sh
# #
# Developer setup entrypoint. # Developer / system setup entrypoint.
# #
# Responsibilities: # Responsibilities:
# - If inside a Nix shell (IN_NIX_SHELL=1): # - If inside a Nix shell (IN_NIX_SHELL=1):
# * Skip venv creation and dependency installation # * Skip venv creation and dependency installation
# * Run `python3 main.py install` # * Run `python3 main.py install`
# - Otherwise: # - If running as root (EUID=0):
# * Run system-level installer (run-package.sh)
# - Otherwise (normal user):
# * Create ~/.venvs/pkgmgr virtual environment if missing # * Create ~/.venvs/pkgmgr virtual environment if missing
# * Install Python dependencies into that venv # * Install Python dependencies into that venv
# * Append auto-activation to ~/.bashrc and ~/.zshrc # * Append auto-activation to ~/.bashrc and ~/.zshrc
# * Run `main.py install` using the venv Python # * Run `main.py install` using the venv Python
# ------------------------------------------------------------ # ------------------------------------------------------------
echo "[installation/main] Starting developer setup..." echo "[installation/main] Starting setup..."
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "${PROJECT_ROOT}" cd "${PROJECT_ROOT}"
@@ -26,20 +28,34 @@ VENV_DIR="${HOME}/.venvs/pkgmgr"
RC_LINE='if [ -d "${HOME}/.venvs/pkgmgr" ]; then . "${HOME}/.venvs/pkgmgr/bin/activate"; if [ -n "${PS1:-}" ]; then echo "Global Python virtual environment '\''~/.venvs/pkgmgr'\'' activated."; fi; fi' RC_LINE='if [ -d "${HOME}/.venvs/pkgmgr" ]; then . "${HOME}/.venvs/pkgmgr/bin/activate"; if [ -n "${PS1:-}" ]; then echo "Global Python virtual environment '\''~/.venvs/pkgmgr'\'' activated."; fi; fi'
# ------------------------------------------------------------ # ------------------------------------------------------------
# Nix shell mode: do not touch venv, only run main.py install # 1) Nix shell mode: do not touch venv, only run main.py install
# ------------------------------------------------------------ # ------------------------------------------------------------
if [[ -n "${IN_NIX_SHELL:-}" ]]; then if [[ -n "${IN_NIX_SHELL:-}" ]]; then
echo "[installation/main] Nix shell detected (IN_NIX_SHELL=1)." echo "[installation/main] Nix shell detected (IN_NIX_SHELL=1)."
echo "[installation/main] Skipping virtualenv creation and dependency installation." echo "[installation/main] Skipping virtualenv creation and dependency installation."
echo "[installation/main] Running main.py install via system python3..." echo "[installation/main] Running main.py install via system python3..."
python3 main.py install python3 main.py install
echo "[installation/main] Developer setup finished (Nix mode)." echo "[installation/main] Setup finished (Nix mode)."
exit 0 exit 0
fi fi
# ------------------------------------------------------------ # ------------------------------------------------------------
# Normal host mode: create/update venv and run main.py install # 2) Root mode: system / distro-level installation
# ------------------------------------------------------------ # ------------------------------------------------------------
if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
echo "[installation/main] Running as root (EUID=0)."
echo "[installation/main] Skipping user virtualenv and shell RC modifications."
echo "[installation/main] Delegating to scripts/installation/run-package.sh..."
bash scripts/installation/run-package.sh
echo "[installation/main] Root/system setup complete."
exit 0
fi
# ------------------------------------------------------------
# 3) Normal user mode: dev setup with venv
# ------------------------------------------------------------
echo "[installation/main] Running in normal user mode (developer setup)."
echo "[installation/main] Ensuring main.py is executable..." echo "[installation/main] Ensuring main.py is executable..."
chmod +x main.py || true chmod +x main.py || true
@@ -47,26 +63,8 @@ chmod +x main.py || true
echo "[installation/main] Ensuring global virtualenv root: ${HOME}/.venvs" echo "[installation/main] Ensuring global virtualenv root: ${HOME}/.venvs"
mkdir -p "${HOME}/.venvs" mkdir -p "${HOME}/.venvs"
if [[ ! -d "${VENV_DIR}" ]]; then echo "[installation/main] Creating/updating virtualenv via helper..."
echo "[installation/main] Creating virtual environment at: ${VENV_DIR}" PKGMGR_VENV_DIR="${VENV_DIR}" bash scripts/installation/venv-create.sh
python3 -m venv "${VENV_DIR}"
else
echo "[installation/main] Virtual environment already exists at: ${VENV_DIR}"
fi
echo "[installation/main] Installing Python tooling into venv..."
"${VENV_DIR}/bin/python" -m ensurepip --upgrade
"${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel
if [[ -f "requirements.txt" ]]; then
echo "[installation/main] Installing dependencies from requirements.txt..."
"${VENV_DIR}/bin/pip" install -r requirements.txt
elif [[ -f "_requirements.txt" ]]; then
echo "[installation/main] Installing dependencies from _requirements.txt..."
"${VENV_DIR}/bin/pip" install -r _requirements.txt
else
echo "[installation/main] No requirements.txt or _requirements.txt found. Skipping dependency installation."
fi
echo "[installation/main] Ensuring ~/.bashrc and ~/.zshrc exist..." echo "[installation/main] Ensuring ~/.bashrc and ~/.zshrc exist..."
touch "${HOME}/.bashrc" "${HOME}/.zshrc" touch "${HOME}/.bashrc" "${HOME}/.zshrc"

View File

@@ -3,6 +3,25 @@ set -euo pipefail
echo "[ubuntu/package] Building Ubuntu (Debian-style) package..." echo "[ubuntu/package] Building Ubuntu (Debian-style) package..."
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
BUILD_ROOT="/tmp/package-manager-ubuntu-build"
rm -rf "${BUILD_ROOT}"
mkdir -p "${BUILD_ROOT}"
echo "[ubuntu/package] Syncing project sources to ${BUILD_ROOT}..."
rsync -a \
--exclude 'packaging/debian' \
"${PROJECT_ROOT}/" "${BUILD_ROOT}/"
echo "[ubuntu/package] Overlaying debian/ metadata from packaging/debian..."
mkdir -p "${BUILD_ROOT}/debian"
cp -a "${PROJECT_ROOT}/packaging/debian/." "${BUILD_ROOT}/debian/"
cd "${BUILD_ROOT}"
echo "[ubuntu/package] Running dpkg-buildpackage..."
dpkg-buildpackage -us -uc -b dpkg-buildpackage -us -uc -b
echo "[ubuntu/package] Installing generated DEB package..." echo "[ubuntu/package] Installing generated DEB package..."

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail
# venv-create.sh
#
# Small helper to create/update a Python virtual environment for pkgmgr.
#
# Usage:
# PKGMGR_VENV_DIR=/home/dev/.venvs/pkgmgr bash scripts/installation/venv-create.sh
# or
# bash scripts/installation/venv-create.sh /home/dev/.venvs/pkgmgr
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "${PROJECT_ROOT}"
VENV_DIR="${PKGMGR_VENV_DIR:-${1:-${HOME}/.venvs/pkgmgr}}"
echo "[venv-create] Using VENV_DIR=${VENV_DIR}"
echo "[venv-create] Ensuring virtualenv parent directory exists..."
mkdir -p "$(dirname "${VENV_DIR}")"
if [[ ! -d "${VENV_DIR}" ]]; then
echo "[venv-create] Creating virtual environment at: ${VENV_DIR}"
python3 -m venv "${VENV_DIR}"
else
echo "[venv-create] Virtual environment already exists at: ${VENV_DIR}"
fi
echo "[venv-create] Installing Python tooling into venv..."
"${VENV_DIR}/bin/python" -m ensurepip --upgrade
"${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel
if [[ -f "requirements.txt" ]]; then
echo "[venv-create] Installing dependencies from requirements.txt..."
"${VENV_DIR}/bin/pip" install -r requirements.txt
elif [[ -f "_requirements.txt" ]]; then
echo "[venv-create] Installing dependencies from _requirements.txt..."
"${VENV_DIR}/bin/pip" install -r _requirements.txt
else
echo "[venv-create] No requirements.txt or _requirements.txt found. Skipping dependency installation."
fi
echo "[venv-create] Done."

View File

@@ -19,9 +19,9 @@ for distro in $DISTROS; do
# Run the command and capture the output # Run the command and capture the output
if OUTPUT=$(docker run --rm \ if OUTPUT=$(docker run --rm \
-e PKGMGR_DEV=1 \ -e PKGMGR_DEV=1 \
-v pkgmgr_nix_store:/nix \ -v pkgmgr_nix_store_${distro}:/nix \
-v "$(pwd):/src" \ -v "$(pwd):/src" \
-v "pkgmgr_nix_cache:/root/.cache/nix" \ -v "pkgmgr_nix_cache_${distro}:/root/.cache/nix" \
"$IMAGE" 2>&1); then "$IMAGE" 2>&1); then
echo "$OUTPUT" echo "$OUTPUT"
echo echo

View File

@@ -10,43 +10,56 @@ for distro in $DISTROS; do
docker run --rm \ docker run --rm \
-v "$(pwd):/src" \ -v "$(pwd):/src" \
-v pkgmgr_nix_store:/nix \ -v "pkgmgr_nix_store_${distro}:/nix" \
-v "pkgmgr_nix_cache:/root/.cache/nix" \ -v "pkgmgr_nix_cache_${distro}:/root/.cache/nix" \
-e PKGMGR_DEV=1 \ -e PKGMGR_DEV=1 \
-e TEST_PATTERN="${TEST_PATTERN}" \ -e TEST_PATTERN="${TEST_PATTERN}" \
--workdir /src \ --workdir /src \
--entrypoint bash \ --entrypoint bash \
"package-manager-test-$distro" \ "package-manager-test-${distro}" \
-c ' -c '
set -e; set -euo pipefail
# Load distro info
if [ -f /etc/os-release ]; then if [ -f /etc/os-release ]; then
. /etc/os-release; . /etc/os-release
fi; fi
echo "Running tests inside distro: $ID"; echo "Running tests inside distro: ${ID:-unknown}"
# Try to load nix environment # Load Nix environment if available
if [ -f "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" ]; then if [ -f "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" ]; then
. "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"; . "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
fi fi
if [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then if [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then
. "$HOME/.nix-profile/etc/profile.d/nix.sh"; . "$HOME/.nix-profile/etc/profile.d/nix.sh"
fi fi
PATH="/nix/var/nix/profiles/default/bin:$HOME/.nix-profile/bin:$PATH"; PATH="/nix/var/nix/profiles/default/bin:$HOME/.nix-profile/bin:$PATH"
command -v nix >/dev/null || { command -v nix >/dev/null || {
echo "ERROR: nix not found."; echo "ERROR: nix not found."
exit 1; exit 1
} }
git config --global --add safe.directory /src || true; # Mark the mounted repository as safe to avoid Git ownership errors.
# Newer Git (e.g. on Ubuntu) complains about the gitdir (/src/.git),
# older versions about the worktree (/src). Nix turns "." into the
# flake input "git+file:///src", which then uses Git under the hood.
if command -v git >/dev/null 2>&1; then
# Worktree path
git config --global --add safe.directory /src || true
# Gitdir path shown in the "dubious ownership" error
git config --global --add safe.directory /src/.git || true
# Ephemeral CI containers: allow all paths as a last resort
git config --global --add safe.directory '*' || true
fi
# Run the E2E tests inside the Nix development shell
nix develop .#default --no-write-lock-file -c \ nix develop .#default --no-write-lock-file -c \
python3 -m unittest discover \ python3 -m unittest discover \
-s /src/tests/e2e \ -s /src/tests/e2e \
-p "$TEST_PATTERN"; -p "$TEST_PATTERN"
' '
done done

View File

@@ -1,24 +1,26 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
: "${distro:=arch}"
echo "============================================================" echo "============================================================"
echo ">>> Running INTEGRATION tests in Arch container" echo ">>> Running INTEGRATION tests in ${distro} container"
echo "============================================================" echo "============================================================"
docker run --rm \ docker run --rm \
-v "$(pwd):/src" \ -v "$(pwd):/src" \
-v pkgmgr_nix_store:/nix \ -v pkgmgr_nix_store_${distro}:/nix \
-v "pkgmgr_nix_cache:/root/.cache/nix" \ -v "pkgmgr_nix_cache_${distro}:/root/.cache/nix" \
--workdir /src \ --workdir /src \
-e PKGMGR_DEV=1 \ -e PKGMGR_DEV=1 \
-e TEST_PATTERN="${TEST_PATTERN}" \ -e TEST_PATTERN="${TEST_PATTERN}" \
--entrypoint bash \ --entrypoint bash \
"package-manager-test-arch" \ "package-manager-test-${distro}" \
-c ' -c '
set -e; set -e;
git config --global --add safe.directory /src || true; git config --global --add safe.directory /src || true;
nix develop .#default --no-write-lock-file -c \ nix develop .#default --no-write-lock-file -c \
python -m unittest discover \ python3 -m unittest discover \
-s tests/integration \ -s tests/integration \
-t /src \ -t /src \
-p "$TEST_PATTERN"; -p "$TEST_PATTERN";

View File

@@ -1,24 +1,26 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
: "${distro:=arch}"
echo "============================================================" echo "============================================================"
echo ">>> Running UNIT tests in Arch container" echo ">>> Running UNIT tests in ${distro} container"
echo "============================================================" echo "============================================================"
docker run --rm \ docker run --rm \
-v "$(pwd):/src" \ -v "$(pwd):/src" \
-v "pkgmgr_nix_cache:/root/.cache/nix" \ -v "pkgmgr_nix_cache_${distro}:/root/.cache/nix" \
-v pkgmgr_nix_store:/nix \ -v pkgmgr_nix_store_${distro}:/nix \
--workdir /src \ --workdir /src \
-e PKGMGR_DEV=1 \ -e PKGMGR_DEV=1 \
-e TEST_PATTERN="${TEST_PATTERN}" \ -e TEST_PATTERN="${TEST_PATTERN}" \
--entrypoint bash \ --entrypoint bash \
"package-manager-test-arch" \ "package-manager-test-${distro}" \
-c ' -c '
set -e; set -e;
git config --global --add safe.directory /src || true; git config --global --add safe.directory /src || true;
nix develop .#default --no-write-lock-file -c \ nix develop .#default --no-write-lock-file -c \
python -m unittest discover \ python3 -m unittest discover \
-s tests/unit \ -s tests/unit \
-t /src \ -t /src \
-p "$TEST_PATTERN"; -p "$TEST_PATTERN";

36
src/pkgmgr/__init__.py Normal file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Top-level pkgmgr package.
We deliberately avoid importing heavy submodules (like the CLI)
on import to keep unit tests fast and to not require optional
dependencies (like PyYAML) unless they are actually used.
Accessing ``pkgmgr.cli`` will load the CLI module lazily via
``__getattr__``. This keeps patterns like
from pkgmgr import cli
working as expected in tests and entry points.
"""
from __future__ import annotations
from importlib import import_module
from typing import Any
__all__ = ["cli"]
def __getattr__(name: str) -> Any:
"""
Lazily expose ``pkgmgr.cli`` as attribute on the top-level package.
This keeps ``import pkgmgr`` lightweight while still allowing
``from pkgmgr import cli`` in tests and entry points.
"""
if name == "cli":
return import_module("pkgmgr.cli")
raise AttributeError(f"module 'pkgmgr' has no attribute {name!r}")

View File

@@ -34,7 +34,6 @@ dependency formats, including:
\033[1;33mNix:\033[0m flake.nix \033[1;33mNix:\033[0m flake.nix
\033[1;33mArch Linux:\033[0m PKGBUILD \033[1;33mArch Linux:\033[0m PKGBUILD
\033[1;33mAnsible:\033[0m requirements.yml \033[1;33mAnsible:\033[0m requirements.yml
\033[1;33mpkgmgr-native:\033[0m pkgmgr.yml
This allows pkgmgr to perform installation, updates, verification, dependency This allows pkgmgr to perform installation, updates, verification, dependency
resolution, and synchronization across complex multi-repo environments with a resolution, and synchronization across complex multi-repo environments with a

View File

@@ -16,10 +16,36 @@ from pkgmgr.actions.repository.list import list_repositories
from pkgmgr.core.command.run import run_command from pkgmgr.core.command.run import run_command
from pkgmgr.actions.repository.create import create_repo from pkgmgr.actions.repository.create import create_repo
from pkgmgr.core.repository.selected import get_selected_repos from pkgmgr.core.repository.selected import get_selected_repos
from pkgmgr.core.repository.dir import get_repo_dir
Repository = Dict[str, Any] Repository = Dict[str, Any]
def _resolve_repository_directory(repository: Repository, ctx: CLIContext) -> str:
"""
Resolve the local filesystem directory for a repository.
Priority:
1. Use repository["directory"] if present.
2. Fallback to get_repo_dir(...) using the repositories base directory
from the CLI context.
"""
repo_dir = repository.get("directory")
if repo_dir:
return repo_dir
base_dir = (
getattr(ctx, "repositories_base_dir", None)
or getattr(ctx, "repositories_dir", None)
)
if not base_dir:
raise RuntimeError(
"Cannot resolve repositories base directory from context; "
"expected ctx.repositories_base_dir or ctx.repositories_dir."
)
return get_repo_dir(base_dir, repository)
def handle_repos_command( def handle_repos_command(
args, args,
ctx: CLIContext, ctx: CLIContext,
@@ -108,8 +134,25 @@ def handle_repos_command(
# path # path
# ------------------------------------------------------------ # ------------------------------------------------------------
if args.command == "path": if args.command == "path":
if not selected:
print("[pkgmgr] No repositories selected for path.")
return
for repository in selected: for repository in selected:
print(repository["directory"]) try:
repo_dir = _resolve_repository_directory(repository, ctx)
except Exception as exc:
ident = (
f"{repository.get('provider', '?')}/"
f"{repository.get('account', '?')}/"
f"{repository.get('repository', '?')}"
)
print(
f"[WARN] Could not resolve directory for {ident}: {exc}"
)
continue
print(repo_dir)
return return
# ------------------------------------------------------------ # ------------------------------------------------------------
@@ -119,14 +162,14 @@ def handle_repos_command(
if not args.shell_command: if not args.shell_command:
print("[ERROR] 'shell' requires a command via -c/--command.") print("[ERROR] 'shell' requires a command via -c/--command.")
sys.exit(2) sys.exit(2)
command_to_run = " ".join(args.shell_command) command_to_run = " ".join(args.shell_command)
for repository in selected: for repository in selected:
print( repo_dir = _resolve_repository_directory(repository, ctx)
f"Executing in '{repository['directory']}': {command_to_run}" print(f"Executing in '{repo_dir}': {command_to_run}")
)
run_command( run_command(
command_to_run, command_to_run,
cwd=repository["directory"], cwd=repo_dir,
preview=args.preview, preview=args.preview,
) )
return return

View File

@@ -197,13 +197,8 @@ 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: Versuche Defaults aus dem installierten Paket ODER Fallback: load default configs from various possible install or development
aus dem Projekt-Root zu laden: layouts (pip-installed, editable install, source repo with src/ layout).
<pkg_root>/config_defaults
<pkg_root>/config
<project_root>/config_defaults
<project_root>/config
""" """
try: try:
import pkgmgr # type: ignore import pkgmgr # type: ignore
@@ -211,14 +206,25 @@ 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
project_root = pkg_root.parent roots = set()
candidates = [ # Case 1: installed package (site-packages/pkgmgr)
pkg_root / "config_defaults", roots.add(pkg_root)
pkg_root / "config",
project_root / "config_defaults", # Case 2: parent directory (site-packages/, src/)
project_root / "config", roots.add(pkg_root.parent)
]
# Case 3: src-layout during development:
# repo_root/src/pkgmgr -> repo_root
parent = pkg_root.parent
if parent.name == "src":
roots.add(parent.parent)
# Candidate config dirs
candidates = []
for root in roots:
candidates.append(root / "config_defaults")
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 +233,6 @@ def _load_defaults_from_package_or_project() -> Dict[str, Any]:
return {"directories": {}, "repositories": []} return {"directories": {}, "repositories": []}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Hauptfunktion # Hauptfunktion
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

Some files were not shown because too many files have changed in this diff Show More