Building my CV

Author: Ally

Table of Contents

  1. Overview
  2. Skills
  3. Experience
  4. Concatenation
  5. Metadata
  6. Local Development
  7. CI/CD
  8. Samples

Overview

My CV is generated by code, meaning everything is in plaintext and easily manageable by version control.

The final output I am looking for is a single document consisting of two pages. The first document is a single page text consisting of a brief introduction, commercial experience, other relevant experience and education; followed by second document, a skill tree.

Document 1 Document 2
Preview Document 1 Preview Document 2

Skills

I have created a roadmap.sh inspired skill tree/graph, it can be seen on my homepage.

This is generated using graphviz with my tree/graph markup as a dot file.

My build process uses taskfile and it will generate various assets from dot files as a pdf, svg, and png.

Unfortunately dot doesn’t have variables, so I use an intermediate dot as source file, and run envsubst to build a true dot file.

skills.sh:

#!/usr/bin/env bash
export SKILLS_FONT_NAME="Cousine"
# etc.

envsubst < src/skills.dot > dot/skills.dot

Unfortunately I have decided to use a non-standard font here too… Obviously this is to be containerised… So I will need to install a font in a container for graphviz to be able to work with.

I have used a multi-stage build to accomplish this.

FROM bitnami/git:latest as font
RUN git -C /tmp clone https://github.com/googlefonts/cousine.git font

FROM nshine/dot:latest
# path inside repo with trailing slash
ARG font_path=fonts/ttf/unhinted/variable_ttf/
USER 0
RUN mkdir /usr/share/fonts/newfont
COPY --from=font /tmp/font /tmp/font
RUN find /tmp/font/$font_path -name '*.ttf' \
  -exec install -v -m644 {} /usr/share/fonts/newfont \;
USER 1000
RUN fc-cache -v -f
CMD ["sh"]

Then the image, and skills tree assets are built like using a file like this using task:

---
version: 3

output: group

vars:
  WEB_COLOR: '#41403e'
  DOCKER_IMAGE_SKILLS: alistaircol/skills
  DOCKER_IMAGE_GRAPHVIZ: dot
  DOCKER_RUN: |
    docker run \
      --rm \
      --tty \
      --volume="$(pwd):/app" \
      --workdir="/app" \
      --user=$(id -u):$(id -g)    
  DOCKER_RUN_SKILLS: "{{.DOCKER_RUN}} {{.DOCKER_IMAGE_SKILLS}}"

tasks:
  lint:
    cmds:
    - "{{.DOCKER_RUN}} {{.DOCKER_IMAGE_YAMLLINT}} ."
    interactive: true

  default:
    cmds:
    - task: docker_pull
    - task: build_dot_image
    - task: clean
    - task: build_dot
    - task: build_png
    - task: build_svg
    - task: build_pdf

# the rest is omitted

The PDF output looks like this:

Preview Document 2

I use pdftk to modify the PDF metadata too:

Meta Document 2

This is a stand-alone repository here and it has CI/CD.

Experience

This is a single page (with some great difficulty squeezing everything in) that is built in PHP by using TCPDF.

Using TCPDF is relatively well documented (though not the easiest to use with the enormous amount of arguments in most method calls) so I won’t explain in great detail.

This is a relatively straightforward process, although I use a non-standard font, so this involves converting a ttf using tools/tcpdf_addfont.php.

The main entry looks like this:

public function build()
{
    $this->addHeaderDecoration();
    $this->addHeaderContent();

    $this->addSummary();
    $this->addTechnicalSkills();

    $this->addCommercialExperience();
    $this->addOtherRelevantExperience();

    $this->addEducation();

    $this->pdf->setPDFVersion('1.4');
}

This is one of the more laborious ways to generate such a document, but the main benefits of generating this way over, say, an HTML/CSS page that is converted to a PDF using a headless browser such as by puppeteer:

Preview Document 1

Modifying the metadata with pdftk again:

Meta Document 1

Concatenation

This is a relatively simple process. I use mnuessler/pdftk to combine the separate documents into a single document.

Document 1 Document 2
Preview Document 1 Preview Document 2

Metadata

Likely no one will care, but I use mnuessler/pdftk to get and update the metadata of the documents with dump_data_utf8 and update_info_utf8 respectively.

Get Metadata

Basically I dump the metadata of the document and delete all lines starting with Info and merge them with a file of the metadata Info attributes I want to change.

This file will be used in the next step to update a document’s metadata.

# Reusable docker run command
docker_run = docker run --rm \
  $(shell tty -s && echo "-i" || echo) \
  -v "$(shell pwd):/app" \
  -w "/app"
  
get_meta_skills_pdf:
  ${docker_run} -t mnuessler/pdftk build/skills.pdf dump_data_utf8 \
    | sed '/^Info/d' \
    | cat .pdf/skills.txt - > build/skills.meta

.pdf.skills.txt:

InfoBegin
InfoKey: Creator
InfoValue: https://ac93.dev
InfoBegin
InfoKey: Producer
InfoValue: Alistair Collins - with Graphviz
InfoBegin
InfoKey: Title
InfoValue: Alistair Collins - Skills
InfoBegin
InfoKey: Subject
InfoValue: Skills
InfoBegin
InfoKey: Author
InfoValue: Alistair Collins

Update Metadata

Just update the document’s metadata to be that which we dumped previously in to the build/skills.meta file.

You may need to move/copy the file since this update cannot be done ‘in-place’ (I append the .tmp suffix to the file after dumping the metadata).

# Reusable docker run command
docker_run = docker run --rm \
  $(shell tty -s && echo "-i" || echo) \
  -v "$(shell pwd):/app" \
  -w "/app"
  
set_meta_skills_pdf:
  ${docker_run} mnuessler/pdftk build/skills.pdf.tmp \
    update_info_utf8 build/skills.meta \
    output build/skills.pdf
Before After
Skills Meta Before Skilsl Meta After

How it could look after updating the metadata after concatenating the documents:

Meta Document Combined

Local Development

If this isn’t overkill enough then strap in. I primarily develop on Mac, so I use pm2 and fswatch to rebuild pre-requisites when things are changed.

brew install fswatch
npm i -g pm2

The ecosystem.config.js will watch:

// pm2 start ecosystem.config.js
// pm2 stop ecosystem.config.js
// pm2 status all

module.exports = {
  apps: [
    {
      name: "resume-docker",
      script: "fswatch --one-event --event=Updated Dockerfile && make skills_docker",
    },
    {
      name: "resume-skills",
      script: "fswatch --one-event --event=Updated skills.sh skills.tmpl && make skills_dot skills_png skills_svg skills_pdf",
    },
    {
      name: "resume-cv",
      script: "fswatch --one-event --event=Updated .env src/ && make build",
    }
  ]
};

PM2 Dashboard

CI/CD

A workflow is configured to run when a tag is pushed.

I use this command below to see the most recent tags, and to see which will be the next logical release.

git --no-pager tag | grep -v "^v" | sort -V

Once I’m happy with the local build, I will tag and push:

git tag 1.2.0 -m 'good enough'
git push origin 1.2.0

Github Workflow

The workflow will build all these assets, and included they are then included in a new release. Then a certain subset of these assets are uploaded to an s3 bucket for use on my website and github profile.

Github Release

Pretty cool (I think) 😎.

Next step… dark mode CV…

Samples

To top