Skip to content

Keep Streamlit App Alive #2796

Keep Streamlit App Alive

Keep Streamlit App Alive #2796

Workflow file for this run

name: Keep Streamlit App Alive
# This workflow pings the Streamlit app to prevent it from going to sleep
# due to inactivity on Streamlit Community Cloud.
#
# Features:
# - Runs every 30 minutes with randomization (pings every ~30-40 minutes)
# - Adds random delay to vary timing and make logs look more natural
# - Tolerates failures without breaking the workflow
#
# To use in another repository:
# 1. Copy this file to .github/workflows/
# 2. Update the STREAMLIT_APP_URL below with your app's URL
# 3. Adjust the schedule if needed (currently runs every 30 minutes)
# 4. Adjust DELAY calculation to change random window (currently 0-20 min)
on:
schedule:
# Run every 30 minutes with randomization (pings roughly every 30-40 minutes)
- cron: '*/30 * * * *'
workflow_dispatch: # Allow manual trigger for testing
env:
# UPDATE THIS: Replace with your Streamlit app URL
STREAMLIT_APP_URL: 'https://f1analysis-app.streamlit.app/'
jobs:
ping-app:
runs-on: ubuntu-latest
timeout-minutes: 25 # Allow for 10-minute random delay + ping time + browser installation
steps:
- name: Random delay (0-10 minutes)
run: |
# Add random delay to vary timing and make logs look more natural
DELAY=$((RANDOM % 600)) # 0-600 seconds (0-10 minutes)
echo "Waiting ${DELAY} seconds before ping..."
sleep $DELAY
- name: Ping Streamlit App
id: ping
run: |
echo "Pinging Streamlit app at: ${{ env.STREAMLIT_APP_URL }}"
echo " Time: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
# Make HTTP request with timeout (use generic browser-like User-Agent)
USER_AGENTS=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
RANDOM_UA=${USER_AGENTS[$RANDOM % ${#USER_AGENTS[@]}]}
# Save body to temp file and capture code for diagnostics
BODY_FILE=$(mktemp)
RESPONSE=$(curl -s -o "$BODY_FILE" -w "%{http_code}" \
--max-time 30 \
--retry 2 \
--retry-delay 5 \
--user-agent "$RANDOM_UA" \
"${{ env.STREAMLIT_APP_URL }}")
echo " Response code: $RESPONSE"
echo " Response body (first 400 chars):"
head -c 400 "$BODY_FILE" | sed -e 's/[\r\n]/ /g'
echo ""
rm -f "$BODY_FILE"
# Export response as step output for later conditional steps
echo "response=$RESPONSE" >> $GITHUB_OUTPUT
# Decide whether the app needs a wake (treat only 2xx as healthy)
NEEDS_WAKE=0
if [ "$RESPONSE" -lt 200 ] || [ "$RESPONSE" -ge 300 ]; then
NEEDS_WAKE=1
fi
# Detect auth/login redirect in the response body and add an annotation
if grep -E -i 'share.streamlit.io|/\-/login' "$BODY_FILE" >/dev/null 2>&1; then
echo "::warning title=Keep-alive ping::Auth/login redirect detected (HTTP $RESPONSE). The ping was redirected to Streamlit auth (e.g., share.streamlit.io or /-/login). If this is unexpected, check your app visibility and access settings. Playwright will attempt to visit the page to wake it. (No email will be sent.)"
NEEDS_WAKE=1
fi
# Export whether we need to run Playwright (useful for future steps)
echo "needs_wake=$NEEDS_WAKE" >> $GITHUB_OUTPUT
if [ "$NEEDS_WAKE" -eq 0 ]; then
echo "App is alive and responding (2xx)"
exit 0
else
echo "App may require waking (HTTP $RESPONSE)"
echo " Playwright will run if necessary"
exit 0 # Don't fail the workflow, just log the issue
fi
- name: Wake Streamlit App (headless Playwright)
if: ${{ !startsWith(steps.ping.outputs.response, '2') }}
uses: actions/setup-node@v4.3.0
with:
node-version: '20'
- name: Restore Playwright & npm cache
if: ${{ !startsWith(steps.ping.outputs.response, '2') }}
uses: actions/cache@v4.2.3
with:
path: |
~/.npm
~/.cache/ms-playwright
~/.cache/playwright
key: ${{ runner.os }}-playwright-cache-v1
restore-keys: |
${{ runner.os }}-playwright-cache-
- name: Install Playwright and browsers
if: ${{ !startsWith(steps.ping.outputs.response, '2') }}
run: |
npm init -y
npm i playwright --silent --no-audit --no-fund
npx playwright install chromium --with-deps
- name: Run headless browser to open the app
if: ${{ !startsWith(steps.ping.outputs.response, '2') }}
env:
STREAMLIT_APP_URL: ${{ env.STREAMLIT_APP_URL }}
run: |
node -e "const {chromium}=require('playwright');(async()=>{let browser; try{browser=await chromium.launch({args:['--no-sandbox','--disable-setuid-sandbox']}); const page=await browser.newPage(); console.log('Playwright: navigating to', process.env.STREAMLIT_APP_URL); await page.goto(process.env.STREAMLIT_APP_URL,{waitUntil:'domcontentloaded',timeout:120000}); await page.waitForSelector('section[data-testid=\"stApp\"]',{timeout:120000}); await page.waitForTimeout(5000); console.log('Playwright: visit complete');}catch(e){console.log('Playwright error:',e && e.message ? e.message : e);}finally{if(browser){try{await browser.close();}catch(_){}}}})();"
- name: Log completion
if: always()
run: |
echo "Keep-alive ping completed"
echo " Next scheduled run: Check the Actions tab for schedule"