CHAPTER 19

Pages & releases

The deploy workflow builds the site perfectly. It still has nowhere to put it — and the buildathon judges need something that won't change under them.

The same week, two loose ends

Sahil's deploy-pages.yml workflow from last chapter runs green on every merge — build succeeds, artifact uploads successfully — and yet there's still no actual public URL anyone can visit. The actions/deploy-pages step has nothing to deploy to until GitHub Pages itself is switched on for the repo. And separately, 06pratyush has a different, related problem: the buildathon submission form wants a link to "the exact version of the site as of the deadline" — not "whatever main happens to look like whenever the judges click it," which could be hours or days later.

Two different needs, and Git's already given you the tools for both — they just haven't been connected to GitHub's actual publishing features yet.

GitHub Pages — free hosting, already half-built

Every GitHub repo can serve static files at a public URL, in one of three shapes:

TypeURLHow
Org siteaccelerate-muj.github.ioA repo named exactly that; served from its default branch root
Project siteaccelerate-muj.github.io/websiteAny other repo, switched on in Settings → Pages
Custom domainWhatever the club ownsCNAME in Settings → Pages, DNS records at the registrar, free HTTPS via Let's Encrypt

Turning it on for real

Settings → Pages → Source: GitHub Actions

Not "Deploy from a branch" — that older mode expects pre-built static files sitting directly in the repo, which doesn't fit a project that needs an actual build step first. "GitHub Actions" as the source means Pages will accept whatever the deploy-pages workflow hands it.

Push to main once more

The exact same workflow from Chapter 18 runs again — except this time there's an actual Pages target listening on the other end. The github-pages environment (remember Environments, from last chapter) shows the live URL directly on the deployment's status.

Custom domain, once the club owns one

Settings → Pages → add the domain. At the DNS host: a CNAME record pointing www at accelerate-muj.github.io, plus the apex A records GitHub documents. "Enforce HTTPS" once the certificate provisions, usually within about ten minutes.

LIMITS WORTH KNOWING BEFORE THEY SURPRISE ANYONE
Repo and site size capped around 1 GB each. A soft ~100 GB/month bandwidth ceiling — comfortable for a club site. Ten builds per hour. Static only: no server-side code, no database. Anything genuinely dynamic has to happen client-side in JavaScript talking to an external API.

Releases — 06pratyush's actual problem, solved

A "release" pairs a Git tag (Chapter 10 already showed exactly how tagging works) with a proper page: a title, formatted notes, and downloadable files. This is the "exact version, permanently" answer — a URL that keeps pointing at the same commit no matter how many more merges happen to main afterward.

the night of the deadline
$ git tag -a v1.0.0 -m "Buildathon submission snapshot"
$ git push origin v1.0.0

Chapter 18's release.yml workflow fires automatically on that tag push — builds the exact tagged commit, opens a Release on GitHub, auto-generates notes from every PR merged since the last tag, and attaches whatever build artifact the club wants judges to be able to download directly. From this second onward, that URL is permanent. Site continues to evolve on main; the submission link never moves.

What good release notes actually look like

## What's new
- Dark-mode redesign of the homepage (#28)
- Events section with past-events history (#31)

## Bug fixes
- Nav overflow on small phones (#19)
- Broken sponsor link in the footer (#34)

## Contributors
Thanks to @ishaan-handle for their first contribution!

**Full changelog**: [v0.9.0...v1.0.0](.../compare/v0.9.0...v1.0.0)

GitHub's "Generate release notes" button produces this automatically from merged PR titles — genuinely close to publish-ready, and specifically the kind of record that makes next year's onboarding easier: a new recruit reading through past releases gets a real, dated history of what the club actually built, in order, instead of having to reconstruct it from memory the way Vidha was doing with the group chat back in Chapter 16.

Semantic versioning, briefly

vMAJOR.MINOR.PATCH — MAJOR for anything that breaks compatibility with what came before, MINOR for new features that don't break anything, PATCH for pure bug fixes. Pre-releases, if the club ever needs a "judge this before it's final" tag, get marked v1.0.0-rc.1 and flagged as a pre-release on the page — they won't show up as "latest."

Packages, mentioned once

GitHub also hosts npm, Docker, and other package registries, free for public packages — worth knowing exists for the day the club publishes something meant to be installed elsewhere, rather than just deployed as a site. Not a near-term need.

CHECK
06pratyush needs a link for the buildathon submission that won't change even as the club keeps merging PRs afterward. What should he actually send?
The live Pages URL deliberately tracks whatever's newest on main — exactly the opposite of what a "judge this exact snapshot" link needs. A tag is the one thing in Git that's guaranteed not to move once created; a Release built from that tag is the correct, permanent answer.

The site is live, deployment is automatic, releases are real. There's one gap left that everything so far has quietly stepped around: what stops someone — accidentally, or on purpose — from committing an actual secret, an API key, straight into this now-public, now-permanently-archived history?