Skip to content

Commit 3aec5cf

Browse files
mariomeyerclaude
andcommitted
feat: multi-arch Docker build (amd64 + arm64) and README rewrite
Add QEMU + buildx multi-platform support for linux/amd64 and linux/arm64 so the image runs on any k8s provider. Rewrite README with ghcr.io pull instructions, k8s Job example, all env vars, output artifacts, and monitoring docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e350420 commit 3aec5cf

2 files changed

Lines changed: 186 additions & 43 deletions

File tree

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ jobs:
6969
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
7070
fi
7171
72+
- uses: docker/setup-qemu-action@v3
73+
7274
- uses: docker/setup-buildx-action@v3
7375

7476
- name: Log in to GHCR
@@ -94,6 +96,7 @@ jobs:
9496
uses: docker/build-push-action@v6
9597
with:
9698
context: .
99+
platforms: linux/amd64,linux/arm64
97100
push: true
98101
tags: ${{ steps.meta.outputs.tags }}
99102
labels: ${{ steps.meta.outputs.labels }}

README.md

Lines changed: 183 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,217 @@
11
# Laravel Upgrade Agent
22

3-
Disposable Docker image that runs Claude Code autonomously to upgrade any Laravel application.
3+
Disposable Docker image that runs [Claude Code](https://docs.anthropic.com/en/docs/claude-code) autonomously to upgrade any Laravel application. Point it at a repo, tell it the target version, and it produces an upgrade branch with one commit per phase.
44

5-
## How It Works
6-
7-
1. Clones your repo, creates an `upgrade/laravel-{version}` branch
8-
2. Runs baseline verification (tests, routes, build)
9-
3. Drops upgrade plan, checklist, and agent guidelines into the repo
10-
4. Launches Claude Code with full permissions via a restart loop ("Ralph loop")
11-
5. Claude works through 6 upgrade phases, committing after each
12-
6. Pushes the branch when done; artifacts copied to `/output`
13-
14-
## Usage
5+
**Multi-arch:** `linux/amd64` and `linux/arm64` — runs on any major cloud/k8s provider.
156

16-
### With Claude Max (recommended)
7+
## Quick Start
178

18-
Generate a token with `claude setup-token`, then:
9+
### Pull from GHCR (recommended)
1910

2011
```bash
21-
docker build -t laravel-upgrade-agent .
22-
2312
docker run --rm \
24-
-e REPO_URL=git@github.com:org/repo.git \
25-
-e TARGET_LARAVEL=13 \
13+
-e REPO_URL=git@github.com:your-org/your-app.git \
14+
-e TARGET_LARAVEL=12 \
2615
-e CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN \
2716
-e GIT_SSH_KEY_B64=$(base64 < ~/.ssh/deploy_key) \
2817
-v ./output:/output \
29-
laravel-upgrade-agent
18+
ghcr.io/reyemtech/laravel-upgrade-agent:latest
3019
```
3120

32-
### With Anthropic API Key
21+
### Build locally
3322

3423
```bash
24+
docker build -t laravel-upgrade-agent .
25+
3526
docker run --rm \
36-
-e REPO_URL=git@github.com:org/repo.git \
37-
-e TARGET_LARAVEL=13 \
38-
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
27+
-e REPO_URL=git@github.com:your-org/your-app.git \
28+
-e TARGET_LARAVEL=12 \
29+
-e CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN \
3930
-e GIT_SSH_KEY_B64=$(base64 < ~/.ssh/deploy_key) \
4031
-v ./output:/output \
4132
laravel-upgrade-agent
4233
```
4334

35+
## How It Works
36+
37+
1. Clones your repo, creates an `upgrade/laravel-{version}` branch
38+
2. Installs dependencies, runs baseline verification
39+
3. Runs **recon** — analyzes package usage, component counts, test suite shape
40+
4. Fetches the official Laravel upgrade guide for the target version
41+
5. Launches Claude Code inside a restart loop ("Ralph loop")
42+
6. Claude works through **6 upgrade phases**, committing after each
43+
7. Captures before/after dependency snapshots for review
44+
8. Pushes the branch (optionally creates a PR with a generated changelog)
45+
9. Writes structured results to `/output`
46+
47+
## Upgrade Phases
48+
49+
| Phase | What it does |
50+
|-------|-------------|
51+
| 1. Core Framework | `laravel/framework` to target version, fix breaking changes |
52+
| 2. First-Party Packages | Horizon, Telescope, Sanctum, Breeze, Pennant, etc. |
53+
| 3. Filament + Livewire | Major version bumps (Filament v4/v5, Flux) |
54+
| 4. Third-Party Composer | Remaining Composer packages |
55+
| 5. NPM + Frontend | Tailwind v4, Vite, build tooling |
56+
| 6. Config Drift | Reconcile config files against latest Laravel stubs |
57+
58+
Unused packages are **removed** instead of upgraded. Each phase runs verification before committing.
59+
60+
## Authentication
61+
62+
You need **one** of:
63+
64+
### Claude Max (recommended)
65+
66+
Generate a token with `claude setup-token`, then pass `CLAUDE_CODE_OAUTH_TOKEN`.
67+
68+
### Anthropic API Key
69+
70+
Pass `ANTHROPIC_API_KEY` from [console.anthropic.com](https://console.anthropic.com).
71+
4472
## Environment Variables
4573

4674
| Variable | Required | Default | Description |
4775
|----------|----------|---------|-------------|
4876
| `REPO_URL` | Yes || Git clone URL (SSH) |
49-
| `TARGET_LARAVEL` | Yes || Target major version (e.g., `13`) |
50-
| `CLAUDE_CODE_OAUTH_TOKEN` | One of || Claude Max token (from `claude setup-token`) |
51-
| `ANTHROPIC_API_KEY` | One of || Anthropic API key (from console.anthropic.com) |
77+
| `TARGET_LARAVEL` | Yes || Target major version (e.g., `12`, `13`) |
78+
| `CLAUDE_CODE_OAUTH_TOKEN` | One of || Claude Max token |
79+
| `ANTHROPIC_API_KEY` | One of || Anthropic API key |
5280
| `GIT_SSH_KEY_B64` | Yes || Base64-encoded deploy key |
5381
| `GIT_PUSH` | No | `true` | Push branch on completion |
54-
| `UPGRADE_FILAMENT` | No | `true` | Include Filament upgrade phase |
55-
| `MAX_RESTARTS` | No | `5` | Max Claude Code restarts |
56-
57-
## Upgrade Phases
58-
59-
1. **Core Framework**`laravel/framework` to target version
60-
2. **First-Party Packages** — Horizon, Telescope, Sanctum, etc.
61-
3. **Filament + Livewire** — major version bumps
62-
4. **Third-Party Composer** — remaining packages
63-
5. **NPM + Frontend** — deps + build
64-
6. **Config Drift** — reconcile against stubs
82+
| `GH_TOKEN` | No || GitHub token — creates a PR with changelog as body |
83+
| `BRANCH_SUFFIX` | No || Append to branch name (e.g., `2026-02-26` -> `upgrade/laravel-12-2026-02-26`) |
84+
| `MAX_RESTARTS` | No | `5` | Max Claude Code restart attempts |
85+
| `MAX_TURNS` | No | `200` | Max agentic turns per Claude Code session |
6586

6687
## Output
6788

6889
After a run, the `/output` volume contains:
6990

70-
- `baseline.log` — pre-upgrade test results
71-
- `run-log.md` — agent decision log
72-
- `checklist.yaml` — final phase statuses
73-
- `plan.md` — the upgrade plan used
74-
- `commits.log` — all commits on the upgrade branch
91+
| File | Description |
92+
|------|-------------|
93+
| `result.json` | Structured outcome — `success` or `incomplete`, phase counts, elapsed time |
94+
| `status.json` | Last status update (poll this for monitoring) |
95+
| `changelog.md` | Agent-maintained changelog (also used as PR body) |
96+
| `before-composer.json` | Pre-upgrade Composer packages |
97+
| `after-composer.json` | Post-upgrade Composer packages |
98+
| `before-npm.json` / `after-npm.json` | Pre/post NPM packages |
99+
| `before-versions.txt` / `after-versions.txt` | Pre/post Laravel/PHP versions |
100+
| `run-log.md` | Agent decision log |
101+
| `checklist.yaml` | Final phase statuses |
102+
| `commits.log` | Git log of the upgrade branch |
103+
| `baseline.log` | Pre-upgrade verification output |
104+
| `recon.log` | Repo analysis output |
105+
106+
### Monitoring a running container
107+
108+
```bash
109+
# Watch status
110+
watch -n5 cat output/status.json
111+
112+
# Check result when done
113+
cat output/result.json
114+
# {"outcome":"success","exit_code":0,"total_phases":6,"completed":6,"failed":0,...}
115+
116+
# Compare dependency changes
117+
diff <(jq -r '.installed[].name' output/before-composer.json | sort) \
118+
<(jq -r '.installed[].name' output/after-composer.json | sort)
119+
```
120+
121+
## Advanced Usage
122+
123+
### Auto-create PR
124+
125+
Pass a GitHub token to automatically create a pull request with the changelog as the body:
126+
127+
```bash
128+
docker run --rm \
129+
-e REPO_URL=git@github.com:your-org/your-app.git \
130+
-e TARGET_LARAVEL=12 \
131+
-e GH_TOKEN=$GH_TOKEN \
132+
-e CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN \
133+
-e GIT_SSH_KEY_B64=$(base64 < ~/.ssh/deploy_key) \
134+
-v ./output:/output \
135+
ghcr.io/reyemtech/laravel-upgrade-agent:latest
136+
```
137+
138+
### Repeat runs (avoid branch collision)
139+
140+
```bash
141+
docker run --rm \
142+
-e REPO_URL=git@github.com:your-org/your-app.git \
143+
-e TARGET_LARAVEL=12 \
144+
-e BRANCH_SUFFIX=$(date +%Y-%m-%d) \
145+
-e CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN \
146+
-e GIT_SSH_KEY_B64=$(base64 < ~/.ssh/deploy_key) \
147+
-v ./output:/output \
148+
ghcr.io/reyemtech/laravel-upgrade-agent:latest
149+
```
150+
151+
### Dry run (no push)
152+
153+
```bash
154+
docker run --rm \
155+
-e REPO_URL=git@github.com:your-org/your-app.git \
156+
-e TARGET_LARAVEL=12 \
157+
-e GIT_PUSH=false \
158+
-e CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN \
159+
-e GIT_SSH_KEY_B64=$(base64 < ~/.ssh/deploy_key) \
160+
-v ./output:/output \
161+
ghcr.io/reyemtech/laravel-upgrade-agent:latest
162+
```
163+
164+
### Run on Kubernetes
165+
166+
```yaml
167+
apiVersion: batch/v1
168+
kind: Job
169+
metadata:
170+
name: laravel-upgrade
171+
spec:
172+
template:
173+
spec:
174+
containers:
175+
- name: upgrade-agent
176+
image: ghcr.io/reyemtech/laravel-upgrade-agent:latest
177+
env:
178+
- name: REPO_URL
179+
value: "git@github.com:your-org/your-app.git"
180+
- name: TARGET_LARAVEL
181+
value: "12"
182+
- name: CLAUDE_CODE_OAUTH_TOKEN
183+
valueFrom:
184+
secretKeyRef:
185+
name: upgrade-agent-secrets
186+
key: claude-token
187+
- name: GIT_SSH_KEY_B64
188+
valueFrom:
189+
secretKeyRef:
190+
name: upgrade-agent-secrets
191+
key: deploy-key-b64
192+
- name: GH_TOKEN
193+
valueFrom:
194+
secretKeyRef:
195+
name: upgrade-agent-secrets
196+
key: gh-token
197+
volumeMounts:
198+
- name: output
199+
mountPath: /output
200+
volumes:
201+
- name: output
202+
emptyDir: {}
203+
restartPolicy: Never
204+
backoffLimit: 0
205+
```
206+
207+
## How the Agent Stays on Track
208+
209+
- **Recon before action** — `recon.sh` maps the repo (package usage, component counts, test shape) so the agent can plan ahead
210+
- **Three-file memory** — `plan.md`, `checklist.yaml`, and `run-log.md` survive context compaction and restarts
211+
- **Ralph loop** — if Claude Code exits before the checklist is complete, it restarts with full context (up to `MAX_RESTARTS` times)
212+
- **Loop breaker** — after 3 failed attempts on the same error, the agent logs the failure, marks the phase as `failed`, and moves on
213+
- **Verification gates** — fast verification after every change, full verification before each phase commit
214+
- **No CLAUDE.md overwrite** — upgrade instructions go to `.upgrade/CLAUDE.md`; your project's own `CLAUDE.md` is preserved
75215

76216
## Security
77217

@@ -80,6 +220,6 @@ After a run, the `/output` volume contains:
80220
- Container is ephemeral — destroyed after run
81221
- Review the upgrade branch before merging
82222

83-
## Ralph Loop
223+
## License
84224

85-
The agent runs inside a restart loop. If Claude Code exits before the checklist is complete, it restarts with full context (reads `run-log.md` and `checklist.yaml` on startup). Max restarts default to 5.
225+
MIT

0 commit comments

Comments
 (0)