Skip to content

Commit 7547523

Browse files
authored
Merge pull request #50 from ClaydeCode/clayde/issue-48-generic-configurable-deployment
Fix #48: Make Clyde ready to be deployed by anyone
2 parents d353709 + aaf10d9 commit 7547523

7 files changed

Lines changed: 122 additions & 36 deletions

File tree

CLAUDE.md

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
# Clayde
22

3-
**Name:** Clayde
4-
**Email:** clayde@vtettenborn.net
5-
**GitHub:** @ClaydeCode
3+
Clayde is a persistent autonomous AI software agent running in a Docker container. My purpose is to help with software development by working on GitHub issues assigned to me. When assigned an issue, I analyze the relevant codebase, implement a solution, open a pull request, and post a comment on the issue summarizing what I did.
64

7-
Clayde is a persistent autonomous AI software agent running on a dedicated VM at `/home/ubuntu/clayde`. My purpose is to help with software development by working on GitHub issues assigned to me. When assigned an issue, I analyze the relevant codebase, implement a solution, open a pull request, and post a comment on the issue summarizing what I did.
8-
9-
The `gh` CLI is authenticated as @ClaydeCode and git is configured with my name and email.
5+
The `gh` CLI is authenticated as the configured bot GitHub account and git is configured with the identity from `CLAYDE_GIT_NAME` and `CLAYDE_GIT_EMAIL`.
106

117
---
128

@@ -27,7 +23,7 @@ The `gh` CLI is authenticated as @ClaydeCode and git is configured with my name
2723
- **Container layout:** Application code at `/opt/clayde`, data at `/data` (single volume mount from host `./data`)
2824
- **Claude:** Dual backend — Anthropic Python SDK (`api`) or Claude Code CLI (`cli`), selected by `CLAYDE_CLAUDE_BACKEND`
2925
- **Git credential helper:** `gh auth git-credential` (configured globally in the container)
30-
- **Git identity:** `user.name = Clayde`, `user.email = clayde@vtettenborn.net`
26+
- **Git identity:** configured at container startup from `CLAYDE_GIT_NAME` and `CLAYDE_GIT_EMAIL` env vars
3127

3228
---
3329

@@ -39,7 +35,6 @@ pyproject.toml # hatchling build; console scripts: clayde, clayde-once
3935
CLAUDE.md # this file — identity + project context
4036
Dockerfile # Python 3.13-slim image with git, gh, uv
4137
docker-compose.yml # container deployment config
42-
gh-issue.md # slash-command prompt for interactive issue work
4338
uv.lock
4439
src/clayde/
4540
__init__.py
@@ -99,10 +94,12 @@ Plain `KEY=VALUE` file (no shell quoting). All keys use `CLAYDE_` prefix and are
9994

10095
| Key | Purpose |
10196
|-----|---------|
102-
| `CLAYDE_GITHUB_TOKEN` | Fine-grained PAT with Issues R/W, Pull Requests R/W, Contents R/W |
103-
| `CLAYDE_GITHUB_USERNAME` | `ClaydeCode` |
97+
| `CLAYDE_GITHUB_TOKEN` | Classic PAT with full `repo` scope |
98+
| `CLAYDE_GITHUB_USERNAME` | The bot account username (e.g. `YourBotName`) |
10499
| `CLAYDE_ENABLED` | Set to `true` to activate; any other value causes immediate exit |
105-
| `CLAYDE_WHITELISTED_USERS` | Comma-separated list of trusted GitHub usernames (e.g. `max-tet,ClaydeCode`) |
100+
| `CLAYDE_WHITELISTED_USERS` | Comma-separated list of trusted GitHub usernames |
101+
| `CLAYDE_GIT_NAME` | Git commit author name (defaults to `CLAYDE_GITHUB_USERNAME` if not set) |
102+
| `CLAYDE_GIT_EMAIL` | Git commit author email (required) |
106103
| `CLAYDE_CLAUDE_API_KEY` | Anthropic API key for Claude SDK calls (required when backend=`api`) |
107104
| `CLAYDE_CLAUDE_MODEL` | Model to use (default: `claude-opus-4-6`) |
108105
| `CLAYDE_CLAUDE_BACKEND` | `api` (default) or `cli` — selects Anthropic SDK or Claude Code CLI |
@@ -290,16 +287,6 @@ Logger names: `clayde.orchestrator`, `clayde.tasks.plan`, `clayde.tasks.implemen
290287

291288
---
292289

293-
## Interactive Issue Work (`gh-issue.md`)
294-
295-
The file `gh-issue.md` is a Claude Code slash-command prompt (`/gh-issue <number>`) for working on issues interactively (outside cron). It runs as a multi-step subagent workflow: Plan → clarify → implement → self-review → address review → return PR URL. Sends push notifications via `apprise ntfy://7yuau0vyes`.
296-
297-
Allowed tools for interactive work: `Bash(gh:*)`, `Bash(git:*)`, `Bash(just:*)`, `Bash(python:*)`, `Bash(pytest:*)`, `Bash(npm:*)`, `Bash(uv:*)`, `Bash(apprise:*)`, `Read`, `Write`, `Edit`, `Glob`, `Grep`
298-
299-
Branch naming for interactive work: `issue/{number}-short-desc`
300-
301-
---
302-
303290
## Testing
304291

