remove monolith

This commit is contained in:
Anthony Stirling 2025-07-17 12:18:57 +01:00
parent 117d906be3
commit cba59a77ab
9 changed files with 173 additions and 288 deletions

View File

@ -2,7 +2,7 @@ name: Auto PR V2 Deployment
on: on:
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened, closed]
permissions: permissions:
@ -12,6 +12,7 @@ permissions:
jobs: jobs:
check-pr: check-pr:
if: github.event.action != 'closed'
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
should_deploy: ${{ steps.check-conditions.outputs.should_deploy }} should_deploy: ${{ steps.check-conditions.outputs.should_deploy }}
@ -212,13 +213,62 @@ jobs:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_API }} password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push V2 monolith image - name: Get commit hashes for frontend and backend
id: commit-hashes
run: |
# Get commit hash for frontend folder
FRONTEND_HASH=$(git log -1 --format="%H" -- frontend/ || echo "no-frontend-changes")
echo "frontend_hash=$FRONTEND_HASH" >> $GITHUB_OUTPUT
# Get commit hash for backend folder (excluding frontend)
BACKEND_HASH=$(git log -1 --format="%H" -- . ':!frontend/' || echo "no-backend-changes")
echo "backend_hash=$BACKEND_HASH" >> $GITHUB_OUTPUT
# Short hashes for tags
echo "frontend_short=${FRONTEND_HASH:0:8}" >> $GITHUB_OUTPUT
echo "backend_short=${BACKEND_HASH:0:8}" >> $GITHUB_OUTPUT
- name: Check if frontend image exists
id: check-frontend
run: |
if docker manifest inspect ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-frontend-${{ steps.commit-hashes.outputs.frontend_short }} >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Frontend image already exists, skipping build"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Frontend image needs to be built"
fi
- name: Check if backend image exists
id: check-backend
run: |
if docker manifest inspect ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-backend-${{ steps.commit-hashes.outputs.backend_short }} >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Backend image already exists, skipping build"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Backend image needs to be built"
fi
- name: Build and push V2 frontend image
if: steps.check-frontend.outputs.exists == 'false'
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with: with:
context: . context: .
file: ./docker/monolith/Dockerfile file: ./docker/frontend/Dockerfile
push: true push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-pr-${{ needs.check-pr.outputs.pr_number }} tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-frontend-${{ steps.commit-hashes.outputs.frontend_short }}
build-args: VERSION_TAG=v2-alpha
platforms: linux/amd64
- name: Build and push V2 backend image
if: steps.check-backend.outputs.exists == 'false'
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: ./docker/backend/Dockerfile
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-backend-${{ steps.commit-hashes.outputs.backend_short }}
build-args: VERSION_TAG=v2-alpha build-args: VERSION_TAG=v2-alpha
platforms: linux/amd64 platforms: linux/amd64
@ -233,16 +283,17 @@ jobs:
run: | run: |
# Use same port strategy as regular PRs - just the PR number # Use same port strategy as regular PRs - just the PR number
V2_PORT=${{ needs.check-pr.outputs.pr_number }} V2_PORT=${{ needs.check-pr.outputs.pr_number }}
BACKEND_PORT=$((V2_PORT + 10000)) # Backend on higher port to avoid conflicts
# Create docker-compose for V2 monolith # Create docker-compose for V2 with separate frontend and backend
cat > docker-compose.yml << EOF cat > docker-compose.yml << EOF
version: '3.3' version: '3.3'
services: services:
stirling-pdf-v2: stirling-pdf-v2-backend:
container_name: stirling-pdf-v2-pr-${{ needs.check-pr.outputs.pr_number }} container_name: stirling-pdf-v2-backend-pr-${{ needs.check-pr.outputs.pr_number }}
image: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-pr-${{ needs.check-pr.outputs.pr_number }} image: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-backend-${{ steps.commit-hashes.outputs.backend_short }}
ports: ports:
- "${V2_PORT}:80" # Frontend port (same as regular PRs) - "${BACKEND_PORT}:8080" # Backend API port
volumes: volumes:
- /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/data:/usr/share/tessdata:rw - /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/data:/usr/share/tessdata:rw
- /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/config:/configs:rw - /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/config:/configs:rw
@ -258,6 +309,17 @@ jobs:
METRICS_ENABLED: "true" METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "false" SYSTEM_GOOGLEVISIBILITY: "false"
restart: on-failure:5 restart: on-failure:5
stirling-pdf-v2-frontend:
container_name: stirling-pdf-v2-frontend-pr-${{ needs.check-pr.outputs.pr_number }}
image: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-frontend-${{ steps.commit-hashes.outputs.frontend_short }}
ports:
- "${V2_PORT}:80" # Frontend port (same as regular PRs)
environment:
VITE_API_BASE_URL: "http://${{ secrets.VPS_HOST }}:${BACKEND_PORT}"
depends_on:
- stirling-pdf-v2-backend
restart: on-failure:5
EOF EOF
# Deploy to VPS # Deploy to VPS
@ -325,3 +387,99 @@ jobs:
body: commentBody body: commentBody
}); });
cleanup-v2-deployment:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup GitHub App Bot
if: github.actor != 'dependabot[bot]'
id: setup-bot
uses: ./.github/actions/setup-bot
continue-on-error: true
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Clean up V2 deployment comments
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ steps.setup-bot.outputs.token }}
script: |
const { owner, repo } = context.repo;
const prNumber = ${{ github.event.pull_request.number }};
// Find and delete V2 deployment comments
const { data: comments } = await github.rest.issues.listComments({
owner,
repo,
issue_number: prNumber
});
const v2Comments = comments.filter(c =>
c.body?.includes("## 🚀 V2 Auto-Deployment Complete!") &&
c.user?.type === "Bot"
);
for (const comment of v2Comments) {
await github.rest.issues.deleteComment({
owner,
repo,
comment_id: comment.id
});
console.log(`Deleted V2 deployment comment (ID: ${comment.id})`);
}
- name: Set up SSH
run: |
mkdir -p ~/.ssh/
echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key
sudo chmod 600 ../private.key
- name: Cleanup V2 deployment
run: |
ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << 'ENDSSH'
if [ -d "/stirling/V2-PR-${{ github.event.pull_request.number }}" ]; then
echo "Found V2 PR directory, proceeding with cleanup..."
# Stop and remove V2 containers
cd /stirling/V2-PR-${{ github.event.pull_request.number }}
docker-compose down || true
# Go back to root before removal
cd /
# Remove V2 PR-specific directories
rm -rf /stirling/V2-PR-${{ github.event.pull_request.number }}
# Clean up V2 containers by name (in case compose cleanup missed them)
docker rm -f stirling-pdf-v2-frontend-pr-${{ github.event.pull_request.number }} || true
docker rm -f stirling-pdf-v2-backend-pr-${{ github.event.pull_request.number }} || true
echo "V2 cleanup completed"
else
echo "V2 PR directory not found, nothing to clean up"
fi
# Note: We don't remove the commit-based images since they can be reused across PRs
# Only remove PR-specific containers and directories
ENDSSH
- name: Cleanup temporary files
if: always()
run: |
rm -f ../private.key
continue-on-error: true

