Repository navigation
ci: add docs checks to every pull request #1167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| name: docs-checks | ||
| # Runs on every pull request into dev or main. Each step reports problems as | ||
| # annotations on the PR's changed lines; later steps still run if an earlier | ||
| # one fails, so one run shows everything. Scripts live in tools/docs-checks/. | ||
| on: | ||
| pull_request: | ||
| branches: | ||
| - dev | ||
| - main | ||
| workflow_dispatch: | ||
| permissions: | ||
| contents: read | ||
| jobs: | ||
| checks: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
| with: | ||
| fetch-depth: 0 | ||
| - uses: actions/setup-python@v7 | ||
| with: | ||
| python-version: 3.x | ||
| - run: pip install -r requirements.txt | ||
| - run: sudo apt-get update && sudo apt-get install -y libimage-exiftool-perl | ||
| - name: List changed files | ||
| id: changed | ||
| run: | | ||
| base="${{ github.event.pull_request.base.sha }}" | ||
| if [ -n "$base" ]; then | ||
| git diff --name-only --diff-filter=AM "$base"...HEAD > changed.txt | ||
| else | ||
| : > changed.txt | ||
| fi | ||
| echo "Changed files:"; cat changed.txt | ||
| - name: Markdown (front matter, headings, alt text, includes) | ||
| run: python tools/docs-checks/check_markdown.py docs | ||
| - name: No secrets in text | ||
| if: always() | ||
| run: python tools/docs-checks/check_secrets.py docs | ||
| - name: Images (GPS location data, file size) | ||
| if: always() | ||
| run: CHANGED_FILES="$(cat changed.txt)" python tools/docs-checks/check_images.py docs/assets | ||
| - name: Build the site | ||
| if: always() | ||
| env: | ||
| DISABLE_MKDOCS_2_WARNING: "true" | ||
| run: mkdocs build -d site | ||
| - name: Links, anchors, and image alt text in the built site | ||
| if: always() | ||
| run: python tools/docs-checks/check_links.py site docs | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,7 @@ mkdocs serve | |
| 4. Verify the build is clean (no broken links or missing-plugin errors) via `mkdocs build`. | ||
| 5. Fill out the PR template and link any related issues. | ||
| 6. Use PR title format: `<type>: <description>` (types: `docs`, `fix`, `feat`, `chore`) | ||
| 7. Automated checks run on every PR: front matter, heading levels, image alt text, snippet includes, secrets in text, GPS location data in photos, and broken links or anchors on the built site. Problems appear as comments on the changed lines. See [tools/docs-checks/README.md](tools/docs-checks/README.md) for what each check looks for, how to fix it, and how to run them locally. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π‘ Minor | β‘ Quick win Call the findings check annotations, not comments. The workflow reports findings through GitHub Actions checks. Contributors who look for pull request comments can miss the annotations in the Checks tab. Replace βcomments on the changed linesβ with βcheck annotations on changed lines,β and point readers to the Checks tab. (docs.github.com) π€ Prompt for AI Agents |
||
|
|
||
| ## Key files and folders | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -415,8 +415,16 @@ shrinks PNGs, so compress video before committing it. | |
| - **Phone recordings** (portrait) take `class="phone-media"`, which | ||
| caps them at phone width and centers them. The class lives in | ||
| `docs/stylesheets/extra.css`; never size media inline. | ||
| - **Trim personal details** (names, email addresses, home names, | ||
| notifications) out of every recording before it's published. | ||
| - **Trim personal details and secrets** (names, email addresses, home | ||
| names, notifications, Wi-Fi passwords, API keys) out of every recording | ||
| and screenshot before it's published. Compile and install logs print | ||
| the device's full configuration, so end a recording before the log | ||
| scrolls through it, and check YAML editor shots for an `api: | ||
| encryption: key:` value. | ||
| - **Strip location data from photos.** Phones save the GPS location in | ||
| every photo. Remove it before committing: | ||
| `exiftool -gps:all= -overwrite_original docs/assets/<file>`. The PR | ||
| checks fail on any photo that still has it. | ||
|
Comment on lines
+424
to
+427
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π‘ Minor | β‘ Quick win π Supported by static analysisπ Script executed: cat tools/docs-checks/check_images.py
sed -n '410,432p' STYLE_GUIDE.mdRepository: ApolloAutomation/docs Length of output: 3420 π Web query:
π‘ Result: Use the wildcard GPS removal command.
Update the command in π Suggested fix-exiftool -gps:all= -overwrite_original docs/assets/<file>
+exiftool "-gps*=" -overwrite_original docs/assets/<file>- exiftool -gps:all= -overwrite_original <file>
+ exiftool "-gps*=" -overwrite_original <file>- print(f"::error file={path}::Photo contains GPS location data. Remove it with: exiftool -gps:all= -overwrite_original \"{path}\"")
+ print(f"::error file={path}::Photo contains GPS location data. Remove it with: exiftool \"-gps*=\" -overwrite_original \"{path}\"")π€ Prompt for AI Agents |
||
| - **Compress** with H.264, no audio track, and fast start, scaled to | ||
| 540px wide for clips and 720px for a full phone walkthrough. Aim for | ||
| under 1 MB per clip and a few MB for a walkthrough: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Air quality sensor comparison | ||
| description: "A technical comparison of air quality sensors used with the AIR-1, including performance, accuracy, and specifications." | ||
| --- | ||
| --8<-- "products/air1/additional-info/air-quality-sensor-comparison.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: MTR-1 Home Assistant zone configuration | ||
| description: "Tutorial for MTR-1 Home Assistant Zone Configuration." | ||
| --- | ||
| --8<-- "products/mtr1/setup/zones-ha.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: HLK Radar Tool app | ||
| description: "Tutorial for MTR-1 HLKRadarTool app Zone Configuration." | ||
| --- | ||
| --8<-- "homey/products/mtr1/setup/zones-hlk.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Facebook | ||
| description: "Join the Apollo Automation community group on Facebook." | ||
| --- | ||
| --8<-- "products/general/contact-us/Facebook.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: GitHub | ||
| description: "Apollo Automation on GitHub, where the firmware and hardware files live." | ||
| --- | ||
| --8<-- "products/general/contact-us/GitHub.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Instagram | ||
| description: "Follow Apollo Automation on Instagram." | ||
| --- | ||
| --8<-- "products/general/contact-us/Instagram.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Reddit | ||
| description: "The Apollo Automation subreddit." | ||
| --- | ||
| --8<-- "products/general/contact-us/Reddit.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: YouTube | ||
| description: "The Apollo Automation YouTube channel, with setup guides, reviews, and live streams." | ||
| --- | ||
| --8<-- "products/general/contact-us/YouTube.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Apollo Automation shop | ||
| description: "The Apollo Automation shop, where you can buy sensors, probes, and accessories." | ||
| --- | ||
| --8<-- "products/general/contact-us/apollo-automation-shop.md:6:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Our team | ||
| description: "Meet the people behind Apollo Automation." | ||
| --- | ||
| --8<-- "products/general/contact-us/our-team.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Getting started | ||
| description: "Generic getting started article." | ||
| --- | ||
| --8<-- "homey/products/general/setup/getting-started.md:6:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Getting started | ||
| description: "Generic getting started article." | ||
| --- | ||
| --8<-- "homey/products/general/setup/getting-started.md:6:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Temp and humidity calibration and offset examples | ||
| description: "Temp and Humidity Calibration and Offset Examples." | ||
| --- | ||
| --8<-- "products/general/temp-hum-calibration.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: LED-1 controlling Govee lights | ||
| description: How to use the Apollo LED-1 to control your Govee/3rd Party permanent outdoor LED lights! | ||
| --- | ||
| --8<-- "products/led1/examples/led-1-controlling-govee-lights.md:7:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: LED-1 FAQ | ||
| description: "Frequently asked questions about the LED-1 controller." | ||
| --- | ||
| --8<-- "products/led1/faq.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Introduction | ||
| description: "Documentation for LED-1, including setup, usage, and best practices." | ||
| --- | ||
| --8<-- "products/led1/introduction.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Getting started | ||
| description: "Getting started with your new LED-1!" | ||
| --- | ||
| --8<-- "products/led1/setup/getting-started-led1.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: LED-1 general tips | ||
| description: "Multiple helpful tips to use your LED-1 to the fullest!" | ||
| --- | ||
| --8<-- "products/led1/setup/led1-general-tips.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: LED-1 pinout guide | ||
| description: "LED-1 Pinout Guide" | ||
| --- | ||
| --8<-- "products/led1/setup/led1-pinout-guide.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: How to replace the LED-1 10 A fuse | ||
| description: "Tutorial showing how to replace the 10 A Fuse on the LED-1." | ||
| --- | ||
| --8<-- "products/led1/setup/led1-replace-fuse.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Putting the LED-1 in boot mode | ||
| description: "Step by step guide for putting The LED-1 In Boot Mode." | ||
| --- | ||
| --8<-- "products/led1/troubleshooting/led1-boot-mode.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: How to find the IP address and hostname of your LED-1 | ||
| description: "Step by step guide for finding the IP address and the hostname of your LED-1 device." | ||
| --- | ||
| --8<-- "products/led1/troubleshooting/led1-find-ip-address-and-hostname.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Factory re-flash LED-1 | ||
| description: "Step by step guide for re-flashing the LED-1 back to factory firmware." | ||
| --- | ||
| --8<-- "products/led1/troubleshooting/led1-reflash.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Control an LED strip with the GPIO header | ||
| description: "Wire a WS2812B LED strip to the Apollo GPIO Header and control it from Home Assistant." | ||
| --- | ||
| --8<-- "products/msr2/examples/gpio-header-control-an-led-strip.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: CO2 calibration tutorial (quick) | ||
| description: "Step by step guide for re-calibrating your SCD40 CO2 sensor" | ||
| --- | ||
| --8<-- "products/rpro1/calibrating-co2.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Bluetooth tracker setup | ||
| description: "Tutorial for how to turn your Apollo device into a BLE tracker to track an iPhone!" | ||
| --- | ||
| --8<-- "products/general/setup/bluetooth-tracking.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Getting started | ||
| description: "Generic getting started article." | ||
| --- | ||
| --8<-- "products/general/setup/getting-started.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: How to wake up your sensor | ||
| description: "How to wake up your Apollo device which is currently sleeping." | ||
| --- | ||
| --8<-- "products/general/battery-sensors/wake-up-battery-sensor.md:5:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| --- | ||
| title: Holiday songs buzzer list | ||
| description: "Holiday songs you can play on the buzzer in your H-3." | ||
| --- | ||
| --8<-- "products/general/holiday-songs.md:7:" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # Docs checks | ||
|
|
||
| These scripts run on every pull request into `dev` or `main` | ||
| (`.github/workflows/docs-checks.yml`). Problems show up as comments on the | ||
| changed lines in the PR. Fix them and push again; the checks re-run. | ||
|
|
||
| | Check | Fails the PR on | How to fix | | ||
| | --- | --- | --- | | ||
| | `check_markdown.py` | Missing front matter `title:` or `description:`, heading levels that skip (`##` to `####`), more than one `#` H1, images without alt text, snippet includes that point at a missing file or start inside the included page's front matter | Add the field, use the next heading level, describe the image inside `![...]`, or start the include after the included page's closing `---` | | ||
| | `check_secrets.py` | ESPHome API encryption keys, generated OTA passwords, passwords typed into YAML, and access tokens | Use `!secret` (or a placeholder like `your-wifi-password`). If the value was real, change it on the device or account too | | ||
| | `check_images.py` | Photos with GPS location data (phones add it by default) | `exiftool -gps:all= -overwrite_original docs/assets/<file>` | | ||
| | `check_links.py` | Broken links, links to a missing `#anchor`, filename case mismatches (GitHub Pages is case-sensitive), and images without alt text, checked on the built site | Fix the link target or anchor; match the file name's case exactly | | ||
|
|
||
| Warnings (shown, but they don't fail the PR): em dashes and the filler words | ||
| the style guide bans, and images or recordings over 1.5 MB that the PR adds | ||
| or changes. | ||
|
|
||
| The checks can't read text inside screenshots or recordings. Before adding | ||
| one, look through it for passwords, API keys, email addresses, and other | ||
| personal details, and crop or trim them out (see the style guide). | ||
|
|
||
| ## Run them locally | ||
|
|
||
| ```bash | ||
| pip install -r requirements.txt | ||
| python tools/docs-checks/check_markdown.py docs | ||
| python tools/docs-checks/check_secrets.py docs | ||
| python tools/docs-checks/check_images.py docs/assets | ||
| mkdocs build -d site | ||
| python tools/docs-checks/check_links.py site docs | ||
| ``` | ||
|
|
||
| `check_images.py` needs exiftool (`brew install exiftool` on macOS, | ||
| `sudo apt install libimage-exiftool-perl` on Debian or Ubuntu). | ||
|
Comment on lines
+33
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π‘ Minor | β‘ Quick win π Supported by static analysisπ Script executed: cat tools/docs-checks/README.md
grep -n 'docs_dir\|admonition' mkdocs.yml
grep -n -i -A8 'Admonitions and next steps' STYLE_GUIDE.md | head -60
grep -rn '!!! \|\[!NOTE\]' CONTRIBUTING.md AGENTS.md README.md 2>/dev/null | headRepository: ApolloAutomation/docs Length of output: 2735 Move the exiftool prerequisite above the local-check commands. This README is rendered on GitHub, not by MkDocs, so Suggested fix ## Run them locally
+> [!NOTE]
+> `check_images.py` needs exiftool (`brew install exiftool` on macOS,
+> `sudo apt install libimage-exiftool-perl` on Debian or Ubuntu).
+
```bash
pip install -r requirements.txt
python tools/docs-checks/check_markdown.py docs
python tools/docs-checks/check_secrets.py docs
python tools/docs-checks/check_images.py docs/assets
mkdocs build -d site
python tools/docs-checks/check_links.py site docs- π€ Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| """Image checks for Apollo Docs pull requests. | ||
|
|
||
| Usage: python tools/docs-checks/check_images.py [assets_dir] | ||
|
|
||
| Fails on: photos that carry GPS location metadata (phones add it by | ||
| default, and it publishes where the photo was taken). | ||
| Warns on: images over 1.5 MB, which slow pages down. Set CHANGED_FILES | ||
| (newline-separated paths) to limit this warning to the files a PR changes. | ||
|
|
||
| Needs exiftool (apt install libimage-exiftool-perl, brew install exiftool). | ||
| To fix a GPS error: exiftool -gps:all= -overwrite_original <file> | ||
| """ | ||
| import os | ||
| import shutil | ||
| import subprocess | ||
| import sys | ||
|
|
||
| ASSETS = sys.argv[1] if len(sys.argv) > 1 else "docs/assets" | ||
| EXTS = (".jpg", ".jpeg", ".png", ".webp", ".heic", ".tif", ".tiff") | ||
| LIMIT = 1.5 * 1024 * 1024 | ||
|
|
||
| if not shutil.which("exiftool"): | ||
| print("::error::exiftool is not installed, so GPS metadata can't be checked.") | ||
| sys.exit(1) | ||
|
|
||
| r = subprocess.run( | ||
| ["exiftool", "-q", "-q", "-r", "-if", "$GPSLatitude or $GPSLongitude", "-p", "$Directory/$FileName", | ||
| *sum((["-ext", e.lstrip(".")] for e in EXTS), []), ASSETS], | ||
| capture_output=True, text=True, | ||
| ) | ||
|
Comment on lines
+26
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π Security & Privacy | π Major | β‘ Quick win Fail when ExifTool cannot complete the GPS check. If ExifTool reports a file-read or processing error and produces no matching output, this script exits successfully. GPS data in that file remains unverified. Handle exit status 1 as a check failure and report π§° Toolsπͺ ast-grep (0.45.3)[error] 26-30: Command coming from incoming request (subprocess-from-request) πͺ Ruff (0.16.9)[error] 26-26: (S603) [error] 27-28: Starting a process with a partial executable path (S607) [warning] 28-28: Avoid quadratic list summation Replace with (RUF017) π€ Prompt for AI Agents |
||
| gps = [l for l in r.stdout.splitlines() if l.strip()] | ||
| for path in gps: | ||
| print(f"::error file={path}::Photo contains GPS location data. Remove it with: exiftool -gps:all= -overwrite_original \"{path}\"") | ||
|
|
||
| changed = os.environ.get("CHANGED_FILES") | ||
| only = {os.path.normpath(f) for f in changed.split()} if changed is not None else None | ||
|
Comment on lines
+35
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π‘ Minor | β‘ Quick win Split changed paths by line, not by whitespace. The workflow supplies newline-separated paths. For a changed file such as π€ Prompt for AI Agents |
||
| big = 0 | ||
| for root, _, files in os.walk(ASSETS): | ||
| for name in files: | ||
| p = os.path.join(root, name) | ||
| if only is not None and os.path.normpath(p) not in only: | ||
| continue | ||
| if os.path.getsize(p) > LIMIT and name.lower().endswith(EXTS + (".gif", ".mp4")): | ||
| big += 1 | ||
| print(f"::warning file={p}::{os.path.getsize(p) / 1048576:.1f} MB. Consider resizing (750 px wide is plenty for most images) or compressing.") | ||
|
|
||
| print(f"check_images: {len(gps)} image(s) with GPS data, {big} large file warning(s)") | ||
| sys.exit(1 if gps else 0) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π Security & Privacy | π‘ Minor | β‘ Quick win
Disable persisted checkout credentials.
actions/checkout@v7persists its token by default. Later steps install dependencies and build code from the pull request, although the localgit diffdoes not need the token. Setpersist-credentials: falseto keep the read-only token unavailable to those steps. (github.com)Proposed change
π Committable suggestion
π§° Tools
πͺ zizmor (1.30.1)
[warning] 17-19: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
π€ Prompt for AI Agents
Source: Linters/SAST tools