initial commit
Some checks failed
build resume / build (push) Failing after 16m16s

This commit is contained in:
Tigor Hutasuhut 2024-06-19 17:04:45 +07:00
commit 268da2262d
29 changed files with 541 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake .

View file

@ -0,0 +1,26 @@
name: build resume
on: push
jobs:
build:
runs-on: ubuntu
steps:
- uses: actions/checkout@v3
- name: "Setup env"
uses: JRMurr/direnv-nix-action@v4.1.0
- name: use typst
run: typst --version
shell: bash
- name: build resume
run: ./build.sh
shell: bash
- uses: actions/upload-artifact@v3
with:
name: Resume
path: resume.pdf
if-no-files-found: error

26
.github/workflows/main.yml vendored Normal file
View file

@ -0,0 +1,26 @@
name: build resume
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Setup env"
uses: JRMurr/direnv-nix-action@v4.1.0
- name: use typst
run: typst --version
shell: bash
- name: build resume
run: ./build.sh
shell: bash
- uses: actions/upload-artifact@v3
with:
name: Resume
path: resume.pdf
if-no-files-found: error

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.direnv
resume.pdf
*.pdf
.DS_Store

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Harkunwar Kochar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

9
README.md Normal file
View file

@ -0,0 +1,9 @@
This is an Attractive Resume Template built with Typst, an open source Latex alternative written in Rust, and compiles to PDF.
To compile it to pdf, make sure typst is installed. The provided flake.nix and .envrc is useful if you have nix and direnv installed. This template using the Mulish Google Font and is provided in the `assets/fonts` directory.
Mirror link at Typst.app https://typst.app/project/rLlknWbYc8XMaZc45BHlMl
Preview:\
<img src="assets/images/attractive-typst-resume-blue.png" width="400px" />
<img src="assets/images/attractive-typst-resume-green.png" width="400px" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

1
build.sh Executable file
View file

@ -0,0 +1 @@
typst --font-path ./assets/fonts compile resume.typ resume.pdf "$@"

60
flake.lock Normal file
View file

@ -0,0 +1,60 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1718530797,
"narHash": "sha256-pup6cYwtgvzDpvpSCFh1TEUjw2zkNpk8iolbKnyFmmU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b60ebf54c15553b393d144357375ea956f89e9a9",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

25
flake.nix Normal file
View file

@ -0,0 +1,25 @@
{
inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable";
utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, utils, ... }:
utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in
{
devShells.default = pkgs.mkShell {
buildInputs = [
pkgs.typst
pkgs.typstyle
pkgs.typst-live
];
shellHook = ''
echo `${pkgs.typst}/bin/typst --version`
'';
};
});
}

229
resume.typ Normal file
View file

