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
79 changes: 79 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: 📈 Coverage gate

# Batch P1-B — enforces the >= 80 % coverage threshold declared in
# pyproject.toml [tool.coverage.report]. Runs on every PR + push to main.

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read
pull-requests: write

concurrency:
group: coverage-${{ github.ref }}
cancel-in-progress: true

jobs:
coverage:
name: 🧪 Pytest + coverage
runs-on: ubuntu-latest
timeout-minutes: 8

steps:
- name: 📥 Checkout code
uses: actions/checkout@v4

- name: 🐍 Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: |
pyproject.toml
uv.lock

- name: 🐍 Set up Python 3.11
run: uv python install 3.11

- name: 📦 Install project + dev extras
run: |
uv venv
uv pip install -e ".[dev]"

- name: ⚡ Warm bytecode cache
run: uv run python -m compileall -q gitpilot tests || true

- name: 🔎 Strict type-check (Batch P1-C gated modules)
run: uv run mypy --config-file mypy.ini

- name: 🧪 Run tests with coverage
env:
GITPILOT_LITE_MODE: "0"
PYTHONWARNINGS: "ignore::RuntimeWarning"
run: |
TMP_CFG="$(mktemp -d)"
GITPILOT_CONFIG_DIR="$TMP_CFG" uv run pytest \
--cov \
--cov-report=term-missing \
--cov-report=xml \
--cov-report=html
rm -rf "$TMP_CFG"

- name: 📤 Upload coverage HTML artefact
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-html
path: htmlcov/
retention-days: 7

- name: 📤 Upload coverage XML artefact
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-xml
path: coverage.xml
retention-days: 7
92 changes: 92 additions & 0 deletions .github/workflows/supply-chain.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: 🔐 Supply chain — SBOM + Sigstore

# Batch P4-E. Additive workflow that produces a CycloneDX SBOM and
# Sigstore signatures for every Python distribution published in a
# GitHub Release. The existing ``release.yml`` workflow is untouched;
# this one runs in parallel after a release is created, then uploads
# the SBOM + signature blobs back to the same release.
#
# Dry-run support: pushing a tag or pressing "Run workflow" with
# ``dry_run: true`` exercises the whole chain (build, SBOM, sign)
# against a temporary tree. Nothing is uploaded.

on:
release:
types: [published]
workflow_dispatch:
inputs:
dry_run:
description: "Run the pipeline without uploading any artefact"
required: false
default: "true"

permissions:
contents: write # upload SBOM + sig to the GitHub release
id-token: write # OIDC for Sigstore keyless signing

concurrency:
group: supply-chain-${{ github.ref }}
cancel-in-progress: true

jobs:
attestations:
name: 🧾 SBOM + 🔏 Sigstore
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: 📥 Checkout
uses: actions/checkout@v4

- name: 🐍 Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true

- name: 🐍 Set up Python 3.11
run: uv python install 3.11

- name: 📦 Install project + dev extras
run: |
uv venv
uv pip install -e ".[dev]"

- name: 🛠️ Build wheel + sdist
run: uv run python -m build --outdir dist/

- name: 🧾 Generate CycloneDX SBOM
run: |
mkdir -p artefacts
uv run python scripts/sbom_fallback.py > artefacts/sbom.json
uv run python - <<'PY'
import json
d = json.load(open("artefacts/sbom.json"))
assert d["bomFormat"] == "CycloneDX", d
print(f"SBOM ok: {len(d['components'])} components")
PY

- name: 🔏 Sign distributions with Sigstore (keyless OIDC)
if: ${{ github.event_name == 'release' || github.event.inputs.dry_run == 'false' }}
uses: sigstore/gh-action-sigstore-python@v3.0.0
with:
inputs: ./dist/*.whl ./dist/*.tar.gz

- name: 📤 Upload SBOM + signatures to the release
if: ${{ github.event_name == 'release' }}
uses: softprops/action-gh-release@v2
with:
fail_on_unmatched_files: true
files: |
artefacts/sbom.json
dist/*.sigstore.json
dist/*.sigstore

- name: 📤 Upload artefacts (dry-run / debugging)
if: ${{ github.event_name != 'release' }}
uses: actions/upload-artifact@v4
with:
name: supply-chain
path: |
artefacts/sbom.json
dist/*
retention-days: 7
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Desktop.ini
# -----------------
# uv will create .venv at project root by default (covered above)
.uv/
.uv-cache/
uv.lock.old

# Local environment files
Expand Down
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed — `make run` now starts the MCP Context Forge stack by default

**Heads-up for upgraders.** Until this release, `make run` started only the
GitPilot backend and frontend; the MCP stack was opt-in via `make run-mcp`
or `make run-all`. As of this release the happy path is:

```bash
make install # uv + npm + MCP image cache
make run # MCP Context Forge + GitPilot backend + frontend
```

`make run` now:

* depends on `run-mcp`, which itself depends on `install-mcp`;
* fails loudly when Docker / Docker Compose v2 / the daemon are missing
(with a clear hint pointing at `make run-bare`);
* polls `http://localhost:${MCP_FORGE_PORT:-4444}/health` after
`docker compose up -d`, so it only continues once the gateway is
actually reachable by the GitPilot backend and UI.

**No-Docker escape hatch** — added `make run-bare`, which starts only the
GitPilot backend + frontend. The MCP Servers tab will show the gateway
as Unreachable, but the rest of the app is fully functional. Use this
on Hugging Face Spaces, CI smoke runs, and any minimal host.

`make run-all` is preserved as the "force-restart the backend" path
(now equivalent to `stop-soft && run`). External tooling that called
it keeps working.

### Other build / docs updates

* `make install` is now opinionated as **runtime-only**: dev/test/build
tooling moves to `make install-dev`; docs tooling to
`make uv-install-docs`; a `make install-full` superset is available.
Existing CI that calls `make test` keeps working — the target now
uses `uv run --extra dev pytest` internally.
* Re-running `make install-mcp` is now incremental: existing clones skip
network fetch unless `MCP_UPDATE=1`; existing images skip rebuild
unless `MCP_BUILD=1`.
* Render deploy doc updated: build command is now
`pip install uv && uv sync --no-dev` (was `uv sync --all-extras`),
start command is `uv run --no-dev gitpilot serve ...`. Hosted users
that relied on dev tooling at runtime should keep the old commands or
switch to `--extra dev`.
* WSL-friendly `uv` defaults — `UV_LINK_MODE=copy` and
`UV_CACHE_DIR=.uv-cache` to avoid hardlink fallback warnings on
`/mnt/c` checkouts.

### Added — MCP Context Forge integration (additive, opt-in)

- **`gitpilot/mcp_plugin/`** — Context Forge plugin (forge_client,
Expand Down
Loading
Loading