From d15c1b1dcbeecadb33e06b4cf7d786ce0dd1065c Mon Sep 17 00:00:00 2001 From: Patrick Lewis <4015312+locus313@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:07:57 -0700 Subject: [PATCH] ci: check docs against scripts Add check-docs.sh and a docs workflow that fails on broken relative links, script folders without a README, and header env vars missing from the folder README. Fix the monitor-domain-hosting README gaps it found. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/scripts/check-docs.sh | 51 +++++++++++++++++++++++++++++ .github/workflows/docs.yml | 34 +++++++++++++++++++ AGENTS.md | 2 +- CONTRIBUTING.md | 5 ++- docs/adr/0003-per-script-readmes.md | 2 +- monitor-domain-hosting/README.md | 23 ++++++++----- 6 files changed, 105 insertions(+), 12 deletions(-) create mode 100755 .github/scripts/check-docs.sh create mode 100644 .github/workflows/docs.yml diff --git a/.github/scripts/check-docs.sh b/.github/scripts/check-docs.sh new file mode 100755 index 0000000..83519ee --- /dev/null +++ b/.github/scripts/check-docs.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# Purpose: Fail when the docs drift from the scripts. +# Checks: (1) relative links in tracked .md files resolve, (2) every script folder has a README.md, +# (3) every env var in a script's "Environment Variables" header appears in its folder README. +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" +fail=0 + +while IFS= read -r md; do + dir=$(dirname "${md}") + while IFS= read -r target; do + path="${target%%#*}" + [ -z "${path}" ] && continue + if [ ! -e "${dir}/${path}" ]; then + echo "BROKEN LINK: ${md} -> ${target}" + fail=1 + fi + done < <(grep -oE '\]\(\.{1,2}/[^)]*\)|\]\(\.{1,2}\)' "${md}" | sed -E 's/^\]\(//; s/\)$//' || true) +done < <(git ls-files '*.md' ':!:.github/skills/*' ':!:.github/agents/*') + +while IFS= read -r script; do + dir=$(dirname "${script}") + readme="${dir}/README.md" + if [ ! -f "${readme}" ]; then + echo "MISSING README: ${readme} (for ${script})" + fail=1 + continue + fi + vars=$(awk ' + /^(#|REM)[[:space:]]+Environment Variables:/ { in_block = 1; next } + in_block && !/^(#|REM)[[:space:]]/ { in_block = 0 } + in_block && !/^(#|REM)[[:space:]]+NOTE:/ && /^(#|REM)[[:space:]]+(-[[:space:]]+)?[A-Z][A-Z0-9_]+[[:space:]]*[-:]/ { + line = $0 + sub(/^(#|REM)[[:space:]]+(-[[:space:]]+)?/, "", line) + sub(/[[:space:]]*[-:].*/, "", line) + print line + } + ' "${script}") + for var in ${vars}; do + if ! grep -q "\`${var}\`" "${readme}"; then + echo "UNDOCUMENTED ENV VAR: ${var} is in ${script} header but not in ${readme}" + fail=1 + fi + done +done < <(git ls-files '*.sh' '*.bat' ':!:.github/*') + +if [ "${fail}" -eq 0 ]; then + echo "Docs checks passed" +fi +exit "${fail}" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..1c6d048 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,34 @@ +name: Docs + +on: + pull_request: + paths: + - '**.md' + - '**.sh' + - '**.bat' + - '.github/scripts/check-docs.sh' + - '.github/workflows/docs.yml' + push: + branches: + - main + paths: + - '**.md' + - '**.sh' + - '**.bat' + - '.github/scripts/check-docs.sh' + - '.github/workflows/docs.yml' + +permissions: + contents: read + +jobs: + docs-check: + name: Docs match scripts + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 1 + + - name: Check links, folder READMEs and documented env vars + run: .github/scripts/check-docs.sh diff --git a/AGENTS.md b/AGENTS.md index a564664..4a58590 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -82,7 +82,7 @@ A new script | Root `README.md` Scripts table, the table above, and the PID lock ## CI and Releases -CI runs shellcheck, the self-update drift check, the self-update test and actionlint; run the first three locally. Never tag releases by hand — merging the auto-opened "Release vYYYY.MM.DD" PR does it. Details are in `CONTRIBUTING.md` → **CI** and **Releases**. +CI runs shellcheck, the self-update drift check, the self-update test and actionlint; run the first three and `.github/scripts/check-docs.sh` locally. Never tag releases by hand — merging the auto-opened "Release vYYYY.MM.DD" PR does it. Details are in `CONTRIBUTING.md` → **CI** and **Releases**. ## Adding a New Script diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6d15e13..d23f857 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,6 +87,7 @@ Before opening a PR: 2. Run the CI helpers locally: - `.github/scripts/check-self-update.sh` - `.github/scripts/test-self-update.sh` + - `.github/scripts/check-docs.sh` 3. Test in a staging or dev Plesk environment and cover the error paths: missing credentials, an empty database list, a missing directory, and concurrent runs (PID lock). 4. Check that files written on Linux have restrictive permissions (600). 5. Test Windows scripts with paths containing spaces and parentheses. @@ -100,6 +101,8 @@ Before opening a PR: - `.github/scripts/test-self-update.sh`: runs the gated updater against a stubbed `curl` (update, pin, checksum mismatch, no release, bad version) - actionlint on the workflows +`.github/workflows/docs.yml` runs `.github/scripts/check-docs.sh` (also on markdown-only changes): relative links resolve, every script folder has a `README.md`, and every env var in a script's header "Environment Variables" block appears in backticks in that README. + ## Releases Never tag by hand. Add a `CHANGELOG.md` entry under `[Unreleased]` for any script change users should pick up. On pushes to `main` that have unreleased entries, `release-pr.yml` opens or updates a "Release vYYYY.MM.DD" PR that renames `[Unreleased]`. Merging it triggers `release.yml`, which re-runs shellcheck and the drift check, then creates the tag and release with `SHA256SUMS`. This needs the repository setting *Actions → Allow GitHub Actions to create pull requests*. @@ -120,4 +123,4 @@ Folder README template: 6. Scheduling 7. Troubleshooting -Keep the env var table in the README in step with the script header. Update `GLOSSARY.md` when you introduce or change a domain term, and add an ADR in `docs/adr/` only for decisions that are hard to reverse, surprising without context, and the result of a real trade-off. +List every header env var in the README table (CI enforces this). Update `GLOSSARY.md` when you introduce or change a domain term, and add an ADR in `docs/adr/` only for decisions that are hard to reverse, surprising without context, and the result of a real trade-off. diff --git a/docs/adr/0003-per-script-readmes.md b/docs/adr/0003-per-script-readmes.md index 8fdca78..4f04670 100644 --- a/docs/adr/0003-per-script-readmes.md +++ b/docs/adr/0003-per-script-readmes.md @@ -15,4 +15,4 @@ Each script is meant to be copied onto a server on its own, and its users are sy - Changing a script means editing its folder README, so the maintenance matrix in `AGENTS.md` points at folder READMEs. - Shared behaviour such as self-update is documented once in the root README and linked from each folder. -- Environment variables are listed in both the script header and the folder README; there is no CI check for drift between them. +- Environment variables are listed in both the script header and the folder README; `check-docs.sh` fails CI when a header variable is missing from the README. Defaults and descriptions are not checked. diff --git a/monitor-domain-hosting/README.md b/monitor-domain-hosting/README.md index 51442c3..64c62c6 100644 --- a/monitor-domain-hosting/README.md +++ b/monitor-domain-hosting/README.md @@ -25,15 +25,20 @@ monitor-aspnet.bat example.com recipient@example.com ## Configuration -Set these variables at the top of `monitor-aspnet.bat` before the first run: - -| Variable | Description | -| --- | --- | -| `MYSQL_PASSWORD` | Replace `` with your Plesk MySQL admin password | -| `SMTP_SERVER` | External SMTP relay hostname | -| `SMTP_PORT` | Typically `25`, `465` (SSL) or `587` (STARTTLS) | -| `SMTP_AUTH_USER` / `SMTP_AUTH_PASS` | Relay credentials; leave blank if not required | -| `SMTP_SECURE` | `ssl` or `starttls` if required; blank for plain SMTP | +Before the first run, edit `monitor-aspnet.bat`: replace the `MYSQL_PASSWORD` placeholder and set the SMTP defaults. Each SMTP value can also be overridden by an environment variable of the same name. + +| Variable | Default | Description | +| --- | --- | --- | +| `MYSQL_PASSWORD` | `` | Set in the script: your Plesk MySQL admin password | +| `MYSQL_PORT` | `8306` | Plesk MySQL port (set in the script) | +| `DOMAIN` | none | Domain to monitor; the first argument overrides it | +| `NOTIFY_EMAIL` | none | Alert recipient; the second argument overrides it | +| `SMTP_SERVER` | `mail.example.com` | External SMTP relay hostname | +| `SMTP_PORT` | `25` | Typically `25`, `465` (SSL) or `587` (STARTTLS) | +| `SMTP_AUTH_USER` / `SMTP_AUTH_PASS` | blank | Relay credentials; leave blank if not required | +| `SMTP_SECURE` | blank | `ssl` or `starttls` if required; blank for plain SMTP | +| `SMTP_FROM` | `plesk-monitor@` | Sender address | +| `STATE_DIR` | `%TEMP%\plesk-monitor` | Directory for state files | > [!WARNING] > Never commit real passwords. Keep the `` placeholder in the repository and edit only your server's copy.