The pipeline used to skip a second installer only when it advertised
capabilities the first had already provided, so a hook with a different
capability still ran on the very same project. file-dedupe was installed
through pip and then again through `make install`, whose first line upgrades
pip; PEP 668 refuses that, and the run went red on a repository that was
already installed correctly.
One hook now installs a repository and the pipeline stops there. The order of
INSTALLERS is the preference: `make install` leads, because it is the hook a
repository writes for itself, while the others infer an installation from a
manifest.
A hook that fails installed nothing, so the rule is one *successful*
installation rather than one attempt: _run_installer returns a bool instead of
re-raising, and the caller moves on to the next hook. To keep that fallback
from turning a broken repository into a silent pass, a repository whose hooks
all fail now exits with the list of what was tried.
The capability bookkeeping the old skip rule needed is gone from the pipeline;
discover_capabilities remains on BaseInstaller and is now unused there.
Verified: 14 targeted unit and integration tests, and a full `pkgmgr update
--all` sweep in the arch image with 0 installation failures. Delta measured at
the consumer: repositories left without a CLI entry point, 16 before and 16
after, diff identical.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Repository-wide mechanical cleanup so `ruff check src tests` has a chance
of passing; no behavioural changes.
- Add `from __future__ import annotations` where PEP 604 unions are used.
This has to come first: pyproject declares requires-python >= 3.9, where
`X | None` is not evaluable at runtime unless annotations are stringified.
- Replace typing.List/Dict/Tuple/Set with the builtin generics and
Optional[X] with X | None, then drop the imports that became unused.
The four actions/*/__init__.py files needed this by hand because ruff
leaves unused imports in __init__.py alone (possible re-exports).
- Strip shebangs from 72 importable modules. None of them are executable
or invoked directly; the entry points are console_scripts and runpy.
- Flatten nested `with` blocks, collapse needless-bool returns, and apply
the remaining mechanical ruff fixes (PIE810, FLY002, PERF102, FURB192,
RUF059, I001).
- Pass check=False explicitly to the four subprocess.run() calls that
inspect returncode themselves. That is the existing default.
Two rewrites are visible to mocks, so their tests move with them:
subprocess.run(stdout=PIPE, stderr=PIPE) became capture_output=True, and
open(path, "r", ...) lost the redundant mode.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Repository-wide formatting pass over src/ and tests/: sorted imports and
__all__ entries, dropped redundant `# -*- coding: utf-8 -*-` headers,
unquoted forward-reference annotations (safe under
`from __future__ import annotations`), merged nested `with` statements,
moved `Iterable` and friends from `typing` to `collections.abc`, and
removed `# noqa: F401` markers that no longer suppress anything.
No behavioural changes. Unit and integration suites show the same four
pre-existing failures before and after the pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`git init` without `-b` inherits the ambient `init.defaultBranch` config,
which is `master` unless the host has configured otherwise, so `pkgmgr
repos create` produced master-based repositories. On top of that, the
push fallback renamed an already-correct `main` branch back to `master`
whenever the initial push failed (missing remote, auth error).
`init()` now requires an explicit `initial_branch` and runs
`git init -b <branch>`; the bootstrapper passes `main` and no longer
falls back to `master` on push failure.
`resolve_base_branch()` keeps its main -> master fallback, since it reads
existing repositories that may legitimately use master.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pkgmgr code-scanning fetches a repository's GitHub code scanning alerts (and analysis metadata) via the gh CLI and writes alerts.json, a readable summary.md digest, and analyses.json into a timestamped directory (default /tmp/<repo>/code-scanner/<YYYYMMDDHHMMSS>) for offline analysis. The repository is resolved from gh or --repo; authentication is delegated to gh; --state filters by alert state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The release flow now sanitises the release message into the changelog house style and validates it before writing: a leading '#' heading becomes a bold line, inline `code` becomes *italic*, and the resulting entry is checked with markdownlint-cli2 (using the target repo's own config, i.e. the same rules it enforces) or a built-in fallback. In an interactive run the editor re-opens with the findings until the entry is clean; with --message it transforms then aborts on any finding. The editor's ignore-marker moved from '#' to ';' so heading lines survive as content.
debian/changelog and the RPM %changelog mirror the transformed body, keeping all three changelogs consistent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
update_debian_changelog and update_spec_changelog wrapped the whole release message in a single prefixed line, so only the first line of a multi-line (already-bulleted) message was indented/dashed while the rest stayed at column 0. That made dpkg-parsechangelog emit 'badly formatted heading line' and rpmspec report 'bad date', failing the package build. Each non-empty body line is now indented (Debian) or dash-prefixed (RPM) individually.
update_changelog no longer auto-prepends a '* ' bullet to the CHANGELOG.md entry: the message is inserted verbatim with blank-line separation, so a caller-provided markdown list stays markdown-lint-clean instead of gaining a stray leading bullet.
Adds regression tests for the multi-line debian/rpm mirroring and the no-injected-bullet behaviour.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
update_changelog used to blindly prepend the new ## [version] entry to
the entire file body. When CHANGELOG.md leads with a # Changelog H1
the result was:
## [new] - YYYY-MM-DD
...
# Changelog
## [old] - ...
which trips markdownlint MD041 (first-line-h1) and MD012
(no-multiple-blanks), and reads as if the document were two stacked
changelogs.
The new _insert_after_h1 helper:
* Synthesises # Changelog when the file is empty.
* Inserts the entry between the existing H1 (plus any intro prose)
and the first existing ## release entry.
* For legacy headerless files (file starts with ##) prepends the
synthesised H1 + entry, so the resulting document is also
MD041-clean.
Tests cover the three layouts (empty, H1+existing-entries,
legacy-headerless). All 61 release-unit tests stay green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Walks a directory of numbered NNN-topic.md files, promotes every file
with zero unchecked task-list items into the directorys README under
the ## Archive section, then deletes the source file. Keeps spec
directories (typically docs/requirements) short and focused on open
work.
The action ships as pkgmgr.actions.archive with four leaf modules
(discovery, inspect, readme, workflow) and is wired into the CLI as
pkgmgr archive [DIR] [--readme PATH] [--dry-run] [--include-template].
Extracted verbatim from cli/contributing/requirements/archive in
infinito-nexus-core so every kpmx-managed repository can rely on the
same archival convention without copy-pasting helpers. Twenty unit
tests cover discovery, inspection, README merge, and end-to-end
workflow paths.
Also: realign tests/unit/pkgmgr/cli/commands/test_release.py with the
new run_release(retry=False) signature shipped in v1.14.0.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Recovers from a release whose tag+commit landed cleanly but whose
post-tag steps (git push, latest-tag bump, twine upload) failed
mid-flight. pkgmgr release --retry skips the version bump, file
rewrites, commit, and tag-creation steps and re-runs only the
idempotent tail: re-push the existing HEAD tag, re-align the floating
latest tag, and (unless --no-publish) re-invoke publish.
The retry logic lives in its own module pkgmgr.actions.release.retry
so the workflow.py orchestrator stays focused on the forward path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previously the release workflow derived the distro-package name from
`os.path.basename(repo_root)`. Renaming the repo folder (e.g.
`infinito-nexus` → `infinito-nexus-core`) silently rewrote
`debian/changelog`'s top entry to the new folder name while
`debian/control` still pinned the legacy `Package:` value. dpkg-source
refuses to build a source package when the two disagree.
Add `resolve_package_name(paths)` that consults the existing packaging
files in priority order (debian/control `Package:` → PKGBUILD
`pkgname=` → RPM `.spec` `Name:`) and only falls back to the folder
basename when no packaging metadata is present. Extend `RepoPaths`
with a `debian_control` slot so `resolve_repo_paths` can discover the
file under both `packaging/debian/control` and the legacy `debian/`
location.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Add mirror visibility subcommand and provision --public flag
* Implement core visibility API with provider support (GitHub, Gitea)
* Extend provider interface and EnsureStatus
* Add unit, integration and e2e tests for visibility handling
https://chatgpt.com/share/6946a44e-4f48-800f-8124-9c0b9b2b6b04
- Add probe_remote_reachable_detail and improved GitRunError metadata
- Print short failure reasons for unreachable remotes
- Provision each git mirror URL via ensure_remote_repository_for_url
https://chatgpt.com/share/6946956e-f738-800f-a446-e2c8bf5595f4
- Adjust tests to expect RuntimeError instead of SystemExit
- Add coverage for missing [project] section in pyproject.toml
- Keep spec macro %{?dist} intact in test fixtures
- Minor cleanup and reformatting of test cases
https://chatgpt.com/share/69454836-4698-800f-9d19-7e67e8e789d6
* Replace legacy GitError usage with a clearer exception hierarchy:
* GitBaseError as the common root for all git-related failures
* GitRunError for subprocess execution failures
* GitQueryError for read-only query failures
* GitCommandError for state-changing command failures
* GitNotRepositoryError to explicitly signal “not a git repository” situations
* Update git runner to detect “not a git repository” stderr and raise GitNotRepositoryError with rich context (cwd, command, stderr)
* Refactor repository verification to use dedicated query helpers instead of ad-hoc subprocess calls:
* get_remote_head_commit (ls-remote) for pull mode
* get_head_commit for local mode
* get_latest_signing_key (%GK) for signature verification
* Add strict vs best-effort behavior in verify_repository:
* Best-effort collection for reporting (does not block when no verification config exists)
* Strict retrieval and explicit error messages when verification is configured
* Clear failure cases when commit/signing key cannot be determined
* Add new unit tests covering:
* get_latest_signing_key output stripping and error wrapping
* get_remote_head_commit parsing, empty output, and error wrapping
* verify_repository success/failure scenarios and “do not swallow GitNotRepositoryError”
* Adjust imports and exception handling across actions/commands/queries to align with GitRunError-based handling while keeping GitNotRepositoryError uncaught for debugging clarity
https://chatgpt.com/share/6943173c-508c-800f-8879-af75d131c79b
- Treat MIRRORS as the only authority for mirror URLs
- Filter non-git URLs (e.g. PyPI) from git remotes and push URLs
- Prefer SSH git URLs when determining primary origin
- Ensure mirror probing only targets valid git remotes
- Refactor repository create into service-based architecture
- Write PyPI metadata exclusively to MIRRORS, never to git config
- Add integration test verifying PyPI is not written into .git/config
- Update preview and unit tests to match new create flow
https://chatgpt.com/share/69415c61-1c5c-800f-86dd-0405edec25db
- Replace direct subprocess usage with core.git.run wrapper
- Introduce dedicated core.git.commands (add, commit, fetch, pull_ff_only, push, clone, tag_annotated, tag_force_annotated, etc.)
- Introduce core.git.queries (list_tags, get_upstream_ref, get_config_value, changelog helpers, etc.)
- Refactor release workflow and git_ops to use command/query split
- Implement semantic vX.Y.Z comparison with safe fallback for non-parseable tags
- Refactor repository clone logic to use core.git.commands.clone with preview support and ssh→https fallback
- Remove legacy run_git_command helpers
- Split and update unit tests to mock command/query boundaries instead of subprocess
- Add comprehensive tests for clone modes, preview behavior, ssh→https fallback, and verification prompts
- Add unit tests for core.git.run error handling and preview mode
- Align public exports (__all__) with new structure
- Improve type hints, docstrings, and error specificity across git helpers
https://chatgpt.com/share/69414735-51d4-800f-bc7b-4b90e35f71e5
- Remove legacy shell-based git helpers from release workflow
- Introduce typed git command wrappers (add, commit, fetch, pull_ff_only, push, tag*)
- Add git queries for upstream detection and tag listing
- Refactor release workflow to use core git commands consistently
- Implement semantic vX.Y.Z tag comparison without external sort
- Ensure prerelease tags (e.g. -rc) do not outrank final releases
- Split and update unit tests to match new command/query architecture
- Update mirror integration tests to use probe_remote_reachable
- Refactor branch action tests to mock git command helpers instead of run_git
- Align changelog tests with get_changelog query API
- Update git core tests to cover run() and query helpers
- Remove legacy run_git assumptions from tests
https://chatgpt.com/share/69412008-9e8c-800f-9ac9-90f390d55380
**Validated by Google's model.**
**Summary:**
The test modifications have been correctly implemented to cover the Git refactoring changes:
1. **Granular Mocking:** The tests have shifted from mocking the monolithic `run_git` or `subprocess` to mocking the new, specific wrapper functions (e.g., `pkgmgr.core.git.commands.fetch`, `pkgmgr.core.git.queries.probe_remote_reachable`). This accurately reflects the architectural change in the source code where business logic now relies on these granular imports.
2. **Structural Alignment:** The test directory structure was updated (e.g., moving tests to `tests/unit/pkgmgr/core/git/queries/`) to match the new source code organization, ensuring logical consistency.
3. **Exception Handling:** The tests were updated to verify specific exception types (like `GitDeleteRemoteBranchError`) rather than generic errors, ensuring the improved error granularity is correctly handled by the CLI.
4. **Integration Safety:** The integration tests in `test_mirror_commands.py` were correctly updated to patch the new query paths, ensuring that network operations remain disabled during testing.
The test changes are consistent with the refactor and provide complete coverage for the new code structure.
https://aistudio.google.com/app/prompts?state=%7B%22ids%22:%5B%2214Br1JG1hxuntmoRzuvme3GKUvQ0heqRn%22%5D,%22action%22:%22open%22,%22userId%22:%22109171005420801378245%22,%22resourceKeys%22:%7B%7D%7D&usp=sharing
- Resolve primary remote via RepoMirrorContext (origin → file order → config → default)
- Always set origin fetch and push URL to primary
- Add additional mirrors as extra push URLs without duplication
- Update remote provisioning and setup commands to use context-based resolution
- Adjust and extend unit tests to cover new origin/push behavior
https://chatgpt.com/share/693f4538-42d4-800f-98c2-2ec264fd2e19
* Introduce publish action with PyPI target detection via MIRRORS
* Resolve version from SemVer git tags on HEAD
* Support preview mode and non-interactive CI usage
* Build and upload artifacts using build + twine with token resolution
* Add CLI wiring (dispatch, command handler, parser)
* Add E2E publish help tests for pkgmgr and nix run
* Add integration tests for publish preview and mirror handling
* Add unit tests for git tag parsing, PyPI URL parsing, workflow preview, and CLI handler
* Clean up dispatch and parser structure while integrating publish
https://chatgpt.com/share/693f0f00-af68-800f-8846-193dca69bd2e
- Move Nix flake installer into installers/nix/ with atomic components
(installer, runner, profile, retry, types)
- Preserve legacy behavior and semantics of NixFlakeInstaller
- Add GitHub API 403 rate-limit retry with Fibonacci backoff + jitter
- Update all imports to new nix module path
- Rename legacy unit tests and adapt patches to new structure
- Add unit test for simulated GitHub 403 retry without realtime sleeping
https://chatgpt.com/share/693e925d-a79c-800f-b0b6-92b8ba260b11
- Adapt DummyCtx to include quiet and force_update flags
- Replace os.system mocking with run_command/subprocess mocks
- Align assertions with new Nix install/upgrade output
- Keep coverage for mandatory vs optional output handling
https://chatgpt.com/share/693db645-c420-800f-b921-9d5c0356d0ac
- Fix undefined repo_dir usage in repository deinstall action
- Centralize and harden get_repo_dir with strict validation and clear errors
- Expand user paths for repository base and binary directories
- Add unit tests for get_repo_dir and deinstall_repos
- Add comprehensive tests for resolve_repos identifier matching
- Remove obsolete command resolution tests no longer applicable
https://chatgpt.com/share/693d7442-c2d0-800f-9ff3-fb84d60eaeb4
* Treat remote tags as the source of truth by force-fetching tags from *origin*
* Update preview output to reflect the real fetch behavior
* Align unit tests with the new forced tag fetch command
https://chatgpt.com/share/693bdfc3-b8b4-800f-8adc-b1dc63c56a89
* Add debug output for latest vs current version tag in release git ops
* Treat “no version tags yet” as highest by definition
* Align unit tests with current *string-based* `tag >= latest` behavior
* Make tag listing mocks less brittle by matching command patterns
* Rename release init test to `test_init.py` for consistent discovery
Refactor the release implementation into a dedicated workflow module with clear separation of concerns. Enforce a safe, deterministic Git flow by always syncing with the remote before modifications, pushing only the current branch and the newly created version tag, and updating the floating *latest* tag only when the released version is the highest. Add explicit user prompts for confirmation and optional branch deletion, with a forced mode to skip interaction. Update CLI wiring to pass all relevant flags, add comprehensive unit tests for the new helpers and workflow entry points, and introduce detailed documentation describing the release process, safety rules, and execution flow.
The old test tests/unit/pkgmgr/actions/test_branch.py has been removed because:
- it targeted the previous monolithic pkgmgr.actions.branch module structure
- its patch targets no longer match the refactored code
- its responsibilities are now fully covered by the new, dedicated unit,
integration, and E2E tests for branch actions and CLI wiring
This avoids redundant coverage and prevents misleading or broken tests
after the branch refactor.
https://chatgpt.com/share/693bcc8d-b84c-800f-8510-8d6c66faf627
This commit removes the `run_command`-based execution model for Nix flake
installations and replaces it with a direct `os.system` invocation.
This ensures that *all* Nix diagnostics (stdout/stderr) are fully visible and
no longer suppressed by wrapper logic.
Key changes:
* Directly run `nix profile install` via `os.system` for full error output
* Correctly decode real exit codes via `os.WIFEXITED` / `os.WEXITSTATUS`
* Preserve mandatory/optional behavior for flake outputs
* Update unit tests to the new execution model using `unittest`
* Add complete coverage for:
* successful installs
* mandatory failures → raise SystemExit(code)
* optional failures → warn and continue
* environment-based disabling via `PKGMGR_DISABLE_NIX_FLAKE_INSTALLER`
* Remove obsolete mocks and legacy test logic that assumed `run_command`
Overall, this improves transparency, debuggability, and correctness of the
Nix flake installer while maintaining full backward compatibility at the
interface level.
https://chatgpt.com/share/693b0a20-99f4-800f-b789-b00a50413612
- Introduce explicit CLI layer model (os-packages, nix, python, makefile)
and central InstallationPipeline to orchestrate installers.
- Move installer orchestration out of install_repos() into
pkgmgr.actions.repository.install.pipeline, using layer precedence and
capability tracking.
- Add pkgmgr.actions.repository.install.layers to classify commands into
layers and compare priorities.
- Rework PythonInstaller to always use isolated environments:
PKGMGR_PIP override → active venv → per-repo venv under ~/.venvs/<identifier>,
avoiding system Python and PEP 668 conflicts.
- Adjust NixFlakeInstaller to install flake outputs based on repository
identity: pkgmgr/package-manager → pkgmgr (mandatory) + default (optional),
all other repos → default (mandatory).
- Tighten MakefileInstaller behaviour, add global
PKGMGR_DISABLE_MAKEFILE_INSTALLER switch, and simplify install target
detection.
- Rewrite resolve_command_for_repo() with explicit Repository typing,
better Python package detection, Nix/PATH resolution, and a
library-only fallback instead of raising on missing CLI.
- Update flake.nix devShell to provide python3 with pip and add pip as a
propagated build input.
- Remove deprecated/wip repository entries from config defaults and drop
the unused config/wip.yml.
https://chatgpt.com/share/69399157-86d8-800f-9935-1a820893e908
Update Debian, RPM, Nix flake, and Python installer unit tests to match the current
installer behavior and to run correctly inside the Nix development shell.
- DebianControlInstaller:
- Add clearer docstrings for supports() behavior.
- Relax final install assertion to accept dpkg -i, sudo dpkg -i, or
sudo apt-get install -y.
- Keep checks for apt-get update, apt-get build-dep, and dpkg-buildpackage.
- RpmSpecInstaller:
- Add docstrings for supports() conditions.
- Mock _prepare_source_tarball() to avoid touching the filesystem.
- Assert builddep, rpmbuild -ba, and sudo dnf install -y commands.
- NixFlakeInstaller:
- Ensure supports() and run() tests simulate a non-Nix-shell environment
via IN_NIX_SHELL and PKGMGR_DISABLE_NIX_FLAKE_INSTALLER.
- Verify that the old profile entry is removed and both pkgmgr and default
flake outputs are installed.
- Confirm _ensure_old_profile_removed() swallows SystemExit.
- PythonInstaller:
- Make supports() and run() tests ignore the real IN_NIX_SHELL environment.
- Assert that pip install . is invoked with cwd set to the repository
directory.
These changes make the unit tests stable in the Nix dev shell and align them
with the current installer implementations.
- Update pkgmgr.actions.branch.open_branch() to resolve the base branch
via _resolve_base_branch(), preferring 'main' and falling back to
'master' when the preferred branch does not exist.
- Adjust the open_branch logic to:
- fetch from origin
- checkout the resolved base branch
- pull the resolved base branch
- create the feature branch
- push the new branch with upstream tracking
- Add and refine unit tests in tests/unit/pkgmgr/actions/test_branch.py
to cover:
- normal branch creation with explicit name and default base
- interactive name prompting when no name is provided
- error handling when fetch fails after successful base resolution
- fallback to 'master' when 'main' is missing.
- Clean up and clarify docstrings and comments for open_branch(),
close_branch(), and _resolve_base_branch(), and fix the module header
comment to match the new package path.
This fixes branch opening in repositories that still use 'master' as
their primary branch while keeping the default behavior for 'main'.
https://chatgpt.com/share/6938838f-7aac-800f-b130-924e07ef48b9
- Updated update_pyproject_version() to gracefully skip missing or unreadable pyproject.toml
- Added corresponding unit test ensuring missing file triggers no exception and no file creation
- Updated test wording for spec changelog section
- Ref: adjustments discussed in ChatGPT conversation (2025-12-09) - https://chatgpt.com/share/69388024-93e4-800f-a09f-bf78a6b9a53f
This commit enhances the behaviour of pull_with_verification() and adds a
comprehensive unit test suite covering all control flows.
Changes:
- Added `preview` parameter to fully disable interaction and execution.
- Improved verification logic:
* Prompt only when not in preview, verification is enabled,
verification info exists, and verification failed.
* Skip prompts entirely when --no-verification is set.
- More explicit construction of `git pull` command with optional extra args.
- Improved messaging and formatting for clarity.
- Ensured directory existence is checked before any verification logic.
- Added detailed comments explaining logic and conditions.
Tests:
- New file tests/unit/pkgmgr/actions/repos/test_pull_with_verification.py
- Covers:
* Preview mode (no input, no subprocess)
* Verification failure – user rejects
* Verification failure – user accepts
* Verification success – immediate git call
* Missing repository directory – skip silently
* --no-verification flag bypasses prompts
* Command formatting with extra args
- Uses systematic mocking for identifier, repo-dir, verify_repository(),
subprocess.run(), and user input.
This significantly strengthens correctness, UX, and test coverage of the
repository pull workflow.
https://chatgpt.com/share/69384aaa-0c80-800f-b4b4-64e6fbdebd3b
- Move the monolithic pkgmgr/actions/release.py implementation into the
pkgmgr.actions.release package, splitting concerns into versioning,
git_ops and files helpers.
- Extend the release orchestration to update Fedora/RPM %changelog
entries via update_spec_changelog(), reusing the same effective
release message as for CHANGELOG.md and debian/changelog.
- Wire the new update_spec_changelog() helper into _release_impl() so
every release keeps project, Debian and RPM metadata in sync.
- Add unit tests for update_spec_changelog() and for the updated release
orchestration behaviour in preview and real modes.
- Remove the deprecated pkgmgr/actions/release.py module.
See ChatGPT discussion: https://chatgpt.com/share/6938368e-0940-800f-92d3-f2ccfddab794