feat: docker
This commit is contained in:
parent
015e608f7c
commit
55e585186e
6 changed files with 174 additions and 55 deletions
8
.dockerignore
Normal file
8
.dockerignore
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
node_modules
|
||||
dist-ssr
|
||||
coverage
|
||||
test-results
|
||||
playwright-report
|
||||
.git
|
||||
.vscode
|
||||
*.log
|
||||
67
.forgejo/workflows/docker-publish.yml
Normal file
67
.forgejo/workflows/docker-publish.yml
Normal 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
7
Dockerfile
Normal 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
|
||||
25
README.md
25
README.md
|
|
@ -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
10
nginx.conf
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue