Skip to content

Serve CI fixtures from localhost so no gating step is live - #7

Merged
mmcky merged 2 commits into
mainfrom
local-server-gate
Aug 3, 2026
Merged

Serve CI fixtures from localhost so no gating step is live#7
mmcky merged 2 commits into
mainfrom
local-server-gate

Conversation

@mmcky

@mmcky mmcky commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

The last item from the test-fixture work, and the exception Copilot caught on #4.

The problem

tests/good-links.html was scanned with fail-on-broken: 'true' and no continue-on-error, so it gated — and its four targets are real sites. A redirect appearing on any of github.com, python.org, jupyter.org or docs.python.org, or an outage while CI happened to run, turns the build red with nothing wrong in the action. #4 could only document that rather than remove it, so tests/README.md had to carry a caveat instead of a rule.

That step had to stay because a genuine 200 over genuine HTTP was the one thing no deterministic fixture provided. Every other one either skips the request entirely (ignore-patterns) or fails transport (.invalid), so none of them exercises what happens when a server actually answers.

The fix

The workflow starts a small server on 127.0.0.1 and asserts four paths against it end to end:

Route Step Asserts
/ok A clean page passes end to end Exits 0 under fail-on-broken: 'true'; broken-link-count == 0
/moved/ok A redirect is followed and counted redirect-count == 1, broken-link-count == 0
/missing An error status is reported broken-link-count == 1
/missing with silent-codes: '404' silent-codes suppresses that error status broken-link-count == 0, exits 0 under fail-on-broken: 'true'

The last pair is what the httpstat.us fixture was for, before the service stopped answering and took the coverage with it. It is now assertable without depending on anyone.

Every gating step in test-action is now offline:

Live Offline
Gates 8 steps
Informational 3 scans

good-links.html and broken-links.html keep their scans, because real DNS, TLS and cross-host redirects are the one thing even a local server cannot cover — but all three live steps now carry continue-on-error and none can turn CI red.

Verification

I ran the generated serve step under the real composite shell (bash --noprofile --norc -e -o pipefail) and then the real link_checker.py against the real generated fixtures:

  • clean → 0 broken, 0 redirects
  • moved → 0 broken, 1 redirect
  • missing → 1 broken, 0 redirects
  • missing with --silent-codes 404 → 0 broken, 0 redirects

All four match the assertions the workflow makes. I also confirmed the Python heredoc dedents correctly out of the YAML block scalar and compiles, and that the server comes up and is polled for readiness rather than raced against.

tests/README.md drops the caveat and states the rule plainly. No change to the action itself — action.yml and link_checker.py are untouched.

Closes the exception Copilot found on #4. tests/good-links.html was
scanned with fail-on-broken and no continue-on-error, so one gating
step depended on github.com, python.org, jupyter.org and
docs.python.org all staying reachable and redirect-free. A redirect
appearing on any of them turns CI red with nothing wrong in the action.

A genuine 200 over genuine HTTP was the reason that step had to stay:
every other deterministic fixture either skips the request
(ignore-patterns) or fails transport (.invalid), so none of them
exercises a successful response. The workflow now starts a small server
on 127.0.0.1 and asserts four paths against it end to end -- a clean
page exits 0 under fail-on-broken, a 301 is followed and counted as one
redirect, a 404 is reported, and the same 404 is silenced by
silent-codes. That last pair is what the httpstat.us fixture was for,
before the service stopped answering and took the coverage with it.

The live scans stay, because real DNS, TLS and cross-host redirects are
the one thing a local server cannot cover, but all three are now
informational and none can gate.

Verified by running the generated serve step under the composite shell
and the real checker against the real fixtures: clean 0 broken /
0 redirects, moved 0 / 1, missing 1 / 0, and missing with
silent-codes 404 back to 0 / 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 3, 2026 06:21
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

🔗 Link Check Results

🚨 1 broken link(s) and 3 redirect(s) were found.

Build Details:


Link Check Summary

  • Total broken links: 1
  • Total redirects found: 3

Broken Links

tests/broken-links.html - 1 broken link(s):
https://this-host-cannot-resolve.invalid - Status: 0 (Connection Error)
Link text: Broken domain

tests/broken-links.html - 3 redirect(s):
🔄 http://github.com/QuantEcon/meta -> https://github.com/QuantEcon/meta (1 redirects)
🔄 https://github.com/QuantEcon/meta/blob/master/README.md -> https://github.com/QuantEcon/meta/blob/main/README.md (1 redirects)
🔄 http://docs.python.org/2.7/library/urllib.html -> https://docs.python.org/2.7/library/urllib.html (1 redirects)

