Fix Artifact Naming #3
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
|
```sh
|
||||||
bun lint
|
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 {
|
export interface Stat {
|
||||||
value: string
|
value: string;
|
||||||
label: string
|
label: string;
|
||||||
color: string
|
color: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Project {
|
export interface Project {
|
||||||
id: string
|
id: string;
|
||||||
title: string
|
title: string;
|
||||||
color: 'primary' | 'info' | 'success' | 'warn'
|
color: "primary" | "info" | "success" | "warn";
|
||||||
description: string
|
description: string;
|
||||||
tags: string[]
|
tags: string[];
|
||||||
link: string
|
link: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PortfolioData {
|
export interface PortfolioData {
|
||||||
name: string
|
name: string;
|
||||||
role: string
|
role: string;
|
||||||
tagline: string
|
tagline: string;
|
||||||
status: string
|
status: string;
|
||||||
about: string
|
about: string;
|
||||||
stats: Stat[]
|
stats: Stat[];
|
||||||
projects: Project[]
|
projects: Project[];
|
||||||
skills: Record<string, string[]>
|
skills: Record<string, string[]>;
|
||||||
social: {
|
social: {
|
||||||
github: string
|
github: string;
|
||||||
linkedin: string
|
linkedin: string;
|
||||||
email: string
|
email: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edit this block to make the portfolio yours.
|
// Edit this block to make the portfolio yours.
|
||||||
export const portfolioData: PortfolioData = {
|
export const portfolioData: PortfolioData = {
|
||||||
name: 'Alexander Voss',
|
name: "Matthew McPeak",
|
||||||
role: 'Product Designer · Developer',
|
role: "Product Designer · Developer",
|
||||||
tagline: 'I craft digital experiences that feel inevitable — clear, classical, enduring.',
|
tagline: "I craft digital experiences that feel inevitable — clear, classical, enduring.",
|
||||||
status: 'Open for opportunities · 2026',
|
status: "Open for opportunities · 2026",
|
||||||
about:
|
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: [
|
stats: [
|
||||||
{ value: '10+', label: 'Years of craft', color: 'var(--primary)' },
|
{ value: "10+", label: "Years of craft", color: "var(--primary)" },
|
||||||
{ value: '40+', label: 'Projects shipped', color: 'var(--info)' },
|
{ value: "40+", label: "Projects shipped", color: "var(--info)" },
|
||||||
{ value: '12', label: 'Satisfied patrons', color: 'var(--success)' },
|
{ value: "12", label: "Satisfied patrons", color: "var(--success)" },
|
||||||
],
|
],
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
id: 'p1',
|
id: "p1",
|
||||||
title: 'Nychthemeron',
|
title: "Nychthemeron",
|
||||||
color: 'primary',
|
color: "primary",
|
||||||
description:
|
description:
|
||||||
'A dual-theme design system — Hades (dark) and Apollo (light) — for classical, lasting interfaces.',
|
"A dual-theme design system — Hades (dark) and Apollo (light) — for classical, lasting interfaces.",
|
||||||
tags: ['Design System', 'Vue', 'CSS'],
|
tags: ["Design System", "Vue", "CSS"],
|
||||||
link: '#',
|
link: "#",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'p2',
|
id: "p2",
|
||||||
title: 'The Oracle',
|
title: "The Oracle",
|
||||||
color: 'info',
|
color: "info",
|
||||||
description: 'A mythology-themed admin interface with consultations, petitioners, and prophecy tracking.',
|
description:
|
||||||
tags: ['UI Kit', 'Dashboard', 'TypeScript'],
|
"A mythology-themed admin interface with consultations, petitioners, and prophecy tracking.",
|
||||||
link: '#',
|
tags: ["UI Kit", "Dashboard", "TypeScript"],
|
||||||
|
link: "#",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'p3',
|
id: "p3",
|
||||||
title: 'Agora',
|
title: "Agora",
|
||||||
color: 'success',
|
color: "success",
|
||||||
description: 'A real-time marketplace for artisans, connecting makers with patrons across the ancient world.',
|
description:
|
||||||
tags: ['Platform', 'Fullstack', 'Postgres'],
|
"A real-time marketplace for artisans, connecting makers with patrons across the ancient world.",
|
||||||
link: '#',
|
tags: ["Platform", "Fullstack", "Postgres"],
|
||||||
|
link: "#",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
skills: {
|
skills: {
|
||||||
Design: ['Figma', 'Design Systems', 'Typography', 'Motion'],
|
Design: ["Figma", "Design Systems", "Typography", "Motion"],
|
||||||
Frontend: ['Vue', 'CSS', 'TypeScript', 'Vite'],
|
Frontend: ["Vue", "CSS", "TypeScript", "Vite"],
|
||||||
Backend: ['Node.js', 'Postgres', 'REST', 'GraphQL'],
|
Backend: ["Node.js", "Postgres", "REST", "GraphQL"],
|
||||||
Tools: ['Git', 'Storybook', 'Figma', 'Linear'],
|
Tools: ["Git", "Storybook", "Figma", "Linear"],
|
||||||
},
|
},
|
||||||
social: {
|
social: {
|
||||||
github: '#',
|
github: "#",
|
||||||
linkedin: '#',
|
linkedin: "#",
|
||||||
email: 'hello@example.com',
|
email: "hello@example.com",
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue