common-python-tasks 0.4.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
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.
- common_python_tasks/data/fastapi/compose-base.yml.j2 +2 -1
- common_python_tasks/data/fastapi/compose-db.yml.j2 +2 -1
- common_python_tasks/data/generic/Dockerfile.j2 +40 -7
- common_python_tasks/docker.py +30 -18
- common_python_tasks/docker_compose.py +7 -4
- common_python_tasks/env.py +25 -12
- common_python_tasks/git.py +3 -6
- common_python_tasks/github.py +37 -9
- common_python_tasks/project.py +345 -36
- common_python_tasks/tasks.py +45 -35
- common_python_tasks/utils.py +24 -0
- {common_python_tasks-0.4.0.dist-info → common_python_tasks-0.5.0.dist-info}/METADATA +68 -49
- {common_python_tasks-0.4.0.dist-info → common_python_tasks-0.5.0.dist-info}/RECORD +15 -15
- {common_python_tasks-0.4.0.dist-info → common_python_tasks-0.5.0.dist-info}/WHEEL +1 -1
- {common_python_tasks-0.4.0.dist-info → common_python_tasks-0.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,7 +9,8 @@ x-common-environment: &common-environment
|
|
|
9
9
|
x-common-build-args: &common-build-args
|
|
10
10
|
PACKAGE_NAME: ${PACKAGE_NAME}
|
|
11
11
|
PYTHON_VERSION: ${PYTHON_VERSION}
|
|
12
|
-
|
|
12
|
+
PACKAGE_MANAGER: ${PACKAGE_MANAGER}
|
|
13
|
+
PACKAGE_MANAGER_VERSION: ${PACKAGE_MANAGER_VERSION}
|
|
13
14
|
|
|
14
15
|
services:
|
|
15
16
|
api:
|
|
@@ -8,7 +8,8 @@ x-common-environment: &common-environment
|
|
|
8
8
|
x-common-build-args: &common-build-args
|
|
9
9
|
PACKAGE_NAME: ${PACKAGE_NAME}
|
|
10
10
|
PYTHON_VERSION: ${PYTHON_VERSION}
|
|
11
|
-
|
|
11
|
+
PACKAGE_MANAGER: ${PACKAGE_MANAGER}
|
|
12
|
+
PACKAGE_MANAGER_VERSION: ${PACKAGE_MANAGER_VERSION}
|
|
12
13
|
WORKDIR_PATH: ${WORKDIR_PATH}
|
|
13
14
|
|
|
14
15
|
services:
|
|
@@ -7,24 +7,48 @@ ARG PYTHON_VERSION=3
|
|
|
7
7
|
# See https://hub.docker.com/_/python for available variants
|
|
8
8
|
ARG PYTHON_VARIANT=slim
|
|
9
9
|
ARG WORKDIR_PATH=/workspace
|
|
10
|
+
{% set package_manager = PACKAGE_MANAGER|default('poetry') %}
|
|
10
11
|
|
|
11
12
|
FROM python:${PYTHON_VERSION} AS builder
|
|
12
13
|
|
|
14
|
+
ARG PACKAGE_VERSION
|
|
15
|
+
|
|
16
|
+
{% if package_manager == 'poetry' %}
|
|
13
17
|
ARG POETRY_VERSION
|
|
14
18
|
ARG POETRY_DYNAMIC_VERSIONING_PLUGIN_VERSION
|
|
15
19
|
ARG POETRY_PLUGIN_EXPORT_VERSION
|
|
16
|
-
ARG PACKAGE_VERSION
|
|
17
20
|
|
|
18
21
|
# Bypass VCS-based version detection: use the host-computed version directly.
|
|
19
22
|
# This avoids requiring a .git directory in the build context and prevents
|
|
20
23
|
# false dirty-version detection from a partial working tree.
|
|
21
24
|
ENV POETRY_DYNAMIC_VERSIONING_BYPASS=${PACKAGE_VERSION}
|
|
22
25
|
|
|
23
|
-
# Install Poetry and required Poetry plugins for build
|
|
24
|
-
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }}
|
|
25
|
-
|
|
26
|
-
"poetry
|
|
27
|
-
"poetry
|
|
26
|
+
# Install Poetry and required Poetry plugins for package build
|
|
27
|
+
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} \
|
|
28
|
+
sh -c 'set -eu; \
|
|
29
|
+
poetry_spec="poetry"; \
|
|
30
|
+
if [ -n "${POETRY_VERSION:-}" ]; then poetry_spec="poetry==$POETRY_VERSION"; fi; \
|
|
31
|
+
dynamic_spec="poetry-dynamic-versioning[plugin]"; \
|
|
32
|
+
if [ -n "${POETRY_DYNAMIC_VERSIONING_PLUGIN_VERSION:-}" ]; then dynamic_spec="poetry-dynamic-versioning[plugin]==$POETRY_DYNAMIC_VERSIONING_PLUGIN_VERSION"; fi; \
|
|
33
|
+
export_spec="poetry-plugin-export"; \
|
|
34
|
+
if [ -n "${POETRY_PLUGIN_EXPORT_VERSION:-}" ]; then export_spec="poetry-plugin-export==$POETRY_PLUGIN_EXPORT_VERSION"; fi; \
|
|
35
|
+
pip install --root-user-action=ignore "$poetry_spec" "$dynamic_spec" "$export_spec"'
|
|
36
|
+
{% else %}
|
|
37
|
+
ARG UV_VERSION
|
|
38
|
+
|
|
39
|
+
# Bypass VCS-based version detection: use the host-computed version directly.
|
|
40
|
+
# This avoids requiring a .git directory in the build context and prevents
|
|
41
|
+
# false dirty-version detection from a partial working tree.
|
|
42
|
+
ENV UV_DYNAMIC_VERSIONING_BYPASS=${PACKAGE_VERSION}
|
|
43
|
+
|
|
44
|
+
# Install uv for package build and dependency export steps
|
|
45
|
+
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} \
|
|
46
|
+
sh -c 'if [ -n "${UV_VERSION:-}" ]; then \
|
|
47
|
+
pip install --root-user-action=ignore "uv==$UV_VERSION"; \
|
|
48
|
+
else \
|
|
49
|
+
pip install --root-user-action=ignore uv; \
|
|
50
|
+
fi'
|
|
51
|
+
{% endif %}
|
|
28
52
|
|
|
29
53
|
# Build package
|
|
30
54
|
WORKDIR /tmp/build
|
|
@@ -32,15 +56,24 @@ COPY . /tmp/build/
|
|
|
32
56
|
|
|
33
57
|
{% if HAS_DEBUG_DEPS %}
|
|
34
58
|
# Export debug requirements when the project defines a debug dependency group
|
|
59
|
+
{% if package_manager == 'poetry' %}
|
|
35
60
|
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} \
|
|
36
61
|
poetry export --only debug --without-hashes -f requirements.txt --output requirements-debug.txt
|
|
62
|
+
{% else %}
|
|
63
|
+
RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }} \
|
|
64
|
+
uv export --group debug --no-hashes --format requirements-txt --output-file requirements-debug.txt
|
|
65
|
+
{% endif %}
|
|
37
66
|
{% endif %}
|
|
38
67
|
|
|
39
|
-
# Build the wheel, caching
|
|
68
|
+
# Build the wheel, caching the package-manager cache directory to speed up subsequent builds.
|
|
40
69
|
# If running this in a CI system, you must be using a persistent builder to take
|
|
41
70
|
# advantage of the cache mount, and you should configure your CI to persist the
|
|
42
71
|
# cache directory between builds
|
|
72
|
+
{% if package_manager == 'poetry' %}
|
|
43
73
|
RUN --mount=type=cache,target=/root/.cache/pypoetry,id=pypoetry-cache{{ CACHE_ID_SUFFIX }} poetry build --format=wheel
|
|
74
|
+
{% else %}
|
|
75
|
+
RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }} uv build --wheel
|
|
76
|
+
{% endif %}
|
|
44
77
|
|
|
45
78
|
FROM python:${PYTHON_VERSION}-${PYTHON_VARIANT} AS runtime
|
|
46
79
|
ARG WORKDIR_PATH=/workspace
|
common_python_tasks/docker.py
CHANGED
|
@@ -93,7 +93,6 @@ def _safe_unlink(path: Path | str | None) -> None:
|
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
def _build_docker_build_command(
|
|
96
|
-
*,
|
|
97
96
|
context_path: Path,
|
|
98
97
|
dockerfile_path: Path,
|
|
99
98
|
build_args: dict[str, str],
|
|
@@ -331,12 +330,12 @@ def build_deps_image(
|
|
|
331
330
|
|
|
332
331
|
full_tag = f"{deps_image_name}:{deps_tag}"
|
|
333
332
|
build_cmd = _build_docker_build_command(
|
|
334
|
-
context_path
|
|
335
|
-
dockerfile_path
|
|
336
|
-
build_args
|
|
337
|
-
archs
|
|
338
|
-
no_cache
|
|
339
|
-
plain
|
|
333
|
+
context_path,
|
|
334
|
+
dockerfile_path,
|
|
335
|
+
build_args,
|
|
336
|
+
archs,
|
|
337
|
+
no_cache,
|
|
338
|
+
plain,
|
|
340
339
|
tags=[full_tag],
|
|
341
340
|
)
|
|
342
341
|
|
|
@@ -451,24 +450,37 @@ def build_image(
|
|
|
451
450
|
commit_tag = version_tag
|
|
452
451
|
|
|
453
452
|
python_version = platform.python_version()
|
|
454
|
-
|
|
453
|
+
package_manager = project.get_package_manager()
|
|
454
|
+
package_manager_version = project.get_package_manager_version(package_manager)
|
|
455
455
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
"POETRY_VERSION":
|
|
456
|
+
manager_build_args: dict[str, str | None]
|
|
457
|
+
if package_manager == project.PackageManager.POETRY:
|
|
458
|
+
manager_build_args = {
|
|
459
|
+
"POETRY_VERSION": package_manager_version,
|
|
460
460
|
"POETRY_DYNAMIC_VERSIONING_PLUGIN_VERSION": project.get_installed_requirement_version(
|
|
461
461
|
"poetry-dynamic-versioning[plugin]"
|
|
462
462
|
),
|
|
463
463
|
"POETRY_PLUGIN_EXPORT_VERSION": project.get_installed_requirement_version(
|
|
464
464
|
"poetry-plugin-export"
|
|
465
465
|
),
|
|
466
|
+
}
|
|
467
|
+
else:
|
|
468
|
+
manager_build_args = {
|
|
469
|
+
"UV_VERSION": package_manager_version,
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
build_args = _merge_build_args(
|
|
473
|
+
{
|
|
474
|
+
"PYTHON_VERSION": python_version,
|
|
475
|
+
"PACKAGE_MANAGER": package_manager,
|
|
476
|
+
"PACKAGE_MANAGER_VERSION": package_manager_version,
|
|
466
477
|
"PACKAGE_VERSION": package_version,
|
|
467
478
|
"PACKAGE_NAME": utils.get_package_name(use_underscores=True),
|
|
468
479
|
"AUTHORS": ",".join(
|
|
469
480
|
[f"{name} <{email}>" for name, email in project.get_authors()]
|
|
470
481
|
),
|
|
471
482
|
"GIT_COMMIT": commit_tag,
|
|
483
|
+
**manager_build_args,
|
|
472
484
|
},
|
|
473
485
|
extra_build_args,
|
|
474
486
|
)
|
|
@@ -503,12 +515,12 @@ def build_image(
|
|
|
503
515
|
all_tags.append(resolved_tag)
|
|
504
516
|
|
|
505
517
|
build_cmd = _build_docker_build_command(
|
|
506
|
-
context_path
|
|
507
|
-
dockerfile_path
|
|
508
|
-
build_args
|
|
509
|
-
archs
|
|
510
|
-
no_cache
|
|
511
|
-
plain
|
|
518
|
+
context_path,
|
|
519
|
+
dockerfile_path,
|
|
520
|
+
build_args,
|
|
521
|
+
archs,
|
|
522
|
+
no_cache,
|
|
523
|
+
plain,
|
|
512
524
|
target=None if omit_target else target,
|
|
513
525
|
tags=all_tags,
|
|
514
526
|
)
|
|
@@ -14,7 +14,7 @@ from poethepoet_tasks import TaskCollection
|
|
|
14
14
|
|
|
15
15
|
from . import utils
|
|
16
16
|
from .env import get_workdir_path
|
|
17
|
-
from .project import
|
|
17
|
+
from .project import get_package_manager, get_package_manager_version
|
|
18
18
|
|
|
19
19
|
LOGGER = logging.getLogger(__name__)
|
|
20
20
|
|
|
@@ -232,7 +232,8 @@ _COMPOSE_VAR_REQUIREMENTS: dict[str, dict[str, set[str]]] = {
|
|
|
232
232
|
"ENVIRONMENT",
|
|
233
233
|
"IMAGE_TAG",
|
|
234
234
|
"PYTHON_VERSION",
|
|
235
|
-
"
|
|
235
|
+
"PACKAGE_MANAGER",
|
|
236
|
+
"PACKAGE_MANAGER_VERSION",
|
|
236
237
|
},
|
|
237
238
|
"compose-db": {
|
|
238
239
|
"PACKAGE_NAME",
|
|
@@ -242,7 +243,8 @@ _COMPOSE_VAR_REQUIREMENTS: dict[str, dict[str, set[str]]] = {
|
|
|
242
243
|
"DB_PORT",
|
|
243
244
|
"IMAGE_TAG",
|
|
244
245
|
"PYTHON_VERSION",
|
|
245
|
-
"
|
|
246
|
+
"PACKAGE_MANAGER",
|
|
247
|
+
"PACKAGE_MANAGER_VERSION",
|
|
246
248
|
"POSTGRES_VERSION",
|
|
247
249
|
"WORKDIR_PATH",
|
|
248
250
|
},
|
|
@@ -328,9 +330,10 @@ def get_compose_env(
|
|
|
328
330
|
"ENVIRONMENT": os.getenv("ENVIRONMENT", "production"),
|
|
329
331
|
"IMAGE_TAG": image_tag or "latest",
|
|
330
332
|
"PACKAGE_NAME": package_name,
|
|
333
|
+
"PACKAGE_MANAGER": get_package_manager(),
|
|
334
|
+
"PACKAGE_MANAGER_VERSION": get_package_manager_version(),
|
|
331
335
|
"PACKAGE_UNDERSCORE_NAME": utils.package_name_to_underscore(package_name),
|
|
332
336
|
"WORKDIR_PATH": get_workdir_path(),
|
|
333
|
-
"POETRY_VERSION": get_poetry_version(),
|
|
334
337
|
"POSTGRES_VERSION": os.getenv("POSTGRES_VERSION", "17"),
|
|
335
338
|
"PYTHON_VERSION": platform.python_version(),
|
|
336
339
|
"SECRET_KEY": os.getenv("SECRET_KEY", ""),
|
common_python_tasks/env.py
CHANGED
|
@@ -186,11 +186,12 @@ def split_delimited_values(
|
|
|
186
186
|
separators: str = ":",
|
|
187
187
|
allow_whitespace: bool = False,
|
|
188
188
|
) -> list[str]:
|
|
189
|
-
"""Split a delimited string while preserving quoted
|
|
189
|
+
"""Split a delimited string while preserving quoted or escaped separators.
|
|
190
190
|
|
|
191
191
|
Args:
|
|
192
192
|
value: The string to split.
|
|
193
|
-
separators: Explicit separator characters to use.
|
|
193
|
+
separators: Explicit separator characters to use. Prefix a separator with
|
|
194
|
+
a backslash to include it literally in a token.
|
|
194
195
|
allow_whitespace: Treat whitespace as additional separators.
|
|
195
196
|
|
|
196
197
|
Returns:
|
|
@@ -207,7 +208,7 @@ def split_delimited_values(
|
|
|
207
208
|
|
|
208
209
|
|
|
209
210
|
def split_colon_delimited_values(value: str) -> list[str]:
|
|
210
|
-
"""Split
|
|
211
|
+
"""Split colon-delimited values while preserving quoted or escaped colons."""
|
|
211
212
|
return split_delimited_values(value, separators=":", allow_whitespace=True)
|
|
212
213
|
|
|
213
214
|
|
|
@@ -221,7 +222,7 @@ def parse_container_build_args(
|
|
|
221
222
|
env_build_args: str | None,
|
|
222
223
|
) -> dict[str, str] | None:
|
|
223
224
|
"""Parse container build args from CLI or environment input."""
|
|
224
|
-
if cli_build_args
|
|
225
|
+
if cli_build_args and any(token.strip() for token in cli_build_args):
|
|
225
226
|
build_arg_tokens = [
|
|
226
227
|
token.strip() for token in cli_build_args if token and token.strip()
|
|
227
228
|
]
|
|
@@ -233,10 +234,15 @@ def parse_container_build_args(
|
|
|
233
234
|
parsed_build_args: dict[str, str] = {}
|
|
234
235
|
for token in build_arg_tokens:
|
|
235
236
|
if "=" not in token:
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
utils.fatal(
|
|
238
|
+
f"Invalid build-arg token {token!r}; expected KEY=VALUE. "
|
|
239
|
+
"Escape literal colons in values as \\: or quote the value."
|
|
240
|
+
)
|
|
238
241
|
key, value = token.split("=", 1)
|
|
239
|
-
|
|
242
|
+
key = key.strip()
|
|
243
|
+
if not key:
|
|
244
|
+
utils.fatal(f"Invalid build-arg token {token!r}; the key must not be empty")
|
|
245
|
+
parsed_build_args[key] = value.strip()
|
|
240
246
|
|
|
241
247
|
return parsed_build_args or None
|
|
242
248
|
|
|
@@ -254,13 +260,20 @@ def parse_container_env_tokens(value: str | list[str] | None) -> list[str]:
|
|
|
254
260
|
return []
|
|
255
261
|
|
|
256
262
|
if isinstance(value, list):
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if not value.strip():
|
|
263
|
+
tokens = [token.strip() for token in value if token and token.strip()]
|
|
264
|
+
elif not value.strip():
|
|
260
265
|
return []
|
|
266
|
+
else:
|
|
267
|
+
tokens = split_colon_delimited_values(value)
|
|
261
268
|
|
|
262
|
-
|
|
263
|
-
|
|
269
|
+
for token in tokens:
|
|
270
|
+
if "=" not in token or not token.split("=", 1)[0].strip():
|
|
271
|
+
utils.fatal(
|
|
272
|
+
f"Invalid container env token {token!r}; expected KEY=VALUE. "
|
|
273
|
+
"Escape literal colons in values as \\: or quote the value."
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
return tokens
|
|
264
277
|
|
|
265
278
|
|
|
266
279
|
def _parse_container_envfile_paths(
|
common_python_tasks/git.py
CHANGED
|
@@ -96,7 +96,7 @@ def compute_next_release_version(
|
|
|
96
96
|
Returns:
|
|
97
97
|
A `NextReleaseVersion` with the serialized new version and normalized component.
|
|
98
98
|
"""
|
|
99
|
-
current_version_text = project.
|
|
99
|
+
current_version_text = project.get_project_version()
|
|
100
100
|
release_component = (
|
|
101
101
|
infer_bump_component_from_git_cliff() if component is None else component
|
|
102
102
|
)
|
|
@@ -181,14 +181,11 @@ def infer_bump_component_from_git_cliff() -> ReleaseComponent:
|
|
|
181
181
|
except Exception:
|
|
182
182
|
utils.fatal(f"Unable to parse bumped version from git-cliff: {version_text!r}")
|
|
183
183
|
|
|
184
|
-
current_version = project.
|
|
184
|
+
current_version = project.get_project_version()
|
|
185
185
|
try:
|
|
186
186
|
current = Version.parse(current_version)
|
|
187
187
|
except Exception:
|
|
188
|
-
utils.fatal(
|
|
189
|
-
"Unable to parse current version from Poetry output: "
|
|
190
|
-
f"{current_version!r}"
|
|
191
|
-
)
|
|
188
|
+
utils.fatal("Unable to parse current project version: " f"{current_version!r}")
|
|
192
189
|
|
|
193
190
|
def _parse_semver(version_value: str | Any) -> tuple[int, int, int]:
|
|
194
191
|
if not isinstance(version_value, str):
|
common_python_tasks/github.py
CHANGED
|
@@ -3,6 +3,8 @@ import logging
|
|
|
3
3
|
import mimetypes
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from functools import lru_cache
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
from typing import Any, Literal
|
|
8
10
|
|
|
@@ -39,7 +41,7 @@ def should_publish_github_release() -> bool:
|
|
|
39
41
|
"""Return whether GitHub Releases publication is enabled.
|
|
40
42
|
|
|
41
43
|
Returns:
|
|
42
|
-
True if GitHub Release publishing is enabled, False if it is skipped.
|
|
44
|
+
`True` if GitHub Release publishing is enabled, `False` if it is skipped.
|
|
43
45
|
"""
|
|
44
46
|
return not env_truthy("SKIP_GITHUB_RELEASE")
|
|
45
47
|
|
|
@@ -51,13 +53,25 @@ def get_github_repository() -> str | None:
|
|
|
51
53
|
The GitHub repository slug in the form `owner/repo`, or `None` if it
|
|
52
54
|
cannot be determined.
|
|
53
55
|
"""
|
|
54
|
-
|
|
56
|
+
return _get_github_repository(
|
|
57
|
+
os.getenv("GITHUB_REPOSITORY", "").strip(),
|
|
58
|
+
Path.cwd(),
|
|
59
|
+
utils.run_command,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@lru_cache(maxsize=32)
|
|
64
|
+
def _get_github_repository(
|
|
65
|
+
repository: str,
|
|
66
|
+
_working_directory: Path,
|
|
67
|
+
run_command: Callable[..., Any],
|
|
68
|
+
) -> str | None:
|
|
55
69
|
if repository:
|
|
56
70
|
if re.fullmatch(r"[^/]+/[^/]+", repository):
|
|
57
71
|
return repository
|
|
58
72
|
LOGGER.warning("Ignoring invalid GITHUB_REPOSITORY value: %s", repository)
|
|
59
73
|
|
|
60
|
-
result =
|
|
74
|
+
result = run_command(
|
|
61
75
|
["git", "remote", "get-url", "origin"],
|
|
62
76
|
capture_output=True,
|
|
63
77
|
acceptable_returncodes={0, 128},
|
|
@@ -94,8 +108,13 @@ def get_github_token() -> str | None:
|
|
|
94
108
|
if token:
|
|
95
109
|
return token
|
|
96
110
|
|
|
111
|
+
return _get_github_cli_token(utils.run_command)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@lru_cache(maxsize=8)
|
|
115
|
+
def _get_github_cli_token(run_command: Callable[..., Any]) -> str | None:
|
|
97
116
|
try:
|
|
98
|
-
result =
|
|
117
|
+
result = run_command(
|
|
99
118
|
["gh", "auth", "token"],
|
|
100
119
|
capture_output=True,
|
|
101
120
|
acceptable_returncodes={0, 1},
|
|
@@ -132,13 +151,18 @@ def get_github_api_base_url() -> str:
|
|
|
132
151
|
The base URL for GitHub API requests, defaulting to GitHub.com API or
|
|
133
152
|
GitHub Enterprise API if configured.
|
|
134
153
|
"""
|
|
135
|
-
|
|
154
|
+
return _get_github_api_base_url(
|
|
155
|
+
os.getenv("GITHUB_API_URL", "").strip(),
|
|
156
|
+
os.getenv("GITHUB_SERVER_URL", "https://github.com").strip(),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@lru_cache(maxsize=32)
|
|
161
|
+
def _get_github_api_base_url(api_url: str, server_url: str) -> str:
|
|
136
162
|
if api_url:
|
|
137
163
|
return api_url.rstrip("/")
|
|
138
164
|
|
|
139
|
-
server_url = (
|
|
140
|
-
os.getenv("GITHUB_SERVER_URL", "https://github.com").strip().rstrip("/")
|
|
141
|
-
)
|
|
165
|
+
server_url = server_url.rstrip("/")
|
|
142
166
|
if server_url.endswith("github.com"):
|
|
143
167
|
return "https://api.github.com"
|
|
144
168
|
return f"{server_url}/api/v3"
|
|
@@ -156,7 +180,11 @@ def get_github_upload_api_base_url(api_base_url: str | None = None) -> str:
|
|
|
156
180
|
Returns:
|
|
157
181
|
The upload base URL for GitHub API requests.
|
|
158
182
|
"""
|
|
159
|
-
|
|
183
|
+
return _get_github_upload_api_base_url(api_base_url or get_github_api_base_url())
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@lru_cache(maxsize=32)
|
|
187
|
+
def _get_github_upload_api_base_url(api_base: str) -> str:
|
|
160
188
|
if api_base == "https://api.github.com":
|
|
161
189
|
return "https://uploads.github.com"
|
|
162
190
|
return api_base
|