View File

@ -14,15 +14,10 @@ docker/
│ ├── Dockerfile # React/Vite frontend with nginx │ ├── Dockerfile # React/Vite frontend with nginx
│ ├── nginx.conf # Nginx configuration │ ├── nginx.conf # Nginx configuration
│ └── entrypoint.sh # Dynamic backend URL setup │ └── entrypoint.sh # Dynamic backend URL setup
├── monolith/ # Single container setup
│ ├── Dockerfile # Combined frontend + backend
│ ├── nginx-monolith.conf # Nginx config for monolith
│ └── start-monolith.sh # Startup script
└── compose/ # Docker Compose files └── compose/ # Docker Compose files
├── docker-compose.yml # Standard setup ├── docker-compose.yml # Standard setup
├── docker-compose.ultra-lite.yml # Ultra-lite setup ├── docker-compose.ultra-lite.yml # Ultra-lite setup
├── docker-compose.fat.yml # Full-featured setup └── docker-compose.fat.yml # Full-featured setup
└── docker-compose.monolith.yml # Single container setup
``` ```
## Usage ## Usage
@ -42,12 +37,6 @@ docker-compose -f docker/compose/docker-compose.ultra-lite.yml up --build
docker-compose -f docker/compose/docker-compose.fat.yml up --build docker-compose -f docker/compose/docker-compose.fat.yml up --build
``` ```
### Single Container (Monolith)
```bash
# Single container with both frontend and backend
docker-compose -f docker/compose/docker-compose.monolith.yml up --build
```
## Access Points ## Access Points
@ -57,7 +46,7 @@ docker-compose -f docker/compose/docker-compose.monolith.yml up --build
## Configuration ## Configuration
- **Backend URL**: Set `BACKEND_URL` environment variable for custom backend locations - **Backend URL**: Set `VITE_API_BASE_URL` environment variable for custom backend locations
- **Custom Ports**: Modify port mappings in docker-compose files - **Custom Ports**: Modify port mappings in docker-compose files
- **Memory Limits**: Adjust memory limits per variant (2G ultra-lite, 4G standard, 6G fat) - **Memory Limits**: Adjust memory limits per variant (2G ultra-lite, 4G standard, 6G fat)

