Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
573fc09
feat(plugins): complete CGC plugin extension system (001-cgc-plugin-e…
ductiletoaster Mar 15, 2026
d0a0bbe
Add spec-kit
ductiletoaster Mar 15, 2026
f7ce8cd
fix(ci): harden CI/CD workflows and test compatibility for plugin system
ductiletoaster Mar 15, 2026
09615d4
fix(ci): replace manylinux2014 image with manylinux_2_39 for glibc 2.…
ductiletoaster Mar 15, 2026
56a45b9
fix(plugins): point entry points to package modules and add re-export…
ductiletoaster Mar 15, 2026
e2615f1
fix(ci): switch Linux build to native ubuntu runner to satisfy falkor…
ductiletoaster Mar 15, 2026
1cb6951
fix(spec): explicitly bundle falkordblite.libs and dummy extension fo…
ductiletoaster Mar 15, 2026
92bf8f5
remove(plugins): drop memory plugin — CGC focuses on code understanding
ductiletoaster Mar 18, 2026
27ea9df
fix(otel): remove dead grpc.experimental code that crashes the receiver
ductiletoaster Mar 18, 2026
6fa7f64
fix(xdebug): add main() entry point so dbgp_server runs as standalone…
ductiletoaster Mar 18, 2026
50abd07
feat(samples): add US5 sample applications for end-to-end plugin vali…
ductiletoaster Mar 18, 2026
49f5915
fix(samples): resolve Docker build failures across all three sample apps
ductiletoaster Mar 18, 2026
c893f6a
fix(otel+samples): fix OTEL processor for Docker and document Docker-…
ductiletoaster Mar 18, 2026
bd6efdd
chore(neo4j): upgrade from 5.15.0 to 2026.02.2
ductiletoaster Mar 18, 2026
e19e15b
feat(samples): add one-shot indexer service, simplify developer workflow
ductiletoaster Mar 18, 2026
083147c
chore(samples): track samples/docker-compose.yml (override gitignore)
ductiletoaster Mar 18, 2026
6423d96
fix(gitignore): scope docker-compose.yml ignore to root only
ductiletoaster Mar 18, 2026
5dd097e
fix(samples): use http module for gateway outbound calls, add peer.se…
ductiletoaster Mar 20, 2026
be32e91
docs(samples): add validation report from end-to-end graph exploration
ductiletoaster Mar 20, 2026
f370954
docs(specs): add User Story 6 — Hosted MCP Server Container Image
ductiletoaster Mar 20, 2026
5416032
docs(specs): apply US6 clarifications and mark all tasks complete
ductiletoaster Mar 20, 2026
95c50da
feat(mcp): add HTTP transport for hosted MCP server
ductiletoaster Mar 20, 2026
c497439
feat(mcp): add Dockerfile.mcp, K8s manifests, and CI/CD registration
ductiletoaster Mar 20, 2026
2c0271c
docs(mcp): add deployment guide, E2E tests, and samples integration
ductiletoaster Mar 20, 2026
ced4526
chore(specify): update speckit scripts and constitution template
ductiletoaster Mar 20, 2026
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
18 changes: 18 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,21 @@ PYTHONDONTWRITEBYTECODE=1

# Optional: Database selection
# DATABASE_TYPE=falkordb # or falkordb-remote or neo4j

# ── Plugin Configuration ───────────────────────────────────────────────────
# Required when using docker-compose.plugins.yml or docker-compose.dev.yml

# Your ingress domain (used by Traefik labels on plugin services)
# DOMAIN=localhost

# OTEL Plugin — span receiver and processor
# OTEL_RECEIVER_PORT=5317
# OTEL_FILTER_ROUTES=/health,/metrics,/ping,/favicon.ico

# Xdebug Plugin — DBGp TCP listener (dev only)
# XDEBUG_LISTEN_HOST=0.0.0.0
# XDEBUG_LISTEN_PORT=9003
# XDEBUG_DEDUP_CACHE_SIZE=10000

# Log level for plugin containers (DEBUG, INFO, WARNING, ERROR)
# LOG_LEVEL=INFO
34 changes: 34 additions & 0 deletions .github/services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[
{
"name": "cgc-core",
"path": ".",
"dockerfile": "Dockerfile",
"image": "cgc-core",
"health_check": "version",
"description": "CodeGraphContext MCP server core"
},
{
"name": "cgc-plugin-otel",
"path": "plugins/cgc-plugin-otel",
"dockerfile": "Dockerfile",
"image": "cgc-plugin-otel",
"health_check": "grpc_ping",
"description": "OpenTelemetry span receiver and graph writer"
},
{
"name": "cgc-plugin-xdebug",
"path": "plugins/cgc-plugin-xdebug",
"dockerfile": "Dockerfile",
"image": "cgc-plugin-xdebug",
"health_check": "tcp_connect",
"description": "Xdebug DBGp call-stack listener"
},
{
"name": "cgc-mcp",
"path": ".",
"dockerfile": "Dockerfile.mcp",
"image": "cgc-mcp",
"health_check": "http_get",
"description": "CGC hosted MCP server — HTTP transport with bundled plugins"
}
]
22 changes: 10 additions & 12 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
Expand Down Expand Up @@ -48,20 +49,17 @@ jobs:
run: |
pyinstaller cgc.spec --clean

- name: Build with PyInstaller (Linux - Manylinux)
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
docker run --rm \
-v ${{ github.workspace }}:/src \
quay.io/pypa/manylinux2014_x86_64 \
/bin/bash -c "
set -e
cd /src
/opt/python/cp312-cp312/bin/python -m pip install --upgrade pip
/opt/python/cp312-cp312/bin/pip install . pyinstaller
/opt/python/cp312-cp312/bin/pyinstaller cgc.spec --clean
"
sudo chown -R $USER:$USER dist build
python -m pip install --upgrade pip
pip install .
pip install pyinstaller

- name: Build with PyInstaller (Linux)
if: runner.os == 'Linux'
run: |
pyinstaller cgc.spec --clean

- name: Rename artifact (Linux/Mac)
if: runner.os != 'Windows'
Expand Down
20 changes: 18 additions & 2 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,26 @@ jobs:
test:
runs-on: ubuntu-latest

services:
falkordb:
image: falkordb/falkordb:latest
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'

- name: Install dependencies
run: |
Expand All @@ -26,6 +38,10 @@ jobs:
pip install pytest

- name: Run end-to-end tests
env:
FALKORDB_HOST: localhost
FALKORDB_PORT: 6379
DATABASE_TYPE: falkordb-remote
run: |
chmod +x tests/run_tests.sh
./tests/run_tests.sh e2e
9 changes: 6 additions & 3 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ jobs:
python-version: "3.12"

- name: Install system deps
continue-on-error: true
run: |
brew update
brew install ripgrep || true
brew install ripgrep

- name: Install CodeGraphContext
run: |
Expand All @@ -35,12 +36,14 @@ jobs:
df -h

- name: Run index (verbose)
continue-on-error: true
run: |
cgc index -f --debug || true
cgc index -f --debug

- name: Try find
continue-on-error: true
run: |
cgc find content "def" --debug || true
cgc find content "def" --debug

- name: Upload logs
if: always()
Expand Down
123 changes: 123 additions & 0 deletions .github/workflows/plugin-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: Build and Publish Plugin Images

on:
push:
tags:
- 'v*.*.*'
pull_request:
branches: [main]
workflow_dispatch:

env:
REGISTRY: ghcr.io
IMAGE_PREFIX: ${{ github.repository_owner }}

jobs:
# ── Read the service matrix from services.json ──────────────────────────
setup:
name: Load service matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.load.outputs.matrix }}
steps:
- uses: actions/checkout@v4

- name: Load services.json into matrix
id: load
run: |
# Filter to plugin services only (skip cgc-core — handled by docker-publish.yml)
MATRIX=$(cat .github/services.json | jq -c '[.[] | select(.name != "cgc-core")]')
echo "matrix=${MATRIX}" >> "$GITHUB_OUTPUT"

# ── Build, smoke-test, and optionally push each plugin image ────────────
build-plugins:
name: Build ${{ matrix.name }}
needs: setup
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
matrix:
include: ${{ fromJson(needs.setup.outputs.matrix) }}
fail-fast: false

steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/${{ matrix.image }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=ref,event=pr
type=sha,prefix=sha-
labels: |
org.opencontainers.image.title=${{ matrix.name }}
org.opencontainers.image.description=${{ matrix.description }}
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}

- name: Build image (load locally for smoke test)
id: build-local
uses: docker/build-push-action@v5
with:
context: ${{ matrix.path }}
file: ${{ matrix.path }}/${{ matrix.dockerfile }}
push: false
load: true
tags: smoke-test/${{ matrix.image }}:ci
cache-from: type=gha,scope=${{ matrix.name }}
cache-to: type=gha,mode=max,scope=${{ matrix.name }}

- name: Smoke test — gRPC import
if: matrix.health_check == 'grpc_ping'
run: docker run --rm smoke-test/${{ matrix.image }}:ci python -c "import grpc; print('gRPC OK')"

- name: Smoke test — socket
if: matrix.health_check == 'tcp_connect'
run: docker run --rm smoke-test/${{ matrix.image }}:ci python -c "import socket; socket.socket(); print('socket OK')"

- name: Push image to GHCR
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v5
with:
context: ${{ matrix.path }}
file: ${{ matrix.path }}/${{ matrix.dockerfile }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.name }}
cache-to: type=gha,mode=max,scope=${{ matrix.name }}
platforms: linux/amd64,linux/arm64

# ── Summary ──────────────────────────────────────────────────────────────
build-summary:
name: Plugin build summary
needs: build-plugins
runs-on: ubuntu-latest
if: always()
steps:
- name: Report overall status
run: |
if [ "${{ needs.build-plugins.result }}" = "success" ]; then
echo "✅ All plugin images built successfully."
else
echo "⚠️ One or more plugin images failed. Check individual job logs."
exit 1
fi
84 changes: 84 additions & 0 deletions .github/workflows/test-plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Plugin Tests

on:
pull_request:
branches: [main]
paths:
- 'plugins/**'
- 'src/codegraphcontext/plugin_registry.py'
- 'tests/unit/plugin/**'
- 'tests/integration/plugin/**'
push:
branches: [main]
paths:
- 'plugins/**'
- 'src/codegraphcontext/plugin_registry.py'
workflow_dispatch:

jobs:
plugin-unit-tests:
name: Plugin unit + integration tests
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip

- name: Install core CGC (no extras) and dev dependencies
run: |
pip install --no-cache-dir packaging pytest pytest-mock
pip install --no-cache-dir -e ".[dev]"

- name: Install stub plugin (editable)
run: pip install --no-cache-dir -e plugins/cgc-plugin-stub

- name: Run plugin unit tests
env:
PYTHONPATH: src
run: pytest tests/unit/plugin/ -v --tb=short

- name: Run plugin integration tests
env:
PYTHONPATH: src
run: pytest tests/integration/plugin/ -v --tb=short

plugin-import-check:
name: Verify plugin packages import cleanly
runs-on: ubuntu-latest
strategy:
matrix:
plugin: [cgc-plugin-stub, cgc-plugin-otel, cgc-plugin-xdebug]
fail-fast: false

steps:
- uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install plugin
run: |
pip install --no-cache-dir typer neo4j packaging || true
pip install --no-cache-dir -e plugins/${{ matrix.plugin }} || true

- name: Check plugin PLUGIN_METADATA
env:
PYTHONPATH: src
run: |
PLUGIN_MOD=$(echo "${{ matrix.plugin }}" | tr '-' '_')
python -c "
import importlib
mod = importlib.import_module('${PLUGIN_MOD}')
meta = getattr(mod, 'PLUGIN_METADATA', None)
assert meta is not None, 'PLUGIN_METADATA missing'
for field in ('name', 'version', 'cgc_version_constraint', 'description'):
assert field in meta, f'PLUGIN_METADATA missing field: {field}'
print(f'✅ ${PLUGIN_MOD} PLUGIN_METADATA OK: {meta[\"name\"]} v{meta[\"version\"]}')
"
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: 'pip'

- name: Install dependencies
run: |
Expand Down
Loading
Loading