Fast, content-focused personal site with automated external blog aggregation, zero-JS architecture, and professional SEO optimization built with Astro.
Status: Production β’ Live at andywoods.me
This is a repo for my personal website: andywoods.me. It's also an open-source repo for other professionals to build their own blogs.
Problem: Tech professionals publish across multiple platforms (personal blog, company blog, publications), creating fragmented portfolios and duplicate content penalties.
Solution: Automated content aggregation system that pulls from multiple sources while maintaining attribution, SEO optimization, and sub-50ms page loads.
Key Benefits:
- Unified portfolio without duplicate content penalties
- Static-first architecture: <$5/month hosting, 50ms CDN response times
- Automated external content scraping with security sanitization
- Zero runtime JavaScript for maximum performance
# Clone and install
git clone https://github.com/awoods187/andy-website.git
cd andy-website
npm install
# Start development server
npm run dev
# β http://localhost:4321Next steps: See Writing Blog Posts or Deployment
| Tool | Version | Verification |
|---|---|---|
| Node.js | 18+ | node --version |
| npm | 9+ | npm --version |
| Python | 3.10+ (optional) | python3 --version |
Note: Python only required for automated blog scraping.
| Category | Technology | Purpose |
|---|---|---|
| Framework | Astro 5 | Zero-JS static site generation |
| Styling | Tailwind CSS 4 | Utility-first CSS framework |
| Validation | Zod | Type-safe content schema |
| Testing | Vitest | Build output validation |
| Scraping | Python + BeautifulSoup | External content aggregation |
| Deployment | Vercel/Netlify | CDN hosting |
Create src/content/blog/my-post.md:
---
title: 'Your Post Title'
date: 2024-10-21
excerpt: 'Brief 1-2 sentence summary for SEO'
tags: ['ai', 'databases']
image: '/images/my-post.jpg' # Optional
draft: false
---
Your Markdown content here...See: Full Writing Guide for frontmatter schema and external post integration.
| Command | Purpose |
|---|---|
npm run dev |
Start dev server (localhost:4321) |
npm run build |
Production build β ./dist/ |
npm test |
Run test suite |
npm run preview |
Preview production build |
Static-First Pipeline:
- Personal posts (Markdown) + External posts (Python scraper) β TypeScript data
- Zod validation at build time
- Astro generates static HTML
- Deploy to CDN (Vercel/Netlify)
Key Trade-offs:
- β Sub-50ms response times, $5/month hosting, zero security vulnerabilities
- β Content updates require rebuild (acceptable for weekly publishing)
Security: HTML sanitization via bleach, CSP headers, no inline scripts.
See Security for details.
π Project Structure
andy-website/
βββ src/ # Source code
β βββ components/ # Reusable Astro components
β βββ content/
β β βββ blog/ # Personal Markdown posts
β β βββ config.ts # Zod content schema
β βββ data/
β β βββ crl-posts.ts # External blog data
β βββ layouts/ # Base page templates
β βββ pages/ # Route pages
β βββ styles/ # Global CSS + Tailwind
βββ docs/ # Project documentation
β βββ setup/ # Setup guides (Buttondown, CI, etc.)
β βββ licenses/ # License details
β βββ CONTENT-GUIDE.md
β βββ DEPLOYMENT.md
β βββ MAINTENANCE.md
β βββ TROUBLESHOOTING.md
β βββ VISUAL_STYLE_GUIDE.md
βββ config/ # Deployment configuration
β βββ vercel.json
βββ scripts/ # Utility scripts
β βββ scrape-crl-posts.py
βββ tests/ # Test files
βββ public/ # Static assets
βββ reports/ # Generated reports (gitignored)
β βββ lighthouse/
βββ astro.config.mjs # Astro configuration
βββ eslint.config.js # ESLint configuration
βββ tsconfig.json # TypeScript configuration
βββ vitest.config.ts # Vitest configuration
βββ package.json # Dependencies and scripts
- Create file in
src/content/blog/ - Add required frontmatter:
title,date,excerpt,tags - Write Markdown content
- Preview with
npm run dev
Manual (1-2 posts): Edit src/data/crl-posts.ts and add entry to array.
Automated (bulk updates):
python3 scripts/scrape-crl-posts.py
# β Generates updated src/data/crl-posts.tsSee: Lines 217-254 in original README for detailed schema.
- Push to GitHub:
git push origin main - Import repository at vercel.com
- Auto-detects Astro configuration
- Deploy β Live at
<project>.vercel.app
Custom Domain: Add DNS A record to 76.76.21.21, CNAME www β
cname.vercel-dns.com
Build command: npm run build β’ Publish directory: dist
See: Lines 350-439 in original README for full DNS setup.
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportCurrent Coverage: 349/349 tests passing across 17 test suites
- Build output validation (30 pages)
- Content schema validation
- RSS feed generation
- Open Graph meta tags validation
- Security headers and CSP
- Accessibility (WCAG compliance)
- Blog formatting consistency
- Dependency health checks
Security Measures:
- HTML sanitization via Python
bleachlibrary (strips<script>, event handlers, tracking pixels) - Strict CSP headers via
vercel.json(X-Frame-Options: DENY, HSTS) - No hardcoded secrets (
.env+.gitignore) - Static output eliminates SQL injection, auth vulnerabilities
Trade-off: CSP includes 'unsafe-inline' for Astro scoped styles
(acceptable for static site with no user-generated content).
Vulnerability Reporting: Contact via LinkedIn
Production Metrics (Lighthouse Mobile, Slow 4G):
- Performance: 89/100
- Accessibility: 95/100
- Best Practices: 100/100 β
- SEO: 100/100 β
Core Web Vitals:
- FCP: 1.1s β’ LCP: 3.7s β’ TBT: 0ms β’ CLS: 0.061
Bundle Sizes:
- HTML: 9-20KB per page
- CSS: 4KB gzipped (shared)
- JS: 0KB for blog pages
Scalability: Static CDN architecture handles 10K+ requests/sec. Not suitable for real-time features or search (requires JS or external service).
- Owner: Andy Woods β’ andrewscottwoods@gmail.com
- Issues: GitHub Issues
- Contact: LinkedIn | X/Twitter
- Last Updated: 2025-01-21
- Update Frequency: Bi-weekly (content), quarterly (dependencies)
- EOL Date: None planned
Maintenance SLA: Security patches within 48 hours, dependency updates quarterly, content updates as needed.
| Change | Location |
|---|---|
| Home bio | src/pages/index.astro |
| Profile photo | public/profile.jpg |
| Navigation | src/components/Header.astro |
| Colors/fonts | src/styles/global.css |
| Analytics | src/components/Analytics.astro |
Visual Branding: See VISUAL_STYLE_GUIDE.md for WPA poster aesthetic
guidelines and image generation prompts.
Dual License Structure:
- Code (MIT): Free to use, modify, redistribute commercially
- Content (CC BY-NC 4.0): Quote with attribution, non-commercial use
Special Exception: AI training permitted on all content (including commercial models).
See: LICENSE.md for full details β’ External Cockroach Labs posts retain
original Β© Cockroach Labs.
- Visual Style Guide:
VISUAL_STYLE_GUIDE.md- Brand-consistent image generation - Code Review:
CODE_REVIEW.md- Security audit summary - Environment Setup:
.env.example- Configuration template - AI Training Policy:
README-AI-POLICY.md- Explicit AI permissions
External Resources:
- Astro: docs.astro.build
- Tailwind: tailwindcss.com/docs
- Design inspiration: tomtunguz.com
Andy Woods β’ Director of Product Management at Cockroach Labs
Built with Astro and Claude Code in under 4 hours using AI-native development practices.