From 3597998b5817bdc724133de744b4ceb6118f2f7e Mon Sep 17 00:00:00 2001 From: Christian Wooddell Date: Wed, 8 Nov 2023 11:17:13 -0800 Subject: [PATCH] Revert "Feature - Add GitHub Action to add contributors to README" --- .github/ISSUE_TEMPLATE/4_badge.md | 10 +- .github/scripts/add_contributor.js | 386 -------------------------- .github/workflows/add_contributor.yml | 32 --- 3 files changed, 7 insertions(+), 421 deletions(-) delete mode 100644 .github/scripts/add_contributor.js delete mode 100644 .github/workflows/add_contributor.yml diff --git a/.github/ISSUE_TEMPLATE/4_badge.md b/.github/ISSUE_TEMPLATE/4_badge.md index c03aa30ed3..6a599b3f0f 100644 --- a/.github/ISSUE_TEMPLATE/4_badge.md +++ b/.github/ISSUE_TEMPLATE/4_badge.md @@ -1,17 +1,21 @@ --- name: 🥇 Leo Contributor Badge about: Claiming a Leo Contributor Badge -title: "Add YOUR_USERNAME to contributors" +title: "[Badge - YOUR_GH_USERNAME]" labels: 'badge' --- ## 🥇 Leo Contributor Badge + + +(Fill in the request here.) + diff --git a/.github/scripts/add_contributor.js b/.github/scripts/add_contributor.js deleted file mode 100644 index 85d4300b64..0000000000 --- a/.github/scripts/add_contributor.js +++ /dev/null @@ -1,386 +0,0 @@ -const { Octokit } = require("@octokit/rest"); -const github = require("@actions/github"); - -const octokit = new Octokit({ - auth: process.env.GITHUB_TOKEN, -}); - -async function processIssue() { - - const context = github.context; - const issueTitle = context.payload.issue.title; - - // Check if the title of the issue matches the expected format - if (!issueTitle.startsWith("Add ") || !issueTitle.endsWith(" to contributors")) { - console.log("This is not a request to add a contributor."); - return; - } - - const { owner, repo } = context.repo; - const issueNumber = context.payload.issue.number; - const contributorName = issueTitle.slice(4, -16); - // Regex checks for both markdown and plain text links - const repoRegex = /Repo: (https?:\/\/github\.com\/[^\s\)]+|\[URL\]\((https:\/\/github\.com\/[^\s\)]+)\))/; - - - const issueOpener = context.payload.issue.user.login; - - // Check if the contributor name in the title matches the username of the issue opener - if (contributorName !== issueOpener) { - let message = `Hey @${issueOpener}, please make sure you're requesting to add your own name in the issue title! 😅`; - await commentAndTagUser(owner, repo, issueNumber, message); - console.log(`The contributor name "${contributorName}" does not match the issue opener's username "${issueOpener}"`); - return; - } - - // Check if the issue body contains a link to a GitHub repository - const repoMatch = context.payload.issue.body.match(repoRegex); - if (!repoMatch) { - let message = `Hey @${contributorName}, you need to include a link to your Leo repo in the issue body! 😄`; - commentAndTagUser(owner, repo, issueNumber, message); - console.error("No repo URL found in the issue body."); - return; - } - - const repoURL = (repoMatch[1] || repoMatch[2]).replace(/\)$/,''); - const [repoName, ownerName] = repoURL.split('/').reverse(); - - // Check if the user has starred the Leo repo - let hasStarred = false; - - // Note: This doesn't handle pagination. If they starred the repo more than 100 starred repos ago we will have an issue. - try { - const starredRepos = await octokit.activity.listReposStarredByUser({ - username: issueOpener, - per_page: 100 - }); - hasStarred = starredRepos.data.some(repo => repo.full_name === 'AleoHQ/leo'); - - } catch (error) { - console.error(`An error occurred while checking starred status: ${error}`); - } - - if (hasStarred) { - console.log(`${contributorName} has starred the repo`); - } else { - let message = `Hey @${contributorName}, you need to star the [Leo repo](https://github.com/AleoHQ/leo) to be added as a contributor! Go give it a 🌟!`; - await commentAndTagUser(owner, repo, issueNumber, message); - console.log(`${contributorName} has not starred the repo`) - return; - } - - // Extract the requested badge from the issue body. - const badgeRegex = /Requested badge: (\w+)/; - const match = context.payload.issue.body.match(badgeRegex); - - if (!match) { - let message = `Hey @${contributorName}, you need to specify the requested badge in the issue body! 😄`; - await commentAndTagUser(owner, repo, issueNumber, message); - console.log('Badge not specified in the issue body.'); - return; - } - - const badgeType = match[1].toLowerCase(); - console.log(`Badge Type: ${badgeType}`); - - const badgeMapping = { - audio: {emoji: '🔊', title: 'Audio'}, - a11y: {emoji: '♿️', title: 'Accessibility'}, - bug: {emoji: '🐛', title: 'Bug reports'}, - blog: {emoji: '📝', title: 'Blogposts'}, - business: {emoji: '💼', title: 'Business Development'}, - code: {emoji: '💻', title: 'Code'}, - content: {emoji: '🖋', title: 'Content'}, - data: {emoji: '🔣', title: 'Data'}, - doc: {emoji: '📖', title: 'Documentation'}, - design: {emoji: '🎨', title: 'Design'}, - example: {emoji: '💡', title: 'Examples'}, - eventOrganizing: {emoji: '📋', title: 'Event Organizers'}, - financial: {emoji: '💵', title: 'Financial Support'}, - fundingFinding: {emoji: '🔍', title: 'Funding/Grant Finders'}, - ideas: {emoji: '🤔', title: 'Ideas & Planning'}, - infra: {emoji: '🚇', title: 'Infrastructure'}, - maintenance: {emoji: '🚧', title: 'Maintenance'}, - mentoring: {emoji: '🧑‍🏫', title: 'Mentoring'}, - platform: {emoji: '📦', title: 'Packaging'}, - plugin: {emoji: '🔌', title: 'Plugin/utility libraries'}, - projectManagement: {emoji: '📆', title: 'Project Management'}, - promotion: {emoji: '📣', title: 'Promotion'}, - question: {emoji: '💬', title: 'Answering Questions'}, - research: {emoji: '🔬', title: 'Research'}, - review: {emoji: '👀', title: 'Reviewed Pull Requests'}, - security: {emoji: '🛡️', title: 'Security'}, - tool: {emoji: '🔧', title: 'Tools'}, - translation: {emoji: '🌍', title: 'Translation'}, - test: {emoji: '⚠️', title: 'Tests'}, - tutorial: {emoji: '✅', title: 'Tutorials'}, - talk: {emoji: '📢', title: 'Talks'}, - userTesting: {emoji: '📓', title: 'User Testing'}, - video: {emoji: '📹', title: 'Videos'} - }; - - // Check that they are author of the linked repo - if (ownerName !== contributorName) { - console.log("owner name", ownerName, "contributor name", contributorName); - let message = `Hey @${contributorName}, you need to link to your own repo! 😄`; - await commentAndTagUser(owner, repo, issueNumber, message); - console.log(`The contributor "${contributorName}" does not own the repo "${repoName}"`); - return; - } - - // Check if the repo contains a valid Leo application - - console.log("repo name", repoName); - try { - await octokit.repos.getContent({ - owner: ownerName, - repo: repoName, - path: 'src/main.leo', - }); - console.log("repo name", repoName); - console.log(`The repository "${repoName}" under owner "${ownerName}" contains a valid Leo application.`); - } catch (error) { - // Check if the error is a 404 and if the message indicates that the repo is not found - if (error.status === 404 && error.response && error.response.data && error.response.data.message.includes('repo not found')) { - console.log(`The repository "${repoName}" under owner "${ownerName}" does not exist or is private.`); - let message = `Hey @${issueOpener}, we could not access the repository you linked. Please ensure the repository exists and is public.`; - await commentAndTagUser(owner, repo, issueNumber, message); - return; - } - // If main.leo is not found, check for any .leo file - try { - const { data } = await octokit.git.getTree({ - owner: ownerName, - repo: repoName, - tree_sha: 'HEAD', - recursive: 'true' - }); - - const leoFiles = data.tree.filter(file => file.path.endsWith('.leo')); - - if (leoFiles.length > 0) { - // Found .leo files but not src/main.leo - console.log(`Found .leo files but not src/main.leo in the repo "${repoName}" under owner "${ownerName}".`); - let message = `Hey @${contributorName}, we found .leo files in your repo but not main.leo! Consider adding a main.leo file to your repo. 😄`; - await commentAndTagUser(owner, repo, issueNumber, message); - return; - } else { - // No .leo files found at all - console.log(`No .leo files found in the repo "${repoName}" under owner "${ownerName}".`); - let message = `Hey @${contributorName}, the repo you linked does not contain a valid Leo application! 😅`; - await commentAndTagUser(owner, repo, issueNumber, message); - return; - } - } catch (error) { - console.error(`Error searching for .leo files in the repo "${repoName}":`, error); - return; - } - } - // Fetch README from the GitHub repo - const { data: readme } = await octokit.repos.getContent({ - owner, - repo, - path: 'README.md', - }); - - const readmeContent = Buffer.from(readme.content, 'base64').toString('utf-8'); - - async function commentAndTagUser(owner, repo, issueNumber, message) { - try { - await octokit.issues.createComment({ - owner, - repo, - issue_number: issueNumber, - body: message, - }); - console.log("Comment added successfully"); - } catch (error) { - console.error("Error creating comment:", error); - } - } - - async function createPRWithBadge({ owner, repo, updatedReadme, readme, contributorName, badgeType, issueNumber }) { - // 1. Create a new branch - const { data: branch } = await octokit.repos.getBranch({ - owner, - repo, - branch: 'master' - }); - const latestCommitSha = branch.commit.sha; - const branchName = `add-${badgeType}-badge-for-${contributorName}-${Date.now()}`; // Unique branch name - await octokit.git.createRef({ - owner, - repo, - ref: `refs/heads/${branchName}`, - sha: latestCommitSha - }); - - // 2. Commit the changes to the new branch - const updatedReadmeBase64 = Buffer.from(updatedReadme).toString('base64'); - await octokit.repos.createOrUpdateFileContents({ - owner, - repo, - path: 'README.md', - message: `Add ${contributorName} to README contributors`, - content: updatedReadmeBase64, - sha: readme.sha, - branch: branchName - }); - - // 3. Open a Pull Request - const { data: createdPR } = await octokit.pulls.create({ - owner, - repo, - title: `Add ${contributorName} to README contributors`, - head: branchName, - base: 'master', - body: `closes #${issueNumber}` - }); - - // 4. Add @AleoHQ/tech-ops as a reviewer - await octokit.pulls.requestReviewers({ - owner, - repo, - pull_number: createdPR.number, - reviewers: ['AleoHQ/tech-ops'] - }); - - console.log(`Created a PR for "${contributorName}" with the "${badgeType}" badge.`); - } - - // Check if the user's name exists in the README - const userRegex = new RegExp(contributorName); - if (userRegex.test(readmeContent)) { - console.log(`The contributor "${contributorName}" is found in the README.`); - - const badgeCheckRegex = new RegExp(``, 'i'); - - if (badgeCheckRegex.test(readmeContent)) { - let message = `Hey @${contributorName}, you already have the "${badgeType}" badge! 😄`; - await commentAndTagUser(owner, repo, issueNumber, message); - console.log(`The contributor "${contributorName}" already has the "${badgeType}" badge.`); - return; - } else { - console.log('Badge not found in the README for the contributor.'); - // Identify the position to insert the new badge for existing contributor - const insertionRegex = new RegExp(`(https://github.com/${contributorName}/[^"]*"[^>]*>.*?)`); - const match = readmeContent.match(insertionRegex); - - if (match) { - const insertionPoint = match.index + match[0].length; - const badgeDetails = badgeMapping[badgeType]; - - if (!badgeDetails) { - let message = `Hey @${contributorName}, the badge type "${badgeType}" is not recognized! 😅`; - await commentAndTagUser(owner, repo, issueNumber, message); - throw new Error(`Badge type "${badgeType}" not recognized.`); - } - - // NOTE: Currently adds link to contributor's page if not tutorial or code - let badgeLink; - switch(badgeType.toLowerCase()) { - case 'tutorial': - badgeLink = `https://github.com/${contributorName}/${repoName}`; - break; - case 'code': - badgeLink = `https://github.com/AleoHQ/leo/commits?author=${contributorName}`; - break; - default: - badgeLink = `https://github.com/${contributorName}`; - break; - } - - const newBadge = `${badgeDetails.emoji}`; - - const updatedReadme = [ - readmeContent.slice(0, insertionPoint), - newBadge, - readmeContent.slice(insertionPoint) - ].join(''); - - await createPRWithBadge({ - owner, - repo, - updatedReadme, - readme, - contributorName, - badgeType, - issueNumber - }); - - console.log(`Created a PR to add the "${badgeType}" badge for the contributor "${contributorName}" in the README.`); - } else { - let message = `Hey @${contributorName}, we had an issue with your request, please reach out 😅`; - await commentAndTagUser(owner, repo, issueNumber, message); - console.error(`Failed to find an insertion point for the "${badgeType}" badge for "${contributorName}".`); - } - } - } else { - console.log(`The contributor "${contributorName}" is NOT found in the README.`); - let contributorCountMatch = readmeContent.match(/Total count contributors: (\d+)/i); - let currentCount = contributorCountMatch ? parseInt(contributorCountMatch[1]) : 0; - - const badgeDetails = badgeMapping[badgeType]; - if (!badgeDetails) { - let message = `Hey @${contributorName}, we had an issue with your request, please reach out 😅`; - await commentAndTagUser(owner, repo, issueNumber, message); - throw new Error(`Badge type "${badgeType}" not recognized.`); - } - - // regex that finds where to place the new badge by finding the second to last that contains s - const trMatches = [...readmeContent.matchAll(/\s*([\s\S]*?)<\/tr>/g)]; - const trMatchesContainingTds = trMatches.filter(trMatch => /]*>[\s\S]*?<\/td>/g.test(trMatch[1])); - const secondToLastTrContainingTds = trMatchesContainingTds[trMatchesContainingTds.length - 2]; - - if (secondToLastTrContainingTds) { - const tdMatchesInTr = secondToLastTrContainingTds[1].match(/]*>[\s\S]*?<\/td>/g) || []; - let updatedReadme; - const newContributorBlock = ` - ${newBadge}${contributorName}
${contributorName}
${badgeDetails.emoji} - `; - - if (tdMatchesInTr.length < 7) { - // Insert the new contributor in the last row if there are less than 7 contributors in that row - const lastTdEndIndex = secondToLastTrContainingTds.index + secondToLastTrContainingTds[0].lastIndexOf('') + 5; - updatedReadme = [ - readmeContent.slice(0, lastTdEndIndex), - newContributorBlock, - readmeContent.slice(lastTdEndIndex) - ].join(''); - } else { - // Insert a new row with the new contributor if there are already 7 contributors in the last row - updatedReadme = [ - readmeContent.slice(0, secondToLastTrContainingTds.index + secondToLastTrContainingTds[0].length), - '\n', - newContributorBlock, - '\n', - readmeContent.slice(secondToLastTrContainingTds.index + secondToLastTrContainingTds[0].length) - ].join(''); - } - - // Increment and update contributor count - currentCount++; - updatedReadme = updatedReadme.replace(/Total count contributors: \d+/i, `Total count contributors: ${currentCount}`); - - await createPRWithBadge({ - owner, - repo, - updatedReadme, - readme, - contributorName, - badgeType, - issueNumber - }); - console.log(`Created a PR to add "${contributorName}" to the README contributors with the "${badgeType}" badge.`); - } else { - let message = `Hey @${contributorName}, we had an issue with your request, please reach out 😅`; - await commentAndTagUser(owner, repo, issueNumber, message); - console.error(`Failed to find the insertion point in the README.`); - } - } -} - -processIssue().catch(error => { - console.error(error); - process.exit(1); -}); diff --git a/.github/workflows/add_contributor.yml b/.github/workflows/add_contributor.yml deleted file mode 100644 index 0d54dcadbc..0000000000 --- a/.github/workflows/add_contributor.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Add-Contributor - -on: - issues: - types: [opened] - -jobs: - validate_and_add_contributor: - runs-on: ubuntu-latest - - permissions: - issues: write - contents: write - pull-requests: write - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Use Node.js 18.x - uses: actions/setup-node@v3 - with: - node-version: '18.x' - - - name: Install dependencies - run: | - npm install @actions/github@6.0.0 @octokit/rest@20.0.2 - - - name: Run the script to process the issue - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: node .github/scripts/add_contributor.js