From d70d8974703330addad753358b2ca2fe6a7a46fe Mon Sep 17 00:00:00 2001 From: Jean-Michel Tremblay Date: Thu, 23 Apr 2026 15:19:53 -0400 Subject: [PATCH] adding file to get effing date in git fix dates / git, resume CI/CD. --- .forgejo/workflows/build.yml | 3 +- scripts/Dockerfile | 8 ++++ scripts/build.sh | 12 +++++- scripts/push_hugo_image.sh | 6 +++ scripts/serve.sh | 11 ++++- site/content/config/cicd-resume.md | 63 ++++++++++++++++++++++++++++ site/layouts/_default/index.html | 2 +- site/layouts/_default/list.html | 13 ++++++ site/layouts/_default/single.html | 7 ++++ site/layouts/partials/post-date.html | 20 +++++++++ 10 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 scripts/Dockerfile create mode 100755 scripts/push_hugo_image.sh create mode 100644 site/content/config/cicd-resume.md create mode 100644 site/layouts/_default/list.html create mode 100644 site/layouts/_default/single.html create mode 100644 site/layouts/partials/post-date.html diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 7c277e7..28abff9 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -18,10 +18,11 @@ jobs: docker run --rm \ -u "$(id -u):$(id -g)" \ -v "$PWD/site:/src" \ + -v "$PWD/.git:/src/.git:ro" \ -v "$PWD/.hugo-cache:/cache" \ -e GOPATH=/cache \ -w /src \ - ghcr.io/gohugoio/hugo:latest \ + forgejo.jmopines.com/jm/hugo-git:latest \ --minify --destination "public" --baseURL "/" - name: Deploy run: | diff --git a/scripts/Dockerfile b/scripts/Dockerfile new file mode 100644 index 0000000..d5a7588 --- /dev/null +++ b/scripts/Dockerfile @@ -0,0 +1,8 @@ +FROM ghcr.io/gohugoio/hugo:latest + +# Install git into the Alpine-based Hugo image so Hugo can query git +# history for `.Lastmod`. We keep the image minimal. +USER root +RUN apk add --no-cache git + +USER hugo diff --git a/scripts/build.sh b/scripts/build.sh index 3a3309c..7f034d1 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -14,11 +14,19 @@ fi echo "[build] Building site into ${SITE_DIR}/public ..." -# Run Hugo in Docker to produce static files. This does not start the dev server. +# Use the prebuilt/pushed Hugo image (hardcoded to the image created by +# scripts/push_hugo_image.sh). This keeps the build script simple and +# predictable. +HUGO_GIT_IMAGE="forgejo.jmopines.com/jm/hugo-git:latest" +USE_IMAGE="${HUGO_GIT_IMAGE}" + +# Run Hugo in Docker to produce static files. Mount the content and the repo +# .git directory so Hugo can resolve git-based dates. Mount .git read-only. docker run --rm \ -v "${SITE_DIR}:/src" \ + -v "${PROJECT_ROOT}/.git:/src/.git:ro" \ -w /src \ - "${HUGO_IMAGE}" \ + "${USE_IMAGE}" \ --minify --destination "public" --baseURL "/" echo "[build] Build complete: ${SITE_DIR}/public" diff --git a/scripts/push_hugo_image.sh b/scripts/push_hugo_image.sh new file mode 100755 index 0000000..8a5480c --- /dev/null +++ b/scripts/push_hugo_image.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Minimal build script: build the image from hugo_git/Dockerfile using hugo_git as context +docker build -t forgejo.jmopines.com/jm/hugo-git:latest . +docker push forgejo.jmopines.com/jm/hugo-git:latest \ No newline at end of file diff --git a/scripts/serve.sh b/scripts/serve.sh index d3a1b91..edc51af 100755 --- a/scripts/serve.sh +++ b/scripts/serve.sh @@ -16,11 +16,20 @@ fi echo "[serve] Serving site at http://localhost:${PORT}/" echo "[serve] Press Ctrl+C to stop." +# Use the prebuilt/pushed Hugo image (hardcoded to the image created by +# scripts/push_hugo_image.sh). This simplifies the serve script and avoids +# local image build logic. +HUGO_GIT_IMAGE="forgejo.jmopines.com/jm/hugo-git:latest" +USE_IMAGE="${HUGO_GIT_IMAGE}" + +# Run Hugo server. Mount the repo .git read-only so git-based info is available +# inside the container when `enableGitInfo = true`. docker run --rm \ -v "${SITE_DIR}:/src" \ + -v "${PROJECT_ROOT}/.git:/src/.git:ro" \ -w /src \ -p "${PORT}:1313" \ - "${HUGO_IMAGE}" server \ + "${USE_IMAGE}" server \ --bind 0.0.0.0 \ --baseURL "http://localhost:${PORT}/" \ --appendPort=false diff --git a/site/content/config/cicd-resume.md b/site/content/config/cicd-resume.md new file mode 100644 index 0000000..dd89064 --- /dev/null +++ b/site/content/config/cicd-resume.md @@ -0,0 +1,63 @@ +--- +title: 'CI/CD resume publication' +Date: 2026-04-23 +draft: false +tags: ['hugo', 'git', 'workflow'] +--- + +I am terrible at word processors / editing so I build my resume [with LaTeX templates](https://forgejo.jmopines.com/jm/resume/src/branch/main/examples/resume). I gain a pretty, highly configurable resume, but it can be a pain to modify, build, share, etc. A couple weeks ago someone pointed out to me that I forgot to update my most recent role to reflect a promotion (staff software engineer, yeahh!!). I was not at home and I had to work quite a bit to only change a word in my resume. This has always been a pain to me so I figured, why not set up a CI/CD pipeline to build and publish my resume. These are the steps I took. + +# ForgeJo +### set up forgejo +This is not a tutorial, but essentially I achieved this by: +* creating a VM to host ForgeJo +* forwarding SSH port 2222 to it +* creating a caddy entry to point to it, so I can access securely from outside. + +You can probably do this with GitHub. + +### set up forgejo runners +On the same host, you can start a forgejo-runner, just like runners in gitlab, and register the runner to your forgejo instance. It will ask you to register and you pass a token that you generated from the ForgeJo instance. + +# actions +This is the nice part. To set up CI/CD pipeline, simply create a `.forgejo/workflows/build.yml` and specify your pipeline there. In my case I want to +* build my resume +* push it as an artifact on ForgeJo -- turns out this is complicated and I only managed to push a zip archive. +* push my resume to the server that will host it. + +The full config can be found [here](https://forgejo.jmopines.com/jm/resume/src/branch/main/.forgejo/workflows/build.yml). Here is a summary: +```yaml +name: Build Resume +on: [push] +jobs: + build: + runs-on: docker + steps: + - name: Checkout + run: | + echo ${{ github.server_url }} + echo ${{ github.repository }} + git clone --branch ${{ github.ref_name }} ${{ github.server_url }}/${{ github.repository }}.git . + - name: Build + run: docker run --rm -w /doc -v $PWD:/doc thomasweise/docker-texlive-full bash /doc/build.sh + - name: Deploy + run: | + echo "${{ secrets.FORGEJO_SSH }}" > /tmp/deploy_key + chmod 600 /tmp/deploy_key + scp -i /tmp/deploy_key -o StrictHostKeyChecking=no examples/resume_jm_tremblay_*.pdf ${{ vars.JMOPINES_USER }}@${{ vars.JMOPINES_IP }}:/var/www/jmopines/resume/ + rm /tmp/deploy_key +``` + + +# Hugo +In Hugo, I (well, the AI), added this to hugo.toml to have a header pointing to the latest resume. the url is the relative path with respect to the site root. + +```toml +[[languages.en.menu.main]] + identifier = "resume" + name = "Resume" + url = "/resume/resume_jm_tremblay_latest.pdf" + weight = 3 +``` + +That's it! now on every build of my resume, a new copy get pushed to serve to my website! diff --git a/site/layouts/_default/index.html b/site/layouts/_default/index.html index e33939d..68826fa 100644 --- a/site/layouts/_default/index.html +++ b/site/layouts/_default/index.html @@ -9,7 +9,7 @@ {{ if .IsHome }} {{ $PageContext = .Site }} {{ end }} - {{ $paginator := .Paginate $PageContext.RegularPages }} + {{ $paginator := .Paginate ($PageContext.RegularPages.ByLastmod.Reverse) }} {{ range $paginator.Pages }}
diff --git a/site/layouts/_default/list.html b/site/layouts/_default/list.html new file mode 100644 index 0000000..92af2fc --- /dev/null +++ b/site/layouts/_default/list.html @@ -0,0 +1,13 @@ +{{ define "main" }} +
+ {{ $paginator := .Paginate (.Pages.ByLastmod.Reverse) }} + {{ range $paginator.Pages }} + + {{ end }} + {{ partial "pagination.html" . }} +
+{{ end }} diff --git a/site/layouts/_default/single.html b/site/layouts/_default/single.html new file mode 100644 index 0000000..3177e35 --- /dev/null +++ b/site/layouts/_default/single.html @@ -0,0 +1,7 @@ +{{ define "main" }} + +{{ end }} diff --git a/site/layouts/partials/post-date.html b/site/layouts/partials/post-date.html new file mode 100644 index 0000000..32647a4 --- /dev/null +++ b/site/layouts/partials/post-date.html @@ -0,0 +1,20 @@ +{{/* + Render creation and modified dates for a post. + - Always show the creation date (`.Date`) in YYYY-MM-DD. + - If `.Lastmod` exists and differs (by date) from `.Date`, show + an "Updated: YYYY-MM-DD" suffix. +*/}} +{{- $created := .Date -}} +{{- $createdFmt := $created.Format "2006-01-02" -}} +{{- $modified := .Lastmod -}} + +{{- /* Print creation date */ -}} +{{- printf "%s" $createdFmt -}} + +{{- /* If we have a modified timestamp, format and show when different */ -}} +{{- if $modified -}} + {{- $modifiedFmt := $modified.Format "2006-01-02" -}} + {{- if ne $modifiedFmt $createdFmt -}} + {{- printf " [Updated: %s]" $modifiedFmt -}} + {{- end -}} +{{- end -}}