Skip to content

Commit a6a4f17

Browse files
committed
Enhance Docker configuration and build process
- Updated .gitignore to include new data and results directories. - Modified docker-compose.yml to improve security with read-only settings and added healthcheck configuration. - Updated Dockerfile to include a healthcheck binary and ensure proper ownership of data directories. - Enhanced Makefile with new commands for verification and Docker build processes. - Updated README and operations documentation to reflect changes in Docker setup and usage instructions. - Adjusted public site test cases for improved accuracy in expected output.
1 parent c28e33f commit a6a4f17

10 files changed

Lines changed: 191 additions & 11 deletions

File tree

.github/workflows/ci.yml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [main, master]
7+
8+
permissions:
9+
contents: read
10+
11+
concurrency:
12+
group: ci-${{ github.workflow }}-${{ github.ref }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
verify:
17+
name: Verify and Docker
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Set up Go
24+
uses: actions/setup-go@v5
25+
with:
26+
go-version-file: go.mod
27+
cache: true
28+
29+
- name: Set up Node
30+
uses: actions/setup-node@v4
31+
with:
32+
node-version: "22"
33+
cache: npm
34+
35+
- name: Install make
36+
run: |
37+
sudo apt-get update
38+
sudo apt-get install -y make
39+
40+
- name: Verify (npm + Go tests + ui8px)
41+
run: make verify
42+
43+
- name: Docker build
44+
run: make docker-build
45+
46+
- name: Compose config
47+
run: make compose-config
48+
49+
kics:
50+
name: KICS IaC scan
51+
runs-on: ubuntu-latest
52+
permissions:
53+
contents: read
54+
security-events: write
55+
steps:
56+
- name: Checkout
57+
uses: actions/checkout@v4
58+
59+
- name: Install make
60+
run: |
61+
sudo apt-get update
62+
sudo apt-get install -y make
63+
64+
- name: Run KICS
65+
run: make kics
66+
67+
- name: Upload KICS SARIF
68+
if: always()
69+
continue-on-error: true
70+
uses: github/codeql-action/upload-sarif@v3
71+
with:
72+
sarif_file: kics-results/results.sarif

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
tmp/
22
dist/
3+
data/
4+
kics-results/
35
node_modules/
46
.DS_Store
57
.manual/

Dockerfile

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,31 @@ WORKDIR /src
3737

3838
COPY --from=assets /src ./
3939
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /out/gocms ./cmd/server \
40-
&& mkdir -p /out/data
40+
&& CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /out/healthcheck ./cmd/healthcheck \
41+
&& mkdir -p /out/data \
42+
&& chown -R 65532:65532 /out/data
4143

4244
FROM gcr.io/distroless/static-debian12:nonroot
4345

4446
WORKDIR /app
4547

4648
COPY --from=build /out/gocms /gocms
47-
COPY --from=build --chown=nonroot:nonroot /out/data /data
49+
COPY --from=build /out/healthcheck /healthcheck
50+
COPY --from=build /out/data /data
4851
COPY --from=assets /src/web/static /app/web/static
4952

5053
ENV APP_BIND=0.0.0.0:8080
5154
ENV APP_DATA_SOURCE=file:/data/gocms.db
5255
ENV GOCMS_PRESET=full
5356
ENV GOCMS_STORAGE_PROFILE=sqlite
5457
ENV GOCMS_DEPLOYMENT_PROFILE=container
58+
ENV HEALTHCHECK_URL=http://127.0.0.1:8080/readyz
5559

5660
EXPOSE 8080
5761

62+
USER nonroot:nonroot
63+
64+
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
65+
CMD ["/healthcheck"]
66+
5867
ENTRYPOINT ["/gocms"]

Makefile

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: run test fmt build deploy docker-ps docker-logs docker-stop
1+
.PHONY: run test fmt build verify deploy docker-build compose-config kics docker-ps docker-logs docker-stop
22

33
run:
44
go run ./cmd/server
@@ -16,7 +16,33 @@ build:
1616
npm run build:versioned
1717
go build ./cmd/server
1818

19+
verify:
20+
npm ci
21+
npm run verify
22+
23+
docker-build:
24+
docker build -t fastygo/cms:ci .
25+
26+
compose-config:
27+
docker compose config
28+
29+
kics:
30+
mkdir -p kics-results
31+
# On Docker Desktop for Windows, KICS may exit with "download not supported for scheme 'c'"; use WSL or rely on GitHub Actions.
32+
docker run --rm \
33+
-v "$(CURDIR):/scan:ro" \
34+
-v "$(CURDIR)/kics-results:/out" \
35+
checkmarx/kics:v2.1.20 scan \
36+
-p /scan/Dockerfile \
37+
-p /scan/docker-compose.yml \
38+
-o /out \
39+
--report-formats sarif,json \
40+
--silent \
41+
--disable-full-descriptions \
42+
--fail-on critical,high,medium,low,info
43+
1944
deploy:
45+
mkdir -p data
2046
docker compose up -d --build cms
2147

2248
docker-ps:

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@ Format source:
9191
gofmt -w ./cmd ./internal
9292
```
9393

94+
## Docker
95+
96+
Build and run with Compose (SQLite data under `./data`; see [Operations — Docker Compose](./docs/operations.md#docker-compose-local-and-ssh-hosts) for host permissions and session keys):
97+
98+
```bash
99+
mkdir -p data
100+
docker compose up -d --build cms
101+
```
102+
94103
## Documentation Map
95104

96105
- [CMS capability overview](./docs/cms-capabilities.md)

cmd/healthcheck/main.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Package main implements a minimal HTTP client for container HEALTHCHECK.
2+
// It defaults to the readiness endpoint used by GoCMS in container deployments.
3+
package main
4+
5+
import (
6+
"fmt"
7+
"net/http"
8+
"os"
9+
"strings"
10+
"time"
11+
)
12+
13+
const defaultURL = "http://127.0.0.1:8080/readyz"
14+
15+
func main() {
16+
url := strings.TrimSpace(os.Getenv("HEALTHCHECK_URL"))
17+
if url == "" {
18+
url = defaultURL
19+
}
20+
21+
client := &http.Client{
22+
Timeout: 4 * time.Second,
23+
}
24+
25+
resp, err := client.Get(url)
26+
if err != nil {
27+
fmt.Fprintf(os.Stderr, "healthcheck: request failed: %v\n", err)
28+
os.Exit(1)
29+
}
30+
defer resp.Body.Close()
31+
32+
if resp.StatusCode != http.StatusOK {
33+
fmt.Fprintf(os.Stderr, "healthcheck: %s returned %s\n", url, resp.Status)
34+
os.Exit(1)
35+
}
36+
}

docker-compose.yml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ services:
55
image: fastygo/cms:local
66
container_name: fastygo-cms
77
restart: unless-stopped
8+
security_opt:
9+
- no-new-privileges:true
10+
cap_drop:
11+
- ALL
12+
read_only: true
13+
tmpfs:
14+
- /tmp
815
environment:
916
APP_BIND: 0.0.0.0:8080
1017
APP_DATA_SOURCE: file:/data/gocms.db
@@ -13,10 +20,14 @@ services:
1320
GOCMS_STORAGE_PROFILE: sqlite
1421
GOCMS_DEPLOYMENT_PROFILE: container
1522
GOCMS_SEED_FIXTURES: ${GOCMS_SEED_FIXTURES:-true}
23+
HEALTHCHECK_URL: http://127.0.0.1:8080/readyz
1624
ports:
1725
- "127.0.0.1:18055:8080"
1826
volumes:
19-
- gocms-data:/data
20-
21-
volumes:
22-
gocms-data:
27+
- ${GOCMS_DATA_DIR:-./data}:/data
28+
healthcheck:
29+
test: ["CMD", "/healthcheck"]
30+
interval: 30s
31+
timeout: 5s
32+
retries: 3
33+
start_period: 25s

docs/operations.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,18 @@ SQLite is the first full provider for this pass. Future MySQL/Postgres/Bolt adap
103103
- audit persistence
104104

105105
Details: [migrations.md](./migrations.md), [cms-capabilities.md](./cms-capabilities.md).
106+
107+
---
108+
109+
## Docker Compose (local and SSH hosts)
110+
111+
The default [`docker-compose.yml`](../docker-compose.yml) binds SQLite data to **`./data`** on the host (`GOCMS_DATA_DIR` overrides the host path). The runtime image uses the distroless **non-root** user (**UID/GID 65532**).
112+
113+
Before the first `docker compose up` on a server:
114+
115+
1. Create the data directory: `mkdir -p ./data` (or your `GOCMS_DATA_DIR`).
116+
2. Ensure the container user can write the database file: `sudo chown -R 65532:65532 ./data`.
117+
118+
Set a strong **`APP_SESSION_KEY`** in production; do not rely on the development default.
119+
120+
Docker **`HEALTHCHECK`** and Compose **`healthcheck`** call the **`/healthcheck`** helper, which requests **`/readyz`** inside the container (override with **`HEALTHCHECK_URL`** if needed).

internal/infra/features/cms/publicsite_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func TestPublicSiteUsesRussianAsDefaultWhenAcceptLanguageOmitted(t *testing.T) {
122122
}
123123
t.Fatalf("expected html lang=ru, body head: %s", head)
124124
}
125-
for _, want := range []string{"Опубликованная запись", "Новости", "Блог", "Публичный сайт на GoCMS."} {
125+
for _, want := range []string{"Опубликованная запись", "Новости", "Блог", "Смотри, что умеет админка GoCMS"} {
126126
if !strings.Contains(body, want) {
127127
t.Fatalf("expected home to contain %q", want)
128128
}
@@ -157,8 +157,8 @@ func TestPublicSiteEnglishLocalePrefixRendersEnglishCopy(t *testing.T) {
157157
}
158158
t.Fatalf("expected English fixture strings on /en/ home, excerpt: %s", head)
159159
}
160-
if !strings.Contains(body, "Public site powered by GoCMS.") {
161-
t.Fatalf("expected English public fixture home hero headline on /en/")
160+
if !strings.Contains(body, "See what GoCMS admin can do") {
161+
t.Fatalf("expected English public fixture home intro on /en/")
162162
}
163163
}
164164

scripts/check-css-apply-only.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const roots = ["web/static/css"];
55
const bannedDeclaration = /^\s*[a-z-]+\s*:/i;
66
let failed = false;
77

8-
const rawCSSExceptions = new Set(["editor.css"]);
8+
const rawCSSExceptions = new Set(["editor.css", "tweakcn.css"]);
99

1010
for (const file of walk(roots)) {
1111
if (!file.endsWith(".css")) continue;

0 commit comments

Comments
 (0)