Skip to content

CI workflows

GitHub Actions in .github/workflows/ build images, gate pull requests and publish the README cards and the docs site. None of them touch the cluster or run terraform plan. Images reach the cluster through Kargo (kargo.md) and manifests through Argo CD (argocd.md).

Workflows

Workflow Trigger Does Writes
build-portfolio.yaml PR and push to main under portfolio/, manual lint and typecheck, then Docker build of portfolio/ with the live deployment.yaml copied in; on PRs the image is smoke tested instead of pushed on main: ghcr.io/mortennordbye/homelab/portfolio:<short-sha> and :0.0.<run>
build-blog.yaml PR and push to main under blog/, manual markdownlint, then Docker build of blog/ with the theme submodule (the render check on PRs) on main: ghcr.io/mortennordbye/homelab/blog:<short-sha> and :0.0.<run>
ci-terraform.yaml PR and push under terraform/ terraform fmt -check, then init -backend=false and validate in every root it finds nothing
container-vulnerability-scan.yaml push to main changing the portfolio or blog prod kustomization.yaml, Sundays 00:00 UTC, manual pulls the image tag prod runs from GHCR and scans it with Trivy (CRITICAL and HIGH, fixed only) SARIF to the Security tab
dependency-review.yml every PR fails on a new dependency with a high severity advisory nothing
scorecard.yml Mondays 03:00 UTC, manual OpenSSF Scorecard SARIF to the Security tab, published Scorecard result
docs.yaml PR and push under docs/, properdocs.yml, overrides/, scripts/docs_site_hooks.py, manual properdocs build --strict GitHub Pages (docs.nordbye.it), not on PRs
render-assets.yaml push to main changing docs/assets/diagrams/*.d2 or docs/assets/logo/source.jpg, manual renders each D2 file to SVG, then PNG, and crops the logo photo to a 512 px round PNG commit to main
kargo-automerge.yaml PR opened, labelled or reopened on a kargo/promotion/* branch squash-merges the PR if its app/<name> label is in KARGO_AUTOMERGE_APPS merge into main
indexnow.yaml push to main changing the portfolio or blog prod kustomization.yaml, manual waits for the key file to be live, then submits the sitemap URLs to IndexNow nothing in the repo
lighthouse.yaml Mondays 06:00 UTC, manual Lighthouse CI, 3 runs each on nordbye.it and blog.nordbye.it lighthouse.json to the lighthouse-data branch, reports to Lighthouse temporary public storage
readme-cards.yaml every 15 minutes, manual two jobs: reads https://nordbye.it/api/v1/infra and renders the cluster card with scripts/render-status-card.mjs; reads stargazer timestamps over GraphQL and renders the chart with scripts/render-star-history.mjs status-data and stars-data branches
sync-certs.yaml daily 05:17 UTC, manual rebuilds portfolio/src/content/certs.json from Credly and the Microsoft Learn transcript a PR from bot/sync-certs when the list changed; opened with GITHUB_TOKEN, so it does not trigger the portfolio CI
reminders.yml 16:00 UTC on the 1st, 8th and 15th, manual posts the Kubernetes, backup or server upkeep reminder to Discord nothing in the repo

What writes to the repo

Morten, Renovate and the sync-certs workflow open PRs that pass the PR checks into main, Kargo's prod promotion PRs are merged by kargo-automerge, and a push to main starts Argo CD syncing, render-assets committing SVGs back and then starting the docs site build, the docs site build, and the portfolio and blog image builds that feed Kargo

Two workflows change main. render-assets commits as GitHub Actions straight to main, and only when the output changed. kargo-automerge merges Kargo's promotion PRs. sync-certs and Renovate open PRs but never merge them. lighthouse and the two readme-cards jobs force-push a single commit to their own data branch (keep_history: false). The README of this repo embeds the star history and status cards from stars-data and status-data over raw.githubusercontent.com. lighthouse.json feeds the Lighthouse card on the GitHub profile README.

The build workflows only push images. Kargo writes the tag in the manifests, and its Warehouses select the 0.0.<run> tag.

Conventions

Every action is pinned to a commit SHA with the version in a comment. Workflows default to permissions: contents: read (kargo-automerge and reminders to none) and widen per job. Most jobs that push, publish or call external services check github.repository_owner == 'mortennordbye', so forks skip them. lighthouse, the readme-cards stars job and the docs deploy do not check it. kargo-automerge instead requires that the PR branch comes from this repo.

Pushes to main from the build workflows, renders, docs, IndexNow and automerge queue. They never cancel a run in progress, so nothing is killed between the build and the push. PR runs, scans and the data-branch jobs cancel the superseded run.

ci-terraform holds no credentials. init runs with -backend=false, so it never reads state. Plans and applies stay local.

Secrets

Secret or variable Used by
GITHUB_TOKEN GHCR push and scan pulls, data branches, Pages, automerge, star history
DOCKERHUB_USERNAME, DOCKERHUB_TOKEN build workflows, Docker Hub login before the build
BW_ACCESS_TOKEN reminders.yml, reads the Discord webhook from Bitwarden
KARGO_AUTOMERGE_APPS (variable) kargo-automerge.yaml, comma separated app names

The IndexNow keys in indexnow.yaml are public by design. The endpoint verifies a submission when it fetches the same key from the site.