Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Loading