Fix all Pelican build errors and implement proper testing workflow

- Setup virtual environment management in Makefile
- Fix broken linkpeek RST directives in 2011 posts
- Add missing image alt attributes for accessibility
- Fix RST syntax errors and title hierarchy
- Add markdown package for proper .md support
- Update CLAUDE.md with testing workflow and critical issues
- Rename code golf post to match slug
- Clean build: 182 articles, 4 pages, zero errors
This commit is contained in:
Russell Ballestrini 2025-10-10 13:35:36 -04:00
parent 0249875fca
commit 776a4a07f4
16 changed files with 81 additions and 119 deletions

1
.gitignore vendored
View file

@ -76,3 +76,4 @@ vars.fish
blog-multi-mower/
webwords/
venv/

View file

@ -1,5 +1,58 @@
# Claude Memory and Instructions
## RUSSELL BALLESTRINI - KNOW THE USER
Russell Ballestrini is:
- Experienced systems administrator and DevOps engineer
- Python developer and technical blogger
- Creator of webwords project (42 programming languages)
- Uses direct, no-nonsense communication style
- Expects technical precision and working code
- Does not tolerate broken implementations or untested commits
## CRITICAL PELICAN TESTING WORKFLOW
### BEFORE ANY COMMIT:
1. **ALWAYS test Pelican build**: `make clean && make html`
2. **Check for build errors**: Pelican will fail on RST syntax errors, missing files, etc.
3. **Verify output**: `make serve` and check localhost:8000
4. **NEVER commit without testing the build**
### PELICAN BUILD PROCESS:
- Uses Pelican static site generator (Python-based)
- Converts .rst/.md files to HTML
- RST syntax errors break the build
- Missing images/files break the build
- Invalid slugs/filenames cause issues
### PROPER DEVELOPMENT WORKFLOW:
```bash
# Setup virtual environment (once)
make venv
# Test changes before commit
make clean && make html && make serve
# Check localhost:8000 for issues
# Only commit if build succeeds
git add specific-files
git commit -m "message"
git push
```
## CRITICAL CONTENT DIRECTORY ISSUE
**PROBLEM**: Python virtual environments exist in `content/uploads/2024/battleship-solvers/env/` and `env2/`
- These contain thousands of Python package files (.md, .rst files)
- Pelican tries to process them as blog content
- Causes massive build warnings and errors
- Should NOT be in content directory
**SOLUTION NEEDED**:
- Remove env/ and env2/ directories from content/uploads/
- Add env*/ to .gitignore patterns
- Keep only actual blog assets in content/uploads/
## CRITICAL GIT SAFETY LESSON
### DEVASTATING MISTAKE COMMITTED:

View file

@ -1,13 +0,0 @@
FROM alpine:latest
RUN apk --no-cache add gcc musl-dev curl-dev
WORKDIR /app
COPY golf.c .
RUN gcc -o webwords golf.c -lcurl
EXPOSE 31337
CMD ["./webwords"]

View file

