Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@ output/
_ul
nul
/tmpclaude-1fc2-cwd
/webapp/job_storage/
76 changes: 76 additions & 0 deletions deploy/update.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/bin/bash
# WriteBot Update Script for Docker Deployments
# Usage: ./deploy/update.sh [--production]

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }

# Determine compose file and container name
if [ "$1" == "--production" ]; then

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use single equals sign for string comparison in bash instead of double equals for better POSIX compliance and portability. While == works in bash, = is the standard POSIX comparison operator and is more portable across different shell implementations.

Suggested change
if [ "$1" == "--production" ]; then
if [ "$1" = "--production" ]; then

Copilot uses AI. Check for mistakes.
COMPOSE_FILE="docker-compose.production.yml"
CONTAINER="writebot-app-production"
log_info "Using production configuration"
else
COMPOSE_FILE="docker-compose.yml"
CONTAINER="writebot-app"
log_info "Using development configuration"
fi

# Check if container is running
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then
log_error "Container ${CONTAINER} is not running!"
log_info "Start it with: docker compose -f ${COMPOSE_FILE} up -d"
exit 1
fi

log_info "Starting update process..."

# Step 1: Pull latest code (if using git)
if [ -d ".git" ]; then
log_info "Pulling latest code..."
git pull origin main

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The git pull command hardcodes the branch name as "main", but some repositories may use "master" or other branch names. Consider using git pull without specifying the branch, which will pull the current branch's tracking remote, or make the branch name configurable through an environment variable or command-line argument.

Suggested change
git pull origin main
if [ -n "${UPDATE_BRANCH:-}" ]; then
git pull origin "${UPDATE_BRANCH}"
else
git pull
fi

Copilot uses AI. Check for mistakes.
fi

# Step 2: Rebuild containers with new code
log_info "Rebuilding containers..."
docker compose -f ${COMPOSE_FILE} build

# Step 3: Run database migrations
log_info "Running database migrations..."
docker exec ${CONTAINER} flask db upgrade

# Step 4: Restart services with new code
log_info "Restarting services..."
docker compose -f ${COMPOSE_FILE} up -d

Comment on lines +47 to +54

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration command runs on a container that may still be using the old code. The sequence should be: (1) rebuild containers, (2) restart services with new code, (3) run migrations on the updated container. Currently, migrations run at step 3 (line 49) but the services are restarted at step 4 (line 53), which means migrations could run against old code if the container hasn't been recreated yet. Consider moving the migration step after the service restart.

Suggested change
# Step 3: Run database migrations
log_info "Running database migrations..."
docker exec ${CONTAINER} flask db upgrade
# Step 4: Restart services with new code
log_info "Restarting services..."
docker compose -f ${COMPOSE_FILE} up -d
# Step 3: Restart services with new code
log_info "Restarting services..."
docker compose -f ${COMPOSE_FILE} up -d
# Step 4: Run database migrations
log_info "Running database migrations..."
docker exec ${CONTAINER} flask db upgrade

Copilot uses AI. Check for mistakes.
# Step 5: Verify health
log_info "Waiting for health check..."
sleep 10

if docker exec ${CONTAINER} python -c "import requests; r = requests.get('http://localhost:5000/api/health', timeout=5); exit(0 if r.status_code == 200 else 1)" 2>/dev/null; then

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The health check script uses the requests library, but requests is not listed as a dependency in requirements-ngc.txt. The health check command on line 59 runs import requests; r = requests.get(...), which will fail if the requests library is not installed in the container. Either add requests to the requirements file or modify the health check to use a different approach, such as using curl or Python's built-in urllib.

Suggested change
if docker exec ${CONTAINER} python -c "import requests; r = requests.get('http://localhost:5000/api/health', timeout=5); exit(0 if r.status_code == 200 else 1)" 2>/dev/null; then
if docker exec ${CONTAINER} python -c "import urllib.request, sys; req = urllib.request.Request('http://localhost:5000/api/health'); r = urllib.request.urlopen(req, timeout=5); sys.exit(0 if r.getcode() == 200 else 1)" 2>/dev/null; then

Copilot uses AI. Check for mistakes.
log_info "Health check passed!"
else
log_warn "Health check failed - check logs with: docker logs ${CONTAINER}"
fi

# Step 6: Restart Celery workers (if using job queue)
if docker ps --format '{{.Names}}' | grep -q "writebot-celery"; then

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grep pattern "writebot-celery" will not match the actual Celery container names. In the development docker-compose.yml, the containers are named "writebot-celery-worker" and "writebot-celery-beat", not "writebot-celery". The pattern should be changed to match the actual container names, for example using "writebot-celery-worker" or a pattern like "writebot-celery-.*" to match both worker and beat containers.

Suggested change
if docker ps --format '{{.Names}}' | grep -q "writebot-celery"; then
if docker ps --format '{{.Names}}' | grep -q "writebot-celery-"; then

Copilot uses AI. Check for mistakes.
log_info "Restarting Celery workers..."
docker compose -f ${COMPOSE_FILE} restart celery-worker celery-beat 2>/dev/null || true
Comment on lines +65 to +68

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script doesn't account for Docker Compose profiles used in production. The docker-compose.production.yml file uses profiles: [celery] for celery-worker and celery-beat services, which means these services won't be started by default with docker compose up -d. To restart Celery services in production when using profiles, you need to specify the profile explicitly, e.g., docker compose -f ${COMPOSE_FILE} --profile celery restart celery-worker celery-beat. Without this, the restart command on line 68 will fail for production deployments that use Celery.

Copilot uses AI. Check for mistakes.
fi

log_info "Update complete!"
echo ""
echo "Useful commands:"
echo " View logs: docker logs -f ${CONTAINER}"
echo " Check status: docker compose -f ${COMPOSE_FILE} ps"
echo " Run shell: docker exec -it ${CONTAINER} bash"
13 changes: 12 additions & 1 deletion requirements-ngc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ openpyxl

# Flask dependencies
flask>=2.3.2
itsdangerous>=2.1.0
python-dotenv>=1.0.0
Flask-Login>=0.6.2
Flask-SQLAlchemy>=3.0.0
Flask-WTF>=1.1.1
Expand All @@ -22,9 +24,18 @@ Flask-Caching>=2.0.0
Flask-Limiter>=3.3.0
Flask-Minify>=0.42
flask-assets>=2.0
Flask-Migrate>=4.0.0

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the direct alembic dependency while the codebase still directly imports and uses it could cause issues. Files like webapp/init_db.py and webapp/manage_migrations.py import directly from alembic (e.g., from alembic.config import Config, from alembic import command). While Flask-Migrate includes alembic as a transitive dependency, relying on transitive dependencies is fragile - if Flask-Migrate changes its alembic dependency or pins a different version, the direct imports in your code could break. Consider keeping the explicit alembic>=1.13.0 dependency, or refactor the code to use Flask-Migrate's API instead of calling alembic directly.

Suggested change
Flask-Migrate>=4.0.0
Flask-Migrate>=4.0.0
alembic>=1.13.0

Copilot uses AI. Check for mistakes.
rjsmin
rcssmin
alembic>=1.13.0
gunicorn>=21.2.0
psycopg2-binary>=2.9.9
redis>=5.0.0

# Job Queue & Email
Flask-Mailman>=1.0.0
celery[redis]>=5.3.0

# Monitoring & Logging
structlog>=24.0.0
sentry-sdk[flask,celery]>=2.0.0
flower>=2.0.0
Loading