.PHONY: html help clean regenerate serve devserver publish pygments jsonresume jsonresume-themes jsonresume-pdf rst2md llms jsonfeed jsonblog plaintext formats training-data BASEDIR=$(CURDIR) INPUTDIR=$(BASEDIR)/content OUTPUTDIR=$(BASEDIR)/output CONFFILE=$(BASEDIR)/pelicanconf.py PUBLISHCONF=$(BASEDIR)/publishconf.py help: @echo 'Makefile for a pelican Web site ' @echo ' ' @echo 'Usage: ' @echo ' make venv setup virtual environment ' @echo ' make resume (re)generate the resume PDF ' @echo ' make jsonresume copy JSONResume to output ' @echo ' make jsonresume-themes install 30 jsonresume themes ' @echo ' make jsonresume-pdf generate PDFs for favorite themes ' @echo ' make cover-letters (re)generate cover letter PDFs ' @echo ' make html (re)generate the web site ' @echo ' make clean remove the generated files ' @echo ' make regenerate regenerate files upon modification ' @echo ' make publish generate using production settings ' @echo ' make serve serve site at http://localhost:8000' @echo ' make devserver serve and regenerate together ' @echo ' make pygments generate pygments stylesheets ' @echo ' make rst2md convert RST to MD in output ' @echo ' make llms generate llms.txt for AI agents ' @echo ' make jsonfeed generate JSON Feed (/feeds/all.json)' @echo ' make jsonblog generate JSON Blog (/blog.json) ' @echo ' make plaintext generate plain text (index.txt) ' @echo ' make formats generate all agent-friendly formats' @echo ' ' THEME_REPO_URL ?= git@github.com:russellballestrini/pelican-svbhack.git venv: python3 -m venv venv venv/bin/pip install -r requirements.txt mkdir -p pelican-themes cd pelican-themes && git clone $(THEME_REPO_URL) venv/bin/pelican-themes -i pelican-themes/pelican-svbhack/ resume: jsonresume venv/bin/rst2pdf content/pages/russell.ballestrini.resume.rst --stylesheets content/pages/russell.ballestrini.resume.style -o output/uploads/russell.ballestrini.resume.pdf @echo "Creating softlinks for various job titles..." @cd output/uploads && ln -sf russell.ballestrini.resume.pdf founder.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf founding.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf software.developer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf software.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf senior.software.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf staff.software.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf principal.software.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf full.stack.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf site.reliability.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf sre.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf devops.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf backend.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf platform.engineer.russell.ballestrini.resume.pdf @cd output/uploads && ln -sf russell.ballestrini.resume.pdf infrastructure.engineer.russell.ballestrini.resume.pdf @echo "Resume softlinks created!" jsonresume: @mkdir -p output/uploads cp content/pages/russell.ballestrini.resume.json output/uploads/russell.ballestrini.resume.json cp content/pages/unturf.resume.json output/uploads/unturf.resume.json @echo "JSONResume files copied to output/uploads/" # JSONResume themes - 30 tested working themes JSONRESUME_THEMES = classy cora dark-classy-responsive elegant eloquent even kards kendall \ light-classy-responsive lucide modern modern-extended modern-plain msresume nameme one \ onepage-plus printclassy projects rnord rocketspacer sceptile simplyelegant stackoverflow \ stackoverflow-ru straightforward tan-responsive techlead timeline-fixed waterfall RESUME_FILE ?= content/pages/russell.ballestrini.resume.json jsonresume-themes: @echo "Installing 30 jsonresume themes..." npm install $(addprefix jsonresume-theme-,$(JSONRESUME_THEMES)) puppeteer --legacy-peer-deps @echo "Done! Run: make jsonresume-pdf" # Export JSONResume as HTML in all themes jsonresume-all-html: @mkdir -p output/uploads/jsonresume-themes @echo "Generating HTML for all themes..." @for theme in $(JSONRESUME_THEMES); do \ echo " Generating $$theme..."; \ node -e "try { \ const t = require('jsonresume-theme-$$theme'); \ const r = require('./$(RESUME_FILE)'); \ require('fs').writeFileSync('output/uploads/jsonresume-themes/russell.ballestrini.$$theme.html', t.render(r)); \ console.log(' OK'); \ } catch(e) { console.log(' Failed:', e.message.split('\n')[0]); }" 2>/dev/null || echo " Failed: $$theme"; \ done @echo "HTML files saved to output/uploads/jsonresume-themes/" @echo "Open in browser & print to PDF" # Favorite JSONResume themes for PDF export JSONRESUME_PDF_THEMES = kendall modern-plain one onepage-plus # Generate PDFs for favorite themes using puppeteer jsonresume-pdf: jsonresume-all-html @mkdir -p output/uploads @echo "Generating PDFs for favorite themes..." @node -e " \ const puppeteer = require('puppeteer'); \ const themes = '$(JSONRESUME_PDF_THEMES)'.split(' '); \ (async () => { \ const browser = await puppeteer.launch({headless: 'new'}); \ for (const theme of themes) { \ const page = await browser.newPage(); \ await page.goto('file://$(CURDIR)/output/uploads/jsonresume-themes/russell.ballestrini.' + theme + '.html', {waitUntil: 'networkidle0'}); \ await page.pdf({path: '$(CURDIR)/output/uploads/russell.ballestrini.' + theme + '.pdf', format: 'A4'}); \ console.log(' Created: russell.ballestrini.' + theme + '.pdf'); \ await page.close(); \ } \ await browser.close(); \ })(); \ " @echo "PDFs saved to output/uploads/" cover-letter: @if [ -z "$(FILE)" ]; then \ echo "Usage: make cover-letter FILE=filename-without-extension"; \ echo "Example: make cover-letter FILE=2025-11-29-runpod-principal-software-engineer-storage-systems-russell-ballestrini"; \ exit 1; \ fi @mkdir -p output/uploads/cover-letters @echo "Generating PDF for $(FILE)..." @venv/bin/rst2pdf content/pages/cover-letters/$(FILE).rst --stylesheets content/pages/cover-letters/cover-letter.style -o output/uploads/cover-letters/$(FILE).pdf @echo "PDF generated: output/uploads/cover-letters/$(FILE).pdf" cover-letters: @mkdir -p output/uploads/cover-letters @for letter in content/pages/cover-letters/*.rst; do \ basename=$$(basename $$letter .rst); \ echo "Generating PDF for $$basename..."; \ venv/bin/rst2pdf $$letter --stylesheets content/pages/cover-letters/cover-letter.style -o output/uploads/cover-letters/$$basename.pdf; \ done @echo "All cover letters generated in output/uploads/cover-letters/" html: pygments venv/bin/pelican content -o output -s pelicanconf.py clean: [ ! -d output ] || rm -rf output regenerate: venv/bin/pelican -r content -o output -s pelicanconf.py serve: venv/bin/pelican -l content -o output -s pelicanconf.py devserver: venv/bin/pelican -lr content -o output -s pelicanconf.py publish: venv/bin/pelican content -o output -s publishconf.py pygments: @echo "Generating pygments stylesheets..." venv/bin/python -c "from pygments.formatters import HtmlFormatter; print(HtmlFormatter(style='monokai').get_style_defs('.highlight'))" > pelican-themes/pelican-svbhack/static/css/pygments-dark.css venv/bin/python -c "from pygments.formatters import HtmlFormatter; print(HtmlFormatter(style='default').get_style_defs('.highlight'))" > pelican-themes/pelican-svbhack/static/css/pygments-light.css @echo "Patching to remove hardcoded backgrounds..." sed -i 's/.highlight { background: #272822; color: #f8f8f2 }/.highlight { color: #f8f8f2 }/' pelican-themes/pelican-svbhack/static/css/pygments-dark.css sed -i 's/.highlight { background: #f8f8f8; }/.highlight { }/' pelican-themes/pelican-svbhack/static/css/pygments-light.css @echo "Done! Pygments stylesheets generated and patched." rst2md: @echo "Converting RST to Markdown in output directory..." venv/bin/python lib/rst2md.py $(OUTPUTDIR) @echo "Done! Markdown files generated alongside RST sources." llms: @echo "Generating llms.txt for AI agents..." venv/bin/python lib/generate_llms_txt.py $(OUTPUTDIR) $(SITEURL) @echo "Done! llms.txt generated in output directory." jsonfeed: @echo "Generating JSON Feed..." venv/bin/python lib/generate_jsonfeed.py $(OUTPUTDIR) $(SITEURL) @echo "Done! JSON Feed generated at /feeds/all.json" jsonblog: @echo "Generating JSON Blog..." venv/bin/python lib/generate_jsonblog.py $(OUTPUTDIR) $(SITEURL) @echo "Done! JSON Blog generated at /blog.json" plaintext: @echo "Generating plain text versions..." venv/bin/python lib/generate_plaintext.py $(OUTPUTDIR) @echo "Done! Plain text files generated (index.txt)." formats: rst2md plaintext jsonfeed jsonblog llms @echo "All agent-friendly formats generated!" TRAINING_DATA_OUT ?= $(BASEDIR)/russell.ballestrini.net.txt training-data: ## Convert all RST posts to plain text for ML training @echo "converting $(words $(wildcard $(INPUTDIR)/*.rst)) RST posts to plain text via pandoc..." @> $(TRAINING_DATA_OUT) @count=0; \ for rst in $(INPUTDIR)/*.rst; do \ pandoc -f rst -t plain --wrap=none "$$rst" >> $(TRAINING_DATA_OUT) 2>/dev/null; \ echo "" >> $(TRAINING_DATA_OUT); \ count=$$((count + 1)); \ done; \ echo "russell.ballestrini.net: $$count posts, $$(wc -l < $(TRAINING_DATA_OUT)) lines" SITEURL ?= https://russell.ballestrini.net