In a static site (or any small product site), βjust push HTMLβ works until the same titles and dates must appear in several places. The practical problem is: how do you keep lists, sitemaps, and deploy config consistent without relying on memory β βremember to update this page,β βremember to run that scriptβ?
That is what a small CI/CD contract is for: one source of truth, a build that generates the rest, and a deploy artifact the host can trust. This post is about that idea on GitHub Pages and Actions. That problem is what led me to add a catalog file, a sync script, and an Actions deploy workflow for my own site β the same boundaries you can reuse on other static projects.
Why a clear deploy contract matters
The hard part of CI/CD is rarely the button that deploys. It is knowing which files are inputs, which files are generated, and which files must reach production. Without that contract, teams rely on memory: βremember to update this,β βremember to run that script,β βremember to include this file.β
That works until it does not. The site may still go live, but the result can be incomplete or inconsistent. So the useful question is not only βhow do we deploy?β It is βwhat does a correct release require?β
In practice, that usually means:
- One source of truth for data that appears in more than one place.
- A build step that turns source into the final output.
- A repeatable pipeline that runs the same way in CI every time.
- A complete deploy artifact with everything the host needs to serve the site.
Two ways to ship on GitHub Pages
Deploy from a branch publishes whatever is in main (or docs/). It is fine for plain static files, but it assumes the repository already contains the final output.
GitHub Actions adds an explicit build stage. The repository can keep sources, templates, and config; the workflow turns those into a deployable artifact.
The difference is architectural: branch deploys publish repository state, while Actions deploys publish build output. Once you care about derived files, validation, or automation, that distinction matters.
βββββββββββββββββββββββββββββββββββββββββββ β Repo (source) β β content Β· config Β· templates β βββββββββββββββββββββββββββββββββββββββββββ€ β GitHub Actions β β validate β build β package β βββββββββββββββββββββββββββββββββββββββββββ€ β Deploy artifact β β static HTML + assets + config β βββββββββββββββββββββββββββββββββββββββββββ€ β Custom domain (CNAME + DNS) β β domain points to deployed artifact β βββββββββββββββββββββββββββββββββββββββββββ
One source of truth
A good CI/CD pipeline starts by separating what people write from what the build should create. If a title, date, or slug appears in three places, it should live in one place and be reused during the build.
For a static site, that source can be a small JSON or YAML file. The pipeline reads it once and generates the repeated parts of the site automatically.
A simple way to think about it:
- You write the source. The real pages, text, images, and the shared data file.
- The pipeline builds the rest. Listing pages, sitemap entries, and any other repeated sections that should stay in sync.
If something is just a copy of data you already have, do not maintain it by hand. Let CI generate it.
Why generate static HTML (not only JSON + JS)
One option is to load JSON in the browser and render repeated sections with JavaScript. That is often fine for performance. The tradeoff is that the browser becomes part of the build process.
Generating HTML in CI moves that responsibility back to the pipeline. The response already contains the final markup, which is usually better for SEO, previews, caching behavior, and debugging. The architecture principle is simple: do as much deterministic work as possible before runtime.
What the workflow does
A good GitHub Actions workflow for a static site usually has four stages:
- Checkout the repository.
- Validate and build generated files from the source data.
- Package only the files that belong in production.
- Deploy the artifact with the hosting provider's official action.
That structure scales well. Today the build step might only generate repeated listing sections. Tomorrow it can lint metadata, fail on broken links, verify feeds, minify assets, or publish preview environments on pull requests.
The important point is that deployment becomes the last stage of a build contract, not the first place where source files happen to land.
Automation boundaries
Not everything belongs in CI, and not everything belongs in the repo as static output. A useful boundary is:
- Humans own intent. They write pages, metadata, and configuration.
- The pipeline owns repetition. It expands templates, regenerates indexes, and packages artifacts.
- The host owns delivery. It serves the final files, TLS, and domain resolution.
Once those boundaries are clear, debugging becomes easier too. Incomplete generated content is a build issue. A broken domain is a deploy or DNS issue. A stale page is often cache invalidation. Good architecture makes failure modes easier to classify.
Principles that carry well beyond static sites
- Single source of truth. Inputs should be authored once and reused by the pipeline.
- Separate build from publish. What developers edit is not always what production should serve.
- Prefer boring automation. A short script in CI beats tribal knowledge in a README alone.
- Keep the deploy artifact honest. Anything production needs must be in the uploaded output.
Those principles apply to mobile release pipelines, backend deploys, static sites, and internal tools. The platform changes; the boundary design does not change that much.
What we gained
The payoff is not only convenience. It is confidence. Once CI owns validation, generation, packaging, and deploy, publishing becomes a repeatable system instead of a checklist someone might forget.
Start small: one source file, one build step, one deploy workflow. Then add checks where mistakes actually happen. Good CI/CD is usually not fancy. It is clear, repeatable, and boring in the best way.
I build native iOS and Android apps and share notes like this as I ship. If architecture, automation, or delivery pipelines are your thing too, let's connect on LinkedIn.