AI-Powered Suggestions

tests/broken-links.html - AI Suggestions:
🤖 http://github.com/QuantEcon/meta
Issue: Redirected 1 times
💡 redirect_update: https://github.com/QuantEcon/meta
Reason: Update to final destination to avoid 1 redirect(s)
🤖 https://github.com/QuantEcon/meta/blob/master/README.md
Issue: Redirected 1 times
💡 redirect_update: https://github.com/QuantEcon/meta/blob/main/README.md
Reason: Update to final destination to avoid 1 redirect(s)
🤖 http://docs.python.org/2.7/library/urllib.html
Issue: Redirected 1 times
💡 redirect_update: https://docs.python.org/2.7/library/urllib.html
Reason: Update to final destination to avoid 1 redirect(s)


Next Steps:

  1. Review the broken links listed above
  2. Update or remove broken links
  3. Consider applying AI suggestions for better alternatives
  4. Push the changes to update this PR

📝 This comment was automatically generated by the AI-Powered Link Checker Action.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes the CI link-check assertions deterministic by serving HTTP fixtures from a local 127.0.0.1 server, removing the last gating dependency on third-party websites while keeping live scans as informational smoke checks.

Changes:

  • Update CI to start a localhost HTTP server and assert clean/redirect/missing/silent-codes behavior end-to-end against it.
  • Convert previously gating live scans (e.g., good-links.html) into informational steps via continue-on-error.
  • Update docs and changelog to reflect that gating coverage is now offline/deterministic.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
.github/workflows/ci.yml Adds a localhost fixture server plus new deterministic CI steps and assertions; makes live scans non-gating.
tests/README.md Updates test-fixture guidance to reflect the new offline gating strategy and removes the prior caveat.
CHANGELOG.md Notes the CI change to locally served fixtures and the remaining informational live scans.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/ci.yml
The handler answered 200 to any path except /missing and /moved, so
"A clean page passes end to end" only proved that the action requested
something and got 200 -- not that it requested the URLs the fixture
names. A typo in a generated fixture, or a regression that mangled the
URL before the request, would have passed green.

Routes are now exhaustive and anything unregistered is a 500 rather
than a 404, so that silent-codes: '404' cannot suppress it. With a 404
default a typo in the missing fixture would still have been silenced
and that step would still have passed.

Verified by injecting a typo into each of the three fixtures in turn:
clean goes from 0 broken to 1, redirect from 1 redirect to 0, and
missing-with-silent-codes from 0 broken to 1. All three assertions go
red where previously the first two would and the third would not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

🔗 Link Check Results

🚨 1 broken link(s) and 3 redirect(s) were found.

Build Details:


Link Check Summary

  • Total broken links: 1
  • Total redirects found: 3

Broken Links

tests/broken-links.html - 1 broken link(s):
https://this-host-cannot-resolve.invalid - Status: 0 (Connection Error)
Link text: Broken domain

tests/broken-links.html - 3 redirect(s):
🔄 http://github.com/QuantEcon/meta -> https://github.com/QuantEcon/meta (1 redirects)
🔄 https://github.com/QuantEcon/meta/blob/master/README.md -> https://github.com/QuantEcon/meta/blob/main/README.md (1 redirects)
🔄 http://docs.python.org/2.7/library/urllib.html -> https://docs.python.org/2.7/library/urllib.html (1 redirects)

AI-Powered Suggestions

tests/broken-links.html - AI Suggestions:
🤖 http://github.com/QuantEcon/meta
Issue: Redirected 1 times
💡 redirect_update: https://github.com/QuantEcon/meta
Reason: Update to final destination to avoid 1 redirect(s)
🤖 https://github.com/QuantEcon/meta/blob/master/README.md
Issue: Redirected 1 times
💡 redirect_update: https://github.com/QuantEcon/meta/blob/main/README.md
Reason: Update to final destination to avoid 1 redirect(s)
🤖 http://docs.python.org/2.7/library/urllib.html
Issue: Redirected 1 times
💡 redirect_update: https://docs.python.org/2.7/library/urllib.html
Reason: Update to final destination to avoid 1 redirect(s)


Next Steps:

  1. Review the broken links listed above
  2. Update or remove broken links
  3. Consider applying AI suggestions for better alternatives
  4. Push the changes to update this PR

📝 This comment was automatically generated by the AI-Powered Link Checker Action.

@mmcky
mmcky merged commit 7103371 into main Aug 3, 2026
4 checks passed
@mmcky
mmcky deleted the local-server-gate branch August 3, 2026 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants