diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yml new file mode 100644 index 0000000000..b1a5a34427 --- /dev/null +++ b/.github/workflows/add-to-project.yml @@ -0,0 +1,186 @@ +--- +name: Add to Project + +on: + pull_request: + types: [opened, reopened] + branches: ['1.8.x'] + +jobs: + add-to-project: + name: Add PR or linked issue to project + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + organization-projects: write + + steps: + - name: Check if PR is from Core team + id: check-core-team + uses: actions/github-script@v7 + with: + script: | + const { data: user } = await github.rest.users.getByUsername({ + username: context.payload.pull_request.user.login + }); + + // Check if user is a member of the Appwrite organization + try { + const { data: membership } = + await github.rest.orgs.getMembershipForUser({ + org: 'appwrite', + username: context.payload.pull_request.user.login + }); + + // Check if user has admin or write permissions (Core team) + const isCoreTeam = membership.role === 'admin' || + (membership.state === 'active' && + ['admin', 'member'].includes(membership.role)); + + console.log( + `User ${context.payload.pull_request.user.login} ` + + `is core team: ${isCoreTeam}` + ); + core.setOutput('is-core-team', isCoreTeam); + + return isCoreTeam; + } catch (error) { + console.log( + `User ${context.payload.pull_request.user.login} ` + + `is not a member of appwrite org` + ); + core.setOutput('is-core-team', false); + return false; + } + + - name: Get linked issues + id: get-linked-issues + if: steps.check-core-team.outputs.is-core-team == 'true' + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.pull_request.number; + + // Get PR body and check for linked issues + const prBody = context.payload.pull_request.body || ''; + + // Look for common patterns that indicate linked issues + const patterns = [ + /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi, + /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+(?:https?:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/)(\d+)/gi, + /#(\d+)/g + ]; + + const linkedIssues = new Set(); + + for (const pattern of patterns) { + let match; + while ((match = pattern.exec(prBody)) !== null) { + linkedIssues.add(parseInt(match[1])); + } + } + + const linkedIssuesArray = Array.from(linkedIssues); + console.log(`Found linked issues: ${linkedIssuesArray.join(', ')}`); + + core.setOutput('linked-issues', JSON.stringify(linkedIssuesArray)); + core.setOutput('has-linked-issues', linkedIssuesArray.length > 0); + + return linkedIssuesArray; + + - name: Add PR to project (no linked issues) + if: > + steps.check-core-team.outputs.is-core-team == 'true' && + steps.get-linked-issues.outputs.has-linked-issues == 'false' + uses: actions/github-script@v7 + with: + script: | + const projectId = 15; // GitHub project ID + const prNumber = context.payload.pull_request.number; + + try { + // Add PR to project using GraphQL + const mutation = ` + mutation($projectId: ID!, $contentId: ID!) { + addProjectV2ItemById(input: { + projectId: $projectId, + contentId: $contentId + }) { + item { + id + } + } + } + `; + + // Get the PR node ID + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + + await github.graphql(mutation, { + projectId: `PVT_kwDOAAH2fM4Afpv_`, + contentId: pr.node_id + }); + + console.log(`Successfully added PR #${prNumber} to project`); + } catch (error) { + console.error(`Failed to add PR to project: ${error.message}`); + core.setFailed(`Failed to add PR to project: ${error.message}`); + } + + - name: Add linked issues to project + if: > + steps.check-core-team.outputs.is-core-team == 'true' && + steps.get-linked-issues.outputs.has-linked-issues == 'true' + uses: actions/github-script@v7 + with: + script: | + const projectId = 15; + const linkedIssues = JSON.parse( + '${{ steps.get-linked-issues.outputs.linked-issues }}' + ); + + for (const issueNumber of linkedIssues) { + try { + // Get the issue node ID + const { data: issue } = await github.rest.issues.get({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber + }); + + // Add issue to project using GraphQL + const mutation = ` + mutation($projectId: ID!, $contentId: ID!) { + addProjectV2ItemById(input: { + projectId: $projectId, + contentId: $contentId + }) { + item { + id + } + } + } + `; + + await github.graphql(mutation, { + projectId: `PVT_kwDOAAH2fM4Afpv_`, + contentId: issue.node_id + }); + + console.log( + `Successfully added issue #${issueNumber} to project` + ); + } catch (error) { + console.error( + `Failed to add issue #${issueNumber} to project: ` + + `${error.message}` + ); + // Continue with other issues even if one fails + } + }