188 lines
6.8 KiB
YAML
188 lines
6.8 KiB
YAML
name: CI/CD Pipeline
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- development
|
|
- main
|
|
|
|
jobs:
|
|
i18n-check:
|
|
name: i18n Catalog Check
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Install Bun
|
|
run: |
|
|
if ! command -v unzip >/dev/null; then
|
|
(command -v apt-get >/dev/null && apt-get update && apt-get install -y unzip) || \
|
|
(command -v apk >/dev/null && apk add --no-cache unzip)
|
|
fi
|
|
arch=$(uname -m)
|
|
case "$arch" in x86_64) target=x64 ;; aarch64|arm64) target=aarch64 ;; *) echo "unsupported arch: $arch"; exit 1 ;; esac
|
|
# musl images (Alpine) need the -musl build; the glibc one fails with spawn ENOENT
|
|
libc=""
|
|
if ldd --version 2>&1 | grep -qi musl; then libc="-musl"; fi
|
|
curl -fsSL -o /tmp/bun.zip "https://github.com/oven-sh/bun/releases/latest/download/bun-linux-${target}${libc}.zip"
|
|
unzip -q -o /tmp/bun.zip -d /tmp/bun-extract
|
|
mkdir -p "$HOME/.bun/bin"
|
|
mv /tmp/bun-extract/bun-linux-*/bun "$HOME/.bun/bin/bun"
|
|
chmod +x "$HOME/.bun/bin/bun"
|
|
echo "$HOME/.bun/bin" >> $GITHUB_PATH
|
|
"$HOME/.bun/bin/bun" --version
|
|
|
|
- name: Install dependencies
|
|
run: bun install --frozen-lockfile
|
|
|
|
- name: Catalogs in sync with source
|
|
run: |
|
|
bunx lingui extract --clean
|
|
git diff --exit-code src/locales || {
|
|
echo "::error::Locale catalogs are stale — run 'bun run extract' and commit src/locales"
|
|
exit 1
|
|
}
|
|
|
|
- name: No missing translations
|
|
run: bunx lingui compile --strict
|
|
|
|
build-app:
|
|
name: Build and Push App Docker Image
|
|
runs-on: ubuntu-latest
|
|
needs: i18n-check
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set environment variables
|
|
run: |
|
|
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
|
|
echo "DOCKER_TAG=latest" >> $GITHUB_ENV
|
|
echo "ENVIRONMENT=prod" >> $GITHUB_ENV
|
|
else
|
|
echo "DOCKER_TAG=dev" >> $GITHUB_ENV
|
|
echo "ENVIRONMENT=dev" >> $GITHUB_ENV
|
|
fi
|
|
|
|
- name: Build and push app Docker image
|
|
run: |
|
|
docker login git.yohler.net -u ${{ github.actor }} -p ${{ secrets.PACKAGES_TOKEN }}
|
|
|
|
docker build \
|
|
-f Dockerfile \
|
|
-t git.yohler.net/kyle/flxn-app:${{ env.DOCKER_TAG }} \
|
|
-t git.yohler.net/kyle/flxn-app:${{ github.sha }} \
|
|
.
|
|
|
|
docker push git.yohler.net/kyle/flxn-app:${{ env.DOCKER_TAG }}
|
|
docker push git.yohler.net/kyle/flxn-app:${{ github.sha }}
|
|
|
|
build-pocketbase:
|
|
name: Build and Push PocketBase Docker Image
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Check if PocketBase Dockerfile or migrations changed
|
|
id: check_changes
|
|
run: |
|
|
if [ "${{ github.event.before }}" == "0000000000000000000000000000000000000000" ] || ! git cat-file -e ${{ github.event.before }} 2>/dev/null; then
|
|
echo "changed=true" >> $GITHUB_OUTPUT
|
|
elif git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -qE "(Dockerfile.pocketbase|pb_migrations/)"; then
|
|
echo "changed=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "changed=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Build and push PocketBase Docker image
|
|
if: steps.check_changes.outputs.changed == 'true' || github.event.before == '0000000000000000000000000000000000000000'
|
|
run: |
|
|
docker login git.yohler.net -u ${{ github.actor }} -p ${{ secrets.PACKAGES_TOKEN }}
|
|
|
|
docker build \
|
|
-f Dockerfile.pocketbase \
|
|
-t git.yohler.net/kyle/flxn-pocketbase:latest \
|
|
-t git.yohler.net/kyle/flxn-pocketbase:${{ github.sha }} \
|
|
.
|
|
|
|
docker push git.yohler.net/kyle/flxn-pocketbase:latest
|
|
docker push git.yohler.net/kyle/flxn-pocketbase:${{ github.sha }}
|
|
|
|
deploy:
|
|
name: Deploy to Kubernetes
|
|
needs: [build-app, build-pocketbase]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: pin rancher host
|
|
run: echo "192.168.4.43 rancher.yohler.net" >> /etc/hosts
|
|
|
|
- name: Set environment variables
|
|
run: |
|
|
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
|
|
echo "ENVIRONMENT=prod" >> $GITHUB_ENV
|
|
echo "NAMESPACE=flxn-prod" >> $GITHUB_ENV
|
|
else
|
|
echo "ENVIRONMENT=dev" >> $GITHUB_ENV
|
|
echo "NAMESPACE=flxn-dev" >> $GITHUB_ENV
|
|
fi
|
|
|
|
- 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: Configure kubectl
|
|
run: |
|
|
mkdir -p $HOME/.kube
|
|
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > $HOME/.kube/config
|
|
chmod 600 $HOME/.kube/config
|
|
kubectl config set-cluster local --insecure-skip-tls-verify=true
|
|
|
|
- name: Verify kubectl access
|
|
run: |
|
|
kubectl version --client
|
|
kubectl get nodes
|
|
|
|
- name: Deploy shared services (SuperTokens)
|
|
run: |
|
|
kubectl apply -k k8s/overlays/shared/
|
|
|
|
- name: Deploy to ${{ env.ENVIRONMENT }}
|
|
run: |
|
|
kubectl apply -k k8s/overlays/${{ env.ENVIRONMENT }}/
|
|
|
|
- name: Force rollout to pull latest image
|
|
run: |
|
|
kubectl rollout restart deployment/flxn-app -n ${{ env.NAMESPACE }}
|
|
kubectl rollout restart deployment/flxn-pocketbase -n ${{ env.NAMESPACE }}
|
|
|
|
- name: Wait for rollout
|
|
run: |
|
|
kubectl rollout status deployment/flxn-app -n ${{ env.NAMESPACE }} --timeout=5m
|
|
kubectl rollout status deployment/flxn-pocketbase -n ${{ env.NAMESPACE }} --timeout=5m
|
|
|
|
- name: Verify deployment
|
|
run: |
|
|
kubectl get pods -n ${{ env.NAMESPACE }} -l app=flxn
|
|
kubectl get svc -n ${{ env.NAMESPACE }} -l app=flxn
|
|
kubectl get ingress -n ${{ env.NAMESPACE }}
|
|
|
|
- name: Check app health
|
|
run: |
|
|
sleep 15
|
|
APP_POD=$(kubectl get pod -n ${{ env.NAMESPACE }} -l component=app -o jsonpath='{.items[0].metadata.name}')
|
|
kubectl exec -n ${{ env.NAMESPACE }} $APP_POD -- wget -O- http://localhost:3000/api/health || echo "Health check failed (endpoint may not exist yet)"
|
|
|
|
- name: Check PocketBase health
|
|
run: |
|
|
PB_POD=$(kubectl get pod -n ${{ env.NAMESPACE }} -l component=pocketbase -o jsonpath='{.items[0].metadata.name}')
|
|
kubectl exec -n ${{ env.NAMESPACE }} $PB_POD -- wget -O- http://localhost:8090/api/health || echo "PocketBase health check completed"
|