Compare commits
No commits in common. "resume-CICD" and "main" have entirely different histories.
resume-CIC
...
main
10 changed files with 8 additions and 173 deletions
|
|
@ -2,7 +2,7 @@ name: Build Website
|
||||||
on: [push]
|
on: [push]
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: shell
|
runs-on: docker
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -12,55 +12,24 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
mkdir -p site/public
|
mkdir -p site/public
|
||||||
chmod 777 site
|
chmod 777 site
|
||||||
# - name: Build
|
|
||||||
# run: |
|
|
||||||
# mkdir -p "$PWD/.hugo-cache"
|
|
||||||
# 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 \
|
|
||||||
# forgejo.jmopines.com/jm/hugo-git:latest \
|
|
||||||
# --minify --destination "public" --baseURL "/"
|
|
||||||
# - name: Deploy
|
|
||||||
# run: |
|
|
||||||
# echo "${{ secrets.FORGEJO_SSH }}" > /tmp/deploy_key
|
|
||||||
# chmod 600 /tmp/deploy_key
|
|
||||||
# rsync -az --delete \
|
|
||||||
# --exclude 'resume/' \
|
|
||||||
# -e "ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no" \
|
|
||||||
# site/public/. \
|
|
||||||
# ${{ vars.JMOPINES_USER }}@${{ vars.JMOPINES_IP }}:/var/www/jmopines/
|
|
||||||
# rm /tmp/deploy_key
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
mkdir -p "$PWD/.hugo-cache"
|
mkdir -p "$PWD/.hugo-cache"
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-u "$(id -u):$(id -g)" \
|
-u "$(id -u):$(id -g)" \
|
||||||
-v "$PWD/site:/src" \
|
-v "$PWD/site:/src" \
|
||||||
-v "$PWD/.git:/src/.git:ro" \
|
|
||||||
-v "$PWD/.hugo-cache:/cache" \
|
-v "$PWD/.hugo-cache:/cache" \
|
||||||
-e GOPATH=/cache \
|
-e GOPATH=/cache \
|
||||||
-w /src \
|
-w /src \
|
||||||
forgejo.jmopines.com/jm/hugo-git:latest \
|
ghcr.io/gohugoio/hugo:latest \
|
||||||
--minify --destination "public" --baseURL "/"
|
--minify --destination "public" --baseURL "/"
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
run: |
|
run: |
|
||||||
set -ex
|
|
||||||
# Verify files exist
|
|
||||||
ls -la site/public/
|
|
||||||
|
|
||||||
# Setup SSH
|
|
||||||
echo "${{ secrets.FORGEJO_SSH }}" > /tmp/deploy_key
|
echo "${{ secrets.FORGEJO_SSH }}" > /tmp/deploy_key
|
||||||
chmod 600 /tmp/deploy_key
|
chmod 600 /tmp/deploy_key
|
||||||
|
rsync -az --delete \
|
||||||
# Sync with verbose output so we see what is happening
|
|
||||||
rsync -avz --delete \
|
|
||||||
--exclude 'resume/' \
|
--exclude 'resume/' \
|
||||||
-e "ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no" \
|
-e "ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no" \
|
||||||
site/public/ \
|
site/public/. \
|
||||||
${{ vars.JMOPINES_USER }}@${{ vars.JMOPINES_IP }}:/var/www/jmopines/
|
${{ vars.JMOPINES_USER }}@${{ vars.JMOPINES_IP }}:/var/www/jmopines/
|
||||||
|
|
||||||
rm /tmp/deploy_key
|
rm /tmp/deploy_key
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -14,19 +14,11 @@ fi
|
||||||
|
|
||||||
echo "[build] Building site into ${SITE_DIR}/public ..."
|
echo "[build] Building site into ${SITE_DIR}/public ..."
|
||||||
|
|
||||||
# Use the prebuilt/pushed Hugo image (hardcoded to the image created by
|
# Run Hugo in Docker to produce static files. This does not start the dev server.
|
||||||
# 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 \
|
docker run --rm \
|
||||||
-v "${SITE_DIR}:/src" \
|
-v "${SITE_DIR}:/src" \
|
||||||
-v "${PROJECT_ROOT}/.git:/src/.git:ro" \
|
|
||||||
-w /src \
|
-w /src \
|
||||||
"${USE_IMAGE}" \
|
"${HUGO_IMAGE}" \
|
||||||
--minify --destination "public" --baseURL "/"
|
--minify --destination "public" --baseURL "/"
|
||||||
|
|
||||||
echo "[build] Build complete: ${SITE_DIR}/public"
|
echo "[build] Build complete: ${SITE_DIR}/public"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#!/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
|
|
||||||
|
|
@ -16,20 +16,11 @@ fi
|
||||||
echo "[serve] Serving site at http://localhost:${PORT}/"
|
echo "[serve] Serving site at http://localhost:${PORT}/"
|
||||||
echo "[serve] Press Ctrl+C to stop."
|
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 \
|
docker run --rm \
|
||||||
-v "${SITE_DIR}:/src" \
|
-v "${SITE_DIR}:/src" \
|
||||||
-v "${PROJECT_ROOT}/.git:/src/.git:ro" \
|
|
||||||
-w /src \
|
-w /src \
|
||||||
-p "${PORT}:1313" \
|
-p "${PORT}:1313" \
|
||||||
"${USE_IMAGE}" server \
|
"${HUGO_IMAGE}" server \
|
||||||
--bind 0.0.0.0 \
|
--bind 0.0.0.0 \
|
||||||
--baseURL "http://localhost:${PORT}/" \
|
--baseURL "http://localhost:${PORT}/" \
|
||||||
--appendPort=false
|
--appendPort=false
|
||||||
|
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
---
|
|
||||||
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!
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
{{ if .IsHome }}
|
{{ if .IsHome }}
|
||||||
{{ $PageContext = .Site }}
|
{{ $PageContext = .Site }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ $paginator := .Paginate ($PageContext.RegularPages.ByLastmod.Reverse) }}
|
{{ $paginator := .Paginate $PageContext.RegularPages }}
|
||||||
|
|
||||||
{{ range $paginator.Pages }}
|
{{ range $paginator.Pages }}
|
||||||
<article class="post on-list">
|
<article class="post on-list">
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
{{ define "main" }}
|
|
||||||
<div class="posts">
|
|
||||||
{{ $paginator := .Paginate (.Pages.ByLastmod.Reverse) }}
|
|
||||||
{{ range $paginator.Pages }}
|
|
||||||
<article class="post on-list">
|
|
||||||
<h2 class="post-title"><a href="{{ .Permalink }}">{{ .Title | markdownify }}</a></h2>
|
|
||||||
<div class="post-meta"><time class="post-date">{{ partial "post-date" . }}</time></div>
|
|
||||||
{{ if .Summary }}<div class="post-content">{{ .Summary }}</div>{{ end }}
|
|
||||||
</article>
|
|
||||||
{{ end }}
|
|
||||||
{{ partial "pagination.html" . }}
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
{{ define "main" }}
|
|
||||||
<article class="post">
|
|
||||||
<h1 class="post-title"><a href="{{ .Permalink }}">{{ .Title | markdownify }}</a></h1>
|
|
||||||
<div class="post-meta"><time class="post-date">{{ partial "post-date" . }}</time></div>
|
|
||||||
<div class="post-content">{{ .Content }}</div>
|
|
||||||
</article>
|
|
||||||
{{ end }}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
{{/*
|
|
||||||
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 -}}
|
|
||||||
Loading…
Reference in a new issue