Skip to content

Commit a0d50e0

Browse files
samroseclaude
andcommitted
feat: add pg-backrest and pgctld to multigres nix-builder-17
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f336ff5 commit a0d50e0

3 files changed

Lines changed: 267 additions & 2 deletions

File tree

Dockerfile-multigres

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin"
3535
WORKDIR /nixpg
3636
COPY . .
3737

38-
RUN nix profile add path:.#psql_17_slim/bin
38+
RUN nix profile add path:.#psql_17_slim/bin path:.#pg-backrest path:.#pgctld
3939

4040
RUN nix store gc
4141

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# pgctld + pgbackrest in multigres images — Implementation Plan
2+
3+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4+
5+
**Goal:** Package `pgctld` (from `github:multigres/multigres`) as a Nix derivation and install both `pgctld` and `pgbackrest` in both multigres Docker image variants (`variant-17` and `variant-orioledb-17`).
6+
7+
**Architecture:** Add `github:multigres/multigres` as a `flake = false` input, build `pgctld` with `buildGoModule` (it is `CGO_ENABLED=0`, pure Go, static), expose as `path:.#pgctld`, and add alongside `pg-backrest` in both nix-builder stages of `Dockerfile-multigres`. The existing per-variant symlink loop in the Dockerfile automatically exposes both binaries at `/usr/bin/` with no further changes.
8+
9+
**Tech Stack:** Nix flakes, `buildGoModule`, Go 1.25 (CGO_ENABLED=0), Alpine 3.21, Dockerfile multi-stage builds.
10+
11+
**Current state of `Dockerfile-multigres`:**
12+
- `nix-builder-17`: already has `pg-backrest` added (partial previous change), needs `pgctld` added
13+
- `nix-builder-orioledb-17`: has neither `pg-backrest` nor `pgctld` yet
14+
15+
---
16+
17+
### Task 1: Add the multigres flake input
18+
19+
**Files:**
20+
- Modify: `flake.nix` (inputs block, after line 33)
21+
22+
**Step 1: Add the two input lines**
23+
24+
In `flake.nix`, inside the `inputs = { ... }` block, add after the existing `nixpkgs-oldstable` line:
25+
26+
```nix
27+
multigres.url = "github:multigres/multigres";
28+
multigres.flake = false;
29+
```
30+
31+
**Step 2: Verify flake evaluates**
32+
33+
```bash
34+
cd /Users/samrose/pg-multigres-image
35+
nix flake show 2>&1 | head -20
36+
```
37+
38+
Expected: output lists packages (no eval error). The `multigres` input will appear in `nix flake metadata`.
39+
40+
**Step 3: Commit**
41+
42+
```bash
43+
git add flake.nix
44+
git commit -m "feat: add multigres flake input for pgctld packaging"
45+
```
46+
47+
---
48+
49+
### Task 2: Create nix/packages/pgctld.nix
50+
51+
**Files:**
52+
- Create: `nix/packages/pgctld.nix`
53+
54+
**Step 1: Create the file with fakeHash**
55+
56+
```nix
57+
{ lib, buildGoModule, multigres-src }:
58+
buildGoModule {
59+
pname = "pgctld";
60+
version = "0.1.0";
61+
src = multigres-src;
62+
subPackages = [ "go/cmd/pgctld" ];
63+
CGO_ENABLED = "0";
64+
ldflags = [
65+
"-w"
66+
"-s"
67+
];
68+
preBuild = ''
69+
cp external/pico/pico.* go/common/web/templates/css/ 2>/dev/null || true
70+
'';
71+
vendorHash = lib.fakeHash;
72+
}
73+
```
74+
75+
**Step 2: Register it in default.nix (needed to build it)**
76+
77+
In `nix/packages/default.nix`, add this line in the `packages = (` block alongside the existing `pg-backrest` line (around line 63):
78+
79+
```nix
80+
pgctld = pkgs.callPackage ./pgctld.nix {
81+
multigres-src = inputs.multigres;
82+
};
83+
```
84+
85+
**Step 3: Run build to get the real vendorHash**
86+
87+
```bash
88+
nix build .#pgctld 2>&1 | grep "got:"
89+
```
90+
91+
Expected: build **fails** with a line like:
92+
```
93+
got: sha256-<base64hash>=
94+
```
95+
96+
Copy that full `sha256-...=` value.
97+
98+
**Step 4: Replace fakeHash with real hash**
99+
100+
In `nix/packages/pgctld.nix`, replace:
101+
```nix
102+
vendorHash = lib.fakeHash;
103+
```
104+
with:
105+
```nix
106+
vendorHash = "sha256-<the-actual-hash-from-step-3>=";
107+
```
108+
109+
**Step 5: Verify the build succeeds**
110+
111+
```bash
112+
nix build .#pgctld
113+
./result/bin/pgctld --version
114+
```
115+
116+
Expected: binary prints version string (e.g. `pgctld version ...`) and exits 0.
117+
If it prints a version or help text, that's fine — the binary exists and is executable.
118+
119+
**Step 6: Commit**
120+
121+
```bash
122+
git add nix/packages/pgctld.nix nix/packages/default.nix
123+
git commit -m "feat: package pgctld from github:multigres/multigres as Nix derivation"
124+
```
125+
126+
---
127+
128+
### Task 3: Update Dockerfile-multigres — nix-builder-17 stage
129+
130+
**Files:**
131+
- Modify: `Dockerfile-multigres` (line 38)
132+
133+
**Step 1: Add pgctld to the nix-builder-17 profile add**
134+
135+
Current line 38:
136+
```dockerfile
137+
RUN nix profile add path:.#psql_17_slim/bin path:.#pg-backrest
138+
```
139+
140+
Change to:
141+
```dockerfile
142+
RUN nix profile add path:.#psql_17_slim/bin path:.#pg-backrest path:.#pgctld
143+
```
144+
145+
**Step 2: Commit**
146+
147+
```bash
148+
git add Dockerfile-multigres
149+
git commit -m "feat: add pg-backrest and pgctld to multigres nix-builder-17"
150+
```
151+
152+
---
153+
154+
### Task 4: Update Dockerfile-multigres — nix-builder-orioledb-17 stage
155+
156+
**Files:**
157+
- Modify: `Dockerfile-multigres` (line 78)
158+
159+
**Step 1: Add both tools to the nix-builder-orioledb-17 profile add**
160+
161+
Current line 78:
162+
```dockerfile
163+
RUN nix profile add path:.#psql_orioledb-17_slim/bin
164+
```
165+
166+
Change to:
167+
```dockerfile
168+
RUN nix profile add path:.#psql_orioledb-17_slim/bin path:.#pg-backrest path:.#pgctld
169+
```
170+
171+
**Step 2: Commit**
172+
173+
```bash
174+
git add Dockerfile-multigres
175+
git commit -m "feat: add pg-backrest and pgctld to multigres nix-builder-orioledb-17"
176+
```
177+
178+
---
179+
180+
### Task 5: Build and verify variant-17
181+
182+
**Step 1: Build the image**
183+
184+
```bash
185+
docker build -f Dockerfile-multigres --target variant-17 -t multigres-17 .
186+
```
187+
188+
Expected: build completes successfully (all stages).
189+
190+
**Step 2: Verify binaries are present**
191+
192+
```bash
193+
docker run --rm multigres-17 which pgctld
194+
docker run --rm multigres-17 which pgbackrest
195+
docker run --rm multigres-17 pgctld --help 2>&1 | head -5
196+
docker run --rm multigres-17 pgbackrest version
197+
```
198+
199+
Expected:
200+
- `which pgctld``/usr/bin/pgctld`
201+
- `which pgbackrest``/usr/bin/pgbackrest`
202+
- `pgctld --help` → prints usage
203+
- `pgbackrest version` → prints version
204+
205+
**Step 3: If any binary is missing**
206+
207+
Check the symlink loop output:
208+
```bash
209+
docker run --rm multigres-17 ls -la /nix/var/nix/profiles/default/bin/ | grep -E "pgctld|pgbackrest"
210+
```
211+
212+
If the nix profile binaries exist there but `/usr/bin/` symlinks are missing, the symlink loop in the variant stage silently skipped them — investigate whether the binary names conflict with existing Alpine binaries.
213+
214+
---
215+
216+
### Task 6: Build and verify variant-orioledb-17
217+
218+
**Step 1: Build the image**
219+
220+
```bash
221+
docker build -f Dockerfile-multigres --target variant-orioledb-17 -t multigres-orioledb-17 .
222+
```
223+
224+
Expected: build completes successfully.
225+
226+
**Step 2: Verify binaries are present**
227+
228+
```bash
229+
docker run --rm multigres-orioledb-17 which pgctld
230+
docker run --rm multigres-orioledb-17 which pgbackrest
231+
docker run --rm multigres-orioledb-17 pgctld --help 2>&1 | head -5
232+
docker run --rm multigres-orioledb-17 pgbackrest version
233+
```
234+
235+
Expected: same as Task 5 Step 2.
236+
237+
**Step 3: Commit if any fixes were needed, then final commit**
238+
239+
```bash
240+
git add -p # stage only what changed
241+
git commit -m "chore: verify pgctld and pgbackrest in both multigres variants"
242+
```
243+
244+
---
245+
246+
## Troubleshooting reference
247+
248+
| Problem | Likely cause | Fix |
249+
|---------|-------------|-----|
250+
| `nix build .#pgctld` fails with "attribute 'multigres' missing" | flake.lock not updated | run `nix flake update multigres` |
251+
| `go build` fails inside nix with "missing generated file" | proto/parser not committed | check `go/pb/` and `go/common/parser/postgres.go` exist (they do — pre-verified) |
252+
| `vendorHash` mismatch | go.sum changed between flake.lock pin and actual repo HEAD | update flake.lock to desired commit then recompute hash |
253+
| Binary present in nix profile but missing from `/usr/bin/` | filename collision with Alpine package | symlink loop uses `2>/dev/null || true`, check `ls /usr/bin/pgctld` manually |
254+
| `preBuild` cp fails | `external/pico/` not in repo (unlikely) | remove the `preBuild` block — pgctld doesn't use web templates |

nix/packages/pgctld.nix

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,22 @@ buildGoModule {
66
subPackages = [ "go/cmd/pgctld" ];
77
env.CGO_ENABLED = "0";
88
ldflags = [
9-
"-w"
109
"-s"
10+
"-w"
1111
];
12+
# The Makefile copies pico CSS assets before go build; pgctld does not use web
13+
# templates so this is a no-op, but kept for safety in case of future imports.
1214
preBuild = ''
1315
cp external/pico/pico.* go/common/web/templates/css/ 2>/dev/null || true
1416
'';
17+
# Tests require a running PostgreSQL instance (integration tests); skip in sandbox.
18+
doCheck = false;
1519
vendorHash = "sha256-0G/l5MlEnyXSoElPbRkn1MaQNCtil3rE/tPZILbhKaA=";
20+
21+
meta = {
22+
description = "PostgreSQL control daemon for Multigres cluster lifecycle management";
23+
mainProgram = "pgctld";
24+
license = lib.licenses.asl20;
25+
platforms = lib.platforms.unix;
26+
};
1627
}

0 commit comments

Comments
 (0)