Run deterministic project checks on pull requests, verify that deliberate mistakes fail, and require the relevant checks before changes reach the production branch. GitHub Actions and a host’s automatic deployment are separate systems. A workflow file alone does not guarantee that Cloudflare waits for a green result or that a direct push cannot publish a failure.
Which checks are worth automating first?
Start with failures the repository can evaluate reliably: missing local links and assets, required metadata, duplicate page titles, invalid structured data syntax, JavaScript syntax or lint errors, and an actual build when the project has one. Add regression tests for important behaviors that have broken before.
Define the check’s scope precisely. A link checker might verify internal destinations but not prove that every external provider is reachable. A schema parser can establish valid JSON but cannot prove that the claims in it are true. A plain HTML site may have no application build step at all.
Separate hard failures from advisory signals. A missing internal page is often deterministic. An external link timeout or a variable performance score may need investigation rather than an automatic release block. Avoid teaching the team to ignore an endlessly red check.
Why should the commands work locally first?
Document each command, the runtime it uses, the files it examines, and what failure looks like. Run it against the current repository before wrapping it in a workflow. A command such as npm run check only exists if the project defines it; it is not a universal static-site validator.
If you introduce package-based tooling, review its dependencies and use a committed lockfile so CI can install the intended versions predictably. If existing dependency-free scripts already cover the need, adding a package is optional rather than a goal.
A five-page site’s check contract
- Local-link check: resolve navigation and asset references against the generated pages; fail with the source file and missing target.
- Metadata check: require one H1, the intended canonical, and unique titles/descriptions for public content pages.
- JavaScript check: parse or lint the project’s own scripts using its chosen tooling.
- Human review: inspect the preview and complete the contact journey.
This is a contract to implement with your project’s tools, not a claim that these commands already exist in every repository.
What should the workflow configuration make explicit?
Store the workflow in .github/workflows/. Configure the intended pull-request events and, if useful, a push check on the production branch as a backstop. Choose a stable, unique job name so reviewers and branch rules refer to the correct result.
- The runner and supported runtime version
- The checked-out revision and dependency-install method, when needed
- Every check command and its failure behavior
- A bounded job timeout and readable logs
- Minimal token permissions, often read-only contents for validation
- Reviewed action dependencies, pinned to verified full commit SHAs where practical
Get action identifiers and immutable commit pins from the actual official repository; do not paste an invented SHA. Keep production deployment credentials out of an ordinary validation job. Avoid running untrusted pull-request code in a privileged pull_request_target workflow without a carefully reviewed security design.
Do not add path filters just to make the workflow shorter unless they preserve the intended gate. A required workflow skipped by path or branch filtering can leave its check pending. Inspect the actual job result and steps; a green summary is not evidence that every expected test ran.
What is a small working starter check?
For a plain HTML repository with the three paths below, save this dependency-free example as scripts/check-page-basics.py. Adjust the explicit page list to match your project. Run it from the repository root with python3 scripts/check-page-basics.py using Python 3.12.
from html.parser import HTMLParser
from pathlib import Path
class Page(HTMLParser):
def __init__(self):
super().__init__()
self.in_title = False
self.title = ""
self.h1 = 0
def handle_starttag(self, tag, attrs):
if tag == "title": self.in_title = True
if tag == "h1": self.h1 += 1
def handle_endtag(self, tag):
if tag == "title": self.in_title = False
def handle_data(self, text):
if self.in_title: self.title += text
errors, titles = [], set()
for name in ["index.html", "services/index.html", "contact/index.html"]:
path = Path(name)
if not path.is_file():
errors.append(f"{name}: file missing")
continue
page = Page()
page.feed(path.read_text(encoding="utf-8"))
title = " ".join(page.title.split()).casefold()
if not title or title in titles:
errors.append(f"{name}: empty or repeated title")
titles.add(title)
if page.h1 != 1:
errors.append(f"{name}: expected one H1, found {page.h1}")
if errors:
print("\n".join(errors))
raise SystemExit(1)
print("PASS: selected pages, titles, and H1 counts")
It checks only the selected files, nonempty unique title text, and one H1 per page. It is not an HTML validator, complete link checker, schema audit, or security test. Extend the check contract separately rather than treating this small example as complete coverage.
Save the following as .github/workflows/page-basics.yml. The official checkout v7 and setup-python v6 commit pins were verified on October 6, 2026; review their release notes and keep dependencies maintained.
name: Page basics
on: [pull_request]
permissions:
contents: read
jobs:
page-basics:
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
with:
python-version: '3.12'
- run: python scripts/check-page-basics.py
In a throwaway test branch, change the Services page title to match the homepage. The local command and PR job should report services/index.html: empty or repeated title and fail. Restore its unique title, rerun, and confirm the corrected commit passes. Do not publish the deliberately broken version.
How do you prove that a check catches the mistake?
- Create a test branch and intentionally misspell one internal destination.
- Run the local command and confirm a useful nonzero failure.
- Open or update the test pull request and inspect the workflow for that exact commit.
- Confirm the failure identifies the source and missing destination.
- Fix the link, rerun, and verify the corrected commit passes.
- Remove the test change before any production merge.
Repeat a similar controlled exercise for other critical checks. A script that always exits successfully can produce reassuring green checks while catching nothing. Tests also need positive examples so the checker does not reject legitimate URLs or intentionally private pages.
Use mocked services or dedicated test fixtures for checks that would otherwise email customers, create purchases, or mutate real accounts. Validation should not create production side effects just to prove that the job runs.
How do you connect checks to the production merge?
Use the repository’s supported branch protection or ruleset features to require the intended check before merging into the production branch. Availability and configuration depend on the repository and plan. Review administrator or other bypass permissions and the handling of stale results after a new commit.
Cloudflare Pages Git integration deploys according to its own branch and deployment settings. It does not inherently wait for an unrelated GitHub Action. A required-check merge path helps because unapproved code should not reach the production branch through that path; direct pushes and permitted bypasses can still defeat the intended process.
Changing branch rules, credentials, or deployment permissions is an administrative decision. Agree on the release path and authorized maintainers before changing it. Do not switch off a failed check merely to publish unless the owner has reviewed the risk and approved a bounded exception.
What still needs checking after green CI?
Open the corresponding preview and inspect the business facts, navigation, mobile layout, keyboard behavior, and important customer actions. An automated accessibility scan is helpful evidence, but it is not complete accessibility verification. A mocked email test does not prove that the production inbox received the message.
After publication, verify the expected commit on the remote, the host’s deployment result, and the actual production pages. Check production-only configuration separately. Keep evidence of what passed, what failed, and what was never tested.
Use the launch checklist for human review, the environment checklist for preview differences, and the build troubleshooting guide when the host fails. Automation is most useful when it makes the release evidence clearer.
Sources and further reading
- GitHub: Workflow syntax
- GitHub: Secure use reference
- GitHub: About protected branches
- Cloudflare Pages: Git integration
- GitHub: Official checkout action
- GitHub: Official setup-python action
Documentation checked October 6, 2026. Examples illustrate a workflow; they are not measured client results or guarantees.
Build on a clear website foundation
This free article extends the beginner workflow with a focused technical topic. If you need the starting process, BUILD • PUBLISH • OWN IT is a 44-page beginner PDF guide for $99.99 USD covering a ChatGPT, GitHub, and Cloudflare website workflow. Review its contents to see what the PDF includes; this article does not promise that every technique here is covered there.
Colby will personally assist if you get stuck following the guide. Additional hands-on work may carry a fee, with the scope and price agreed before work begins. Applicable tax is shown at checkout, and third-party service charges are separate. Read the purchase terms for details.