pipeline { agent any options { timeout(time: 20, unit: 'MINUTES') timestamps() buildDiscarder(logRotator(numToKeepStr: '10')) disableConcurrentBuilds() } triggers { cron('H/30 * * * *') } environment { VENV_DIR = 'venv' PIP_CACHE_DIR = "${WORKSPACE}/.pip-cache" PYTHONUNBUFFERED = '1' } stages { stage('Checkout Code') { steps { checkout scm } } stage('Setup Python & Install Dependencies') { steps { sh ''' set -euxo pipefail python3 -m venv "${VENV_DIR}" # Always use venv python explicitly "${VENV_DIR}/bin/python" -m pip install --upgrade pip wheel setuptools "${VENV_DIR}/bin/pip" install --cache-dir "${PIP_CACHE_DIR}" -U \ atproto \ tweety-ns \ playwright \ httpx \ arrow \ python-dotenv \ moviepy \ fastfeedparser \ beautifulsoup4 \ charset-normalizer \ Pillow # Verify required imports "${VENV_DIR}/bin/python" -c "import fastfeedparser; print('fastfeedparser OK')" "${VENV_DIR}/bin/python" -c "import moviepy; print('moviepy OK')" # Check FFmpeg ffmpeg -version # Install Playwright browser binaries in this workspace environment "${VENV_DIR}/bin/python" -m playwright install chromium ''' } } stage('Run Script') { steps { withCredentials([ string(credentialsId: 'TWITTER_USERNAME', variable: 'TWITTER_USERNAME'), string(credentialsId: 'TWITTER_PASSWORD', variable: 'TWITTER_PASSWORD'), string(credentialsId: 'TWITTER_3CAT_EMAIL', variable: 'TWITTER_3CAT_EMAIL'), string(credentialsId: 'TWITTER_3CAT_HANDLE', variable: 'TWITTER_3CAT_HANDLE'), string(credentialsId: 'BSKY_3CAT_HANDLE', variable: 'BSKY_3CAT_HANDLE'), string(credentialsId: 'BSKY_3CAT_APP_PASSWORD', variable: 'BSKY_3CAT_APP_PASSWORD') ]) { sh ''' set -euxo pipefail "${VENV_DIR}/bin/python" twitter2bsky_daemon.py \ --twitter-username "$TWITTER_USERNAME" \ --twitter-password "$TWITTER_PASSWORD" \ --twitter-email "$TWITTER_3CAT_EMAIL" \ --twitter-handle "$TWITTER_3CAT_HANDLE" \ --bsky-handle "$BSKY_3CAT_HANDLE" \ --bsky-password "$BSKY_3CAT_APP_PASSWORD" ''' } } } } post { always { // Optional: keep logs/artifacts if your script writes any archiveArtifacts artifacts: '*.log, *.json', allowEmptyArchive: true } } }