Add CV/resume web app at cv.ops.eblu.me
Container (nginx:alpine), k8s manifests, ArgoCD app, Caddy route, and deploy workflow. Content is built and released from the separate cv repo (forge.ops.eblu.me/eblume/cv). Also removes unnecessary sh -c wrapper around tar in build_docs Dagger function. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a800bdc8b9
commit
5e6fc79921
12 changed files with 327 additions and 3 deletions
|
|
@ -78,9 +78,12 @@ class BlumeopsCi:
|
|||
.with_exec(["npx", "quartz", "build", "-d", "docs"])
|
||||
.with_exec(
|
||||
[
|
||||
"sh",
|
||||
"-c",
|
||||
f"tar -czf /docs-{version}.tar.gz -C public .",
|
||||
"tar",
|
||||
"-czf",
|
||||
f"/docs-{version}.tar.gz",
|
||||
"-C",
|
||||
"public",
|
||||
".",
|
||||
]
|
||||
)
|
||||
.file(f"/docs-{version}.tar.gz")
|
||||
|
|
|
|||
123
.forgejo/workflows/cv-deploy.yaml
Normal file
123
.forgejo/workflows/cv-deploy.yaml
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# CV Deploy Workflow
|
||||
#
|
||||
# Updates the CV deployment to a specific package version, commits
|
||||
# the change, and syncs via ArgoCD.
|
||||
#
|
||||
# Usage:
|
||||
# 1. Release a new CV package from the cv repo first
|
||||
# 2. Go to Actions > Deploy CV > Run workflow
|
||||
# 3. Enter the version to deploy, or leave as "latest"
|
||||
|
||||
name: Deploy CV
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'CV package version to deploy (e.g., v1.0.0, or "latest")'
|
||||
required: true
|
||||
default: 'latest'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: k8s
|
||||
steps:
|
||||
- name: Resolve version
|
||||
id: version
|
||||
run: |
|
||||
INPUT_VERSION="${{ inputs.version }}"
|
||||
|
||||
if [ "$INPUT_VERSION" = "latest" ]; then
|
||||
echo "Resolving latest CV package version..."
|
||||
VERSION=$(curl -s "https://forge.ops.eblu.me/api/v1/packages/eblume?type=generic&q=cv" \
|
||||
| jq -r '[.[] | select(.name == "cv")] | sort_by(.version) | last | .version // empty')
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Error: No CV packages found"
|
||||
exit 1
|
||||
fi
|
||||
echo "Resolved latest version: $VERSION"
|
||||
else
|
||||
VERSION="$INPUT_VERSION"
|
||||
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Error: Version must be in format vX.Y.Z (e.g., v1.0.0)"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify the package exists
|
||||
TARBALL="cv-${VERSION}.tar.gz"
|
||||
PACKAGE_URL="https://forge.ops.eblu.me/api/packages/eblume/generic/cv/${VERSION}/${TARBALL}"
|
||||
if ! curl -fsSL --head "$PACKAGE_URL" > /dev/null 2>&1; then
|
||||
echo "Error: Package not found at $PACKAGE_URL"
|
||||
echo "Run the 'Release CV' workflow in the cv repo first."
|
||||
exit 1
|
||||
fi
|
||||
echo "Package verified: $PACKAGE_URL"
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Update CV deployment
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TARBALL="cv-${VERSION}.tar.gz"
|
||||
DEPLOYMENT_FILE="argocd/manifests/cv/deployment.yaml"
|
||||
RELEASE_URL="https://forge.ops.eblu.me/api/packages/eblume/generic/cv/${VERSION}/${TARBALL}"
|
||||
|
||||
echo "Updating $DEPLOYMENT_FILE with CV_RELEASE_URL..."
|
||||
sed -i "s|value: \"https://forge.ops.eblu.me/api/packages/eblume/generic/cv/[^\"]*\"|value: \"${RELEASE_URL}\"|" "$DEPLOYMENT_FILE"
|
||||
|
||||
echo "Updated deployment:"
|
||||
grep -A1 "CV_RELEASE_URL" "$DEPLOYMENT_FILE"
|
||||
|
||||
- name: Commit release changes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
|
||||
git config user.name "Forgejo Actions"
|
||||
git config user.email "actions@forge.ops.eblu.me"
|
||||
|
||||
git add argocd/manifests/cv/deployment.yaml
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit (already at $VERSION)"
|
||||
else
|
||||
git commit -m "Update CV release to $VERSION
|
||||
|
||||
[skip ci]"
|
||||
git push origin HEAD:main
|
||||
echo "Changes committed and pushed"
|
||||
fi
|
||||
|
||||
- name: Deploy CV
|
||||
env:
|
||||
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
|
||||
run: |
|
||||
echo "Syncing CV app via ArgoCD..."
|
||||
|
||||
argocd app sync cv \
|
||||
--server argocd.ops.eblu.me \
|
||||
--grpc-web \
|
||||
--prune
|
||||
|
||||
argocd app wait cv \
|
||||
--server argocd.ops.eblu.me \
|
||||
--grpc-web \
|
||||
--timeout 120
|
||||
|
||||
echo "CV app synced successfully!"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
echo "================================================"
|
||||
echo "CV Deployed: $VERSION"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
echo "CV should now be live at:"
|
||||
echo " https://cv.ops.eblu.me/"
|
||||
|
|
@ -73,6 +73,9 @@ caddy_services:
|
|||
- name: docs
|
||||
host: "docs.{{ caddy_domain }}"
|
||||
backend: "https://docs.tail8d86e.ts.net"
|
||||
- name: cv
|
||||
host: "cv.{{ caddy_domain }}"
|
||||
backend: "https://cv.tail8d86e.ts.net"
|
||||
- name: sifaka
|
||||
host: "nas.{{ caddy_domain }}"
|
||||
backend: "http://sifaka:5000"
|
||||
|
|
|
|||
18
argocd/apps/cv.yaml
Normal file
18
argocd/apps/cv.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: cv
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git
|
||||
targetRevision: main
|
||||
path: argocd/manifests/cv
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: cv
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
43
argocd/manifests/cv/deployment.yaml
Normal file
43
argocd/manifests/cv/deployment.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: cv
|
||||
namespace: cv
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cv
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cv
|
||||
spec:
|
||||
containers:
|
||||
- name: cv
|
||||
image: registry.ops.eblu.me/blumeops/cv:v1.0.0
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
env:
|
||||
- name: CV_RELEASE_URL
|
||||
value: "https://forge.ops.eblu.me/api/packages/eblume/generic/cv/v0.1.0/cv-v0.1.0.tar.gz"
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "10m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 80
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
27
argocd/manifests/cv/ingress-tailscale.yaml
Normal file
27
argocd/manifests/cv/ingress-tailscale.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cv-tailscale
|
||||
namespace: cv
|
||||
annotations:
|
||||
tailscale.com/proxy-class: "default"
|
||||
tailscale.com/proxy-group: "ingress"
|
||||
tailscale.com/tags: "tag:k8s"
|
||||
gethomepage.dev/enabled: "true"
|
||||
gethomepage.dev/name: "CV"
|
||||
gethomepage.dev/group: "Apps"
|
||||
gethomepage.dev/icon: "mdi-file-document"
|
||||
gethomepage.dev/description: "Resume / CV"
|
||||
gethomepage.dev/href: "https://cv.ops.eblu.me"
|
||||
gethomepage.dev/pod-selector: "app=cv"
|
||||
spec:
|
||||
ingressClassName: tailscale
|
||||
defaultBackend:
|
||||
service:
|
||||
name: cv
|
||||
port:
|
||||
number: 80
|
||||
tls:
|
||||
- hosts:
|
||||
- cv
|
||||
8
argocd/manifests/cv/kustomization.yaml
Normal file
8
argocd/manifests/cv/kustomization.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: cv
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress-tailscale.yaml
|
||||
13
argocd/manifests/cv/service.yaml
Normal file
13
argocd/manifests/cv/service.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cv
|
||||
namespace: cv
|
||||
spec:
|
||||
selector:
|
||||
app: cv
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 80
|
||||
21
containers/cv/Dockerfile
Normal file
21
containers/cv/Dockerfile
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# CV/Resume Static Site Server
|
||||
# Downloads and serves a CV site tarball (HTML+CSS+PDF) via nginx
|
||||
#
|
||||
# Configuration (via environment):
|
||||
# CV_RELEASE_URL - URL to download the CV content tarball
|
||||
#
|
||||
# The container downloads the tarball on startup, extracts it, and serves with nginx.
|
||||
|
||||
FROM nginx:alpine
|
||||
|
||||
# Install curl for downloading release assets
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
# Copy startup script and nginx config
|
||||
COPY start.sh /start.sh
|
||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||
RUN chmod +x /start.sh
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["/start.sh"]
|
||||
33
containers/cv/default.conf
Normal file
33
containers/cv/default.conf
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Enable gzip compression
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Force PDF download
|
||||
location = /resume.pdf {
|
||||
add_header Content-Disposition 'attachment; filename="erich-blume-resume.pdf"';
|
||||
}
|
||||
|
||||
# Serve files directly
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /healthz {
|
||||
access_log off;
|
||||
return 200 "ok\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
31
containers/cv/start.sh
Normal file
31
containers/cv/start.sh
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
HTML_DIR="/usr/share/nginx/html"
|
||||
|
||||
# Check for required environment variable
|
||||
if [ -z "$CV_RELEASE_URL" ]; then
|
||||
echo "Error: CV_RELEASE_URL environment variable is required"
|
||||
echo "Set it to the URL of the CV content tarball to serve"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Downloading CV content from: $CV_RELEASE_URL"
|
||||
|
||||
# Download the tarball
|
||||
if ! curl -fsSL "$CV_RELEASE_URL" -o /tmp/cv.tar.gz; then
|
||||
echo "Error: Failed to download CV content from $CV_RELEASE_URL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clear existing content and extract
|
||||
rm -rf "${HTML_DIR:?}"/*
|
||||
echo "Extracting CV content to $HTML_DIR"
|
||||
tar -xzf /tmp/cv.tar.gz -C "$HTML_DIR"
|
||||
rm /tmp/cv.tar.gz
|
||||
|
||||
echo "CV content extracted successfully"
|
||||
echo "Starting nginx..."
|
||||
|
||||
# Start nginx in foreground
|
||||
exec nginx -g "daemon off;"
|
||||
1
docs/changelog.d/cv-web-app.feature.md
Normal file
1
docs/changelog.d/cv-web-app.feature.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Add CV/resume web app at cv.ops.eblu.me — container, k8s manifests, Caddy route, and deploy workflow. Content built from separate cv repo.
|
||||
Loading…
Add table
Add a link
Reference in a new issue