201 lines
6.1 KiB
Bash
Executable File
201 lines
6.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
echo "🚀 Starting Static Pages Deployment..."
|
|
|
|
# 1. Resolve repository name fallback
|
|
if [ -z "${REPO_NAME:-}" ]; then
|
|
REPO_NAME=$(basename "${GITHUB_REPOSITORY:-app}")
|
|
fi
|
|
|
|
# 2. Locate static build assets
|
|
ASSET_DIR="${INPUT_PATH:-./dist}"
|
|
if [ ! -d "$ASSET_DIR" ]; then
|
|
echo "Specified path '$ASSET_DIR' not found. Searching common output directories..."
|
|
if [ -d "./dist" ]; then
|
|
ASSET_DIR="./dist"
|
|
elif [ -d "./build/web" ]; then
|
|
ASSET_DIR="./build/web"
|
|
elif [ -d "./web/dist" ]; then
|
|
ASSET_DIR="./web/dist"
|
|
elif [ -d "./public" ]; then
|
|
ASSET_DIR="./public"
|
|
elif [ -d "./build" ]; then
|
|
ASSET_DIR="./build"
|
|
elif [ -d "./out" ]; then
|
|
ASSET_DIR="./out"
|
|
elif [ -d "./_site" ]; then
|
|
ASSET_DIR="./_site"
|
|
else
|
|
echo "::error::Could not find static asset directory! Checked: '$ASSET_DIR', ./dist, ./build/web, ./public, ./build, ./out, ./_site"
|
|
exit 1
|
|
fi
|
|
fi
|
|
echo "📁 Using static assets from: $ASSET_DIR"
|
|
|
|
# 3. Resolve target domain & router name
|
|
DOMAIN="${INPUT_DOMAIN:-}"
|
|
if [ -n "$DOMAIN" ]; then
|
|
DOMAIN=$(echo "$DOMAIN" | sed -E 's#^https?://##' | sed 's#/.*$##')
|
|
else
|
|
CLEAN_REPO=$(echo "$REPO_NAME" | tr '_' '-' | tr '[:upper:]' '[:lower:]')
|
|
DOMAIN="${CLEAN_REPO}.rodydavis.dev"
|
|
fi
|
|
|
|
APP_NAME="${INPUT_APP_NAME:-$REPO_NAME}"
|
|
APP_NAME=$(echo "$APP_NAME" | tr '_' '-' | tr '[:upper:]' '[:lower:]')
|
|
ROUTER_NAME=$(echo "$APP_NAME" | tr -cd '[:alnum:]')
|
|
|
|
# 4. Resolve container image tag
|
|
REGISTRY="${INPUT_REGISTRY:-git.rodydavis.dev}"
|
|
if [ -n "${INPUT_IMAGE_NAME:-}" ]; then
|
|
IMAGE_NAME="$INPUT_IMAGE_NAME"
|
|
else
|
|
REPO_LOWER=$(echo "$GITHUB_REPOSITORY" | tr '[:upper:]' '[:lower:]')
|
|
IMAGE_NAME="${REGISTRY}/${REPO_LOWER}:latest"
|
|
fi
|
|
|
|
echo "🌐 Routing Target: https://${DOMAIN}"
|
|
echo "📦 Container Name: ${APP_NAME}-web"
|
|
echo "🏷️ Image Tag: ${IMAGE_NAME}"
|
|
|
|
# 5. Prepare build bundle in temporary staging directory
|
|
BUILD_STAGING=$(mktemp -d)
|
|
trap 'rm -rf "$BUILD_STAGING"' EXIT
|
|
|
|
mkdir -p "${BUILD_STAGING}/html"
|
|
cp -R "${ASSET_DIR}/." "${BUILD_STAGING}/html/"
|
|
|
|
# Configure SPA fallback rule
|
|
if [ "${INPUT_SPA:-true}" = "true" ]; then
|
|
SPA_DIRECTIVE='try_files $uri $uri/ /index.html;'
|
|
else
|
|
SPA_DIRECTIVE='try_files $uri $uri/ =404;'
|
|
fi
|
|
|
|
cat <<EOF > "${BUILD_STAGING}/nginx.conf"
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name localhost;
|
|
|
|
root /usr/share/nginx/html;
|
|
index index.html index.htm;
|
|
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_min_length 1024;
|
|
gzip_proxied expired no-cache no-store private auth;
|
|
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript application/json application/manifest+json image/svg+xml;
|
|
|
|
location = /sw.js {
|
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
expires 0;
|
|
}
|
|
|
|
location = /manifest.json {
|
|
add_header Cache-Control "no-cache, must-revalidate";
|
|
expires 0;
|
|
}
|
|
|
|
location / {
|
|
${SPA_DIRECTIVE}
|
|
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
}
|
|
|
|
location ~* \.(?:css|js|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc|woff|woff2)$ {
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
|
|
error_page 500 502 503 504 /50x.html;
|
|
location = /50x.html {
|
|
root /usr/share/nginx/html;
|
|
}
|
|
}
|
|
EOF
|
|
|
|
cat <<EOF > "${BUILD_STAGING}/Dockerfile"
|
|
FROM nginx:alpine
|
|
WORKDIR /usr/share/nginx/html
|
|
RUN rm -rf ./*
|
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
COPY html/ ./
|
|
EXPOSE 80
|
|
CMD ["nginx", "-g", "daemon off;"]
|
|
EOF
|
|
|
|
NETWORK="${INPUT_NETWORK:-apps-net}"
|
|
ENTRYPOINTS="${INPUT_ENTRYPOINTS:-web}"
|
|
|
|
cat <<EOF > "${BUILD_STAGING}/docker-compose.yml"
|
|
version: '3.8'
|
|
|
|
services:
|
|
${APP_NAME}-web:
|
|
image: ${IMAGE_NAME}
|
|
container_name: ${APP_NAME}-web
|
|
restart: unless-stopped
|
|
networks:
|
|
- ${NETWORK}
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(\`${DOMAIN}\`)"
|
|
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=${ENTRYPOINTS}"
|
|
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=80"
|
|
|
|
networks:
|
|
${NETWORK}:
|
|
external: true
|
|
EOF
|
|
|
|
# 6. Docker Build
|
|
echo "🔨 Building Docker image..."
|
|
docker build -t "$IMAGE_NAME" -f "${BUILD_STAGING}/Dockerfile" "$BUILD_STAGING"
|
|
|
|
# 7. Push image to registry
|
|
if [ "${INPUT_PUSH_IMAGE:-true}" = "true" ]; then
|
|
if [ -n "${INPUT_GIT_TOKEN:-}" ]; then
|
|
echo "🔐 Logging into ${REGISTRY}..."
|
|
echo "$INPUT_GIT_TOKEN" | docker login "$REGISTRY" -u "${GITHUB_ACTOR}" --password-stdin
|
|
fi
|
|
echo "📤 Pushing image to ${IMAGE_NAME}..."
|
|
docker push "$IMAGE_NAME"
|
|
else
|
|
echo "⏭️ Skipping image push (push_image=false)"
|
|
fi
|
|
|
|
# 8. Deploy to TrueNAS
|
|
if [ "${INPUT_DEPLOY:-true}" = "true" ]; then
|
|
if [ -z "${INPUT_SSH_KEY:-}" ]; then
|
|
echo "⚠️ No ssh_key provided for TrueNAS. Skipping remote deployment step."
|
|
else
|
|
HOST="${INPUT_HOST:-10.0.0.218}"
|
|
USER="${INPUT_USER:-root}"
|
|
|
|
echo "🚢 Deploying to TrueNAS SCALE (${HOST})..."
|
|
mkdir -p ~/.ssh
|
|
echo "$INPUT_SSH_KEY" > ~/.ssh/truenas_deploy_key
|
|
chmod 600 ~/.ssh/truenas_deploy_key
|
|
ssh-keyscan -H "$HOST" >> ~/.ssh/known_hosts 2>/dev/null
|
|
|
|
REMOTE_DIR="/mnt/jupiter/apps/${APP_NAME}"
|
|
echo "Ensuring remote directory ${REMOTE_DIR} on ${HOST}..."
|
|
ssh -i ~/.ssh/truenas_deploy_key -o StrictHostKeyChecking=no "${USER}@${HOST}" "mkdir -p ${REMOTE_DIR}"
|
|
|
|
echo "Syncing docker-compose.yml to ${REMOTE_DIR}..."
|
|
scp -i ~/.ssh/truenas_deploy_key -o StrictHostKeyChecking=no "${BUILD_STAGING}/docker-compose.yml" "${USER}@${HOST}:${REMOTE_DIR}/docker-compose.yml"
|
|
|
|
echo "Pulling latest image and restarting stack..."
|
|
ssh -i ~/.ssh/truenas_deploy_key -o StrictHostKeyChecking=no "${USER}@${HOST}" "cd ${REMOTE_DIR} && docker compose pull && docker compose up -d --remove-orphans"
|
|
|
|
rm -f ~/.ssh/truenas_deploy_key
|
|
echo "✅ Deployment complete! Accessible at: https://${DOMAIN}"
|
|
fi
|
|
else
|
|
echo "⏭️ Skipping deployment step (deploy=false)"
|
|
fi
|