mirror of
https://github.com/runyanjake/jakesphotos.git
synced 2026-06-25 07:14:53 -07:00
Remove extra jenkinsfile concerns
This commit is contained in:
parent
9f0a963f37
commit
851b67a0f5
4
.dockerignore
Normal file
4
.dockerignore
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
build
|
||||
src/generated
|
||||
.git
|
||||
38
Dockerfile
38
Dockerfile
@ -1,32 +1,28 @@
|
||||
# Use the official Node.js image as a build stage
|
||||
FROM node:20-alpine AS build
|
||||
|
||||
# Set the working directory
|
||||
# Shared dependency layer, reused by the ci and build targets.
|
||||
FROM node:20-alpine AS deps
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Copy package.json and package-lock.json
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy the rest of the application code
|
||||
# Lint + content validation target, used by CI (docker build --target ci).
|
||||
FROM deps AS ci
|
||||
COPY . .
|
||||
RUN node scripts/build-content.js && npx eslint src --ignore-pattern 'src/generated/**' --max-warnings=0
|
||||
|
||||
# Build the React application
|
||||
# Production build target: generates content then bundles the static site.
|
||||
FROM deps AS build
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Use a lightweight web server to serve the build files
|
||||
# Serve the static bundle with nginx.
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy the build files from the previous stage
|
||||
COPY --from=build /app/build /usr/share/nginx/html
|
||||
|
||||
# Use custom nginx config so client-side routes (e.g. /contact, /about) fall back to index.html
|
||||
# Custom config so client-side routes fall back to index.html.
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Expose port 80
|
||||
EXPOSE 80
|
||||
# Liveness probe. Use 127.0.0.1 (not localhost): nginx listens IPv4-only and
|
||||
# busybox wget would resolve localhost to ::1 and get connection refused.
|
||||
HEALTHCHECK --interval=5s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget -q -O /dev/null http://127.0.0.1:80/ || exit 1
|
||||
|
||||
# Start Nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
79
Jenkinsfile
vendored
79
Jenkinsfile
vendored
@ -4,8 +4,8 @@ pipeline {
|
||||
environment {
|
||||
DISCORD_WEBHOOK = credentials('discord-pws-builds-channel-webhook')
|
||||
|
||||
COMPOSE_PROJECT = 'jakesphotos'
|
||||
APP_HOST = 'jakesphotos.whitney.rip'
|
||||
COMPOSE_SERVICE = 'portfolio'
|
||||
CONTAINER_NAME = 'jakesphotos'
|
||||
}
|
||||
|
||||
options {
|
||||
@ -36,59 +36,51 @@ pipeline {
|
||||
|
||||
stage('Lint & Type-check') {
|
||||
steps {
|
||||
// Run checks in a clean, throwaway container so nothing leaks from the agent.
|
||||
sh '''
|
||||
set -e
|
||||
docker run --rm -v "$PWD":/app -w /app node:20-alpine sh -c '
|
||||
set -e
|
||||
npm ci
|
||||
node scripts/build-content.js
|
||||
npx eslint src --max-warnings=0
|
||||
' || { echo "ERROR: static quality checks failed"; exit 1; }
|
||||
'''
|
||||
// Static checks run inside the image's ci target; nothing touches the agent.
|
||||
sh 'docker build --target ci -t ${CONTAINER_NAME}-ci:${BUILD_NUMBER} .'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Teardown') {
|
||||
steps {
|
||||
sh '''
|
||||
set -e
|
||||
docker compose down --remove-orphans || {
|
||||
echo "ERROR: failed to tear down previous deployment"; exit 1
|
||||
}
|
||||
set -eu
|
||||
docker compose down --remove-orphans || true
|
||||
# container_name is fixed, so a stale container can survive "down"
|
||||
# and then block "up" with a name conflict; reap it explicitly.
|
||||
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build & Deploy') {
|
||||
steps {
|
||||
// The React build consumes only static content/, so there are no build-time secrets to inject.
|
||||
sh '''
|
||||
set -e
|
||||
docker compose build || { echo "ERROR: build failed"; exit 1; }
|
||||
docker compose up -d || { echo "ERROR: deploy failed"; exit 1; }
|
||||
'''
|
||||
// No build-time secrets: the React build consumes only static content/.
|
||||
sh 'docker compose up -d --build'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Health Check') {
|
||||
steps {
|
||||
sh '''
|
||||
set -e
|
||||
echo "Waiting for $COMPOSE_PROJECT to report live..."
|
||||
for i in $(seq 1 30); do
|
||||
running=$(docker inspect -f '{{.State.Running}}' "$COMPOSE_PROJECT" 2>/dev/null || echo false)
|
||||
if [ "$running" != "true" ]; then
|
||||
echo "ERROR: container $COMPOSE_PROJECT is not running"
|
||||
docker logs --tail 50 "$COMPOSE_PROJECT" || true
|
||||
exit 1
|
||||
fi
|
||||
if docker exec "$COMPOSE_PROJECT" wget -q -O /dev/null http://localhost:80/; then
|
||||
echo "$COMPOSE_PROJECT is live."; exit 0
|
||||
fi
|
||||
set -eu
|
||||
cid="$(docker compose ps -q "$COMPOSE_SERVICE")"
|
||||
[ -n "$cid" ] || { echo "ERROR: $COMPOSE_SERVICE container not found" >&2; exit 1; }
|
||||
|
||||
# Wait for the image's HEALTHCHECK to report healthy, failing fast on terminal states.
|
||||
deadline=$(( $(date +%s) + 90 ))
|
||||
while :; do
|
||||
status="$(docker inspect -f '{{.State.Status}}' "$cid")"
|
||||
health="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$cid")"
|
||||
[ "$status" = "running" ] && [ "$health" = "healthy" ] && break
|
||||
[ "$health" = "unhealthy" ] && { echo "ERROR: $COMPOSE_SERVICE reported unhealthy" >&2; exit 1; }
|
||||
case "$status" in
|
||||
exited|dead) echo "ERROR: $COMPOSE_SERVICE container $status before becoming healthy" >&2; exit 1 ;;
|
||||
esac
|
||||
[ "$(date +%s)" -ge "$deadline" ] && { echo "ERROR: timed out waiting for healthy (status=$status, health=$health)" >&2; exit 1; }
|
||||
sleep 2
|
||||
done
|
||||
echo "ERROR: $COMPOSE_PROJECT did not become healthy in time"; exit 1
|
||||
echo "$COMPOSE_SERVICE healthy"
|
||||
'''
|
||||
}
|
||||
}
|
||||
@ -96,15 +88,14 @@ pipeline {
|
||||
stage('Smoke Test') {
|
||||
steps {
|
||||
sh '''
|
||||
set -e
|
||||
echo "Smoke testing https://$APP_HOST/ ..."
|
||||
body=$(curl -fsS --retry 5 --retry-delay 3 "https://$APP_HOST/") || {
|
||||
echo "ERROR: request to https://$APP_HOST/ failed"; exit 1
|
||||
}
|
||||
echo "$body" | grep -q 'id="root"' || {
|
||||
echo "ERROR: response did not contain expected app markup"; exit 1
|
||||
}
|
||||
echo "Smoke test passed."
|
||||
set -eu
|
||||
cid="$(docker compose ps -q "$COMPOSE_SERVICE")"
|
||||
[ -n "$cid" ] || { echo "ERROR: $COMPOSE_SERVICE container not found" >&2; exit 1; }
|
||||
|
||||
# Real request from inside the container: GET / must serve the SPA shell.
|
||||
body="$(docker exec "$cid" wget -q -O - http://127.0.0.1:80/)" || { echo "ERROR: GET / did not return a successful response" >&2; exit 1; }
|
||||
echo "$body" | grep -q 'id="root"' || { echo "ERROR: GET / response missing expected app markup" >&2; exit 1; }
|
||||
echo "smoke test passed"
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user