-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
217 lines (174 loc) · 11.4 KB
/
Copy pathMakefile
File metadata and controls
217 lines (174 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 Netresearch DTT GmbH
SHELL := /bin/bash
.DEFAULT_GOAL := help
# ────────────────────────────────────────────────────────────────────
# Lifecycle
# ────────────────────────────────────────────────────────────────────
help: ## Show this help
@printf 'glpi-docker-compose-stack — make targets\n\nUsage: make <target>\n\n'
@awk 'BEGIN {FS = ":.*?##"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-22s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
init: ## Bootstrap .env (random DB passwords, idempotent)
@./bin/init.sh
up: .env ## Start the stack (detached)
docker compose up -d
# Guard: bringing the stack up without a populated .env poisons the db-data
# volume with an empty MARIADB_ROOT_PASSWORD that survives `make clean`.
# This rule fails before docker compose sees the empty env vars.
.env:
@printf '\033[1;31m[make]\033[0m No .env found — run `make init` first.\n' >&2
@exit 1
down: ## Stop the stack (preserves volumes)
docker compose down
restart: ## Restart app + web only
docker compose restart app web
logs: ## Tail logs (all services)
docker compose logs -f --tail=100
logs-app: ## Tail logs (app service)
docker compose logs -f --tail=100 app
ps: ## Show container status
docker compose ps
# ────────────────────────────────────────────────────────────────────
# Overlays & integrations
# ────────────────────────────────────────────────────────────────────
#
# Overlay state lives in .env as COMPOSE_FILE=<colon-separated paths>.
# docker compose reads this automatically, so `make up` works without
# manual `-f` chaining once an overlay is enabled.
overlays: .env ## Show currently-enabled compose overlays
@./bin/compose-file.sh list
enable-traefik: .env ## Add Traefik reverse-proxy overlay (TLS termination + label-based routing)
@./bin/compose-file.sh add examples/compose.traefik.yml
@printf '\033[1;34m[next]\033[0m Set GLPI_HOST in .env to your public hostname (e.g. glpi.example.com), then `make up`. Requires an existing `traefik` external network.\n'
disable-traefik: .env ## Remove Traefik overlay
@./bin/compose-file.sh remove examples/compose.traefik.yml
enable-caddy: .env ## Add Caddy reverse-proxy overlay (TLS termination + auto-HTTPS)
@./bin/compose-file.sh add examples/compose.caddy.yml
@printf '\033[1;34m[next]\033[0m Set GLPI_HOST in .env to your public hostname (e.g. glpi.example.com), then `make up`.\n'
disable-caddy: .env ## Remove Caddy overlay
@./bin/compose-file.sh remove examples/compose.caddy.yml
enable-observability: .env ## Add Prometheus + Grafana overlay (MariaDB + nginx metrics)
@./bin/compose-file.sh add examples/compose.observability.yml
@printf '\033[1;34m[next]\033[0m The nginx /stub_status endpoint is NOT enabled by default — see the overlay header for the two opt-in paths.\n'
disable-observability: .env ## Remove observability overlay
@./bin/compose-file.sh remove examples/compose.observability.yml
# ────────────────────────────────────────────────────────────────────
# Backup / restore
# ────────────────────────────────────────────────────────────────────
backup-up: .env ## Start the opt-in backup service (phpbu, behind the `backup` profile)
docker compose --profile backup up -d backup
backup: ## Run a backup now (requires `make backup-up`; normally ofelia at 03:00)
docker compose --profile backup exec -T backup phpbu --configuration=/config/backup.json
backup-list: ## List backup archives
docker compose --profile backup exec -T backup ls -lh /backups
backup-verify: ## Sanity-check that last night's backup is on disk + non-zero
@docker compose --profile backup exec -T backup sh -c '\
latest=$$(ls -t /backups/db/*.sql.gz 2>/dev/null | head -1); \
if [ -z "$$latest" ]; then \
echo "✗ no DB backups in /backups/db"; exit 1; \
fi; \
size=$$(stat -c%s "$$latest"); \
age_h=$$(( ($$(date +%s) - $$(stat -c%Y "$$latest")) / 3600 )); \
if [ "$$size" -lt 1024 ]; then \
echo "✗ $$latest is $$size bytes — likely empty"; exit 1; \
fi; \
if [ "$$age_h" -gt 26 ]; then \
echo "✗ newest dump is $${age_h}h old (should be < 26h)"; exit 1; \
fi; \
echo "✓ newest dump $$latest ($$size bytes, $${age_h}h old)"'
health: ## Aggregated health state of all services + non-healthy summary
@docker compose ps --format '{{.Service}}\t{{.Status}}' | column -ts $$'\t'
@unhealthy=$$(docker compose ps --filter "health=unhealthy" -q | wc -l | tr -d ' '); \
if [ "$$unhealthy" != "0" ]; then \
printf '\n\033[1;31m%s unhealthy service(s)\033[0m — see docker compose logs <service>\n' "$$unhealthy"; \
exit 1; \
else \
printf '\n\033[1;32mAll services healthy.\033[0m\n'; \
fi
# ────────────────────────────────────────────────────────────────────
# Testing (local)
# ────────────────────────────────────────────────────────────────────
test-image: ## Build runtime image + run container-structure-test (image-surface check)
docker buildx build --target runtime --platform linux/amd64 --load -t glpi-php-fpm:test .
@command -v container-structure-test >/dev/null 2>&1 \
|| { echo "Install: https://github.com/GoogleContainerTools/container-structure-test/releases"; exit 1; }
container-structure-test test --image glpi-php-fpm:test --config tests/container-structure-test.yaml
test-bats: ## Run bats regression suite for bin/ helpers (uses bats/bats Docker image — no local install)
docker run --rm -v "$(CURDIR)":/code -w /code bats/bats:latest tests/bin/
hardening-check: ## Assert every overlay keeps no-new-privileges + cap_drop:[ALL] (tests/lint/hardening-check.sh)
./tests/lint/hardening-check.sh
test: test-image test-bats hardening-check ## Run the local test suite (image surface + bats + overlay hardening)
# ────────────────────────────────────────────────────────────────────
# Dev convenience
# ────────────────────────────────────────────────────────────────────
dev: ## Seed compose.override.yml from the example (if missing) + `make up`
@if [ ! -f compose.override.yml ]; then \
cp compose.override.yml.example compose.override.yml; \
printf '\033[1;34m[make]\033[0m seeded compose.override.yml from example\n'; \
else \
printf '\033[1;34m[make]\033[0m compose.override.yml already present — keeping\n'; \
fi
$(MAKE) up
build: ## Build the runtime image locally (glpi-php-fpm:local, supply-chain-pinned) — no push
@v=$$(tr -d '[:space:]' < .glpi-version); \
url="https://github.com/glpi-project/glpi/releases/download/$$v/glpi-$$v.tgz"; \
printf '\033[1;34m[build]\033[0m resolving sha256 of %s\n' "$$url"; \
sha=$$(curl -fsSL "$$url" | sha256sum | cut -d' ' -f1); \
[ -n "$$sha" ] || { printf '\033[1;31m[build]\033[0m could not hash %s\n' "$$url" >&2; exit 1; }; \
printf '\033[1;34m[build]\033[0m GLPI %s sha256=%s\n' "$$v" "$$sha"; \
docker buildx build --target runtime --platform linux/amd64 --load \
-t glpi-php-fpm:local \
--build-arg GLPI_VERSION="$$v" \
--build-arg GLPI_SHA256="$$sha" .
lint: ## Run hadolint + shellcheck + yamllint via Docker (no local install)
@printf '\033[1;34m[lint]\033[0m hadolint Dockerfile\n'
docker run --rm -i -v $(CURDIR):/work -w /work \
hadolint/hadolint:latest-alpine \
hadolint --config .hadolint.yaml Dockerfile
@printf '\033[1;34m[lint]\033[0m shellcheck rootfs/usr/local/bin + bin\n'
docker run --rm -v $(CURDIR):/work -w /work \
koalaman/shellcheck:stable \
$$(find rootfs/usr/local/bin bin -type f \( -name '*.sh' -o -perm -u+x \) 2>/dev/null)
@printf '\033[1;34m[lint]\033[0m yamllint compose + workflows\n'
docker run --rm -v $(CURDIR):/work -w /work \
cytopia/yamllint:1 \
-d "{extends: default, rules: {line-length: disable, document-start: disable, truthy: {check-keys: false}, comments: {min-spaces-from-content: 1}}}" \
.github/workflows .hadolint.yaml compose.yml
@printf '\033[1;34m[lint]\033[0m actionlint .github/workflows\n'
docker run --rm -v $(CURDIR):/work -w /work \
rhysd/actionlint:latest -color
# ────────────────────────────────────────────────────────────────────
# Upgrade
# ────────────────────────────────────────────────────────────────────
pull: ## Pull latest images
docker compose pull
upgrade: pull up logs-app ## Pull + recreate + follow logs
# ────────────────────────────────────────────────────────────────────
# Maintenance
# ────────────────────────────────────────────────────────────────────
shell: ## Shell into the app container
docker compose exec app sh
console: ## Run a GLPI console command (use: make console CMD="cache:configure --help")
docker compose exec -u www-data app php bin/console $(CMD)
restore: ## Print pointer to the disaster-recovery runbook (not automated)
@printf '\033[1;33m[restore]\033[0m This target intentionally does NOT automate restore.\n'
@printf ' Restoring is destructive and context-specific — follow the\n'
@printf ' runbook step by step:\n\n'
@printf ' docs/runbook-restore.md\n\n'
@printf ' TL;DR: stop app+scheduler, pick a dump from the backups volume,\n'
@printf ' drop+recreate the database, gunzip | mariadb, restore the\n'
@printf ' files/ and config/ tarballs, then `make restart`.\n'
clean: ## DESTRUCTIVE: down + delete ALL volumes (db + uploads + backups)
@read -r -p "This deletes ALL data — the database, uploads AND the backups volume. Type 'yes' to proceed: " ans; \
[ "$$ans" = "yes" ] || { echo "aborted"; exit 1; }
docker compose down -v
.PHONY: \
help init up down restart logs logs-app ps \
backup backup-up backup-list backup-verify \
health test test-image test-bats hardening-check \
dev build lint pull upgrade \
shell console restore clean \
overlays \
enable-traefik disable-traefik \
enable-caddy disable-caddy \
enable-observability disable-observability