From bdb6aaf7f2b696503ae71a7654e1c7a5aa4ca9e1 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 2 Oct 2024 22:57:28 +0200 Subject: [PATCH] cv: switch from LaTeX to Typst --- .github/workflows/build.yml | 136 +++++++-------- Makefile | 54 ------ flake.lock | 211 ++++++++++++++++++++++- flake.nix | 192 +++++++++++++++++---- nix/imports/pkgs.nix | 14 ++ nix/pkgs/sign-pdf/package.nix | 21 +++ nix/pkgs/typst-packages/package.nix | 18 ++ src/cv/common/lib.typ | 134 +++++++++++++++ src/cv/common/metadata.typ | 55 ++++++ src/cv/cv-theme.typ | 242 +++++++++++++++++++++++++++ src/cv/developercv.cls | 208 ----------------------- src/cv/index.tex | 251 ---------------------------- src/cv/main.typ | 11 ++ src/cv/preamble.tex | 33 ---- src/cv/version.tex | 1 - 15 files changed, 932 insertions(+), 649 deletions(-) delete mode 100644 Makefile create mode 100644 nix/imports/pkgs.nix create mode 100644 nix/pkgs/sign-pdf/package.nix create mode 100644 nix/pkgs/typst-packages/package.nix create mode 100644 src/cv/common/lib.typ create mode 100644 src/cv/common/metadata.typ create mode 100644 src/cv/cv-theme.typ delete mode 100644 src/cv/developercv.cls delete mode 100755 src/cv/index.tex create mode 100644 src/cv/main.typ delete mode 100644 src/cv/preamble.tex delete mode 100644 src/cv/version.tex diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d7400c9..7e9efa2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,82 +1,84 @@ -name: Build +name: Release -on: [push] +on: + push: + branches: + - master + paths: + - "src/**" + - "resources/**" + - "**.nix" + - ".github/workflows/*.yml" jobs: - dependencies: - name: Build dependencies - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.version }} + dependencies: + name: Build dependencies + runs-on: ubuntu-latest + outputs: + branch: ${{ steps.extract_branch.outputs.branch }} - steps: - - name: Check out source files - uses: actions/checkout@v3 + steps: + - name: Check out source files + uses: actions/checkout@v4 - - name: Create global variables - id: version - run: echo "::set-output name=version::$(git rev-parse --short HEAD)" + - name: Extract branch name + shell: bash + run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT + id: extract_branch - build: - name: Build PDF files - runs-on: ubuntu-latest - needs: dependencies + build: + name: Build PDF files + runs-on: ubuntu-latest + needs: [dependencies] - steps: - - name: Set git to use LF - run: | - git config --global core.autocrlf false - git config --global core.eol lf + steps: + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf - - name: Check out source files - uses: actions/checkout@v3 - with: - fetch-depth: 1 + - name: Check out source files + uses: actions/checkout@v4 - - name: Create global variables - id: version - run: echo "::set-output name=version::$(git rev-parse --short HEAD)" + - name: Install Nix + uses: cachix/install-nix-action@v27 - - name: Install Nix - uses: cachix/install-nix-action@v29 + - name: Build document + run: | + mkdir -p output + nix build .#cv --out-link result-cv --quiet + cp -vr --dereference $(readlink -f result-cv) cv + cp -ar cv/* output/ - - name: Build document - run: | - mkdir -p output - nix build . --out-link result-cv --quiet - cp -vr --dereference $(readlink -f result-cv) cv - cp -ar cv/* output/ + - name: Upload build assets + uses: actions/upload-artifact@v4 + with: + name: artefacts + path: output - - name: Upload build assets - uses: actions/upload-artifact@v4 - with: - name: assets - path: output + assets: + name: Create release + runs-on: ubuntu-latest + needs: [dependencies, build] - release: - name: "Create tag/pre-release" - runs-on: ubuntu-latest - needs: [dependencies,build] - outputs: - upload_url: ${{ steps.create_release.outputs.upload_url }} - steps: - - name: Download build assets (${{ matrix.assets.input }}) - uses: actions/download-artifact@v4 + steps: + - name: Download build assets (${{ matrix.assets.input }}) + uses: actions/download-artifact@v4 - - name: Rename files - working-directory: assets - run: | - for f in *.pdf; do cp ${f} ../$(printf '%s\n' "cv.pdf"); done + - name: Rename files + working-directory: artefacts + run: | + for f in *.pdf; do cp ${f} ../$(printf '%s\n' "${{ github.run_number }}--${f%.pdf}--${{ github.sha }}.pdf"); done - - name: Create pre-release (${{ needs.dependencies.outputs.version }}) - id: create_release - uses: softprops/action-gh-release@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: v${{ github.run_number }}-${{ needs.dependencies.outputs.version }} - name: Version ${{ github.run_number }} (${{ needs.dependencies.outputs.version }}) - draft: false - prerelease: true - files: | - *.pdf + - name: Create pre-release (v${{ github.run_number }}-${{ github.sha }}) + id: create_release + uses: softprops/action-gh-release@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ github.run_number }}-${{ github.sha }} + name: Release ${{ github.run_number }} (${{ github.sha }}) + draft: false + prerelease: true + files: | + ./*.pdf diff --git a/Makefile b/Makefile deleted file mode 100644 index 5bdf787..0000000 --- a/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -INPUT ?= src/cv/index.tex -OUTPUT ?= $(shell basename "$(shell dirname "$(INPUT)")") -OUTPUT_DIRECTORY = build -LATEXMK_ARGS ?= -halt-on-error -MP -logfilewarninglist -pdf -shell-escape -interaction=nonstopmode -file-line-error -output-directory=$(OUTPUT_DIRECTORY) -TEXINPUTS = "$(shell pwd)/src//:" - -TEXLIVE_RUN = TEXINPUTS=$(TEXINPUTS) -PANDOC_RUN = pandoc -PLANTUML_RUN = plantuml -CONVERT_RUN = convert -LATEXMK_COMMAND = $(TEXLIVE_RUN) latexmk $(LATEXMK_ARGS) - -# Make does not offer a recursive wildcard function, so here's one: -rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)) - -.PHONY: build view - -cv : build - -%: - $(MAKE) build INPUT=src/$@/index.tex - -build : - $(LATEXMK_COMMAND) -jobname=$(OUTPUT) $(INPUT) - $(MAKE) chmodbuild - -plantuml : - $(PLANTUML_RUN) -tsvg src/presentation/resources/*.plantuml - -pandoc : - $(PANDOC_RUN) -s $(INPUT) -o $(OUTPUT) - -latexindent : - $(TEXLIVE_RUN) latexindent - -clean : - rm -rf build - -lint : - $(foreach file, $(call rwildcard,$(shell dirname "$(INPUT)"),*.tex), latexindent -l -w $(file);) - -chmodbuild: - $(TEXLIVE_RUN) chmod 777 build - -watch: - $(LATEXMK_COMMAND) -pvc -jobname=$(OUTPUT) $(INPUT) - $(MAKE) chmodbuild - -fresh: - $(MAKE) chmodbuild clean build - -buildall: - $(MAKE) clean - $(foreach file, $(wildcard src/**/index.tex), $(MAKE) build INPUT=$(file);) diff --git a/flake.lock b/flake.lock index eea44bd..851a3e7 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,42 @@ { "nodes": { + "crane": { + "locked": { + "lastModified": 1727060013, + "narHash": "sha256-/fC5YlJy4IoAW9GhkJiwyzk0K/gQd9Qi4rRcoweyG9E=", + "owner": "ipetkov", + "repo": "crane", + "rev": "6b40cc876c929bfe1e3a24bf538ce3b5622646ba", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "typst-dev", + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1727245890, + "narHash": "sha256-B4gUhZxqdn24PqL7z7ZuvLOS84HVskhKRByWdgA4/RI=", + "owner": "nix-community", + "repo": "fenix", + "rev": "de3acda8b67b92abeeb35ac236924afd959874ad", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -18,18 +55,36 @@ "type": "github" } }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_2" + }, + "locked": { + "lastModified": 1726153070, + "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1727811607, - "narHash": "sha256-2ByOBflaIUJKeF9q6efVcYHljZXGZ7MnCWtseRvmpm8=", + "lastModified": 1728018373, + "narHash": "sha256-NOiTvBbRLIOe5F6RbHaAh6++BNjsb149fGZd1T4+KBg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1839883cd0068572aed75fb9442b508bbd9ef09c", + "rev": "bc947f541ae55e999ffdb4013441347d83b00feb", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-unstable", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } @@ -46,11 +101,103 @@ "url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" } }, + "nixpkgs-lib_2": { + "locked": { + "lastModified": 1725233747, + "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + } + }, + "nixpkgs-unstable": { + "locked": { + "lastModified": 1728217273, + "narHash": "sha256-p/gvyVf24WFs0bted3c71uSQk++ZOYRWbg3bjRoePu4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "50b3bd3fed0442bcbf7f58355e990da84af1749d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1726937504, + "narHash": "sha256-bvGoiQBvponpZh8ClUcmJ6QnsNKw0EMrCQJARK3bI1c=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "9357f4f23713673f310988025d9dc261c20e70c6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pkgs-by-name-for-flake-parts": { + "locked": { + "lastModified": 1727519927, + "narHash": "sha256-3SNX6BuaisoX9PKYI+fh3geZ3jBgKKkAtHcWuHRU0+o=", + "owner": "drupol", + "repo": "pkgs-by-name-for-flake-parts", + "rev": "91debb07d81ff25b8e3b48914b6abd6f11dc26e2", + "type": "github" + }, + "original": { + "owner": "drupol", + "repo": "pkgs-by-name-for-flake-parts", + "type": "github" + } + }, "root": { "inputs": { "flake-parts": "flake-parts", "nixpkgs": "nixpkgs", - "systems": "systems" + "nixpkgs-unstable": "nixpkgs-unstable", + "pkgs-by-name-for-flake-parts": "pkgs-by-name-for-flake-parts", + "systems": "systems", + "typst-dev": "typst-dev", + "typst-packages": "typst-packages" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1727104575, + "narHash": "sha256-lB/ZS0SnHyE8Z3G8DIL/QJPg6w6x5ZhgVO2pBqnz89g=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "3d0343251fe084b335b55c17a52bb4a3527b1bd0", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "rust-manifest": { + "flake": false, + "locked": { + "narHash": "sha256-tB9BZB6nRHDk5ELIVlGYlIjViLKBjQl52nC1avhcCwA=", + "type": "file", + "url": "https://static.rust-lang.org/dist/channel-rust-1.81.0.toml" + }, + "original": { + "type": "file", + "url": "https://static.rust-lang.org/dist/channel-rust-1.81.0.toml" } }, "systems": { @@ -67,6 +214,60 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "typst-dev": { + "inputs": { + "crane": "crane", + "fenix": "fenix", + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_2", + "rust-manifest": "rust-manifest", + "systems": "systems_2" + }, + "locked": { + "lastModified": 1727984442, + "narHash": "sha256-P888F4AKTXop9u4hxwCk5Y9Sn1Y7LdlJApSTKVE2Wis=", + "owner": "typst", + "repo": "typst", + "rev": "60f9f66950f1fbef3d6042533e768b9ee933dd24", + "type": "github" + }, + "original": { + "owner": "typst", + "repo": "typst", + "type": "github" + } + }, + "typst-packages": { + "flake": false, + "locked": { + "lastModified": 1728027662, + "narHash": "sha256-NKh7kDXjz4fQoWvGJNRT0NF9KwxNs9vFl7fWnw36At8=", + "owner": "typst", + "repo": "packages", + "rev": "2b56fa0f972c616b1481c4572e897f3c9782f853", + "type": "github" + }, + "original": { + "owner": "typst", + "repo": "packages", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index ffd71e6..bfc8ac0 100644 --- a/flake.nix +++ b/flake.nix @@ -1,50 +1,182 @@ { inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; systems.url = "github:nix-systems/default"; + typst-dev.url = "github:typst/typst"; + typst-packages = { + flake = false; + url = "github:typst/packages"; + }; + pkgs-by-name-for-flake-parts.url = "github:drupol/pkgs-by-name-for-flake-parts"; }; outputs = - inputs@{ self, flake-parts, ... }: - flake-parts.lib.mkFlake { inherit inputs; } { + inputs@{ ... }: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { systems = import inputs.systems; + imports = [ + ./nix/imports/pkgs.nix + inputs.pkgs-by-name-for-flake-parts.flakeModule + ]; + perSystem = - { pkgs, config, ... }: + { + pkgs, + lib, + config, + ... + }: let - tex = pkgs.texlive.combine { inherit (pkgs.texlive) scheme-full latex-bin latexmk; }; + # Change here to typst-dev if needed + typst = pkgs.nixpkgs-unstable.typst; + + fontsConf = pkgs.symlinkJoin { + name = "typst-fonts"; + paths = with pkgs; [ + font-awesome + roboto + inriafonts + fg-virgil + liberation_ttf + inconsolata-nerdfont + newcomputermodern + ]; + }; + + typst-wrapper-factory = + typstDrv: typst-packages: typstFontPaths: + pkgs.writeShellApplication { + name = "typst-wrapper"; + + runtimeInputs = [ + typstDrv + typst-packages + ]; + + text = '' + TYPST_FONT_PATHS=${typstFontPaths} XDG_CACHE_HOME=${typst-packages} ${lib.getExe typstDrv} "$@" + ''; + }; + + typst-wrapper = typst-wrapper-factory typst config.packages.typst-packages fontsConf; + + mkBuildDocumentDrv = + documentName: + pkgs.stdenvNoCC.mkDerivation { + name = "build-" + documentName; + + src = pkgs.lib.cleanSource ./.; + + buildInputs = [ typst-wrapper ]; + + buildPhase = '' + runHook preBuild + + ${lib.getExe typst-wrapper} \ + compile \ + --root ./. \ + --input rev="${inputs.self.rev or ""}" \ + --input shortRev="${inputs.self.shortRev or ""}" \ + --input builddate="$(date -u -d @${toString (inputs.self.lastModified or "")})" \ + --font-path ${fontsConf} \ + ./src/${documentName}/main.typ \ + ${documentName}.pdf + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -m640 -D ${documentName}.* -t $out + + runHook postInstall + ''; + }; + + mkBuildDocumentScript = + documentName: + pkgs.writeShellApplication { + name = "build-${documentName}"; + + runtimeInputs = [ typst-wrapper ]; + + text = '' + ${lib.getExe typst-wrapper} \ + compile \ + --root ./. \ + --input rev="${inputs.self.rev or ""}" \ + --input shortRev="${inputs.self.shortRev or ""}" \ + --input builddate="$(date -u -d @${toString (inputs.self.lastModified or "")})" \ + --font-path ${fontsConf} \ + ./src/${documentName}/main.typ \ + ${documentName}.pdf + ''; + }; + + mkWatchDocumentScript = + documentName: + pkgs.writeShellApplication { + name = "watch-${documentName}"; + + runtimeInputs = [ typst-wrapper ]; + + text = '' + ${lib.getExe typst-wrapper} \ + watch \ + --root ./. \ + --input rev="${inputs.self.rev or ""}" \ + --input shortRev="${inputs.self.shortRev or ""}" \ + --input builddate="$(date -u -d @${toString (inputs.self.lastModified or "")})" \ + --font-path ${fontsConf} \ + ./src/${documentName}/main.typ \ + ${documentName}.pdf + ''; + }; + + documentDrvs = lib.genAttrs (lib.attrNames ( + lib.filterAttrs (k: v: (v == "directory")) (builtins.readDir ./src) + )) (d: mkBuildDocumentDrv d); + + scriptDrvs = + { + "sign-pdf" = config.packages.sign-pdf; + } + // lib.foldl' ( + a: i: + a + // { + "build-${i}" = mkBuildDocumentScript i; + "watch-${i}" = mkWatchDocumentScript i; + } + ) { } (lib.attrNames documentDrvs); in { + pkgsDirectory = ./nix/pkgs; + packages = documentDrvs; + devShells.default = pkgs.mkShellNoCC { - packages = [ - pkgs.coreutils - tex - pkgs.gnumake + packages = (lib.attrValues scriptDrvs) ++ [ + typst + typst-wrapper + pkgs.gnuplot + pkgs.pympress ]; - }; - packages.default = pkgs.stdenvNoCC.mkDerivation { - pname = "cv-short"; - version = self.shortRev or self.lastModifiedDate; - src = self; - buildInputs = [ - pkgs.coreutils - tex - pkgs.gnumake - ]; - configurePhase = '' - runHook preConfigure - substituteInPlace "src/cv/version.tex" \ - --replace-fail "dev" "${config.packages.default.version}" - runHook postConfigure - ''; - installPhase = '' - runHook preInstall - mkdir -p $out - cp build/cv.pdf $out/ - runHook postInstall + shellHook = '' + echo "Typst version: ${typst.version}" + echo "Typst bin: ${lib.getExe typst}" + echo "Typst wrapper bin: ${lib.getExe typst-wrapper}" + echo "Typst packages directory: ${config.packages.typst-packages}" + echo "Typst fonts directory: ${fontsConf}" ''; + + env = { + TYPST_FONT_PATHS = fontsConf; + }; }; }; }; diff --git a/nix/imports/pkgs.nix b/nix/imports/pkgs.nix new file mode 100644 index 0000000..6e5a329 --- /dev/null +++ b/nix/imports/pkgs.nix @@ -0,0 +1,14 @@ +{ inputs, self, ... }: +{ + perSystem = + { system, ... }: + { + _module.args.pkgs = import self.inputs.nixpkgs { + inherit system; + overlays = [ + inputs.typst-dev.overlays.default + (final: prev: { nixpkgs-unstable = import inputs.nixpkgs-unstable { inherit system; }; }) + ]; + }; + }; +} diff --git a/nix/pkgs/sign-pdf/package.nix b/nix/pkgs/sign-pdf/package.nix new file mode 100644 index 0000000..703e271 --- /dev/null +++ b/nix/pkgs/sign-pdf/package.nix @@ -0,0 +1,21 @@ +{ pkgs, ... }: +pkgs.writeShellApplication { + name = "sign-pdf"; + + runtimeInputs = [ pkgs.open-pdf-sign ]; + + text = '' + open-pdf-sign \ + --certificate cert.pem \ + --key private-key.pem \ + --no-hint \ + --timestamp \ + --tsa http://timestamp.digicert.com \ + --baseline-lt \ + --add-page \ + --page \ + -1 \ + --width 19 \ + "$@" + ''; +} diff --git a/nix/pkgs/typst-packages/package.nix b/nix/pkgs/typst-packages/package.nix new file mode 100644 index 0000000..86cc021 --- /dev/null +++ b/nix/pkgs/typst-packages/package.nix @@ -0,0 +1,18 @@ +{ stdenvNoCC, inputs }: + +stdenvNoCC.mkDerivation { + name = "typst-packages"; + + src = inputs.typst-packages; + + dontBuild = true; + + installPhase = '' + runHook preInstall + + mkdir -p $out/typst/packages + cp -r packages/preview $out/typst/packages/ + + runHook postInstall + ''; +} diff --git a/src/cv/common/lib.typ b/src/cv/common/lib.typ new file mode 100644 index 0000000..4bfe44a --- /dev/null +++ b/src/cv/common/lib.typ @@ -0,0 +1,134 @@ +#import "metadata.typ": * +#import "@preview/fontawesome:0.4.0": * + +#let languageItem( + lang: "", + level: "", + comment: "" +) = { + block( + grid( + columns: (1fr, 1fr), + align: (left, right), + )[ + #text(weight: "bold", lang) + #box(width: 1fr, repeat[.]) + ][ + #box(width: 1fr, repeat[.]) + #text(fill: black.lighten(70%))[#level] + #text(fill: black.lighten(70%))[#comment] + ], + ) +} + +#let educationEntry( + title: "", + school: "", + date: "", + type: "", + grade: "", + body, +) = { + set text(size: font-defaults.footnotesize) + block( + grid( + columns: (1fr, 4fr), + align: (left, left), + )[ + #date\ + #text(fill: black.lighten(70%))[#grade] + ][ + #grid( + columns: (1fr, 1fr), + align: (left, right), + text(weight: "bold", title), school, + ) + #body + ], + ) +} + +#let jobEntry( + title: "", + company: "", + location: "", + date: "", + type: "", + tags: (), + body, +) = { + block( + grid( + columns: (1fr, 5fr), + align: (left, left), + )[ + #date\ + #text(fill: black.lighten(70%))[#type]\ + #text(fill: black.lighten(70%))[#location] + ][ + #if title != "" and company != "" { + grid( + columns: (1fr, 1fr), + align: (left, right), + text(weight: "bold", title), company, + ) + } + #body + #{ + set text(font: "New Computer Modern Mono", fill: black.lighten(60%)) + grid(columns: (1fr), tags.join(" / ")) + } + ], + ) +} + +#let linkItem(body, icon: "") = { + block( + grid( + columns: (auto, auto), + column-gutter: .3em, + align: horizon, + box(width: 2em, height: 2em, fill: black)[ + #{ + set align(center + horizon) + fa-icon(icon, fill: white) + } + ], + body, + ), + ) +} + +#let customBox( + body, + title: "", +) = { + block( + grid(rows: 2)[ + #{ + block(fill: black, inset: .3em)[ + #text(fill: white, size: font.large)[#upper(title)] + ] + } + ][ + #v(.5em) + #body + ], + ) +} + +#let featureBar( + title: "Skills", + value: 100%, +) = { + { + block()[ + #grid( + columns: (1fr, 1fr), + column-gutter: 1em, + align: horizon, + align(right)[#title], line(stroke: .75em + black, length: value), + ) + ] + } +} diff --git a/src/cv/common/metadata.typ b/src/cv/common/metadata.typ new file mode 100644 index 0000000..1b05e5d --- /dev/null +++ b/src/cv/common/metadata.typ @@ -0,0 +1,55 @@ +// Enter your thesis data here: +#let title = "Reproducibility in Software Engineering" +#let subtitle = "Research, analysis, development" +#let doi = "10.5281/zenodo.12666898" +#let university = "University of Mons" +#let faculty = "Faculty of Sciences" +#let degree = "Master" +#let program = "Computer Science" +#let view = degree + "'s Thesis in " + program +#let advisors = none +#let author = "Pol Dellaiera" +#let authorOrcId = "0009-0008-7972-7160" +#let startDate = "2023 - 2024" +#let submissionDate = "12 June 2024" +#let body-font = "Roboto" +#let sans-font = "New Computer Modern Sans" +#let page-margin = (left: 5mm, right: 5mm, top: 5mm, bottom: 5mm,) +#let rev = if "rev" in sys.inputs { + sys.inputs.rev +} else { + "" +} +#let shortRev = if "shortRev" in sys.inputs { + sys.inputs.shortRev +} else { + "" +} +#let builddate = if "builddate" in sys.inputs { + sys.inputs.builddate +} else { + "" +} + +// Default font sizes from original LaTeX style file. +#let font-defaults = ( + tiny: 6pt, + scriptsize: 7pt, + footnotesize: 9pt, + small: 9pt, + normalsize: 10pt, + large: 12pt, + Large: 14pt, + LARGE: 17pt, + huge: 20pt, + Huge: 25pt, +) + +#let font = ( + Large: font-defaults.Large + 0.4pt, // Actual font size. + footnote: font-defaults.footnotesize, + large: font-defaults.large, + small: font-defaults.small, + normal: font-defaults.normalsize, + script: font-defaults.scriptsize, +) diff --git a/src/cv/cv-theme.typ b/src/cv/cv-theme.typ new file mode 100644 index 0000000..968ff49 --- /dev/null +++ b/src/cv/cv-theme.typ @@ -0,0 +1,242 @@ +#import "@preview/fontawesome:0.4.0": * +#import "common/metadata.typ": * +#import "common/lib.typ": * + +#let resume( + firstname: "", + lastname: "", + body, +) = { + // --- Page configuration --- + set page( + margin: page-margin, + numbering: "1", + number-align: center, + paper: "a4", + footer: "" + ) + + // --- Typography --- + set text( + font: body-font, + size: font.normal, + lang: "en", + hyphenate: false, + ) + + // --- Paragraphs --- + // Source: https://typst.app/docs/guides/guide-for-latex-users/ + set par(justify: true) + show par: set block(spacing: .75em) + + // --- Links --- + show link: it => { + underline(it, stroke: .2pt + rgb("#000000").lighten(65%)) + } + + { + grid( + columns: (1fr, 3fr), + align: (left, right), + )[ + #text(size: 3em, weight: "bold")[#firstname]\ + #text(size: 3em, weight: "bold")[#lastname]\ + #text(size: 1.12em)[#subtitle]\ + ][ + #{ + grid(columns: 3, rows: 2, column-gutter: 1em, row-gutter: .5em, align: left)[ + #linkItem(icon: "map-pin")[#link("https://www.openstreetmap.org//#map=15/50.59690/4.32280")[Nivelles, Belgium]] + ][ + #linkItem(icon: "github")[#link("https://github.com/drupol")[github.com/drupol]] + ][ + #linkItem(icon: "envelope")[#link("mailto:pol.dellaiera@protonmail.com")[pol.dellaiera\@protonmail.com]] + ][ + #linkItem(icon: "globe")[#link("https://not-a-number.io")[not-a-number.io]] + ][ + #linkItem(icon: "github")[#link("https://github.com/loophp")[github.com/loophp]] + ][ + #linkItem(icon: "mastodon")[#link("https://mathstodon.xyz/@Pol")[\@pol\@mathstodon.xyz]] + ] + } + ] + + customBox(title: [About])[ + #grid( + columns: (1fr, 1fr) + )[ + Since beginning my web development journey in 2010, I have acquired a wealth + of experience across diverse environments, including innovative start-ups + and established consultancies. + A highly motivated, self-taught professional, I am passionate about solving + intricate problems by implementing elegant, streamlined solutions. + + My insatiable curiosity and meticulous nature have made me a perpetual + learner, constantly striving to expand my knowledge. + + I take great satisfaction in creating simple, natural, and efficient + solutions that harmoniously balance aesthetics and functionality. + ][ + #box(inset: (left: 2em))[ + #featureBar(title: "Linux/NixOS", value: 97%) + #featureBar(title: "Object Oriented Programming", value: 90%) + #featureBar(title: "PHP/Python", value: 87%) + #featureBar(title: "Functional Programming", value: 85%) + #featureBar(title: "Git/Jujutsu", value: 85%) + #featureBar(title: "Algorithm", value: 85%) + #featureBar(title: "Docker", value: 75%) + #featureBar(title: "Typst", value: 70%) + #featureBar(title: "LaTeX", value: 65%) + ] + ] + ] + + customBox(title: [Experience])[ + #jobEntry( + title: "Senior Application Architect", + company: [#link("https://ec.europa.eu")[European Commission]], + location: "Bruxelles", + type: "Full time", + date: "6/2024 -- present", + tags: ( + link("https://en.wikipedia.org/wiki/Python_(programming_language)")[Python], + link("https://en.wikipedia.org/wiki/MongoDB")[MongoDB], + link("https://en.wikipedia.org/wiki/Nix_(package_manager)")[Nix], + link("https://en.wikipedia.org/wiki/Git")[Git], + link("https://github.com/martinvonz/jj")[Jujutsu], + link("https://en.wikipedia.org/wiki/Infrastructure_as_code")[IAC], + ), + )[ + Hired on behalf of a consultancy company, working at + #link("https://ec.europa.eu/info/departments/informatics_en")[Digit B.4] (#emph[Software Engineering Capabilities]). + In this role, I am part of a team of developers, responsible for + developing #emph[GPT\@EC], an internal AI chatbot application based on + GPT technology. My primary focus is on designing and building a + scalable, robust solution, ensuring optimal performance, security, and + integration within the European Commission's ecosystem. + ] + + #jobEntry( + title: "Application Architect", + company: [#link("https://ec.europa.eu")[European Commission]], + location: "Bruxelles", + type: "Full time", + date: "7/2019 -- 6/2024", + tags: ( + link("https://en.wikipedia.org/wiki/Nix_(package_manager)")[Nix], + link("https://symfony.com/")[Symfony], + link("https://www.doctrine-project.org/")[Doctrine], + link("https://api-platform.com/")[API Platform], + link("https://en.wikipedia.org/wiki/Oracle_Database")[Oracle], + link("https://en.wikipedia.org/wiki/Docker_(software)")[Docker], + link("https://en.wikipedia.org/wiki/Infrastructure_as_code")[IAC], + ), + )[ + Hired on behalf of a consultancy company, working at + #link("https://ec.europa.eu/info/departments/informatics_en")[Digit B.4] (#emph[Software Engineering Capabilities]), + where I work in the Developer's Journey team. In this role, I guide teams and clients + through the migration process from ColdFusion to PHP. Additionally, I design and implement authentication libraries + solutions and the necessary development infrastructure for multiple teams, with + a focus on creating reproducible and ephemeral development environments based on Nix. + ] + + #jobEntry(date: [#text( + size: font-defaults.footnotesize, + fill: black.lighten(75%), + )[Before 2019]])[ + #{ + set text(size: font-defaults.footnotesize, fill: black.lighten(75%)) + [This is the public and short version of my CV. Please ask for the full + version by sending me an email.] + } + ] + ] + + grid( + columns: (1fr, 1fr), + column-gutter: 1em, + )[ + #customBox(title: [Education])[ + #educationEntry( + title: "MSc Computer Science", + school: [#link("https://web.umons.ac.be")[University of Mons]], + type: "Full time", + grade: [Cum Laude], + date: "2021 -- 2024", + )[ + Thesis: #link("https://doi.org/10.5281/zenodo.12666898")["Reproducibility in Software Engineering"] + ] + + #educationEntry( + title: "BSc Computer Science", + school: [#link("https://www.heh.be")[Haute Ecoles en Hainaut]], + type: "Full time", + grade: [Cum Laude], + date: "2001 -- 2005", + )[ + IT and systems, specialisation in network and telecommunications + ] + + #educationEntry( + title: "Music theory / Piano", + school: [#link("https://academiedenivelles.be")[Académie de musique de Nivelles]], + type: "Full time", + date: "2018 -- 2021", + )[] + ] + ][ + #customBox(title: [Certificates])[ + #educationEntry( + title: "Blockchain: Understanding Its Uses and Implications", + school: [#link("https://courses.edx.org/certificates/01fdb9d9242546e8bc45153468dfd785")[The Linux Foundation]], + type: "Full time", + date: "01/2020", + )[] + + #educationEntry( + title: "Acquia Certified Developer", + school: [#link("https://certification.acquia.com/user/249")[Acquia]], + type: "Full time", + date: "09/2015", + )[] + + #educationEntry( + title: "Acquia Certified Back End Specialist", + school: [#link("https://certification.acquia.com/user/249")[Acquia]], + type: "Full time", + date: "09/2015", + )[] + ] + ] + + grid( + columns: (1fr, 2fr, 1fr), + column-gutter: 1em, + )[ + #customBox(title: [Languages])[ + - #languageItem(lang: "French", level: "native") + - #languageItem(lang: "English", level: "B2") + - #languageItem(lang: "Italian", level: "A2") + - #languageItem(lang: "Dutch", level: "A2") + ] + ][ + #customBox(title: [Hobbies])[ + Besides my work and the geek stuff, I’m currently fulfilling a childhood dream, I’m learning music and piano! + I love photography and I learned by myself most of the secrets of a reflex camera, just for fun. + I swim a lot and I also really like riding my mountain bike. + ] + ][ + #customBox(title: [Non Profit])[ + Contributor in many open-source projects. Official maintainer of the #link("https://nixos.org")[NixOS Linux distribution]. + + I am also an OpenStreetMap user and contributor. + ] + ] + + customBox(title: [Favorite quotes])[ + - Simplicity is the ultimate sophistication. - Leonardo da Vinci + - Only when the last tree has died and the last river been poisoned and the last fish been caught will we realize we cannot eat money. - Indian author + - We may regard the present state of the universe as the effect of its past and the cause of its future. An intellect which at a certain moment would know all forces that set nature in motion, and all positions of all items of which nature is composed, if this intellect were also vast enough to submit these data to analysis, it would embrace in a single formula the movements of the greatest bodies of the universe and those of the tiniest atom; for such an intellect nothing would be uncertain and the future just like the past would be present before its eyes. - Pierre Simon Laplace + ] + + } +} diff --git a/src/cv/developercv.cls b/src/cv/developercv.cls deleted file mode 100644 index e95dd6d..0000000 --- a/src/cv/developercv.cls +++ /dev/null @@ -1,208 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Developer CV -% LaTeX Class -% Version 1.0 (28/1/19) -% -% This class originates from: -% http://www.LaTeXTemplates.com -% -% Authors: -% Jan Vorisek (jan@vorisek.me) -% Based on a template by Jan Küster (info@jankuester.com) -% Modified for LaTeX Templates by Vel (vel@LaTeXTemplates.com) -% -% License: -% The MIT License (see included LICENSE file) -% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%---------------------------------------------------------------------------------------- -% CLASS CONFIGURATION -%---------------------------------------------------------------------------------------- - -\NeedsTeXFormat{LaTeX2e} -\ProvidesClass{developercv}[2019/01/28 Developer CV class v1.0] - -\DeclareOption*{\PassOptionsToClass{\CurrentOption}{extarticle}} % Pass through any options to the base class -\ProcessOptions\relax % Process given options - -\LoadClass{extarticle} % Load the base class - -%---------------------------------------------------------------------------------------- -% PACKAGES AND OTHER DOCUMENT CONFIGURATIONS -%---------------------------------------------------------------------------------------- - -\setlength{\parindent}{0mm} % Suppress paragraph indentation - -\usepackage[hidelinks]{hyperref} % Required for links but hide the default boxes around links - -\newcommand{\lorem}{Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus.} % Dummy text - -\pagestyle{empty} % No headers or footers - -\usepackage{moresize} % Provides more font size commands (\HUGE and \ssmall) -\usepackage{ragged2e} -%---------------------------------------------------------------------------------------- -% PAGE LAYOUT -%---------------------------------------------------------------------------------------- - -\usepackage{geometry} % Required for adjusting page dimensions and margins - -\geometry{ - paper=a4paper, % Paper size, change to letterpaper for US letter size - top=.5cm, % Top margin - bottom=.5cm, % Bottom margin - left=.5cm, % Left margin - right=.5cm, % Right margin - headheight=0.75cm, % Header height - footskip=1cm, % Space from the bottom margin to the baseline of the footer - headsep=0.5cm, % Space from the top margin to the baseline of the header - %showframe, % Uncomment to show how the type block is set on the page -} - -%---------------------------------------------------------------------------------------- -% FONTS -%---------------------------------------------------------------------------------------- - -\usepackage[utf8]{inputenc} % Required for inputting international characters -\usepackage[T1]{fontenc} % Output font encoding for international characters - -\usepackage[default]{raleway} -%\usepackage[defaultsans]{droidsans} -%\usepackage{cmbright} -%\usepackage{fetamont} -%\usepackage[default]{gillius} -\usepackage{roboto} - -\renewcommand*\familydefault{\sfdefault} % Force the sans-serif version of any font used - -%------------------------------------------------ - -\usepackage{fontawesome5} % Required for FontAwesome 5 icons - -% Command to output an icon in a black square box with text to the right -\newcommand{\icon}[3]{% The first parameter is the FontAwesome icon name, the second is the box size and the third is the text - \vcenteredhbox{\colorbox{black}{\makebox(#2, #2){\textcolor{white}{\large\csname fa#1\endcsname}}}}% Icon and box - \hspace{0.2cm}% Whitespace - \vcenteredhbox{\textcolor{black}{#3}}% Text -} - -%---------------------------------------------------------------------------------------- -% GRAPHICS DEFINITIONS -%---------------------------------------------------------------------------------------- - -\usepackage{tikz} % Required for creating the plots -\usetikzlibrary{shapes, backgrounds} -\tikzset{x=1cm, y=1cm} % Default tikz units - -% Command to vertically centre adjacent content -\newcommand{\vcenteredhbox}[1]{% The only parameter is for the content to centre - \begingroup% - \setbox0=\hbox{#1}\parbox{\wd0}{\box0}% - \endgroup% -} - -%---------------------------------------------------------------------------------------- -% CHARTS -%---------------------------------------------------------------------------------------- - -\newcounter{barcount} - -% Environment to hold a new bar chart -\newenvironment{barchart}[1]{ % The only parameter is the maximum bar width, in cm - \newcommand{\barwidth}{0.35} - \newcommand{\barsep}{0.2} - - % Command to add a bar to the bar chart - \newcommand{\baritem}[2]{ % The first argument is the bar label and the second is the percentage the current bar should take up of the total width - \pgfmathparse{##2} - \let\perc\pgfmathresult - - \pgfmathparse{#1} - \let\barsize\pgfmathresult - - \pgfmathparse{\barsize*##2/100} - \let\barone\pgfmathresult - - \pgfmathparse{(\barwidth*\thebarcount)+(\barsep*\thebarcount)} - \let\barx\pgfmathresult - - \filldraw[fill=black!65, draw=none] (0,-\barx) rectangle (\barone,-\barx-\barwidth); - - \node [label=180:\textcolor{black!65}{##1}] at (0,-\barx-0.175) {}; - \addtocounter{barcount}{1} - } - \begin{tikzpicture} - \setcounter{barcount}{0} -}{ - \end{tikzpicture} -} - -%------------------------------------------------ - -\newcounter{a} -\newcounter{b} -\newcounter{c} - -% Command to output a number of automatically-sized bubbles from a string in the format of '/