From d7cf0b7fac33e7f443cadb11e28144457627c34a Mon Sep 17 00:00:00 2001 From: Michael Harp Date: Wed, 12 Aug 2026 08:41:30 -0400 Subject: [PATCH] Add test CI: internal link check and markdownlint Mirrors the openvox-docs approach. A new test workflow runs two jobs on PRs: html-proofer over the built _site (internal links, images, and scripts only -- external checks are flaky because sites like Slack 403 non-browser clients), and markdownlint-cli2 over tracked Markdown files using the openvox-docs ruleset. The Images/Scripts checks also verify hashed Vite asset paths, so a stale manifest fails CI instead of shipping broken pages. Co-Authored-By: Claude Fable 5 Signed-off-by: Michael Harp --- .github/workflows/test.yml | 54 ++++++++++++++++++++++ .markdownlint.yaml | 30 +++++++++++++ Gemfile | 4 ++ Gemfile.lock | 92 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 .markdownlint.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..9594b74 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,54 @@ +name: Test + +on: + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + markdown_lint: + name: Markdown lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: List tracked Markdown files + id: tracked-markdown + shell: bash + # Lint git-tracked files only, so build output (_site, assets) and + # node_modules never leak into the lint run. + run: | + { + echo 'files<> "${GITHUB_OUTPUT}" + - name: Lint tracked Markdown files + uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.0.0 + with: + globs: ${{ steps.tracked-markdown.outputs.files }} + + internal_links: + name: Internal links + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '26' + cache: 'npm' + - uses: ruby/setup-ruby@89f90524b88a01fe6e0b732220432cc6142926af # v1.313.0 + with: + ruby-version: '3.4' + bundler-cache: true + - name: Install npm dependencies + run: npm ci + - name: Build assets + run: npm run build + - name: Build site + run: bundle exec jekyll build + # External links are checked manually, not in CI: sites like Slack + # 403 non-browser clients, which makes external checks flaky. + # Images/Scripts verify built asset paths exist, so a stale Vite + # manifest fails here instead of shipping broken pages. + - name: Check internal links and assets + run: bundle exec htmlproofer _site --disable-external --no-enforce-https --ignore-empty-alt --checks Links,Images,Scripts diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..6cf1294 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,30 @@ +--- +# Mirrors the openvox-docs ruleset so Markdown conventions match across +# OpenVox repos. +default: true + +# Source line length doesn't affect the rendered page, so the cap is high +# (400) to flag only genuinely unreadable outliers rather than forcing +# reflows of normal prose. Table rows can't wrap, so they're exempt. +line-length: + line_length: 400 + tables: false + +blank_lines: + maximum: 2 + +no-duplicate-heading: + siblings_only: true + +single-title: + front_matter_title: "" + +no-inline-html: + allowed_elements: + - br + +descriptive-link-text: false + +table: + style: 'consistent' + aligned_delimiter: true diff --git a/Gemfile b/Gemfile index 066e30b..27e2490 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,7 @@ source "https://rubygems.org" gem "jekyll", "~> 4.4" + +group :development do + gem "html-proofer", "~> 5.0" +end diff --git a/Gemfile.lock b/Gemfile.lock index 128d022..0199ab4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,16 +1,30 @@ GEM remote: https://rubygems.org/ specs: + Ascii85 (2.0.1) addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) + afm (1.0.0) + async (2.44.1) + console (~> 1.29) + fiber-annotation + io-event (~> 1.11) base64 (0.3.0) + benchmark (0.5.0) bigdecimal (4.1.2) colorator (1.1.0) concurrent-ruby (1.3.8) + console (1.37.0) + fiber-annotation + fiber-local (~> 1.1) + json csv (3.3.6) em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) + ethon (0.18.0) + ffi (>= 1.15.0) + logger eventmachine (1.2.7) ffi (1.17.4) ffi (1.17.4-aarch64-linux-gnu) @@ -23,6 +37,10 @@ GEM ffi (1.17.4-x86_64-darwin) ffi (1.17.4-x86_64-linux-gnu) ffi (1.17.4-x86_64-linux-musl) + fiber-annotation (0.2.0) + fiber-local (1.1.0) + fiber-storage + fiber-storage (1.0.1) forwardable-extended (2.6.0) google-protobuf (4.35.1) bigdecimal @@ -51,9 +69,21 @@ GEM google-protobuf (4.35.1-x86_64-linux-musl) bigdecimal rake (~> 13.3) + hashery (2.1.2) + html-proofer (5.2.2) + addressable (~> 2.3) + async (~> 2.1) + benchmark (~> 0.5) + nokogiri (~> 1.13) + pdf-reader (~> 2.11) + rainbow (~> 3.0) + typhoeus (~> 1.3) + yell (~> 2.0) + zeitwerk (~> 2.5) http_parser.rb (0.8.1) i18n (1.15.2) concurrent-ruby (~> 1.0) + io-event (1.19.4) jekyll (4.4.1) addressable (~> 2.4) base64 (~> 0.2) @@ -89,9 +119,36 @@ GEM rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) mercenary (0.4.0) + mini_portile2 (2.8.9) + nokogiri (1.19.4) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + nokogiri (1.19.4-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.19.4-aarch64-linux-musl) + racc (~> 1.4) + nokogiri (1.19.4-arm-linux-gnu) + racc (~> 1.4) + nokogiri (1.19.4-arm-linux-musl) + racc (~> 1.4) + nokogiri (1.19.4-arm64-darwin) + racc (~> 1.4) + nokogiri (1.19.4-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.19.4-x86_64-linux-gnu) + racc (~> 1.4) + nokogiri (1.19.4-x86_64-linux-musl) + racc (~> 1.4) pathutil (0.16.2) forwardable-extended (~> 2.6) + pdf-reader (2.16.0) + Ascii85 (>= 1.0, < 3.0, != 2.0.0) + afm (>= 0.2.1, < 2) + hashery (~> 2.0) + ttfunk public_suffix (7.0.5) + racc (1.8.1) + rainbow (3.1.1) rake (13.4.2) rb-fsevent (0.11.2) rb-inotify (0.11.1) @@ -132,8 +189,13 @@ GEM google-protobuf (~> 4.31) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) + ttfunk (1.7.0) + typhoeus (1.6.0) + ethon (>= 0.18.0) unicode-display_width (2.6.0) webrick (1.9.2) + yell (2.2.2) + zeitwerk (2.8.3) PLATFORMS aarch64-linux-android @@ -157,16 +219,23 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES + html-proofer (~> 5.0) jekyll (~> 4.4) CHECKSUMS + Ascii85 (2.0.1) sha256=15cb5d941808543cbb9e7e6aea3c8ec3877f154c3461e8b3673e97f7ecedbe5a addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af + afm (1.0.0) sha256=5bd4d6f6241e7014ef090985ec6f4c3e9745f6de0828ddd58bc1efdd138f4545 + async (2.44.1) sha256=079bce0d019fb27cc58ec110fd9b9af4991d97b801d1c6cc6def1d1954dcf9b2 base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b + benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd colorator (1.1.0) sha256=e2f85daf57af47d740db2a32191d1bdfb0f6503a0dfbc8327d0c9154d5ddfc38 concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1 + console (1.37.0) sha256=8093b286c2595a063849c098594fee5155ecc7967d36f3aa8cbe7569c8f3efd7 csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456 em-websocket (0.5.3) sha256=f56a92bde4e6cb879256d58ee31f124181f68f8887bd14d53d5d9a292758c6a8 + ethon (0.18.0) sha256=b598afc9f30448cb068b850714b7d6948e941476095d04f90a4ac65b8d6efcb2 eventmachine (1.2.7) sha256=994016e42aa041477ba9cff45cbe50de2047f25dd418eba003e84f0d16560972 ffi (1.17.4) sha256=bcd1642e06f0d16fc9e09ac6d49c3a7298b9789bcb58127302f934e437d60acf ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df @@ -179,6 +248,9 @@ CHECKSUMS ffi (1.17.4-x86_64-darwin) sha256=aa70390523cf3235096cf64962b709b4cfbd5c082a2cb2ae714eb0fe2ccda496 ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e + fiber-annotation (0.2.0) sha256=7abfadf1d119f508867d4103bf231c0354d019cc39a5738945dec2edadaf6c03 + fiber-local (1.1.0) sha256=c885f94f210fb9b05737de65d511136ea602e00c5105953748aa0f8793489f06 + fiber-storage (1.0.1) sha256=f48e5b6d8b0be96dac486332b55cee82240057065dc761c1ea692b2e719240e1 forwardable-extended (2.6.0) sha256=1bec948c469bbddfadeb3bd90eb8c85f6e627a412a3e852acfd7eaedbac3ec97 google-protobuf (4.35.1) sha256=a3a6471331d918f58dfa4d014a8f6286f0af2cf4840216bde52fcf2ea3fe3726 google-protobuf (4.35.1-aarch64-linux-gnu) sha256=50ca44d0eeff3f8475e630a1accdd974256f3510694d574e2c9d6119ea8bc9e1 @@ -189,8 +261,11 @@ CHECKSUMS google-protobuf (4.35.1-x86_64-darwin) sha256=66b62b4df00931018a692806df66393efa960d6d2b7da69735187249f950d3ee google-protobuf (4.35.1-x86_64-linux-gnu) sha256=c786439087512a3fbd199e9897d265b855f951d4027e218ea55e858d45969edd google-protobuf (4.35.1-x86_64-linux-musl) sha256=91890eb0002934a339fdb7d77a147c46b7474b6799db27872b747b905837f744 + hashery (2.1.2) sha256=d239cc2310401903f6b79d458c2bbef5bf74c46f3f974ae9c1061fb74a404862 + html-proofer (5.2.2) sha256=6e5e63c89ef9413246eeffaeba8dd03db62546992d7a2b85d166ac2a5903a241 http_parser.rb (0.8.1) sha256=9ae8df145b39aa5398b2f90090d651c67bd8e2ebfe4507c966579f641e11097a i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5 + io-event (1.19.4) sha256=c291146e2e70849b9d6b64078f8e98f0154bfd3bc4f9759861ff13e9895e0cce jekyll (4.4.1) sha256=4c1144d857a5b2b80d45b8cf5138289579a9f8136aadfa6dd684b31fe2bc18c1 jekyll-sass-converter (3.1.0) sha256=83925d84f1d134410c11d0c6643b0093e82e3a3cf127e90757a85294a3862443 jekyll-watch (2.2.1) sha256=bc44ed43f5e0a552836245a54dbff3ea7421ecc2856707e8a1ee203a8387a7e1 @@ -201,8 +276,21 @@ CHECKSUMS listen (3.10.0) sha256=c6e182db62143aeccc2e1960033bebe7445309c7272061979bb098d03760c9d2 logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 mercenary (0.4.0) sha256=b25a1e4a59adca88665e08e24acf0af30da5b5d859f7d8f38fba52c28f405138 + mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289 + nokogiri (1.19.4) sha256=50c951611c92bca05c51411aef45f1cbc50f2821c4802758c5c6d34696533ab5 + nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f + nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af + nokogiri (1.19.4-arm-linux-gnu) sha256=a301313e38bb065d68239e79734bcd6f56fb6efaacebde29e9abf2a4735340ca + nokogiri (1.19.4-arm-linux-musl) sha256=588923c101bcfa78869734d247d25b598674323e7f22474fc468f6e5647311eb + nokogiri (1.19.4-arm64-darwin) sha256=a46db9853286e6597b36ebc6953817d15acf3a299583eb3f89fdc6f91dd63527 + nokogiri (1.19.4-x86_64-darwin) sha256=7fd17057d3e1f00e9954a74b3cd76595d3d4a5ef233b7ed9599047c204f70551 + nokogiri (1.19.4-x86_64-linux-gnu) sha256=379fae440b28915e3f19d752ce2dcf8465ed2b2fbefd2a7ca0dd497bc981a06a + nokogiri (1.19.4-x86_64-linux-musl) sha256=17dfb7c1fa194ae02fbf7c51a7afc8d278045ab3fdacfd86f91d02d7b274470b pathutil (0.16.2) sha256=e43b74365631cab4f6d5e4228f812927efc9cb2c71e62976edcb252ee948d589 + pdf-reader (2.16.0) sha256=bf1b5564c085264d8279bde3cf1467778b75841494d20c0fe7da3dad475f3732 public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 + racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f + rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e @@ -225,8 +313,12 @@ CHECKSUMS sass-embedded (1.102.0-x86_64-linux-gnu) sha256=9815b7604123a9a44a65d44d5e58e7af1c7005174b745cda83bad4c9e3760ab3 sass-embedded (1.102.0-x86_64-linux-musl) sha256=9e3c15eb5d83784adf3b946d4fc5d8a32aed25566a2fee939f25dd1e1d217ddf terminal-table (3.0.2) sha256=f951b6af5f3e00203fb290a669e0a85c5dd5b051b3b023392ccfd67ba5abae91 + ttfunk (1.7.0) sha256=2370ba484b1891c70bdcafd3448cfd82a32dd794802d81d720a64c15d3ef2a96 + typhoeus (1.6.0) sha256=bacc41c23e379547e29801dc235cd1699b70b955a1ba3d32b2b877aa844c331d unicode-display_width (2.6.0) sha256=12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131 + yell (2.2.2) sha256=1d166f3cc3b6dc49a59778ea7156ed6d8de794c15106d48ffd6cbb061b9b26bc + zeitwerk (2.8.3) sha256=2c85125a8467ce069e20123d1e709a08955c9d29c118c25b46b7b7fafdbb92e5 BUNDLED WITH 4.0.3