Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
df8b24d
Add health checks and GHCR deploy flow
heidi-dang Jan 27, 2026
e3c713d
Add readiness probe and extend deploy smoke tests
heidi-dang Jan 27, 2026
5dafd8e
Ignore .code and add CI guard
heidi-dang Jan 27, 2026
fbc88b8
Fix import order in health tests
heidi-dang Jan 27, 2026
c9c9006
Normalize import block spacing in health tests
heidi-dang Jan 27, 2026
64f9a4a
Add Gemini assistant chat support
heidi-dang Jan 27, 2026
aa0d7e3
Format imports in health tests
heidi-dang Jan 27, 2026
6224d8c
Merge pull request #1 from heidi-dang/github-workflow
heidi-dang Jan 27, 2026
080831a
Add Gemini UI notice and improve Gemini error handling
heidi-dang Jan 27, 2026
78bf563
Fix workflow expressions and add repo guard to PR checks
heidi-dang Jan 27, 2026
b61ba0d
Merge pull request #2 from heidi-dang/workflow-stability
heidi-dang Jan 27, 2026
2f4f135
Add one-click VPS deploy script with Traefik, DuckDNS, and Let's Encrypt
heidi-dang Jan 27, 2026
b6755e4
Merge branch 'main' into implement-GEMINI-api
heidi-dang Jan 27, 2026
4c7df1e
Add DEVELOPMENT roadmap with phased plan
heidi-dang Jan 27, 2026
7ed036d
Merge remote-tracking branch 'origin/implement-GEMINI-api' into imple…
heidi-dang Jan 27, 2026
4e44fb2
Sort imports in assistant chat session
heidi-dang Jan 27, 2026
e8ab8b8
Merge pull request #3 from heidi-dang/implement-GEMINI-api
heidi-dang Jan 27, 2026
349837d
Limit workflows to main branch
heidi-dang Jan 27, 2026
6d510a3
Merge pull request #4 from heidi-dang/workflow-main-only
heidi-dang Jan 27, 2026
ef33257
Guard against tracked .env and .code
heidi-dang Jan 27, 2026
03e7e5d
Merge pull request #5 from heidi-dang/repo-guards-env-code
heidi-dang Jan 27, 2026
c4d0112
Fix Traefik Docker API version mismatch
heidi-dang Jan 27, 2026
8a43a83
Merge pull request #6 from heidi-dang/traefik-docker-api-fix
heidi-dang Jan 27, 2026
3fb4a23
Automate VPS deploy via deploy.sh
heidi-dang Jan 27, 2026
5e1dd83
Merge pull request #7 from heidi-dang/deploy-automation
heidi-dang Jan 27, 2026
9153a57
Fix Traefik routing and allow Docker access
heidi-dang Jan 27, 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
27 changes: 27 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.git
.gitignore
.code
__pycache__/
*.pyc
*.pyo
*.pyd
*.swp
*.swo
*.tmp
.env
.env.*
env/
venv/
.venv/
ENV/
node_modules/
ui/node_modules/
ui/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
coverage/
dist/
build/
tmp/
*.log
58 changes: 54 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
name: CI
name: Push CI

on:
pull_request:
branches: [master, main]
push:
branches: [master, main]
branches: [main]

jobs:
repo-guards:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Ensure .code/ and .env are not tracked
shell: bash
run: |
tracked_code="$(git ls-files -- .code)"
tracked_env="$(git ls-files -- .env)"

if [ -n "$tracked_code" ] || [ -n "$tracked_env" ]; then
echo "Local-only policy and secrets files must not be tracked."
if [ -n "$tracked_code" ]; then
echo "Tracked .code/ entries:"
echo "$tracked_code"
fi
if [ -n "$tracked_env" ]; then
echo "Tracked .env entries:"
echo "$tracked_env"
fi
exit 1
fi

python:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -39,3 +60,32 @@ jobs:
run: npm run lint
- name: Type check & Build
run: npm run build

docker-image:
needs: [python, ui]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
env:
IMAGE_NAME: ghcr.io/${{ github.repository }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ env.IMAGE_NAME }}:latest
${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
113 changes: 113 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: Deploy to VPS

on:
workflow_run:
workflows: ["Push CI"]
branches: [main]
types:
- completed

permissions:
contents: read

concurrency:
group: deploy-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: false

jobs:
deploy:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
env:
DEPLOY_PATH: ${{ secrets.VPS_DEPLOY_PATH || '/opt/autocoder' }}
TARGET_BRANCH: ${{ secrets.VPS_BRANCH || 'main' }}
VPS_PORT: ${{ secrets.VPS_PORT || '22' }}
DOMAIN: ${{ secrets.VPS_DOMAIN }}
DUCKDNS_TOKEN: ${{ secrets.VPS_DUCKDNS_TOKEN }}
LETSENCRYPT_EMAIL: ${{ secrets.VPS_LETSENCRYPT_EMAIL }}
APP_PORT: ${{ secrets.VPS_APP_PORT || '8888' }}
REPO_URL: https://github.com/${{ github.repository }}.git
IMAGE_LATEST: ghcr.io/${{ github.repository }}:latest
IMAGE_SHA: ghcr.io/${{ github.repository }}:${{ github.event.workflow_run.head_sha }}
steps:
- name: Deploy over SSH with Docker Compose
uses: appleboy/ssh-action@v1.2.4
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ env.VPS_PORT }}
envs: DEPLOY_PATH,TARGET_BRANCH,VPS_PORT,DOMAIN,DUCKDNS_TOKEN,LETSENCRYPT_EMAIL,APP_PORT,REPO_URL,IMAGE_LATEST,IMAGE_SHA
script: |
set -euo pipefail

if [ -z "${DEPLOY_PATH:-}" ]; then
echo "VPS_DEPLOY_PATH secret is required"; exit 1;
fi

if [ -z "${DOMAIN:-}" ] || [ -z "${DUCKDNS_TOKEN:-}" ] || [ -z "${LETSENCRYPT_EMAIL:-}" ]; then
echo "VPS_DOMAIN, VPS_DUCKDNS_TOKEN, and VPS_LETSENCRYPT_EMAIL secrets are required."; exit 1;
fi

if [ ! -d "$DEPLOY_PATH/.git" ]; then
echo "ERROR: $DEPLOY_PATH is missing a git repo. Clone the repository there and keep your .env file."; exit 1;
fi

cd "$DEPLOY_PATH"

if [ ! -f ./deploy.sh ]; then
echo "ERROR: deploy.sh not found in $DEPLOY_PATH. Ensure the repo is up to date."; exit 1;
fi

chmod +x ./deploy.sh

if [ ! -f .env ]; then
echo "WARNING: .env not found in $DEPLOY_PATH. Deployment will continue without it.";
fi

if [ "$(id -u)" -eq 0 ]; then
RUNNER=""
else
if ! command -v sudo >/dev/null 2>&1; then
echo "sudo is required to run deploy.sh as root."; exit 1;
fi
RUNNER="sudo"
fi

$RUNNER env \
AUTOCODER_AUTOMATED=1 \
AUTOCODER_ASSUME_YES=1 \
DOMAIN="${DOMAIN}" \
DUCKDNS_TOKEN="${DUCKDNS_TOKEN}" \
LETSENCRYPT_EMAIL="${LETSENCRYPT_EMAIL}" \
REPO_URL="${REPO_URL}" \
DEPLOY_BRANCH="${TARGET_BRANCH}" \
APP_DIR="${DEPLOY_PATH}" \
APP_PORT="${APP_PORT}" \
IMAGE="${IMAGE_SHA:-$IMAGE_LATEST}" \
./deploy.sh

echo "Running smoke test on http://127.0.0.1:${APP_PORT}/health and /readiness ..."
retries=12
until curl -fsS --max-time 5 "http://127.0.0.1:${APP_PORT}/health" >/dev/null; do
retries=$((retries - 1))
if [ "$retries" -le 0 ]; then
echo "Health check failed after retries."
exit 1
fi
echo "Waiting for health... ($retries retries left)"
sleep 5
done

retries=12
until curl -fsS --max-time 5 "http://127.0.0.1:${APP_PORT}/readiness" >/dev/null; do
retries=$((retries - 1))
if [ "$retries" -le 0 ]; then
echo "Readiness check failed after retries."
exit 1
fi
echo "Waiting for readiness... ($retries retries left)"
sleep 5
done

echo "Service responded successfully to health and readiness."
71 changes: 71 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: PR Check

on:
pull_request:
branches: [main]
Comment on lines +3 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the default branch and workflow branch triggers

echo "=== Default branch ==="
git remote show origin 2>/dev/null | grep 'HEAD branch' || git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "Could not determine default branch"

echo ""
echo "=== Branches in the repository ==="
git branch -r | head -20

echo ""
echo "=== Branch triggers in all workflows ==="
rg -n "branches:" .github/workflows/ --type yaml || echo "No branch triggers found"

echo ""
echo "=== Content of pr-check.yml (first 20 lines) ==="
head -20 .github/workflows/pr-check.yml

Repository: leonvanzyl/autocoder

Length of output: 844


Critical: All workflows configured to trigger on non-existent main branch, but repository uses master.

The repository's default branch is master, but all three workflows (pr-check.yml, deploy.yml, ci.yml) are configured to trigger on branches: [main], which doesn't exist. These workflows won't run for any pull requests.

Update all workflow files to trigger on master:

Fix all workflows
 on:
   pull_request:
-    branches: [main]
+    branches: [master]

Apply this change to .github/workflows/pr-check.yml, .github/workflows/deploy.yml, and .github/workflows/ci.yml.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
on:
pull_request:
branches: [main]
on:
pull_request:
branches: [master]
🤖 Prompt for AI Agents
In @.github/workflows/pr-check.yml around lines 3 - 5, The workflow trigger is
configured to use the non-existent branch name "main" (see the on: pull_request:
branches: [main] block); update this across the three workflow files
(pr-check.yml, deploy.yml, ci.yml) by changing branches: [main] to branches:
[master] so PR events target the repository's default branch; ensure you update
the same "on: pull_request:" block in each file and commit the three modified
workflow files.


permissions:
contents: read

