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
4 changes: 2 additions & 2 deletions .claude/commands/finish-prs.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ cd <worktree>
git merge origin/dash
```

**Merge, never rebase.** If the merge conflicts, do NOT resolve it here — `/github-review-pr` Phase A0 owns conflict resolution and carries the per-file playbook (`lib/kamal/version.rb` → base's side; `Gemfile.lock` → take either side then `bundle install`; `kamal.gemspec` / `bin/release` → whatever `origin/main` has; `proxy/run.rb` → keep `ghcr.io/mhenrixon` and treat a `MINIMUM_VERSION` conflict as a release-ordering question; new multi-host fixtures → `loadbalancer: false`). Abort the merge (`git merge --abort`), and let step 2d handle it — Phase A0 runs first inside that command by design.
**Merge, never rebase.** If the merge conflicts, do NOT resolve it here — `/github-review-pr` Phase A0 owns conflict resolution and carries the per-file playbook (`lib/kamal/version.rb` → base's side; `Gemfile.lock` → take either side then `bundle install`; `kamal.gemspec` / `bin/release` → whatever `origin/main` has; `proxy/run.rb` → keep `ghcr.io/zoolutions` and treat a `MINIMUM_VERSION` conflict as a release-ordering question; new multi-host fixtures → `loadbalancer: false`). Abort the merge (`git merge --abort`), and let step 2d handle it — Phase A0 runs first inside that command by design.

If the merge is clean, commit it (git's default merge message is fine) and continue.

Expand Down Expand Up @@ -149,7 +149,7 @@ If the user merges a PR **out of the planned order**, adapt: drop it from the re
Two fork-specific things worth surfacing once, at the end, rather than fixing mid-queue:

- **Upstream drift.** If several PRs in the queue conflicted against `dash` in the same file, `main` may have moved and `dash` may be behind it. The durable fix is the routine sync in `.claude/rules/upstream-sync.md` (`git checkout main && git merge --ff-only upstream/main`, then `git checkout dash && git merge main`) — a commit to `dash`, so mention it, don't do it unprompted.
- **Release ordering.** If any PR in the queue moves `Kamal::Configuration::Proxy::Run::MINIMUM_VERSION`, the referenced `ghcr.io/mhenrixon/kamal-proxy` tag must already be published — proxy image first, gem second. Flag it before the user merges, because merging a gem PR that names an unpublished proxy tag breaks integration tests on `dash`.
- **Release ordering.** If any PR in the queue moves `Kamal::Configuration::Proxy::Run::MINIMUM_VERSION`, the referenced `ghcr.io/zoolutions/kamal-proxy` tag must already be published — proxy image first, gem second. Flag it before the user merges, because merging a gem PR that names an unpublished proxy tag breaks integration tests on `dash`.

---

Expand Down
4 changes: 2 additions & 2 deletions .claude/commands/github-review-failures.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Look for:
- `NoMethodError: undefined method` -> API change in `Kamal::Commands`/`Kamal::Configuration`
- `Mocha::ExpectationError` -> mock/stub no longer matches the call site
- `expected: X, got: Y` on a proxy version string -> check whether the test hardcoded a proxy tag instead of interpolating `Kamal::Configuration::Proxy::Run::MINIMUM_VERSION`
- Integration test failure pulling `ghcr.io/mhenrixon/kamal-proxy:<tag>` -> the tag isn't published yet (see Known/Expected Failures below), not a code bug
- Integration test failure pulling `ghcr.io/zoolutions/kamal-proxy:<tag>` -> the tag isn't published yet (see Known/Expected Failures below), not a code bug

### golangci-lint / go test failures (proxy)

Expand Down Expand Up @@ -179,7 +179,7 @@ If there are still pending checks, report which checks are running and what was
| Failure | Cause | Action |
|---|---|---|
| `test/commands/builder_test.rb` (2 tests) fail locally, pass in CI | Apple-Silicon-only: host-arch-dependent buildx assertions | Flag as expected on Apple Silicon; do not alter assertions |
| Integration suite fails pulling `ghcr.io/mhenrixon/kamal-proxy:<MINIMUM_VERSION>` | Tag not yet published to ghcr.io, or `MINIMUM_VERSION` was bumped without a matching proxy release | Check `docker buildx imagetools inspect ghcr.io/mhenrixon/kamal-proxy:<tag>`; if unpublished, this is a release-ordering issue, not a code bug — see `.claude/rules/upstream-sync.md` |
| Integration suite fails pulling `ghcr.io/zoolutions/kamal-proxy:<MINIMUM_VERSION>` | Tag not yet published to ghcr.io, or `MINIMUM_VERSION` was bumped without a matching proxy release | Check `docker buildx imagetools inspect ghcr.io/zoolutions/kamal-proxy:<tag>`; if unpublished, this is a release-ordering issue, not a code bug — see `.claude/rules/upstream-sync.md` |
| Any check failing on a PR whose base is `main` | `main` must stay a pristine fast-forward mirror of upstream | Flag it; the PR should retarget `dash` |

---
Expand Down
4 changes: 2 additions & 2 deletions .claude/commands/github-review-pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ gh pr view <PR_NUMBER> --json mergeable,mergeStateStatus,baseRefName
- **`lib/kamal/version.rb`**: take the BASE's side (`dash`'s). The fork version is only ever written by `bin/release-dash` at release time on `dash` — a feature branch never bumps it on purpose; a bump on the branch is accidental.
- **`Gemfile.lock`** (tracked at the repo root): take either side, then run `bundle install` and commit the settled result. Never hand-merge a lockfile. (CI deletes it before the test matrix, but the committed file must still be consistent.)
- **Upstream-owned files** (`kamal.gemspec`, `bin/release`): these must stay byte-identical to upstream — resolve to what `origin/main` (the upstream mirror) has (`git show origin/main:kamal.gemspec`); never hand-merge fork content into them. If a dependency change is involved, mirror it into `dash.gemspec` by hand and check with `diff kamal.gemspec dash.gemspec`.
- **`lib/kamal/configuration/proxy/run.rb`**: keep the `ghcr.io/mhenrixon` repository; a `MINIMUM_VERSION` conflict is a release-ordering question (proxy image first, gem second) — resolve per `.claude/rules/upstream-sync.md` and flag it in the report.
- **`lib/kamal/configuration/proxy/run.rb`**: keep the `ghcr.io/zoolutions` repository; a `MINIMUM_VERSION` conflict is a release-ordering question (proxy image first, gem second) — resolve per `.claude/rules/upstream-sync.md` and flag it in the report.
- **`test/cli/proxy_test.rb`, `test/commands/proxy_test.rb`**: keep the ghcr org and the `#{...MINIMUM_VERSION}` interpolation; adopt the other side's new assertions around them.
- **`test/integration/docker/deployer/setup.sh`**: a shell script — there is no Ruby interpolation here. Keep the ghcr image and set its literal tag equal to `Kamal::Configuration::Proxy::Run::MINIMUM_VERSION` (per the Conflict playbook in `.claude/rules/upstream-sync.md`).
- **`.github/workflows/ci.yml`**: keep the `dash` entry under push branches.
Expand Down Expand Up @@ -118,7 +118,7 @@ gh pr view <PR_NUMBER> --json mergeable,mergeStateStatus,baseRefName
3. **Diagnose root cause per failure.** Common categories on this repo:
- **Rubocop** (`rubocop-rails-omakase`) — style violation, fastest to fix
- **Unit test failure** — check first whether it's one of the two known Apple-Silicon-only `test/commands/builder_test.rb` failures (`.claude/rules/testing.md`); if so, confirm it also fails on a clean checkout of the PR's base and note it as pre-existing, don't "fix" the assertion
- **Integration test failure** — needs Docker + the published proxy image at `ghcr.io/mhenrixon/kamal-proxy:$MINIMUM_VERSION`; if the image tag named by `MINIMUM_VERSION` isn't published yet, that's an environment/ordering issue, not a code bug — see `.claude/rules/upstream-sync.md` release ordering
- **Integration test failure** — needs Docker + the published proxy image at `ghcr.io/zoolutions/kamal-proxy:$MINIMUM_VERSION`; if the image tag named by `MINIMUM_VERSION` isn't published yet, that's an environment/ordering issue, not a code bug — see `.claude/rules/upstream-sync.md` release ordering
- **actionlint / zizmor** — workflow YAML issue in `.github/workflows/*`
- **Multi-host fixture** — a new/changed fixture under `test/fixtures/` with a primary role that has >1 host must set `loadbalancer: false` under `proxy:`, or the loadbalancer auto-activates and the Docker-in-Docker harness can't resolve inner VM hostnames
4. **Fix locally, cheapest first**: rubocop → unit tests → integration/build issues.
Expand Down
4 changes: 2 additions & 2 deletions .claude/commands/lfg.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Create a TaskCreate todo list with specific implementation steps.
2. Read existing patterns in similar CLI commands under `lib/kamal/cli/`
3. Understand dependencies and integration points across the layer cake (`lib/kamal/commander.rb`, `lib/kamal/commands/`, `lib/kamal/configuration/`)
4. Check existing test coverage under `test/` (mirrors `lib/` structure; skip `test/integration` unless the change is deploy-path-sensitive)
5. If touching proxy behavior, review `lib/kamal/configuration/proxy/` — `run.rb` owns `MINIMUM_VERSION` and the fork's default `ghcr.io/mhenrixon/kamal-proxy` repository
5. If touching proxy behavior, review `lib/kamal/configuration/proxy/` — `run.rb` owns `MINIMUM_VERSION` and the fork's default `ghcr.io/zoolutions/kamal-proxy` repository
6. If touching multi-host or load balancing, review `lib/kamal/commands/loadbalancer.rb` and the `loadbalancer:` validation in `lib/kamal/configuration/validator/proxy.rb` — the dash-only loadbalancer auto-activates for any primary role with >1 web host
7. Check `ROADMAP.md` for whether this item is already scoped (evidence-linked anchors, R1-R5 sequencing) — align implementation with the anchor's stated fix location

