Skip to content

Commit ff51cdf

Browse files
committed
refactor: single entrypoint for build
1 parent 4116f28 commit ff51cdf

11 files changed

Lines changed: 286 additions & 18 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Checkpoint
2+
3+
- Timestamp: 2026-03-29 08:24 UTC
4+
- Scope completed:
5+
- Implemented shared site build entrypoint at `.github/scripts/build-site.sh`.
6+
- Routed `.github/workflows/gh-pages.yml` and `.github/workflows/run-htmltest.yml` through the shared script.
7+
- Added local `make render` production-style render path in `Makefile`.
8+
- Refactored CI and script configuration for maintainability (KISS/DRY) and validated local build variants.
9+
- Synced contributor and agent documentation with the new shared build path.
10+
- Files touched:
11+
- `.github/scripts/build-site.sh`
12+
- `.github/workflows/gh-pages.yml`
13+
- `.github/workflows/run-htmltest.yml`
14+
- `Dockerfile`
15+
- `Makefile`
16+
- `README.md`
17+
- `AGENTS.md`
18+
- `.gitignore`
19+
- `.agent/working-memory/session.md`
20+
- `.agent/working-memory/plan-shared-build-entrypoint.md`
21+
- Validation performed:
22+
- Local smoke test: `make render` completed successfully through shared script.
23+
- Local shared-script variant: `OUTPUT_DIR=/src/dist` completed successfully.
24+
- Local shared-script variant: `BASE_URL=https://example.test/subpath` completed successfully.
25+
- Workflow and script diagnostics (`get_errors`) returned no issues for changed files.
26+
- Risks/blockers:
27+
- CI workflows were not executed from this environment; GitHub Actions run status remains to be confirmed remotely.
28+
- Local render target proactively clears generated outputs (`public`, `resources/_gen`, `.hugo_build.lock`) to avoid stale root-owned artifacts from prior runs.
29+
- Next checkpoint goal:
30+
- Confirm both GitHub workflows pass in remote CI and update README examples if workflow output-path conventions change (for example, `dist` vs `public`).
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Shared Build Entrypoint Plan
2+
3+
## Goal
4+
5+
Create one repository-owned build entrypoint that all site render paths use:
6+
7+
- local production-style build
8+
- htmltest render
9+
- GitHub Pages build
10+
11+
The entrypoint should centralize the Hugo invocation, container environment, cache configuration, and output-path handling so future fixes are made once.
12+
13+
## Current state
14+
15+
- Local development uses `make serve`, which starts the container default `hugo server` command.
16+
- Local shell access uses `make shell`, which opens an interactive shell in the same container.
17+
- `run-htmltest.yml` duplicates a Docker Compose + `hugo build` command.
18+
- `gh-pages.yml` duplicates a similar Docker Compose + `hugo build` command with Pages-specific `BASE_URL` handling.
19+
- Shared infrastructure already exists at the container/image level via `docker-compose.yml` and `Dockerfile`, but the build command itself is duplicated.
20+
21+
## Target design
22+
23+
Introduce a single script entrypoint under the repository, for example:
24+
25+
- `.github/scripts/build-site.sh`
26+
27+
Responsibilities:
28+
29+
- enforce strict shell behavior
30+
- normalize environment variables and defaults
31+
- configure git safe-directory behavior inside the container
32+
- require absolute Hugo cache path inside the container
33+
- accept output directory as an argument or env var
34+
- accept optional base URL override
35+
- run a single canonical `hugo build` command
36+
37+
Example interface:
38+
39+
- `OUTPUT_DIR=/src/public BASE_URL=https://example.test/ .github/scripts/build-site.sh`
40+
- `OUTPUT_DIR=/src/dist .github/scripts/build-site.sh`
41+
42+
## Implementation phases
43+
44+
### Phase 1: Create canonical build script
45+
46+
- Add a shell script to the repo and make it the only place that calls `hugo build` for CI-style production renders.
47+
- Set defaults for:
48+
- `HUGO_ENV=production`
49+
- `HUGO_CACHEDIR=/src/.hugo_cache`
50+
- `OUTPUT_DIR=/src/public`
51+
- Build the Hugo command incrementally so optional flags like `--baseURL` are only added when present.
52+
- Keep the script container-oriented; assume execution happens inside the `hugo` service with `/src` mounted.
53+
54+
Success criteria:
55+
56+
- No workflow contains a long inline `hugo build` command anymore.
57+
- The script can render successfully for both default and overridden output directories.
58+
59+
### Phase 2: Route GitHub Pages through the script
60+
61+
- Replace the inline Hugo command in `gh-pages.yml` with a call to the shared script.
62+
- Keep Pages-only concerns in the workflow:
63+
- `actions/configure-pages`
64+
- cache restore/save
65+
- artifact upload
66+
- deploy action
67+
- Pass only the parameters the shared script needs:
68+
- `BASE_URL`
69+
- `HUGO_CACHEDIR`
70+
- optional explicit `OUTPUT_DIR=/src/public`
71+
72+
Success criteria:
73+
74+
- `gh-pages.yml` still owns deployment orchestration.
75+
- The actual site build command lives only in the shared script.
76+
77+
### Phase 3: Route htmltest through the script
78+
79+
- Replace the inline Hugo command in `run-htmltest.yml` with a call to the shared script.
80+
- Decide whether htmltest should build to `/src/public` for exact parity or continue using `/src/dist`.
81+
- Preferred direction: build to `/src/public` and point htmltest at that output if feasible, because it reduces environment drift.
82+
- If htmltest must keep `/src/dist`, use the shared script with `OUTPUT_DIR=/src/dist`.
83+
84+
Success criteria:
85+
86+
- htmltest and gh-pages differ only in workflow-specific orchestration, not in core site build logic.
87+
88+
### Phase 4: Add a local production-build entrypoint
89+
90+
- Add a Make target such as `make render` or `make build-site` that runs the same shared script inside the `hugo` service.
91+
- Keep `make serve` as the fast feedback path for local content work.
92+
- Document that `make render` is the local equivalent of CI production rendering.
93+
94+
Example target behavior:
95+
96+
- `docker compose build --pull hugo`
97+
- `docker compose run --rm --no-deps --entrypoint sh hugo -c '.github/scripts/build-site.sh'`
98+
99+
Success criteria:
100+
101+
- There is a documented local command that exercises the same production build path as CI.
102+
- Contributors no longer need to reconstruct the CI build command manually.
103+
104+
### Phase 5: Document the contract
105+
106+
- Update `README.md` to distinguish:
107+
- local live preview via `make serve`
108+
- local production-equivalent render via `make render`
109+
- CI consumers of the shared build entrypoint
110+
- Document the required environment variables and defaults.
111+
- Note that the deploy job publishes artifacts only; it does not build the site.
112+
113+
Success criteria:
114+
115+
- Repository docs match actual workflow behavior.
116+
- The shared-entrypoint contract is easy to extend for future checks.
117+
118+
## Design constraints
119+
120+
- Keep project commands container-based, consistent with `AGENTS.md`.
121+
- Do not move GitHub Pages deployment logic into the shared script.
122+
- Keep host-path concerns in workflows and Make targets; keep container-path concerns in the script.
123+
- Preserve the current absolute container cache directory requirement.
124+
- Avoid introducing host-level dependencies on local `hugo`, `go`, `node`, or `npm`.
125+
126+
## Recommended normalization choices
127+
128+
- Canonical container cache dir: `/src/.hugo_cache`
129+
- Canonical default output dir: `/src/public`
130+
- Canonical local production command: `make render`
131+
- Canonical live-preview command: `make serve`
132+
- Canonical script location: `.github/scripts/build-site.sh`
133+
134+
## Open decisions
135+
136+
- Whether htmltest should consume `/src/public` instead of `/src/dist` for exact deploy parity.
137+
- Whether the local render target should run as the host UID/GID to avoid root-owned artifacts.
138+
- Whether git safe-directory setup should remain inline in the container command or be fully absorbed by the script.
139+
140+
## Suggested implementation order
141+
142+
1. Add the shared script.
143+
2. Switch `gh-pages.yml` to use it.
144+
3. Switch `run-htmltest.yml` to use it.
145+
4. Add `make render`.
146+
5. Update `README.md`.
147+
148+
## Validation plan
149+
150+
1. Run the shared script locally in the `hugo` container with default settings.
151+
2. Run it locally with `OUTPUT_DIR=/src/dist`.
152+
3. Confirm `gh-pages.yml` still builds and uploads the Pages artifact.
153+
4. Confirm `run-htmltest.yml` renders and htmltest reads the intended output directory.
154+
5. Compare generated `public` output between the local render path and the Pages workflow path for parity.

