Skip to content

JD-Gonz/JD-Gonz.github.io

Repository files navigation

JD-Gonz.github.io

Personal portfolio site for JD Gonzalez. Hosted on GitHub Pages at JD-Gonz.github.io.

The site is a static HTML page, but content is driven by JSON files and the static HTML is regenerated by a build step. Editors never touch HTML by hand — they update data/*.json, push, and a GitHub Actions workflow rebuilds and deploys automatically.

How it's wired

┌────────────────┐    ┌───────────────────────┐    ┌─────────────────────────┐
│ data/*.json    │ ─► │ scripts/build.js      │ ─► │ index.html (local + CI; │
│ + src/index.   │    │   (uses lib/render.js)│    │ not committed)          │
│   template.html│    │                       │    └─────────────────────────┘
└────────────────┘    └───────────────────────┘

There is also an Express server (server.js + app.js) that reads the same template and data at runtime. Use it for local preview and for the backend JSON API. It is not used in production — GitHub Pages serves index.html directly.

Project layout

.
├── index.html                 # GENERATED — `npm run build` (not in git)
├── src/
│   └── index.template.html    # source template with @@TOKEN@@ comments
├── data/
│   ├── site.json              # meta/OG, header, about, banner, contact, copyright
│   ├── highlights.json        # personal-highlights cards
│   └── projects.json          # portfolio carousel cards
├── scripts/
│   └── build.js               # renders template + data → index.html
├── lib/
│   ├── content.js             # JSON loader + cache (fs.watch in dev)
│   └── render.js              # template renderer (escape / raw-HTML tokens)
├── routes/
│   └── api.js                 # /api/site, /api/highlights, /api/projects
├── app.js                     # Express app factory (local dev / tests)
├── server.js                  # process entry (listen, signals)
├── test/
│   └── app.test.js            # node:test integration suite
├── public/                    # static assets (css, js, images, fonts, pdf)
└── .github/workflows/
    └── deploy.yml             # CI: build + deploy to GitHub Pages on every push

Updating site content

Edit JSON, commit, and push — CI runs npm run build before deploy. Optionally run npm run build locally to refresh ./index.html.

Section Edit
Page <title>, OpenGraph tags data/site.jsonmeta
Header logo / tagline data/site.jsonheader
About Me copy + image data/site.jsonabout
Personal Highlights intro data/site.jsonhighlightsIntro
Personal Highlights cards (3) data/highlights.json
Portfolio banner data/site.jsonbanner
Portfolio carousel cards data/projects.json
Contact links / resume link data/site.jsoncontact
Copyright footer data/site.jsoncopyright
Layout / structural changes src/index.template.html
npm install
# edit data/*.json …
npm run build     # optional locally — writes ./index.html for static preview
git add data/
git commit -m "content: update projects list"
git push

index.html is listed in .gitignore because CI always runs npm run build before deploy, so the live site never depends on a committed copy. Run npm run build locally when you want ./index.html on disk (for opening the file directly or for sanity-checking a render before push).

Conventions

  • String fields are HTML-escaped when rendered.
  • Fields whose name ends in Html (e.g. banner.bodyHtml, copyright.itemsHtml) are inserted as raw HTML and must be trusted.
  • projects[*].imageHref is optional — supply it only when the image should link somewhere different from the title (e.g. a live demo vs. its GitHub repo).

Local development

npm install
npm run build     # optional: materialize ./index.html for static file preview
npm run dev       # nodemon; http://localhost:8080

The dev server renders from src/index.template.html and does not require ./index.html to exist. Edits to src/, data/, lib/, routes/, app.js, or server.js trigger a nodemon restart. Edits to data/*.json alone are picked up live without a restart (a fs.watch in lib/content.js drops the in-memory caches).

The Express server also exposes the JSON API — useful for building alternative clients or debugging.

Running tests

npm test

test/app.test.js boots the app on an ephemeral port and asserts:

  • every project and highlight title from /data reaches the rendered HTML,
  • site copy from data/site.json is present,
  • no @@TOKEN@@ comments leak through,
  • /api/* endpoints return the expected payloads,
  • security headers are attached.

Production (GitHub Pages)

The site is deployed by the GitHub Actions Pages source, not from a branch root. Every push to master or main runs tests, rebuilds the static site, and uploads it as a Pages artifact — nothing is committed back to the repo.

One-time setup

In the repo's settings:

  1. Settings → Pages → Build and deployment → Source → choose "GitHub Actions".

That is the only change required in the GitHub UI. The workflow file (.github/workflows/deploy.yml) handles everything else.

CI workflow — .github/workflows/deploy.yml

Triggers on push to master or main and on manual workflow_dispatch. Two jobs:

  1. build
    • Checks out the repo, installs Node 20, runs npm ci.
    • Runs npm test — a failing test aborts the workflow so a broken build never reaches Pages.
    • Runs npm run build to regenerate index.html from the current template and data.
    • Stages exactly what ships into _site/: index.html, public/, plus an empty .nojekyll marker.
    • Uploads _site/ via actions/upload-pages-artifact@v3.
  2. deploy (depends on build)
    • actions/deploy-pages@v4 publishes the artifact to the github-pages environment.
    • The deploy URL is exposed as a job output so it shows up as a clickable link in the Actions UI.

Concurrency is scoped to the pages group with cancel-in-progress: false, matching GitHub's official recommendation — a running deploy won't be cancelled mid-flight by a newer push, but only one deploy is in progress at a time.

Why Actions-source instead of branch-source

  • No bot commits back to master. History stays clean — every commit is intentional.
  • Failed tests block the deploy. The previous "branch root" model happily served a broken index.html because Pages only cared about file bytes, not the build.
  • The workflow file contains the whole deploy contract; nothing depends on the Pages "Source" dropdown besides the one-time toggle.

Operational hooks (Express server only)

These apply when you run the Express server — they are not part of the GitHub Pages deployment.

  • POST /api/_cache/clear — drop in-memory JSON + rendered-template caches without restarting the process.
  • SIGTERM / SIGINT — graceful shutdown; in-flight requests drain before exit.

Middleware (Express only)

  • helmet — security headers (CSP intentionally disabled; tightening it is a separate pass).
  • compression — gzip/Brotli on responses.
  • express.static('/public') — 7-day Cache-Control with ETag.

API (Express server only)

Method Path Response
GET /api/site data/site.json
GET /api/highlights data/highlights.json
GET /api/projects data/projects.json
POST /api/_cache/clear { ok: true } and invalidates caches
* /api/<unknown> 404 { error: "Not Found", path }

License

ISC

About

My personal website

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors