Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ We provide a convenience script for building docs locally, using Docker;

For now, this will only work for CFEngine team members - you need to be able to clone the private (enterprise) repos with SSH.

When the script finishes, it gives you instructions for how to start a webserver and see the resulting docs page.
When the script finishes, it starts a Docker container serving the resulting docs page at <http://127.0.0.1:8000/>. See the comments at the top of the script for options, e.g. `SERVE_ONLY=1` to restart the webserver without rebuilding, or `SKIP_SERVE=1` to not start it at all.

## Writing guidelines

Expand Down
91 changes: 86 additions & 5 deletions build-locally.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
# DOCKER docker binary to use (default: docker)
# IMAGE_NAME tag for the build image (default: cfengine-docs-hugo)
# SKIP_PUBLISH=1 skip the _publish.sh step (just build)
# SKIP_SERVE=1 don't start the serve container (site + search)
# SERVE_ONLY=1 skip the build; just (re)start the serve container
# against the previously built site
# SITE_PORT host port for the served site (default: 8000)
# SERVE_IMAGE tag for the serve image (default: cfengine-docs-serve)
#

set -euo pipefail

Expand All @@ -35,6 +41,9 @@ PACKAGE_BUILD="${PACKAGE_BUILD:-n/a}"
LTS_VERSION="${LTS_VERSION:-}"
DOCKER="${DOCKER:-docker}"
IMAGE_NAME="${IMAGE_NAME:-cfengine-docs-hugo}"
SITE_PORT="${SITE_PORT:-8000}"
SERVE_IMAGE="${SERVE_IMAGE:-cfengine-docs-serve}"
SERVE_CONTAINER="cfengine-docs-serve"

