41749fd7b4
get_issue raised 'NoneType' object is not iterable on issues whose labels/assignees Gitea returns as null or with non-dict elements (the #13 class), which reached clients as an opaque JSON-RPC -32603 with no detail. - read_tools: skip non-dict label/assignee entries in get_issue_tool - server: detect a wrapped GiteaNotFoundError via the __cause__ chain and return 404 / JSON-RPC -32000 with a clear message; include the exception type name in masked internal errors so future masked failures are diagnosable without exposing messages or stack traces - tests: cover non-dict collection elements and the not-found / typed-error responses - ci: rewrite docker.yml to build, smoke-test and push the image to the Gitea container registry on merge to main/dev, matching the hiddenden.cafe pattern (only REGISTRY_TOKEN required) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
126 lines
4.4 KiB
YAML
126 lines
4.4 KiB
YAML
name: docker
|
|
|
|
on:
|
|
# Test on every branch push; registry push is gated per-step to main/dev.
|
|
push:
|
|
branches:
|
|
- '**'
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
- dev
|
|
|
|
jobs:
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Lint: ruff + black + mypy.
|
|
# ---------------------------------------------------------------------------
|
|
lint:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.12"
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements-dev.txt
|
|
- name: Run lint
|
|
run: |
|
|
ruff check src tests
|
|
ruff format --check src tests
|
|
black --check src tests
|
|
mypy src
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Test: pytest with coverage gate.
|
|
# ---------------------------------------------------------------------------
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.12"
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements-dev.txt
|
|
- name: Run tests
|
|
run: pytest --cov=aegis_gitea_mcp --cov-report=term-missing --cov-fail-under=80
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Build the Docker image, smoke-test it, push to Gitea (push events to
|
|
# main/dev only), then clean up so nothing lingers on the self-hosted
|
|
# runner.
|
|
# ---------------------------------------------------------------------------
|
|
docker:
|
|
needs: [lint, test]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Compute image name & tags
|
|
id: meta
|
|
shell: bash
|
|
run: |
|
|
IMAGE="git.hiddenden.cafe/${GITHUB_REPOSITORY,,}"
|
|
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
|
|
echo "sha_tag=${IMAGE}:sha-${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT"
|
|
if [ "${GITHUB_REF_NAME}" = "main" ]; then
|
|
# Production: stable :latest + :main
|
|
echo "branch_tags=${IMAGE}:latest ${IMAGE}:main" >> "$GITHUB_OUTPUT"
|
|
else
|
|
# dev (and any other branch): tag with the branch name
|
|
echo "branch_tags=${IMAGE}:${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Build image
|
|
shell: bash
|
|
run: docker build -f docker/Dockerfile -t "${{ steps.meta.outputs.sha_tag }}" .
|
|
|
|
- name: Smoke-test image
|
|
shell: bash
|
|
run: |
|
|
docker run --rm --entrypoint python "${{ steps.meta.outputs.sha_tag }}" \
|
|
-c "import aegis_gitea_mcp"
|
|
echo "Image imports cleanly."
|
|
|
|
- name: Log in to Gitea Container Registry
|
|
if: github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'dev')
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: git.hiddenden.cafe
|
|
username: ${{ github.actor }}
|
|
# PAT with write:package scope, stored as the REGISTRY_TOKEN secret.
|
|
# The auto-provided GITEA_TOKEN lacks package-write permission on
|
|
# this instance, so we use a dedicated token here.
|
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
|
|
|
- name: Tag & push
|
|
if: github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'dev')
|
|
shell: bash
|
|
run: |
|
|
for tag in ${{ steps.meta.outputs.branch_tags }} ${{ steps.meta.outputs.sha_tag }}; do
|
|
docker tag "${{ steps.meta.outputs.sha_tag }}" "$tag"
|
|
docker push "$tag"
|
|
echo "Pushed $tag"
|
|
done
|
|
|
|
# Always runs — removes exactly what this run created, even on failure.
|
|
# Scoped on purpose: if the runner shares the host Docker daemon, a global
|
|
# prune would also wipe other homelab services. We never create volumes
|
|
# here, so only dangling images + build cache are swept.
|
|
- name: Cleanup
|
|
if: always()
|
|
shell: bash
|
|
run: |
|
|
docker rmi -f ${{ steps.meta.outputs.sha_tag }} ${{ steps.meta.outputs.branch_tags }} || true
|
|
docker image prune -f || true
|
|
docker builder prune -f || true
|