Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dc04a53
Scaffold Astro 7 project alongside the old site
shell Oct 12, 2026
70b2f4f
Add YAML, date, grouping and inline Markdown helpers
shell Oct 12, 2026
00f18cb
Add content collections, schemas, résumé from CV and seed content
shell Oct 12, 2026
bf80f51
Add design tokens, base layout, theme toggle and homepage
shell Oct 12, 2026
0223e67
Add about, contact and 404 pages
shell Oct 12, 2026
bfcd828
Add résumé page from CV data
shell Oct 12, 2026
a0a18fe
Add life timeline, books and links pages
shell Oct 12, 2026
a79d775
Generate Open Graph images per page
shell Oct 12, 2026
5cc6a46
Add link check, nginx container image and CI
shell Oct 12, 2026
6801892
Fix final review findings: content-agnostic dist tests, 404 meta, str…
shell Oct 12, 2026
75f3540
Content edits: homepage heading, About extras, résumé title
shell Oct 13, 2026
1b10d8c
Add photo band behind page headers
shell Oct 13, 2026
4927410
Rebuild homepage: photo hero, subtitle, chips, intro, section cards
shell Oct 13, 2026
d69e204
Life: single timeline coloured by decade, merged milestones
shell Oct 13, 2026
4b2f9ca
Links: cards with notes and domains
shell Oct 13, 2026
e9033b6
Books: ISBN field and cover shelves with fallback tiles
shell Oct 13, 2026
3292548
Add npm run covers to fetch Open Library book covers
shell Oct 13, 2026
f983260
Mark About story placeholder; visual check
shell Oct 13, 2026
0d9d7d8
Harden cover script; print styles and small a11y fixes
shell Oct 13, 2026
cff48bc
About sidebar layout; homepage, nav and width polish
shell Oct 13, 2026
ec54d30
Read Books from Notion with covers; Life edits; consistent page spacing
shell Oct 13, 2026
f1a57de
Testimonials page from LinkedIn; résumé header refresh
shell Oct 13, 2026
0908267
Contact rows, hello@penk.in, email obfuscation
shell Oct 13, 2026
4153125
404: big 404 over the ascii.rest command city
shell Oct 13, 2026
ede659a
404: larger city on wide screens
shell Oct 13, 2026
ade88a8
Links: 40 sites in compact cards; Life and homepage touches
shell Oct 13, 2026
84afb2e
Ignore docs/ (planning notes stay local)
shell Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
node_modules
dist
.astro
.git
.superpowers
docs
css
js
images
index.html
index-old.html
about.html
screen.css
favicon.png
vladimir.asc
48 changes: 48 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: CI

on:
pull_request:
push:
branches: [master]

permissions:
contents: read
packages: write

jobs:
image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
if: github.event_name == 'push'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# The Dockerfile's build stage runs unit tests, astro check, the build,
# built-output tests and the link check, so a failing check means no image.
- name: Build (and push on master)
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name == 'push' }}
tags: |
ghcr.io/shell/penk.in:${{ github.sha }}
ghcr.io/shell/penk.in:latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Trigger deploy in shell/infra
if: github.event_name == 'push'
env:
GH_TOKEN: ${{ secrets.INFRA_DISPATCH_TOKEN }}
run: |
gh api repos/shell/infra/dispatches \
-f event_type=deploy-penk-in \
-f "client_payload[sha]=${{ github.sha }}"
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
.DS_Store
.DS_Store
node_modules/
/dist/
.astro/
.superpowers/
docs/
Empty file added .nojekyll
Empty file.
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Stage 1: test and build the static site
FROM node:24-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm test

# Stage 2: serve dist/ with nginx
FROM nginx:stable-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD wget -q --spider http://127.0.0.1/ || exit 1
9 changes: 9 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
site: 'https://penk.in',
trailingSlash: 'never',
build: { format: 'file' },
integrations: [sitemap({ filter: (page) => !page.endsWith('/404') })],
});
28 changes: 28 additions & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
absolute_redirect off;
server_tokens off;

gzip on;
gzip_types text/css application/javascript application/xml image/svg+xml text/plain;

error_page 404 /404.html;

# Email links point here so the address never appears in the HTML (anti-harvesting).
location = /email {
add_header X-Robots-Tag "noindex, nofollow";
return 302 "mailto:hello@penk.in";
}

location /_astro/ {
add_header Cache-Control "public, max-age=31536000, immutable";
try_files $uri =404;
}

location / {
add_header Cache-Control "public, max-age=300";
try_files $uri $uri.html $uri/index.html =404;
}
}
Loading
Loading