Expand Down Expand Up @@ -119,7 +119,7 @@ Write the MINIMUM code to make the test pass. Follow project patterns:
| Add a `v*` git tag | Use `dash-v<version>` (gem) or coordinate with proxy's `v<base>.<n>` (image) |
| Skip Thor command conventions | Follow existing `lib/kamal/cli/*.rb` patterns (options, hooks, `Kamal::Cli::Base`) |
| Bypass `Kamal::Commander` for target/config resolution | Route through `KAMAL` singleton (`Kamal::Commander`) |
| Reference upstream proxy repository defaults | Default to `ghcr.io/mhenrixon/kamal-proxy` per `lib/kamal/configuration/proxy/run.rb` |
| Reference upstream proxy repository defaults | Default to `ghcr.io/zoolutions/kamal-proxy` per `lib/kamal/configuration/proxy/run.rb` |
| Add a multi-host integration fixture without opting out | Set `loadbalancer: false` under `proxy:` — the dind harness can't resolve inner VM hostnames |

### 4.3: Refactor
Expand Down
2 changes: 1 addition & 1 deletion .claude/commands/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Investigation tells you what the codebase says; this phase finds what the REQUES

- Develop 2-3 candidate approaches with real tradeoffs. Pick one and say why; record why the others lost.
- The chosen design must respect project invariants: Thor CLI commands stay thin and delegate to `Kamal::Commands::*` builders; configuration parsing/validation stays in `Kamal::Configuration`; remote execution stays behind SSHKit; the loadbalancer auto-activates for any primary role with >1 web host (don't special-case around that without updating the validator); `Kamal::Utils.older_version?`/`Gem::Version` semantics govern proxy version comparisons — never suffix tags.
- Decide the test strategy: minitest + mocha, unit tests under `test/**` (excluding `test/integration`), integration tests only when the change touches real deploy behavior (needs Docker + a published `ghcr.io/mhenrixon/kamal-proxy` image at `MINIMUM_VERSION`). Two builder tests are known-failing on Apple Silicon only — don't plan a fix for those unless that's the task.
- Decide the test strategy: minitest + mocha, unit tests under `test/**` (excluding `test/integration`), integration tests only when the change touches real deploy behavior (needs Docker + a published `ghcr.io/zoolutions/kamal-proxy` image at `MINIMUM_VERSION`). Two builder tests are known-failing on Apple Silicon only — don't plan a fix for those unless that's the task.
- If the change requires a new `MINIMUM_VERSION`, the plan must sequence proxy-repo work before gem-repo work (release ordering is a hard constraint — see `.claude/rules/upstream-sync.md` → Release procedure) and interpolate the version in test expectations rather than hardcoding it.

## Phase 4 — Emit the plan artifact
Expand Down
2 changes: 1 addition & 1 deletion .claude/commands/review-pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Business logic in Cli::* Thor command -> Push logic down into Commander/Co
Direct shell interpolation in Commands::* -> Build args as arrays, let SSHKit/Docker quote them
Raw ENV reads scattered in Configuration -> Route through Kamal::Configuration::Env / config accessors
Hardcoded proxy image tag -> Kamal::Configuration::Proxy::Run::MINIMUM_VERSION
Hardcoded ghcr.io/mhenrixon repo string -> Proxy::Run#repository default / config override
Hardcoded ghcr.io/zoolutions repo string -> Proxy::Run#repository default / config override
New CLI option without Thor `desc`/`option` -> Follow existing Cli::* option declarations
Missing `--parallel`-unsafe rubocop offense -> Run `bundle exec rubocop --parallel` clean
Test hits Docker/network without being under -> Belongs in test/integration, not test/ (unit run
Expand Down
8 changes: 4 additions & 4 deletions .claude/commands/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ docker :login, server,
### Docker Registry / GHCR Credentials

- `Kamal::Commands::Registry#login` skips entirely when `registry_config.local?` — verify no code path logs in with empty/default credentials
- Fork-specific: `ghcr.io/mhenrixon` pulls (`lib/kamal/configuration/proxy/run.rb`, `boot.rb`) use the same registry credential path as the app image — a credential leak here exposes the proxy image pull, not just the app
- Fork-specific: `ghcr.io/zoolutions` pulls (`lib/kamal/configuration/proxy/run.rb`, `boot.rb`) use the same registry credential path as the app image — a credential leak here exposes the proxy image pull, not just the app
- Never persist `docker login` credentials to a file the deploy user doesn't control; `docker logout` (`Kamal::Commands::Registry#logout`) must run in `ensure`/ensure-equivalent blocks for any new command that logs in

### Error Page Upload — Path Traversal
Expand Down Expand Up @@ -109,7 +109,7 @@ ERROR_PAGES_GLOB = "{4??.html,5??.html}"
- [ ] Proxy: request/response buffers are size-capped, not unbounded
- [ ] Proxy: hop-by-hop headers stripped before backend forwarding
- [ ] Proxy: error pages render via `html/template` (escaped), never `text/template`
- [ ] Fork-owned defaults (`MINIMUM_VERSION`, `ghcr.io/mhenrixon` repository) unchanged unless the sync runbook calls for it
- [ ] Fork-owned defaults (`MINIMUM_VERSION`, `ghcr.io/zoolutions` repository) unchanged unless the sync runbook calls for it

## Security Tools

Expand All @@ -120,7 +120,7 @@ bundle exec rubocop --parallel
# Gem: unit tests (no Docker required)
bundle exec ruby -Itest -e 'Dir["test/**/*_test.rb"].grep_v(/integration/).each { |f| require File.expand_path(f) }'

# Gem: full suite incl. integration (needs Docker + published ghcr.io/mhenrixon/kamal-proxy:$MINIMUM_VERSION)
# Gem: full suite incl. integration (needs Docker + published ghcr.io/zoolutions/kamal-proxy:$MINIMUM_VERSION)
bin/test

# Gem: audit shell-escaping coverage in command builders
Expand All @@ -142,7 +142,7 @@ go vet ./...
| `text/template` for proxy error pages | `html/template` — auto-escapes, already in use |
| Unbounded request/response buffering | Size-capped buffer pool (`proxy_buffer_pool.go`) |
| ACME solver answering any domain's challenge | Validate the challenge is for the domain being issued |
| Editing `MINIMUM_VERSION` or `ghcr.io/mhenrixon` defaults casually | Follow `.claude/rules/upstream-sync.md` — proxy image ships before the gem references it |
| Editing `MINIMUM_VERSION` or `ghcr.io/zoolutions` defaults casually | Follow `.claude/rules/upstream-sync.md` — proxy image ships before the gem references it |
| Committing a fix straight to `main` | `main` is fast-forward-only; branch off `dash`, PR into `dash` |

## Handoff
Expand Down
2 changes: 1 addition & 1 deletion .claude/commands/tdd.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ bundle exec rubocop --parallel

### Step 8: Full Suite Before Pushing

Only when the change touches deploy/proxy/boot flow — needs Docker and the published proxy image (`ghcr.io/mhenrixon/kamal-proxy:$MINIMUM_VERSION`):
Only when the change touches deploy/proxy/boot flow — needs Docker and the published proxy image (`ghcr.io/zoolutions/kamal-proxy:$MINIMUM_VERSION`):

```bash
bin/test
Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ Use direct tools when:
## Repo-Specific Notes

- Two repos, one workflow: gem work here, proxy work in `../kamal-proxy` — never assume a single-repo change covers a proxy version bump. Cross-reference `../kamal-proxy/CLAUDE.md`.
- Unit-test-only exploration is fine without Docker; verifying integration behavior requires the full `bin/test` (Docker + published `ghcr.io/mhenrixon/kamal-proxy:$MINIMUM_VERSION`) — don't dispatch an agent to "run integration tests" unless that's actually available.
- Unit-test-only exploration is fine without Docker; verifying integration behavior requires the full `bin/test` (Docker + published `ghcr.io/zoolutions/kamal-proxy:$MINIMUM_VERSION`) — don't dispatch an agent to "run integration tests" unless that's actually available.
- Two builder tests are Apple-Silicon-only failures (host-arch dependent, pass in CI) — don't let an agent chase them as regressions.
- Before dispatching a Plan agent on anything touching `main`, remind it: `main` is fast-forward-only, never commit there.
4 changes: 2 additions & 2 deletions .claude/rules/coding-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ These are on top of the general rules above — see `CLAUDE.md` and `.claude/rul

- **Never edit `kamal.gemspec`, `bin/release`, or other upstream-owned files** — the fork's equivalents are `dash.gemspec` and `bin/release-dash`. Upstream files must stay byte-identical so syncs never conflict.
- **Interpolate `Kamal::Configuration::Proxy::Run::MINIMUM_VERSION` in tests** — never hardcode a proxy tag like `"v0.9.2.1"` in an assertion; see `test/commands/proxy_test.rb`.
- **New code that touches the proxy image org** uses `ghcr.io/mhenrixon/kamal-proxy` (via `Proxy::Run#repository` / `Proxy::Boot#repository_name`), not `basecamp/kamal-proxy`.
- **New code that touches the proxy image org** uses `ghcr.io/zoolutions/kamal-proxy` (via `Proxy::Run#repository` / `Proxy::Boot#repository_name`), not `basecamp/kamal-proxy`.
- **Loadbalancer-only code** (`Kamal::Cli::Proxy#loadbalancer`, `KAMAL.loadbalancer`, `Configuration::Proxy#load_balancing?`) is fork-owned — keep it isolated behind `load_balancing?` checks so it degrades cleanly when unset, since it auto-activates when the primary role has >1 host.

## Testing (Minitest + Mocha, not RSpec)
Expand All @@ -116,7 +116,7 @@ end
```bash
bundle exec ruby -Itest -e 'Dir["test/**/*_test.rb"].grep_v(/integration/).each { |f| require File.expand_path(f) }'
```
- Integration tests (`test/integration`) run real deploys in Docker and need `ghcr.io/mhenrixon/kamal-proxy:$MINIMUM_VERSION` published first. Run the full suite with `bin/test`.
- Integration tests (`test/integration`) run real deploys in Docker and need `ghcr.io/zoolutions/kamal-proxy:$MINIMUM_VERSION` published first. Run the full suite with `bin/test`.
- Two builder tests are known-failing on Apple Silicon only (host-arch dependent) — don't chase them locally, they pass in CI.

## Code Quality Checklist
Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/git-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Two builder tests are known-failing on Apple Silicon only (host-arch dependent)

## Release Ordering — Hard Constraint

**Proxy image before gem, always.** `Kamal::Configuration::Proxy::Run::MINIMUM_VERSION` must name a tag already published at `ghcr.io/mhenrixon/kamal-proxy` before `bin/release-dash` runs — integration tests and `kamal proxy boot` pull it.
**Proxy image before gem, always.** `Kamal::Configuration::Proxy::Run::MINIMUM_VERSION` must name a tag already published at `ghcr.io/zoolutions/kamal-proxy` before `bin/release-dash` runs — integration tests and `kamal proxy boot` pull it.

```bash
# 1. ../kamal-proxy, on dash
Expand Down
Loading