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 docker.yml # uses); if it is absent the job fails loudly instead of publishing anonymously. on: push: branches: - dev - main 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. # # dev -> aegis-gitea-mcp-dev X.Y.Z.dev (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 - 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" PKG_VERSION="${BASE}.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 docker.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}"