Skip to content

Commit 27d4376

Browse files
authored
Merge pull request #13 from offspot/2026
OCI Support (new ghcr images) and stack update
2 parents c1a5aba + f914509 commit 27d4376

File tree

9 files changed

+116
-129
lines changed

9 files changed

+116
-129
lines changed

.github/workflows/Publish.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ on:
66

77
jobs:
88
publish:
9-
runs-on: ubuntu-22.04
9+
runs-on: ubuntu-24.04
1010
permissions:
1111
id-token: write # mandatory for PyPI trusted publishing
1212

1313
steps:
14-
- uses: actions/checkout@v3
14+
- uses: actions/checkout@v6
1515

1616
- name: Set up Python
17-
uses: actions/setup-python@v4
17+
uses: actions/setup-python@v6
1818
with:
1919
python-version-file: pyproject.toml
2020
architecture: x64
@@ -25,7 +25,7 @@ jobs:
2525
python -m build --sdist --wheel
2626
2727
- name: Upload to PyPI
28-
uses: pypa/gh-action-pypi-publish@release/v1.8
28+
uses: pypa/gh-action-pypi-publish@release/v1
2929
# dont specify anything for Trusted Publishing
3030
# https://docs.pypi.org/trusted-publishers
3131
# with:

.github/workflows/QA.yaml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ on:
88

99
jobs:
1010
check-qa:
11-
runs-on: ubuntu-22.04
11+
runs-on: ubuntu-24.04
1212

1313
steps:
14-
- uses: actions/checkout@v3
14+
- uses: actions/checkout@v6
1515

1616
- name: Set up Python
17-
uses: actions/setup-python@v4
17+
uses: actions/setup-python@v6
1818
with:
1919
python-version-file: pyproject.toml
2020
architecture: x64
@@ -24,9 +24,6 @@ jobs:
2424
pip install -U pip
2525
pip install -e .[lint,scripts,test,check,visual]
2626
27-
- name: Check black formatting
28-
run: inv lint-black
29-
3027
- name: Check ruff
3128
run: inv lint-ruff
3229

.github/workflows/Tests.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ jobs:
1010
run-tests:
1111
strategy:
1212
matrix:
13-
os: [ubuntu-22.04]
14-
python: ["3.8", "3.9", "3.10", "3.11"]
13+
os: [ubuntu-24.04]
14+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
1515
runs-on: ${{ matrix.os }}
1616

1717
steps:
18-
- uses: actions/checkout@v3
18+
- uses: actions/checkout@v6
1919

2020
- name: Set up Python ${{ matrix.python }}
21-
uses: actions/setup-python@v4
21+
uses: actions/setup-python@v6
2222
with:
2323
python-version: ${{ matrix.python }}
2424
architecture: x64
@@ -32,18 +32,18 @@ jobs:
3232
run: inv coverage --args "-vvv"
3333

3434
- name: Upload coverage report to codecov
35-
if: matrix.python == '3.11'
35+
if: matrix.python == '3.10'
3636
uses: codecov/codecov-action@v3
3737
with:
3838
token: ${{ secrets.CODECOV_TOKEN }}
3939

4040
build_python:
41-
runs-on: ubuntu-22.04
41+
runs-on: ubuntu-24.04
4242
steps:
4343
- uses: actions/checkout@v3
4444

4545
- name: Set up Python
46-
uses: actions/setup-python@v4
46+
uses: actions/setup-python@v6
4747
with:
4848
python-version-file: pyproject.toml
4949
architecture: x64

.pre-commit-config.yaml