View File

@ -1,40 +0,0 @@
services:
stirling-pdf-monolith:
build:
context: ../..
dockerfile: docker/monolith/Dockerfile
container_name: stirling-pdf-monolith
restart: on-failure:5
deploy:
resources:
limits:
memory: 4G
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:80/ && curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP'"]
interval: 10s
timeout: 15s
retries: 16
ports:
- "3000:80" # Frontend access
- "8080:8080" # Direct backend access (for debugging)
volumes:
- ../../stirling/latest/data:/usr/share/tessdata:rw
- ../../stirling/latest/config:/configs:rw
- ../../stirling/latest/logs:/logs:rw
environment:
DISABLE_ADDITIONAL_FEATURES: "true"
SECURITY_ENABLELOGIN: "false"
LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Single container Stirling-PDF
UI_APPNAMENAVBAR: Stirling-PDF Monolith
SYSTEM_MAXFILESIZE: "100"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "true"
SHOW_SURVEY: "true"
volumes:
stirling-data:
stirling-config:
stirling-logs:

View File

@ -32,7 +32,7 @@ RUN chmod +x /entrypoint.sh
EXPOSE 80 EXPOSE 80
# Environment variables for flexibility # Environment variables for flexibility
ENV BACKEND_URL=http://backend:8080 ENV VITE_API_BASE_URL=http://backend:8080
# Use custom entrypoint # Use custom entrypoint
ENTRYPOINT ["/entrypoint.sh"] ENTRYPOINT ["/entrypoint.sh"]

View File

@ -1,10 +1,10 @@
#!/bin/sh #!/bin/sh
# Set default backend URL if not provided # Set default backend URL if not provided
BACKEND_URL=${BACKEND_URL:-"http://backend:8080"} VITE_API_BASE_URL=${VITE_API_BASE_URL:-"http://backend:8080"}
# Replace the placeholder in nginx.conf with the actual backend URL # Replace the placeholder in nginx.conf with the actual backend URL
sed -i "s|\${BACKEND_URL}|${BACKEND_URL}|g" /etc/nginx/nginx.conf sed -i "s|\${VITE_API_BASE_URL}|${VITE_API_BASE_URL}|g" /etc/nginx/nginx.conf
# Start nginx # Start nginx
exec nginx -g "daemon off;" exec nginx -g "daemon off;"

View File

