Skip to content

Commit 2092d8c

Browse files
committed
docker: integrate chatmail/docker submodule
Add Docker Compose setup documentation and CI/CD pipeline for automated Docker image building and testing. All Docker-specific files (Dockerfile, compose configs, init scripts) are managed in the chatmail/docker submodule at commit 73523173. Files added: - .dockerignore: build context filter - .gitmodules: docker submodule reference - doc/source/docker.rst: comprehensive Docker installation guide - .github/workflows: Docker build and test jobs in CI pipeline Files modified: - .gitignore: add docker data directories - doc/source/getting_started.rst: link to Docker documentation - doc/source/index.rst: add Docker installation section
1 parent d96259b commit 2092d8c

9 files changed

Lines changed: 693 additions & 4 deletions

File tree

.dockerignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
data/
2+
venv/
3+
__pycache__
4+
*.pyc
5+
*.orig
6+
*.ini
7+
.pytest_cache
8+
.env
9+
10+
# Slim build context — .git/ alone can be 100s of MB
11+
.git
12+
.github/
13+
docs/
14+
tests/
15+
16+
# Exclude markdown files but keep www/src/*.md (used by WebsiteDeployer)
17+
*.md
18+
!www/**/*.md

.github/workflows/test-and-deploy-ipv4only.yaml

Lines changed: 198 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,77 @@ on:
44
push:
55
branches:
66
- main
7+
- j4n/docker-pr
78
pull_request:
89
paths-ignore:
910
- 'scripts/**'
1011
- '**/README.md'
1112
- 'CHANGELOG.md'
1213
- 'LICENSE'
1314

15+
env:
16+
REGISTRY: ghcr.io
17+
IMAGE_NAME: ${{ github.repository }}
18+
1419
jobs:
20+
build-docker:
21+
name: Build Docker image
22+
runs-on: ubuntu-latest
23+
permissions:
24+
contents: read
25+
packages: write
26+
outputs:
27+
image: ${{ steps.image-ref.outputs.image }}
28+
steps:
29+
- uses: actions/checkout@v4
30+
with:
31+
submodules: true
32+
33+
- name: Set up Docker Buildx
34+
uses: docker/setup-buildx-action@v3
35+
36+
- name: Login to GHCR
37+
if: github.event_name == 'push'
38+
uses: docker/login-action@v3
39+
with:
40+
registry: ${{ env.REGISTRY }}
41+
username: ${{ github.actor }}
42+
password: ${{ secrets.GITHUB_TOKEN }}
43+
44+
- name: Extract metadata (tags, labels)
45+
id: meta
46+
uses: docker/metadata-action@v5
47+
with:
48+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
49+
tags: |
50+
# Tagged releases: v1.2.3 -> :1.2.3, :1.2, :latest
51+
type=semver,pattern={{version}}
52+
type=semver,pattern={{major}}.{{minor}}
53+
# Branch pushes: foo/docker-pr -> :foo-docker-pr
54+
type=ref,event=branch
55+
# Always: :sha-<hash>
56+
type=sha
57+
58+
- name: Build and push
59+
uses: docker/build-push-action@v6
60+
with:
61+
context: .
62+
file: docker/chatmail_relay.dockerfile
63+
push: ${{ github.event_name == 'push' }}
64+
tags: ${{ steps.meta.outputs.tags }}
65+
labels: ${{ steps.meta.outputs.labels }}
66+
cache-from: type=gha
67+
cache-to: type=gha,mode=max
68+
build-args: |
69+
GIT_HASH=${{ github.sha }}
70+
71+
- name: Output image reference
72+
id: image-ref
73+
run: |
74+
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
75+
IMAGE="${{ env.REGISTRY }}/$(echo "${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]'):sha-${SHORT_SHA}"
76+
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
77+
1578
deploy:
1679
name: deploy on staging-ipv4.testrun.org, and run tests
1780
runs-on: ubuntu-latest
@@ -22,6 +85,8 @@ jobs:
2285
concurrency: staging-ipv4.testrun.org
2386
steps:
2487
- uses: actions/checkout@v4
88+
with:
89+
submodules: true
2590

2691
- name: prepare SSH
2792
run: |
@@ -55,6 +120,7 @@ jobs:
55120
run: echo venv/bin >>$GITHUB_PATH
56121

