Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
706d6dafe0 | ||
|
|
03ddce427c | ||
|
|
9642d6884c | ||
|
|
b2074d6635 | ||
|
|
a6dddf25e7 | ||
|
|
01130a7385 |
@@ -32,6 +32,7 @@ concurrency:
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: docker
|
||||
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-full:latest
|
||||
timeout-minutes: 30
|
||||
outputs:
|
||||
is-release: ${{ steps.check.outputs.is-release }}
|
||||
@@ -115,6 +116,7 @@ jobs:
|
||||
needs: [build-and-push]
|
||||
if: always() && needs.build-and-push.result == 'success'
|
||||
runs-on: docker
|
||||
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
- name: Check unit test speed
|
||||
run: |
|
||||
. .venv/bin/activate 2>/dev/null || true
|
||||
python3 -m devx.tools.check_test_speed --max-seconds 8 --max-single-seconds 0.5
|
||||
python3 -m devx.tools.check_test_speed --max-seconds 15 --max-single-seconds 0.5
|
||||
- name: Documentation gate (coverage + stale refs + lint + version refs + prose)
|
||||
env:
|
||||
DEVX_DOC_COVERAGE_STRICT: "1"
|
||||
|
||||
@@ -59,7 +59,7 @@ repos:
|
||||
|
||||
- id: check-test-speed
|
||||
name: unit test speed check
|
||||
entry: .venv/bin/python -m devx.tools.check_test_speed --max-seconds 6 --max-single-seconds 0.5
|
||||
entry: .venv/bin/python -m devx.tools.check_test_speed --max-seconds 15 --max-single-seconds 0.5
|
||||
language: system
|
||||
types: [python]
|
||||
pass_filenames: false
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.49.2] - 2026-08-07
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Add fallback URL for tea download
|
||||
|
||||
## [0.49.1] - 2026-08-07
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Add container images to build-images workflow
|
||||
|
||||
## [0.49.0] - 2026-08-07
|
||||
|
||||
### Features
|
||||
|
||||
@@ -16,12 +16,12 @@ quality badges.
|
||||
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
|
||||
## Why devx?
|
||||
|
||||
@@ -87,7 +87,7 @@ extra index and list devx in your dependencies:
|
||||
```toml
|
||||
[project]
|
||||
dependencies = [
|
||||
"devx>=0.49.0",
|
||||
"devx>=0.49.2",
|
||||
]
|
||||
|
||||
[tool.pip]
|
||||
@@ -101,8 +101,8 @@ pip install -e .
|
||||
```
|
||||
|
||||
> **Note:** If your project requires a specific devx version, pin it in
|
||||
> `dependencies` (for example, `"devx==0.49.0"`) or use a version constraint
|
||||
> (for example, `"devx>=0.49.0,<0.50"`).
|
||||
> `dependencies` (for example, `"devx==0.49.2"`) or use a version constraint
|
||||
> (for example, `"devx>=0.49.2,<0.50"`).
|
||||
|
||||
### Optional extras
|
||||
|
||||
|
||||
+8
-8
@@ -12,12 +12,12 @@ project to be reusable across all oblachno-oss repositories.
|
||||
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -74,14 +74,14 @@ Add devx to your `pyproject.toml` dependencies and configure the registry:
|
||||
```toml
|
||||
[project]
|
||||
dependencies = [
|
||||
"devx>=0.49.0",
|
||||
"devx>=0.49.2",
|
||||
]
|
||||
|
||||
[tool.pip]
|
||||
extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple"
|
||||
```
|
||||
|
||||
Pin a specific version if needed: `"devx==0.49.0"` or `"devx>=0.49.0,<0.50"`.
|
||||
Pin a specific version if needed: `"devx==0.49.2"` or `"devx>=0.49.2,<0.50"`.
|
||||
|
||||
### Optional extras
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ Add devx to your `pyproject.toml`:
|
||||
```toml
|
||||
[project]
|
||||
dependencies = [
|
||||
"devx>=0.49.0",
|
||||
"devx>=0.49.2",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"devx>=0.49.0",
|
||||
"devx>=0.49.2",
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
|
||||
|
||||
__version__ = "0.49.0"
|
||||
__version__ = "0.49.2"
|
||||
|
||||
@@ -70,6 +70,25 @@ def _download(url: str, dest: Path) -> None:
|
||||
shutil.copyfileobj(resp, f)
|
||||
|
||||
|
||||
def _download_with_fallback(urls: list[str], binary_name: str) -> Path:
|
||||
"""Try downloading a binary from a list of URLs, falling back on failure.
|
||||
|
||||
Returns the path to the installed binary. Raises if all URLs fail.
|
||||
"""
|
||||
target_dir = _ensure_target_dir()
|
||||
dest = target_dir / binary_name
|
||||
errors: list[str] = []
|
||||
for url in urls:
|
||||
try:
|
||||
_download(url, dest)
|
||||
dest.chmod(0o755)
|
||||
return dest
|
||||
except Exception as exc: # noqa: BLE001
|
||||
errors.append(f"{url}: {exc}")
|
||||
click.echo(f" {binary_name}: retrying — {exc}")
|
||||
raise click.ClickException(f"Failed to download {binary_name} from all URLs: {'; '.join(errors)}")
|
||||
|
||||
|
||||
def _download_and_extract_tarball(url: str, binary_name: str) -> Path:
|
||||
"""Download a tarball, extract the binary, and install it to TARGET_DIR.
|
||||
|
||||
@@ -164,8 +183,13 @@ def install_tea() -> bool:
|
||||
click.echo("tea: already installed")
|
||||
return True
|
||||
arch = _arch()
|
||||
url = f"https://dl.gitea.com/tea/{TEA_VERSION}/tea-{TEA_VERSION}-linux-{arch}"
|
||||
dest = _download_binary(url, "tea")
|
||||
# dl.gitea.com is the primary CDN, but it can return 403 from some networks.
|
||||
# Fall back to the gitea.com release downloads URL.
|
||||
urls = [
|
||||
f"https://dl.gitea.com/tea/{TEA_VERSION}/tea-{TEA_VERSION}-linux-{arch}",
|
||||
f"https://gitea.com/gitea/tea/releases/download/v{TEA_VERSION}/tea-{TEA_VERSION}-linux-{arch}",
|
||||
]
|
||||
dest = _download_with_fallback(urls, "tea")
|
||||
click.echo(f"tea: installed to {dest}")
|
||||
return True
|
||||
|
||||
|
||||
@@ -233,6 +233,33 @@ class TestInstallTea:
|
||||
assert install_tools.install_tea() is True
|
||||
assert (tmp_path / "tea").exists()
|
||||
|
||||
def test_install_fallback_to_second_url(self, tmp_path: Path) -> None:
|
||||
"""First URL fails (403), second URL succeeds."""
|
||||
call_count = [0]
|
||||
|
||||
def _download_side_effect(url: str, dest: Path) -> None:
|
||||
call_count[0] += 1
|
||||
if call_count[0] == 1:
|
||||
raise OSError("HTTP Error 403: Forbidden")
|
||||
Path(dest).write_bytes(b"binary")
|
||||
|
||||
with patch.object(install_tools, "_is_installed", return_value=False):
|
||||
with patch.object(install_tools, "TARGET_DIR", tmp_path):
|
||||
with patch.object(platform, "machine", return_value="x86_64"):
|
||||
with patch.object(install_tools, "_download", side_effect=_download_side_effect):
|
||||
assert install_tools.install_tea() is True
|
||||
assert (tmp_path / "tea").exists()
|
||||
assert call_count[0] == 2
|
||||
|
||||
def test_install_all_urls_fail(self, tmp_path: Path) -> None:
|
||||
"""All URLs fail — should raise ClickException."""
|
||||
with patch.object(install_tools, "_is_installed", return_value=False):
|
||||
with patch.object(install_tools, "TARGET_DIR", tmp_path):
|
||||
with patch.object(platform, "machine", return_value="x86_64"):
|
||||
with patch.object(install_tools, "_download", side_effect=OSError("403 Forbidden")):
|
||||
with pytest.raises(ClickException, match="Failed to download tea"):
|
||||
install_tools.install_tea()
|
||||
|
||||
|
||||
class TestInstallHadolint:
|
||||
def test_already_installed(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user