Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5843ccd30 | ||
|
|
3cb7852cb4 |
@@ -1,3 +1,8 @@
|
||||
## [1.8.3] - 2025-12-16
|
||||
|
||||
* MIRRORS now supports plain URL entries, ensuring metadata-only sources like PyPI are recorded without ever being added to the Git configuration.
|
||||
|
||||
|
||||
## [1.8.2] - 2025-12-16
|
||||
|
||||
* * ***pkgmgr tools code*** is more robust and predictable: it now fails early with clear errors if VS Code is not installed or a repository is not yet identified.
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
rec {
|
||||
pkgmgr = pyPkgs.buildPythonApplication {
|
||||
pname = "package-manager";
|
||||
version = "1.8.2";
|
||||
version = "1.8.3";
|
||||
|
||||
# Use the git repo as source
|
||||
src = ./.;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maintainer: Kevin Veen-Birkenbach <info@veen.world>
|
||||
|
||||
pkgname=package-manager
|
||||
pkgver=1.8.2
|
||||
pkgver=1.8.3
|
||||
pkgrel=1
|
||||
pkgdesc="Local-flake wrapper for Kevin's package-manager (Nix-based)."
|
||||
arch=('any')
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
package-manager (1.8.3-1) unstable; urgency=medium
|
||||
|
||||
* MIRRORS now supports plain URL entries, ensuring metadata-only sources like PyPI are recorded without ever being added to the Git configuration.
|
||||
|
||||
-- Kevin Veen-Birkenbach <kevin@veen.world> Tue, 16 Dec 2025 19:49:51 +0100
|
||||
|
||||
package-manager (1.8.2-1) unstable; urgency=medium
|
||||
|
||||
* * ***pkgmgr tools code*** is more robust and predictable: it now fails early with clear errors if VS Code is not installed or a repository is not yet identified.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: package-manager
|
||||
Version: 1.8.2
|
||||
Version: 1.8.3
|
||||
Release: 1%{?dist}
|
||||
Summary: Wrapper that runs Kevin's package-manager via Nix flake
|
||||
|
||||
@@ -74,6 +74,9 @@ echo ">>> package-manager removed. Nix itself was not removed."
|
||||
/usr/lib/package-manager/
|
||||
|
||||
%changelog
|
||||
* Tue Dec 16 2025 Kevin Veen-Birkenbach <kevin@veen.world> - 1.8.3-1
|
||||
- MIRRORS now supports plain URL entries, ensuring metadata-only sources like PyPI are recorded without ever being added to the Git configuration.
|
||||
|
||||
* Tue Dec 16 2025 Kevin Veen-Birkenbach <kevin@veen.world> - 1.8.2-1
|
||||
- * ***pkgmgr tools code*** is more robust and predictable: it now fails early with clear errors if VS Code is not installed or a repository is not yet identified.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "kpmx"
|
||||
version = "1.8.2"
|
||||
version = "1.8.3"
|
||||
description = "Kevin's package-manager tool (pkgmgr)"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from collections.abc import Iterable, Mapping
|
||||
from typing import Union
|
||||
from urllib.parse import urlparse
|
||||
from typing import Mapping
|
||||
|
||||
from .types import MirrorMap, Repository
|
||||
|
||||
@@ -32,7 +33,7 @@ def read_mirrors_file(repo_dir: str, filename: str = "MIRRORS") -> MirrorMap:
|
||||
"""
|
||||
Supports:
|
||||
NAME URL
|
||||
URL → auto name = hostname
|
||||
URL -> auto-generate name from hostname
|
||||
"""
|
||||
path = os.path.join(repo_dir, filename)
|
||||
mirrors: MirrorMap = {}
|
||||
@@ -52,7 +53,8 @@ def read_mirrors_file(repo_dir: str, filename: str = "MIRRORS") -> MirrorMap:
|
||||
# Case 1: "name url"
|
||||
if len(parts) == 2:
|
||||
name, url = parts
|
||||
# Case 2: "url" → auto-generate name
|
||||
|
||||
# Case 2: "url" -> auto name
|
||||
elif len(parts) == 1:
|
||||
url = parts[0]
|
||||
parsed = urlparse(url)
|
||||
@@ -67,21 +69,56 @@ def read_mirrors_file(repo_dir: str, filename: str = "MIRRORS") -> MirrorMap:
|
||||
continue
|
||||
|
||||
mirrors[name] = url
|
||||
|
||||
except OSError as exc:
|
||||
print(f"[WARN] Could not read MIRRORS file at {path}: {exc}")
|
||||
|
||||
return mirrors
|
||||
|
||||
|
||||
MirrorsInput = Union[Mapping[str, str], Iterable[str]]
|
||||
|
||||
|
||||
def write_mirrors_file(
|
||||
repo_dir: str,
|
||||
mirrors: Mapping[str, str],
|
||||
mirrors: MirrorsInput,
|
||||
filename: str = "MIRRORS",
|
||||
preview: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
Write MIRRORS in one of two formats:
|
||||
|
||||
1) Mapping[str, str] -> "NAME URL" per line (legacy / compatible)
|
||||
2) Iterable[str] -> "URL" per line (new preferred)
|
||||
|
||||
Strings are treated as a single URL (not iterated character-by-character).
|
||||
"""
|
||||
path = os.path.join(repo_dir, filename)
|
||||
lines = [f"{name} {url}" for name, url in sorted(mirrors.items())]
|
||||
|
||||
lines: list[str]
|
||||
|
||||
if isinstance(mirrors, Mapping):
|
||||
items = [
|
||||
(str(name), str(url))
|
||||
for name, url in mirrors.items()
|
||||
if url is not None and str(url).strip()
|
||||
]
|
||||
items.sort(key=lambda x: (x[0], x[1]))
|
||||
lines = [f"{name} {url}" for name, url in items]
|
||||
|
||||
else:
|
||||
if isinstance(mirrors, (str, bytes)):
|
||||
urls = [str(mirrors).strip()]
|
||||
else:
|
||||
urls = [
|
||||
str(url).strip()
|
||||
for url in mirrors
|
||||
if url is not None and str(url).strip()
|
||||
]
|
||||
|
||||
urls = sorted(set(urls))
|
||||
lines = urls
|
||||
|
||||
content = "\n".join(lines) + ("\n" if lines else "")
|
||||
|
||||
if preview:
|
||||
@@ -94,5 +131,6 @@ def write_mirrors_file(
|
||||
with open(path, "w", encoding="utf-8") as fh:
|
||||
fh.write(content)
|
||||
print(f"[INFO] Wrote MIRRORS file at {path}")
|
||||
|
||||
except OSError as exc:
|
||||
print(f"[ERROR] Failed to write MIRRORS file at {path}: {exc}")
|
||||
|
||||
@@ -12,8 +12,8 @@ class MirrorBootstrapper:
|
||||
"""
|
||||
MIRRORS is the single source of truth.
|
||||
|
||||
We write defaults to MIRRORS and then call mirror setup which will
|
||||
configure git remotes based on MIRRORS content (but only for git URLs).
|
||||
Defaults are written to MIRRORS and mirror setup derives
|
||||
git remotes exclusively from that file (git URLs only).
|
||||
"""
|
||||
|
||||
def write_defaults(
|
||||
@@ -25,10 +25,8 @@ class MirrorBootstrapper:
|
||||
preview: bool,
|
||||
) -> None:
|
||||
mirrors = {
|
||||
# preferred SSH url is supplied by CreateRepoPlanner.primary_remote
|
||||
"origin": primary,
|
||||
# metadata only: must NEVER be configured as a git remote
|
||||
"pypi": f"https://pypi.org/project/{name}/",
|
||||
primary,
|
||||
f"https://pypi.org/project/{name}/",
|
||||
}
|
||||
write_mirrors_file(repo_dir, mirrors, preview=preview)
|
||||
|
||||
@@ -41,7 +39,8 @@ class MirrorBootstrapper:
|
||||
preview: bool,
|
||||
remote: bool,
|
||||
) -> None:
|
||||
# IMPORTANT: do NOT set repo["mirrors"] here.
|
||||
# IMPORTANT:
|
||||
# Do NOT set repo["mirrors"] here.
|
||||
# MIRRORS file is the single source of truth.
|
||||
setup_mirrors(
|
||||
selected_repos=[repo],
|
||||
|
||||
@@ -75,12 +75,12 @@ class TestCreateRepoPypiNotInGitConfig(unittest.TestCase):
|
||||
|
||||
mirrors_content = mirrors_file.read_text(encoding="utf-8")
|
||||
self.assertIn(
|
||||
"pypi https://pypi.org/project/repo/",
|
||||
"https://pypi.org/project/repo/",
|
||||
mirrors_content,
|
||||
"PyPI mirror entry must exist in MIRRORS",
|
||||
)
|
||||
self.assertIn(
|
||||
"origin git@github.com:acme/repo.git",
|
||||
"git@github.com:acme/repo.git",
|
||||
mirrors_content,
|
||||
"origin SSH URL must exist in MIRRORS",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user