Compare commits

...
12 Commits
Author SHA1 Message Date
kevinveenbirkenbach 0e7e23dce5 Release version 1.13.1
Mark stable commit / mark-stable (push) Waiting to run
2026-03-20 02:57:25 +01:00
kevinveenbirkenbach 9d53f4c6f5 Fix GPG verification runtime handling 2026-03-20 02:51:51 +01:00
kevinveenbirkenbach a46d85b541 Release version 1.13.0
Mark stable commit / mark-stable (push) Waiting to run
2026-03-20 01:29:38 +01:00
kevinveenbirkenbach acaea11eb6 Set CentOS image to latest 2026-03-20 01:28:49 +01:00
kevinveenbirkenbach 056d21a859 Release version 1.12.5
Mark stable commit / mark-stable (push) Waiting to run
2026-02-24 09:35:39 +01:00
kevinveenbirkenbach 612ba5069d Increase stable gate wait time to 2 hours 2026-02-24 09:34:45 +01:00
kevinveenbirkenbach 551e245218 Release version 1.12.4
Mark stable commit / mark-stable (push) Waiting to run
2026-02-24 09:32:01 +01:00
kevinveenbirkenbach 814523eac2 Gate stable tag updates on successful main CI 2026-02-24 09:30:24 +01:00
kevinveenbirkenbach 4f2c5013a7 Release version 1.12.3 2026-02-24 08:29:34 +01:00
kevinveenbirkenbach e01bb8c39a nix: pin flake input to nixos-25.11 and track flake.lock 2026-02-24 08:23:33 +01:00
kevinveenbirkenbach 461a3c334d Release version 1.12.2 2026-02-24 07:40:55 +01:00
kevinveenbirkenbach e3de46c6a4 Removed infinito-sphinx from package manager, because it's managed now via docker in infinito.nexus 2026-02-24 07:40:01 +01:00
22 changed files with 358 additions and 80 deletions

No files matched your search

