Skip to content

Commit 2b322ff

Browse files
committed
ci: Docker-in-LXC support for lxc-test workflow
- Add cmlxc_ref input to test feature branches - Disable AppArmor for Docker-in-LXC systemd support - Cache localchat-docker image (strip Docker images before export) - Split cache into restore/save for better failure handling - Per-service failure diagnostics (dovecot, postfix, failed units) - install incus-base instead of full incus package - trimmed=: normalise whitespace via xargs before eval so indented commands in the multiline cmlxc_commands input parse correctly and display cleanly in CI ::group:: labels - get_service_logs.sh: debug logging calls /usr/local/sbin/get-service-logs (installed via COPY in the Docker image) which dumps per-service journalctl output, failed units, dovecot config, and TLS cert paths
1 parent 8379740 commit 2b322ff

2 files changed

Lines changed: 69 additions & 16 deletions

File tree

.github/workflows/lxc-test.yml

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ jobs:
4343
run: |
4444
i=1
4545
while IFS= read -r line || [ -n "$line" ]; do
46-
[[ "$line" =~ ^[[:space:]]*(#|$) ]] && continue
47-
echo "cmd${i}=${line}" >> "$GITHUB_OUTPUT"
46+
trimmed=$(echo "$line" | xargs)
47+
[[ -z "$trimmed" || "$trimmed" == "#"* ]] && continue
48+
echo "cmd${i}=${trimmed}" >> "$GITHUB_OUTPUT"
4849
i=$((i+1))
4950
done <<'EOF'
5051
${{ inputs.cmlxc_commands }}
@@ -68,22 +69,25 @@ jobs:
6869
repository: chatmail/cmlxc
6970
ref: ${{ inputs.cmlxc_version }}
7071
path: cmlxc
71-
fetch-depth: 0
7272

7373
- name: Install Incus (Zabbly)
7474
run: |
7575
sudo mkdir -p /etc/apt/keyrings
7676
sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc
7777
echo "deb [signed-by=/etc/apt/keyrings/zabbly.asc] https://pkgs.zabbly.com/incus/stable $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/zabbly-incus.list
7878
sudo apt-get update
79-
sudo apt-get install -y incus
79+
sudo apt-get install -y incus-base
8080
8181
- name: Initialise Incus
8282
run: |
8383
sudo systemctl stop docker.socket docker || true
8484
sudo iptables -P FORWARD ACCEPT
8585
sudo sysctl -w fs.inotify.max_user_instances=65535
8686
sudo sysctl -w fs.inotify.max_user_watches=65535
87+
# Disable AppArmor restrictions so Docker-in-LXC containers
88+
# can run systemd (needs cgroup notification socket access).
89+
sudo systemctl stop apparmor || true
90+
sudo apparmor_parser -R /etc/apparmor.d/* 2>/dev/null || true
8791
sudo incus admin init --auto
8892
sudo chmod 666 /var/lib/incus/unix.socket
8993
@@ -97,17 +101,19 @@ jobs:
97101
python -m pip install --upgrade pip
98102
pip install ./cmlxc
99103
100-
- name: Cache Incus images
104+
- name: Restore Incus image cache
101105
id: cache-images
102-
uses: actions/cache@v5
106+
uses: actions/cache/restore@v5
103107
with:
104108
path: /tmp/incus-cache
105-
key: incus-v4-${{ runner.os }}-${{ hashFiles('cmlxc/src/cmlxc/*.py') }}
109+
key: incus-v5-${{ runner.os }}-${{ hashFiles('cmlxc/src/cmlxc/*.py') }}
110+
restore-keys: |
111+
incus-v5-${{ runner.os }}-
106112
107113
- name: Import cached images
108114
run: |
109115
mkdir -p /tmp/incus-cache
110-
for alias in localchat-base localchat-builder; do
116+
for alias in localchat-base localchat-builder localchat-cmdeploy localchat-docker; do
111117
if [ -f /tmp/incus-cache/$alias.tar.gz ]; then
112118
echo "Importing: $alias"
113119
incus image import /tmp/incus-cache/$alias.tar.gz --alias $alias || true
@@ -170,12 +176,12 @@ jobs:
170176
set -eu
171177
i=0
172178
while IFS= read -r cmd || [ -n "$cmd" ]; do
173-
[[ "$cmd" =~ ^[[:space:]]*(#|$) ]] && continue
179+
trimmed=$(echo "$cmd" | xargs)
180+
[[ -z "$trimmed" || "$trimmed" == "#"* ]] && continue
174181
i=$((i+1))
175182
if [ $i -le 12 ]; then continue; fi
176-
177-
echo "::group::Run: $cmd"
178-
eval "$cmd" || { echo "::endgroup::"; exit 1; }
183+
echo "::group::Run: $trimmed"
184+
eval "$trimmed" || { echo "::endgroup::"; exit 1; }
179185
echo "::endgroup::"
180186
done <<< "$CMLXC_COMMANDS"
181187
@@ -184,26 +190,73 @@ jobs:
184190
run: |
185191
for c in $(incus list -c n --format csv); do
186192
echo "::group::Logs for $c"
187-
incus exec "$c" -- journalctl -p warning --no-pager -n 100 || true
193+
incus exec "$c" -- journalctl --no-pager -n 200 || true
194+
# Dump Docker container logs if present
195+
svc=chatmail
196+
if incus exec "$c" -- docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "$svc"; then
197+
echo "--- docker logs $svc ---"
198+
incus exec "$c" -- docker logs "$svc" --tail 200 2>&1 || true
199+
echo "--- dovecot journal ---"
200+
incus exec "$c" -- docker exec "$svc" journalctl -u dovecot --no-pager -n 50 2>&1 || true
201+
echo "--- postfix journal ---"
202+
incus exec "$c" -- docker exec "$svc" journalctl -u postfix --no-pager -n 50 2>&1 || true
203+
echo "--- failed units ---"
204+
incus exec "$c" -- docker exec "$svc" systemctl --failed --no-pager 2>&1 || true
205+
echo "--- dovecot -n (effective config) ---"
206+
incus exec "$c" -- docker exec "$svc" dovecot -n 2>&1 | tail -40 || true
207+
echo "--- ssl cert check ---"
208+
incus exec "$c" -- docker exec "$svc" ls -la /etc/ssl/certs/mailserver.pem /etc/ssl/private/mailserver.key 2>&1 || true
209+
fi
188210
echo "::endgroup::"
189211
done
190212
191213
- name: Export images for cache
192214
if: always() && steps.cache-images.outputs.cache-hit != 'true'
193215
run: |
194216
mkdir -p /tmp/incus-cache
217+
# Publish the builder LXC container as a cached image (the Docker
218+
# container inside gets recreated on compose up, so the LXC is clean).
219+
# Only skip localchat-cmdeploy on failure -- it bakes deploy state
220+
# directly into the LXC and would carry broken config into the next run.
195221
if incus list -c n --format csv | grep -q builder-localchat; then
196-
incus exec builder-localchat -- rm -rf /root/relays /root/minitest-venv /root/.ssh/config*
222+
echo "Cleaning up builder container before publishing ..."
223+
incus exec builder-localchat -- bash -c 'rm -rf /root/relays/* /root/.cache/* /root/.npm /root/.bun'
197224
echo "Publishing builder container as image ..."
198225
incus publish builder-localchat --alias localchat-builder --force || true
199226
fi
200-
for alias in localchat-base localchat-builder; do
227+
# Publish Docker relay container with engine only (strip images to keep cache small)
228+
for ct in $(incus list -c n --format csv | grep -v builder); do
229+
if incus exec "$ct" -- docker info >/dev/null 2>&1; then
230+
echo "Stripping Docker images from $ct ..."
231+
incus exec "$ct" -- docker system prune -af --volumes 2>/dev/null || true
232+
echo "Publishing $ct as localchat-docker ..."
233+
incus publish "$ct" --alias localchat-docker --force || true
234+
break
235+
fi
236+
done
237+
exported=0
238+
if [ "${{ job.status }}" = "success" ]; then
239+
aliases="localchat-base localchat-builder localchat-cmdeploy localchat-docker"
240+
else
241+
aliases="localchat-base localchat-builder localchat-docker"
242+
fi
243+
for alias in $aliases; do
201244
if incus image list --format csv -c l | grep -q "^$alias$"; then
202245
echo "Exporting: $alias"
203246
incus image export $alias /tmp/incus-cache/$alias || true
204247
if [ -f /tmp/incus-cache/$alias ] && [ ! -f /tmp/incus-cache/$alias.tar.gz ]; then
205248
mv /tmp/incus-cache/$alias /tmp/incus-cache/$alias.tar.gz
206249
fi
250+
exported=$((exported+1))
207251
fi
208252
done
253+
echo "exported=$exported" >> "$GITHUB_OUTPUT"
254+
id: export-images
255+
256+
- name: Save Incus image cache
257+
if: always() && steps.export-images.outputs.exported > 0
258+
uses: actions/cache/save@v5
259+
with:
260+
path: /tmp/incus-cache
261+
key: incus-v5-${{ runner.os }}-${{ hashFiles('cmlxc/src/cmlxc/*.py') }}
209262

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc
2929
echo "deb [signed-by=/etc/apt/keyrings/zabbly.asc] https://pkgs.zabbly.com/incus/stable $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/zabbly-incus.list
3030
sudo apt-get update
31-
sudo apt-get install -y incus
31+
sudo apt-get install -y incus-base
3232
3333
- name: Initialise Incus
3434
run: |

0 commit comments

Comments
 (0)