Lines changed: 0 additions & 21 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Added
11+
12+
- Support for OCI image indexes and manifest (#12)
13+
814
## [1.1.0] - 2024-01-30
915

1016
### Added

pyproject.toml

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,50 +8,53 @@ authors = [
88
{ name = "Kiwix", email = "dev@kiwix.org" },
99
]
1010
keywords = ["oci", "image", "docker", "kiwix"]
11-
requires-python = ">=3.8"
11+
requires-python = ">=3.10"
1212
description = "Export docker image into tar file directly from registry API"
1313
readme = "README.md"
1414
license = {text = "GPL-3.0-or-later"}
1515
classifiers = [
1616
"Programming Language :: Python :: 3",
17-
"Programming Language :: Python :: 3.8",
18-
"Programming Language :: Python :: 3.9",
1917
"Programming Language :: Python :: 3.10",
2018
"Programming Language :: Python :: 3.11",
19+
"Programming Language :: Python :: 3.12",
20+
"Programming Language :: Python :: 3.13",
21+
"Programming Language :: Python :: 3.14",
2122
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
2223
]
2324
dependencies = [
2425
"requests>=2,<3",
25-
"pathvalidate==3.2.0",
26+
"types-requests>=2,<3",
27+
"pathvalidate>=3.2.0",
2628
]
2729
dynamic = ["version"]
2830

2931
[project.optional-dependencies]
3032
scripts = [
31-
"invoke==2.2.0",
33+
"invoke==2.2.1",
3234
]
3335
lint = [
34-
"black==24.1.1",
35-
"ruff==0.1.15",
36+
"black==26.1.0",
37+
"ruff==0.15.4",
3638
]
3739
check = [
38-
"pyright==1.1.349",
40+
"pyright==1.1.408",
3941
]
4042
test = [
41-
"pytest==8.0.0",
42-
"coverage==7.4.1",
43+
"pytest==9.0.2",
44+
"coverage==7.13.4",
4345
]
4446
dev = [
45-
"pre-commit==3.6.0",
46-
"debugpy==1.6.7",
47+
"pre-commit==4.5.1",
48+
"debugpy==1.8.20",
4749
"docker-export[scripts]",
4850
"docker-export[lint]",
4951
"docker-export[test]",
5052
"docker-export[check]",
5153
"docker-export[visual]",
5254
]
5355
visual = [
54-
"humanfriendly>=8.0",
56+
"humanfriendly>=10.0",
57+
"types-humanfriendly>=10",
5558
"progressbar2>=4.0"
5659
]
5760

@@ -71,7 +74,7 @@ exclude = [
7174
]
7275

7376
[[tool.hatch.envs.default.matrix]]
74-
python = ["3.8"]
77+
python = ["3.10"]
7578

7679
[tool.hatch.envs.default]
7780
features = ["dev"]
@@ -80,7 +83,7 @@ features = ["dev"]
8083
features = ["scripts", "test", "visual"]
8184

8285
[[tool.hatch.envs.test.matrix]]
83-
python = ["3.8", "3.9", "3.10", "3.11"]
86+
python = ["3.10", "3.11", "3.12", "3.13", "3.14"]
8487

8588
[tool.hatch.envs.test.scripts]
8689
run = "inv test --args '{args}'"
@@ -94,10 +97,8 @@ skip-install = false
9497
features = ["scripts", "lint", "visual"]
9598

9699
[tool.hatch.envs.lint.scripts]
97-
black = "inv lint-black --args '{args}'"
98100
ruff = "inv lint-ruff --args '{args}'"
99101
all = "inv lintall --args '{args}'"
100-
fix-black = "inv fix-black --args '{args}'"
101102
fix-ruff = "inv fix-ruff --args '{args}'"
102103
fixall = "inv fixall --args '{args}'"
103104

@@ -108,14 +109,13 @@ features = ["scripts", "check", "test", "visual"]
108109
pyright = "inv check-pyright --args '{args}'"
109110
all = "inv checkall --args '{args}'"
110111

111-
[tool.black]
112-
line-length = 88
113-
target-version = ['py38']
114112

115113
[tool.ruff]
116-
target-version = "py38"
114+
target-version = "py310"
117115
line-length = 88
118116
src = ["src"]
117+
118+
[tool.ruff.lint]
119119
select = [
120120
"A", # flake8-builtins
121121
# "ANN", # flake8-annotations
@@ -194,17 +194,13 @@ unfixable = [
194194
"F401",
195195
]
196196

197-
[tool.ruff.isort]
197+
[tool.ruff.lint.isort]
198198
known-first-party = ["docker_export"]
199199

200-
[tool.ruff.flake8-bugbear]
201-
# add exceptions to B008 for fastapi.
202-
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
203-
204-
[tool.ruff.flake8-tidy-imports]
200+
[tool.ruff.lint.flake8-tidy-imports]
205201
ban-relative-imports = "all"
206202

207-
[tool.ruff.per-file-ignores]
203+
[tool.ruff.lint.per-file-ignores]
208204
# Tests can use magic values, assertions, and relative imports
209205
"tests/**/*" = ["PLR2004", "S101", "TID252"]
210206

@@ -236,6 +232,6 @@ exclude_lines = [
236232
include = ["src", "tests", "tasks.py"]
237233
exclude = [".env/**", ".venv/**"]
238234
extraPaths = ["src"]
239-
pythonVersion = "3.8"
235+
pythonVersion = "3.10"
240236
typeCheckingMode="strict"
241237
reportImplicitStringConcatenation=false

src/docker_export/__init__.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
except ImportError:
2828
progressbar = None
2929
try:
30-
import humanfriendly
30+
import humanfriendly # pyright: ignore [reportMissingTypeStubs]
3131
except ImportError:
3232
humanfriendly = None
3333

@@ -37,6 +37,15 @@
3737
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
3838
logger = logging.getLogger("docker-export")
3939
logging.getLogger("urllib3").setLevel(logging.WARNING)
40+
CT_DOCKER_MANIFEST_LIST = "application/vnd.docker.distribution.manifest.list.v2+json"
41+
CT_DOCKER_MANIFEST = "application/vnd.docker.distribution.manifest.v2+json"
42+
CT_OCI_INDEXES = "application/vnd.oci.image.index.v1+json"
43+
CT_OCI_MANIFEST = "application/vnd.oci.image.manifest.v1+json"
44+
CT_OCI_IMAGE_CONFIG = "application/vnd.oci.image.config.v1+json"
45+
CT_OCI_EMPTY = "application/vnd.oci.image.config.v1+json"
46+
CT_OCI_LAYERS = (
47+
"application/vnd.oci.image.layer.v1.tar,application/vnd.oci.image.layer.v1.tar+gzip"
48+
)
4049

4150

4251
class ImageNotFoundError(Exception): ...
@@ -128,8 +137,8 @@ def finish(self):
128137
print("")
129138

130139

131-
@dataclass
132-
class Platform:
140+
@dataclass(frozen=True)
141+
class Platform: # noqa: PLW1641
133142
architecture: str
134143
os: str
135144
variant: str
@@ -260,7 +269,7 @@ def url(self) -> str:
260269
"hub.docker.com" if self.registry == "index.docker.io" else self.registry
261270
)
262271
prefix = "r/" if self.registry == "index.docker.io" else ""
263-
return f"https://{domain}/{prefix}/{self.fullname}"
272+
return f"https://{domain}/{prefix}{'/' if prefix else ''}{self.fullname}"
264273

265274
@classmethod
266275
def parse(
@@ -384,7 +393,7 @@ def get_manifests(image: Image, auth: RegistryAuth):
384393
f"/manifests/{image.reference}",
385394
headers=dict(
386395
**auth.headers,
387-
**{"Accept": "application/vnd.docker.distribution.manifest.list.v2+json"},
396+
**{"Accept": ", ".join([CT_DOCKER_MANIFEST_LIST, CT_OCI_INDEXES])},
388397
),
389398
timeout=REQUEST_TIMEOUT,
390399
)
@@ -415,13 +424,12 @@ def get_layers_manifest_for(
415424
f"/manifests/{reference}",
416425
headers=dict(
417426
**auth.headers,
418-
**{"Accept": "application/vnd.docker.distribution.manifest.v2+json"},
427+
**{"Accept": ", ".join([CT_DOCKER_MANIFEST, CT_OCI_MANIFEST])},
419428
),
420429
timeout=REQUEST_TIMEOUT,
421430
)
422431
if resp.status_code != http.HTTPStatus.OK:
423-
raise OSError("HTTP {resp.status_code}: {resp.reason} -- {resp.text}")
424-
432+
raise OSError(f"HTTP {resp.status_code}: {resp.reason} -- {resp.text}")
425433
return resp.json()
426434

427435

@@ -438,7 +446,7 @@ def get_layers_from_v1_manifest(
438446
)
439447

440448
return {
441-
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
449+
"mediaType": CT_DOCKER_MANIFEST,
442450
"schemaVersion": 2,
443451
"config": {
444452
"mediaType": "application/vnd.docker.container.image.v1+json",
@@ -447,7 +455,7 @@ def get_layers_from_v1_manifest(
447455
},
448456
"layers": [
449457
{
450-
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
458+
"mediaType": CT_DOCKER_MANIFEST,
451459
"digest": layer["blobSum"],
452460
# "size": None,
453461
"platform": {"architecture": architecture, "os": os},
@@ -524,7 +532,7 @@ def download_layer_blob(
524532
f"https://{image.registry}/v2/{image.fullname}/blobs/{layer_digest}",
525533
headers=dict(
526534
**auth.headers,
527-
**{"Accept": "application/vnd.docker.distribution.manifest.v2+json"},
535+
**{"Accept": CT_DOCKER_MANIFEST},
528536
),
529537
stream=True,
530538
timeout=REQUEST_TIMEOUT,
@@ -536,7 +544,7 @@ def download_layer_blob(
536544
layer["urls"][0],
537545
headers=dict(
538546
**auth.headers,
539-
**{"Accept": "application/vnd.docker.distribution.manifest.v2+json"},
547+
**{"Accept": CT_DOCKER_MANIFEST},
540548
),
541549
stream=True,
542550
timeout=REQUEST_TIMEOUT,

0 commit comments

Comments
 (0)