Skip to content

Make beautiful charts #179

Make beautiful charts

Make beautiful charts #179

Workflow file for this run

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
});