Skip to content

Commit 59f3de9

Browse files
committed
Add Docker build instructions
Signed-off-by: Nicholas K. Dionysopoulos <nicholas@akeeba.com>
1 parent 1512a6b commit 59f3de9

2 files changed

Lines changed: 316 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,5 @@ Language files in `languages/` using `.ini` format. English (GB) is the only off
142142
## Docker
143143

144144
`Dockerfile` uses PHP 8.4 Apache. `docker-compose.yml` for full stack. Alternative FrankenPHP setup in `docker-compose-frankenphp.yml`. Docker detection via absence of `src/.not_docker` file.
145+
146+
When asked to build, test, or publish the Docker image to GHCR, read `assets/Docker Build.md` first — it contains the full step-by-step procedure including prerequisites, local testing with `docker-compose.override.yml`, tagging conventions, multi-arch Buildx commands, and the optional GitHub Actions workflow.

assets/Docker Build.md

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
# Docker Build & GHCR Release Instructions
2+
3+
## Prerequisites
4+
5+
### 1. Docker with Buildx
6+
7+
Buildx is required for multi-architecture builds (`linux/amd64` + `linux/arm64`).
8+
9+
```bash
10+
# Verify Docker is installed and running
11+
docker version
12+
13+
# Verify buildx is available (included in Docker Desktop ≥ 2.1 and Docker Engine ≥ 19.03)
14+
docker buildx version
15+
```
16+
17+
If you need multi-arch builds, create a builder that can cross-compile:
18+
19+
```bash
20+
docker buildx create --name panopticon-builder --use
21+
docker buildx inspect --bootstrap
22+
```
23+
24+
### 2. GitHub CLI (`gh`)
25+
26+
```bash
27+
gh --version # must be ≥ 2.x
28+
gh auth status # must show "Logged in to github.com"
29+
```
30+
31+
If not logged in: `gh auth login`.
32+
33+
### 3. GHCR write access
34+
35+
Your GitHub account (or token) needs `write:packages` scope on `akeeba/panopticon`.
36+
37+
```bash
38+
gh auth status --show-token 2>&1 | grep -i packages
39+
# Expected output includes "write:packages"
40+
```
41+
42+
If the scope is missing, re-authenticate:
43+
44+
```bash
45+
gh auth login --scopes "write:packages,read:packages,repo"
46+
```
47+
48+
### 4. Docker logged in to GHCR
49+
50+
```bash
51+
echo $(gh auth token) | docker login ghcr.io -u $(gh api user --jq .login) --password-stdin
52+
# Expected: "Login Succeeded"
53+
```
54+
55+
### 5. Repository is clean and built
56+
57+
The `.dockerignore` excludes `node_modules/`, build artefacts, and dev files, but `vendor/` and `media/` (compiled assets) **must be present** because the image copies them in.
58+
59+
```bash
60+
# From the repo root
61+
composer install --no-dev --optimize-autoloader
62+
# This also runs npm install, SCSS compilation, and JS transpilation
63+
```
64+
65+
---
66+
67+
## Creating the Image
68+
69+
The `Dockerfile` at the repo root produces an Apache + PHP 8.4 image.
70+
71+
### Single-arch build (local, fast)
72+
73+
```bash
74+
# From the repo root
75+
docker build \
76+
--tag ghcr.io/akeeba/panopticon:latest \
77+
--tag ghcr.io/akeeba/panopticon:$(git describe --tags --abbrev=0) \
78+
.
79+
```
80+
81+
Replace `$(git describe --tags --abbrev=0)` with the version tag manually if not on a tagged commit (e.g. `1.2.3`).
82+
83+
### Multi-arch build (amd64 + arm64, for release)
84+
85+
```bash
86+
docker buildx build \
87+
--platform linux/amd64,linux/arm64 \
88+
--tag ghcr.io/akeeba/panopticon:latest \
89+
--tag ghcr.io/akeeba/panopticon:$(git describe --tags --abbrev=0) \
90+
--provenance false \
91+
.
92+
# Add --push here only when ready to release (see Deploying section)
93+
```
94+
95+
`--provenance false` avoids a Docker manifest quirk that breaks `docker pull` for some clients when using Buildx without pushing.
96+
97+
### Build-time PHP version override
98+
99+
The `Dockerfile` exposes a `PHP_VERSION` build argument (default `8.4`):
100+
101+
```bash
102+
docker build --build-arg PHP_VERSION=8.3 --tag ghcr.io/akeeba/panopticon:8.3 .
103+
```
104+
105+
---
106+
107+
## Testing the Image
108+
109+
### 1. Create the test directory structure
110+
111+
`docker-compose.override.yml` (in the repo root) binds `./docker-testing/` as persistent data volumes instead of named Docker volumes, making data easily inspectable and disposable.
112+
113+
```bash
114+
mkdir -p docker-testing/user_code docker-testing/config docker-testing/db
115+
```
116+
117+
### 2. Create `.env.docker`
118+
119+
The entrypoint reads this file. Create it from the distributed example:
120+
121+
```bash
122+
cp .env.dist .env.docker
123+
```
124+
125+
Then edit `.env.docker` with at minimum:
126+
127+
```ini
128+
# Database (must match what the mysql container uses)
129+
MYSQL_DATABASE=panopticon
130+
MYSQL_USER=panopticon
131+
MYSQL_PASSWORD=panopticon
132+
MYSQL_ROOT_PASSWORD=root
133+
134+
# Panopticon bootstrap
135+
PANOPTICON_DB_HOST=mysql
136+
PANOPTICON_DB_PREFIX=pnptc_
137+
PANOPTICON_USING_ENV=0
138+
139+
# Number of parallel task:run CRON workers
140+
PANOPTICON_CRON_JOBS=2
141+
142+
# Admin account auto-created on first run
143+
ADMIN_USERNAME=admin
144+
ADMIN_PASSWORD=adminpassword
145+
ADMIN_NAME=Administrator
146+
ADMIN_EMAIL=admin@example.com
147+
```
148+
149+
### 3. Start the stack
150+
151+
```bash
152+
# docker-compose.override.yml is automatically merged by docker compose
153+
docker compose up --build
154+
```
155+
156+
Watch the logs until you see `Starting Apache` — the entrypoint has finished setup.
157+
158+
### 4. Verify
159+
160+
```bash
161+
# Web UI
162+
open http://localhost:4280
163+
164+
# CLI smoke test
165+
docker exec panopticon_php php cli/panopticon.php list
166+
167+
# Check cron is running
168+
docker exec panopticon_php crontab -u panopticon -l
169+
```
170+
171+
### 5. Test the pre-built image (without rebuilding)
172+
173+
Edit `docker-compose.yml`: comment out the `build:` block and uncomment the `image:` line:
174+
175+
```yaml
176+
php:
177+
image: ghcr.io/akeeba/panopticon:latest
178+
# build: ...
179+
```
180+
181+
Then:
182+
183+
```bash
184+
docker compose up
185+
```
186+
187+
This confirms the image works end-to-end without any local source code.
188+
189+
### 6. Tear down
190+
191+
```bash
192+
docker compose down
193+
rm -rf docker-testing/db docker-testing/config
194+
```
195+
196+
---
197+
198+
## Deploying the Image to GHCR
199+
200+
### Tag conventions
201+
202+
| Tag | Meaning |
203+
|---|---|
204+
| `latest` | Most recent stable release |
205+
| `1.2.3` | Exact version |
206+
| `1.2` | Latest patch for minor series (optional) |
207+
208+
### Push a single-arch image (quick path)
209+
210+
```bash
211+
VERSION=$(git describe --tags --abbrev=0) # e.g. 1.2.3
212+
213+
docker build \
214+
--tag ghcr.io/akeeba/panopticon:${VERSION} \
215+
--tag ghcr.io/akeeba/panopticon:latest \
216+
.
217+
218+
docker push ghcr.io/akeeba/panopticon:${VERSION}
219+
docker push ghcr.io/akeeba/panopticon:latest
220+
```
221+
222+
### Push a multi-arch image (release path)
223+
224+
Buildx builds and pushes in one step. Use this for all public releases:
225+
226+
```bash
227+
VERSION=$(git describe --tags --abbrev=0)
228+
229+
docker buildx build \
230+
--platform linux/amd64,linux/arm64 \
231+
--tag ghcr.io/akeeba/panopticon:${VERSION} \
232+
--tag ghcr.io/akeeba/panopticon:latest \
233+
--provenance false \
234+
--push \
235+
.
236+
```
237+
238+
`--push` sends the multi-arch manifest directly to GHCR without loading it into the local Docker daemon.
239+
240+
### Verify the push
241+
242+
```bash
243+
# Inspect the manifest
244+
docker buildx imagetools inspect ghcr.io/akeeba/panopticon:latest
245+
246+
# Pull and run a quick sanity check
247+
docker run --rm ghcr.io/akeeba/panopticon:latest php --version
248+
```
249+
250+
### Make the package public (first time only)
251+
252+
GHCR packages are private by default. To make it publicly pullable:
253+
254+
1. Go to `https://github.com/akeeba/panopticon/pkgs/container/panopticon`
255+
2. Click **Package settings**
256+
3. Under **Danger Zone**, change visibility to **Public**
257+
258+
### Automate with GitHub Actions (optional)
259+
260+
To trigger a build-and-push automatically on every new tag, add `.github/workflows/docker.yml`:
261+
262+
```yaml
263+
name: Publish Docker image
264+
265+
on:
266+
push:
267+
tags: ['*']
268+
269+
jobs:
270+
push:
271+
runs-on: ubuntu-latest
272+
permissions:
273+
packages: write
274+
contents: read
275+
steps:
276+
- uses: actions/checkout@v4
277+
278+
- name: Set up QEMU (for arm64 emulation)
279+
uses: docker/setup-qemu-action@v3
280+
281+
- name: Set up Buildx
282+
uses: docker/setup-buildx-action@v3
283+
284+
- name: Log in to GHCR
285+
uses: docker/login-action@v3
286+
with:
287+
registry: ghcr.io
288+
username: ${{ github.actor }}
289+
password: ${{ secrets.GITHUB_TOKEN }}
290+
291+
- name: Install PHP dependencies
292+
run: composer install --no-dev --optimize-autoloader
293+
294+
- name: Extract tags
295+
id: meta
296+
uses: docker/metadata-action@v5
297+
with:
298+
images: ghcr.io/akeeba/panopticon
299+
tags: |
300+
type=semver,pattern={{version}}
301+
type=semver,pattern={{major}}.{{minor}}
302+
type=raw,value=latest
303+
304+
- name: Build and push
305+
uses: docker/build-push-action@v6
306+
with:
307+
context: .
308+
platforms: linux/amd64,linux/arm64
309+
push: true
310+
tags: ${{ steps.meta.outputs.tags }}
311+
provenance: false
312+
```
313+
314+
Push this file, then the next `git push --tags` will trigger it automatically.

0 commit comments

Comments
 (0)