pkgmap-py 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pkgmap_py-0.1.0/PKG-INFO +155 -0
- pkgmap_py-0.1.0/README.md +140 -0
- pkgmap_py-0.1.0/pyproject.toml +56 -0
- pkgmap_py-0.1.0/pyproject.toml.orig +44 -0
- pkgmap_py-0.1.0/src/pkgmap/__init__.py +26 -0
- pkgmap_py-0.1.0/src/pkgmap/apps.py +83 -0
- pkgmap_py-0.1.0/src/pkgmap/cli.py +171 -0
- pkgmap_py-0.1.0/src/pkgmap/consumer.py +145 -0
- pkgmap_py-0.1.0/src/pkgmap/db.py +90 -0
- pkgmap_py-0.1.0/src/pkgmap/downloader.py +64 -0
- pkgmap_py-0.1.0/src/pkgmap/logger.py +40 -0
- pkgmap_py-0.1.0/src/pkgmap/pipeline.py +253 -0
- pkgmap_py-0.1.0/src/pkgmap/resolver.py +203 -0
- pkgmap_py-0.1.0/src/pkgmap/schema.py +78 -0
pkgmap_py-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: pkgmap-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Cross-distribution Linux package name resolution
|
|
5
|
+
Author: Levizor
|
|
6
|
+
Author-email: Levizor <levizor@disroot.org>
|
|
7
|
+
License: MIT
|
|
8
|
+
Requires-Dist: typer>=0.12.0
|
|
9
|
+
Requires-Dist: zstandard>=0.22.0
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Project-URL: Homepage, https://github.com/Levizor/pkgmap
|
|
12
|
+
Project-URL: Repository, https://github.com/Levizor/pkgmap
|
|
13
|
+
Project-URL: Issues, https://github.com/Levizor/pkgmap/issues
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# pkgmap
|
|
17
|
+
|
|
18
|
+
Cross-distribution Linux package name resolution.
|
|
19
|
+
|
|
20
|
+
Different distributions often package the exact same software under different names (for example, `ninja` on Arch is `ninja-build` on Debian and Alpine; `bat` on Arch is `rust-bat` on Debian and Fedora; `fd` on Arch is `rust-fd-find` on Debian; Nixpkgs uses specific attribute paths). `pkgmap` provides an offline SQLite database and a companion Python library/CLI to translate package names between any pair of Linux distributions, resolve packages across all repositories, or look up native packages by upstream project name.
|
|
21
|
+
|
|
22
|
+
Data is updated daily from [Repology](https://repology.org) and indexed for instant local lookups. On first run, `pkgmap` automatically downloads and decompresses the latest daily database release (`~/.cache/pkgmap/pkgmap.db`).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## CLI Usage
|
|
27
|
+
|
|
28
|
+
### Translate between distributions
|
|
29
|
+
|
|
30
|
+
Use `--from` (`-f`) and `--to` (`-t`):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# What is Arch's 'bat' called in Debian?
|
|
34
|
+
pkgmap resolve bat --from arch --to debian
|
|
35
|
+
# Output: rust-bat (0.22.1) [category: misc]
|
|
36
|
+
|
|
37
|
+
# What is Arch's 'fd' in Nixpkgs?
|
|
38
|
+
pkgmap resolve fd -f arch -t nixpkgs
|
|
39
|
+
# Output: fd (10.5.0)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Resolve across all distributions
|
|
43
|
+
|
|
44
|
+
Omit `--to` to see equivalents across every indexed distribution:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pkgmap resolve bat --from arch
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Output:
|
|
51
|
+
```text
|
|
52
|
+
alpine_3_20: bat (0.24.0)
|
|
53
|
+
debian_12: rust-bat (0.22.1) [category: misc]
|
|
54
|
+
fedora_rawhide: rust-bat (0.26.1)
|
|
55
|
+
nix_unstable: bat (0.26.1)
|
|
56
|
+
ubuntu_24_04: rust-bat (0.24.0)
|
|
57
|
+
...
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Look up by upstream project name
|
|
61
|
+
|
|
62
|
+
Omit `--from` (or pass `--project` / `-p`) to resolve packages directly from an upstream project name:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pkgmap resolve ripgrep --to debian
|
|
66
|
+
# Output: rust-ripgrep (13.0.0) [category: misc]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### JSON output
|
|
70
|
+
|
|
71
|
+
To output machine-readable JSON (great for automation and scripts), add `--json` / `-j`:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pkgmap resolve bat -f arch -t debian --json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
[
|
|
79
|
+
{
|
|
80
|
+
"name": "rust-bat",
|
|
81
|
+
"version": "0.22.1",
|
|
82
|
+
"category": "misc",
|
|
83
|
+
"repo": "debian_12",
|
|
84
|
+
"repo_family": "debuntu",
|
|
85
|
+
"install_name": "rust-bat"
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Other commands
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# List available distributions and repository slugs
|
|
94
|
+
pkgmap repos
|
|
95
|
+
|
|
96
|
+
# Manually refresh the local database snapshot
|
|
97
|
+
pkgmap update
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Python API
|
|
103
|
+
|
|
104
|
+
You can also use `pkgmap` as a Python library:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from pkgmap import resolve, resolve_all, resolve_batch
|
|
108
|
+
|
|
109
|
+
# 1. Single package resolution
|
|
110
|
+
res = resolve("bat", from_repo="arch", to_repo="debian")
|
|
111
|
+
if res:
|
|
112
|
+
print(res.install_name, res.version)
|
|
113
|
+
# Output: rust-bat 0.22.1
|
|
114
|
+
|
|
115
|
+
# 2. Query all repositories for equivalents
|
|
116
|
+
matches = resolve_all("bat", from_repo="arch")
|
|
117
|
+
for r in matches:
|
|
118
|
+
print(f"{r.repo}: {r.install_name}")
|
|
119
|
+
|
|
120
|
+
# 3. High-performance batch resolution (executed in a single SQL query)
|
|
121
|
+
packages = ["bat", "fd", "ripgrep"]
|
|
122
|
+
results = resolve_batch(packages, from_repo="arch", to_repo="nixpkgs")
|
|
123
|
+
|
|
124
|
+
for pkg, match in results.items():
|
|
125
|
+
if match:
|
|
126
|
+
print(f"{pkg} -> {match.install_name}")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### API Methods
|
|
130
|
+
|
|
131
|
+
- `resolve(package, from_repo=None, to_repo=None, db_path=None)`: Resolves a package to a single match.
|
|
132
|
+
- `resolve_all(package, from_repo=None, to_repo=None, db_path=None)`: Returns all matching packages.
|
|
133
|
+
- `resolve_batch(packages, from_repo=None, to_repo=None, db_path=None)`: Resolves a sequence of packages in a single batch query, returning `{package: PackageResolution | None}`.
|
|
134
|
+
- `resolve_batch_all(packages, from_repo=None, to_repo=None, db_path=None)`: Batch resolution returning `{package: list[PackageResolution]}`.
|
|
135
|
+
- `list_repositories(db_path=None)`: Returns all repositories with package counts and families.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
By default, the database is stored at:
|
|
142
|
+
- `~/.cache/pkgmap/pkgmap.db`
|
|
143
|
+
|
|
144
|
+
You can customize the location using:
|
|
145
|
+
- The `PKGMAP_DB_PATH` environment variable:
|
|
146
|
+
```bash
|
|
147
|
+
export PKGMAP_DB_PATH="/path/to/custom/pkgmap.db"
|
|
148
|
+
```
|
|
149
|
+
- Or the `--db-path` argument on the CLI / `db_path` parameter in Python.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Attribution
|
|
154
|
+
|
|
155
|
+
Data is sourced daily from [Repology](https://repology.org). Repology's data is published under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# pkgmap
|
|
2
|
+
|
|
3
|
+
Cross-distribution Linux package name resolution.
|
|
4
|
+
|
|
5
|
+
Different distributions often package the exact same software under different names (for example, `ninja` on Arch is `ninja-build` on Debian and Alpine; `bat` on Arch is `rust-bat` on Debian and Fedora; `fd` on Arch is `rust-fd-find` on Debian; Nixpkgs uses specific attribute paths). `pkgmap` provides an offline SQLite database and a companion Python library/CLI to translate package names between any pair of Linux distributions, resolve packages across all repositories, or look up native packages by upstream project name.
|
|
6
|
+
|
|
7
|
+
Data is updated daily from [Repology](https://repology.org) and indexed for instant local lookups. On first run, `pkgmap` automatically downloads and decompresses the latest daily database release (`~/.cache/pkgmap/pkgmap.db`).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## CLI Usage
|
|
12
|
+
|
|
13
|
+
### Translate between distributions
|
|
14
|
+
|
|
15
|
+
Use `--from` (`-f`) and `--to` (`-t`):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# What is Arch's 'bat' called in Debian?
|
|
19
|
+
pkgmap resolve bat --from arch --to debian
|
|
20
|
+
# Output: rust-bat (0.22.1) [category: misc]
|
|
21
|
+
|
|
22
|
+
# What is Arch's 'fd' in Nixpkgs?
|
|
23
|
+
pkgmap resolve fd -f arch -t nixpkgs
|
|
24
|
+
# Output: fd (10.5.0)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Resolve across all distributions
|
|
28
|
+
|
|
29
|
+
Omit `--to` to see equivalents across every indexed distribution:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pkgmap resolve bat --from arch
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Output:
|
|
36
|
+
```text
|
|
37
|
+
alpine_3_20: bat (0.24.0)
|
|
38
|
+
debian_12: rust-bat (0.22.1) [category: misc]
|
|
39
|
+
fedora_rawhide: rust-bat (0.26.1)
|
|
40
|
+
nix_unstable: bat (0.26.1)
|
|
41
|
+
ubuntu_24_04: rust-bat (0.24.0)
|
|
42
|
+
...
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Look up by upstream project name
|
|
46
|
+
|
|
47
|
+
Omit `--from` (or pass `--project` / `-p`) to resolve packages directly from an upstream project name:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pkgmap resolve ripgrep --to debian
|
|
51
|
+
# Output: rust-ripgrep (13.0.0) [category: misc]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### JSON output
|
|
55
|
+
|
|
56
|
+
To output machine-readable JSON (great for automation and scripts), add `--json` / `-j`:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pkgmap resolve bat -f arch -t debian --json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
[
|
|
64
|
+
{
|
|
65
|
+
"name": "rust-bat",
|
|
66
|
+
"version": "0.22.1",
|
|
67
|
+
"category": "misc",
|
|
68
|
+
"repo": "debian_12",
|
|
69
|
+
"repo_family": "debuntu",
|
|
70
|
+
"install_name": "rust-bat"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Other commands
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# List available distributions and repository slugs
|
|
79
|
+
pkgmap repos
|
|
80
|
+
|
|
81
|
+
# Manually refresh the local database snapshot
|
|
82
|
+
pkgmap update
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Python API
|
|
88
|
+
|
|
89
|
+
You can also use `pkgmap` as a Python library:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from pkgmap import resolve, resolve_all, resolve_batch
|
|
93
|
+
|
|
94
|
+
# 1. Single package resolution
|
|
95
|
+
res = resolve("bat", from_repo="arch", to_repo="debian")
|
|
96
|
+
if res:
|
|
97
|
+
print(res.install_name, res.version)
|
|
98
|
+
# Output: rust-bat 0.22.1
|
|
99
|
+
|
|
100
|
+
# 2. Query all repositories for equivalents
|
|
101
|
+
matches = resolve_all("bat", from_repo="arch")
|
|
102
|
+
for r in matches:
|
|
103
|
+
print(f"{r.repo}: {r.install_name}")
|
|
104
|
+
|
|
105
|
+
# 3. High-performance batch resolution (executed in a single SQL query)
|
|
106
|
+
packages = ["bat", "fd", "ripgrep"]
|
|
107
|
+
results = resolve_batch(packages, from_repo="arch", to_repo="nixpkgs")
|
|
108
|
+
|
|
109
|
+
for pkg, match in results.items():
|
|
110
|
+
if match:
|
|
111
|
+
print(f"{pkg} -> {match.install_name}")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### API Methods
|
|
115
|
+
|
|
116
|
+
- `resolve(package, from_repo=None, to_repo=None, db_path=None)`: Resolves a package to a single match.
|
|
117
|
+
- `resolve_all(package, from_repo=None, to_repo=None, db_path=None)`: Returns all matching packages.
|
|
118
|
+
- `resolve_batch(packages, from_repo=None, to_repo=None, db_path=None)`: Resolves a sequence of packages in a single batch query, returning `{package: PackageResolution | None}`.
|
|
119
|
+
- `resolve_batch_all(packages, from_repo=None, to_repo=None, db_path=None)`: Batch resolution returning `{package: list[PackageResolution]}`.
|
|
120
|
+
- `list_repositories(db_path=None)`: Returns all repositories with package counts and families.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
By default, the database is stored at:
|
|
127
|
+
- `~/.cache/pkgmap/pkgmap.db`
|
|
128
|
+
|
|
129
|
+
You can customize the location using:
|
|
130
|
+
- The `PKGMAP_DB_PATH` environment variable:
|
|
131
|
+
```bash
|
|
132
|
+
export PKGMAP_DB_PATH="/path/to/custom/pkgmap.db"
|
|
133
|
+
```
|
|
134
|
+
- Or the `--db-path` argument on the CLI / `db_path` parameter in Python.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Attribution
|
|
139
|
+
|
|
140
|
+
Data is sourced daily from [Repology](https://repology.org). Repology's data is published under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pkgmap-py"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Cross-distribution Linux package name resolution"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"typer>=0.12.0",
|
|
9
|
+
"zstandard>=0.22.0",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[[project.authors]]
|
|
13
|
+
name = "Levizor"
|
|
14
|
+
email = "levizor@disroot.org"
|
|
15
|
+
|
|
16
|
+
[project.license]
|
|
17
|
+
text = "MIT"
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/Levizor/pkgmap"
|
|
21
|
+
Repository = "https://github.com/Levizor/pkgmap"
|
|
22
|
+
Issues = "https://github.com/Levizor/pkgmap/issues"
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
pkgmap = "pkgmap.apps:cli_app"
|
|
26
|
+
pkgmap-pipeline = "pkgmap.apps:pipeline_app"
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = [
|
|
30
|
+
"pytest>=8.0.0",
|
|
31
|
+
"ruff>=0.4.0",
|
|
32
|
+
"pre-commit",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["uv_build>=0.9.0,<0.10.0"]
|
|
37
|
+
build-backend = "uv_build"
|
|
38
|
+
|
|
39
|
+
[tool.uv.build-backend]
|
|
40
|
+
module-name = "pkgmap"
|
|
41
|
+
|
|
42
|
+
[tool.ruff.lint]
|
|
43
|
+
select = [
|
|
44
|
+
"E4",
|
|
45
|
+
"E7",
|
|
46
|
+
"E9",
|
|
47
|
+
"F",
|
|
48
|
+
"I",
|
|
49
|
+
"B",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint.isort]
|
|
53
|
+
combine-as-imports = true
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pkgmap-py"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Cross-distribution Linux package name resolution"
|
|
5
|
+
authors = [{ name = "Levizor", email = "levizor@disroot.org" }]
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"typer>=0.12.0",
|
|
11
|
+
"zstandard>=0.22.0",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Homepage = "https://github.com/Levizor/pkgmap"
|
|
16
|
+
Repository = "https://github.com/Levizor/pkgmap"
|
|
17
|
+
Issues = "https://github.com/Levizor/pkgmap/issues"
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
pkgmap = "pkgmap.apps:cli_app"
|
|
21
|
+
pkgmap-pipeline = "pkgmap.apps:pipeline_app"
|
|
22
|
+
|
|
23
|
+
[dependency-groups]
|
|
24
|
+
dev = [
|
|
25
|
+
"pytest>=8.0.0",
|
|
26
|
+
"ruff>=0.4.0",
|
|
27
|
+
"pre-commit",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["uv_build>=0.9.0,<0.10.0"]
|
|
32
|
+
build-backend = "uv_build"
|
|
33
|
+
|
|
34
|
+
[tool.uv.build-backend]
|
|
35
|
+
module-name = "pkgmap"
|
|
36
|
+
|
|
37
|
+
[tool.ruff.lint]
|
|
38
|
+
select = ["E4", "E7", "E9", "F", "I", "B"]
|
|
39
|
+
|
|
40
|
+
[tool.ruff.lint.isort]
|
|
41
|
+
combine-as-imports = true
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Cross-distribution Linux package name resolution library."""
|
|
2
|
+
|
|
3
|
+
from pkgmap.db import ensure_database, get_database_path, update_database
|
|
4
|
+
from pkgmap.resolver import (
|
|
5
|
+
PackageResolution,
|
|
6
|
+
list_repositories,
|
|
7
|
+
resolve,
|
|
8
|
+
resolve_all,
|
|
9
|
+
resolve_batch,
|
|
10
|
+
resolve_batch_all,
|
|
11
|
+
resolve_project,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
__all__ = [
|
|
16
|
+
"PackageResolution",
|
|
17
|
+
"ensure_database",
|
|
18
|
+
"get_database_path",
|
|
19
|
+
"list_repositories",
|
|
20
|
+
"resolve",
|
|
21
|
+
"resolve_all",
|
|
22
|
+
"resolve_batch",
|
|
23
|
+
"resolve_batch_all",
|
|
24
|
+
"resolve_project",
|
|
25
|
+
"update_database",
|
|
26
|
+
]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
from pkgmap import pipeline
|
|
7
|
+
from pkgmap.cli import cli_app
|
|
8
|
+
from pkgmap.pipeline import download_dump_file, set_github_output
|
|
9
|
+
|
|
10
|
+
pipeline_app = typer.Typer(
|
|
11
|
+
name="pkgmap-pipeline",
|
|
12
|
+
help="Download dump, transform, and build package map database",
|
|
13
|
+
add_completion=False,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pipeline_app.command(name="download")
|
|
18
|
+
def pipeline_download(
|
|
19
|
+
link: Annotated[
|
|
20
|
+
str | None,
|
|
21
|
+
typer.Option("--link", "-l", help="Dump link to download from"),
|
|
22
|
+
] = None,
|
|
23
|
+
dest_dir: Annotated[
|
|
24
|
+
Path | None,
|
|
25
|
+
typer.Option("--dest-dir", "-d", help="Directory where to save the dump file"),
|
|
26
|
+
] = None,
|
|
27
|
+
) -> None:
|
|
28
|
+
"""Download the latest Repology database dump."""
|
|
29
|
+
dump_file, dump_date = download_dump_file(dump_link=link, dest_dir=dest_dir)
|
|
30
|
+
set_github_output(
|
|
31
|
+
date=dump_date,
|
|
32
|
+
tag=f"db-{dump_date}",
|
|
33
|
+
dump_path=str(dump_file.resolve()),
|
|
34
|
+
)
|
|
35
|
+
typer.echo(f"Downloaded Repology dump ({dump_date}) to {dump_file}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pipeline_app.command(name="build")
|
|
39
|
+
def pipeline_build(
|
|
40
|
+
input_dump: Annotated[
|
|
41
|
+
Path | None,
|
|
42
|
+
typer.Option(
|
|
43
|
+
"--input-dump",
|
|
44
|
+
"-i",
|
|
45
|
+
help="Dump file to take as an input to the processing pipeline",
|
|
46
|
+
),
|
|
47
|
+
] = None,
|
|
48
|
+
output_file: Annotated[
|
|
49
|
+
Path,
|
|
50
|
+
typer.Option(
|
|
51
|
+
"-o",
|
|
52
|
+
"--output-path",
|
|
53
|
+
help="Path for the output SQLite database",
|
|
54
|
+
),
|
|
55
|
+
] = Path("pkgmap.db"),
|
|
56
|
+
batch_size: Annotated[
|
|
57
|
+
int,
|
|
58
|
+
typer.Option(
|
|
59
|
+
"--batch-size",
|
|
60
|
+
help="Number of records per batch in the processing pipeline",
|
|
61
|
+
),
|
|
62
|
+
] = 50_000,
|
|
63
|
+
release_notes: Annotated[
|
|
64
|
+
Path | None,
|
|
65
|
+
typer.Option("--release-notes", help="Path to generate release notes markdown"),
|
|
66
|
+
] = None,
|
|
67
|
+
dump_date: Annotated[
|
|
68
|
+
str | None,
|
|
69
|
+
typer.Option("--dump-date", help="Snapshot date (YYYY-MM-DD)"),
|
|
70
|
+
] = None,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Process Repology dump and generate SQLite package map database."""
|
|
73
|
+
pipeline.main(
|
|
74
|
+
dump_file=input_dump,
|
|
75
|
+
output_file=output_file,
|
|
76
|
+
batch_size=batch_size,
|
|
77
|
+
release_notes=release_notes,
|
|
78
|
+
dump_date=dump_date,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
cli_app()
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from pkgmap.db import get_database_path, update_database
|
|
8
|
+
from pkgmap.resolver import list_repositories, resolve_all, resolve_project
|
|
9
|
+
|
|
10
|
+
cli_app = typer.Typer(
|
|
11
|
+
name="pkgmap",
|
|
12
|
+
help="Cross-distribution Linux package name resolution CLI",
|
|
13
|
+
add_completion=False,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@cli_app.callback()
|
|
18
|
+
def cli_callback(
|
|
19
|
+
verbose: Annotated[
|
|
20
|
+
bool,
|
|
21
|
+
typer.Option("--verbose", "-v", help="Enable verbose debug logging"),
|
|
22
|
+
] = False,
|
|
23
|
+
) -> None:
|
|
24
|
+
if verbose:
|
|
25
|
+
import logging
|
|
26
|
+
|
|
27
|
+
for name in ("pkgmap", "pkgmap-db", "pkgmap-downloader", "pkgmap-consumer"):
|
|
28
|
+
log = logging.getLogger(name)
|
|
29
|
+
log.setLevel(logging.DEBUG)
|
|
30
|
+
for handler in log.handlers:
|
|
31
|
+
handler.setLevel(logging.DEBUG)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@cli_app.command(name="resolve")
|
|
35
|
+
def resolve_cmd(
|
|
36
|
+
package: Annotated[
|
|
37
|
+
str, typer.Argument(help="Package or upstream project name to resolve")
|
|
38
|
+
],
|
|
39
|
+
to_repo: Annotated[
|
|
40
|
+
str | None,
|
|
41
|
+
typer.Option(
|
|
42
|
+
"--to",
|
|
43
|
+
"-t",
|
|
44
|
+
help="Target repository/distribution (e.g. nixpkgs, alpine). If omitted, matches across all repositories.",
|
|
45
|
+
),
|
|
46
|
+
] = None,
|
|
47
|
+
from_repo: Annotated[
|
|
48
|
+
str | None,
|
|
49
|
+
typer.Option(
|
|
50
|
+
"--from",
|
|
51
|
+
"-f",
|
|
52
|
+
help="Source repository/distribution (e.g. debian, arch). If omitted, package is resolved by upstream project name.",
|
|
53
|
+
),
|
|
54
|
+
] = None,
|
|
55
|
+
project: Annotated[
|
|
56
|
+
bool,
|
|
57
|
+
typer.Option(
|
|
58
|
+
"--project",
|
|
59
|
+
"-p",
|
|
60
|
+
help="Resolve package directly as an upstream project name",
|
|
61
|
+
),
|
|
62
|
+
] = False,
|
|
63
|
+
all_matches: Annotated[
|
|
64
|
+
bool,
|
|
65
|
+
typer.Option("--all", "-a", help="Show all matching target packages"),
|
|
66
|
+
] = False,
|
|
67
|
+
json_output: Annotated[
|
|
68
|
+
bool,
|
|
69
|
+
typer.Option("--json", "-j", help="Output results in JSON format"),
|
|
70
|
+
] = False,
|
|
71
|
+
db_path: Annotated[
|
|
72
|
+
Path | None,
|
|
73
|
+
typer.Option("--db-path", help="Custom path to pkgmap.db database"),
|
|
74
|
+
] = None,
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Resolve a package name across Linux distributions."""
|
|
77
|
+
if project and from_repo:
|
|
78
|
+
typer.secho(
|
|
79
|
+
"Error: Cannot specify both --from and --project.",
|
|
80
|
+
fg=typer.colors.RED,
|
|
81
|
+
)
|
|
82
|
+
raise typer.Exit(code=2)
|
|
83
|
+
|
|
84
|
+
if from_repo and not project:
|
|
85
|
+
results = resolve_all(
|
|
86
|
+
package, from_repo=from_repo, to_repo=to_repo, db_path=db_path
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
results = resolve_project(package, to_repo=to_repo, db_path=db_path)
|
|
90
|
+
|
|
91
|
+
if not results:
|
|
92
|
+
if json_output:
|
|
93
|
+
typer.echo(json.dumps([]))
|
|
94
|
+
else:
|
|
95
|
+
if from_repo and not project:
|
|
96
|
+
if to_repo:
|
|
97
|
+
msg = f"No equivalent package found for '{package}' from '{from_repo}' to '{to_repo}'."
|
|
98
|
+
else:
|
|
99
|
+
msg = f"No equivalent package found for '{package}' from '{from_repo}'."
|
|
100
|
+
else:
|
|
101
|
+
if to_repo:
|
|
102
|
+
msg = f"No equivalent package found for project '{package}' in '{to_repo}'."
|
|
103
|
+
else:
|
|
104
|
+
msg = f"No equivalent package found for project '{package}'."
|
|
105
|
+
typer.secho(msg, fg=typer.colors.YELLOW)
|
|
106
|
+
raise typer.Exit(code=1)
|
|
107
|
+
|
|
108
|
+
items_to_show = results if (all_matches or to_repo is None) else results[:1]
|
|
109
|
+
|
|
110
|
+
if json_output:
|
|
111
|
+
data = [
|
|
112
|
+
{
|
|
113
|
+
"name": r.name,
|
|
114
|
+
"version": r.version,
|
|
115
|
+
"category": r.category,
|
|
116
|
+
"repo": r.repo,
|
|
117
|
+
"repo_family": r.repo_family,
|
|
118
|
+
"install_name": r.install_name,
|
|
119
|
+
}
|
|
120
|
+
for r in items_to_show
|
|
121
|
+
]
|
|
122
|
+
typer.echo(json.dumps(data, indent=2))
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
for res in items_to_show:
|
|
126
|
+
version_str = f" ({res.version})" if res.version else ""
|
|
127
|
+
meta_parts = []
|
|
128
|
+
if res.category and res.repo_family != "nix":
|
|
129
|
+
meta_parts.append(f"category: {res.category}")
|
|
130
|
+
meta_str = f" [{', '.join(meta_parts)}]" if meta_parts else ""
|
|
131
|
+
repo_prefix = f"{res.repo}: " if to_repo is None else ""
|
|
132
|
+
typer.echo(f"{repo_prefix}{res.install_name}{version_str}{meta_str}")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@cli_app.command(name="update")
|
|
136
|
+
def update_cmd(
|
|
137
|
+
db_path: Annotated[
|
|
138
|
+
Path | None,
|
|
139
|
+
typer.Option("--db-path", help="Custom path to save pkgmap.db database"),
|
|
140
|
+
] = None,
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Explicitly download and update the local package map database."""
|
|
143
|
+
target_path = get_database_path(db_path)
|
|
144
|
+
typer.echo(f"Updating package database at {target_path}...")
|
|
145
|
+
update_database(db_path=target_path, show_progress=True)
|
|
146
|
+
typer.secho(
|
|
147
|
+
f"Successfully updated database at {target_path}", fg=typer.colors.GREEN
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@cli_app.command(name="repos")
|
|
152
|
+
def repos_cmd(
|
|
153
|
+
db_path: Annotated[
|
|
154
|
+
Path | None,
|
|
155
|
+
typer.Option("--db-path", help="Custom path to pkgmap.db database"),
|
|
156
|
+
] = None,
|
|
157
|
+
) -> None:
|
|
158
|
+
"""List all available distributions and repositories."""
|
|
159
|
+
repos = list_repositories(db_path=db_path)
|
|
160
|
+
typer.echo(f"{'Repository':<25} {'Family':<15} {'Packages':>10}")
|
|
161
|
+
typer.echo("-" * 52)
|
|
162
|
+
for r in sorted(repos, key=lambda x: x.name):
|
|
163
|
+
typer.echo(f"{r.name:<25} {r.family:<15} {r.package_count:>10,}")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def main() -> None:
|
|
167
|
+
cli_app()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == "__main__":
|
|
171
|
+
main()
|