Baseline resume from 5/10/2026
This commit is contained in:
commit
868eb5a77e
42
Makefile
Normal file
42
Makefile
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Build targets for the resume.
|
||||
#
|
||||
# Single content source: resume.yaml
|
||||
#
|
||||
# make -> PDF (default; renders .tex from YAML, then pdflatex twice)
|
||||
# make odt -> ODT (renders directly from YAML)
|
||||
# make docx -> DOCX (ODT -> LibreOffice convert -> DOCX)
|
||||
# make all -> PDF + ODT + DOCX
|
||||
# make clean -> remove TeX aux files
|
||||
# make distclean -> also remove generated PDF/ODT/DOCX/TEX
|
||||
|
||||
YAML := resume.yaml
|
||||
TEX := MikeEberlein_Resume.tex
|
||||
PDF := MikeEberlein_Resume.pdf
|
||||
ODT := MikeEberlein_Resume.odt
|
||||
DOCX := MikeEberlein_Resume.docx
|
||||
|
||||
.PHONY: all pdf odt docx clean distclean
|
||||
|
||||
all: $(PDF) $(ODT) $(DOCX)
|
||||
pdf: $(PDF)
|
||||
odt: $(ODT)
|
||||
docx: $(DOCX)
|
||||
|
||||
$(TEX): $(YAML) render_tex.py
|
||||
python3 render_tex.py
|
||||
|
||||
$(PDF): $(TEX)
|
||||
pdflatex -interaction=nonstopmode -halt-on-error $(TEX)
|
||||
pdflatex -interaction=nonstopmode -halt-on-error $(TEX)
|
||||
|
||||
$(ODT): $(YAML) render_odt.py
|
||||
python3 render_odt.py
|
||||
|
||||
$(DOCX): $(ODT)
|
||||
soffice --headless --convert-to docx $(ODT)
|
||||
|
||||
clean:
|
||||
rm -f *.aux *.log *.out *.toc *.fls *.fdb_latexmk *.synctex.gz
|
||||
|
||||
distclean: clean
|
||||
rm -f $(PDF) $(ODT) $(DOCX) $(TEX)
|
||||
BIN
MikeEberlein_Resume.docx
Normal file
BIN
MikeEberlein_Resume.docx
Normal file
Binary file not shown.
BIN
MikeEberlein_Resume.odt
Normal file
BIN
MikeEberlein_Resume.odt
Normal file
Binary file not shown.
BIN
MikeEberlein_Resume.pdf
Normal file
BIN
MikeEberlein_Resume.pdf
Normal file
Binary file not shown.
146
MikeEberlein_Resume.tex
Normal file
146
MikeEberlein_Resume.tex
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
% Mike Eberlein — single-page resume (generated from resume.yaml).
|
||||
% Build: pdflatex MikeEberlein_Resume.tex (run twice for stable layout)
|
||||
% Do not edit by hand — re-run `python3 render_tex.py` after changing resume.yaml.
|
||||
%
|
||||
% Fonts: Roboto via the roboto LaTeX package (texlive-fonts-extra). Local copies
|
||||
% of the .ttf files also live in ./fonts/ for portability.
|
||||
|
||||
\documentclass[letterpaper,10pt]{extarticle}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[sfdefault]{roboto}
|
||||
\usepackage{microtype}
|
||||
\usepackage[letterpaper,margin=0.5in,top=0.35in,bottom=0.3in]{geometry}
|
||||
\usepackage[dvipsnames]{xcolor}
|
||||
\usepackage{titlesec}
|
||||
\usepackage{enumitem}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{ragged2e}
|
||||
\usepackage{parskip}
|
||||
|
||||
\newcommand{\RobotoLight}{\fontseries{l}\selectfont}
|
||||
\newcommand{\RobotoMedium}{\fontseries{m}\selectfont}
|
||||
|
||||
\definecolor{accent}{HTML}{1F3864}
|
||||
\definecolor{body}{HTML}{222222}
|
||||
\definecolor{muted}{HTML}{555555}
|
||||
\color{body}
|
||||
|
||||
\hypersetup{colorlinks=true, urlcolor=accent, linkcolor=accent}
|
||||
|
||||
\setlength{\parindent}{0pt}
|
||||
\setlength{\parskip}{0pt}
|
||||
\linespread{1.0}
|
||||
\pagestyle{empty}
|
||||
|
||||
\titleformat{\section}
|
||||
{\color{accent}\RobotoMedium\bfseries\large}
|
||||
{}{0pt}
|
||||
{\MakeUppercase}
|
||||
\titlespacing*{\section}{0pt}{4pt}{0pt}
|
||||
\newcommand{\sectionrule}{\vspace{-4pt}{\color{accent}\rule{\linewidth}{0.5pt}}\par\vspace{1pt}}
|
||||
|
||||
\newcommand{\job}[2]{%
|
||||
\vspace{3pt}%
|
||||
\noindent\textbf{#1}\hfill\textbf{#2}\par\vspace{0pt}%
|
||||
}
|
||||
\newcommand{\role}[2]{%
|
||||
\vspace{1pt}%
|
||||
\noindent\textit{\textcolor{muted}{#1}}\hfill\textit{\textcolor{muted}{#2}}\par%
|
||||
}
|
||||
\newcommand{\subrole}[2]{%
|
||||
\vspace{1pt}%
|
||||
\noindent\hspace{0.18in}#1\hfill\textit{\textcolor{muted}{#2}}\par%
|
||||
}
|
||||
|
||||
\newlist{bullets}{itemize}{2}
|
||||
\setlist[bullets]{leftmargin=0.22in, itemsep=0pt, topsep=0pt, parsep=0pt, label={\textbullet}}
|
||||
\newlist{subbullets}{itemize}{2}
|
||||
\setlist[subbullets]{leftmargin=0.42in, itemsep=0pt, topsep=0pt, parsep=0pt, label={\textbullet}, before=\color{muted}}
|
||||
|
||||
\newcommand{\name}[1]{%
|
||||
\begin{center}%
|
||||
{\color{accent}\fontseries{l}\fontsize{26}{30}\selectfont #1}%
|
||||
\end{center}%
|
||||
\vspace{10pt}%
|
||||
}
|
||||
\newcommand{\contact}[1]{%
|
||||
\begin{center}\textcolor{muted}{\small #1}\end{center}%
|
||||
\vspace{2pt}%
|
||||
}
|
||||
|
||||
|
||||
\begin{document}
|
||||
|
||||
\name{MIKE EBERLEIN}
|
||||
\contact{Engineering Manager • Infotainment \& Software-Defined Vehicles \textbullet{} \href{mailto:mike.t.eberlein@gmail.com}{mike.t.eberlein@gmail.com}}
|
||||
|
||||
{\justifying\noindent
|
||||
Engineering manager with 15+ years building market-leading automotive infotainment systems, spanning manual and automated test, UI development, embedded service development, program management, and people leadership. Effective at translating ambiguous platform goals into shippable software across Android, Yocto Linux, and QNX, and at growing teams that deliver on aggressive vehicle-program timing.\par}
|
||||
|
||||
\section{Experience}\sectionrule
|
||||
|
||||
\job{General Motors}{Oct 2014 – Present}
|
||||
\role{Engineering Group Manager}{May 2021 – Present}
|
||||
|
||||
\subrole{Infotainment Vehicle Data}{2023 – Present}
|
||||
\begin{subbullets}
|
||||
\item Lead software engineers delivering the mechanisms that surface vehicle data from in-vehicle modules to applications and services across Android, Yocto Linux, and QNX.
|
||||
\end{subbullets}
|
||||
|
||||
\subrole{Development Quality Assurance}{2021 – 2023}
|
||||
\begin{subbullets}
|
||||
\item Led a cross-functional team of 20+ engineers driving functional test for infotainment platform development; established cross-component knowledge sharing and team planning practices.
|
||||
\end{subbullets}
|
||||
|
||||
\subrole{Ultifi Platform \& Vehicle Data}{2021 – 2023}
|
||||
\begin{subbullets}
|
||||
\item Led development of core embedded components for GM's software-defined vehicle platform, partnering with architecture, systems, and downstream test to drive end-to-end delivery.
|
||||
\end{subbullets}
|
||||
|
||||
\subrole{Business Manager}{2021 – 2023}
|
||||
\begin{subbullets}
|
||||
\item Owned and defended the platform org budget; ran SOWs with multiple suppliers in partnership with procurement; built workload models and managed headcount allocation.
|
||||
\end{subbullets}
|
||||
|
||||
\role{Infotainment Software Program Manager}{Jan 2017 – May 2021}
|
||||
\begin{bullets}
|
||||
\item Lead software program manager for MY22/MY23 Center Stack Module (CSM) development — owned software delivery, timing, and scope from program initiation through late vehicle milestones.
|
||||
\item Drove impact analysis, feature commitment, and delivery tracking with partner teams across GM; managed Tier-X suppliers on deliveries, defects, and change negotiations.
|
||||
\item MY20–MY23 program execution commitment lead: scoped and estimated new features in concept phase, then tracked scope and staffing through development.
|
||||
\item Built Python 3 / Excel / Rational Team Concert API tooling for task breakdown, inter-team dependencies, and historical-data-driven staffing models.
|
||||
\end{bullets}
|
||||
|
||||
\role{Infotainment Software Engineer}{Oct 2014 – Jan 2017}
|
||||
\begin{bullets}
|
||||
\item Developed Android (Java) infotainment apps including a hybrid powertrain optimization HMI and the USB / over-the-air software update client for the head unit.
|
||||
\item Built Windows-based developer tooling with CAN simulation to enable powertrain teams to iterate against the infotainment system off-vehicle.
|
||||
\end{bullets}
|
||||
|
||||
\job{Harman International}{Oct 2008 – Oct 2014}
|
||||
\role{Software Engineer}{Mar 2011 – Oct 2014}
|
||||
\begin{bullets}
|
||||
\item Designed and built navigation infotainment applications in ActionScript 2/3, Lua, and C++ on QNX-based head units shipped to multiple OEMs.
|
||||
\item Managed the navigation engine supplier — deliveries, feature implementation, and defect resolution — and built Excel-based dashboards over the internal defect database for program-level reporting.
|
||||
\end{bullets}
|
||||
|
||||
\role{Software Verification Engineer / UI Developer}{Oct 2008 – Mar 2011}
|
||||
\begin{bullets}
|
||||
\item Converted Photoshop design files into ActionScript 2 UI components consumed by engineering teams.
|
||||
\item Authored automated test tools using VB.NET, CAN, ACV-LAN, and proprietary trace tools; analyzed requirements, ran in-vehicle test drives, and validated infotainment systems with customers.
|
||||
\end{bullets}
|
||||
|
||||
\section{Education}\sectionrule
|
||||
|
||||
\job{B.S. Computer Science, Lawrence Technological University}{Apr 2011}
|
||||
\begin{bullets}
|
||||
\item Graduated summa cum laude from the Honors College. Coursework in C, C++, Java, Visual Basic, mobile development, and scripting.
|
||||
\item Senior capstone: Simulink-based engine control module for the Formula SAE team. Math club member and math modeling competitor.
|
||||
\end{bullets}
|
||||
|
||||
\section{Certifications}\sectionrule
|
||||
|
||||
Certified Scrum Professional (2012–2015) \textbullet{} Certified Scrum Master (2010–2019) \textbullet{} SAFe Agilist (2020–2023)
|
||||
|
||||
\end{document}
|
||||
68
README.md
Normal file
68
README.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# Mike Eberlein — Resume
|
||||
|
||||
Single-page resume for Mike Eberlein (Engineering Manager — automotive infotainment / software-defined vehicles).
|
||||
|
||||
**Single content source: `resume.yaml`.** Two renderers (LaTeX and ODT) read the YAML and emit format-specific files. DOCX is produced by converting the ODT with LibreOffice. Edit `resume.yaml`; the generated `.tex` should not be edited by hand.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
| --- | --- |
|
||||
| `resume.yaml` | **Source of truth.** Edit this. |
|
||||
| `render_tex.py` | YAML → `MikeEberlein_Resume.tex`. LaTeX preamble lives inline. |
|
||||
| `render_odt.py` | YAML → `MikeEberlein_Resume.odt`. ODT styles live inline. |
|
||||
| `MikeEberlein_Resume.tex` | Generated. Do not edit by hand. |
|
||||
| `MikeEberlein_Resume.pdf` | Built from `.tex` via `pdflatex`. |
|
||||
| `MikeEberlein_Resume.odt` | Built from YAML via `render_odt.py`. |
|
||||
| `MikeEberlein_Resume.docx` | Built from `.odt` via LibreOffice. |
|
||||
| `Makefile` | Build orchestration. |
|
||||
| `fonts/` | Bundled Roboto `.ttf` files (Regular, Italic, Bold, BoldItalic, Light, Medium). |
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
make # PDF (default)
|
||||
make odt # ODT
|
||||
make docx # DOCX (via LibreOffice from the ODT)
|
||||
make all # PDF + ODT + DOCX
|
||||
make clean # remove TeX aux files
|
||||
make distclean # also remove generated PDF/ODT/DOCX/TEX
|
||||
```
|
||||
|
||||
Required system packages (Debian/Ubuntu):
|
||||
|
||||
```bash
|
||||
sudo apt install texlive-latex-recommended texlive-latex-extra texlive-fonts-extra
|
||||
sudo apt install libreoffice # for the .docx output
|
||||
sudo apt install python3-yaml # for the renderers (usually pre-installed)
|
||||
```
|
||||
|
||||
## Editing
|
||||
|
||||
Open `resume.yaml`. The schema:
|
||||
|
||||
```yaml
|
||||
header: { name, tagline, email }
|
||||
summary: <paragraph>
|
||||
experience:
|
||||
- company, dates
|
||||
roles:
|
||||
- title, dates
|
||||
# either:
|
||||
bullets: [ ... ]
|
||||
# or, for nested sub-roles (used for one role with concurrent focus areas):
|
||||
subroles:
|
||||
- title, dates
|
||||
bullets: [ ... ]
|
||||
education:
|
||||
- school, dates, bullets
|
||||
certifications: [ ... ]
|
||||
```
|
||||
|
||||
After editing, run `make all` to rebuild every format from the same content.
|
||||
|
||||
## Layout / styling
|
||||
|
||||
Both renderers contain their own styling code (LaTeX preamble in `render_tex.py`, ODT XML styles in `render_odt.py`). Tweak typography, colors, spacing, etc. there. The two renderers aim for visual parity but are not byte-identical — pdflatex and LibreOffice differ on font metrics and line breaking.
|
||||
|
||||
The DOCX is produced from the ODT (LibreOffice's ODT→DOCX conversion is essentially lossless because the formats are siblings). DOCX fidelity is therefore nearly identical to ODT.
|
||||
BIN
fonts/Roboto-Bold.ttf
Normal file
BIN
fonts/Roboto-Bold.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-BoldItalic.ttf
Normal file
BIN
fonts/Roboto-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Italic.ttf
Normal file
BIN
fonts/Roboto-Italic.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Light.ttf
Normal file
BIN
fonts/Roboto-Light.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Medium.ttf
Normal file
BIN
fonts/Roboto-Medium.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Regular.ttf
Normal file
BIN
fonts/Roboto-Regular.ttf
Normal file
Binary file not shown.
219
render_odt.py
Normal file
219
render_odt.py
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Render resume.yaml -> MikeEberlein_Resume.odt.
|
||||
|
||||
Builds the ODT zip from scratch (mimetype, manifest, meta, styles, content).
|
||||
The ODT styling lives inline as STYLES below; content comes from resume.yaml.
|
||||
|
||||
DOCX is produced by piping this output through LibreOffice:
|
||||
soffice --headless --convert-to docx MikeEberlein_Resume.odt
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import yaml
|
||||
import zipfile
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
DATA = os.path.join(HERE, "resume.yaml")
|
||||
OUT = os.path.join(HERE, "MikeEberlein_Resume.odt")
|
||||
|
||||
MIMETYPE = "application/vnd.oasis.opendocument.text"
|
||||
|
||||
MANIFEST = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.3">
|
||||
<manifest:file-entry manifest:full-path="/" manifest:version="1.3" manifest:media-type="application/vnd.oasis.opendocument.text"/>
|
||||
<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>
|
||||
<manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/>
|
||||
<manifest:file-entry manifest:full-path="meta.xml" manifest:media-type="text/xml"/>
|
||||
</manifest:manifest>
|
||||
'''
|
||||
|
||||
META = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3">
|
||||
<office:meta><dc:title>Mike Eberlein Resume</dc:title><meta:generator>render_odt.py</meta:generator></office:meta>
|
||||
</office:document-meta>
|
||||
'''
|
||||
|
||||
STYLES = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<office:document-styles
|
||||
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
||||
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
|
||||
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
|
||||
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
|
||||
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
|
||||
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
|
||||
office:version="1.3">
|
||||
<office:font-face-decls>
|
||||
<style:font-face style:name="Roboto" svg:font-family="Roboto"/>
|
||||
<style:font-face style:name="Roboto Light" svg:font-family="'Roboto Light'"/>
|
||||
</office:font-face-decls>
|
||||
<office:styles>
|
||||
<style:default-style style:family="paragraph">
|
||||
<style:paragraph-properties fo:hyphenate="false"/>
|
||||
<style:text-properties style:font-name="Roboto" fo:font-size="9pt" fo:color="#222222"/>
|
||||
</style:default-style>
|
||||
<style:style style:name="Standard" style:family="paragraph" style:class="text">
|
||||
<style:paragraph-properties fo:margin-top="0in" fo:margin-bottom="0in" fo:line-height="105%"/>
|
||||
<style:text-properties style:font-name="Roboto" fo:font-size="9pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Name" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:text-align="center" fo:margin-bottom="0.04in"/>
|
||||
<style:text-properties style:font-name="Roboto Light" fo:font-size="20pt" fo:color="#1F3864" fo:letter-spacing="0.04in"/>
|
||||
</style:style>
|
||||
<style:style style:name="Contact" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:text-align="center" fo:margin-bottom="0.06in"/>
|
||||
<style:text-properties fo:font-size="9pt" fo:color="#555555"/>
|
||||
</style:style>
|
||||
<style:style style:name="Summary" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:text-align="justify" fo:margin-bottom="0.04in"/>
|
||||
<style:text-properties fo:font-size="9pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="SectionHead" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0.03in" fo:margin-bottom="0.01in" fo:border-bottom="0.5pt solid #1F3864" fo:padding-bottom="0.005in"/>
|
||||
<style:text-properties style:font-name="Roboto" fo:font-size="11pt" fo:color="#1F3864" fo:font-weight="bold" fo:letter-spacing="0.02in"/>
|
||||
</style:style>
|
||||
<style:style style:name="JobHead" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0.03in"/>
|
||||
<style:text-properties fo:font-weight="bold" fo:font-size="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="RoleLine" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0.015in"/>
|
||||
<style:text-properties fo:font-style="italic" fo:font-size="9pt" fo:color="#555555"/>
|
||||
</style:style>
|
||||
<style:style style:name="SubRole" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0.012in" fo:margin-left="0.22in"/>
|
||||
<style:text-properties fo:font-size="9pt" fo:color="#222222"/>
|
||||
</style:style>
|
||||
<style:style style:name="Bullet" style:family="paragraph" style:parent-style-name="Standard" style:list-style-name="L1">
|
||||
<style:paragraph-properties fo:margin-left="0.18in" fo:text-indent="-0.13in"/>
|
||||
</style:style>
|
||||
<style:style style:name="SubBullet" style:family="paragraph" style:parent-style-name="Standard" style:list-style-name="L1">
|
||||
<style:paragraph-properties fo:margin-left="0.4in" fo:text-indent="-0.13in"/>
|
||||
<style:text-properties fo:font-size="9pt" fo:color="#555555"/>
|
||||
</style:style>
|
||||
<style:style style:name="Bold" style:family="text"><style:text-properties fo:font-weight="bold"/></style:style>
|
||||
<style:style style:name="Italic" style:family="text"><style:text-properties fo:font-style="italic" fo:color="#555555"/></style:style>
|
||||
|
||||
<text:list-style style:name="L1">
|
||||
<text:list-level-style-bullet text:level="1" text:bullet-char="•">
|
||||
<style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
|
||||
<style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.18in" fo:text-indent="-0.13in" fo:margin-left="0.18in"/>
|
||||
</style:list-level-properties>
|
||||
</text:list-level-style-bullet>
|
||||
</text:list-style>
|
||||
</office:styles>
|
||||
<office:automatic-styles>
|
||||
<style:page-layout style:name="PL1">
|
||||
<style:page-layout-properties fo:page-width="8.5in" fo:page-height="11in"
|
||||
fo:margin-top="0.25in" fo:margin-bottom="0.2in" fo:margin-left="0.5in" fo:margin-right="0.5in"
|
||||
style:print-orientation="portrait"/>
|
||||
</style:page-layout>
|
||||
</office:automatic-styles>
|
||||
<office:master-styles>
|
||||
<style:master-page style:name="Standard" style:page-layout-name="PL1"/>
|
||||
</office:master-styles>
|
||||
</office:document-styles>
|
||||
'''
|
||||
|
||||
CONTENT_HEAD = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<office:document-content
|
||||
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
||||
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
|
||||
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
|
||||
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
|
||||
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
|
||||
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
|
||||
office:version="1.3">
|
||||
<office:automatic-styles>
|
||||
<style:style style:name="JobTab" style:family="paragraph" style:parent-style-name="JobHead">
|
||||
<style:paragraph-properties>
|
||||
<style:tab-stops><style:tab-stop style:position="7.5in" style:type="right"/></style:tab-stops>
|
||||
</style:paragraph-properties>
|
||||
</style:style>
|
||||
<style:style style:name="RoleTab" style:family="paragraph" style:parent-style-name="RoleLine">
|
||||
<style:paragraph-properties>
|
||||
<style:tab-stops><style:tab-stop style:position="7.5in" style:type="right"/></style:tab-stops>
|
||||
</style:paragraph-properties>
|
||||
</style:style>
|
||||
<style:style style:name="SubRoleTab" style:family="paragraph" style:parent-style-name="SubRole">
|
||||
<style:paragraph-properties>
|
||||
<style:tab-stops><style:tab-stop style:position="7.5in" style:type="right"/></style:tab-stops>
|
||||
</style:paragraph-properties>
|
||||
</style:style>
|
||||
</office:automatic-styles>
|
||||
<office:body><office:text>'''
|
||||
|
||||
CONTENT_TAIL = '</office:text></office:body></office:document-content>'
|
||||
|
||||
# ---------- XML helpers ----------
|
||||
XML_ESCAPES = {"&": "&", "<": "<", ">": ">"}
|
||||
|
||||
def xesc(s: str) -> str:
|
||||
return "".join(XML_ESCAPES.get(c, c) for c in s)
|
||||
|
||||
def p(style, content):
|
||||
return f'<text:p text:style-name="{style}">{content}</text:p>'
|
||||
|
||||
def b(text):
|
||||
return f'<text:span text:style-name="Bold">{xesc(text)}</text:span>'
|
||||
|
||||
def i(text):
|
||||
return f'<text:span text:style-name="Italic">{xesc(text)}</text:span>'
|
||||
|
||||
TAB = '<text:tab/>'
|
||||
|
||||
def bullet(text, style="Bullet"):
|
||||
return (f'<text:list text:style-name="L1"><text:list-item>'
|
||||
f'<text:p text:style-name="{style}">{xesc(text)}</text:p>'
|
||||
f'</text:list-item></text:list>')
|
||||
|
||||
def render(data) -> str:
|
||||
parts = []
|
||||
h = data["header"]
|
||||
parts.append(p("Name", xesc(h["name"])))
|
||||
contact = f'{xesc(h["tagline"])} • {xesc(h["email"])}'
|
||||
parts.append(p("Contact", contact))
|
||||
parts.append(p("Summary", xesc(data["summary"].strip())))
|
||||
|
||||
parts.append(p("SectionHead", "EXPERIENCE"))
|
||||
for company in data["experience"]:
|
||||
parts.append(f'<text:p text:style-name="JobTab">{b(company["company"])}{TAB}{xesc(company["dates"])}</text:p>')
|
||||
for role in company["roles"]:
|
||||
parts.append(f'<text:p text:style-name="RoleTab">{i(role["title"])}{TAB}{i(role["dates"])}</text:p>')
|
||||
if "subroles" in role:
|
||||
for sr in role["subroles"]:
|
||||
parts.append(f'<text:p text:style-name="SubRoleTab">{xesc(sr["title"])}{TAB}{i(sr["dates"])}</text:p>')
|
||||
for blt in sr["bullets"]:
|
||||
parts.append(bullet(blt, style="SubBullet"))
|
||||
elif "bullets" in role:
|
||||
for blt in role["bullets"]:
|
||||
parts.append(bullet(blt, style="Bullet"))
|
||||
|
||||
parts.append(p("SectionHead", "EDUCATION"))
|
||||
for ed in data["education"]:
|
||||
parts.append(f'<text:p text:style-name="JobTab">{b(ed["school"])}{TAB}{xesc(ed["dates"])}</text:p>')
|
||||
for blt in ed["bullets"]:
|
||||
parts.append(bullet(blt, style="Bullet"))
|
||||
|
||||
parts.append(p("SectionHead", "CERTIFICATIONS"))
|
||||
parts.append(p("Standard", " • ".join(xesc(c) for c in data["certifications"])))
|
||||
|
||||
return CONTENT_HEAD + "".join(parts) + CONTENT_TAIL
|
||||
|
||||
def main():
|
||||
with open(DATA, "r", encoding="utf-8") as f:
|
||||
data = yaml.safe_load(f)
|
||||
content = render(data)
|
||||
|
||||
with zipfile.ZipFile(OUT, "w", zipfile.ZIP_DEFLATED) as z:
|
||||
zi = zipfile.ZipInfo("mimetype")
|
||||
zi.compress_type = zipfile.ZIP_STORED
|
||||
z.writestr(zi, MIMETYPE)
|
||||
z.writestr("META-INF/manifest.xml", MANIFEST)
|
||||
z.writestr("meta.xml", META)
|
||||
z.writestr("styles.xml", STYLES)
|
||||
z.writestr("content.xml", content)
|
||||
print(f"wrote {OUT}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
168
render_tex.py
Normal file
168
render_tex.py
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Render resume.yaml -> MikeEberlein_Resume.tex.
|
||||
|
||||
The LaTeX preamble (styles, macros, page layout) lives inline as PREAMBLE
|
||||
below; if you want to tweak typography or colors, edit there. Content is read
|
||||
from resume.yaml.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
DATA = os.path.join(HERE, "resume.yaml")
|
||||
OUT = os.path.join(HERE, "MikeEberlein_Resume.tex")
|
||||
|
||||
PREAMBLE = r"""% Mike Eberlein — single-page resume (generated from resume.yaml).
|
||||
% Build: pdflatex MikeEberlein_Resume.tex (run twice for stable layout)
|
||||
% Do not edit by hand — re-run `python3 render_tex.py` after changing resume.yaml.
|
||||
%
|
||||
% Fonts: Roboto via the roboto LaTeX package (texlive-fonts-extra). Local copies
|
||||
% of the .ttf files also live in ./fonts/ for portability.
|
||||
|
||||
\documentclass[letterpaper,10pt]{extarticle}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[sfdefault]{roboto}
|
||||
\usepackage{microtype}
|
||||
\usepackage[letterpaper,margin=0.5in,top=0.35in,bottom=0.3in]{geometry}
|
||||
\usepackage[dvipsnames]{xcolor}
|
||||
\usepackage{titlesec}
|
||||
\usepackage{enumitem}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{ragged2e}
|
||||
\usepackage{parskip}
|
||||
|
||||
\newcommand{\RobotoLight}{\fontseries{l}\selectfont}
|
||||
\newcommand{\RobotoMedium}{\fontseries{m}\selectfont}
|
||||
|
||||
\definecolor{accent}{HTML}{1F3864}
|
||||
\definecolor{body}{HTML}{222222}
|
||||
\definecolor{muted}{HTML}{555555}
|
||||
\color{body}
|
||||
|
||||
\hypersetup{colorlinks=true, urlcolor=accent, linkcolor=accent}
|
||||
|
||||
\setlength{\parindent}{0pt}
|
||||
\setlength{\parskip}{0pt}
|
||||
\linespread{1.0}
|
||||
\pagestyle{empty}
|
||||
|
||||
\titleformat{\section}
|
||||
{\color{accent}\RobotoMedium\bfseries\large}
|
||||
{}{0pt}
|
||||
{\MakeUppercase}
|
||||
\titlespacing*{\section}{0pt}{4pt}{0pt}
|
||||
\newcommand{\sectionrule}{\vspace{-4pt}{\color{accent}\rule{\linewidth}{0.5pt}}\par\vspace{1pt}}
|
||||
|
||||
\newcommand{\job}[2]{%
|
||||
\vspace{3pt}%
|
||||
\noindent\textbf{#1}\hfill\textbf{#2}\par\vspace{0pt}%
|
||||
}
|
||||
\newcommand{\role}[2]{%
|
||||
\vspace{1pt}%
|
||||
\noindent\textit{\textcolor{muted}{#1}}\hfill\textit{\textcolor{muted}{#2}}\par%
|
||||
}
|
||||
\newcommand{\subrole}[2]{%
|
||||
\vspace{1pt}%
|
||||
\noindent\hspace{0.18in}#1\hfill\textit{\textcolor{muted}{#2}}\par%
|
||||
}
|
||||
|
||||
\newlist{bullets}{itemize}{2}
|
||||
\setlist[bullets]{leftmargin=0.22in, itemsep=0pt, topsep=0pt, parsep=0pt, label={\textbullet}}
|
||||
\newlist{subbullets}{itemize}{2}
|
||||
\setlist[subbullets]{leftmargin=0.42in, itemsep=0pt, topsep=0pt, parsep=0pt, label={\textbullet}, before=\color{muted}}
|
||||
|
||||
\newcommand{\name}[1]{%
|
||||
\begin{center}%
|
||||
{\color{accent}\fontseries{l}\fontsize{26}{30}\selectfont #1}%
|
||||
\end{center}%
|
||||
\vspace{10pt}%
|
||||
}
|
||||
\newcommand{\contact}[1]{%
|
||||
\begin{center}\textcolor{muted}{\small #1}\end{center}%
|
||||
\vspace{2pt}%
|
||||
}
|
||||
"""
|
||||
|
||||
# LaTeX special characters that need escaping in content text.
|
||||
LATEX_ESCAPES = {
|
||||
"&": r"\&",
|
||||
"%": r"\%",
|
||||
"$": r"\$",
|
||||
"#": r"\#",
|
||||
"_": r"\_",
|
||||
"{": r"\{",
|
||||
"}": r"\}",
|
||||
"~": r"\textasciitilde{}",
|
||||
"^": r"\textasciicircum{}",
|
||||
"\\": r"\textbackslash{}",
|
||||
}
|
||||
|
||||
def tex_escape(s: str) -> str:
|
||||
out = []
|
||||
for ch in s:
|
||||
out.append(LATEX_ESCAPES.get(ch, ch))
|
||||
return "".join(out)
|
||||
|
||||
def emit_bullets(items, env="bullets"):
|
||||
lines = [f"\\begin{{{env}}}"]
|
||||
for b in items:
|
||||
lines.append(f" \\item {tex_escape(b)}")
|
||||
lines.append(f"\\end{{{env}}}")
|
||||
return "\n".join(lines)
|
||||
|
||||
def render(data) -> str:
|
||||
out = [PREAMBLE, ""]
|
||||
out.append(r"\begin{document}")
|
||||
out.append("")
|
||||
h = data["header"]
|
||||
out.append(f"\\name{{{tex_escape(h['name'])}}}")
|
||||
contact = (
|
||||
f"{tex_escape(h['tagline'])} \\textbullet{{}} "
|
||||
f"\\href{{mailto:{h['email']}}}{{{tex_escape(h['email'])}}}"
|
||||
)
|
||||
out.append(f"\\contact{{{contact}}}")
|
||||
out.append("")
|
||||
out.append("{\\justifying\\noindent")
|
||||
out.append(tex_escape(data["summary"].strip()) + r"\par}")
|
||||
out.append("")
|
||||
out.append(r"\section{Experience}\sectionrule")
|
||||
out.append("")
|
||||
for company in data["experience"]:
|
||||
out.append(f"\\job{{{tex_escape(company['company'])}}}{{{tex_escape(company['dates'])}}}")
|
||||
for role in company["roles"]:
|
||||
out.append(f"\\role{{{tex_escape(role['title'])}}}{{{tex_escape(role['dates'])}}}")
|
||||
if "subroles" in role:
|
||||
out.append("")
|
||||
for sr in role["subroles"]:
|
||||
out.append(f"\\subrole{{{tex_escape(sr['title'])}}}{{{tex_escape(sr['dates'])}}}")
|
||||
out.append(emit_bullets(sr["bullets"], env="subbullets"))
|
||||
out.append("")
|
||||
elif "bullets" in role:
|
||||
out.append(emit_bullets(role["bullets"], env="bullets"))
|
||||
out.append("")
|
||||
out.append(r"\section{Education}\sectionrule")
|
||||
out.append("")
|
||||
for ed in data["education"]:
|
||||
out.append(f"\\job{{{tex_escape(ed['school'])}}}{{{tex_escape(ed['dates'])}}}")
|
||||
out.append(emit_bullets(ed["bullets"], env="bullets"))
|
||||
out.append("")
|
||||
out.append(r"\section{Certifications}\sectionrule")
|
||||
out.append("")
|
||||
out.append(" \\textbullet{} ".join(tex_escape(c) for c in data["certifications"]))
|
||||
out.append("")
|
||||
out.append(r"\end{document}")
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
def main():
|
||||
with open(DATA, "r", encoding="utf-8") as f:
|
||||
data = yaml.safe_load(f)
|
||||
tex = render(data)
|
||||
with open(OUT, "w", encoding="utf-8") as f:
|
||||
f.write(tex)
|
||||
print(f"wrote {OUT}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
81
resume.yaml
Normal file
81
resume.yaml
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Single source of truth for the resume content.
|
||||
# Renderers read this and emit MikeEberlein_Resume.tex and MikeEberlein_Resume.odt.
|
||||
# Edit content here; layout/styling lives in the renderers.
|
||||
|
||||
header:
|
||||
name: MIKE EBERLEIN
|
||||
tagline: "Engineering Manager • Infotainment & Software-Defined Vehicles"
|
||||
email: mike.t.eberlein@gmail.com
|
||||
|
||||
summary: >-
|
||||
Engineering manager with 15+ years building market-leading automotive
|
||||
infotainment systems, spanning manual and automated test, UI development,
|
||||
embedded service development, program management, and people leadership.
|
||||
Effective at translating ambiguous platform goals into shippable software
|
||||
across Android, Yocto Linux, and QNX, and at growing teams that deliver on
|
||||
aggressive vehicle-program timing.
|
||||
|
||||
experience:
|
||||
- company: General Motors
|
||||
dates: Oct 2014 – Present
|
||||
roles:
|
||||
- title: Engineering Group Manager
|
||||
dates: May 2021 – Present
|
||||
subroles:
|
||||
- title: Infotainment Vehicle Data
|
||||
dates: 2023 – Present
|
||||
bullets:
|
||||
- Lead software engineers delivering the mechanisms that surface vehicle data from in-vehicle modules to applications and services across Android, Yocto Linux, and QNX.
|
||||
- title: Development Quality Assurance
|
||||
dates: 2021 – 2023
|
||||
bullets:
|
||||
- Led a cross-functional team of 20+ engineers driving functional test for infotainment platform development; established cross-component knowledge sharing and team planning practices.
|
||||
- title: Ultifi Platform & Vehicle Data
|
||||
dates: 2021 – 2023
|
||||
bullets:
|
||||
- Led development of core embedded components for GM's software-defined vehicle platform, partnering with architecture, systems, and downstream test to drive end-to-end delivery.
|
||||
- title: Business Manager
|
||||
dates: 2021 – 2023
|
||||
bullets:
|
||||
- Owned and defended the platform org budget; ran SOWs with multiple suppliers in partnership with procurement; built workload models and managed headcount allocation.
|
||||
|
||||
- title: Infotainment Software Program Manager
|
||||
dates: Jan 2017 – May 2021
|
||||
bullets:
|
||||
- Lead software program manager for MY22/MY23 Center Stack Module (CSM) development — owned software delivery, timing, and scope from program initiation through late vehicle milestones.
|
||||
- Drove impact analysis, feature commitment, and delivery tracking with partner teams across GM; managed Tier-X suppliers on deliveries, defects, and change negotiations.
|
||||
- "MY20–MY23 program execution commitment lead: scoped and estimated new features in concept phase, then tracked scope and staffing through development."
|
||||
- Built Python 3 / Excel / Rational Team Concert API tooling for task breakdown, inter-team dependencies, and historical-data-driven staffing models.
|
||||
|
||||
- title: Infotainment Software Engineer
|
||||
dates: Oct 2014 – Jan 2017
|
||||
bullets:
|
||||
- Developed Android (Java) infotainment apps including a hybrid powertrain optimization HMI and the USB / over-the-air software update client for the head unit.
|
||||
- Built Windows-based developer tooling with CAN simulation to enable powertrain teams to iterate against the infotainment system off-vehicle.
|
||||
|
||||
- company: Harman International
|
||||
dates: Oct 2008 – Oct 2014
|
||||
roles:
|
||||
- title: Software Engineer
|
||||
dates: Mar 2011 – Oct 2014
|
||||
bullets:
|
||||
- Designed and built navigation infotainment applications in ActionScript 2/3, Lua, and C++ on QNX-based head units shipped to multiple OEMs.
|
||||
- Managed the navigation engine supplier — deliveries, feature implementation, and defect resolution — and built Excel-based dashboards over the internal defect database for program-level reporting.
|
||||
|
||||
- title: Software Verification Engineer / UI Developer
|
||||
dates: Oct 2008 – Mar 2011
|
||||
bullets:
|
||||
- Converted Photoshop design files into ActionScript 2 UI components consumed by engineering teams.
|
||||
- Authored automated test tools using VB.NET, CAN, ACV-LAN, and proprietary trace tools; analyzed requirements, ran in-vehicle test drives, and validated infotainment systems with customers.
|
||||
|
||||
education:
|
||||
- school: B.S. Computer Science, Lawrence Technological University
|
||||
dates: Apr 2011
|
||||
bullets:
|
||||
- Graduated summa cum laude from the Honors College. Coursework in C, C++, Java, Visual Basic, mobile development, and scripting.
|
||||
- "Senior capstone: Simulink-based engine control module for the Formula SAE team. Math club member and math modeling competitor."
|
||||
|
||||
certifications:
|
||||
- Certified Scrum Professional (2012–2015)
|
||||
- Certified Scrum Master (2010–2019)
|
||||
- SAFe Agilist (2020–2023)
|
||||
Loading…
Reference in New Issue
Block a user