diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 952f3a4603..b9ba5511ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ concurrency: jobs: autofix: timeout-minutes: 10 - runs-on: ubuntu-latest + runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }} steps: - name: Checkout Code 🛎 uses: actions/checkout@v4 diff --git a/.github/workflows/close-conflicted-prs.yml b/.github/workflows/close-conflicted-prs.yml new file mode 100644 index 0000000000..5b22ec8791 --- /dev/null +++ b/.github/workflows/close-conflicted-prs.yml @@ -0,0 +1,69 @@ +name: Close Old Conflicted PRs + +on: + schedule: + - cron: '0 0 * * *' + +jobs: + close_conflicted_prs: + runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }} + permissions: + pull-requests: write + steps: + - name: Close PRs with conflicts older than 3 days + uses: actions/github-script@v7 + with: + script: | + const prs = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open' + }); + + const now = new Date(); + for (const pr of prs.data) { + const details = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number + }); + + if (details.data.mergeable === null) { + continue; + } + + if (details.data.mergeable === false) { + const timeline = await github.rest.issues.listEventsForTimeline({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pr.number, per_page: 100 }); + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number + }); + + let conflictStartTime = new Date(pr.updated_at); + + for (const event of timeline.data) { + if (event.event === 'cross-referenced' && event.commit_id) { + conflictStartTime = new Date(event.created_at); + break; + } + } + + const conflictAgeDays = (now - conflictStartTime) / (1000 * 60 * 60 * 24); + + if (conflictAgeDays >= 3) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + body: "This PR has had merge conflicts for more than 3 days. It will be automatically closed. Please resolve the conflicts and reopen the PR if you'd like to continue working on it." + }); + + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + state: 'closed' + }); + } + } + } diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml new file mode 100644 index 0000000000..2a72ec2c40 --- /dev/null +++ b/.github/workflows/close-stale-issues.yml @@ -0,0 +1,21 @@ +name: Close Stale Issues + +on: + schedule: + - cron: '0 0 * * *' + +jobs: + stale: + runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }} + permissions: + issues: write + contents: read + steps: + - uses: actions/stale@v9 + with: + days-before-issue-stale: 3 + days-before-issue-close: 0 + days-before-pr-stale: -1 + stale-issue-label: 'stale' + stale-issue-message: 'This issue is stale (3+ days) and will be closed.' + close-issue-message: 'Closing stale issue.' diff --git a/.github/workflows/deploy-to-prod-command.yml b/.github/workflows/deploy-to-prod-command.yml index 10d63b788f..d29c757ba6 100644 --- a/.github/workflows/deploy-to-prod-command.yml +++ b/.github/workflows/deploy-to-prod-command.yml @@ -11,7 +11,7 @@ permissions: jobs: deploy-to-production: - runs-on: ubuntu-latest + runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }} name: Merge Staging to Production if: github.event.issue.pull_request && contains(github.event.issue.labels.*.name, 'production-deploy') && startsWith(github.event.comment.body, '/deploy') && github.event.comment.author_association == 'MEMBER' steps: diff --git a/.github/workflows/lingo-dev.yml b/.github/workflows/lingo-dev.yml index 18c5490348..74aa2944e8 100644 --- a/.github/workflows/lingo-dev.yml +++ b/.github/workflows/lingo-dev.yml @@ -22,7 +22,7 @@ concurrency: jobs: main: timeout-minutes: 15 - runs-on: ubuntu-latest + runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }} steps: - name: Checkout Code 🛎 uses: actions/checkout@v4 diff --git a/.github/workflows/sync-production.yml b/.github/workflows/sync-production.yml index ff4694c534..9f3f349bee 100644 --- a/.github/workflows/sync-production.yml +++ b/.github/workflows/sync-production.yml @@ -11,7 +11,7 @@ permissions: jobs: sync-branches: - runs-on: ubuntu-latest + runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }} name: Syncing branches steps: diff --git a/.husky/pre-commit b/.husky/pre-commit index e02c24e2b5..22c2457047 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -pnpm lint-staged \ No newline at end of file +pnpm dlx oxlint@1.9.0 --deny-warnings \ No newline at end of file diff --git a/AGENT.md b/AGENT.md index 68e20f816a..e0f5cbfc3a 100644 --- a/AGENT.md +++ b/AGENT.md @@ -105,3 +105,10 @@ This is a pnpm workspace monorepo with the following structure: - Uses Cloudflare Workers for backend deployment - iOS app is part of the monorepo - CLI tool `nizzy` helps manage environment and sync operations + +## IMPORTANT RESTRICTIONS + +- **NEVER run project-wide lint/format commands** (`pnpm check`, `pnpm lint`, `pnpm format`, `pnpm check:format`) +- These commands format/lint the entire codebase and cause unnecessary changes +- Only use targeted linting/formatting on specific files when absolutely necessary +- Focus on the specific task at hand without touching unrelated files diff --git a/apps/mail/app/(auth)/login/login-client.tsx b/apps/mail/app/(auth)/login/login-client.tsx index fb770b5e0d..0af8aeeac6 100644 --- a/apps/mail/app/(auth)/login/login-client.tsx +++ b/apps/mail/app/(auth)/login/login-client.tsx @@ -201,13 +201,13 @@ function LoginClientContent({ providers, isProd }: LoginClientProps) {