Baseline resume from 5/10/2026

This commit is contained in:
Mike Eberlein 2026-05-10 14:45:28 -04:00
commit 868eb5a77e
15 changed files with 724 additions and 0 deletions

42
Makefile Normal file
View 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

Binary file not shown.

BIN
MikeEberlein_Resume.odt Normal file

Binary file not shown.

BIN
MikeEberlein_Resume.pdf Normal file

Binary file not shown.

146
MikeEberlein_Resume.tex Normal file
View 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 MY20MY23 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 (20122015) \textbullet{} Certified Scrum Master (20102019) \textbullet{} SAFe Agilist (20202023)
\end{document}

68
README.md Normal file
View 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

Binary file not shown.

BIN
fonts/Roboto-BoldItalic.ttf Normal file

Binary file not shown.

BIN
fonts/Roboto-Italic.ttf Normal file

Binary file not shown.

BIN
fonts/Roboto-Light.ttf Normal file

Binary file not shown.

BIN
fonts/Roboto-Medium.ttf Normal file

Binary file not shown.

BIN
fonts/Roboto-Regular.ttf Normal file

Binary file not shown.

219
render_odt.py Normal file
View 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="&apos;Roboto Light&apos;"/>
</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 = {"&": "&amp;", "<": "&lt;", ">": "&gt;"}
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"])} &#8226; {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", " &#8226; ".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
View 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
View 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.
- "MY20MY23 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 (20122015)
- Certified Scrum Master (20102019)
- SAFe Agilist (20202023)