305292
Run the test suite after any feature development or bug fix:

Dockerfile

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,16 @@ COPY src/ src/
3737
COPY CLAUDE.md ./
3838
RUN uv sync --frozen --no-dev
3939

40+
# Copy entrypoint script
41+
COPY entrypoint.sh /opt/clayde/entrypoint.sh
42+
RUN chmod +x /opt/clayde/entrypoint.sh
43+
4044
# Create data directories and set ownership
4145
RUN mkdir -p /data/repos /data/logs && chown -R clayde:clayde /data
4246

43-
# Switch to non-root user and configure git
47+
# Switch to non-root user and configure git credential helper
4448
USER clayde
4549
RUN mkdir -p /home/clayde/.claude && \
46-
git config --global credential.helper '!gh auth git-credential' && \
47-
git config --global user.name "Clayde" && \
48-
git config --global user.email "clayde@vtettenborn.net"
50+
git config --global credential.helper '!gh auth git-credential'
4951

50-
ENTRYPOINT ["/opt/clayde/.venv/bin/clayde"]
52+
ENTRYPOINT ["/opt/clayde/entrypoint.sh"]

README.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,56 @@ Clayde will start its loop, checking for assigned issues every 5 minutes (config
143143

144144
| Key | Purpose |
145145
|---|---|
146-
| `CLAYDE_GITHUB_TOKEN` | Fine-grained PAT (Issues R/W, PRs R/W, Contents R/W) |
147-
| `CLAYDE_GITHUB_USERNAME` | `ClaydeCode` |
146+
| `CLAYDE_GITHUB_TOKEN` | Classic PAT with full `repo` scope |
147+
| `CLAYDE_GITHUB_USERNAME` | The bot account username |
148+
| `CLAYDE_GIT_NAME` | Git commit author name (defaults to `CLAYDE_GITHUB_USERNAME` if not set) |
149+
| `CLAYDE_GIT_EMAIL` | Git commit author email (required) |
148150
| `CLAYDE_ENABLED` | Set to `true` to activate |
149151
| `CLAYDE_WHITELISTED_USERS` | Comma-separated trusted GitHub usernames |
150152
| `CLAYDE_CLAUDE_BACKEND` | `api` (default) or `cli` |
151153
| `CLAYDE_CLAUDE_API_KEY` | Anthropic API key (required when backend=`api`) |
152154
| `CLAYDE_CLAUDE_MODEL` | Model to use (default: `claude-opus-4-6`) |
155+
156+
---
157+
158+
## Deploying Your Own Instance
159+
160+
Clayde is designed to be deployed by anyone. To run your own instance:
161+
162+
### 1. Create a dedicated bot GitHub account
163+
164+
Create a GitHub account for your bot (e.g. `my-bot`). This is the account that will be assigned issues and open pull requests.
165+
166+
### 2. Create a GitHub Personal Access Token for the bot
167+
168+
From the bot account, create a classic personal access token with the full **`repo`** scope.
169+
170+
### 3. Configure the instance
171+
172+
```bash
173+
mkdir -p data/logs data/repos
174+
cp config.env.template data/config.env
175+
```
176+
177+
Edit `data/config.env`:
178+
179+
```
180+
CLAYDE_GITHUB_TOKEN=github_pat_...
181+
CLAYDE_GITHUB_USERNAME=my-bot
182+
CLAYDE_GIT_EMAIL=my-bot@example.com
183+
CLAYDE_ENABLED=true
184+
CLAYDE_WHITELISTED_USERS=your-username,my-bot
185+
```
186+
187+
### 4. Choose a Claude backend and start
188+
189+
Follow the backend instructions in the [Setup](#setup) section above, then run:
190+
191+
```bash
192+
docker compose up -d
193+
```
194+
195+
### 5. Assign issues to your bot
196+
197+
In any repository the bot has access to, assign issues to the bot account. Clayde will pick them up automatically on the next loop cycle.
198+

config.env.template

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
# Clayde configuration — copy to config.env and fill in values
1+
# Clayde configuration — copy to data/config.env and fill in your values
2+
3+
# GitHub identity: the bot account Clayde runs as
4+
CLAYDE_GITHUB_USERNAME=your-bot-username
25
CLAYDE_GITHUB_TOKEN=
3-
CLAYDE_GITHUB_USERNAME=ClaydeCode
6+
7+
# Git identity for commits (name defaults to CLAYDE_GITHUB_USERNAME if not set)
8+
CLAYDE_GIT_NAME=
9+
CLAYDE_GIT_EMAIL=your-bot@example.com
10+
11+
# Set to true to activate Clayde; any other value causes immediate exit
412
CLAYDE_ENABLED=false
5-
CLAYDE_WHITELISTED_USERS_RAW=max-tet,ClaydeCode
13+
14+
# Comma-separated list of trusted GitHub usernames allowed to approve plans
15+
CLAYDE_WHITELISTED_USERS=your-username,your-bot-username
616

717
# Claude backend: "api" (Anthropic SDK, requires CLAYDE_CLAUDE_API_KEY)
8-
# "cli" (Claude Code CLI, requires OAuth credentials mounted)
18+
# "cli" (Claude Code CLI, requires OAuth credentials mounted)
919
CLAYDE_CLAUDE_BACKEND=api
1020
CLAYDE_CLAUDE_API_KEY=

entrypoint.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
# Configure git identity from environment variables at container startup.
3+
# CLAYDE_GIT_NAME defaults to CLAYDE_GITHUB_USERNAME if not set.
4+
# CLAYDE_GIT_EMAIL is required.
5+
6+
GIT_NAME="${CLAYDE_GIT_NAME:-$CLAYDE_GITHUB_USERNAME}"
7+
GIT_EMAIL="${CLAYDE_GIT_EMAIL}"
8+
9+
if [ -z "$GIT_NAME" ]; then
10+
echo "ERROR: CLAYDE_GIT_NAME (or CLAYDE_GITHUB_USERNAME) must be set" >&2
11+
exit 1
12+
fi
13+
14+
if [ -z "$GIT_EMAIL" ]; then
15+
echo "ERROR: CLAYDE_GIT_EMAIL must be set" >&2
16+
exit 1
17+
fi
18+
19+
git config --global user.name "$GIT_NAME"
20+
git config --global user.email "$GIT_EMAIL"
21+
22+
exec /opt/clayde/.venv/bin/clayde "$@"

src/clayde/config.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@ class Settings(BaseSettings):
2020
)
2121

2222
github_token: str = ""
23-
github_username: str = "ClaydeCode"
23+
github_username: str = ""
2424
enabled: bool = False
25-
whitelisted_users: str = "max-tet,ClaydeCode"
25+
whitelisted_users: str = ""
2626
claude_api_key: str = ""
2727
claude_model: str = "claude-opus-4-6"
2828
claude_backend: str = "api" # "api" or "cli"
29+
git_name: str = ""
30+
git_email: str = ""
31+
32+
@property
33+
def effective_git_name(self) -> str:
34+
return self.git_name or self.github_username
2935

3036
# Claude invocation tuning
3137
claude_tool_loop_timeout_s: int = 1800

tests/test_config.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,24 @@ def test_defaults(self, monkeypatch):
2222
monkeypatch.delenv("CLAYDE_GITHUB_TOKEN", raising=False)
2323
monkeypatch.delenv("CLAYDE_ENABLED", raising=False)
2424
monkeypatch.delenv("CLAYDE_WHITELISTED_USERS", raising=False)
25+
monkeypatch.delenv("CLAYDE_GITHUB_USERNAME", raising=False)
2526
s = Settings(_env_file=None)
2627
assert s.github_token == ""
2728
assert s.enabled is False
28-
assert s.github_username == "ClaydeCode"
29-
assert s.whitelisted_users_list == ["max-tet", "ClaydeCode"]
29+
assert s.github_username == ""
30+
assert s.whitelisted_users_list == []
31+
32+
def test_effective_git_name_falls_back_to_username(self, monkeypatch):
33+
monkeypatch.setenv("CLAYDE_GITHUB_USERNAME", "my-bot")
34+
monkeypatch.delenv("CLAYDE_GIT_NAME", raising=False)
35+
s = Settings(_env_file=None)
36+
assert s.effective_git_name == "my-bot"
37+
38+
def test_effective_git_name_uses_explicit_value(self, monkeypatch):
39+
monkeypatch.setenv("CLAYDE_GITHUB_USERNAME", "my-bot")
40+
monkeypatch.setenv("CLAYDE_GIT_NAME", "My Bot")
41+
s = Settings(_env_file=None)
42+
assert s.effective_git_name == "My Bot"
3043

3144
def test_loads_from_env_file(self, tmp_path, monkeypatch):
3245
env_file = tmp_path / "config.env"

0 commit comments

Comments
 (0)