diff --git a/README.md b/README.md new file mode 100644 index 0000000..e71068e --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# My resume + +## How to generate using Guix + +1. Install `just` (and optionally `bat`) by adding to your Guix Home configuration or running `guix install just bat`. + +2. To verify the commands `just` will run on your system, run `bat ./Justfile` (or `cat ./Justfile`). + +3. Run `just` to generate `./out/resume.pdf`. Run `just run` to generate then open `./out/resume.pdf`. Run `just dev` to open a shell environment with `typst` and the required font. diff --git a/src/main.typ b/src/main.typ new file mode 100644 index 0000000..2388a57 --- /dev/null +++ b/src/main.typ @@ -0,0 +1,144 @@ +#{ + import "resume_page.typ": resume_page + + let config = ( + identity: ( + name: "Ethan Reece", + email: "contact@ethanreece.com", + phone: "(208) 515-2094", + location: "Richardson, TX", + website: "https://ethanreece.com/" + ), + skills: ( + ( + title: "DevOps and System Administration", + items: ( + "Linux", + "Git", + "GitHub Actions", + "Docker", + "Guix", + ), + ), + ( + title: "Programming Languages", + items: ( + "JavaScript", + "C#", + "Java", + "C", + "C++", + "Guile", + ), + ), + ( + title: "Layout and Web Development", + items: ( + "HTML", + "CSS", + "React", + "ASP.NET", + "Express.js", + "Astro", + "Typst", + ), + ), + ), + education: ( + ( + title: "The University of Texas at Dallas", + degree: "Bachelor of Science in Computer Science", + graduation: "May 2026", + gpa: "Current GPA: 3.764", + coursework: ( + "Probability and Statistics for CS", + "Data Structures and Algorithms", + "Software Engineering", + "C/C++ Programming in UNIX", + "Computer Architecture", + "Discrete Mathematics II", + ), + highlights: ( + "Collegium V Honors Program", + ), + ), + ), + work_experience: ( + ( + title: "Scrumfish Software", + location: "Nampa, ID", + timeframe: "June – August (2022, 2023)", + role: "Software Engineering Intern", + skills: ( + "C#/ASP.NET", + "JavaScript/React", + "Microsoft SQL Server", + "GitHub", + ), + highlights: ( + "Developed ASP.NET APIs for data management, multifactor authentication, and document conversion.", + "Created a React-based UI for a book critique platform, supporting document viewing, sharing, and annotation.", + "Automated the update of email templates in the database using SQL and PowerShell scripts.", + ), + ), + ( + title: "R-Technics", + location: "Boise, ID", + timeframe: "June – August (2022, 2023)", + role: "Software Engineering Intern", + skills: ( + "Azure", + "Windows Server", + "Docker", + "C#/ASP.NET", + ), + highlights: ( + "Supported server maintenance through update research, implementation, and monitoring.", + "Developed a C# program to fetch song lyrics and metadata from an API, later integrated into the main product.", + "Enhanced a server application with Docker and Linux support; established an Azure DevOps pipeline for build automation.", + ), + ), + ), + professional_development: ( + ( + title: "Personal Server Administration", + timeframe: "2020 – Present", + role: "Hobby", + skills: ( + "Linux", + "Docker", + "Guix/Guile", + "Git", + "Proxmox", + "Google Cloud", + ), + highlights: ( + "Manage a physical server using Proxmox, Debian VMs, and Docker for several applications.", + "Migrate new applications to Guix to utilize declarative configuration for reduced state, fine-grained system control, and increased resource efficiency.", + "Implement a VPN via Google Cloud Compute Engine to navigate CGNAT limitations.", + "Host various services including Forgejo, TrueNAS, Nextcloud, and Vaultwarden.", + ), + ), + ( + title: "Cybersecurity Courses", + timeframe: "February 2023 – November 2023", + role: "CodePath CYB101, CYB102", + skills: ( + "Linux", + ), + highlights: ( + "Gained knowledge in cybersecurity tools and defense strategies.", + ), + ), + ), + ) + + set document( + title: "Resume", + author: config.identity.name, + keywords: "resume", + date: none, + ) + + resume_page(..config) +} diff --git a/src/resume_page.typ b/src/resume_page.typ new file mode 100644 index 0000000..97ce252 --- /dev/null +++ b/src/resume_page.typ @@ -0,0 +1,373 @@ +#let resume_page( + identity: ( + name: "", + email: "", + phone: "", + location: "", + website: "", + ), + skills: ( + ( + title: "", + items: (""), + ), + ), + education: ( + ( + title: "", + degree: "", + graduation: "", + gpa: "", + coursework: (""), + highlights: (""), + ), + ), + work_experience: ( + ( + title: "", + location: "", + timeframe: "", + role: "", + skills: (""), + highlights: (""), + ), + ), + professional_development: ( + ( + title: "", + timeframe: "", + role: "", + skills: (""), + highlights: (""), + ), + ), +) = { + let _block( + below: 0em, + width: 100%, + padding: ( + top: 0em, + right: 0em, + bottom: 0em, + left: 0em, + ), + body + ) = block( + below: below, + width: width, + align( + top + left, + pad( + ..padding, + body + ) + ) + ) + + let _text( + font: "Inter", + size: 10pt, + weight: "regular", + block: ( + below: 0em, + width: 100%, + padding: ( + top: 0em, + right: 0em, + bottom: 0em, + left: 0em, + ), + ), + body + ) = text( + font: font, + size: size, + weight: weight, + _block( + ..block, + body + ) + ) + + let _heading(title: "") = _text( + size: 13pt, + block: (below: .95em), + heading( + _text( + size: 13pt, + weight: "bold", + title + ) + ) + ) + + let section_base( + title: "", + body + ) = _text( + block: (below: 2.5em), + { + _heading(title: title) + _text( + block: ( + padding: (left: 1em), + ), + body + ) + } + ) + + let section_item( + title: "", + subtitle: "", + role: "", + timeframe: "", + skills: (""), + highlights: (""), + ) = _text( + block: (below: 1.5em), + { + _text( + block: (below: 0.95em), + grid( + columns: (40%, 60%), + rows: 2, + gutter: 0.75em, + align: ( + left, + right, + ), + grid( + columns: 2, + gutter: 1.5em, + align: bottom, + text( + weight: "semibold", + title + ), + text( + size: 9pt, + weight: "extralight", + subtitle + ) + ), + text( + weight: "regular", + timeframe + ), + text( + weight: "light", + size: 9.5pt, + role + ), + text( + weight: "light", + size: 9.5pt, + skills.join(", "), + ), + ) + ) + _text( + block: ( + padding: (left: 2em), + ), + for highlight in highlights { + _text( + block: (below: 1em), + par( + justify: true, + highlight + ) + ) + } + ) + } + ) + + let section( + title: "", + items: ( + ( + title: "", + subtitle: "", + role: "", + timeframe: "", + skills: (""), + highlights: (""), + ), + ), + ) = section_base( + title: title, + for item in items { + section_item(..item) + } + ) + + let title_block(title: "") = _text( + size: 28pt, + block: (below: 0.60em), + weight: "extrabold", + align( + center, + title + ) + ) + + let contact_info_block( + email: "", + phone: "", + location: "", + website: "", + ) = _text( + size: 10.5pt, + block: (below: 2.75em), + weight: "medium", + align( + center, + grid( + columns: 4, + rows: 1, + gutter: 4em, + email, + phone, + location, + website, + ) + ) + ) + + let skills_block( + skills: ( + ( + title: "", + items: (""), + ), + ), + ) = section_base( + title: "Skills", + _text( + grid( + columns: (33%, 67%), + gutter: 1em, + ..for skill in skills { + ( + _text( + weight: "semibold", + skill.title + ), + skill.items.join(", "), + ) + } + ) + ) + ) + + let education_block( + education: ( + ( + title: "", + degree: "", + graduation: "", + gpa: "", + coursework: (""), + highlights: (""), + ), + ), + ) = section( + title: "Education", + items: education.map(school => ( + title: school.title, + role: school.degree, + timeframe: school.graduation, + skills: ( + school.gpa, + ), + highlights: ( + ..school.highlights, + { + _text( + weight: "medium", + block: ( + below: 0.75em, + ), + "Relevant Coursework" + ) + _text( + size: 9.5pt, + block: ( + padding: ( + left: 2em, + ), + ), + school.coursework.join(", ") + ) + } + ), + )) + ) + + let work_experience_block( + work_experience: ( + ( + title: "", + location: "", + timeframe: "", + role: "", + skills: (""), + highlights: (""), + ), + ), + ) = section( + title: "Work Experience", + items: work_experience.map(company => ( + title: company.title, + subtitle: company.location, + timeframe: company.timeframe, + role: company.role, + skills: company.skills, + highlights: company.highlights, + )), + ) + + let professional_development_block( + professional_development: ( + ( + title: "", + timeframe: "", + role: "", + skills: (""), + highlights: (""), + ), + ), + ) = section( + title: "Professional Development", + items: professional_development.map(item => ( + title: item.title, + timeframe: item.timeframe, + role: item.role, + skills: item.skills, + highlights: item.highlights, + )), + ) + + page( + paper: "us-letter", + margin: ( + x: 0.5in, + y: 0.4in, + ), + { + title_block(title: identity.name) + contact_info_block( + email: identity.email, + phone: identity.phone, + location: identity.location, + website: identity.website, + ) + skills_block(skills: skills) + education_block(education: education) + work_experience_block(work_experience: work_experience) + professional_development_block(professional_development: professional_development) + } + ) +}