+2 -2
View File
@@ -2,8 +2,8 @@ name: CI
on: on:
push: push:
branches-ignore: branches:
- main - '**'
pull_request: pull_request:
concurrency: concurrency:
+51 -43
View File
@@ -6,56 +6,64 @@ concurrency:
on: on:
push: push:
branches: tags:
- main # still run tests for main - 'v*'
jobs: jobs:
test-unit:
uses: ./.github/workflows/test-unit.yml
test-integration:
uses: ./.github/workflows/test-integration.yml
test-env-virtual:
uses: ./.github/workflows/test-env-virtual.yml
test-env-nix:
uses: ./.github/workflows/test-env-nix.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
lint-shell:
uses: ./.github/workflows/lint-shell.yml
lint-python:
uses: ./.github/workflows/lint-python.yml
mark-stable: mark-stable:
needs:
- lint-shell
- lint-python
- test-unit
- test-integration
- test-env-nix
- test-env-virtual
- test-e2e
- test-virgin-user
- 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 # Required to move/update the tag actions: read
contents: write
steps: steps:
- name: Wait for CI success on main for this commit
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
SHA="${GITHUB_SHA}"
API_URL="https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/workflows/ci.yml/runs?head_sha=${SHA}&event=push&per_page=20"
WAIT_INTERVAL_SECONDS=20
MAX_ATTEMPTS=360 # 2 hours max wait
STATUS=""
CONCLUSION=""
echo "Waiting for CI on main for ${SHA} (up to 2 hours)..."
for attempt in $(seq 1 "${MAX_ATTEMPTS}"); do
RESPONSE="$(curl -fsSL \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"${API_URL}")"
STATUS="$(printf '%s' "${RESPONSE}" | jq -r '.workflow_runs[] | select(.head_branch=="main") | .status' | head -n1)"
CONCLUSION="$(printf '%s' "${RESPONSE}" | jq -r '.workflow_runs[] | select(.head_branch=="main") | .conclusion' | head -n1)"
if [[ -n "${STATUS}" ]]; then
echo "CI status=${STATUS} conclusion=${CONCLUSION:-none} (attempt ${attempt}/${MAX_ATTEMPTS})"
else
echo "No CI run for main found yet (attempt ${attempt}/${MAX_ATTEMPTS})"
fi
if [[ "${STATUS}" == "completed" ]]; then
if [[ "${CONCLUSION}" == "success" ]]; then
echo "CI succeeded for ${SHA}."
break
fi
echo "CI failed for ${SHA} (conclusion=${CONCLUSION})."
exit 1
fi
sleep "${WAIT_INTERVAL_SECONDS}"
done
if [[ "${STATUS}" != "completed" || "${CONCLUSION}" != "success" ]]; then
echo "Timed out waiting for successful CI on main for ${SHA}."
exit 1
fi
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
+1 -2
View File
@@ -24,10 +24,9 @@ package-manager-*
.DS_Store .DS_Store
Thumbs.db Thumbs.db
# Nix Cache to speed up tests # Nix cache to speed up tests
.nix/ .nix/
.nix-dev-installed .nix-dev-installed
flake.lock
# Ignore logs # Ignore logs
*.log *.log
+30
View File
@@ -1,3 +1,33 @@
## [1.13.1] - 2026-03-20
* Fixed misleading GPG verification failures by adding explicit git and gnupg runtime dependencies and surfacing signing-key lookup errors accurately.
## [1.13.0] - 2026-03-20
* Set CentOS docker image to latest
## [1.12.5] - 2026-02-24
* The stable-tag workflow now waits up to two hours for a successful main-branch CI run on the same commit before updating stable.
## [1.12.4] - 2026-02-24
* The release pipeline now updates the stable tag only for v* tags after a successful CI run on main for the same commit, while avoiding duplicate test executions.
## [1.12.3] - 2026-02-24
* Stabilized Nix-based builds by switching to nixos-25.11 and committing flake.lock, ensuring reproducible pkgmgr test/runtime environments (with pip) and avoiding transient sphinx/Python 3.11 breakage.
## [1.12.2] - 2026-02-24
* Removed infinito-sphinx package
## [1.12.1] - 2026-02-14 ## [1.12.1] - 2026-02-14
* pkgmgr now prefers distro-managed nix binaries on Arch before profile/PATH resolution, preventing libllhttp mismatch failures after pacman system upgrades. * pkgmgr now prefers distro-managed nix binaries on Arch before profile/PATH resolution, preventing libllhttp mismatch failures after pacman system upgrades.
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1771714954,
"narHash": "sha256-nhZJPnBavtu40/L2aqpljrfUNb2rxmWTmSjK2c9UKds=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "afbbf774e2087c3d734266c22f96fca2e78d3620",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+5 -2
View File
@@ -6,7 +6,7 @@
}; };
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
}; };
outputs = { self, nixpkgs }: outputs = { self, nixpkgs }:
@@ -32,7 +32,7 @@
rec { rec {
pkgmgr = pyPkgs.buildPythonApplication { pkgmgr = pyPkgs.buildPythonApplication {
pname = "package-manager"; pname = "package-manager";
version = "1.12.1"; version = "1.13.1";
# Use the git repo as source # Use the git repo as source
src = ./.; src = ./.;
@@ -51,6 +51,8 @@
pyPkgs.pyyaml pyPkgs.pyyaml
pyPkgs.jinja2 pyPkgs.jinja2
pyPkgs.pip pyPkgs.pip
pkgs.git
pkgs.gnupg
]; ];
doCheck = false; doCheck = false;
@@ -87,6 +89,7 @@
buildInputs = [ buildInputs = [
pythonWithDeps pythonWithDeps
pkgs.git pkgs.git
pkgs.gnupg
ansiblePkg ansiblePkg
]; ];
+1 -1
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=1.12.1 pkgver=1.13.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')
+36
View File
@@ -1,3 +1,39 @@
package-manager (1.13.1-1) unstable; urgency=medium
* Fixed misleading GPG verification failures by adding explicit git and gnupg runtime dependencies and surfacing signing-key lookup errors accurately.
-- Kevin Veen-Birkenbach <kevin@veen.world> Fri, 20 Mar 2026 02:57:25 +0100
package-manager (1.13.0-1) unstable; urgency=medium
* Set CentOS docker image to latest
-- Kevin Veen-Birkenbach <kevin@veen.world> Fri, 20 Mar 2026 01:29:38 +0100
package-manager (1.12.5-1) unstable; urgency=medium
* The stable-tag workflow now waits up to two hours for a successful main-branch CI run on the same commit before updating stable.
-- Kevin Veen-Birkenbach <kevin@veen.world> Tue, 24 Feb 2026 09:35:39 +0100
package-manager (1.12.4-1) unstable; urgency=medium
* The release pipeline now updates the stable tag only for v* tags after a successful CI run on main for the same commit, while avoiding duplicate test executions.
-- Kevin Veen-Birkenbach <kevin@veen.world> Tue, 24 Feb 2026 09:32:01 +0100
package-manager (1.12.3-1) unstable; urgency=medium
* Stabilized Nix-based builds by switching to nixos-25.11 and committing flake.lock, ensuring reproducible pkgmgr test/runtime environments (with pip) and avoiding transient sphinx/Python 3.11 breakage.
-- Kevin Veen-Birkenbach <kevin@veen.world> Tue, 24 Feb 2026 08:29:34 +0100
package-manager (1.12.2-1) unstable; urgency=medium
* Removed infinito-sphinx package
-- Kevin Veen-Birkenbach <kevin@veen.world> Tue, 24 Feb 2026 07:40:55 +0100
package-manager (1.12.1-1) unstable; urgency=medium package-manager (1.12.1-1) unstable; urgency=medium
* pkgmgr now prefers distro-managed nix binaries on Arch before profile/PATH resolution, preventing libllhttp mismatch failures after pacman system upgrades. * pkgmgr now prefers distro-managed nix binaries on Arch before profile/PATH resolution, preventing libllhttp mismatch failures after pacman system upgrades.
+19 -1
View File
@@ -1,5 +1,5 @@
Name: package-manager Name: package-manager
Version: 1.12.1 Version: 1.13.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
@@ -74,6 +74,24 @@ echo ">>> package-manager removed. Nix itself was not removed."
/usr/lib/package-manager/ /usr/lib/package-manager/
%changelog %changelog
* Fri Mar 20 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.13.1-1
- Fixed misleading GPG verification failures by adding explicit git and gnupg runtime dependencies and surfacing signing-key lookup errors accurately.
* Fri Mar 20 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.13.0-1
- Set CentOS docker image to latest
* Tue Feb 24 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.12.5-1
- The stable-tag workflow now waits up to two hours for a successful main-branch CI run on the same commit before updating stable.
* Tue Feb 24 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.12.4-1
- The release pipeline now updates the stable tag only for v* tags after a successful CI run on main for the same commit, while avoiding duplicate test executions.
* Tue Feb 24 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.12.3-1
- Stabilized Nix-based builds by switching to nixos-25.11 and committing flake.lock, ensuring reproducible pkgmgr test/runtime environments (with pip) and avoiding transient sphinx/Python 3.11 breakage.
* Tue Feb 24 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.12.2-1
- Removed infinito-sphinx package
* Sat Feb 14 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.12.1-1 * Sat Feb 14 2026 Kevin Veen-Birkenbach <kevin@veen.world> - 1.12.1-1
- pkgmgr now prefers distro-managed nix binaries on Arch before profile/PATH resolution, preventing libllhttp mismatch failures after pacman system upgrades. - pkgmgr now prefers distro-managed nix binaries on Arch before profile/PATH resolution, preventing libllhttp mismatch failures after pacman system upgrades.
+1 -1
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "kpmx" name = "kpmx"
version = "1.12.1" version = "1.13.1"
description = "Kevin's package-manager tool (pkgmgr)" description = "Kevin's package-manager tool (pkgmgr)"
readme = "README.md" readme = "README.md"
requires-python = ">=3.9" requires-python = ">=3.9"
+1 -1
View File
@@ -5,7 +5,7 @@ set -euo pipefail
: "${BASE_IMAGE_DEBIAN:=debian:stable-slim}" : "${BASE_IMAGE_DEBIAN:=debian:stable-slim}"
: "${BASE_IMAGE_UBUNTU:=ubuntu:latest}" : "${BASE_IMAGE_UBUNTU:=ubuntu:latest}"
: "${BASE_IMAGE_FEDORA:=fedora:latest}" : "${BASE_IMAGE_FEDORA:=fedora:latest}"
: "${BASE_IMAGE_CENTOS:=quay.io/centos/centos:stream9}" : "${BASE_IMAGE_CENTOS:=quay.io/centos/centos:latest}"
resolve_base_image() { resolve_base_image() {
local PKGMGR_DISTRO="$1" local PKGMGR_DISTRO="$1"
@@ -16,6 +16,7 @@ fi
pacman -S --noconfirm --needed \ pacman -S --noconfirm --needed \
base-devel \ base-devel \
git \ git \
gnupg \
rsync \ rsync \
curl \ curl \
ca-certificates \ ca-certificates \
@@ -6,6 +6,7 @@ echo "[centos/dependencies] Installing CentOS build dependencies..."
dnf -y update dnf -y update
dnf -y install \ dnf -y install \
git \ git \
gnupg2 \
rsync \ rsync \
rpm-build \ rpm-build \
make \ make \
@@ -9,6 +9,7 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
debhelper \ debhelper \
dpkg-dev \ dpkg-dev \
git \ git \
gnupg \
rsync \ rsync \
bash \ bash \
curl \ curl \
@@ -6,6 +6,7 @@ echo "[fedora/dependencies] Installing Fedora build dependencies..."
dnf -y update dnf -y update
dnf -y install \ dnf -y install \
git \ git \
gnupg2 \
rsync \ rsync \
rpm-build \ rpm-build \
make \ make \
@@ -9,6 +9,7 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
debhelper \ debhelper \
dpkg-dev \ dpkg-dev \
git \ git \
gnupg \
tzdata \ tzdata \
lsb-release \ lsb-release \
rsync \ rsync \
-11
View File
@@ -369,17 +369,6 @@ repositories:
- 44D8F11FD62F878E - 44D8F11FD62F878E
- B5690EEEBB952194 - B5690EEEBB952194
- account: kevinveenbirkenbach
alias: infinito-sphinx
description: Contains the logic and configuration for generating documentation using Sphinx for Infinito.Nexus.
homepage: https://github.com/kevinveenbirkenbach/infinito-sphinx
provider: github.com
repository: infinito-sphinx
verified:
gpg_keys:
- 44D8F11FD62F878E
- B5690EEEBB952194
- account: kevinveenbirkenbach - account: kevinveenbirkenbach
description: A lightweight Python utility to generate dynamic color schemes from a single base color. Provides HSL-based color transformations for theming, UI design, and CSS variable generation. Optimized for integration in Python projects, Flask applications, and Ansible roles. description: A lightweight Python utility to generate dynamic color schemes from a single base color. Provides HSL-based color transformations for theming, UI design, and CSS variable generation. Optimized for integration in Python projects, Flask applications, and Ansible roles.
homepage: https://github.com/kevinveenbirkenbach/colorscheme-generator homepage: https://github.com/kevinveenbirkenbach/colorscheme-generator
@@ -1,13 +1,33 @@
from __future__ import annotations from __future__ import annotations
from ..errors import GitQueryError, GitRunError import subprocess
from ..run import run
from ..errors import GitNotRepositoryError, GitQueryError
class GitLatestSigningKeyQueryError(GitQueryError): class GitLatestSigningKeyQueryError(GitQueryError):
"""Raised when querying the latest commit signing key fails.""" """Raised when querying the latest commit signing key fails."""
def _is_not_repository(stderr: str) -> bool:
return "not a git repository" in (stderr or "").lower()
def _looks_like_gpg_runtime_error(stderr: str) -> bool:
lowered = (stderr or "").lower()
markers = (
"cannot run gpg",
"can't check signature",
"no public key",
"failed to create temporary file",
"can't connect to the keyboxd",
"error opening key db",
"gpg failed",
"no such file or directory",
)
return any(marker in lowered for marker in markers)
def get_latest_signing_key(*, cwd: str = ".") -> str: def get_latest_signing_key(*, cwd: str = ".") -> str:
""" """
Return the GPG signing key ID of the latest commit, via: Return the GPG signing key ID of the latest commit, via:
@@ -17,9 +37,46 @@ def get_latest_signing_key(*, cwd: str = ".") -> str:
Returns: Returns:
The key id string (may be empty if commit is not signed). The key id string (may be empty if commit is not signed).
""" """
cmd = ["git", "log", "-1", "--format=%GK"]
try: try:
return run(["log", "-1", "--format=%GK"], cwd=cwd).strip() result = subprocess.run(
except GitRunError as exc: cmd,
cwd=cwd,
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
except OSError as exc:
raise GitLatestSigningKeyQueryError( raise GitLatestSigningKeyQueryError(
"Failed to query latest signing key.", "Failed to query latest signing key.\n"
f"Command: {' '.join(cmd)}\n"
f"Reason: {exc}"
) from exc ) from exc
stdout = (result.stdout or "").strip()
stderr = (result.stderr or "").strip()
if result.returncode != 0:
if _is_not_repository(stderr):
raise GitNotRepositoryError(
f"Not a git repository: {cwd!r}\n"
f"Command: {' '.join(cmd)}\n"
f"STDERR:\n{stderr}"
)
raise GitLatestSigningKeyQueryError(
"Failed to query latest signing key.\n"
f"Command: {' '.join(cmd)}\n"
f"Exit code: {result.returncode}\n"
f"STDOUT:\n{stdout}\n"
f"STDERR:\n{stderr}"
)
if not stdout and stderr and _looks_like_gpg_runtime_error(stderr):
raise GitLatestSigningKeyQueryError(
"Failed to query latest signing key.\n"
f"Command: {' '.join(cmd)}\n"
f"STDERR:\n{stderr}"
)
return stdout
+6 -3
View File
@@ -16,6 +16,7 @@ def verify_repository(repo, repo_dir, mode="local", no_verification=False):
commit_hash = "" commit_hash = ""
signing_key = "" signing_key = ""
signing_key_query_failed = False
# best-effort info collection # best-effort info collection
try: try:
@@ -59,6 +60,7 @@ def verify_repository(repo, repo_dir, mode="local", no_verification=False):
except GitLatestSigningKeyQueryError as exc: except GitLatestSigningKeyQueryError as exc:
error_details.append(str(exc)) error_details.append(str(exc))
signing_key = "" signing_key = ""
signing_key_query_failed = True
commit_check_passed = True commit_check_passed = True
gpg_check_passed = True gpg_check_passed = True
@@ -78,9 +80,10 @@ def verify_repository(repo, repo_dir, mode="local", no_verification=False):
if expected_gpg_keys: if expected_gpg_keys:
if not signing_key: if not signing_key:
gpg_check_passed = False gpg_check_passed = False
error_details.append( if not signing_key_query_failed:
f"Expected one of GPG keys: {expected_gpg_keys}, but no signing key was found." error_details.append(
) f"Expected one of GPG keys: {expected_gpg_keys}, but no signing key was found."
)
elif signing_key not in expected_gpg_keys: elif signing_key not in expected_gpg_keys:
gpg_check_passed = False gpg_check_passed = False
error_details.append( error_details.append(
@@ -0,0 +1,57 @@
from __future__ import annotations
import re
import unittest
from pathlib import Path
def _find_repo_root() -> Path:
here = Path(__file__).resolve()
for parent in here.parents:
if (parent / "pyproject.toml").is_file() and (
parent / "src" / "pkgmgr"
).is_dir():
return parent
raise RuntimeError(
"Could not determine repository root for pkgmgr integration test"
)
class TestGitVerificationRuntimeDependencies(unittest.TestCase):
def test_flake_app_includes_git_and_gpg_runtime_tools(self) -> None:
repo_root = _find_repo_root()
flake_text = (repo_root / "flake.nix").read_text(encoding="utf-8")
self.assertIn("pkgs.git", flake_text)
self.assertIn("pkgs.gnupg", flake_text)
def test_distro_dependency_scripts_install_gpg_tools(self) -> None:
repo_root = _find_repo_root()
expected_packages = {
"arch": "gnupg",
"debian": "gnupg",
"ubuntu": "gnupg",
"fedora": "gnupg2",
"centos": "gnupg2",
}
missing: list[str] = []
for distro, package_name in expected_packages.items():
script_path = (
repo_root / "scripts" / "installation" / distro / "dependencies.sh"
)
content = script_path.read_text(encoding="utf-8")
if not re.search(rf"\b{re.escape(package_name)}\b", content):
missing.append(
f"{distro}: expected package {package_name} in {script_path}"
)
if missing:
self.fail(
"Git signature verification runtime dependencies are incomplete:\n"
+ "\n".join(f" - {item}" for item in missing)
)
if __name__ == "__main__":
unittest.main()
@@ -1,7 +1,8 @@
import unittest import unittest
import subprocess
from unittest.mock import patch from unittest.mock import patch
from pkgmgr.core.git.errors import GitNotRepositoryError, GitRunError from pkgmgr.core.git.errors import GitNotRepositoryError
from pkgmgr.core.git.queries.get_latest_signing_key import ( from pkgmgr.core.git.queries.get_latest_signing_key import (
GitLatestSigningKeyQueryError, GitLatestSigningKeyQueryError,
get_latest_signing_key, get_latest_signing_key,
@@ -10,25 +11,53 @@ from pkgmgr.core.git.queries.get_latest_signing_key import (
class TestGetLatestSigningKey(unittest.TestCase): class TestGetLatestSigningKey(unittest.TestCase):
@patch( @patch(
"pkgmgr.core.git.queries.get_latest_signing_key.run", "pkgmgr.core.git.queries.get_latest_signing_key.subprocess.run",
return_value="ABCDEF1234567890\n", return_value=subprocess.CompletedProcess(
args=["git", "log", "-1", "--format=%GK"],
returncode=0,
stdout="ABCDEF1234567890\n",
stderr="",
),
) )
def test_strips_output(self, _mock_run) -> None: def test_strips_output(self, _mock_run) -> None:
out = get_latest_signing_key(cwd="/tmp/repo") out = get_latest_signing_key(cwd="/tmp/repo")
self.assertEqual(out, "ABCDEF1234567890") self.assertEqual(out, "ABCDEF1234567890")
@patch( @patch(
"pkgmgr.core.git.queries.get_latest_signing_key.run", "pkgmgr.core.git.queries.get_latest_signing_key.subprocess.run",
side_effect=GitRunError("boom"), return_value=subprocess.CompletedProcess(
args=["git", "log", "-1", "--format=%GK"],
returncode=1,
stdout="",
stderr="boom",
),
) )
def test_wraps_git_run_error(self, _mock_run) -> None: def test_wraps_git_run_error(self, _mock_run) -> None:
with self.assertRaises(GitLatestSigningKeyQueryError): with self.assertRaisesRegex(GitLatestSigningKeyQueryError, "boom"):
get_latest_signing_key(cwd="/tmp/repo") get_latest_signing_key(cwd="/tmp/repo")
@patch( @patch(
"pkgmgr.core.git.queries.get_latest_signing_key.run", "pkgmgr.core.git.queries.get_latest_signing_key.subprocess.run",
side_effect=GitNotRepositoryError("no repo"), return_value=subprocess.CompletedProcess(
args=["git", "log", "-1", "--format=%GK"],
returncode=128,
stdout="",
stderr="fatal: not a git repository",
),
) )
def test_does_not_catch_not_repository_error(self, _mock_run) -> None: def test_does_not_catch_not_repository_error(self, _mock_run) -> None:
with self.assertRaises(GitNotRepositoryError): with self.assertRaises(GitNotRepositoryError):
get_latest_signing_key(cwd="/tmp/no-repo") get_latest_signing_key(cwd="/tmp/no-repo")
@patch(
"pkgmgr.core.git.queries.get_latest_signing_key.subprocess.run",
return_value=subprocess.CompletedProcess(
args=["git", "log", "-1", "--format=%GK"],
returncode=0,
stdout="",
stderr="error: cannot run gpg: No such file or directory",
),
)
def test_raises_when_git_reports_gpg_runtime_error(self, _mock_run) -> None:
with self.assertRaisesRegex(GitLatestSigningKeyQueryError, "cannot run gpg"):
get_latest_signing_key(cwd="/tmp/repo")
@@ -77,6 +77,23 @@ class TestVerifyRepository(unittest.TestCase):
self.assertEqual(commit, "") self.assertEqual(commit, "")
self.assertEqual(key, "") self.assertEqual(key, "")
def test_verified_gpg_query_error_does_not_add_missing_key_fallback(self) -> None:
repo = {"verified": {"commit": None, "gpg_keys": ["ABC"]}}
with (
patch("pkgmgr.core.repository.verify.get_head_commit", return_value=""),
patch(
"pkgmgr.core.repository.verify.get_latest_signing_key",
side_effect=GitLatestSigningKeyQueryError("cannot run gpg"),
),
):
ok, errors, commit, key = verify_repository(repo, "/tmp/repo", mode="local")
self.assertFalse(ok)
self.assertIn("cannot run gpg", " ".join(errors))
self.assertFalse(any("no signing key was found" in e for e in errors))
self.assertEqual(commit, "")
self.assertEqual(key, "")
def test_strict_pull_collects_remote_error_message(self) -> None: def test_strict_pull_collects_remote_error_message(self) -> None:
repo = {"verified": {"commit": "expected", "gpg_keys": None}} repo = {"verified": {"commit": "expected", "gpg_keys": None}}
with ( with (