
Local Build Check
- 1 installs
- 772 repo stars
- Updated August 1, 2026
- mongodb/docs
local-build-check skill documents Run local docs build checks on pending content/ changes before opening a PR.
About
local-build-check skill documents Run local docs build checks on pending content/ changes before opening a PR. Catches broken directives, bad refs, missing includes, invalid substitutions, and indentation errors using the Snooty RST parser. Applies unambiguous fixes from a learned per-repo knowledge base, surfaces ambiguous ones, an. name: local-build-check description: >
- Run local docs build checks on pending content/ changes before opening a PR.
- User: `check the build before I open a PR`
- Platform-specific setup patterns for local-build-check.
- Evidence-backed steps from upstream SKILL.md.
- When-to-use criteria for local-build-check versus alternatives.
Local Build Check by the numbers
- 1 all-time installs (skills.sh)
- Ranked #765 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
local-build-check capabilities & compatibility
- Capabilities
- local build check quick start · local build check when to use guidance · local build check integration patterns
- Use cases
- database
What local-build-check says it does
Run local docs build checks on pending content/ changes before opening a PR.
Catches broken directives, bad refs, missing includes, invalid substitutions,
npx skills add https://github.com/mongodb/docs --skill local-build-checkAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 772 |
| Last updated | August 1, 2026 |
| Repository | mongodb/docs ↗ |
How do I use local-build-check correctly?
Run local docs build checks on pending content/ changes before opening a PR. Catches broken directives, bad refs, missing includes, invalid substitutions, and indentation errors using the Snooty RST p
Who is it for?
Teams implementing local-build-check workflows from the catalog.
Skip if: Skip when requirements clearly match a different specialized stack.
When should I use this skill?
User asks about local-build-check, run local docs build checks on pending content/ changes before opening a pr. catches broke.
What you get
Working local-build-check setup with validated configuration and next steps.
Files
Local Build Check
Catch and fix docs build errors before Netlify does using the Snooty RST parser.
When NOT to use
- Changes are entirely in
code-example-tests/→ use Grove skills instead. - Changes are entirely in
platform/→ stop immediately and tell the user
verbatim: "The local build check covers content/ only. For platform/ changes, run pnpm typecheck and/or pnpm lint in the platform/ directory instead." Do not invoke the skill.
- The user just wants a content review, not a build check.
Example interaction
User: check the build before I open a PR
Claude: 1 file changed. Waiting for build...
(build completes; introduced: 1 error, pre-existing: 2 warnings)
Introduced error content/atlas/source/security/set-up-unified-aws-access.txt:47:Unknown directive type "seelso"— renaming to.. seealso::.
(applies Edit, re-runs — 0 introduced errors)
Files fixed: content/atlas/source/security/set-up-unified-aws-access.txtPre-existing warnings (not blocking): 2.
Ready to commit?
Layout
This skill writes to and reads from a per-repo, gitignored directory:
.claude/build-errors/
├── knowledge.md # distilled "pattern → fix recipe" entries (consult first)
└── log.jsonl # append-only raw error log (one JSON object per line)Both files are gitignored by default. To promote the team's collective knowledge so other writers benefit, the user can add an exception:
# .gitignore
.claude/build-errors/
!.claude/build-errors/knowledge.mdThen git add .claude/build-errors/knowledge.md. Mention this option to the user only when a new high-confidence pattern has just been added — not on every run.
Step 1: Start the build and consult the knowledge base
First, run the venv check alone — this takes ~5ms and must happen before the background build starts so the result is accurate:
[[ -d "${HOME}/.cache/docs-mongodb-internal/local-build-check/.venv" ]] && echo "cached" || echo "first-run"If this returns first-run, immediately tell the user:
First run — Snooty is installing (uv, venv, snooty-parser clone). This
will take 1–3 minutes. Subsequent runs are faster: seconds for small
projects, ~45s for large ones like Atlas (~4,700 files).
Then, in the same response turn, fire these three tool calls in parallel — the build starts immediately, nothing else blocks it:
Bash tool (`run_in_background: true`):
bash .claude/skills/local-build-check/scripts/check.shUse bash explicitly — do not rely on the execute bit. The script self-verifies the environment (uv, Snooty venv), detects changed projects via its own git diff, and exits with "No pending content/ changes — nothing to build-check." if there is nothing to check.
Read tool: .claude/build-errors/knowledge.md (if it exists).
Bash tool (foreground, fast):
{ git diff --name-only origin/main...HEAD -- 'content/**'; \
git diff --name-only HEAD -- 'content/**'; \
git ls-files --others --exclude-standard 'content/**'; } | sort -uThis gives you the changed file list for knowledge base matching.
Once these three calls return, check the knowledge base: if any entry's Match files glob overlaps with the changed paths, output a heads-up to the user:
Heads-up from knowledge base: The following known pattern applies to
your changed files:<pattern name>—<Common cause one-liner>. I'll
flag it if the build surfaces it.
Do not preemptively edit files based on the knowledge base — only apply a fix after the build actually surfaces the matching error.
Output a one-line status: "N files changed. Waiting for build..."
Then go completely silent — no tool calls, no reads, no commands — until the background-task notification arrives. There is nothing useful to check mid-build: check.sh deletes /tmp/lbc-latest.log at startup, so that file does not exist while the build runs. Any read attempt returns "file not found." Wait for the notification.
When the background-task notification arrives, read its output:
- If it printed "No pending
content/changes — nothing to build-check",
report that phrase verbatim and stop. Do not present any build result table.
- Otherwise, read
/tmp/lbc-latest.log(the symlinkcheck.shwrote at
the very end of the run) to get the full structured output for Step 2. Do not search for the timestamped path — the symlink is always current.
If the symlink does not exist yet: snooty build internally spawns worker subprocesses that may outlive the main snooty process. check.sh writes the symlink only after all diagnostics are parsed, so the symlink's existence — not any snooty process being alive — is the correct completion signal. If /tmp/lbc-latest.log is absent after the notification, read it directly without polling for snooty processes. If it still does not appear within ~10s, re-check once with a foreground Bash read of the path; do not start a pgrep polling loop.
This runs the Snooty RST parser against each changed project. It sets up a Python venv (using uv; installs itself if missing), runs snooty build with DIAGNOSTICS_FORMAT=JSON, and parses the output for ERROR and WARNING diagnostics. A non-zero error count sets OVERALL_RC=1. The venv and snooty-parser clone are cached outside the repo at the path shown in the venv check above. To pick up a newer Snooty version, pass --refresh-snooty.
Snooty catches:
| Error type | Example message |
|---|---|
| Broken directive | Unknown directive type "seelso" |
| Bad include path | Include file not found |
| Unknown ref | Unknown target name: "foo" |
| Duplicate label | Duplicate explicit target name |
| Invalid substitution | Undefined substitution referenced |
| Indentation error | Unexpected indentation |
| Malformed role | Unknown interpreted text role |
The script writes structured output to /tmp/local-build-check-<timestamp>.log and prints the path on exit. JSON diagnostics from Snooty are written to a separate /tmp/snooty-diagnostics-<project>-<timestamp>.log. Read both files to extract errors.
Step 2: Parse errors and propose fixes
The current diff for classification purposes is the working-tree diff only: files returned by git diff --name-only HEAD -- 'content/**' and git ls-files --others --exclude-standard 'content/**'. Files that were committed to the branch in earlier commits — but are not modified in the current working tree — are not part of the current diff, regardless of whether they exist on origin/main.
- Introduced (
INTROlines) — diagnostics in files that are part of
the current working-tree diff, plus "Target not found" errors in any file whose referenced label was added, modified, or removed in the current diff (a label changed in one file causes downstream reference failures in unchanged files). Only introduced ERRORs set rc=1 and block. Introduced WARNINGs are surfaced for the user's attention but do not fail the check.
- Pre-existing (
PRElines) — diagnostics in files that are NOT part
of the current working-tree diff and are not traceable to a label touched by the current diff. This includes errors in files committed earlier on this branch. List them in the summary as "pre-existing (not blocking)" with counts only — no file details, no fix options, no questions. Move on immediately. Do not offer to fix pre-existing errors unless the user explicitly asks in a follow-up.
Handling "Page not included in any toctree" warnings
This warning fires when a page isn't reachable from any `.. toctree:: directive and isn't marked :orphan:`. It should always be fixed, not ignored — an unattached page won't appear in the site navigation for offline docs.
There are two causes:
A — New page not wired in (INTRO warning on the new file itself): The diff added a page that isn't yet in any toctree.
1. Collect every introduced file with this warning. 2. Find the correct parent toctree file. Walk up the file hierarchy from the new page until you find the nearest RST file that contains a `.. toctree::` directive covering that subdirectory. Add the new page's filename (without extension) to that directive. 3. Re-run the build check to confirm the warning is gone.
B — Existing pages detached by a malformed toctree entry: The diff modified a file that contains a `.. toctree::` directive. A malformed entry (wrong path, missing title prefix, stray leading space) can silently drop pages from the tree, causing "not included" warnings on those pages even though they haven't changed. The script automatically promotes these to INTRO when any changed file contains a toctree directive, so they appear as INTRO warnings in the log — not PRE. Treat them like any other introduced warning:
1. Read the changed file's toctree entries. 2. Check each entry for malformed syntax: missing title prefix, incorrect path, stray indent, or bare filename without path. 3. Fix any malformed entries, then re-run to confirm the warnings clear.
EOL version handling: Running the build and surfacing diagnostics is read-only and safe for any version, including EOL (pre-7.0) directories. If the changed files are in an EOL version directory, run the check and report all diagnostics as you normally would — but do not apply any fixes. Surface the errors to the user and stop, noting that edits to EOL versions are not permitted.
For each introduced error:
1. Match against knowledge.md. If a recipe matches and the fix is deterministic (e.g., "rename directive ..exmaple:: to ..example::"), apply it via Edit and mark pattern_id in the log. 2. Otherwise, classify. If the fix is unambiguous from the error message alone (e.g., "Unknown target name: foo" with exactly one nearby typo of foo), propose the edit, apply it, and surface what changed. 3. If ambiguous, stop and ask. Do not proceed on unconfirmed details. Present exactly the top three most likely options — no more. Do not append implicit suggestions or partial fixes after the numbered list.
Cap the auto-fix loop at two iterations of (apply → re-run failing stage). If errors remain after two passes, stop and surface them all.
Step 3: Update the log
Append one JSON object per error to .claude/build-errors/log.jsonl:
{"ts":"2026-05-01T14:32:00Z","stage":"snooty","project":"atlas","file":"content/atlas/source/foo.txt","line":42,"severity":"ERROR","message":"Unknown directive type \"exmaple\"","pattern_id":"misspelled-directive","fix_applied":"renamed to example","outcome":"resolved"}Use outcome: "resolved" | "surfaced" | "skipped".
If a new pattern was solved successfully — meaning no entry in the knowledge.md you read in Step 1 matched this error — ask the user whether to add a new entry to knowledge.md. Before asking, verify the error message does not match any existing Match regex in knowledge.md; if it does, increment Times applied successfully on the matching entry instead of proposing a duplicate. Use this template for new entries:
## Pattern: <short name>
- **Match regex:** `<regex against error message>`
- **Match files:** `<glob, e.g. content/**/*.txt>`
- **Common cause:** <one sentence>
- **Fix recipe:** <imperative steps>
- **First seen:** <YYYY-MM-DD>
- **Times applied successfully:** 1If the user agrees, append to knowledge.md and increment the counter on subsequent successful applications.
Step 4: Summary
If the script exited with "no pending content/ changes," report that phrase verbatim — do not say "0 errors found" or present any build result table.
Otherwise report to the user, in this order:
1. Errors found / fixed / remaining, with counts. 2. Files touched by auto-applied fixes (paths, one per line). 3. New knowledge entries added (if any), with a one-line offer to promote knowledge.md for the team if it isn't already. 4. Next step prompt: "Ready to commit?" — wait for explicit instruction (per CLAUDE.md Definition of Done).
Do not run ./lint-docs.sh here — this skill validates the build, not SEO/findability. The user runs lint separately when ready to commit.
Failure modes to handle
- Auto-fix loop hits the cap → list every remaining error grouped by
file, do not retry.
#!/usr/bin/env bash
# Entry script for the local-build-check skill.
# Orchestrates the Snooty RST parser against any content/ projects with
# pending changes. Writes structured output to a log file whose path is
# printed on exit.
#
# Usage:
# check.sh # run on changed projects
# check.sh --project <name> # force a specific project
# check.sh --refresh-snooty # delete cached venv/clone before running
# check.sh --check-env # only verify environment
set -uo pipefail
TS=$(date +%Y%m%d-%H%M%S)
LOG=/tmp/local-build-check-${TS}.log
rm -f /tmp/lbc-latest.log # remove stale symlink from previous run immediately
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || {
echo "[check] not in a git repository" >&2
exit 2
}
# Cache lives outside the skill directory so the skill-validator doesn't count
# the snooty-parser clone and venv as skill content.
SNOOTY_TOOLS="${HOME}/.cache/docs-mongodb-internal/local-build-check"
mkdir -p "$SNOOTY_TOOLS"
CHECK_ENV_ONLY=0
REFRESH_SNOOTY=0
FORCED_PROJECT=""
while [[ $# -gt 0 ]]; do
case "$1" in
--check-env) CHECK_ENV_ONLY=1; shift ;;
--refresh-snooty) REFRESH_SNOOTY=1; shift ;;
--project) FORCED_PROJECT="$2"; shift 2 ;;
*) echo "[check] unknown arg: $1" >&2; exit 2 ;;
esac
done
log() { printf '[check] %s\n' "$*" | tee -a "$LOG"; }
log_raw() { printf '%s\n' "$*" >> "$LOG"; }
check_env() {
log "env ok: node=$(node -v 2>/dev/null || echo n/a)"
return 0
}
if [[ $CHECK_ENV_ONLY -eq 1 ]]; then
check_env
rc=$?
echo "$LOG"
exit $rc
fi
check_env || exit $?
detect_projects() {
if [[ -n "$FORCED_PROJECT" ]]; then
echo "$FORCED_PROJECT"
return
fi
{
git -C "$REPO_ROOT" diff --name-only origin/main...HEAD -- 'content/**' 2>/dev/null || true
git -C "$REPO_ROOT" diff --name-only HEAD -- 'content/**' 2>/dev/null || true
git -C "$REPO_ROOT" ls-files --others --exclude-standard 'content/**' 2>/dev/null || true
} | awk -F/ '$1=="content" && $2!="" {print $2}' | sort -u
}
find_snooty_root() {
# Walk up from a changed file's directory to find the nearest snooty.toml.
# Prints the containing directory, or returns 1 if not found.
local file_path="${REPO_ROOT}/$1"
local dir
dir=$(dirname "$file_path")
while [[ "$dir" != "$REPO_ROOT" && "$dir" != "/" ]]; do
if [[ -f "${dir}/snooty.toml" ]]; then
echo "$dir"
return 0
fi
dir=$(dirname "$dir")
done
return 1
}
get_changed_relative_paths() {
# Returns changed file paths relative to the snooty source root (source/).
# Snooty diagnostic paths are relative to source/, so this lets us match
# diagnostics to the files that introduced them.
local project="$1"
local snooty_root="$2"
local source_root="${snooty_root}/source"
{
git -C "$REPO_ROOT" diff --name-only origin/main...HEAD -- "content/${project}/**" 2>/dev/null || true
git -C "$REPO_ROOT" diff --name-only HEAD -- "content/${project}/**" 2>/dev/null || true
git -C "$REPO_ROOT" ls-files --others --exclude-standard "content/${project}/**" 2>/dev/null || true
} | sort -u | while IFS= read -r f; do
local abs="${REPO_ROOT}/${f}"
if [[ "$abs" == "${source_root}/"* ]]; then
echo "${abs#${source_root}/}"
fi
done | sort -u
}
find_snooty_roots_for_project() {
local project="$1"
{
git -C "$REPO_ROOT" diff --name-only origin/main...HEAD -- "content/${project}/**" 2>/dev/null || true
git -C "$REPO_ROOT" diff --name-only HEAD -- "content/${project}/**" 2>/dev/null || true
git -C "$REPO_ROOT" ls-files --others --exclude-standard "content/${project}/**" 2>/dev/null || true
} | sort -u | while IFS= read -r f; do
[[ -z "$f" ]] && continue
find_snooty_root "$f" 2>/dev/null || true
done | sort -u
# Fallback for forced project with no staged changes: use snooty.toml at
# content/<project>/ directly.
if [[ -n "$FORCED_PROJECT" ]]; then
[[ -f "${REPO_ROOT}/content/${project}/snooty.toml" ]] && \
echo "${REPO_ROOT}/content/${project}"
fi
}
run_snooty() {
local project="$1"
local snooty_root="$2"
local diag_log="/tmp/snooty-diagnostics-${project}-${TS}.log"
local changed_paths="/tmp/snooty-changed-${project}-${TS}.txt"
if [[ $REFRESH_SNOOTY -eq 1 ]]; then
log "[snooty] --refresh-snooty: removing cached venv and clone"
rm -rf "${SNOOTY_TOOLS}/.venv" "${SNOOTY_TOOLS}/snooty-parser"
fi
log "[snooty] setting up environment for $project ($snooty_root)"
log_raw "===== BEGIN snooty-setup $project ====="
(
set -e
cd "$SNOOTY_TOOLS"
if ! command -v uv &>/dev/null; then
echo "Installing uv..."
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
fi
if [[ ! -d ".venv" ]]; then
uv venv --python 3.12 .venv
fi
source .venv/bin/activate
# Skip clone+install if the snooty binary is present AND the editable
# install's source directory is intact. The source dir check catches the
# case where snooty-parser/ was deleted after an editable install: the
# binary exists but the .pth-linked package is gone, causing an import
# crash at runtime. Use --refresh-snooty to force a full reinstall.
if [[ ! -f ".venv/bin/snooty" ]] || [[ ! -d "snooty-parser/snooty" ]]; then
if [[ ! -d "snooty-parser" ]]; then
git clone https://github.com/mongodb/snooty-parser.git
fi
cd snooty-parser
uv pip install flit wheel typing_extensions
uv pip install -e .
fi
) >>"$LOG" 2>&1
local setup_rc=$?
log_raw "===== END snooty-setup $project (rc=$setup_rc) ====="
if [[ $setup_rc -ne 0 ]]; then
log "[snooty] setup FAILED (rc=$setup_rc)"
OVERALL_RC=1
return 1
fi
# Collect changed paths relative to snooty source root for diff-filtering
get_changed_relative_paths "$project" "$snooty_root" > "$changed_paths"
# Check if any changed file contains a .. toctree:: directive.
# Used to promote "Page not included in any toctree" warnings in unchanged
# files: when the user edits a toctree file, detached-page warnings are a
# downstream effect of that change, not pre-existing noise.
local toctree_changed_file="/tmp/snooty-toctree-changed-${project}-${TS}.txt"
local source_root="${snooty_root}/source"
while IFS= read -r rel_path; do
local abs="${source_root}/${rel_path}"
if [[ -f "$abs" ]] && grep -qE '^\.\. toctree::' "$abs" 2>/dev/null; then
echo "$rel_path" >> "$toctree_changed_file"
fi
done < "$changed_paths"
[[ -f "$toctree_changed_file" ]] || touch "$toctree_changed_file"
# Collect RST label names touched by the diff (added or removed lines
# containing ".. _label-name:"). Used to promote "Target not found" errors
# in unchanged files that reference a label defined/broken in the diff.
local affected_labels_file="/tmp/snooty-affected-labels-${project}-${TS}.txt"
{
git -C "$REPO_ROOT" diff origin/main...HEAD -- "content/${project}/**" 2>/dev/null || true
git -C "$REPO_ROOT" diff HEAD -- "content/${project}/**" 2>/dev/null || true
} | grep '^[+-]' \
| sed -nE 's/^[+-].*\.\. _([a-zA-Z0-9][a-zA-Z0-9_-]*):.*/\1/p' \
| sort -u > "$affected_labels_file"
local snooty_bin="${SNOOTY_TOOLS}/.venv/bin/snooty"
local python="${SNOOTY_TOOLS}/.venv/bin/python3"
log "[snooty] building $snooty_root"
log_raw "===== BEGIN snooty-build $project ====="
DIAGNOSTICS_FORMAT=JSON "$snooty_bin" build "$snooty_root" \
>"$diag_log" 2>>"$LOG"
local build_rc=$?
log_raw "===== END snooty-build $project (rc=$build_rc) ====="
# Detect a parser crash: non-zero exit with no JSON diagnostics written.
# Snooty can crash mid-postprocess (e.g. KeyError on malformed directive
# options) before emitting any JSON, producing a false-clean result if we
# only check diagnostic counts. Treat this as a blocking introduced error.
if [[ $build_rc -ne 0 && ! -s "$diag_log" ]]; then
log "[snooty] CRASH: parser exited $build_rc with no diagnostics written"
log_raw "INTRO ERROR ${project} ? Snooty crashed (rc=$build_rc) — no JSON diagnostics produced. Check stderr above for the traceback."
OVERALL_RC=1
return 1
fi
# Categorize diagnostics: introduced by diff vs pre-existing.
# A diagnostic is INTRO if:
# (a) it is in a changed file, OR
# (b) it is a "Target not found" error whose target name was touched
# by the diff (label defined or modified in a changed file), OR
# (c) it is a "Page not included in any toctree" warning and a changed
# file contains a .. toctree:: directive (page detached by toctree edit).
local categorized
categorized=$("$python" - "$diag_log" "$changed_paths" "$affected_labels_file" "$toctree_changed_file" <<'PYEOF'
import sys, json, re
diag_log, changed_file, labels_file, toctree_file = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
with open(changed_file) as f:
changed = {l.strip() for l in f if l.strip()}
with open(labels_file) as f:
affected_labels = {l.strip() for l in f if l.strip()}
with open(toctree_file) as f:
toctree_changed = {l.strip() for l in f if l.strip()}
counts = {'INTRO': {'ERROR': 0, 'WARNING': 0}, 'PRE': {'ERROR': 0, 'WARNING': 0}}
lines = []
with open(diag_log) as f:
for line in f:
try:
d = json.loads(line.strip()).get('diagnostic', {})
sev = d.get('severity', '')
if sev not in ('ERROR', 'WARNING'):
continue
path = d.get('path', '')
msg = d.get('message', '')
in_changed = path in changed
target_in_diff = False
if not in_changed and affected_labels:
m = re.search(r'[Tt]arget not found.*?"(?:label:)?([^"]+)"', msg)
if m and m.group(1) in affected_labels:
target_in_diff = True
toctree_detached = False
if not in_changed and toctree_changed and 'not included in any toctree' in msg.lower():
toctree_detached = True
kind = 'INTRO' if in_changed or target_in_diff or toctree_detached else 'PRE'
counts[kind][sev] += 1
lines.append(f"{kind}\t{sev}\t{path}\t{d.get('start','?')}\t{msg}")
except:
pass
c = counts
print(f"COUNTS\t{c['INTRO']['ERROR']}\t{c['INTRO']['WARNING']}\t{c['PRE']['ERROR']}\t{c['PRE']['WARNING']}")
for l in lines:
print(l)
PYEOF
)
local intro_err intro_warn pre_err pre_warn
IFS=$'\t' read -r _ intro_err intro_warn pre_err pre_warn \
<<< "$(echo "$categorized" | grep '^COUNTS')"
log "[snooty] introduced: errors=${intro_err} warnings=${intro_warn} | pre-existing: errors=${pre_err} warnings=${pre_warn} | diagnostics=$diag_log"
if [[ ${intro_err:-0} -gt 0 || ${intro_warn:-0} -gt 0 ]]; then
log_raw "===== SNOOTY INTRODUCED DIAGNOSTICS ====="
echo "$categorized" | grep '^INTRO' >>"$LOG"
log_raw "===== END INTRODUCED ====="
fi
if [[ ${pre_err:-0} -gt 0 || ${pre_warn:-0} -gt 0 ]]; then
log_raw "===== SNOOTY PRE-EXISTING DIAGNOSTICS (not blocking) ====="
echo "$categorized" | grep '^PRE' >>"$LOG"
log_raw "===== END PRE-EXISTING ====="
fi
[[ ${intro_err:-0} -gt 0 ]] && OVERALL_RC=1
}
PROJECTS=$(detect_projects)
if [[ -z "$PROJECTS" ]]; then
log "no pending content/ changes"
echo "$LOG"
exit 0
fi
log "projects: $(echo $PROJECTS | tr '\n' ' ')"
OVERALL_RC=0
for project in $PROJECTS; do
if [[ ! -f "${REPO_ROOT}/content/${project}/snooty.toml" ]] && \
! find "${REPO_ROOT}/content/${project}" -maxdepth 2 -name snooty.toml -print -quit 2>/dev/null | grep -q .; then
log "skip $project: no snooty.toml found (not a docs site)"
continue
fi
while IFS= read -r snooty_root; do
[[ -z "$snooty_root" ]] && continue
run_snooty "$project" "$snooty_root"
done < <(find_snooty_roots_for_project "$project" | sort -u)
done
log "done (rc=$OVERALL_RC)"
ln -sf "$LOG" /tmp/lbc-latest.log
echo "$LOG"
exit $OVERALL_RC
Related skills
FAQ
What does local-build-check do?
local-build-check skill documents Run local docs build checks on pending content/ changes before opening a PR.
When should I use local-build-check?
User asks about local-build-check, run local docs build checks on pending content/ changes before opening a pr. catches broke.
Is this skill safe to install?
Review the Security Audits panel on this page before installing in production.