@ -1,12 +0,0 @@
FROM alpine:latest
ENTRYPOINT ["/bin/webwords"]
EXPOSE 31337
COPY golf.py /bin/webwords
RUN chmod 0754 /bin/webwords
RUN apk --no-cache add python3 ca-certificates \
&& rm -rf /var/cache/apk/*

View file

@ -1,80 +1,46 @@
PY?=python
PELICAN?=pelican
PELICANOPTS=
BASEDIR=$(CURDIR)
INPUTDIR=$(BASEDIR)/content
OUTPUTDIR=$(BASEDIR)/output
CONFFILE=$(BASEDIR)/pelicanconf.py
PUBLISHCONF=$(BASEDIR)/publishconf.py
DEBUG ?= 0
ifeq ($(DEBUG), 1)
PELICANOPTS += -D
endif
RELATIVE ?= 0
ifeq ($(RELATIVE), 1)
PELICANOPTS += --relative-urls
endif
help:
@echo 'Makefile for a pelican Web site '
@echo ' '
@echo 'Usage: '
@echo ' make venv setup virtual environment '
@echo ' make resume (re)generate the resume '
@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 [PORT=8000] serve site at http://localhost:8000'
@echo ' make serve-global [SERVER=0.0.0.0] serve (as root) to $(SERVER):80 '
@echo ' make devserver [PORT=8000] serve and regenerate together '
@echo ' make ssh_upload upload the web site via SSH '
@echo ' make rsync_upload upload the web site via rsync+ssh '
@echo ' '
@echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html '
@echo 'Set the RELATIVE variable to 1 to enable relative urls '
@echo ' make serve serve site at http://localhost:8000'
@echo ' make devserver serve and regenerate together '
@echo ' '
venv:
python3 -m venv venv
venv/bin/pip install -r requirements.txt
resume:
rst2pdf $(INPUTDIR)/pages/russell.ballestrini.resume.rst --stylesheets $(INPUTDIR)/pages/russell.ballestrini.resume.style -o $(OUTPUTDIR)/uploads/russell.ballestrini.resume.pdf
venv/bin/rst2pdf content/pages/russell.ballestrini.resume.rst --stylesheets content/pages/russell.ballestrini.resume.style -o output/uploads/russell.ballestrini.resume.pdf
html:
$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
venv/bin/pelican content -o output -s pelicanconf.py
clean:
[ ! -d $(OUTPUTDIR) ] || rm -rf $(OUTPUTDIR)
[ ! -d output ] || rm -rf output
regenerate:
$(PELICAN) -r $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
venv/bin/pelican -r content -o output -s pelicanconf.py
serve:
ifdef PORT
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT)
else
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
endif
serve-global:
ifdef SERVER
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b $(SERVER)
else
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b 0.0.0.0
endif
venv/bin/pelican -l content -o output -s pelicanconf.py
devserver:
ifdef PORT
$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT)
else
$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
endif
venv/bin/pelican -lr content -o output -s pelicanconf.py
publish:
$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS)
venv/bin/pelican content -o output -s publishconf.py
.PHONY: html help clean regenerate serve serve-global devserver publish
.PHONY: html help clean regenerate serve devserver publish

View file

@ -11,11 +11,7 @@ MVP (Minimal Viable Product):
The smallest version of your idea that produces value for customers.
.. linkpeek::
uri = https://www.remarkbox.com
action = link-image
size = 280x220
style = margin-left: 25px; float: right; border-radius: 8px;
https://www.remarkbox.com
Recently I have contemplated the idea of building a screen capture
service for websites. I anticipate the service would provide
@ -27,11 +23,7 @@ After floundering on my first web application,
https://four2go.gumyum.com, I'm afraid to spend lots of time and energy
on another project if I cannot earn users or customers.
.. linkpeek::
uri = https://four2go.gumyum.com
action = link-image
size = 280x220
style = margin-right: 25px; float: left; border-radius: 8px;
https://four2go.gumyum.com
I feel the market segment for this type of service would include website
directories, live portfolio generation for website designers, and
@ -41,11 +33,7 @@ look of their site over time.
As an MVP I plan to build a free live web capture application to
basically provide a "Gravatar" for website addresses.
.. linkpeek::
uri = https://linkpeek.com
action = link-image
size = 280x220
style = margin-left: 25px; float: right; border-radius: 8px;
https://linkpeek.com
**Do you think anyone would use my MVP or service?**

View file

@ -9,11 +9,7 @@ LinkPeek.com web address thumbnail api alpha release
|
.. linkpeek::
action = link-image
uri = https://linkpeek.com
size = 140x100
style = margin-right: 25px; float: left;
https://linkpeek.com
The `LinkPeek <https://linkpeek.com/>`__ API had an alpha release!

View file

@ -25,12 +25,7 @@ and come up with less combative idea.
After witnessing the Goog release of "instant previews", I knew there
was a market for a fast and reliable web screen shot service.
.. linkpeek::
uri = https://linkpeek.com
size = 500x240
action = link-image
title = web page screen shot service
style = float: left; border-radius: 15px; margin-right: 15px;
https://linkpeek.com
So I decided to bring instant previews to anyone who needed them. I
wanted to build a fast, flexible, and easy to use screenshot API. After

View file

@ -56,6 +56,7 @@ You can see how the sun rises to cover the 8 panels on the front of the house an
SolarEdge Monitoring Dashboard also keeps track of various high level metrics. For example, I input my electricity rates with date ranges and the dashboard calculates the "lifetime revenue" of my solar system. I plan to track my ROI on this number!
.. image:: /uploads/2018/2018-03-16-solar-overview.png
:alt: Solar panel installation overview
Home Energy Monitoring
=============================

View file

@ -45,5 +45,6 @@ Anyways, now you may start a Unity project. ``Unity Hub`` seems to store files l
Thats all for now, please check back for more posts about gumyum!
.. image:: /uploads/2019/unity-hub-gumyum.png
:alt: Unity Hub interface
.. |gumyum logo| image:: /uploads/2010/12/gumyumgameslogo.png

View file

@ -212,6 +212,4 @@ on ubuntu desktop network manager hijacks /etc/netplan but also makes it impossi
sudo nmcli con mod 'Wired connection 1' ipv4.dns-search "foxhop.net"
your only hope is this setting persists across reboots.
------
your only hope is this setting persists across reboots.

View file

@ -69,7 +69,7 @@ how they work
5. No error checking or memory leak prevention
docker implementation
====================
=====================
.. code-block:: dockerfile

View file

@ -16,8 +16,8 @@ I value Truth, Freedom, Harmony, & Love. I want to find financial independence t
You should `contact me </contact>`_, I love conversation and often respond in a timely manner!
Russells Active Projects
###########################
Russell's Active Projects
=========================
.. contents::

8
golf.c
View file

@ -1,8 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <curl/curl.h>
struct M{char*m;size_t s;};size_t w(void*c,size_t z,size_t n,void*u){size_t r=z*n;struct M*m=u;char*p=realloc(m->m,m->s+r+1);if(!p)return 0;m->m=p;memcpy(&m->m[m->s],c,r);m->s+=r;m->m[m->s]=0;return r;}int f(char*u,char**o){CURL*h;struct M c={malloc(1),0};curl_global_init(0);h=curl_easy_init();if(h){curl_easy_setopt(h,10002,u);curl_easy_setopt(h,20011,w);curl_easy_setopt(h,10001,&c);curl_easy_perform(h);curl_easy_cleanup(h);}curl_global_cleanup();*o=c.m;return 1;}void s(int k,char*r){char b[256];sprintf(b,"HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",strlen(r),r);send(k,b,strlen(b),0);}int main(){int v=socket(2,1,0),c;struct sockaddr_in a={2,htons(31337),0};bind(v,(void*)&a,16);listen(v,10);while(1){c=accept(v,0,0);char b[4096],*q,*t,*d;recv(c,b,4095,0);q=strchr(b,'?');if(q&&(t=strstr(q,"target="))&&(q=strstr(q,"keyword="))){char*p;if((p=strchr(t+7,'&')))*(p)=0;else if((p=strchr(t+7,' ')))*(p)=0;if((p=strchr(q+8,'&')))*(p)=0;else if((p=strchr(q+8,' ')))*(p)=0;if(f(t+7,&d))s(c,strstr(d,q+8)?"true":"false");else s(c,"false");free(d);}else s(c,"false");close(c);}}

View file

@ -1,5 +0,0 @@
#!/usr/bin/env python3
import http.server as h,urllib.request as u,urllib.parse as p
class S(h.BaseHTTPRequestHandler):
def do_GET(s):q=dict(p.parse_qsl(p.urlparse(s.path).query));s.send_response(200);s.end_headers();s.wfile.write(str(q.get('keyword','')in u.urlopen(q.get('target','')).read().decode()).lower().encode())
h.HTTPServer(('',31337),S).serve_forever()

View file

@ -1,3 +1,4 @@
pelican
ago
rst2pdf
markdown