@ -28,7 +28,7 @@ http {
# Proxy API calls to backend # Proxy API calls to backend
location /api/ { location /api/ {
proxy_pass ${BACKEND_URL}/api/; proxy_pass ${VITE_API_BASE_URL}/api/;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

View File

@ -1,153 +0,0 @@
# Monolith Dockerfile - Frontend + Backend in same container
# Build the application
FROM gradle:8.14-jdk21 AS build
COPY build.gradle .
COPY settings.gradle .
COPY gradlew .
COPY gradle gradle/
COPY app/core/build.gradle core/.
COPY app/common/build.gradle common/.
COPY app/proprietary/build.gradle proprietary/.
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
# Set the working directory
WORKDIR /app
# Copy the entire project to the working directory
COPY . .
# Build the application with DISABLE_ADDITIONAL_FEATURES=true
RUN DISABLE_ADDITIONAL_FEATURES=true \
STIRLING_PDF_DESKTOP_UI=false \
./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube
# Build frontend
FROM node:20-alpine AS frontend-build
WORKDIR /app/frontend
# Copy frontend package files
COPY frontend/package*.json ./
# Install frontend dependencies
RUN npm ci
# Copy frontend source
COPY frontend/ ./
# Build frontend
RUN npm run build
# Main stage - Backend with frontend files
FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be023728e11715
# Copy necessary files
COPY scripts /scripts
COPY pipeline /pipeline
COPY app/core/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
# first /app directory is for the build stage, second is for the final image
COPY --from=build /app/app/core/build/libs/*.jar app.jar
# Copy built frontend files
COPY --from=frontend-build /app/frontend/dist /usr/share/nginx/html
ARG VERSION_TAG
LABEL org.opencontainers.image.title="Stirling-PDF Monolith"
LABEL org.opencontainers.image.description="Single container with both frontend and backend for Stirling-PDF"
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.vendor="Stirling-Tools"
LABEL org.opencontainers.image.url="https://www.stirlingpdf.com"
LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
LABEL maintainer="Stirling-Tools"
LABEL org.opencontainers.image.authors="Stirling-Tools"
LABEL org.opencontainers.image.version="${VERSION_TAG}"
LABEL org.opencontainers.image.keywords="PDF, manipulation, monolith, single-container"
# Set Environment Variables
ENV DISABLE_ADDITIONAL_FEATURES=true \
VERSION_TAG=$VERSION_TAG \
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
JAVA_CUSTOM_OPTS="" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
PGID=1000 \
UMASK=022 \
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
UNO_PATH=/usr/lib/libreoffice/program \
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
PATH=$PATH:/opt/venv/bin \
STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf \
TMPDIR=/tmp/stirling-pdf \
TEMP=/tmp/stirling-pdf \
TMP=/tmp/stirling-pdf
# Install nginx and all dependencies
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
apk upgrade --no-cache -a && \
apk add --no-cache \
ca-certificates \
tzdata \
tini \
bash \
curl \
shadow \
su-exec \
openssl \
openssl-dev \
openjdk21-jre \
nginx \
# Doc conversion
gcompat \
libc6-compat \
libreoffice \
# pdftohtml
poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced features)
unpaper \
tesseract-ocr-data-eng \
tesseract-ocr-data-chi_sim \
tesseract-ocr-data-deu \
tesseract-ocr-data-fra \
tesseract-ocr-data-por \
ocrmypdf \
# CV
py3-opencv \
python3 \
py3-pip \
py3-pillow@testing \
py3-pdf2image@testing && \
python3 -m venv /opt/venv && \
/opt/venv/bin/pip install --upgrade pip setuptools && \
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
fc-cache -f -v && \
chmod +x /scripts/* && \
chmod +x /scripts/init.sh && \
# User permissions
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline /tmp/stirling-pdf && \
chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/nginx/html
# Copy nginx configuration for monolith
COPY docker/monolith/nginx-monolith.conf /etc/nginx/nginx.conf
# Copy startup script
COPY docker/monolith/start-monolith.sh /start-monolith.sh
RUN chmod +x /start-monolith.sh
# Expose both ports
EXPOSE 80 8080
# Set user and run command
ENTRYPOINT ["tini", "--"]
CMD ["/start-monolith.sh"]

View File

@ -1,49 +0,0 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html index.htm;
# Handle client-side routing - support subpaths
location / {
try_files $uri $uri/ /index.html;
}
# Proxy API calls to backend running on same container
location /api/ {
proxy_pass http://localhost:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
}

View File

@ -1,20 +0,0 @@
#!/bin/bash
# Start the Java backend in the background
echo "Starting Java backend..."
su-exec stirlingpdfuser:stirlingpdfgroup bash -c "
cd /home/stirlingpdfuser && \
java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar &
/opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1 &
"
# Wait for backend to start
echo "Waiting for backend to start..."
until curl -f http://localhost:8080/api/v1/info/status >/dev/null 2>&1; do
sleep 2
done
echo "Backend started, starting nginx..."
# Start nginx in the foreground
nginx -g "daemon off;"