57122
- name: upload TLS cert after rebuilding
123+
id: wait-for-vps
58124
run: |
59125
echo " --- wait until staging-ipv4.testrun.org VPS is rebuilt --- "
60126
rm ~/.ssh/known_hosts
@@ -68,8 +134,8 @@ jobs:
68134
rsync -avz dkimkeys-restore/dkimkeys root@staging-ipv4.testrun.org:/etc/ || true
69135
ssh -o StrictHostKeyChecking=accept-new -v root@staging-ipv4.testrun.org chown root:root -R /var/lib/acme || true
70136
71-
- name: run deploy-chatmail offline tests
72-
run: pytest --pyargs cmdeploy
137+
- name: run deploy-chatmail offline tests
138+
run: pytest --pyargs cmdeploy
73139

74140
- name: setup dependencies
75141
run: |
@@ -102,3 +168,133 @@ jobs:
102168
- name: cmdeploy dns
103169
run: ssh root@staging-ipv4.testrun.org "cd relay && scripts/cmdeploy dns -v --ssh-host localhost"
104170

171+
# --- Docker deploy (push only, runs even if bare failed) ---
172+
173+
- name: stop bare services
174+
if: >-
175+
!cancelled() && github.event_name == 'push'
176+
&& steps.wait-for-vps.outcome == 'success'
177+
run: |
178+
ssh root@staging-ipv4.testrun.org 'systemctl stop postfix dovecot nginx opendkim unbound filtermail doveauth chatmail-metadata iroh-relay mtail fcgiwrap acmetool 2>/dev/null || true'
179+
180+
- name: install Docker on VPS
181+
if: >-
182+
!cancelled() && github.event_name == 'push'
183+
&& steps.wait-for-vps.outcome == 'success'
184+
run: |
185+
ssh root@staging-ipv4.testrun.org 'apt-get update && apt-get install -y ca-certificates curl'
186+
ssh root@staging-ipv4.testrun.org 'install -m 0755 -d /etc/apt/keyrings'
187+
ssh root@staging-ipv4.testrun.org 'curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc && chmod a+r /etc/apt/keyrings/docker.asc'
188+
ssh root@staging-ipv4.testrun.org 'echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo $VERSION_CODENAME) stable" > /etc/apt/sources.list.d/docker.list'
189+
ssh root@staging-ipv4.testrun.org 'apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin'
190+
191+
- name: prepare Docker bind mounts
192+
if: >-
193+
!cancelled() && github.event_name == 'push'
194+
&& steps.wait-for-vps.outcome == 'success'
195+
run: |
196+
ssh root@staging-ipv4.testrun.org 'mkdir -p /srv/chatmail/certs /srv/chatmail/dkim'
197+
ssh root@staging-ipv4.testrun.org 'cp -a /var/lib/acme/. /srv/chatmail/certs/ && cp -a /etc/dkimkeys/. /srv/chatmail/dkim/' || true
198+
199+
- name: upload chatmail.ini for Docker
200+
if: >-
201+
!cancelled() && github.event_name == 'push'
202+
&& steps.wait-for-vps.outcome == 'success'
203+
run: |
204+
# Reuse chatmail.ini already created by the bare-metal deploy steps
205+
ssh root@staging-ipv4.testrun.org "cp relay/chatmail.ini /srv/chatmail/chatmail.ini"
206+
207+
- name: deploy with Docker
208+
if: >-
209+
!cancelled() && github.event_name == 'push'
210+
&& steps.wait-for-vps.outcome == 'success'
211+
run: |
212+
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
213+
GHCR_IMAGE="${{ env.REGISTRY }}/$(echo "${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]'):sha-${SHORT_SHA}"
214+
rsync -avz --exclude='.git' --exclude='venv' --exclude='__pycache__' ./ root@staging-ipv4.testrun.org:/srv/chatmail/relay/
215+
# Login to GHCR on VPS and pull pre-built image
216+
echo "${{ secrets.GITHUB_TOKEN }}" | ssh root@staging-ipv4.testrun.org 'docker login ghcr.io -u ${{ github.actor }} --password-stdin'
217+
ssh root@staging-ipv4.testrun.org "docker pull ${GHCR_IMAGE}"
218+
ssh root@staging-ipv4.testrun.org "cd /srv/chatmail/relay && CHATMAIL_IMAGE=${GHCR_IMAGE} MAIL_DOMAIN=staging-ipv4.testrun.org docker compose -f docker/docker-compose.yaml -f docker/docker-compose.ci.yaml up -d"
219+
220+
- name: wait for container healthy
221+
if: >-
222+
!cancelled() && github.event_name == 'push'
223+
&& steps.wait-for-vps.outcome == 'success'
224+
run: |
225+
# Stream journald inside the container
226+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail journalctl -f --no-pager' &
227+
LOG_PID=$!
228+
trap "kill $LOG_PID 2>/dev/null || true" EXIT
229+
for i in $(seq 1 60); do
230+
status=$(ssh root@staging-ipv4.testrun.org 'docker inspect --format={{.State.Health.Status}} chatmail 2>/dev/null' || echo "missing")
231+
echo " [$i/60] status=$status"
232+
if [ "$status" = "healthy" ]; then
233+
echo "Container is healthy."
234+
exit 0
235+
fi
236+
if [ "$status" = "unhealthy" ]; then
237+
echo "Container is unhealthy!"
238+
break
239+
fi
240+
sleep 5
241+
done
242+
echo "Container did not become healthy."
243+
kill $LOG_PID 2>/dev/null || true
244+
echo "--- failed units ---"
245+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail systemctl --failed --no-pager' || true
246+
echo "--- service logs ---"
247+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail journalctl -u dovecot -u postfix -u nginx -u unbound --no-pager -n 50' || true
248+
echo "--- listening ports ---"
249+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail ss -tlnp' || true
250+
echo "--- chatmail.ini ---"
251+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail cat /etc/chatmail/chatmail.ini' || true
252+
exit 1
253+
254+
- name: show container state
255+
if: >-
256+
!cancelled() && github.event_name == 'push'
257+
&& steps.wait-for-vps.outcome == 'success'
258+
run: |
259+
echo "--- listening ports ---"
260+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail ss -tlnp'
261+
echo "--- chatmail.ini ---"
262+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail cat /etc/chatmail/chatmail.ini'
263+
264+
- name: Docker integration tests
265+
if: >-
266+
!cancelled() && github.event_name == 'push'
267+
&& steps.wait-for-vps.outcome == 'success'
268+
run: |
269+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail cmdeploy test --slow --ssh-host @local'
270+
271+
- name: Docker DNS
272+
if: >-
273+
!cancelled() && github.event_name == 'push'
274+
&& steps.wait-for-vps.outcome == 'success'
275+
run: |
276+
# Reset zone file in case bare DNS already appended to it
277+
git checkout .github/workflows/staging-ipv4.testrun.org-default.zone
278+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail chown opendkim:opendkim -R /etc/dkimkeys'
279+
ssh root@staging-ipv4.testrun.org 'docker exec chatmail cmdeploy dns --ssh-host @local --zonefile /opt/chatmail/staging.zone --verbose'
280+
ssh root@staging-ipv4.testrun.org 'docker cp chatmail:/opt/chatmail/staging.zone /tmp/staging.zone'
281+
scp root@staging-ipv4.testrun.org:/tmp/staging.zone staging-generated.zone
282+
cat staging-generated.zone >> .github/workflows/staging-ipv4.testrun.org-default.zone
283+
cat .github/workflows/staging-ipv4.testrun.org-default.zone
284+
scp .github/workflows/staging-ipv4.testrun.org-default.zone root@ns.testrun.org:/etc/nsd/staging-ipv4.testrun.org.zone
285+
ssh root@ns.testrun.org nsd-checkzone staging-ipv4.testrun.org /etc/nsd/staging-ipv4.testrun.org.zone
286+
ssh root@ns.testrun.org systemctl reload nsd
287+
288+
- name: Docker final DNS check
289+
if: >-
290+
!cancelled() && github.event_name == 'push'
291+
&& steps.wait-for-vps.outcome == 'success'
292+
run: ssh root@staging-ipv4.testrun.org 'docker exec chatmail cmdeploy dns -v --ssh-host @local'
293+
294+
# --- Cleanup ---
295+
296+
- name: add SSH keys
297+
if: >-
298+
!cancelled()
299+
&& steps.wait-for-vps.outcome == 'success'
300+
run: ssh root@staging-ipv4.testrun.org 'curl -s https://github.com/hpk42.keys https://github.com/j4n.keys >> .ssh/authorized_keys'

0 commit comments

Comments
 (0)