diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e25469f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,6 @@ +# These organizations support the ILN Frontend project +github: [Invoice-Liquidity-Network] + +# Drips Network Wave funding +custom: + - https://drips.network/waves diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..bdff9f9 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,66 @@ +# Automatic PR labeling based on changed file paths +# Labels are applied when a PR touches files in the specified paths + +ci: + - .github/workflows/**/* + - .github/labeler.yml + - .github/dependabot.yml + - .github/CODEOWNERS + - .husky/**/* + - .gitcliff.toml + - eslint.config.mjs + - prettierrc + - vitest.config.ts + - playwright.config.ts + - stryker.conf.json + - .lighthouserc.json + +documentation: + - docs/**/* + - README.md + - CONTRIBUTING.md + - DESIGN.md + - SECURITY.md + - CHANGELOG.md + - PR_NEW.md + +testing: + - __tests__/**/* + - e2e/**/* + - vitest.setup.ts + - vitest.shims.d.ts + +ui: + - src/components/**/* + - src/app/**/page.tsx + - src/app/**/layout.tsx + - styles/**/* + +hooks: + - src/hooks/**/* + - src/context/**/* + +contracts: + - src/lib/soroban.ts + - src/lib/horizon.ts + - src/lib/indexer-websocket.ts + - src/lib/contract/**/* + - src/lib/invoice-nft.ts + +api: + - app/api/**/* + +utils: + - src/utils/**/* + - src/lib/**/* # (excluding contract-specific files above) + +types: + - src/types/**/* + - src/constants.ts + +dependencies: + - package.json + - pnpm-lock.yaml + - tsconfig.json + - next.config.ts + - postcss.config.mjs diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml new file mode 100644 index 0000000..db13c22 --- /dev/null +++ b/.github/workflows/pr-labeler.yml @@ -0,0 +1,26 @@ +name: Pull Request Labeler + +on: + pull_request_target: + types: [opened, edited, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + labeler: + name: Apply labels based on changed files + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Run Labeler + uses: actions/labeler@v5 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/labeler.yml + sync-labels: true diff --git a/.github/workflows/stale-assignments.yml b/.github/workflows/stale-assignments.yml new file mode 100644 index 0000000..40a741f --- /dev/null +++ b/.github/workflows/stale-assignments.yml @@ -0,0 +1,155 @@ +name: Stale Assignment Reclaimer + +on: + schedule: + # Run daily at 00:00 UTC + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + stale-assignments: + name: Reclaim abandoned issue assignments + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Reclaim stale assignments + uses: actions/github-script@v7 + with: + script: | + const { Octokit } = require('@octokit/rest'); + const octokit = new Octokit({ auth: github.token }); + + // Configuration (can be adjusted here) + const WARNING_DAYS = 7; // Days before warning comment + const RECLAIM_DAYS = 14; // Days before unassignment + const WARNING_LABEL = 'stale-assignment-warning'; + const RECLAIM_LABEL = 'assignment-reclaimed'; + + const owner = context.repo.owner; + const repo = context.repo.repo; + + // Get all open issues with assignees + const issues = await octokit.rest.issues.listForRepo({ + owner, + repo, + state: 'open', + assignee: '*', + per_page: 100 + }); + + const now = new Date(); + + for (const issue of issues.data) { + const assignees = issue.assignees.map(a => a.login); + if (assignees.length === 0) continue; + + const assignedAt = new Date(issue.created_at); + const updatedAt = new Date(issue.updated_at); + const daysSinceUpdate = Math.floor((now - updatedAt) / (1000 * 60 * 60 * 24)); + const daysSinceAssignment = Math.floor((now - assignedAt) / (1000 * 60 * 60 * 24)); + + // Check if there's a linked PR + const hasLinkedPR = issue.body?.includes('Closes') || + issue.body?.includes('Fixes') || + issue.body?.includes('Resolves') || + issue.body?.includes('#'); + + // Check if there are open PRs referencing this issue + const prs = await octokit.rest.pulls.list({ + owner, + repo, + state: 'open', + per_page: 100 + }); + + const linkedPR = prs.data.find(pr => + pr.body?.includes(`#${issue.number}`) || + pr.title.includes(`#${issue.number}`) + ); + + // Skip if there's active PR activity + if (linkedPR) { + console.log(`Issue #${issue.number} has active PR #${linkedPR.number} - skipping`); + continue; + } + + // Check for existing labels + const hasWarningLabel = issue.labels.some(l => l.name === WARNING_LABEL); + const hasReclaimLabel = issue.labels.some(l => l.name === RECLAIM_LABEL); + + // Stage 1: Warning comment + if (daysSinceUpdate >= WARNING_DAYS && !hasWarningLabel && !hasReclaimLabel) { + const warningComment = "⚠️ **Stale Assignment Warning**\n\n" + + "This issue has been assigned to @" + assignees.join(', @') + " for " + daysSinceAssignment + " days with no linked PR activity for " + daysSinceUpdate + " days.\n\n" + + "To keep this assignment active, please:\n" + + "1. Open a draft PR if you're working on it\n" + + "2. Comment with an update on your progress\n" + + "3. Unassign yourself if you're no longer working on it\n\n" + + "If no activity is detected within " + (RECLAIM_DAYS - WARNING_DAYS) + " days, the assignment will be automatically removed to allow others to claim this issue.\n\n" + + "---\n" + + "*This is an automated message. Contact maintainers if you believe this is an error.*"; + + await octokit.rest.issues.createComment({ + owner, + repo, + issue_number: issue.number, + body: warningComment + }); + + await octokit.rest.issues.addLabels({ + owner, + repo, + issue_number: issue.number, + labels: [WARNING_LABEL] + }); + + console.log("Added warning to issue #" + issue.number); + } + + // Stage 2: Reclaim assignment + if (daysSinceUpdate >= RECLAIM_DAYS && !hasReclaimLabel) { + const reclaimComment = "🔄 **Assignment Reclaimed**\n\n" + + "This issue was previously assigned to @" + assignees.join(', @') + " but has shown no activity for " + daysSinceUpdate + " days.\n\n" + + "The assignment has been automatically removed to allow other contributors to claim this issue. If you were still working on it, please re-assign yourself and open a PR promptly.\n\n" + + "---\n" + + "*This is an automated message. Contact maintainers if you believe this is an error.*"; + + await octokit.rest.issues.createComment({ + owner, + repo, + issue_number: issue.number, + body: reclaimComment + }); + + // Remove all assignees + await octokit.rest.issues.removeAssignees({ + owner, + repo, + issue_number: issue.number, + assignees: assignees + }); + + // Update labels + await octokit.rest.issues.removeLabel({ + owner, + repo, + issue_number: issue.number, + name: WARNING_LABEL + }).catch(() => {}); // Ignore if label doesn't exist + + await octokit.rest.issues.addLabels({ + owner, + repo, + issue_number: issue.number, + labels: [RECLAIM_LABEL] + }); + + console.log("Reclaimed assignment for issue #" + issue.number); + } + } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2eb5bcc..1d1b2fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -800,6 +800,44 @@ When you open a PR, GitHub will automatically suggest reviewers based on the fil If you become a regular contributor to a specific area of the codebase, you can request to be added as a code owner. Contact a maintainer to discuss this. +## Stale Assignment Policy + +To maintain effective Wave throughput and ensure issues don't get claimed and abandoned, this repository uses an automated stale assignment reclaimer. + +### How It Works + +The stale assignment bot runs daily and monitors assigned issues: + +1. **Warning Stage (7 days of inactivity)** + - If an issue has been assigned for 7+ days with no linked PR activity, a warning comment is added + - The issue is labeled with `stale-assignment-warning` + - The assignee is notified with instructions to either: + - Open a draft PR + - Comment with a progress update + - Unassign themselves if no longer working on it + +2. **Reclaim Stage (14 days of inactivity)** + - If no activity is detected for 14+ days, the assignment is automatically removed + - The issue is labeled with `assignment-reclaimed` + - Other contributors can then claim the issue + +### Configuration + +The timeout periods are configurable in `.github/workflows/stale-assignments.yml`: +- `WARNING_DAYS`: Days before warning comment (default: 7) +- `RECLAIM_DAYS`: Days before unassignment (default: 14) + +### For Contributors + +- **When claiming an issue**: Open a draft PR within 7 days to show active work +- **If you need more time**: Comment on the issue with a progress update to reset the timer +- **If you can't complete it**: Unassign yourself promptly so others can claim it +- **After reclamation**: If your assignment was reclaimed but you're still working on it, re-assign yourself and open a PR promptly + +### Exemptions + +Issues with linked open PRs are automatically exempt from the stale assignment check. + ## Code of Conduct Please be respectful and constructive in all interactions. We aim to create a welcoming environment for all contributors. diff --git a/README.md b/README.md index f77314d..1454cc2 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,7 @@ The analytics view brings together yield, volume, and market trends for deeper p ## 🔗 Useful Links & Documentation +- **Documentation Index**: Browse all project documentation in [docs/README.md](docs/README.md). - **Getting Started Guide**: Refer to the [Quick Start](#-quick-start) section. - **Developer Quickstart**: Follow the full setup guide in [docs/developer-quickstart.md](docs/developer-quickstart.md). - **Component Library (Storybook)**: Browse the full component library with interactive controls, variants, and a11y checks at the [published Storybook](https://invoice-liquidity-network.github.io/ILN-Frontend) (deployed from `main`). diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..180e6c7 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,35 @@ +# Documentation Index + +This directory contains comprehensive documentation for the ILN Frontend project. + +## Core Documentation + +- **[architecture.md](architecture.md)** - Frontend architecture overview covering design decisions, folder structure, data flow patterns, and architectural trade-offs. Essential for understanding the codebase structure and development conventions. + +- **[developer-quickstart.md](developer-quickstart.md)** - Step-by-step guide from fresh clone to running local development environment. Covers prerequisites, installation, and initial setup for all platforms. + +## Testing & Quality Assurance + +- **[testing.md](testing.md)** - Comprehensive testing guide covering unit tests (Vitest), end-to-end tests (Playwright), and testing best practices. + +- **[VISUAL_REGRESSION_WORKFLOW.md](VISUAL_REGRESSION_WORKFLOW.md)** - Explains the Chromatic visual regression testing workflow, including how to approve/reject UI changes and add new Storybook stories. + +- **[LIGHTHOUSE_CI.md](LIGHTHOUSE_CI.md)** - Documentation for Lighthouse CI performance budget tests, including performance thresholds, tested pages, and how to review reports. + +## CI/CD & Infrastructure + +- **[ci-cd.md](ci-cd.md)** - CI/CD pipeline documentation covering GitHub Actions workflows, deployment processes, and self-hosted runner configuration. + +## Domain-Specific Documentation + +- **[contract-fixtures.md](contract-fixtures.md)** - Documentation for contract test fixtures used in integration testing with Stellar smart contracts. + +- **[repo-size-audit.md](repo-size-audit.md)** - Repository size analysis and optimization recommendations for maintaining a lean codebase. + +## Hooks Documentation + +- **[hooks/](hooks/)** - Detailed documentation for custom React hooks, including usage examples and API references. + +--- + +**Note**: This index should be updated whenever new documentation is added to the `docs/` directory to maintain discoverability.