mirror of
https://github.com/usestrix/strix.git
synced 2026-09-30 01:52:18 +00:00
Merge 336d86af4b into ae38fe70cd
This commit is contained in:
commit
2fdc425051
4 changed files with 92 additions and 16 deletions
43
.github/workflows/typecheck.yml
vendored
Normal file
43
.github/workflows/typecheck.yml
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
name: Type check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
mypy:
|
||||
# `--platform` is what selects the branches mypy resolves, so all three run
|
||||
# on Linux. Checking out on three runner OSes would cost three times as much
|
||||
# and still check whatever platform each runner happened to be.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux
|
||||
- darwin
|
||||
- win32
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --frozen
|
||||
|
||||
- name: mypy --platform ${{ matrix.platform }}
|
||||
run: uv run mypy --platform ${{ matrix.platform }} strix/
|
||||
7
Makefile
7
Makefile
|
|
@ -46,8 +46,11 @@ lint:
|
|||
@echo "✅ Linting complete!"
|
||||
|
||||
type-check:
|
||||
@echo "🔍 Type checking with mypy..."
|
||||
uv run mypy strix/
|
||||
@echo "🔍 Type checking with mypy (linux, darwin, win32)..."
|
||||
@for platform in linux darwin win32; do \
|
||||
echo " → $$platform"; \
|
||||
uv run mypy --platform $$platform strix/ || exit 1; \
|
||||
done
|
||||
@echo "🔍 Type checking with pyright..."
|
||||
uv run pyright strix/
|
||||
@echo "✅ Type checking complete!"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import hashlib
|
|||
import json
|
||||
import logging
|
||||
import secrets
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import urllib.parse
|
||||
|
|
@ -103,27 +104,52 @@ def logout() -> None:
|
|||
AUTH_PATH.unlink()
|
||||
|
||||
|
||||
# `fcntl` exists only on POSIX. Resolve it once behind a platform guard, which
|
||||
# mypy narrows on every `--platform` it is asked to check, rather than at the
|
||||
# call sites where it would need a per-platform ignore.
|
||||
if sys.platform != "win32":
|
||||
import fcntl
|
||||
|
||||
_HAS_FILE_LOCKING = True
|
||||
|
||||
def _lock_exclusive(descriptor: int) -> None:
|
||||
fcntl.flock(descriptor, fcntl.LOCK_EX)
|
||||
|
||||
def _unlock(descriptor: int) -> None:
|
||||
fcntl.flock(descriptor, fcntl.LOCK_UN)
|
||||
|
||||
else:
|
||||
_HAS_FILE_LOCKING = False
|
||||
|
||||
def _lock_exclusive(descriptor: int) -> None:
|
||||
"""Windows has no advisory file lock; the in-process lock still holds."""
|
||||
|
||||
def _unlock(descriptor: int) -> None:
|
||||
"""Windows has no advisory file lock; the in-process lock still holds."""
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _refresh_guard() -> Iterator[None]:
|
||||
"""Serialize token refresh within (lock) and across (flock) Strix processes,
|
||||
so concurrent runs can't both spend the single-use refresh token."""
|
||||
with _refresh_lock:
|
||||
if not _HAS_FILE_LOCKING:
|
||||
yield
|
||||
return
|
||||
try:
|
||||
import fcntl
|
||||
|
||||
lock_path = AUTH_PATH.with_suffix(".lock")
|
||||
lock_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
handle = lock_path.open("w")
|
||||
except (ImportError, OSError):
|
||||
except OSError:
|
||||
yield
|
||||
return
|
||||
try:
|
||||
with contextlib.suppress(OSError):
|
||||
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
|
||||
_lock_exclusive(handle.fileno())
|
||||
yield
|
||||
finally:
|
||||
with contextlib.suppress(OSError):
|
||||
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
|
||||
_unlock(handle.fileno())
|
||||
handle.close()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -50,15 +50,19 @@ _EXTRA_FILE_MODE = 0o644
|
|||
|
||||
|
||||
def _host_identity_env() -> dict[str, str]:
|
||||
# Read the platform through a local so it is not narrowed to whichever OS is
|
||||
# type-checking: comparing sys.platform directly makes one of these branches
|
||||
# statically dead, and which one flips between Linux and macOS.
|
||||
platform_name: str = sys.platform
|
||||
if platform_name != "linux":
|
||||
return {}
|
||||
# Bind-mount ownership only needs mapping on Linux, where the container uid
|
||||
# must match the host's.
|
||||
return {"STRIX_HOST_UID": str(os.getuid()), "STRIX_HOST_GID": str(os.getgid())}
|
||||
# The outer guard is a real platform check, so mypy narrows it under every
|
||||
# --platform it is asked about and `os.getuid` resolves where it exists.
|
||||
if sys.platform != "win32":
|
||||
# Inside it, read the platform through a local so it is not narrowed to
|
||||
# whichever OS is type-checking: comparing sys.platform directly makes
|
||||
# one of these branches statically dead, and which one flips between
|
||||
# Linux and macOS.
|
||||
platform_name: str = sys.platform
|
||||
if platform_name == "linux":
|
||||
# Bind-mount ownership only needs mapping on Linux, where the
|
||||
# container uid must match the host's.
|
||||
return {"STRIX_HOST_UID": str(os.getuid()), "STRIX_HOST_GID": str(os.getgid())}
|
||||
return {}
|
||||
|
||||
|
||||
def build_bind_mounts(local_sources: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue