I currently maintain a GitHub repo called react-coding-challenges. It involves a variety of different difficulty challenges people can check out and complete. I also have a separate, private repository for the solutions to these challenges. The solutions are invite-only, meaning I need to invite each person as a collaborator before they get access (see the why here).
This was fine at the start, with a small amount of people requesting access. Now however, I have a steady stream of requests (usually via email which goes in to my spam folder) and it's becoming increasingly difficult/time consuming to manually do this process.
So what did I do? I built a MERN application that can automate this entire process with no-touch. Check it out at solutions.alexgurr.com.
How Does It Work?
Client
The user clicks the main CTA button
This uses a great library called react-github-login.
import GitHubLogin from 'react-github-login';
<GitHubLogin
clientId="CLIENT_ID"
onSuccess={joinRepo}
onFailure={onFailure}
// We only need the read:user scope to determine who the user is
scope="read:user"
// This is irrelevant because the auth window gets closed
redirectUri=""
// Use animate.css to give the button a pulse effect
className={`button animate__animated animate__pulse animate__infinite animate__slow ${loading ? 'is-loading' : ''}`}
>
<div>
<span className="icon">
<i className="fab fa-github"></i>
</span>
<span>Let Me In</span>
</div>
</GitHubLogin>
The library starts the GitHub OAuth login process and calls our callback with an oAuth code
const joinRepo = async ({ code }) => {
try {
// The code gets sent to the server
await axios.post(config.serverUrl, { code });
setJoined(true);
} catch(e) {
// if it's a 409, the user is already in the repository
if (e.response && e.response.status && e.response.status === 409) {
setAlreadyIn(true);
return void setJoined(true);
}
toast("Oops, something went wrong.", { type: 'error', position: "bottom-left", hideProgressBar: true })
}
}
Server
Generate an access token for the user, using a client secret/id & the client code
const { data } = await axios.post('https://github.com/login/oauth/access_token', {
client_id: 'ID',
client_secret: 'SECRET',
code: code
});
Retrieve the user's information using the generated access token
const { data: user } = await axios.get('https://api.github.com/user', {
headers: { Authorization: `token ${data.split('&')[0].split('=')[1]}` }
});
Check if the user's a collaborator already
We use the @octokit/rest
library for the more complex GitHub API actions, which is a node GitHub SDK
await octokit.repos.checkCollaborator({
owner: GITHUB_UN,
repo: 'react-coding-solutions',
username
});
If they are already a collaborator, we return at this point and return a response with 409 status code.
Invite the user as a collaborator and return a success (201) response
await octokit.repos.addCollaborator({
owner: GITHUB_UN,
repo: 'react-coding-solutions',
username
});
Store the user record in our database
We use mongodb and mongoose as our user record store. This record write is non-blocking and we don't wait for it to finish before returning a response.
User.create({ username });
Overall, this was quite an easy app to build. I hope this gives some insight into how you could invite users to GitHub repos, or provide some inspiration to go and automate things!
Top comments (0)