# repo_name url default_branch
REPOS=(
Expand All @@ -43,8 +52,66 @@ REPOS=(
"enterprise git@github.com:cfengine/enterprise.git master"
"masterfiles git@github.com:cfengine/masterfiles.git master"
"nt-docs git@github.com:northerntechhq/nt-docs.git main"
"infra git@github.com:NorthernTechHQ/infra.git master"
)

# Serve the built site plus a working search in one Docker container
serve_site() {
local site_dir="$DOC_WORK/generator/_site"
local server_js="$WORK_DIR/infra/services/docs-cfengine-com/search-server/server.js"
local flex_pkg="$WORK_DIR/nt-docs/scripts/search/index/package.json"
local label="$BRANCH"
[ -n "$LTS_VERSION" ] && label="lts"

if [ ! -f "$server_js" ] || [ ! -d "$site_dir/assets/searchIndex" ]; then
echo "error: no built site (or no infra checkout) under tmp/; run a full build first" >&2
return 1
fi

echo "==> Building serve image $SERVE_IMAGE"
"$DOCKER" build --tag "$SERVE_IMAGE" "$SCRIPT_DIR/generator/serve"

echo "==> Starting serve container $SERVE_CONTAINER on port $SITE_PORT"
"$DOCKER" rm -f "$SERVE_CONTAINER" >/dev/null 2>&1 || true
"$DOCKER" run -d --name "$SERVE_CONTAINER" \
-p "$SITE_PORT:8000" \
-e DOCS_PATH=/docs \
-v "$site_dir:/site:ro" \
-v "$site_dir:/docs/$label:ro" \
-v "$server_js:/search/server.js:ro" \
"$SERVE_IMAGE" >/dev/null

local query_url="http://127.0.0.1:$SITE_PORT/docs/search/$label/?searchQuery=cfengine"
for _ in $(seq 1 15); do
if curl -fsS --max-time 60 "$query_url" >/dev/null 2>&1; then
echo " search is up: $query_url"
return 0
fi
sleep 1
done
echo " search did not respond; check: $DOCKER logs $SERVE_CONTAINER" >&2
return 1
}

# Block until Ctrl-C, then stop and remove the serve container.
wait_serve() {
echo " Press Ctrl-C to stop it."
trap '"$DOCKER" rm -f "$SERVE_CONTAINER" >/dev/null 2>&1 || true
echo "==> Stopped $SERVE_CONTAINER"; exit 0' INT TERM
# docker wait runs in the background so the interruptible `wait`
# builtin blocks instead — a trapped signal fires immediately.
"$DOCKER" wait "$SERVE_CONTAINER" >/dev/null 2>&1 &
wait $! || true
}

# start the serve container against an existing build, no rebuild.
if [ -n "${SERVE_ONLY:-}" ]; then
serve_site
echo "==> Open http://127.0.0.1:$SITE_PORT/"
wait_serve
exit 0
fi

# 1. Clone (or update) the sibling repos under tmp/cache/, then export a
# clean working copy (no .git) to tmp/work/. We mount the .git-free
# copy because the container does `chmod -R` over each repo, and on
Expand Down Expand Up @@ -131,15 +198,29 @@ done
"$PACKAGE_BUILD" "$LTS_VERSION"

# 4. Optionally package the result (mirrors the Jenkins pipeline).
# _publish.sh mutates _site in place for the offline archive, so restore it from packed-for-shipping.tar.gz (what production deploys) afterwards.
if [ -z "${SKIP_PUBLISH:-}" ]; then
echo "==> Packaging output"
"$DOCKER" run "${RUN_FLAGS[@]}" "$IMAGE_NAME" \
mkdir -p "$DOC_WORK/output"
"$DOCKER" run "${RUN_FLAGS[@]}" -v "$DOC_WORK/output:/nt/output" "$IMAGE_NAME" \
bash -x documentation/generator/_scripts/_publish.sh "$BRANCH"
tar -xzf "$DOC_WORK/output/packed-for-shipping.tar.gz" -C "$DOC_WORK/generator"
fi

# 5. Serve the site with a working search (see serve_site above).
if [ -z "${SKIP_SERVE:-}" ]; then
serve_site
fi

echo "==> Done. Generated site is in: $DOC_WORK/generator/_site"
echo " Tarballs (if packaged) are in: $DOC_WORK/output/"
echo " Start a webserver:"
echo " python3 -m http.server --directory $DOC_WORK/generator/_site/"
echo " And then open in your browser:"
echo " http://127.0.0.1:8000/"
if [ -z "${SKIP_SERVE:-}" ]; then
echo " Documentation is served at:"
echo " http://127.0.0.1:$SITE_PORT/"
wait_serve
else
echo " Start a webserver (no search):"
echo " python3 -m http.server --directory $DOC_WORK/generator/_site/"
echo " And then open in your browser:"
echo " http://127.0.0.1:8000/"
fi
23 changes: 23 additions & 0 deletions generator/serve/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Serves a locally built docs site together with a working search,
# mirroring production docs.cfengine.com: nginx serves the static site and
# proxies /docs/search to the search server (see nginx.conf), the same way
# Apache does in production. The search server itself is the server.js
# from NorthernTechHQ/infra.
#
# Expected mounts (see serve_site in build-locally.sh):
# /site built _site (nginx root)
# /docs/<branch> the same _site, where server.js looks for
# <branch>/assets/searchIndex under DOCS_PATH=/docs
# /search/server.js search server from the infra repo
FROM nginx:alpine

# flexsearch must match the version the search index was built with
# (nt-docs scripts/search/index)
ARG FLEXSEARCH_VERSION=0.7.31
RUN apk add --no-cache nodejs npm \
&& npm install --global "flexsearch@${FLEXSEARCH_VERSION}"

COPY nginx.conf /etc/nginx/conf.d/default.conf

# server.js resolves flexsearch from the global node_modules via NODE_PATH
CMD ["sh", "-c", "NODE_PATH=$(npm root -g) node /search/server.js & exec nginx -g 'daemon off;'"]
11 changes: 11 additions & 0 deletions generator/serve/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Same routing as production Apache (NorthernTechHQ/infra,
# files/docs_cfengine/apache/docs.conf):
# ProxyPass "/docs/search" "http://localhost:8082"
server {
listen 8000;
root /site;

location /docs/search/ {
proxy_pass http://127.0.0.1:8082/;
}
}
Loading