Skip to content

Commit

Permalink
feat: add automatic contributors list from GitHub
Browse files Browse the repository at this point in the history
Closes #866
Supersedes #986
  • Loading branch information
diogotcorreia committed Jan 10, 2024
1 parent 8b8e4e3 commit 3ec5e22
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 3 deletions.
5 changes: 5 additions & 0 deletions gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ module.exports = {
{ key: 'guides', name: '📚 Guias' },
{ key: 'archive', name: '📥 Arquivo' },
],
github: {
owner: "leic-pt",
repository: "resumos-leic",
},
navbar: {
siteTitle: 'Resumos LEIC-A',
links: [
Expand Down Expand Up @@ -49,6 +53,7 @@ module.exports = {
path: `${__dirname}/content`,
},
},
`gatsby-source-github-contributors`,
`gatsby-plugin-image`,
`gatsby-plugin-sharp`,
{
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
},
"license": "GPL-3.0",
"devDependencies": {
"@octokit/graphql": "^7.0.2",
"prettier": "^3.0.0"
},
"dependencies": {
Expand Down
101 changes: 101 additions & 0 deletions plugins/gatsby-source-github-contributors/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const { graphql } = require("@octokit/graphql");

// mutates `contributors`
function mergeContributors(contributors, pullRequests) {
pullRequests.filter(({author}) => !!author.login).forEach(({author, labels}) => {
const { name, login } = author;
const contributor = contributors[login] || (contributors[login] = {});
const labelSet = contributor.labels || (contributor.labels = new Set());

labels.nodes.filter(label => !!label.name).forEach(label => labelSet.add(label.name));

if (!contributor.name) {
contributor.name = name;
}
})
}

exports.sourceNodes = async ({
actions,
cache,
createContentDigest,
createNodeId,
getNodesByType,
}) => {
const { createNode, touchNode } = actions

const githubToken = process.env.GITHUB_TOKEN;
if (!githubToken) {
console.info("No GitHub token found (GITHUB_TOKEN env var), skipping contributors list");
return;
}

const graphqlGh = graphql.defaults({
headers: {
authorization: `bearer ${githubToken}`,
}
});

const contributors = {};

// TODO cache old pagination
// const lastUpdated = await cache.get("lastUpdated")
// // 30 days cache
// if (lastUpdated > Date.now() - 1000 * 60 * 60 * 24 * 30) {
// getNodesByType("Space").forEach(node => touchNode(node))
// return
// }

// await cache.set("lastUpdated", Date.now())

const getPullRequests = async (cursor) => {
const { repository } = await graphqlGh(`
query fetchPullRequests($cursor: String) {
repository(owner: "leic-pt", name: "resumos-leic") {
pullRequests(first: 10, after: $cursor, states: [MERGED], orderBy: {field: UPDATED_AT, direction: DESC}) {
pageInfo {
endCursor
hasNextPage
}
nodes {
updatedAt
labels (first: 50) {
nodes {
name
}
}
author {
... on User {
name
login
}
}
}
}
}
}`);

return repository?.pullRequests
}

mergeContributors(contributors, (await getPullRequests(null)).nodes);

Object.entries(contributors).forEach(([username, contributor]) => {
const data = {
username,
...contributor,
name: contributor.name || username,
labels: [...(contributor.labels || [])],
};

createNode({
...data,
id: createNodeId(data.username),
internal: {
type: "Contributor",
content: JSON.stringify(data),
contentDigest: createContentDigest(data),
},
})
});
}
4 changes: 4 additions & 0 deletions plugins/gatsby-source-github-contributors/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "gatsby-source-github-contributors",
"description": "Fetch GitHub contributors for this repository"
}
60 changes: 60 additions & 0 deletions src/components/Contributors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Link, useStaticQuery, graphql } from 'gatsby';
import React, { useMemo } from 'react';
import ExternalLink from './ExternalLink';
import SearchBar from './SearchBar';
import ThemeSettings from './ThemeSettings';

const Contributors = ({ getLabelName, contributors }) => {
const data = useStaticQuery(graphql`
query ContributorsQuery {
site {
siteMetadata {
github {
owner
repository
}
}
}
}
`);

const {owner, repository} = data.site.siteMetadata.github;

const sortedContributors = useMemo(() => {
return contributors.map(contributor => {
const labels = contributor.labels
.map(getLabelName)
.filter(Boolean)
.map(label => ({ label, bold: false }));
return {
username: contributor.username,
name: contributor.name,
labels,
boldCount: labels.filter(({bold}) => bold).length,
}
})
.sort((a,b) => a.name.localeCompare(b.name))
.sort((a,b) => b.boldCount - a.boldCount)
.sort((a,b) => b.labels.length - a.labels.length)
}, [contributors, getLabelName])

return (
<ul>
{sortedContributors.map(contributor => (
<li key={contributor.username}>
<a href={`https://github.com/${owner}/${repository}/commits?author=${encodeURIComponent(contributor.username)}`}
target="_blank" rel="nofollow noopener noreferrer">
{contributor.name}</a>
{' ('}{contributor.labels.map(({label, bold}, index) => (
<>
{index > 0 && ', '}
{bold ? (<strong>{label}</strong>) : label}
</>
))})
</li>
))}
</ul>
);
};