@ -0,0 +1,229 @@
#import "template.typ": *
#set page(margin: (
left: 10mm,
right: 10mm,
top: 15mm,
bottom: 15mm,
))
#let linkify(lnk, txt, color: "#4273B0") = {
underline(link(lnk, text(rgb(color), txt)))
}
#set text(font: "Mulish")
#show: project.with(
theme: rgb("#0F83C0"),
name: "Tigor Hutasuhut",
title: "Software Engineer",
contact: (
contact(
text: "tigor.hutasuhut@gmail.com",
link: "mailto:tigor.hutasuhut@gmail.com",
),
contact(
text: "GitHub.com/tigorlazuardi",
link: "https://www.github.com/tigorlazuardi",
),
contact(
text: "LinkedIn.com/in/tigor-hutasuhut-00346463",
link: "https://www.linkedin.com/in/tigor-hutasuhut-00346463/",
),
contact(
text: "git.tigor.web.id",
link: "https://git.tigor.web.id",
),
),
main: (
section(
title: "Work Experience",
content: (
subSection(
title: "Bareksa Marketplace Indonesia",
titleEnd: "Jakarta, Indonesia",
subTitle: "Software Engineer",
subTitleEnd: [*November 2019 - Present*],
),
subSection(
title: [#underline[Stock Project]],
content: list(
[
Developed *high performance and cheap to scale _stateful_* Market Data streams infrastructure that can hold against
*thousands of connections* without noticeable delay from IDX Burse data to consumers devices and can hold against
irregular conditions like Market Panics.
],
[ Implemented high performance aggregators and runners for Market Data that can serve final data to users without delay. ],
[ Collaborated with Frontend and Product teams to implement HTTP API and WebSocket for Stock Platform ],
[ Main researcher and MVP provider to demo and integrate new technology stack to existing ones. ],
),
),
subSection(
title: [#underline[Internal Toolings]],
content: list(
[
Developed and Maintain library for standardized logging, http body responses, metrics, and tracing integration to
support faster debugs and performance review.
],
[
Developed integration tool and library for easy integration of apps with secure secret management
#underline(link("https://vault.hashicorp.com",text(rgb("#4273B0"),"Vault Hashicorp"))).
],
),
),
subSection(
title: [#underline[SBN Project]],
content: list(
[ Implement HTTP APIs for integration with *_Kemenkeu_*],
[ Create Cron Jobs to ensure the internal system is as close as possible to *_Kemenkeu_* system state. ],
[ Transform *Sync* operation Buy/Sell process to *_Async_* infrastructure to support _backpressure_ when consumers rush to buy new SBN products. ],
[ Create batch job runners to integrate and syncs data with *_Bina Artha_*. ],
),
),
subSection(
title: [#underline[Gold Project]],
content: list(
[ Implement HTTP APIs for integration with *_Pegadaian_*],
[ Create Cron Jobs to ensure the internal system is as close as possible to *_Pegadaian_*'s open-loop system state. ],
),
),
),
),
section(
title: "Projects",
content: (
subSection(
title: underline(
link(
"https://git.tigor.web.id/tigor/redmage",
text(rgb("#4273B0"), "Redmage [WIP]"),
),
),
content: list(
[
A performant image scraper and downloader for Reddit using Go for the backend and a
combination of #linkify("https://github.com/a-h/templ", "templ"), #linkify("https://tailwindcss.com/", "Tailwind") #linkify("https://alpinejs.dev/", "AlpineJS"), and #linkify("https://htmx.org", "HTMX")
for the interactive frontend.
],
[Designed with simplicity and easy deployments in-mind like output single binary and using SQLite for the database.],
[Fault tolerant system with #underline(link("https://watermill.io/",text(rgb("#4273B0"),"Watermill"))) Pub/Sub library.],
[Easy management of Schedulers, Devices, and Subreddits via it's Web UI.],
[Demo: #linkify("https://redmage-demo.tigor.web.id", "https://redmage-demo.tigor.web.id")],
),
),
subSection(
title: underline(
link(
"https://git.tigor.web.id/tigor/nixos",
text(rgb("#4273B0"), "Homeserver"),
),
),
content: list(
[
A self-host, on-prem machine. Using #linkify("https://podman.io/", "Podman") for the containerization,
#linkify("https://caddyserver.com", "Caddy") for the reverse proxy
and #linkify("https://nixos.org", "NixOS") for the OS and
#linkify("https://git.tigor.web.id/tigor/NixOS/src/branch/main/system/podman", "declarative configuration of Podman containers and Caddy virtual hosts").
.
],
[
Hosts over many services, like
#linkify("https://git.tigor.web.id", "Git Server and its CI Runners (Forgejo)"),
#linkify("https://nextcloud.tigor.web.id", "Nextcloud"),
#linkify("https://qbittorrent.tigor.web.id", "Seedbox (Qbittorrent)").
#linkify("https://pihole.tigor.web.id", "Pihole (DNS Management, Ad Blocker, and DHCP Server)"),
#linkify("https://jellyfin.tigor.web.id", "Mediaserver (Jellyfin)"),
OpenVPN.
],
),
),
subSection(
title: underline(
link(
"https://git.tigor.web.id/tigor/nixos",
text(rgb("#4273B0"), "NixOS"),
),
),
content: list(
[
#linkify("https://nixos.org", "NixOS") Configuration for homeserver and devices.
],
[
The declarative nature of NixOS allows for easy reproducibility on hardware failures,
lock-file system to ensure the same dependencies installed across instances,
and a generation system allows rollback on os failures, thus ensuring a very robust and stable system.
],
),
),
),
),
section(
title: "Skills",
content: (
subSection(
title: "Languages",
content: (
"Go",
"Typescript",
"Javascript",
"Lua",
"Nix",
"Rust",
"Java",
"SQL",
"HTML",
"CSS",
).join(" • "),
),
subSection(
title: "Technologies",
content: (
"Kafka",
"Postgresql",
"MongoDB",
"InfluxDB",
"Docker",
"Kubernetes",
"Gitlab CI/CD",
"Github Actions",
"NixOS",
"Neovim",
"NodeJS",
"Git",
"Hashicorp Vault",
"Redis",
"HTMX",
"Tailwind",
"React",
"NextJS",
"WebExtension",
).join(" • "),
),
),
),
section(
title: "Education",
content: (
subSection(
title: [
#set par(justify: false)
University of Indonesia
],
subTitle: "Bachelor of Economics",
content: [
],
),
subSection(
title: [
#set par(justify: false)
Hacktiv8 Indonesia
],
subTitle: "Fullstack Javascript Immersive Bootcamp",
content: [
],
),
),
),
),
)

139
template.typ Normal file
View file

@ -0,0 +1,139 @@
#let contact(text: "", link: none) = {
(text: text, link: link)
}
#let subSection(title: "", titleEnd: none, subTitle: none, subTitleEnd: none, content: []) = {
(title: title, titleEnd: titleEnd, subTitle: subTitle, subTitleEnd: subTitleEnd, content: content)
}
#let section(title: "", content: subSection()) = {
(title: title, content: content)
}
#let project(
theme: rgb("#4273B0"),
name: "",
email: none,
title: none,
contact: ((text: [], link: "")),
skills: (
languages: ()
),
main: (
(title: "", content: [])
),
sidebar: (),
body) = {
let backgroundTitle(content) = {
align(center, box(fill: theme, text(white, size: 1.25em, weight: "bold", upper(content)), width: 1fr, inset: 0.3em))
}
let secondaryTitle(content) = {
text(weight: "bold", size: 1.125em, upper(content))
}
let italicColorTitle(content) = {
text(weight: "bold", style: "italic", size: 1.125em, theme, content)
}
let formattedName = block(upper(text(2.5em, weight: "bold", theme, name)))
let formattedTitle = block(upper(text(2.25em, gray.darken(50%), title)))
let titleColumn = align(center)[
#formattedName
#formattedTitle
]
let contactColumn = align(center)[#contact.map(c => {
if c.link == none [
#c.text\
] else [
#underline(link(c.link, text(theme, c.text)))\
]
}).join()]
grid(
columns: (4fr, 5fr),
column-gutter: 0em,
contactColumn,
titleColumn,
)
set par(justify: true)
let formattedLanguageSkills = [
#text(skills.languages.join(" • "))
]
let createLeftRight(left: [], right: none) = {
if (right == none) {
align(start, text(left))
} else {
grid(
columns: (1fr, 1fr),
align(start, text(left)),
align(end, right),
)
}
}
// let parseContentList(contentList) = {
// if contentList.format == "bulletJoin" [
// #text(contentList.content.join(" • "))
// ] else if contentList.format == "bulletList" [
// #contentList.content.map(c => list(c)).join()
// ]
// }
let parseSubSections(subSections) = {
subSections.map(s => {
[
#createLeftRight(
left: secondaryTitle(s.title),
right: if s.titleEnd != none {
italicColorTitle(s.titleEnd)
}
)
#if s.subTitle != none or s.subTitleEnd != none [
#text(
top-edge: 0.2em,
createLeftRight(
left: italicColorTitle(s.subTitle),
right: s.subTitleEnd
),
)
]
#s.content
]
}).join()
}
let parseSection(section) = {
section.map(m => {
[
#backgroundTitle(m.title)
#parseSubSections(m.content)
]
}).join()
}
let mainSection = parseSection(main)
let sidebarSection = parseSection(sidebar)
grid(
columns: (0fr, 1fr),
column-gutter: 0em,
sidebarSection,
mainSection,
)
// Main body.
set par(justify: true)
show: columns.with(3, gutter: 1.3em)
// body
}