Files
AegisGitea-MCP/.gitea/workflows/publish.yml
T
Latte 1a039e32ab ci: single Gitea Actions pipeline, uv-based image, scoped docker cleanup
Replace lint/test/docker workflows with ci.yml (gitleaks, lint, test-unit, test-integration, test-mcp, build-and-push, docs-check, security). Rebuild the Dockerfile as a digest-pinned multi-stage uv build running as uid 1000, add .dockerignore, a hardened compose file and a gitleaks config. The integration harness now falls back to /etc/hostname and /proc/self to find its own container.
2026-10-14 15:59:19 +02:00

169 lines
6.2 KiB
YAML

name: publish
# Build the Python package with uv and publish it to the self-hosted Gitea PyPI
# registry on merge. dev -> a dev package (aegis-gitea-mcp-dev, .devN versions);
# main -> the stable package (aegis-gitea-mcp). Gated on lint + tests so a release
# can never ship red. Reuses the REGISTRY_TOKEN package secret (same one ci.yml
# uses); if it is absent the job fails loudly instead of publishing anonymously.
on:
push:
branches:
- dev
- main
jobs:
# ---------------------------------------------------------------------------
# 1. Lint (same gate as ci.yml, via uv and the lockfile).
# ---------------------------------------------------------------------------
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: Set up uv
uses: astral-sh/setup-uv@v5
with:
version: "0.12.5"
enable-cache: false
- name: Install dependencies
run: uv sync --locked
- name: Run lint
run: |
uv run ruff check .
uv run ruff format --check .
uv run mypy src
# ---------------------------------------------------------------------------
# 2. Test: unit tests with the coverage gate from pyproject.toml.
# ---------------------------------------------------------------------------
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
with:
version: "0.12.5"
enable-cache: false
- name: Install dependencies
run: uv sync --locked
- name: Run tests
run: uv run pytest
# ---------------------------------------------------------------------------
# 3. Build with uv and publish to the Gitea PyPI registry.
#
# dev -> aegis-gitea-mcp-dev X.Y.Z.dev<run_number> (always unique)
# main -> aegis-gitea-mcp X.Y.Z (no-op if already there)
#
# The package name + version are patched into pyproject.toml at build time
# only — never committed. The committed file keeps name "aegis-gitea-mcp"
# and version "X.Y.Z".
# ---------------------------------------------------------------------------
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
with:
version: "0.12.5"
enable-cache: false
- name: Require publish credentials
shell: bash
env:
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
if [ -z "${REGISTRY_TOKEN}" ]; then
echo "::error::REGISTRY_TOKEN secret is not set. Configure a PAT with write:package." >&2
exit 1
fi
- name: Compute channel (package name + version)
id: chan
shell: bash
run: |
BASE="$(grep -E '^version = ' pyproject.toml | head -1 | sed -E 's/^version = "([^"]+)".*/\1/')"
if [ "${GITHUB_REF_NAME}" = "main" ]; then
PKG_NAME="aegis-gitea-mcp"
PKG_VERSION="${BASE}"
CHANNEL="stable"
else
PKG_NAME="aegis-gitea-mcp-dev"
# Drop an existing .devN suffix (the committed version may be a
# development version) so the result has exactly one dev segment.
PKG_VERSION="${BASE%%.dev*}.dev${GITHUB_RUN_NUMBER}"
CHANNEL="dev"
fi
echo "pkg_name=${PKG_NAME}" >> "$GITHUB_OUTPUT"
echo "pkg_version=${PKG_VERSION}" >> "$GITHUB_OUTPUT"
echo "channel=${CHANNEL}" >> "$GITHUB_OUTPUT"
echo "Publishing ${PKG_NAME} ${PKG_VERSION} (${CHANNEL})"
- name: Patch package name + version (build only, not committed)
shell: bash
run: |
sed -i -E "s/^name = \".*\"/name = \"${{ steps.chan.outputs.pkg_name }}\"/" pyproject.toml
sed -i -E "s/^version = \".*\"/version = \"${{ steps.chan.outputs.pkg_version }}\"/" pyproject.toml
echo "--- patched [project] header ---"
grep -E '^(name|version) = ' pyproject.toml
- name: Build sdist + wheel
shell: bash
run: uv build
- name: Upload build artifacts
# Best-effort: some Gitea act_runner versions don't fully support the
# v4 artifact backend. The real deliverable is published to the registry
# below, so a failed artifact upload must not fail the release.
continue-on-error: true
uses: actions/upload-artifact@v4
with:
name: dist-${{ steps.chan.outputs.channel }}
path: dist/*
- name: Publish to Gitea PyPI registry
shell: bash
env:
# Reuse the existing package secret (same one ci.yml uses). The
# token authenticates as its owning Gitea user, so GITHUB_ACTOR is the
# username and the token is the password.
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
# --check-url makes uv skip files already in the registry, so a main
# push that did not bump the version is a clean no-op instead of a 409.
uv publish \
--publish-url https://git.hiddenden.cafe/api/packages/Hiddenden/pypi \
--check-url https://git.hiddenden.cafe/api/packages/Hiddenden/pypi/simple/ \
--username "${GITHUB_ACTOR}" \
--password "${REGISTRY_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}"