export default Contributors;
32 changes: 29 additions & 3 deletions src/components/HomePageLayout.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { graphql } from 'gatsby';
import React from 'react';
import React, { useCallback } from 'react';
import IstLogo from '../images/ist-logo.svg';
import '../styles/homepage.css';
import '../styles/main.css';
Expand All @@ -8,9 +8,25 @@ import Footer from './Footer';
import Navbar from './Navbar';
import PageMetadata from './PageMetadata';
import SectionButton, { SectionButtonLayout } from './SectionButton';
import Contributors from './Contributors';

const HomePageLayout = ({ data }) => {
const { markdownRemark: page } = data;
const { markdownRemark: page, allContributor: contributors } = data;

const getLabelName = useCallback((label) => {
const { years } = page.frontmatter;

for (const year of years) {
for (const semester of year.semesters) {
for (const course of semester.courses) {
if (course.link === `/${label}`) {
return course.name;
}
}
}
}
}, [page.frontmatter.years]);

return (
<div className='home-page-container'>
<Navbar />
Expand Down Expand Up @@ -47,7 +63,10 @@ const HomePageLayout = ({ data }) => {
</div>
))}
</div>
<div className='content' dangerouslySetInnerHTML={{ __html: page.html }} />
<div className='content'>
<div dangerouslySetInnerHTML={{ __html: page.html }} />
<Contributors getLabelName={getLabelName} contributors={contributors.nodes} />
</div>
<Footer />
</div>
);
Expand Down Expand Up @@ -80,6 +99,13 @@ export const pageQuery = graphql`
}
}
}
allContributor {
nodes {
username
name
labels
}
}
}
`;

Expand Down
58 changes: 58 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1860,6 +1860,54 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@octokit/endpoint@^9.0.0":
version "9.0.4"
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.4.tgz#8afda5ad1ffc3073d08f2b450964c610b821d1ea"
integrity sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==
dependencies:
"@octokit/types" "^12.0.0"
universal-user-agent "^6.0.0"

"@octokit/graphql@^7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-7.0.2.tgz#3df14b9968192f9060d94ed9e3aa9780a76e7f99"
integrity sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==
dependencies:
"@octokit/request" "^8.0.1"
"@octokit/types" "^12.0.0"
universal-user-agent "^6.0.0"

"@octokit/openapi-types@^19.1.0":
version "19.1.0"
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-19.1.0.tgz#75ec7e64743870fc73e1ab4bc6ec252ecdd624dc"
integrity sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==

"@octokit/request-error@^5.0.0":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-5.0.1.tgz#277e3ce3b540b41525e07ba24c5ef5e868a72db9"
integrity sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==
dependencies:
"@octokit/types" "^12.0.0"
deprecation "^2.0.0"
once "^1.4.0"

"@octokit/request@^8.0.1":
version "8.1.6"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.1.6.tgz#a76a859c30421737a3918b40973c2ff369009571"
integrity sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==
dependencies:
"@octokit/endpoint" "^9.0.0"
"@octokit/request-error" "^5.0.0"
"@octokit/types" "^12.0.0"
universal-user-agent "^6.0.0"

"@octokit/types@^12.0.0":
version "12.4.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.4.0.tgz#8f97b601e91ce6b9776ed8152217e77a71be7aac"
integrity sha512-FLWs/AvZllw/AGVs+nJ+ELCDZZJk+kY0zMen118xhL2zD0s1etIUHm1odgjP7epxYU1ln7SZxEUWYop5bhsdgQ==
dependencies:
"@octokit/openapi-types" "^19.1.0"

"@parcel/[email protected]":
version "2.8.3"
resolved "https://registry.yarnpkg.com/@parcel/bundler-default/-/bundler-default-2.8.3.tgz#d64739dbc2dbd59d6629861bf77a8083aced5229"
Expand Down Expand Up @@ -4775,6 +4823,11 @@ dependency-graph@^0.11.0:
resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27"
integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==

deprecation@^2.0.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==

dequal@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
Expand Down Expand Up @@ -11866,6 +11919,11 @@ unist-util-visit@^4.0.0:
unist-util-is "^5.0.0"
unist-util-visit-parents "^5.1.1"

universal-user-agent@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa"
integrity sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==

universalify@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
Expand Down

0 comments on commit 3ec5e22

Please sign in to comment.