From 499bf98d92ce6cddff2e326cd5cfc0f651af38a5 Mon Sep 17 00:00:00 2001 From: Latte Date: Sat, 27 Jun 2026 11:18:07 +0200 Subject: [PATCH] ci: build and publish package to Gitea registry on tag Add .gitea/workflows/publish.yml: on a v* tag, gate on the existing lint + test jobs, then build sdist+wheel with uv and publish to the self-hosted Gitea PyPI registry using least-privilege Actions secrets (GITEA_PACKAGE_USER / GITEA_PACKAGE_TOKEN). The job fails loudly when the secrets are absent rather than publishing anonymously, uploads the built artifacts, and leaves a disabled public-PyPI stub. Public PyPI is intentionally not published in this pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitea/workflows/publish.yml | 115 +++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 116 insertions(+) create mode 100644 .gitea/workflows/publish.yml diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml new file mode 100644 index 0000000..f5a482c --- /dev/null +++ b/.gitea/workflows/publish.yml @@ -0,0 +1,115 @@ +name: publish + +# Build the Python package with uv and publish it to the self-hosted Gitea PyPI +# registry on a version tag. Gated on lint + tests so a release can never ship +# red. Publishing uses least-privilege Gitea Actions secrets; if they are absent +# the job fails loudly instead of publishing anonymously. +on: + push: + tags: + - 'v*' + +jobs: + # --------------------------------------------------------------------------- + # 1. Lint: ruff + black + mypy (same gate as the other workflows). + # --------------------------------------------------------------------------- + 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 with uv and publish to the Gitea PyPI registry. + # --------------------------------------------------------------------------- + publish: + needs: [lint, 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: Set up uv + uses: astral-sh/setup-uv@v5 + + - name: Require publish credentials + shell: bash + env: + GITEA_PACKAGE_USER: ${{ secrets.GITEA_PACKAGE_USER }} + GITEA_PACKAGE_TOKEN: ${{ secrets.GITEA_PACKAGE_TOKEN }} + run: | + if [ -z "${GITEA_PACKAGE_USER}" ] || [ -z "${GITEA_PACKAGE_TOKEN}" ]; then + echo "::error::GITEA_PACKAGE_USER / GITEA_PACKAGE_TOKEN secrets are not set." >&2 + echo "Configure a least-privilege PAT with write:package as Actions secrets." >&2 + exit 1 + fi + + - name: Build sdist + wheel + shell: bash + run: uv build + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/* + + - name: Publish to Gitea PyPI registry + shell: bash + env: + GITEA_PACKAGE_USER: ${{ secrets.GITEA_PACKAGE_USER }} + GITEA_PACKAGE_TOKEN: ${{ secrets.GITEA_PACKAGE_TOKEN }} + run: | + uv publish \ + --publish-url https://git.hiddenden.cafe/api/packages/Hiddenden/pypi \ + --username "${GITEA_PACKAGE_USER}" \ + --password "${GITEA_PACKAGE_TOKEN}" + + # Optional second step to also publish to public PyPI lives behind its own + # secret. Intentionally left as a disabled stub — this pass does NOT push + # to public PyPI. + # + # - name: Publish to public PyPI + # if: ${{ secrets.PYPI_TOKEN != '' }} + # shell: bash + # env: + # PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + # run: uv publish --username __token__ --password "${PYPI_TOKEN}" diff --git a/README.md b/README.md index cde8f4d..c8acda0 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,7 @@ Gitea workflows were added under `.gitea/workflows/`: - `lint.yml`: Ruff + formatting + mypy. - `test.yml`: lint + pytest + enforced coverage (`>=80%`). - `docker.yml`: lint+test gated Docker build, SHA tag, `latest` tag on `main`. +- `publish.yml`: on a `v*` tag, lint+test gated `uv build` + publish the Python package to the Gitea PyPI registry (see `docs/packaging.md`). ## Docker hardening