Initial deploy setup — CV static site
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled
Dockerfile (nginx alpine), K3s manifests (cv namespace, NodePort 30111), Gitea Actions workflow for build + rollout. Targets cv.arthurbarre.fr. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
commit
851ab1fc55
62
.gitea/workflows/deploy.yml
Normal file
62
.gitea/workflows/deploy.yml
Normal file
@ -0,0 +1,62 @@
|
||||
name: Build & Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
REGISTRY: git.arthurbarre.fr
|
||||
IMAGE: ordinarthur/cv
|
||||
NAMESPACE: cv
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to Gitea Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ordinarthur
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE }}:latest
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ github.sha }}
|
||||
|
||||
- name: Install kubectl
|
||||
run: |
|
||||
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||
chmod +x kubectl
|
||||
mv kubectl /usr/local/bin/
|
||||
|
||||
- name: Deploy to K3s
|
||||
run: |
|
||||
mkdir -p ~/.kube
|
||||
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
|
||||
chmod 600 ~/.kube/config
|
||||
|
||||
kubectl apply -f k3s/cv/namespace.yml
|
||||
|
||||
kubectl -n $NAMESPACE create secret docker-registry gitea-registry \
|
||||
--docker-server=$REGISTRY \
|
||||
--docker-username=ordinarthur \
|
||||
--docker-password=${{ secrets.REGISTRY_PASSWORD }} \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
kubectl apply -f k3s/cv/deployment.yml
|
||||
kubectl apply -f k3s/cv/service.yml
|
||||
|
||||
kubectl -n $NAMESPACE set image deployment/cv \
|
||||
cv=$REGISTRY/$IMAGE:${{ github.sha }}
|
||||
|
||||
kubectl -n $NAMESPACE rollout status deployment/cv --timeout=120s
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.pdf
|
||||
.DS_Store
|
||||
node_modules/
|
||||
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM nginx:1.27-alpine
|
||||
|
||||
WORKDIR /usr/share/nginx/html
|
||||
RUN rm -f index.html
|
||||
|
||||
COPY Resume-Arthur-barre-fr.html cv-short.html photo.jpg ./
|
||||
RUN cp Resume-Arthur-barre-fr.html index.html
|
||||
|
||||
EXPOSE 80
|
||||
1120
Resume-Arthur-barre-fr.html
Normal file
1120
Resume-Arthur-barre-fr.html
Normal file
File diff suppressed because it is too large
Load Diff
706
cv-short.html
Normal file
706
cv-short.html
Normal file
@ -0,0 +1,706 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Arthur Barré — Fullstack Developer · Résumé</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet" />
|
||||
<style>
|
||||
:root {
|
||||
--bg: #F4F2EC;
|
||||
--ink: #0A0A0A;
|
||||
--muted: #6B6B66;
|
||||
--accent: #FF4D00;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html, body {
|
||||
margin: 0; padding: 0;
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
font-family: 'Inter', system-ui, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 8pt;
|
||||
line-height: 1.4;
|
||||
}
|
||||
a { color: inherit; text-decoration: none; }
|
||||
|
||||
.page {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
padding: 8mm;
|
||||
margin: 0 auto;
|
||||
background: var(--bg);
|
||||
position: relative;
|
||||
}
|
||||
.frame {
|
||||
border: 1px solid var(--ink);
|
||||
height: calc(297mm - 16mm);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.label, .mono {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.topbar {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
border-bottom: 1px solid var(--ink);
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.topbar > div { padding: 6px 10px; border-right: 1px solid var(--ink); }
|
||||
.topbar > div:last-child { border-right: none; text-align: right; }
|
||||
.topbar .val { color: var(--ink); display: block; margin-top: 2px; }
|
||||
.topbar .accent { color: var(--accent); }
|
||||
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: 8fr 4fr;
|
||||
border-bottom: 1px solid var(--ink);
|
||||
}
|
||||
.hero-left { padding: 14px 18px; border-right: 1px solid var(--ink); display: flex; flex-direction: column; justify-content: space-between; }
|
||||
.hero h1 {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-weight: 300;
|
||||
font-size: 38pt;
|
||||
line-height: 0.92;
|
||||
letter-spacing: -0.035em;
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
.hero h1 em { font-style: italic; color: var(--accent); font-weight: 300; }
|
||||
.hero .pitch {
|
||||
margin-top: 12px;
|
||||
font-size: 8.5pt;
|
||||
line-height: 1.5;
|
||||
color: var(--ink);
|
||||
max-width: 56ch;
|
||||
}
|
||||
.hero .pitch b { font-weight: 600; }
|
||||
.hero .pitch .accent { color: var(--accent); font-weight: 500; }
|
||||
.hero-meta {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.hero-meta .k { color: var(--muted); margin-bottom: 2px; }
|
||||
.hero-meta .v { color: var(--ink); }
|
||||
.hero-meta .v.accent { color: var(--accent); }
|
||||
|
||||
.hero-right { display: flex; flex-direction: column; }
|
||||
.specimen-cell { padding: 10px 12px; border-bottom: 1px solid var(--ink); }
|
||||
.specimen-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.specimen-photo-wrap {
|
||||
position: relative;
|
||||
margin-top: 6px;
|
||||
border: 1px solid var(--ink);
|
||||
overflow: hidden;
|
||||
aspect-ratio: 1 / 1;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
.specimen-photo {
|
||||
width: 100%; height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
filter: grayscale(0.12) contrast(1.02);
|
||||
}
|
||||
.contact-cell {
|
||||
padding: 10px 12px;
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
.contact-cell .k { color: var(--muted); }
|
||||
.contact-cell a:hover { color: var(--accent); }
|
||||
|
||||
.skills-strip {
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid var(--ink);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
.skills-strip .chip {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
padding: 3px 7px;
|
||||
border: 1px solid var(--ink);
|
||||
border-radius: 999px;
|
||||
line-height: 1;
|
||||
}
|
||||
.skills-strip .chip.accent { color: var(--accent); border-color: var(--accent); }
|
||||
.skills-strip .chip.solid { background: var(--ink); color: var(--bg); border-color: var(--ink); }
|
||||
.skills-strip .skills-lead {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.body-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 7fr 5fr;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
.col-left { border-right: 1px solid var(--ink); display: flex; flex-direction: column; }
|
||||
.col-right { display: flex; flex-direction: column; }
|
||||
|
||||
.col-section-title {
|
||||
padding: 6px 14px;
|
||||
border-bottom: 1px solid var(--ink);
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: var(--ink);
|
||||
}
|
||||
.col-section-title .num-tag { color: var(--accent); font-weight: 500; }
|
||||
.col-section-title .count { color: var(--muted); }
|
||||
|
||||
.xp {
|
||||
padding: 8px 14px;
|
||||
border-bottom: 1px solid var(--ink);
|
||||
display: grid;
|
||||
grid-template-columns: 28px 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
.xp:last-child { border-bottom: none; }
|
||||
.xp .num {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 9pt;
|
||||
color: var(--ink);
|
||||
line-height: 1.2;
|
||||
}
|
||||
.xp h3 {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 10.5pt;
|
||||
letter-spacing: -0.012em;
|
||||
line-height: 1.15;
|
||||
}
|
||||
.xp h3 .role-sep { color: var(--muted); font-weight: 300; }
|
||||
.xp .when {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
color: var(--accent);
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.xp .when .where { color: var(--muted); margin-left: 4px; }
|
||||
.xp ul {
|
||||
margin: 5px 0 0;
|
||||
padding-left: 12px;
|
||||
font-size: 7.8pt;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.xp ul li { margin: 1px 0; }
|
||||
.xp ul li b { font-weight: 600; }
|
||||
|
||||
.pj {
|
||||
padding: 8px 14px;
|
||||
border-bottom: 1px solid var(--ink);
|
||||
}
|
||||
.pj:last-child { border-bottom: none; }
|
||||
.pj-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
.pj h3 {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 10pt;
|
||||
letter-spacing: -0.012em;
|
||||
}
|
||||
.pj .url-mono {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
color: var(--accent);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.pj .pj-meta {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
color: var(--muted);
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
margin-top: 1px;
|
||||
}
|
||||
.pj .pj-desc {
|
||||
margin-top: 4px;
|
||||
font-size: 7.8pt;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.pj .pj-desc b { font-weight: 600; }
|
||||
.pj .pj-stack {
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 3px;
|
||||
}
|
||||
.pj .pj-stack .chip {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 6.5pt;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
padding: 2px 5px;
|
||||
border: 1px solid var(--ink);
|
||||
border-radius: 999px;
|
||||
line-height: 1;
|
||||
}
|
||||
.pj .pj-stack .chip.accent { color: var(--accent); border-color: var(--accent); }
|
||||
|
||||
.formations {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
border-top: 1px solid var(--ink);
|
||||
}
|
||||
.formations .fo {
|
||||
padding: 8px 14px;
|
||||
border-right: 1px solid var(--ink);
|
||||
display: grid;
|
||||
grid-template-columns: 28px 1fr auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
.formations .fo:last-child { border-right: none; }
|
||||
.formations .fo .num {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 8pt;
|
||||
}
|
||||
.formations .fo h4 {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 9pt;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.formations .fo .school {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 6.8pt;
|
||||
color: var(--accent);
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
margin-top: 1px;
|
||||
}
|
||||
.formations .fo .when {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 6.8pt;
|
||||
color: var(--muted);
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
border-top: 1px solid var(--ink);
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 7pt;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.footer > div { padding: 6px 12px; border-right: 1px solid var(--ink); }
|
||||
.footer > div:last-child { border-right: none; text-align: right; color: var(--accent); }
|
||||
|
||||
@page { size: A4; margin: 0; }
|
||||
@media print {
|
||||
body { background: var(--bg); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
.page {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
page-break-after: auto;
|
||||
break-after: auto;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.print-btn, .switch-btn, .lang-toggle { display: none !important; }
|
||||
a { color: inherit; }
|
||||
}
|
||||
|
||||
/* DOWNLOAD BUTTON */
|
||||
.print-btn {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
border: 1px solid var(--ink);
|
||||
padding: 12px 18px;
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 9pt;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
box-shadow: 4px 4px 0 var(--ink);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
}
|
||||
.print-btn:hover { transform: translate(-2px, -2px); box-shadow: 6px 6px 0 var(--ink); }
|
||||
.print-btn:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
|
||||
.print-btn svg { vertical-align: middle; margin-right: 6px; }
|
||||
|
||||
/* VERSION SWITCHER (top-right) */
|
||||
.switch-btn {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
border: 1px solid var(--ink);
|
||||
padding: 10px 16px;
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 8.5pt;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
box-shadow: 4px 4px 0 var(--ink);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease, color 0.1s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.switch-btn:hover { background: var(--accent); color: #fff; transform: translate(-2px, -2px); box-shadow: 6px 6px 0 var(--ink); }
|
||||
.switch-btn .arrow { display: inline-block; transition: transform 0.15s ease; }
|
||||
.switch-btn:hover .arrow { transform: translateX(3px); }
|
||||
|
||||
/* LANG TOGGLE (bottom-right, left of download) */
|
||||
.lang-toggle {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 220px;
|
||||
z-index: 9999;
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
border: 1px solid var(--ink);
|
||||
padding: 12px 16px;
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-size: 9pt;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
box-shadow: 4px 4px 0 var(--ink);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.lang-toggle:hover { transform: translate(-2px, -2px); box-shadow: 6px 6px 0 var(--ink); }
|
||||
.lang-toggle:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
|
||||
.lang-toggle .lang-opt { opacity: 0.4; transition: opacity 0.1s ease, color 0.1s ease; cursor: pointer; }
|
||||
.lang-toggle .lang-opt.active { opacity: 1; color: var(--accent); font-weight: 600; }
|
||||
.lang-toggle .sep { color: var(--muted); opacity: 0.5; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="page">
|
||||
<div class="frame">
|
||||
|
||||
<div class="topbar">
|
||||
<div><span data-en="Document">Document</span><span class="val">CV / 2026</span></div>
|
||||
<div><span data-en="Format">Format</span><span class="val" data-en="Single-page">Single-page</span></div>
|
||||
<div><span data-en="Based in">Basé à</span><span class="val accent">Marseille — FR</span></div>
|
||||
<div><span data-en="Contact">Contact</span><span class="val">contact.arthurbarre@gmail.com</span></div>
|
||||
</div>
|
||||
|
||||
<div class="hero">
|
||||
<div class="hero-left">
|
||||
<div>
|
||||
<div class="label" data-en="[ Profile ] — Fullstack Developer">[ Profil ] — Fullstack Developer</div>
|
||||
<h1>Arthur Barré <em>—</em><br/>Fullstack <em>Developer.</em></h1>
|
||||
<p class="pitch" data-en='<b>7 years of experience</b> shipping <span class="accent">end-to-end</span> products — from Node / Symfony / Python back-ends to polished React / Vue front-ends, all the way to self-hosted Proxmox / K3s infrastructure. I <b>ship</b>, I <b>measure</b>, and I love working fast with teams aiming for <span class="accent">product excellence</span>.'>
|
||||
<b>7 ans d'expérience</b> à concevoir des produits <span class="accent">end-to-end</span> — du back-end Node / Symfony / Python à un front-end React / Vue ciselé, jusqu'à l'infra auto-hébergée Proxmox / K3s. Je <b>livre</b>, je <b>mesure</b>, et j'aime travailler vite avec des équipes qui visent <span class="accent">l'excellence produit</span>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="hero-meta">
|
||||
<div><div class="k" data-en="Role">Role</div><div class="v">Fullstack</div></div>
|
||||
<div><div class="k" data-en="Years">Years</div><div class="v" data-en="7+ yrs">7+ ans</div></div>
|
||||
<div><div class="k" data-en="Focus">Focus</div><div class="v" data-en="SaaS · AI · Product">SaaS · IA · Produit</div></div>
|
||||
<div><div class="k" data-en="Status">Status</div><div class="v accent" data-en="Open · Permanent / Freelance">Ouvert · CDI / Freelance</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-right">
|
||||
<div class="specimen-cell">
|
||||
<div class="specimen-header">
|
||||
<span>[ FIG. A ]</span>
|
||||
<span style="color:var(--accent);" data-en="Age 25">25 ans</span>
|
||||
</div>
|
||||
<div class="specimen-photo-wrap">
|
||||
<img class="specimen-photo" src="photo.jpg" alt="Arthur Barré" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-cell">
|
||||
<div><span class="k" data-en="Tel —">Tél —</span> 06 13 17 61 17</div>
|
||||
<div><span class="k">Mail —</span> <a href="mailto:contact.arthurbarre@gmail.com">contact.arthurbarre@gmail.com</a></div>
|
||||
<div><span class="k">Site —</span> <a href="https://arthurbarre.fr" style="color:var(--accent);">arthurbarre.fr</a></div>
|
||||
<div><span class="k" data-en="Loc —">Lieu —</span> Marseille 13001</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="skills-strip">
|
||||
<span class="skills-lead">Stack —</span>
|
||||
<span class="chip solid">TypeScript</span>
|
||||
<span class="chip accent">React</span>
|
||||
<span class="chip accent">Next.js</span>
|
||||
<span class="chip">Vue.js</span>
|
||||
<span class="chip">Node.js</span>
|
||||
<span class="chip">AdonisJS</span>
|
||||
<span class="chip">Fastify</span>
|
||||
<span class="chip">Symfony</span>
|
||||
<span class="chip">Python</span>
|
||||
<span class="chip">PostgreSQL</span>
|
||||
<span class="chip">MongoDB</span>
|
||||
<span class="chip">GraphQL</span>
|
||||
<span class="chip">Docker</span>
|
||||
<span class="chip">K3s</span>
|
||||
<span class="chip">Proxmox</span>
|
||||
<span class="chip">Stripe</span>
|
||||
<span class="chip">React Native</span>
|
||||
<span class="chip">IoT</span>
|
||||
</div>
|
||||
|
||||
<div class="body-grid">
|
||||
|
||||
<div class="col-left">
|
||||
<div class="col-section-title">
|
||||
<span><span class="num-tag">[ 01 ]</span> <span data-en="Work experience">Expériences professionnelles</span></span>
|
||||
<span class="count" data-en="03 items · 2019 — Present">03 items · 2019 — Présent</span>
|
||||
</div>
|
||||
|
||||
<div class="xp">
|
||||
<div class="num">03</div>
|
||||
<div>
|
||||
<h3 data-en='Fullstack Developer <span class="role-sep">— Permanent</span>'>Développeur Fullstack <span class="role-sep">— CDI</span></h3>
|
||||
<div class="when" data-en='2023 — Present <span class="where">· thecamp · Aix-en-Provence</span>'>2023 — Présent <span class="where">· thecamp · Aix-en-Provence</span></div>
|
||||
<ul data-en='<li>Full-stack dev on a <b>multi-tech</b> environment (React, Vue, PHP/Symfony, Python, Node) — front-end, back-end and <b>IoT</b>.</li><li>Designed & shipped a Python-based <b>BMS</b> (Building Management System) — centralizes and translates equipment data.</li><li>Integrated a <b>Symfony</b> app with an interactive front-end to visualize and control building state.</li><li>Tech choices, <b>software architecture</b>, scalability and maintainability across a Docker + Linux stack.</li>'>
|
||||
<li>Dev full-stack sur un environnement <b>multi-technos</b> (React, Vue, PHP/Symfony, Python, Node) — front-end, back-end et <b>IoT</b>.</li>
|
||||
<li>Conception & mise en prod du système de <b>GTB</b> (Gestion Technique du Bâtiment) en Python — centralise et traduit les données équipements.</li>
|
||||
<li>Intégration d'une <b>application Symfony</b> avec restitution dans un front-end interactif pour visualiser et piloter l'état des bâtiments.</li>
|
||||
<li>Choix techniques, <b>architecture logicielle</b>, scalabilité et maintenabilité dans un environnement Docker + Linux.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="xp">
|
||||
<div class="num">02</div>
|
||||
<div>
|
||||
<h3 data-en='Fullstack Developer <span class="role-sep">— Apprenticeship</span>'>Développeur Fullstack <span class="role-sep">— Alternance</span></h3>
|
||||
<div class="when">2021 — 2023 <span class="where">· talents'in & between · Marseille</span></div>
|
||||
<ul data-en='<li>Built a <b>REST API</b> in Java/Spring Boot encoding business rules for a complex HR product.</li><li>Consumed the APIs from a Vue.js front-end for clear end-to-end data flow ownership.</li><li>Led the <b>new tech stack</b> post-acquisition by Between: <b>Java + Spring Boot + Vue.js</b>.</li><li>Migrated dev environment (Mac → Linux) and rolled out collaborative <b>SCRUM</b> practices.</li>'>
|
||||
<li>Développement d'une <b>API REST</b> en Java/Spring Boot intégrant les règles métier d'un produit RH complexe.</li>
|
||||
<li>Consommation des API côté front (Vue.js) pour une vision claire du flux et des données end-to-end.</li>
|
||||
<li>Pilotage de la <b>nouvelle stack technique</b> après rachat par Between : <b>Java + Spring Boot + Vue.js</b>.</li>
|
||||
<li>Adaptation d'environnement de dev (Mac → Linux) et mise en place de pratiques collaboratives <b>SCRUM</b>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="xp">
|
||||
<div class="num">01</div>
|
||||
<div>
|
||||
<h3 data-en='Frontend Developer <span class="role-sep">— Apprenticeship</span>'>Développeur Frontend <span class="role-sep">— Alternance</span></h3>
|
||||
<div class="when">2019 — 2021 <span class="where">· EasyMovie · Paris</span></div>
|
||||
<ul data-en='<li>Built & maintained a <b>React component library</b> used across every product page.</li><li>Worked with <b>cross-functional teams</b> (PO, Design, iOS, Android, QA, DevOps) in a structured production cycle.</li><li>Full cycle <b>Develop → Pre-Prod → Production</b> with systematic peer code reviews.</li>'>
|
||||
<li>Dev & maintenance d'une <b>librairie de composants React</b> intégrée dans toutes les pages produit.</li>
|
||||
<li>Collab avec équipes <b>pluridisciplinaires</b> (PO, Design, iOS, Android, QA, DevOps) dans un cycle de prod structuré.</li>
|
||||
<li>Cycle complet <b>Develop → Pre-Prod → Production</b> avec revues de code croisées systématiques.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-right">
|
||||
<div class="col-section-title">
|
||||
<span><span class="num-tag">[ 02 ]</span> <span data-en="Projects & Freelance">Projets & Freelance</span></span>
|
||||
<span class="count">03 selects</span>
|
||||
</div>
|
||||
|
||||
<div class="pj">
|
||||
<div class="pj-head">
|
||||
<h3>Rubis</h3>
|
||||
<a class="url-mono" href="https://rubis.pro">→ rubis.pro</a>
|
||||
</div>
|
||||
<div class="pj-meta" data-en="SaaS B2B · Freelance · 2025 — Ongoing">SaaS B2B · Freelance · 2025 — En cours</div>
|
||||
<div class="pj-desc" data-en="<b>SaaS for automated dunning of unpaid invoices</b> for French SMBs, designed and built end-to-end. Self-hosted infrastructure on Proxmox + K3s via Gitea CI."><b>SaaS de relance automatique de factures impayées</b> pour TPE-PME, conçu et développé end-to-end. Infra auto-hébergée sur Proxmox + K3s via Gitea CI.</div>
|
||||
<div class="pj-stack">
|
||||
<span class="chip accent">AdonisJS</span>
|
||||
<span class="chip accent">React 19</span>
|
||||
<span class="chip">Astro 6</span>
|
||||
<span class="chip">Tailwind v4</span>
|
||||
<span class="chip">Postgres</span>
|
||||
<span class="chip">K3s</span>
|
||||
<span class="chip">Stripe</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pj">
|
||||
<div class="pj-head">
|
||||
<h3>Rebours Studio</h3>
|
||||
<a class="url-mono" href="https://rebours.studio">→ rebours.studio</a>
|
||||
</div>
|
||||
<div class="pj-meta" data-en="E-commerce · Freelance · 2025">E-commerce · Freelance · 2025</div>
|
||||
<div class="pj-desc" data-en="Minimalist <b>showcase + e-commerce</b> site for a Paris-based art furniture studio. Custom <b>Stripe checkout</b> and premium visual identity.">Site <b>vitrine + e-commerce</b> minimaliste pour un atelier parisien de mobilier d'art. <b>Tunnel de paiement Stripe</b> sur-mesure et identité visuelle haut de gamme.</div>
|
||||
<div class="pj-stack">
|
||||
<span class="chip accent">Next.js</span>
|
||||
<span class="chip">React</span>
|
||||
<span class="chip">Tailwind</span>
|
||||
<span class="chip">Stripe</span>
|
||||
<span class="chip">API media</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pj">
|
||||
<div class="pj-head">
|
||||
<h3>Freedge.app</h3>
|
||||
<a class="url-mono" href="https://freedge.app">→ freedge.app</a>
|
||||
</div>
|
||||
<div class="pj-meta" data-en="AI SaaS · Personal · 2024">SaaS IA · Perso · 2024</div>
|
||||
<div class="pj-desc" data-en="<b>AI-powered recipe generation</b> end-to-end: Fastify + Prisma, <b>GPT-4 Turbo & DALL·E 3</b> integration, multi-tier Stripe billing."><b>Génération de recettes par IA</b> end-to-end : Fastify + Prisma, intégration <b>GPT-4 Turbo & DALL·E 3</b>, paiement Stripe multi-plans.</div>
|
||||
<div class="pj-stack">
|
||||
<span class="chip accent">Fastify</span>
|
||||
<span class="chip accent">OpenAI</span>
|
||||
<span class="chip">React</span>
|
||||
<span class="chip">Prisma</span>
|
||||
<span class="chip">Stripe</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="formations">
|
||||
<div class="fo">
|
||||
<div class="num">01</div>
|
||||
<div>
|
||||
<h4 data-en='Software Architect <span style="color:var(--muted);font-weight:300;">— Master's</span>'>Architecte Logiciel <span style="color:var(--muted);font-weight:300;">— Master 2</span></h4>
|
||||
<div class="school">Epitech · Marseille</div>
|
||||
</div>
|
||||
<div class="when">2021<br/>2023</div>
|
||||
</div>
|
||||
<div class="fo">
|
||||
<div class="num">02</div>
|
||||
<div>
|
||||
<h4 data-en='Multimedia Project Manager <span style="color:var(--muted);font-weight:300;">— Bachelor's</span>'>Chef de Projet Multimédia <span style="color:var(--muted);font-weight:300;">— Bachelor</span></h4>
|
||||
<div class="school">Hetic · Paris</div>
|
||||
</div>
|
||||
<div class="when">2018<br/>2021</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div>Arthur Barré</div>
|
||||
<div>arthurbarre.fr</div>
|
||||
<div>06 13 17 61 17</div>
|
||||
<div data-en="Available — let's build">Disponible — let's build</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="switch-btn" href="Resume-Arthur-barre-fr.html" aria-label="Voir la version longue">
|
||||
<span data-en="Long version · 3 pages">Version longue · 3 pages</span>
|
||||
<span class="arrow">→</span>
|
||||
</a>
|
||||
|
||||
<button class="lang-toggle" aria-label="Toggle language">
|
||||
<span class="lang-opt" data-lang="en">EN</span>
|
||||
<span class="sep">/</span>
|
||||
<span class="lang-opt active" data-lang="fr">FR</span>
|
||||
</button>
|
||||
|
||||
<button class="print-btn" onclick="window.print()" aria-label="Télécharger en PDF">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter">
|
||||
<path d="M12 3v12m0 0l-5-5m5 5l5-5M5 21h14"/>
|
||||
</svg>
|
||||
<span data-en="Download PDF">Download PDF</span>
|
||||
</button>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const STORAGE_KEY = 'cv-lang';
|
||||
const detect = () => {
|
||||
const saved = localStorage.getItem(STORAGE_KEY);
|
||||
if (saved === 'fr' || saved === 'en') return saved;
|
||||
const nav = (navigator.language || 'fr').toLowerCase();
|
||||
return nav.startsWith('fr') ? 'fr' : 'en';
|
||||
};
|
||||
let current = detect();
|
||||
|
||||
const apply = (lang) => {
|
||||
document.documentElement.setAttribute('lang', lang);
|
||||
document.querySelectorAll('[data-en]').forEach(el => {
|
||||
if (!el.hasAttribute('data-fr')) el.setAttribute('data-fr', el.innerHTML);
|
||||
el.innerHTML = lang === 'en' ? el.getAttribute('data-en') : el.getAttribute('data-fr');
|
||||
});
|
||||
document.querySelectorAll('.lang-toggle .lang-opt').forEach(o => {
|
||||
o.classList.toggle('active', o.dataset.lang === lang);
|
||||
});
|
||||
document.title = lang === 'en'
|
||||
? 'Arthur Barré — Fullstack Developer · Résumé'
|
||||
: 'Arthur Barré — Fullstack Developer · Résumé';
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
apply(current);
|
||||
const btn = document.querySelector('.lang-toggle');
|
||||
if (!btn) return;
|
||||
btn.addEventListener('click', () => {
|
||||
current = current === 'fr' ? 'en' : 'fr';
|
||||
localStorage.setItem(STORAGE_KEY, current);
|
||||
apply(current);
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
45
k3s/cv/deployment.yml
Normal file
45
k3s/cv/deployment.yml
Normal file
@ -0,0 +1,45 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: cv
|
||||
namespace: cv
|
||||
labels:
|
||||
app: cv
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cv
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cv
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: gitea-registry
|
||||
containers:
|
||||
- name: cv
|
||||
image: git.arthurbarre.fr/ordinarthur/cv:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
resources:
|
||||
requests:
|
||||
memory: "16Mi"
|
||||
cpu: "10m"
|
||||
limits:
|
||||
memory: "64Mi"
|
||||
cpu: "100m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
4
k3s/cv/namespace.yml
Normal file
4
k3s/cv/namespace.yml
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: cv
|
||||
14
k3s/cv/service.yml
Normal file
14
k3s/cv/service.yml
Normal file
@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cv
|
||||
namespace: cv
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: cv
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 80
|
||||
nodePort: 30111
|
||||
Loading…
x
Reference in New Issue
Block a user