Make beautiful charts #179
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 Vercel | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| env: | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
| jobs: | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - run: bun install | |
| - name: ESLint | |
| run: bun run lint | |
| format: | |
| name: Format | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - run: bun install | |
| - name: Prettier check | |
| run: bunx prettier --check . | |
| test: | |
| name: Unit Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - run: bun install | |
| - name: Vitest | |
| run: bun run test -- --run | |
| security: | |
| name: Security Audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - run: bun install | |
| - name: Dependency vulnerability audit | |
| run: bun audit --audit-level=critical | |
| accessibility: | |
| name: Accessibility Audit | |
| needs: [lint, format, test] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - run: bun install | |
| - name: Axe WCAG 2.1 AA audit | |
| run: bunx vitest --run components/__tests__/AxeAccessibilityAudit.test.tsx | |
| - name: ARIA roles & labels | |
| run: bunx vitest --run components/__tests__/Accessibility.test.tsx | |
| - name: Color contrast | |
| run: bunx vitest --run components/__tests__/ColorContrast.test.tsx | |
| - name: Keyboard navigation | |
| run: bunx vitest --run components/__tests__/KeyboardNavigation.test.tsx | |
| e2e: | |
| name: E2E Tests | |
| needs: [lint, format, test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - run: bun install | |
| - name: Install Playwright browsers | |
| run: bunx playwright install --with-deps chromium | |
| - name: Build | |
| run: bun run build | |
| - name: Run E2E tests | |
| run: bun run test:e2e | |
| - name: Upload E2E report | |
| uses: actions/upload-artifact@v4 | |
| if: failure() | |
| with: | |
| name: playwright-report | |
| path: playwright-report/ | |
| retention-days: 14 | |
| lighthouse: | |
| name: Lighthouse Audit | |
| needs: [lint, format, test] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - run: bun install | |
| - name: Build | |
| run: bun run build | |
| - name: Run Lighthouse CI | |
| run: bun run lighthouse | |
| - name: Upload Lighthouse report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: lighthouse-report | |
| path: .lighthouseci/ | |
| retention-days: 14 | |
| # Mirrors Ditectrev/Practice-Tests-Exams-Platform/.github/workflows/vercel-deploy.yml: | |
| # - vercel env add … then vercel build / deploy | |
| # - main: production on Vercel; PRs: preview on Vercel | |
| # | |
| # Environment secrets (GitHub "Production" vs "Preview"): only Appwrite targets differ per env: | |
| # APPWRITE_DATABASE_ID, APPWRITE_COLLECTION_ID_TRIAL_SESSIONS, APPWRITE_COLLECTION_ID_AI_KEYS, APPWRITE_COLLECTION_ID_SUBSCRIPTIONS | |
| # All other deploy secrets stay repository-wide: VERCEL_*, NEXT_PUBLIC_APPWRITE_*, APPWRITE_API_KEY, | |
| # AI_PROVIDER, AI_API_KEY (Hosted AI / Ditectrev AI on server — synced to Vercel below). | |
| # Set GitHub variable NEXT_PUBLIC_SITE_URL (e.g. https://theopenstock.com) for canonical URLs, sitemap, and OG. | |
| # With `environment:` set, GitHub still exposes repository secrets to the job; environment adds/overrides | |
| # only the names defined on that environment (here: Appwrite DB + collection ids). | |
| deploy: | |
| name: Deploy to Vercel | |
| needs: [lint, format, test, security, accessibility] | |
| if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Preview' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - run: bun install | |
| - name: Install Vercel CLI | |
| run: npm install --global vercel@latest | |
| - name: Sync Environment Variables to Vercel | |
| run: | | |
| ENV_TYPE=${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }} | |
| vercel env add NEXT_PUBLIC_APPWRITE_ENDPOINT $ENV_TYPE "" --value "${{ secrets.NEXT_PUBLIC_APPWRITE_ENDPOINT }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| vercel env add NEXT_PUBLIC_APPWRITE_PROJECT_ID $ENV_TYPE "" --value "${{ secrets.NEXT_PUBLIC_APPWRITE_PROJECT_ID }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| if [ -n "${{ vars.NEXT_PUBLIC_SITE_URL }}" ]; then | |
| vercel env add NEXT_PUBLIC_SITE_URL $ENV_TYPE "" --value "${{ vars.NEXT_PUBLIC_SITE_URL }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| fi | |
| if [ -n "${{ vars.NEXT_PUBLIC_GTM_ID }}" ]; then | |
| vercel env add NEXT_PUBLIC_GTM_ID $ENV_TYPE "" --value "${{ vars.NEXT_PUBLIC_GTM_ID }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| fi | |
| vercel env add APPWRITE_API_KEY $ENV_TYPE "" --value "${{ secrets.APPWRITE_API_KEY }}" --yes --force --sensitive --token=${{ secrets.VERCEL_TOKEN }} | |
| vercel env add APPWRITE_DATABASE_ID $ENV_TYPE "" --value "${{ secrets.APPWRITE_DATABASE_ID }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| vercel env add APPWRITE_COLLECTION_ID_AI_KEYS $ENV_TYPE "" --value "${{ secrets.APPWRITE_COLLECTION_ID_AI_KEYS }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| vercel env add APPWRITE_COLLECTION_ID_TRIAL_SESSIONS $ENV_TYPE "" --value "${{ secrets.APPWRITE_COLLECTION_ID_TRIAL_SESSIONS }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| vercel env add APPWRITE_COLLECTION_ID_SUBSCRIPTIONS $ENV_TYPE "" --value "${{ secrets.APPWRITE_COLLECTION_ID_SUBSCRIPTIONS }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| if [ -n "${{ vars.FINNHUB_BASE_URL }}" ]; then | |
| vercel env add FINNHUB_BASE_URL $ENV_TYPE "" --value "${{ vars.FINNHUB_BASE_URL }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| fi | |
| vercel env add FINNHUB_API_KEY $ENV_TYPE "" --value "${{ secrets.FINNHUB_API_KEY }}" --yes --force --sensitive --token=${{ secrets.VERCEL_TOKEN }} | |
| vercel env add AI_PROVIDER $ENV_TYPE "" --value "${{ secrets.AI_PROVIDER }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| vercel env add AI_API_KEY $ENV_TYPE "" --value "${{ secrets.AI_API_KEY }}" --yes --force --sensitive --token=${{ secrets.VERCEL_TOKEN }} | |
| if [ -n "${{ vars.AI_MODEL }}" ]; then | |
| vercel env add AI_MODEL $ENV_TYPE "" --value "${{ vars.AI_MODEL }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| fi | |
| if [ -n "${{ vars.YAHOO_FINANCE_API_URL }}" ]; then | |
| vercel env add YAHOO_FINANCE_API_URL $ENV_TYPE "" --value "${{ vars.YAHOO_FINANCE_API_URL }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| fi | |
| if [ -n "${{ vars.STRIPE_PRICE_ADS_FREE }}" ]; then | |
| vercel env add STRIPE_PRICE_ADS_FREE $ENV_TYPE "" --value "${{ vars.STRIPE_PRICE_ADS_FREE }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| fi | |
| if [ -n "${{ vars.STRIPE_PRICE_LOCAL }}" ]; then | |
| vercel env add STRIPE_PRICE_LOCAL $ENV_TYPE "" --value "${{ vars.STRIPE_PRICE_LOCAL }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| fi | |
| if [ -n "${{ vars.STRIPE_PRICE_BYOK }}" ]; then | |
| vercel env add STRIPE_PRICE_BYOK $ENV_TYPE "" --value "${{ vars.STRIPE_PRICE_BYOK }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| fi | |
| if [ -n "${{ vars.STRIPE_PRICE_HOSTED_AI }}" ]; then | |
| vercel env add STRIPE_PRICE_HOSTED_AI $ENV_TYPE "" --value "${{ vars.STRIPE_PRICE_HOSTED_AI }}" --yes --force --token=${{ secrets.VERCEL_TOKEN }} | |
| fi | |
| vercel env add STRIPE_SECRET_KEY $ENV_TYPE "" --value "${{ secrets.STRIPE_SECRET_KEY }}" --yes --force --sensitive --token=${{ secrets.VERCEL_TOKEN }} | |
| vercel env add STRIPE_WEBHOOK_SECRET $ENV_TYPE "" --value "${{ secrets.STRIPE_WEBHOOK_SECRET }}" --yes --force --sensitive --token=${{ secrets.VERCEL_TOKEN }} | |
| env: | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
| - name: Pull Vercel Environment Information | |
| id: vercel-pull | |
| run: vercel pull --yes --environment=${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }} --token=${{ secrets.VERCEL_TOKEN }} | |
| env: | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
| - name: Build Project Artifacts | |
| run: vercel build ${{ github.ref == 'refs/heads/main' && '--prod' || '' }} --token=${{ secrets.VERCEL_TOKEN }} | |
| env: | |
| NEXT_PUBLIC_GTM_ID: ${{ vars.NEXT_PUBLIC_GTM_ID }} | |
| NEXT_PUBLIC_SITE_URL: ${{ vars.NEXT_PUBLIC_SITE_URL }} | |
| NEXT_PUBLIC_APPWRITE_ENDPOINT: ${{ secrets.NEXT_PUBLIC_APPWRITE_ENDPOINT }} | |
| NEXT_PUBLIC_APPWRITE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_PROJECT_ID }} | |
| APPWRITE_API_KEY: ${{ secrets.APPWRITE_API_KEY }} | |
| APPWRITE_DATABASE_ID: ${{ secrets.APPWRITE_DATABASE_ID }} | |
| APPWRITE_COLLECTION_ID_AI_KEYS: ${{ secrets.APPWRITE_COLLECTION_ID_AI_KEYS }} | |
| APPWRITE_COLLECTION_ID_TRIAL_SESSIONS: ${{ secrets.APPWRITE_COLLECTION_ID_TRIAL_SESSIONS }} | |
| APPWRITE_COLLECTION_ID_SUBSCRIPTIONS: ${{ secrets.APPWRITE_COLLECTION_ID_SUBSCRIPTIONS }} | |
| FINNHUB_BASE_URL: ${{ vars.FINNHUB_BASE_URL }} | |
| FINNHUB_API_KEY: ${{ secrets.FINNHUB_API_KEY }} | |
| YAHOO_FINANCE_API_URL: ${{ vars.YAHOO_FINANCE_API_URL }} | |
| STRIPE_PRICE_ADS_FREE: ${{ vars.STRIPE_PRICE_ADS_FREE }} | |
| STRIPE_PRICE_LOCAL: ${{ vars.STRIPE_PRICE_LOCAL }} | |
| STRIPE_PRICE_BYOK: ${{ vars.STRIPE_PRICE_BYOK }} | |
| STRIPE_PRICE_HOSTED_AI: ${{ vars.STRIPE_PRICE_HOSTED_AI }} | |
| STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }} | |
| STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }} | |
| - name: Deploy Project Artifacts to Vercel | |
| id: vercel-deploy | |
| run: | | |
| DEPLOYMENT_OUTPUT=$(vercel deploy ${{ github.ref == 'refs/heads/main' && '--prod' || '--prebuilt' }} --token=${{ secrets.VERCEL_TOKEN }} --yes 2>&1) | |
| echo "$DEPLOYMENT_OUTPUT" | |
| DEPLOYMENT_URL=$(echo "$DEPLOYMENT_OUTPUT" | grep -o 'https://[^[:space:]]*\.vercel\.app' | head -1) | |
| if [ -z "$DEPLOYMENT_URL" ]; then | |
| DEPLOYMENT_URL=$(echo "$DEPLOYMENT_OUTPUT" | tail -1 | grep -o 'https://[^[:space:]]*' | head -1) | |
| fi | |
| echo "deployment_url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT | |
| echo "Deployment URL: $DEPLOYMENT_URL" | |
| env: | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
| - name: Comment PR with deployment URL | |
| if: github.event_name == 'pull_request' && steps.vercel-deploy.outcome == 'success' && steps.vercel-deploy.outputs.deployment_url != '' | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const deploymentUrl = '${{ steps.vercel-deploy.outputs.deployment_url }}'; | |
| const comment = `🚀 **Vercel Preview Deployment Ready!** | |
| Preview: ${deploymentUrl} | |
| --- | |
| *This comment was automatically generated by GitHub Actions*`; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); |