simplify calendar into a month-first agenda #133
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: Validate | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| concurrency: | |
| group: validate-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| env: | |
| BETTER_AUTH_SECRET: ci-build-secret-gradientpeak-2026-validate-32chars | |
| DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres | |
| POSTGRES_URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres | |
| SUPABASE_DB_URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres | |
| NEXT_PRIVATE_SUPABASE_URL: https://example.supabase.co | |
| NEXT_PUBLIC_SUPABASE_URL: https://example.supabase.co | |
| NEXT_PRIVATE_SUPABASE_SECRET_KEY: ci-build-service-role-key | |
| SUPABASE_SERVICE_ROLE_KEY: ci-build-service-role-key | |
| jobs: | |
| changes: | |
| name: Changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| docs_only: ${{ steps.filter.outputs.docs == 'true' && steps.filter.outputs.non_docs != 'true' }} | |
| api_live_db_related: ${{ steps.filter.outputs.api_live_db_related }} | |
| db_schema_related: ${{ steps.filter.outputs.db_schema_related }} | |
| mobile_runtime_related: ${{ steps.filter.outputs.mobile_runtime_related }} | |
| mobile_test_related: ${{ steps.filter.outputs.mobile_test_related }} | |
| web_related: ${{ steps.filter.outputs.web_related }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Detect docs-only changes | |
| id: filter | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| filters: | | |
| docs: | |
| - '**/*.md' | |
| - '**/*.mdx' | |
| - 'docs/**' | |
| - '.github/workflows/validate.yml' | |
| non_docs: | |
| - '**' | |
| - '!**/*.md' | |
| - '!**/*.mdx' | |
| - '!docs/**' | |
| - '!.github/workflows/validate.yml' | |
| mobile_runtime_related: | |
| - 'apps/mobile/**' | |
| - '!apps/mobile/.maestro/**' | |
| - '!apps/mobile/scripts/generate-maestro-preview-flow.mts' | |
| - '!apps/mobile/scripts/maestro-local.sh' | |
| - '!apps/mobile/scripts/prepare-maestro-device.sh' | |
| - '!apps/mobile/scripts/run-maestro-lane.mjs' | |
| - 'packages/api/**' | |
| - 'packages/auth/**' | |
| - 'packages/core/**' | |
| - 'packages/db/**' | |
| - 'packages/tailwindcss/**' | |
| - 'packages/ui/**' | |
| - 'tooling/**' | |
| - 'package.json' | |
| - 'pnpm-lock.yaml' | |
| - 'pnpm-workspace.yaml' | |
| - 'turbo.json' | |
| db_schema_related: | |
| - 'packages/db/**' | |
| api_live_db_related: | |
| - 'packages/api/**' | |
| - 'packages/db/**' | |
| - 'packages/supabase/**' | |
| mobile_test_related: | |
| - 'apps/mobile/.maestro/**' | |
| - 'apps/mobile/scripts/generate-maestro-preview-flow.mts' | |
| - 'apps/mobile/scripts/maestro-local.sh' | |
| - 'apps/mobile/scripts/prepare-maestro-device.sh' | |
| - 'apps/mobile/scripts/run-maestro-lane.mjs' | |
| - 'scripts/ci-export-local-env.sh' | |
| - 'scripts/test-e2e.sh' | |
| web_related: | |
| - 'apps/web/**' | |
| - 'packages/api/**' | |
| - 'packages/auth/**' | |
| - 'packages/core/**' | |
| - 'packages/db/**' | |
| - 'packages/tailwindcss/**' | |
| - 'packages/ui/**' | |
| - 'tooling/**' | |
| - 'package.json' | |
| - 'pnpm-lock.yaml' | |
| - 'pnpm-workspace.yaml' | |
| - 'turbo.json' | |
| docs-only: | |
| name: Docs Only | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| if: needs.changes.outputs.docs_only == 'true' | |
| steps: | |
| - name: Skip full validation for docs-only changes | |
| run: | | |
| printf 'Docs-only change detected; skipping full validation suite.\n' | |
| format-and-lint: | |
| name: Format And Lint | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run Biome check | |
| run: pnpm check | |
| typecheck: | |
| name: Typecheck | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run type checks | |
| run: pnpm check-types | |
| build: | |
| name: Build | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run builds | |
| run: pnpm build | |
| unit-and-integration: | |
| name: Unit And Integration | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run unit and integration tests | |
| run: pnpm test | |
| db-schema-parity: | |
| name: DB Schema Parity | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| steps: | |
| - name: Checkout | |
| if: needs.changes.outputs.db_schema_related == 'true' | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| if: needs.changes.outputs.db_schema_related == 'true' | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| if: needs.changes.outputs.db_schema_related == 'true' | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| if: needs.changes.outputs.db_schema_related == 'true' | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup Supabase CLI | |
| if: needs.changes.outputs.db_schema_related == 'true' | |
| uses: supabase/setup-cli@v1 | |
| with: | |
| version: latest | |
| - name: Start local Supabase | |
| if: needs.changes.outputs.db_schema_related == 'true' | |
| run: pnpm run self-host:up | |
| - name: Wait for Supabase | |
| if: needs.changes.outputs.db_schema_related == 'true' | |
| run: bash ./scripts/wait-for-port.sh 127.0.0.1 54321 supabase 180 | |
| - name: Apply local app schema migrations | |
| if: needs.changes.outputs.db_schema_related == 'true' | |
| run: pnpm --filter @repo/db db:migrate | |
| - name: Check schema parity | |
| if: needs.changes.outputs.db_schema_related == 'true' | |
| run: pnpm --filter @repo/db db:schema:check | |
| - name: Skip schema parity when DB schema is unchanged | |
| if: needs.changes.outputs.db_schema_related != 'true' | |
| run: | | |
| printf 'No packages/db changes detected; skipping schema parity check.\n' | |
| api-live-db: | |
| name: API Live DB Smoke | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| steps: | |
| - name: Checkout | |
| if: needs.changes.outputs.api_live_db_related == 'true' | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| if: needs.changes.outputs.api_live_db_related == 'true' | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| if: needs.changes.outputs.api_live_db_related == 'true' | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| if: needs.changes.outputs.api_live_db_related == 'true' | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup Supabase CLI | |
| if: needs.changes.outputs.api_live_db_related == 'true' | |
| uses: supabase/setup-cli@v1 | |
| with: | |
| version: latest | |
| - name: Start local Supabase | |
| if: needs.changes.outputs.api_live_db_related == 'true' | |
| run: pnpm run self-host:up | |
| - name: Wait for Supabase | |
| if: needs.changes.outputs.api_live_db_related == 'true' | |
| run: bash ./scripts/wait-for-port.sh 127.0.0.1 54321 supabase 180 | |
| - name: Apply local app schema migrations | |
| if: needs.changes.outputs.api_live_db_related == 'true' | |
| run: pnpm --filter @repo/db db:migrate | |
| - name: Run API live DB smoke tests | |
| if: needs.changes.outputs.api_live_db_related == 'true' | |
| run: pnpm --filter @repo/api test:live-db | |
| - name: Skip live DB smoke when API and DB are unchanged | |
| if: needs.changes.outputs.api_live_db_related != 'true' | |
| run: | | |
| printf 'No API or DB changes detected; skipping API live DB smoke tests.\n' | |
| mobile-component-and-jest: | |
| name: Mobile Component And Jest | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| if: needs.changes.outputs.docs_only != 'true' && needs.changes.outputs.mobile_runtime_related == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run mobile Jest tests | |
| run: pnpm --filter mobile test:jest | |
| full-validation-suite: | |
| name: Full Validation Suite | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| - format-and-lint | |
| - typecheck | |
| - build | |
| - unit-and-integration | |
| - db-schema-parity | |
| - api-live-db | |
| if: needs.changes.outputs.docs_only != 'true' | |
| steps: | |
| - name: Validation gate passed | |
| run: | | |
| printf 'All prerequisite validation jobs passed.\n' | |
| mobile-e2e-build: | |
| name: Mobile E2E Build | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| if: needs.changes.outputs.docs_only != 'true' && (needs.changes.outputs.mobile_runtime_related == 'true' || needs.changes.outputs.mobile_test_related == 'true') | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: 17 | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v5 | |
| - name: Restore generated Android project cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: apps/mobile/android | |
| key: mobile-android-project-${{ runner.os }}-${{ hashFiles('apps/mobile/app.config.ts', 'apps/mobile/package.json', 'apps/mobile/scripts/android-emulator.sh', 'pnpm-lock.yaml') }} | |
| - name: Restore Android E2E APK cache | |
| id: mobile-e2e-apk-cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: apps/mobile/android/app/build/outputs/apk/release | |
| key: mobile-e2e-apk-${{ runner.os }}-${{ hashFiles('apps/mobile/app.config.ts', 'apps/mobile/package.json', 'apps/mobile/scripts/android-emulator.sh', 'pnpm-lock.yaml') }} | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build bundled Android E2E APK | |
| if: needs.changes.outputs.mobile_runtime_related == 'true' || steps.mobile-e2e-apk-cache.outputs.cache-hit != 'true' | |
| run: pnpm --filter mobile android:e2e:build | |
| - name: Upload Android E2E APK | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: mobile-e2e-apk | |
| path: apps/mobile/android/app/build/outputs/apk/release/*.apk | |
| compression-level: 0 | |
| retention-days: 3 | |
| if-no-files-found: error | |
| web-e2e: | |
| name: Web E2E | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| - full-validation-suite | |
| if: needs.changes.outputs.docs_only != 'true' && needs.changes.outputs.web_related == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup Supabase CLI | |
| uses: supabase/setup-cli@v1 | |
| with: | |
| version: latest | |
| - name: Start local Supabase | |
| run: pnpm run self-host:up | |
| - name: Wait for Supabase | |
| run: bash ./scripts/wait-for-port.sh 127.0.0.1 54321 supabase 180 | |
| - name: Apply local app schema migrations | |
| run: pnpm --filter @repo/db db:migrate | |
| - name: Export local test environment | |
| run: bash ./scripts/ci-export-local-env.sh >> "$GITHUB_ENV" | |
| - name: Seed E2E users | |
| run: node ./apps/mobile/scripts/seed-e2e-users.mjs | |
| - name: Install Playwright browsers | |
| run: pnpm exec playwright install --with-deps chromium | |
| - name: Start web E2E server | |
| run: nohup pnpm run dev:e2e:web > /tmp/gradientpeak-web-e2e.log 2>&1 & | |
| - name: Wait for web server | |
| run: bash ./scripts/wait-for-port.sh 127.0.0.1 3000 web-e2e 180 | |
| - name: Run Playwright | |
| run: pnpm --filter web test:e2e | |
| - name: Upload Playwright artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-artifacts | |
| path: | | |
| apps/web/playwright-report | |
| apps/web/test-results | |
| /tmp/gradientpeak-web-e2e.log | |
| if-no-files-found: ignore | |
| mobile-e2e-core: | |
| name: Mobile E2E | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| - full-validation-suite | |
| - mobile-component-and-jest | |
| - mobile-e2e-build | |
| if: needs.changes.outputs.docs_only != 'true' && (needs.changes.outputs.mobile_runtime_related == 'true' || needs.changes.outputs.mobile_test_related == 'true') | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.33.0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: 17 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup Supabase CLI | |
| uses: supabase/setup-cli@v1 | |
| with: | |
| version: latest | |
| - name: Start local Supabase | |
| run: pnpm run self-host:up | |
| - name: Wait for Supabase | |
| run: bash ./scripts/wait-for-port.sh 127.0.0.1 54321 supabase 180 | |
| - name: Apply local app schema migrations | |
| run: pnpm --filter @repo/db db:migrate | |
| - name: Export local test environment | |
| run: bash ./scripts/ci-export-local-env.sh >> "$GITHUB_ENV" | |
| - name: Seed E2E users | |
| run: node ./apps/mobile/scripts/seed-e2e-users.mjs | |
| - name: Install Maestro | |
| run: | | |
| curl -Ls "https://get.maestro.mobile.dev" | bash | |
| echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" | |
| - name: Download bundled Android E2E APK | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: mobile-e2e-apk | |
| path: apps/mobile/.maestro/build-artifacts | |
| - name: Generate Maestro assets | |
| run: pnpm --filter mobile generate:maestro | |
| - name: Start E2E web server | |
| run: nohup pnpm run dev:e2e:web > /tmp/gradientpeak-web-e2e.log 2>&1 & | |
| - name: Wait for E2E web server | |
| run: bash ./scripts/wait-for-port.sh 127.0.0.1 3000 web-e2e 180 | |
| - name: Enable KVM group perms | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| - name: Restore Android emulator snapshot cache | |
| id: avd-cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.android/avd/* | |
| ~/.android/adb* | |
| key: validate-avd-${{ runner.os }}-api30-x86_64-pixel_7 | |
| - name: Create Android emulator snapshot | |
| if: steps.avd-cache.outputs.cache-hit != 'true' | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: 30 | |
| arch: x86_64 | |
| profile: pixel_7 | |
| force-avd-creation: false | |
| emulator-boot-timeout: 900 | |
| emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| disable-animations: false | |
| script: echo "Android emulator snapshot ready." | |
| - name: Run Maestro smoke flows | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| env: | |
| GP_MAESTRO_RUNTIME: installed | |
| with: | |
| api-level: 30 | |
| arch: x86_64 | |
| profile: pixel_7 | |
| force-avd-creation: false | |
| emulator-boot-timeout: 900 | |
| emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| disable-animations: false | |
| script: | | |
| adb install -r "$(echo "$GITHUB_WORKSPACE"/apps/mobile/.maestro/build-artifacts/*.apk)" | |
| pnpm --filter mobile maestro:flow -- --device emulator-5554 .maestro/flows/main/auth_navigation.yaml | |
| pnpm --filter mobile maestro:flow -- --device emulator-5554 .maestro/flows/main/tabs_smoke.yaml | |
| - name: Upload Maestro artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: maestro-mobile-e2e | |
| path: | | |
| apps/mobile/.maestro/artifacts | |
| apps/mobile/.maestro/emulator.log | |
| /tmp/gradientpeak-web-e2e.log | |
| if-no-files-found: ignore |