feat: docker
Some checks failed
Build and publish Docker image / build (pull_request) Has been cancelled
Build and publish Docker image / docker (pull_request) Has been cancelled

This commit is contained in:
Matthew McPeak 2026-06-10 17:03:17 -04:00
parent 015e608f7c
commit 55e585186e
Signed by: McPeakML
GPG key ID: 3D64A2E70F58D07C
6 changed files with 174 additions and 55 deletions

8
.dockerignore Normal file
View file

@ -0,0 +1,8 @@
node_modules
dist-ssr
coverage
test-results
playwright-report
.git
.vscode
*.log

View file

@ -0,0 +1,67 @@
name: Build and publish Docker image
on:
push:
tags:
- 'v*'
pull_request:
jobs:
build:
runs-on: docker
container: oven/bun:1
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Audit dependencies
run: bun audit
- name: Run unit tests
run: bun run test:unit run
- name: Build
run: bun run build
- name: Upload dist
uses: actions/upload-artifact@v4
with:
name: dist
path: dist
docker:
needs: build
if: startsWith(github.ref, 'refs/tags/v')
runs-on: docker
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download dist
uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: git.mcpeakdev.com
username: ${{ github.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Extract image tag
id: meta
run: echo "tag=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
git.mcpeakdev.com/mcpeakml/portfolio:${{ steps.meta.outputs.tag }}
git.mcpeakdev.com/mcpeakml/portfolio:latest

7
Dockerfile Normal file
View file

@ -0,0 +1,7 @@
# syntax=docker/dockerfile:1
FROM nginx:1-alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

View file

@ -71,3 +71,28 @@ bun test:e2e --debug
```sh
bun lint
```
## Docker
The Dockerfile serves a pre-built `dist/` directory via nginx, so build the
app first:
```sh
bun run build
docker build -t portfolio .
docker run --rm -p 8080:80 portfolio
```
The site will be available at http://localhost:8080.
### CI publishing
A Forgejo Actions workflow (`.forgejo/workflows/docker-publish.yml`) builds the
image and pushes it to the Forgejo container registry whenever a `v*` tag is
pushed (e.g. `v1.2.3`):
- `git.mcpeakdev.com/mcpeakml/portfolio:<version>`
- `git.mcpeakdev.com/mcpeakml/portfolio:latest`
This requires a `REGISTRY_TOKEN` repository secret containing a token with
`write:package` permission for the registry.

10
nginx.conf Normal file
View file

@ -0,0 +1,10 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}

View file

@ -1,83 +1,85 @@
export interface Stat {
value: string
label: string
color: string
value: string;
label: string;
color: string;
}
export interface Project {
id: string
title: string
color: 'primary' | 'info' | 'success' | 'warn'
description: string
tags: string[]
link: string
id: string;
title: string;
color: "primary" | "info" | "success" | "warn";
description: string;
tags: string[];
link: string;
}
export interface PortfolioData {
name: string
role: string
tagline: string
status: string
about: string
stats: Stat[]
projects: Project[]
skills: Record<string, string[]>
name: string;
role: string;
tagline: string;
status: string;
about: string;
stats: Stat[];
projects: Project[];
skills: Record<string, string[]>;
social: {
github: string
linkedin: string
email: string
}
github: string;
linkedin: string;
email: string;
};
}
// Edit this block to make the portfolio yours.
export const portfolioData: PortfolioData = {
name: 'Alexander Voss',
role: 'Product Designer · Developer',
tagline: 'I craft digital experiences that feel inevitable — clear, classical, enduring.',
status: 'Open for opportunities · 2026',
name: "Matthew McPeak",
role: "Product Designer · Developer",
tagline: "I craft digital experiences that feel inevitable — clear, classical, enduring.",
status: "Open for opportunities · 2026",
about:
'Based in Athens. I work at the intersection of design and engineering, building systems and products that are precise, durable, and a pleasure to use. Ten years in, I still believe the best interface is the one that gets out of the way.',
"Based in Athens. I work at the intersection of design and engineering, building systems and products that are precise, durable, and a pleasure to use. Ten years in, I still believe the best interface is the one that gets out of the way.",
stats: [
{ value: '10+', label: 'Years of craft', color: 'var(--primary)' },
{ value: '40+', label: 'Projects shipped', color: 'var(--info)' },
{ value: '12', label: 'Satisfied patrons', color: 'var(--success)' },
{ value: "10+", label: "Years of craft", color: "var(--primary)" },
{ value: "40+", label: "Projects shipped", color: "var(--info)" },
{ value: "12", label: "Satisfied patrons", color: "var(--success)" },
],
projects: [
{
id: 'p1',
title: 'Nychthemeron',
color: 'primary',
id: "p1",
title: "Nychthemeron",
color: "primary",
description:
'A dual-theme design system — Hades (dark) and Apollo (light) — for classical, lasting interfaces.',
tags: ['Design System', 'Vue', 'CSS'],
link: '#',
"A dual-theme design system — Hades (dark) and Apollo (light) — for classical, lasting interfaces.",
tags: ["Design System", "Vue", "CSS"],
link: "#",
},
{
id: 'p2',
title: 'The Oracle',
color: 'info',
description: 'A mythology-themed admin interface with consultations, petitioners, and prophecy tracking.',
tags: ['UI Kit', 'Dashboard', 'TypeScript'],
link: '#',
id: "p2",
title: "The Oracle",
color: "info",
description:
"A mythology-themed admin interface with consultations, petitioners, and prophecy tracking.",
tags: ["UI Kit", "Dashboard", "TypeScript"],
link: "#",
},
{
id: 'p3',
title: 'Agora',
color: 'success',
description: 'A real-time marketplace for artisans, connecting makers with patrons across the ancient world.',
tags: ['Platform', 'Fullstack', 'Postgres'],
link: '#',
id: "p3",
title: "Agora",
color: "success",
description:
"A real-time marketplace for artisans, connecting makers with patrons across the ancient world.",
tags: ["Platform", "Fullstack", "Postgres"],
link: "#",
},
],
skills: {
Design: ['Figma', 'Design Systems', 'Typography', 'Motion'],
Frontend: ['Vue', 'CSS', 'TypeScript', 'Vite'],
Backend: ['Node.js', 'Postgres', 'REST', 'GraphQL'],
Tools: ['Git', 'Storybook', 'Figma', 'Linear'],
Design: ["Figma", "Design Systems", "Typography", "Motion"],
Frontend: ["Vue", "CSS", "TypeScript", "Vite"],
Backend: ["Node.js", "Postgres", "REST", "GraphQL"],
Tools: ["Git", "Storybook", "Figma", "Linear"],
},
social: {
github: '#',
linkedin: '#',
email: 'hello@example.com',
github: "#",
linkedin: "#",
email: "hello@example.com",
},
}
};