Langflow Migration Test: Latest → Nightly #29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "Langflow Migration Test: Latest → Nightly" | |
| on: | |
| schedule: | |
| - cron: "0 7 * * *" # 04:00 BRT (UTC-3) | |
| workflow_dispatch: | |
| env: | |
| LANGFLOW_PORT: 7860 | |
| LANGFLOW_URL: http://localhost:7860 | |
| PG_USER: langflow | |
| PG_PASSWORD: langflow_test_pw | |
| PG_DB: langflow | |
| STATE_FILE: /tmp/migration-test-state.json | |
| REPORT_FILE: /tmp/migration-report.md | |
| # Fixed key shared by both containers so credentials encrypted by latest | |
| # can be decrypted by nightly after DB migration. | |
| LANGFLOW_SECRET_KEY: "migration-test-fixed-secret-32chars!!" | |
| permissions: | |
| contents: read | |
| issues: write | |
| jobs: | |
| migration-test: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_USER: langflow | |
| POSTGRES_PASSWORD: langflow_test_pw | |
| POSTGRES_DB: langflow | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies | |
| run: | | |
| pip install requests playwright pytest pytest-playwright | |
| playwright install chromium --with-deps | |
| - name: Generate ephemeral Fernet key for this run | |
| run: | | |
| python - <<'PY' | |
| import base64 | |
| import os | |
| key = base64.urlsafe_b64encode(os.urandom(32)).decode() | |
| with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as github_env: | |
| github_env.write(f"LANGFLOW_SECRET_KEY={key}\n") | |
| PY | |
| # ── Phase 1: Langflow Latest ────────────────────────────── | |
| - name: Pull Langflow latest | |
| run: | | |
| docker pull langflowai/langflow:latest | |
| docker inspect langflowai/langflow:latest --format='{{index .RepoDigests 0}}' > /tmp/latest-digest.txt | |
| echo "Latest digest: $(cat /tmp/latest-digest.txt)" | |
| - name: Start Langflow latest | |
| run: | | |
| docker run -d --name langflow \ | |
| --network host \ | |
| -e LANGFLOW_DATABASE_URL="postgresql://$PG_USER:$PG_PASSWORD@localhost:5432/$PG_DB" \ | |
| -e LANGFLOW_AUTO_LOGIN=true \ | |
| -e LANGFLOW_SKIP_AUTH_AUTO_LOGIN=true \ | |
| -e LANGFLOW_SUPERUSER=langflow \ | |
| -e LANGFLOW_SUPERUSER_PASSWORD=langflow123 \ | |
| -e LANGFLOW_STORE=false \ | |
| -e LANGFLOW_SECRET_KEY="$LANGFLOW_SECRET_KEY" \ | |
| langflowai/langflow:latest | |
| - name: Wait for Langflow latest | |
| run: python tests/github-workflows/migration/wait_for_langflow.py | |
| - name: Create flow, configure and execute on latest | |
| run: python tests/github-workflows/migration/setup_latest.py | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| - name: Save latest container logs | |
| if: always() | |
| run: docker logs langflow > /tmp/langflow-latest.log 2>&1 || true | |
| # ── Phase 2: Upgrade to Nightly ─────────────────────────── | |
| - name: Stop Langflow latest | |
| run: docker stop langflow && docker rm langflow | |
| - name: Pull Langflow nightly | |
| run: | | |
| docker pull langflowai/langflow-nightly:latest | |
| docker inspect langflowai/langflow-nightly:latest --format='{{index .RepoDigests 0}}' > /tmp/nightly-digest.txt | |
| echo "Nightly digest: $(cat /tmp/nightly-digest.txt)" | |
| - name: Start Langflow nightly (same database) | |
| run: | | |
| docker run -d --name langflow \ | |
| --network host \ | |
| -e LANGFLOW_DATABASE_URL="postgresql://$PG_USER:$PG_PASSWORD@localhost:5432/$PG_DB" \ | |
| -e LANGFLOW_AUTO_LOGIN=true \ | |
| -e LANGFLOW_SKIP_AUTH_AUTO_LOGIN=true \ | |
| -e LANGFLOW_SUPERUSER=langflow \ | |
| -e LANGFLOW_SUPERUSER_PASSWORD=langflow123 \ | |
| -e LANGFLOW_STORE=false \ | |
| -e LANGFLOW_SECRET_KEY="$LANGFLOW_SECRET_KEY" \ | |
| langflowai/langflow-nightly:latest | |
| - name: Wait for Langflow nightly (includes migration) | |
| id: nightly_start | |
| run: python tests/github-workflows/migration/wait_for_langflow.py --timeout 180 | |
| - name: Save nightly container logs | |
| if: always() | |
| run: docker logs langflow > /tmp/langflow-nightly.log 2>&1 || true | |
| # ── Phase 3: Verify Migration ───────────────────────────── | |
| - name: Verify migration via API | |
| run: python tests/github-workflows/migration/verify_migration_api.py | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| - name: Verify migration via UI (Playwright) | |
| run: pytest tests/github-workflows/migration/test_ui_migration.py -v --tracing=retain-on-failure --output=test-results | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| # ── Phase 4: Report ─────────────────────────────────────── | |
| - name: Generate report | |
| if: always() | |
| run: python tests/github-workflows/migration/generate_report.py | |
| - name: Print report | |
| if: always() | |
| run: cat /tmp/migration-report.md | |
| - name: Upload test artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: migration-test-${{ github.run_number }} | |
| path: | | |
| test-results/ | |
| /tmp/migration-report.md | |
| /tmp/langflow-latest.log | |
| /tmp/langflow-nightly.log | |
| /tmp/migration-test-state.json | |
| /tmp/latest-digest.txt | |
| /tmp/nightly-digest.txt | |
| - name: Cleanup containers | |
| if: always() | |
| run: docker stop langflow 2>/dev/null; docker rm langflow 2>/dev/null; true | |
| - name: Close issue on success | |
| if: success() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const { data: existing } = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'migration-test', | |
| state: 'open', | |
| }); | |
| if (existing.length > 0) { | |
| const issue = existing[0]; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: [ | |
| `## ✅ Run #${context.runNumber} — ${new Date().toISOString().split('T')[0]}`, | |
| '', | |
| 'Migration test passed. Closing this issue.', | |
| '', | |
| `[Workflow Run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`, | |
| ].join('\n'), | |
| }); | |
| await github.rest.issues.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| state: 'closed', | |
| }); | |
| } | |
| - name: Create or update issue on failure | |
| if: failure() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| let report = 'Migration test failed. See workflow artifacts for details.'; | |
| try { | |
| report = fs.readFileSync('/tmp/migration-report.md', 'utf8'); | |
| } catch (e) { | |
| console.log('Report not available:', e.message); | |
| } | |
| const { data: existing } = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'migration-test', | |
| state: 'open', | |
| }); | |
| const body = [ | |
| `## Run #${context.runNumber} — ${new Date().toISOString().split('T')[0]}`, | |
| '', | |
| report, | |
| '', | |
| `[Workflow Run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`, | |
| '', | |
| '/cc @lice-reis', | |
| ].join('\n'); | |
| if (existing.length > 0) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: existing[0].number, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `Langflow Migration Test Failed (latest → nightly)`, | |
| body, | |
| labels: ['migration-test', 'automated'], | |
| }); | |
| } |