.agent/working-memory/session.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,16 @@
2323
- host path for GitHub Actions cache: `./.hugo_cache`
2424
- container path for Hugo `--cacheDir`: `/src/.hugo_cache`
2525
- Running the container as `--user "$(id -u):$(id -g)"` avoids root-owned workspace files, but it also means default cache locations like `/cache/modules` may be unwritable.
26+
27+
## Shared build entrypoint planning (updated 2026-03-29)
28+
29+
- Plan created: `.agent/working-memory/plan-shared-build-entrypoint.md`
30+
- Objective: route local production render, htmltest render, and GitHub Pages build through one repository-owned build script.
31+
- Preferred shape: keep workflow orchestration in YAML, move only the canonical Hugo build logic into a shared container-executed shell script.
32+
33+
## Checkpoint and docs sync (updated 2026-03-29)
34+
35+
- Checkpoint added: `.agent/checkpoints/checkpoint-20260329-0824.md`.
36+
- Contributor docs synced: `README.md` now documents shared build entrypoint and `make render`.
37+
- Agent docs synced: `AGENTS.md` now references `.github/scripts/build-site.sh` and local production-style render guidance.
38+
- Repo hygiene: `.gitignore` now ignores generated `/.hugo_cache/` and `/dist/` build outputs.

.github/scripts/build-site.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/sh
2+
3+
set -eu
4+
5+
# In docker compose runs with --user, HOME may resolve to '/' and be unwritable.
6+
HOME="${HOME:-/tmp}"
7+
if [ "$HOME" = "/" ] || [ ! -w "$HOME" ]; then
8+
HOME=/tmp
9+
fi
10+
export HOME
11+
export npm_config_cache="${npm_config_cache:-/tmp/.npm}"
12+
export HUGO_ENV="${HUGO_ENV:-production}"
13+
export NODE_PATH="${NODE_PATH:-/tmp/node_modules}"
14+
15+
HUGO_CACHEDIR="${HUGO_CACHEDIR:-/src/.hugo_cache}"
16+
OUTPUT_DIR="${OUTPUT_DIR:-/src/public}"
17+
BASE_URL="${BASE_URL:-}"
18+
19+
require_absolute_path() {
20+
value="$1"
21+
name="$2"
22+
case "$value" in
23+
/*) ;;
24+
*)
25+
echo "ERROR: ${name} must be an absolute path, got: ${value}" >&2
26+
exit 1
27+
;;
28+
esac
29+
}
30+
31+
require_absolute_path "$HUGO_CACHEDIR" HUGO_CACHEDIR
32+
require_absolute_path "$OUTPUT_DIR" OUTPUT_DIR
33+
34+
mkdir -p "$HUGO_CACHEDIR" "$OUTPUT_DIR" "$npm_config_cache"
35+
36+
git config --global --add safe.directory /src
37+
git config --global core.quotepath false
38+
39+
set -- build \
40+
--gc \
41+
--minify \
42+
--cacheDir "$HUGO_CACHEDIR" \
43+
-d "$OUTPUT_DIR"
44+
45+
if [ -n "$BASE_URL" ]; then
46+
trimmed_base_url=${BASE_URL%/}
47+
set -- "$@" --baseURL "${trimmed_base_url}/"
48+
fi
49+
50+
echo "Running: hugo $*" >&2
51+
exec hugo "$@"

.github/workflows/gh-pages.yml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ jobs:
3737
id: pages
3838
uses: actions/configure-pages@v5
3939

40-
- name: Configure Git
41-
run: |
42-
git config core.quotepath false
43-
4440
- name: Restore build cache
4541
uses: actions/cache/restore@v4
4642
with:
@@ -57,12 +53,9 @@ jobs:
5753
docker compose run --rm --no-deps \
5854
--entrypoint sh \
5955
--user "$(id -u):$(id -g)" \
60-
-e HOME=/tmp \
61-
-e npm_config_cache=/tmp/.npm \
62-
-e HUGO_ENV=production \
6356
-e HUGO_CACHEDIR="${HUGO_CACHE_CONTAINER_DIR}" \
6457
-e BASE_URL="${BASE_URL}" \
65-
hugo -lc 'git config --global --add safe.directory /src && git config --global core.quotepath false && hugo build --gc --minify --baseURL "${BASE_URL%/}/" --cacheDir "${HUGO_CACHEDIR}"'
58+
hugo -c '.github/scripts/build-site.sh'
6659
6760
- name: Save build cache
6861
if: always()

.github/workflows/run-htmltest.yml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,21 @@ jobs:
1010
htmltest:
1111
runs-on: ubuntu-latest
1212
env:
13-
HUGO_CACHE_HOST_DIR: ./.hugo_cache
1413
HUGO_CACHE_CONTAINER_DIR: /src/.hugo_cache
1514
steps:
1615
- uses: actions/checkout@v4
1716
with:
1817
fetch-depth: 0
1918

2019
- name: Build Hugo site in Docker Compose
21-
env:
22-
HUGO_ENV: "production"
2320
run: |
2421
docker compose build --pull hugo
2522
docker compose run --rm --no-deps \
2623
--entrypoint sh \
2724
--user "$(id -u):$(id -g)" \
28-
-e HOME=/tmp \
29-
-e npm_config_cache=/tmp/.npm \
3025
-e HUGO_CACHEDIR="${HUGO_CACHE_CONTAINER_DIR}" \
31-
-e HUGO_ENV="${HUGO_ENV}" \
32-
hugo -lc 'git config --global --add safe.directory /src && git config --global core.quotepath false && hugo build --gc --minify --cacheDir "${HUGO_CACHEDIR}" -d /src/dist'
26+
-e OUTPUT_DIR=/src/dist \
27+
hugo -c '.github/scripts/build-site.sh'
3328
3429
- name: run htmltest
3530
continue-on-error: true

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ node_modules
66
/public/
77
/resources/_gen/
88
.hugo_build.lock
9+
/.hugo_cache/
10+
/dist/
911

1012
# Executable may be added to repository
1113
hugo.exe

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Agent-generated artifacts must be written under `.agent/`.
3131

3232
- Run project commands in containers only; do not assume local `go`, `hugo`, `node`, or `npm` are installed on the host.
3333
- Use Docker Compose with the `hugo` service for build/test/update tasks (for example: `docker compose run --rm hugo <command>`).
34+
- Prefer the shared Hugo production build entrypoint `.github/scripts/build-site.sh` for render operations used by CI and local production-style checks.
35+
- Use `make render` for the local production-style render path and `make serve` for local hot-reload preview.
3436
- If a command cannot run in the current container setup, document the limitation and propose a container-based alternative.
3537

3638
## Artifact guidance

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ RUN apk add --no-cache nodejs npm go
1818
# packages are never re-fetched from the network unnecessarily.
1919
COPY package.json package-lock.json /tmp/
2020
RUN --mount=type=cache,target=/root/.npm \
21-
cd /tmp && npm ci
21+
cd /tmp && npm ci && \
22+
ln -sf /tmp/node_modules/.bin/postcss /usr/local/bin/postcss && \
23+
ln -sf /tmp/node_modules/.bin/autoprefixer /usr/local/bin/autoprefixer
2224

25+
ENV NODE_PATH="/tmp/node_modules"
2326
ENV PATH="/tmp/node_modules/.bin:${PATH}"
2427

2528
CMD ["server", "--bind", "0.0.0.0"]

Makefile

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ UID=$(shell id -u)
88
GID=$(shell id -g)
99

1010
# Controls
11-
.PHONY : commands clean stop serve
11+
.PHONY : commands clean stop serve render
1212
.NOTPARALLEL:
1313
all : commands
1414

@@ -24,6 +24,19 @@ build :
2424
serve : build
2525
@docker compose up -d
2626

27+
## render : run the production-style site render locally.
28+
render : build
29+
docker compose run --rm --no-deps \
30+
--entrypoint sh \
31+
hugo -c 'rm -f /src/.hugo_build.lock && rm -rf /src/public /src/resources/_gen'
32+
docker compose run --rm --no-deps \
33+
--entrypoint sh \
34+
--user "$(UID):$(GID)" \
35+
-e HOME=/tmp \
36+
-e npm_config_cache=/tmp/.npm \
37+
-e HUGO_CACHEDIR=/src/.hugo_cache \
38+
hugo -c '.github/scripts/build-site.sh'
39+
2740
## shell : open a hugo shell
2841
shell : build
2942
docker compose run --rm hugo shell

0 commit comments

Comments
 (0)