diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 09ee295..9ad7293 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -25,6 +25,9 @@ jobs: run: | FORCE=$(if git log --pretty=format:"%s" HEAD^..HEAD | grep -q '\[force\]'; then echo "--force"; else echo ""; fi) uv run dpn $FORCE build-matrix --event ${{ github.event_name }} + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_PUBLIC_TOKEN: ${{ secrets.DOCKERHUB_PUBLIC_TOKEN }} deploy: diff --git a/src/docker_python_nodejs/docker_hub.py b/src/docker_python_nodejs/docker_hub.py index ea3c04c..4fb6bb0 100644 --- a/src/docker_python_nodejs/docker_hub.py +++ b/src/docker_python_nodejs/docker_hub.py @@ -1,3 +1,4 @@ +import os from typing import TypedDict import requests @@ -43,15 +44,37 @@ class DockerTagResponse(TypedDict): results: list[DockerTagDict] -def fetch_tags(package: str, page: int = 1) -> list[DockerTagDict]: +class DockerCreateAccessTokenResponse(TypedDict): + access_token: str + + +def token_auth() -> str: + """Return a short lived access token for Docker Hub authentication. Expires in 10min""" + user = os.getenv("DOCKERHUB_USERNAME", "") + token = os.getenv("DOCKERHUB_PUBLIC_TOKEN", "") + if not user or not token: + return "" + + res = requests.post( + "https://hub.docker.com/v2/auth/token", + json={"identifier": user, "secret": token}, + timeout=10.0, + ) + res.raise_for_status() + data: DockerCreateAccessTokenResponse = res.json() + return data["access_token"] + + +def fetch_tags(package: str, token_auth: str, page: int = 1) -> list[DockerTagDict]: """Fetch available docker tags.""" result = requests.get( f"https://registry.hub.docker.com/v2/namespaces/library/repositories/{package}/tags", params={"page": page, "page_size": 100}, + headers={"Authorization": f"Bearer {token_auth}"} if token_auth else None, timeout=10.0, ) result.raise_for_status() data: DockerTagResponse = result.json() if not data["next"]: return data["results"] - return data["results"] + fetch_tags(package, page=page + 1) + return data["results"] + fetch_tags(package, token_auth, page=page + 1) diff --git a/src/docker_python_nodejs/versions.py b/src/docker_python_nodejs/versions.py index d862fa4..b1eb820 100644 --- a/src/docker_python_nodejs/versions.py +++ b/src/docker_python_nodejs/versions.py @@ -12,7 +12,7 @@ from docker_python_nodejs.readme import format_supported_versions -from .docker_hub import DockerImageDict, DockerTagDict, fetch_tags +from .docker_hub import DockerImageDict, DockerTagDict, fetch_tags, token_auth from .nodejs_versions import ( fetch_node_releases, fetch_node_unofficial_releases, @@ -129,7 +129,8 @@ def decide_python_versions(distros: list[str], supported_versions: list[Supporte # FIXME: can we avoid enumerating all tags to speed up things? logger.debug("Fetching tags for python") - tags = [tag for tag in fetch_tags("python") if python_wanted_tag_pattern.match(tag["name"])] + docker_auth = token_auth() + tags = [tag for tag in fetch_tags("python", token_auth=docker_auth) if python_wanted_tag_pattern.match(tag["name"])] versions: list[PythonVersion] = [] for supported_version in supported_versions: