diff --git a/packages/docs/package.json b/packages/docs/package.json index 1402901c35..357a892888 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "npm run gen:examples && npm run gen:props && next dev", - "build-docs": "echo $VTEX_GITHUB_BOT_TOKEN && npm --prefix ../../ run build && npm run gen:contributors && npm run gen:examples && npm run gen:props && next build", + "build-docs": "npm --prefix ../../ run build && npm run gen:contributors && npm run gen:examples && npm run gen:props && next build", "start": "next start", "gen:examples": "node ./scripts/build-examples.mjs", "gen:props": "node ./scripts/build-props.mjs", diff --git a/packages/docs/scripts/build-contributors.mjs b/packages/docs/scripts/build-contributors.mjs index 78044c8e49..9421a117a8 100644 --- a/packages/docs/scripts/build-contributors.mjs +++ b/packages/docs/scripts/build-contributors.mjs @@ -1,34 +1,38 @@ -import { config } from 'dotenv' +import { config } from "dotenv"; -import { graphql } from '@octokit/graphql' -import path from 'node:path' -import fse from 'fs-extra' -import { format } from 'prettier' +import { graphql } from "@octokit/graphql"; +import path from "node:path"; +import fse from "fs-extra"; +import { format } from "prettier"; -config() +config(); -const statsOutputDirectory = `${path.dirname('')}/__contributions__` -const contributorsOutputDirectory = `${path.dirname('')}/pages/guides/contributor` -const VTEX_ORG = 'vtex' -const REPO_NAME = 'shoreline' -const token = process.env.VTEX_GITHUB_BOT_TOKEN -const startDate = new Date('2024-01-01T00:00:00Z').toISOString() +const statsOutputDirectory = `${path.dirname("")}/__contributions__`; +const contributorsOutputDirectory = `${path.dirname("")}/pages/guides/contributor`; +const VTEX_ORG = "vtex"; +const REPO_NAME = "shoreline"; +const token = process.env.VTEX_GITHUB_BOT_TOKEN ?? ""; +const startDate = new Date("2024-01-01T00:00:00Z").toISOString(); + +if (!token) { + console.log("Github token invalid!"); +} const graphqlWithAuth = graphql.defaults({ - headers: { - authorization: `token ${token}`, - }, -}) + headers: { + authorization: `token ${token}`, + }, +}); -const pageSize = 100 +const pageSize = 100; async function fetchAllIssues() { - let hasNextPage = true - let endCursor = null - let allIssues = [] + let hasNextPage = true; + let endCursor = null; + let allIssues = []; - while (hasNextPage) { - const query = ` + while (hasNextPage) { + const query = ` query ($repoOwner: String!, $repoName: String!, $first: Int!, $after: String) { repository(owner: $repoOwner, name: $repoName) { issues(first: $first, after: $after, orderBy: {field: CREATED_AT, direction: DESC}) { @@ -63,63 +67,63 @@ async function fetchAllIssues() { } } } - ` - - try { - const result = await graphqlWithAuth(query, { - repoOwner: VTEX_ORG, - repoName: REPO_NAME, - first: pageSize, - after: endCursor, - }) - - const issuesPage = result.repository.issues - - allIssues = [...allIssues, ...issuesPage.nodes] - hasNextPage = issuesPage.pageInfo.hasNextPage - endCursor = issuesPage.pageInfo.endCursor - } catch (error) { - console.error('Error fetching issues:', error) - break - } - } - - return allIssues + `; + + try { + const result = await graphqlWithAuth(query, { + repoOwner: VTEX_ORG, + repoName: REPO_NAME, + first: pageSize, + after: endCursor, + }); + + const issuesPage = result.repository.issues; + + allIssues = [...allIssues, ...issuesPage.nodes]; + hasNextPage = issuesPage.pageInfo.hasNextPage; + endCursor = issuesPage.pageInfo.endCursor; + } catch (error) { + console.error("Error fetching issues:", error); + break; + } + } + + return allIssues; } function filterIssuesByUser(username, issues = []) { - const issuesCreatedByUser = issues.filter( - (issue) => issue.author.login === username && issue.createdAt >= startDate - ) - - const issuesAssignedByUser = issues.filter((issue) => { - return ( - issue.createdAt >= startDate && - issue.assignees.nodes.some((assign) => assign.login === username) - ) - }) - - const issuesCommentedByUser = issues.filter((issue) => { - return ( - issue.createdAt >= startDate && - issue.comments.nodes.some((comment) => comment.author.login === username) - ) - }) - - return { - issues: issuesCreatedByUser.length, - assigns: issuesAssignedByUser.length, - comments: issuesCommentedByUser.length, - } + const issuesCreatedByUser = issues.filter( + (issue) => issue.author.login === username && issue.createdAt >= startDate, + ); + + const issuesAssignedByUser = issues.filter((issue) => { + return ( + issue.createdAt >= startDate && + issue.assignees.nodes.some((assign) => assign.login === username) + ); + }); + + const issuesCommentedByUser = issues.filter((issue) => { + return ( + issue.createdAt >= startDate && + issue.comments.nodes.some((comment) => comment.author.login === username) + ); + }); + + return { + issues: issuesCreatedByUser.length, + assigns: issuesAssignedByUser.length, + comments: issuesCommentedByUser.length, + }; } async function fetchAllPullRequests() { - let hasNextPage = true - let endCursor = null - let allPullRequests = [] + let hasNextPage = true; + let endCursor = null; + let allPullRequests = []; - while (hasNextPage) { - const query = ` + while (hasNextPage) { + const query = ` query ($owner: String!, $name: String!, $cursor: String) { repository(owner: $owner, name: $name) { pullRequests(first: 100, after: $cursor) { @@ -154,174 +158,176 @@ async function fetchAllPullRequests() { } } } - ` - - try { - const result = await graphqlWithAuth(query, { - owner: VTEX_ORG, - name: REPO_NAME, - cursor: endCursor, - }) - - const pullRequests = result.repository.pullRequests.nodes - allPullRequests = [...allPullRequests, ...pullRequests] - hasNextPage = result.repository.pullRequests.pageInfo.hasNextPage - endCursor = result.repository.pullRequests.pageInfo.endCursor - } catch (error) { - console.error('Error fetching pull requests:', error) - break - } - } - - return allPullRequests + `; + + try { + const result = await graphqlWithAuth(query, { + owner: VTEX_ORG, + name: REPO_NAME, + cursor: endCursor, + }); + + const pullRequests = result.repository.pullRequests.nodes; + allPullRequests = [...allPullRequests, ...pullRequests]; + hasNextPage = result.repository.pullRequests.pageInfo.hasNextPage; + endCursor = result.repository.pullRequests.pageInfo.endCursor; + } catch (error) { + console.error("Error fetching pull requests:", error); + break; + } + } + + return allPullRequests; } function filterPullsByUser(username, pulls = []) { - const pullsCreatedByUser = pulls.filter( - (pull) => pull.author.login === username && pull.createdAt >= startDate - ) - - const pullsReviewedByUser = pulls.filter((pull) => { - return ( - pull.author.login !== username && - pull.createdAt >= startDate && - pull.participants.nodes.some((participant) => { - return participant.login === username - }) - ) - }) - - const pullsMergedByUser = pullsCreatedByUser.filter( - (pull) => pull.state === 'MERGED' - ) - - return { - pulls: pullsCreatedByUser.length, - reviews: pullsReviewedByUser.length, - merged: pullsMergedByUser.length, - } + const pullsCreatedByUser = pulls.filter( + (pull) => pull.author.login === username && pull.createdAt >= startDate, + ); + + const pullsReviewedByUser = pulls.filter((pull) => { + return ( + pull.author.login !== username && + pull.createdAt >= startDate && + pull.participants.nodes.some((participant) => { + return participant.login === username; + }) + ); + }); + + const pullsMergedByUser = pullsCreatedByUser.filter( + (pull) => pull.state === "MERGED", + ); + + return { + pulls: pullsCreatedByUser.length, + reviews: pullsReviewedByUser.length, + merged: pullsMergedByUser.length, + }; } class ContributorsSet { - constructor() { - this.map = {} - } - - add(contributor) { - if (!contributor || !this.isHumanContributor(contributor)) return - - if (!this.map[contributor.username]) { - this.map[contributor.username] = contributor - } - } - - isHumanContributor(contributor) { - const bots = [ - 'github-actions', - 'changeset-bot', - 'vtexgithubbot', - 'netlify', - 'vercel', - 'dependabot', - 'renovate', - ] - return !bots.includes(contributor.username) - } - - toArray() { - return Object.values(this.map) - } + constructor() { + this.map = {}; + } + + add(contributor) { + if (!contributor || !this.isHumanContributor(contributor)) return; + + if (!this.map[contributor.username]) { + this.map[contributor.username] = contributor; + } + } + + isHumanContributor(contributor) { + const bots = [ + "github-actions", + "changeset-bot", + "vtexgithubbot", + "netlify", + "vercel", + "dependabot", + "renovate", + ]; + return !bots.includes(contributor.username); + } + + toArray() { + return Object.values(this.map); + } } function getRepositoryContributors(issues, pulls) { - const contributors = new ContributorsSet() - - issues.forEach((issue) => { - contributors.add({ - username: issue.author.login, - image: issue.author.avatarUrl, - }) - issue.comments.nodes.forEach((comment) => { - contributors.add({ - username: comment.author.login, - image: comment.author.avatarUrl, - }) - }) - }) - - pulls.forEach((pull) => { - contributors.add({ - username: pull.author.login, - image: pull.author.avatarUrl, - }) - pull.comments.nodes.forEach((comment) => { - contributors.add({ - username: comment.author.login, - image: comment.author.avatarUrl, - }) - }) - }) - - return contributors.toArray() + const contributors = new ContributorsSet(); + + issues.forEach((issue) => { + contributors.add({ + username: issue.author.login, + image: issue.author.avatarUrl, + }); + issue.comments.nodes.forEach((comment) => { + contributors.add({ + username: comment.author.login, + image: comment.author.avatarUrl, + }); + }); + }); + + pulls.forEach((pull) => { + contributors.add({ + username: pull.author.login, + image: pull.author.avatarUrl, + }); + pull.comments.nodes.forEach((comment) => { + contributors.add({ + username: comment.author.login, + image: comment.author.avatarUrl, + }); + }); + }); + + return contributors.toArray(); } function getContributorStats(username, issues, pulls) { - const issuesStats = filterIssuesByUser(username, issues) - const pullsStats = filterPullsByUser(username, pulls) - - const rate = - (issuesStats.issues + - issuesStats.assigns + - issuesStats.comments + - pullsStats.pulls + - pullsStats.reviews + - pullsStats.merged) / - 6 - - return { ...issuesStats, ...pullsStats, rate } + const issuesStats = filterIssuesByUser(username, issues); + const pullsStats = filterPullsByUser(username, pulls); + + const rate = + (issuesStats.issues + + issuesStats.assigns + + issuesStats.comments + + pullsStats.pulls + + pullsStats.reviews + + pullsStats.merged) / + 6; + + return { ...issuesStats, ...pullsStats, rate }; } function getIssuesOnFire(issues) { - issues.sort((a, b) => b.comments.nodes.length - a.comments.nodes.length) + issues.sort((a, b) => b.comments.nodes.length - a.comments.nodes.length); - const openedIssues = issues.filter((issue) => issue.state === 'OPEN') + const openedIssues = issues.filter((issue) => issue.state === "OPEN"); - openedIssues.sort((a, b) => b.comments.nodes.length - a.comments.nodes.length) + openedIssues.sort( + (a, b) => b.comments.nodes.length - a.comments.nodes.length, + ); - const issuesOnFire = openedIssues.slice(0, 4) + const issuesOnFire = openedIssues.slice(0, 4); - return issuesOnFire + return issuesOnFire; } async function main() { - if (!token) { - console.log('⚠️ Missing Github token') - console.log( - 'To run this script locally you must create a .env file with the VTEX_GITHUB_BOT_TOKEN which gives access to the public_repo scope' - ) - return - } - - const pulls = await fetchAllPullRequests() - const issues = await fetchAllIssues() - - const contributors = getRepositoryContributors(issues, pulls) - const issuesOnFire = getIssuesOnFire(issues) - const stats = contributors.map((contributor) => { - const stats = getContributorStats(contributor.username, issues, pulls) - - return { - ...contributor, - stats, - } - }) - - stats.sort((a, b) => b.stats.rate - a.stats.rate) - - /** - * Generate contributors stats file - */ - const code = ` + if (!token) { + console.log("⚠️ Missing Github token"); + console.log( + "To run this script locally you must create a .env file with the VTEX_GITHUB_BOT_TOKEN which gives access to the public_repo scope", + ); + return; + } + + const pulls = await fetchAllPullRequests(); + const issues = await fetchAllIssues(); + + const contributors = getRepositoryContributors(issues, pulls); + const issuesOnFire = getIssuesOnFire(issues); + const stats = contributors.map((contributor) => { + const stats = getContributorStats(contributor.username, issues, pulls); + + return { + ...contributor, + stats, + }; + }); + + stats.sort((a, b) => b.stats.rate - a.stats.rate); + + /** + * Generate contributors stats file + */ + const code = ` export interface Contributor { username: string image: string @@ -355,26 +361,26 @@ export function getContributors() { !maintainers.includes(contributor.username) && contributor.stats.rate > 0 ) } - ` - - const formattedCode = await format(code, { - parser: 'typescript', - semi: false, - singleQuote: true, - }) - - fse.outputFile(`${statsOutputDirectory}/stats.ts`, formattedCode, (err) => { - if (err) { - console.log(err) - } else { - console.log('✅ Contributor stats generated') - } - }) - - /** - * Generate issues stats file - */ - const issuesCode = ` + `; + + const formattedCode = await format(code, { + parser: "typescript", + semi: false, + singleQuote: true, + }); + + fse.outputFile(`${statsOutputDirectory}/stats.ts`, formattedCode, (err) => { + if (err) { + console.log(err); + } else { + console.log("✅ Contributor stats generated"); + } + }); + + /** + * Generate issues stats file + */ + const issuesCode = ` interface Author { login: string avatarUrl: string @@ -392,31 +398,31 @@ export interface Issue { } export const issuesOnFire: Issue[] = ${JSON.stringify(issuesOnFire)} - ` - - const formattedIssuesCode = await format(issuesCode, { - parser: 'typescript', - semi: false, - singleQuote: true, - }) - - fse.outputFile( - `${statsOutputDirectory}/issues.ts`, - formattedIssuesCode, - (err) => { - if (err) { - console.log(err) - } else { - console.log('✅ Issues on fire generated') - } - } - ) - - /** - * Generate contributor page files - */ - const contributorsPromises = contributors.map((contributor) => { - const mdxCode = ` + `; + + const formattedIssuesCode = await format(issuesCode, { + parser: "typescript", + semi: false, + singleQuote: true, + }); + + fse.outputFile( + `${statsOutputDirectory}/issues.ts`, + formattedIssuesCode, + (err) => { + if (err) { + console.log(err); + } else { + console.log("✅ Issues on fire generated"); + } + }, + ); + + /** + * Generate contributor page files + */ + const contributorsPromises = contributors.map((contributor) => { + const mdxCode = ` --- toc: false --- @@ -428,33 +434,33 @@ import { getContributor } from '../../../__contributions__/stats';
- ` - - return format(mdxCode, { - parser: 'mdx', - semi: false, - singleQuote: true, - }) - }) - - const contributorsMDX = await Promise.all(contributorsPromises) - - for (const i in contributors) { - const contributor = contributors[i] - const contributorMDX = contributorsMDX[i] - - fse.outputFile( - `${contributorsOutputDirectory}/${contributor.username}.mdx`, - contributorMDX, - (err) => { - if (err) { - console.log(err) - } else { - console.log(`✅ ${contributor.username} page generated`) - } - } - ) - } + `; + + return format(mdxCode, { + parser: "mdx", + semi: false, + singleQuote: true, + }); + }); + + const contributorsMDX = await Promise.all(contributorsPromises); + + for (const i in contributors) { + const contributor = contributors[i]; + const contributorMDX = contributorsMDX[i]; + + fse.outputFile( + `${contributorsOutputDirectory}/${contributor.username}.mdx`, + contributorMDX, + (err) => { + if (err) { + console.log(err); + } else { + console.log(`✅ ${contributor.username} page generated`); + } + }, + ); + } } -main() +main();