v0.7.0 - Docker Files Published #72
Workflow file for this run
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: Deploy to Cloudflare Workers | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version tag (e.g., v1.0.0)" | |
| required: true | |
| type: string | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| env: | |
| PNPM_VERSION: 10.19.0 | |
| NODE_VERSION: "20" | |
| # Only run for app releases (v*), not package releases (@tuvixrss/*) | |
| # Package releases like @tuvixrss/tricorder@x.y.z should not trigger deployment | |
| jobs: | |
| check-release-type: | |
| name: Check if this is an app release | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should-deploy: ${{ steps.check.outputs.should-deploy }} | |
| steps: | |
| - name: Check release tag format | |
| id: check | |
| run: | | |
| TAG="${{ github.event.release.tag_name || github.event.inputs.version }}" | |
| if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then | |
| echo "should-deploy=true" >> $GITHUB_OUTPUT | |
| echo "✅ App release detected: $TAG" | |
| elif [[ "$TAG" =~ ^@tuvixrss/ ]]; then | |
| echo "should-deploy=false" >> $GITHUB_OUTPUT | |
| echo "⏭️ Package release detected: $TAG - skipping deployment" | |
| else | |
| echo "should-deploy=true" >> $GITHUB_OUTPUT | |
| echo "⚠️ Unknown tag format: $TAG - proceeding with deployment" | |
| fi | |
| deploy-api: | |
| name: Deploy API to Cloudflare Workers | |
| runs-on: ubuntu-latest | |
| needs: [check-release-type] | |
| if: needs.check-release-type.outputs.should-deploy == 'true' | |
| environment: | |
| name: production | |
| env: | |
| # Set from secret for conditional step execution | |
| SENTRY_DSN: ${{ secrets.SENTRY_DSN }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ github.event.release.tag_name || github.event.inputs.version || github.ref }} | |
| - name: Setup Node.js and pnpm | |
| uses: ./.github/actions/setup | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| pnpm-version: ${{ env.PNPM_VERSION }} | |
| - name: Build Tricorder | |
| run: pnpm run build:tricorder | |
| - name: Type check API | |
| run: pnpm run type-check:api | |
| - name: Run API tests | |
| run: pnpm run test:api | |
| - name: Build API | |
| run: pnpm run build:api | |
| - name: Create wrangler.toml from example | |
| uses: ./.github/actions/substitute-d1-database-id | |
| with: | |
| d1-database-id: ${{ secrets.D1_DATABASE_ID }} | |
| - name: Get worker name | |
| id: worker-name | |
| uses: ./.github/actions/get-worker-name | |
| - name: Get release version | |
| id: release-version | |
| run: | | |
| RELEASE_VERSION="${{ github.event.release.tag_name || github.event.inputs.version || github.sha }}" | |
| echo "version=$RELEASE_VERSION" >> $GITHUB_OUTPUT | |
| echo "Release version: $RELEASE_VERSION" | |
| - name: Set Sentry Release (Backend) | |
| if: env.SENTRY_DSN != '' | |
| run: | | |
| RELEASE_VERSION="${{ steps.release-version.outputs.version }}" | |
| cd packages/api | |
| echo "$RELEASE_VERSION" | pnpm exec wrangler secret put SENTRY_RELEASE --name "${{ steps.worker-name.outputs.name }}" | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| - name: Deploy to Cloudflare Workers | |
| id: deploy | |
| uses: cloudflare/wrangler-action@v3 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| workingDirectory: packages/api | |
| command: deploy | |
| packageManager: pnpm | |
| - name: Output API deployment URL | |
| if: success() | |
| run: | | |
| API_URL="https://${{ steps.worker-name.outputs.name }}.workers.dev" | |
| echo "API_URL=$API_URL" >> $GITHUB_OUTPUT | |
| echo "## API Deployment" >> $GITHUB_STEP_SUMMARY | |
| echo "- **URL:** $API_URL" >> $GITHUB_STEP_SUMMARY | |
| - name: Prepare D1 migrations | |
| if: success() | |
| working-directory: packages/api | |
| run: | | |
| # Copy migrations from drizzle/ to migrations/ (Wrangler expects migrations/ folder) | |
| # This matches the logic in scripts/migrate-d1.sh lines 56-57 | |
| mkdir -p migrations | |
| cp drizzle/*.sql migrations/ 2>/dev/null || true | |
| if [ ! "$(ls -A migrations 2>/dev/null)" ]; then | |
| echo "::error::No migration files found in drizzle/ directory" | |
| exit 1 | |
| fi | |
| echo "✅ Prepared $(ls migrations/*.sql | wc -l) migration file(s)" | |
| - name: Verify D1 database access | |
| if: success() | |
| working-directory: packages/api | |
| run: | | |
| echo "🔍 Verifying D1 database access..." | |
| # Verify we can access the database (this will fail early if permissions are wrong) | |
| pnpm exec wrangler d1 migrations list tuvix --remote || { | |
| echo "::error::Failed to access D1 database. Check:" | |
| echo " 1. API token has 'Account.Cloudflare D1:Edit' permission" | |
| echo " 2. Account ID matches the database's account" | |
| echo " 3. Database ID is correct for this account" | |
| exit 1 | |
| } | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| D1_DATABASE_ID: ${{ secrets.D1_DATABASE_ID }} | |
| - name: Run database migrations | |
| if: success() | |
| uses: cloudflare/wrangler-action@v3 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| workingDirectory: packages/api | |
| command: d1 migrations apply tuvix --remote | |
| packageManager: pnpm | |
| env: | |
| D1_DATABASE_ID: ${{ secrets.D1_DATABASE_ID }} | |
| - name: Cleanup migrations folder | |
| if: always() | |
| working-directory: packages/api | |
| run: | | |
| rm -rf migrations | |
| echo "✅ Cleaned up migrations folder" | |
| deploy-app: | |
| name: Deploy App to Cloudflare Pages | |
| runs-on: ubuntu-latest | |
| needs: [check-release-type, deploy-api] | |
| if: needs.check-release-type.outputs.should-deploy == 'true' | |
| environment: | |
| name: production | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ github.event.release.tag_name || github.event.inputs.version || github.ref }} | |
| - name: Setup Node.js and pnpm | |
| uses: ./.github/actions/setup | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| pnpm-version: ${{ env.PNPM_VERSION }} | |
| - name: Type check App | |
| run: pnpm run type-check:app | |
| - name: Run App tests | |
| run: pnpm run test:app | |
| - name: Get release version | |
| id: release-version | |
| run: | | |
| RELEASE_VERSION="${{ github.event.release.tag_name || github.event.inputs.version || github.sha }}" | |
| echo "version=$RELEASE_VERSION" >> $GITHUB_OUTPUT | |
| echo "Release version: $RELEASE_VERSION" | |
| - name: Verify Sentry Configuration | |
| id: verify-sentry | |
| env: | |
| VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} | |
| run: | | |
| echo "🔍 Checking VITE_SENTRY_DSN secret..." | |
| echo "Secret exists check: ${{ secrets.VITE_SENTRY_DSN != '' }}" | |
| echo "Secret length: ${#VITE_SENTRY_DSN}" | |
| echo "Secret starts with: ${VITE_SENTRY_DSN:0:20}..." | |
| if [ -z "$VITE_SENTRY_DSN" ] || [ "$VITE_SENTRY_DSN" = "" ]; then | |
| echo "⚠️ WARNING: VITE_SENTRY_DSN secret is not set or is empty in GitHub Secrets" | |
| echo " Frontend Sentry will not be initialized." | |
| echo " Set it in: Settings → Secrets and variables → Actions → VITE_SENTRY_DSN" | |
| echo "has_dsn=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "✅ VITE_SENTRY_DSN is configured (length: ${#VITE_SENTRY_DSN} chars)" | |
| echo "has_dsn=true" >> $GITHUB_OUTPUT | |
| # Store the DSN for the build step (masked for security) | |
| echo "dsn_length=${#VITE_SENTRY_DSN}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Build App | |
| env: | |
| VITE_API_URL: ${{ secrets.VITE_API_URL }} | |
| VITE_APP_VERSION: ${{ steps.release-version.outputs.version }} | |
| VITE_SENTRY_ENVIRONMENT: ${{ secrets.VITE_SENTRY_ENVIRONMENT || 'production' }} | |
| VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} | |
| run: | | |
| echo "🔍 Build-time Sentry DSN check:" | |
| echo "VITE_SENTRY_DSN type: $(if [ -z "$VITE_SENTRY_DSN" ]; then echo 'unset'; else echo 'set'; fi)" | |
| echo "VITE_SENTRY_DSN length: ${#VITE_SENTRY_DSN}" | |
| echo "VITE_SENTRY_DSN starts with: ${VITE_SENTRY_DSN:0:20}..." | |
| echo "Verification step result: ${{ steps.verify-sentry.outputs.has_dsn }}" | |
| # Verify the DSN is actually set and not empty | |
| if [ -z "$VITE_SENTRY_DSN" ] || [ "$VITE_SENTRY_DSN" = "" ]; then | |
| echo "❌ ERROR: VITE_SENTRY_DSN is empty or unset during build!" | |
| echo " This will cause Sentry to not initialize in the frontend." | |
| echo " Check that the secret is set correctly in GitHub Secrets." | |
| exit 1 | |
| fi | |
| echo "✅ Building with Sentry DSN configured (length: ${#VITE_SENTRY_DSN} chars)" | |
| pnpm run build:app | |
| - name: Verify Production Pages Project Name | |
| run: | | |
| echo "🔍 Verifying Pages project configuration..." | |
| echo "Project Name: ${{ secrets.CLOUDFLARE_PAGES_PROJECT_NAME }}" | |
| echo "Environment: production" | |
| echo "⚠️ IMPORTANT: Ensure this project name matches your PRODUCTION Pages project" | |
| echo " If this shows a dev project name, update the secret in:" | |
| echo " Settings → Environments → production → Secrets → CLOUDFLARE_PAGES_PROJECT_NAME" | |
| - name: Deploy to Cloudflare Pages | |
| id: deploy-app | |
| uses: cloudflare/wrangler-action@v3 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| command: pages deploy packages/app/dist --project-name=${{ secrets.CLOUDFLARE_PAGES_PROJECT_NAME }} --branch=main | |
| gitHubToken: ${{ secrets.GITHUB_TOKEN }} | |
| packageManager: pnpm | |
| - name: Output App deployment URL | |
| if: success() | |
| run: | | |
| APP_URL="https://${{ secrets.CLOUDFLARE_PAGES_PROJECT_NAME }}.pages.dev" | |
| echo "APP_URL=$APP_URL" >> $GITHUB_OUTPUT | |
| echo "## App Deployment" >> $GITHUB_STEP_SUMMARY | |
| echo "- **URL:** $APP_URL" >> $GITHUB_STEP_SUMMARY | |
| notify: | |
| name: Notify Deployment Status | |
| runs-on: ubuntu-latest | |
| needs: [check-release-type, deploy-api, deploy-app] | |
| if: always() && needs.check-release-type.outputs.should-deploy == 'true' | |
| environment: | |
| name: production | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Create wrangler.toml from example | |
| uses: ./.github/actions/substitute-d1-database-id | |
| with: | |
| d1-database-id: ${{ secrets.D1_DATABASE_ID }} | |
| - name: Get worker name | |
| id: worker-name | |
| uses: ./.github/actions/get-worker-name | |
| - name: Deployment Summary | |
| run: | | |
| echo "## 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Deployed version:** \`${{ github.event.release.tag_name || github.event.inputs.version }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Deployment Status" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Service | Status | URL |" >> $GITHUB_STEP_SUMMARY | |
| echo "|---------|--------|-----|" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ needs.deploy-api.result }}" == "success" ]; then | |
| echo "| API | ✅ Success | https://${{ steps.worker-name.outputs.name }}.workers.dev |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| API | ❌ Failed | - |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.deploy-app.result }}" == "success" ]; then | |
| echo "| App | ✅ Success | https://${{ secrets.CLOUDFLARE_PAGES_PROJECT_NAME }}.pages.dev |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| App | ❌ Failed | - |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Quick Links" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ needs.deploy-api.result }}" == "success" ]; then | |
| echo "- [API Worker](https://${{ steps.worker-name.outputs.name }}.workers.dev)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.deploy-app.result }}" == "success" ]; then | |
| echo "- [App Pages](https://${{ secrets.CLOUDFLARE_PAGES_PROJECT_NAME }}.pages.dev)" >> $GITHUB_STEP_SUMMARY | |
| fi |