concurrency:
group: pr-check-${{ github.event.pull_request.head.repo.full_name }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
repo-guards:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Ensure .code/ and .env are not tracked
shell: bash
run: |
tracked_code="$(git ls-files -- .code)"
tracked_env="$(git ls-files -- .env)"

if [ -n "$tracked_code" ] || [ -n "$tracked_env" ]; then
echo "Local-only policy and secrets files must not be tracked."
if [ -n "$tracked_code" ]; then
echo "Tracked .code/ entries:"
echo "$tracked_code"
fi
if [ -n "$tracked_env" ]; then
echo "Tracked .env entries:"
echo "$tracked_env"
fi
exit 1
fi

python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"
cache-dependency-path: requirements.txt
- name: Install dependencies
run: pip install -r requirements.txt
- name: Lint with ruff
run: ruff check .
- name: Run security tests
run: python test_security.py

ui:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ui
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check & Build
run: npm run build
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ temp/
nul
issues/

# Repository-specific
.code/

# Browser profiles for parallel agent execution
.browser-profiles/

Expand All @@ -16,6 +19,9 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Local Codex/Claude configuration (do not commit)
.code/

# ===================
# Node.js
# ===================
Expand Down
63 changes: 63 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# AutoCoder Development Roadmap

This roadmap breaks work into clear phases so you can pick the next most valuable items quickly.

## Phase 0 — Baseline (ship ASAP)
- **PR discipline:** Enforce branch protection requiring “PR Check” (already configured in workflows; ensure GitHub rule is on).
- **Secrets hygiene:** Move all deploy secrets into repo/environment secrets; prohibit `.env` commits via pre-commit hook.
- **Smoke tests:** Keep `/health` and `/readiness` endpoints green; add UI smoke (landing page loads) to CI.

## Phase 1 — Reliability & Observability
- **Structured logging:** Add JSON logging for FastAPI (uvicorn access + app logs) with request IDs; forward to stdout for Docker/Traefik.
- **Error reporting:** Wire Sentry (or OpenTelemetry + OTLP) for backend exceptions and front-end errors.
- **Metrics:** Expose `/metrics` (Prometheus) for FastAPI; Traefik already exposes metrics option—enable when scraping is available.
- **Tracing:** Add OTEL middleware to FastAPI; propagate trace IDs through to Claude/Gemini calls when possible.

## Phase 2 — Platform & DevX
- **Local dev parity:** Add `docker-compose.dev.yml` with hot-reload for FastAPI + Vite UI; document one-command setup.
- **Makefile/taskfile:** Common commands (`make dev`, `make test`, `make lint`, `make format`, `make seed`).
- **Pre-commit:** Ruff, mypy, black (if adopted), eslint/prettier for `ui/`.
- **Typed APIs:** Add mypy strict mode to `server/` and type `schemas.py` fully (Pydantic v2 ConfigDict).

## Phase 3 — Product & Agent Quality
- **Model selection UI:** Let users choose assistant provider (Claude/Gemini) in settings; display active provider badge in chat.
- **Tooling guardrails:** For Gemini (chat-only), show “no tools” notice in UI and fallback logic to Claude when tools needed.
- **Conversation persistence:** Add pagination/search over assistant history; export conversation to file.
- **Feature board:** Surface feature stats/graph from MCP in the UI (read-only dashboard).

## Phase 4 — Security & Compliance
- **AuthN/AuthZ:** Add optional login (JWT/OIDC) gate for UI/API; role for “admin” vs “viewer” at least.
- **Rate limiting:** Enable per-IP rate limits at Traefik and per-token limits in FastAPI.
- **Audit trails:** Log agent actions and feature state changes with user identity.
- **Headers/HTTPS:** HSTS via Traefik, content-security-policy header from FastAPI.

## Phase 5 — Performance & Scale
- **Caching:** CDN/Traefik static cache for UI assets; server-side cache for model list/status endpoints.
- **Worker separation:** Optionally split agent runner from API via separate services and queues (e.g., Redis/RQ or Celery).
- **Background jobs:** Move long-running tasks to scheduler/worker with backoff and retries.

## Phase 6 — Testing & Quality Gates
- **Backend tests:** Add pytest suite for key routers (`/api/setup/status`, assistant chat happy-path with mock Claude/Gemini).
- **Frontend tests:** Add Vitest + React Testing Library smoke tests for core pages (dashboard loads, settings save).
- **E2E:** Playwright happy-path (login optional, start agent, view logs).
- **Coverage:** Fail CI if coverage drops below threshold (start at 60–70%).

## Phase 7 — Deployment & Ops
- **Blue/green deploy:** Add image tagging `:sha` + `:latest` (already for CI) with Traefik service labels to toggle.
- **Backups:** Snapshot `~/.autocoder` data volume; document restore.
- **Runbooks:** Add `RUNBOOK.md` for common ops (restart, rotate keys, renew certs, roll back).

## Phase 8 — Documentation & Onboarding
- **Getting started:** Short path for “run locally in 5 minutes” (scripted).
- **Config matrix:** Document required/optional env vars (Claude, Gemini, DuckDNS, Traefik, TLS).
- **Architecture:** One-page diagram: UI ↔ FastAPI ↔ Agent subprocess ↔ Claude/Gemini; MCP servers; Traefik front.

## Stretch Ideas
- **Telemetry-driven tuning:** Auto-select model/provider based on latency/cost SLA.
- **Cost controls:** Show per-run token/cost estimates; configurable budgets.
- **Offline/edge mode:** Ollama provider toggle with cached models.

## How to use this roadmap
- Pick the next phase that unblocks your current goal (reliability → platform → product).
- Keep PRs small and scoped to one bullet.
- Update this document when a bullet ships or is reprioritized.
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Build frontend and backend for production

# 1) Build the React UI
FROM node:20-alpine AS ui-builder
WORKDIR /app/ui
COPY ui/package*.json ./
RUN npm ci
COPY ui/ .
RUN npm run build

# 2) Build the Python backend with the compiled UI assets
FROM python:3.11-slim AS runtime
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy source code and built UI
COPY . .
COPY --from=ui-builder /app/ui/dist ./ui/dist

EXPOSE 8888
CMD ["uvicorn", "server.main:app", "--host", "0.0.0.0", "--port", "8888"]
Loading