Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ updates:
directory: "/"
schedule:
interval: weekly
# Wait before proposing a freshly published release, mirroring the safe-chain
# minimum-package-age posture: a compromised version is usually yanked within
# days, so a cooldown keeps it out of an auto-opened PR in the first place.
cooldown:
default-days: 7
groups:
python-deps:
patterns: ["*"]
Expand All @@ -14,6 +19,8 @@ updates:
directory: "/"
schedule:
interval: weekly
cooldown:
default-days: 7
groups:
actions:
patterns: ["*"]
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false # no job pushes; don't leave the token in .git/config
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
Expand Down Expand Up @@ -47,6 +49,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false # no job pushes; don't leave the token in .git/config
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
Expand All @@ -70,6 +74,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false # no job pushes; don't leave the token in .git/config
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
Expand All @@ -88,6 +94,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false # no job pushes; don't leave the token in .git/config
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
Expand Down Expand Up @@ -116,6 +124,8 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false # no job pushes; don't leave the token in .git/config
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ build/
.coverage
coverage.xml
htmlcov/
mutants/

# Editor/agent local artifacts: keep personal settings local, but track the
# team-shared bits (.claude/settings.json, agents/, skills/).
Expand Down
18 changes: 18 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ repos:
args: [--fix]
- id: ruff-format

# Lint the GitHub Actions workflows themselves (the jobs in ci.yml). Both hooks
# self-scope to .github/workflows/. actionlint-py is the pip-packaged wrapper
# (bundles the actionlint binary; no Go/Docker), and brings shellcheck-py so
# embedded `run:` shell is shellcheck'd the same way check.sh checks install.sh.
- repo: https://github.com/Mateusz-Grzelinski/actionlint-py
rev: v1.7.12.24
hooks:
- id: actionlint
additional_dependencies: [shellcheck-py>=0.9.0.5]

# Security audit for the workflows: catches the Actions-specific smells (unpinned
# actions, over-broad permissions/tokens, injection-prone expressions) that
# complement the SHA-pinning + least-privilege posture already in ci.yml.
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.25.2
hooks:
- id: zizmor

- repo: local
hooks:
- id: pytest
Expand Down
28 changes: 13 additions & 15 deletions aai_cli/auth/ams.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
from __future__ import annotations

import httpx2 as httpx
from pydantic import TypeAdapter, ValidationError

from aai_cli import jsonshape
from aai_cli.auth import endpoints
from aai_cli.errors import APIError, NotAuthenticated

_TIMEOUT = 30.0
_HTTP_ERROR_MIN_STATUS = 400
_JSON_OBJECT: TypeAdapter[dict[str, object]] = TypeAdapter(dict[str, object])
_JSON_OBJECTS: TypeAdapter[list[dict[str, object]]] = TypeAdapter(list[dict[str, object]])


def _detail(resp: httpx.Response) -> str:
fallback = resp.text or f"HTTP {resp.status_code}"
try:
body: object = resp.json()
mapping = _JSON_OBJECT.validate_python(body)
if "detail" in mapping:
return str(mapping["detail"])
except (TypeError, ValueError, ValidationError):
except ValueError:
return fallback
mapping = jsonshape.as_mapping(body)
if mapping is not None and "detail" in mapping:
return str(mapping["detail"])
return fallback


Expand All @@ -39,22 +37,22 @@ def _json_or_raise(resp: httpx.Response) -> object:

def _json_object_or_raise(resp: httpx.Response) -> dict[str, object]:
data = _json_or_raise(resp)
try:
return _JSON_OBJECT.validate_python(data)
except ValidationError as exc:
mapping = jsonshape.as_mapping(data)
if mapping is None:
raise APIError(
f"AMS request returned unexpected JSON: expected object, got {type(data).__name__}."
) from exc
)
return mapping


def _json_object_list_or_raise(resp: httpx.Response) -> list[dict[str, object]]:
data = _json_or_raise(resp)
try:
return _JSON_OBJECTS.validate_python(data)
except ValidationError as exc:
objects = jsonshape.as_object_list(data)
if objects is None:
raise APIError(
f"AMS request returned unexpected JSON: expected list of objects, got {type(data).__name__}."
) from exc
)
return objects


def _client(session_jwt: str | None = None) -> httpx.Client:
Expand Down
Loading
Loading