Automatically add all existing repo contributors with all-contributors-cli
Tags: python, programming, productivity
Published: 2022-01-10 16:15:32


## Introduction
A while ago, I posted a question on Dev.to to find out more about technologies for recognizing GitHub Repo contributions:
> Hello guys,
>
> I am aware of all-contributors bot that helps with adding new contributors to an OSS project on GitHub, but it does not seem to be able to automatically add all contributors(those who previously contributed, before the point of using all-contributors), perhaps with some sensible defaults.
> I am wondering if there are any alternative tools out there that you are using to recognize contributors in your GitHub Repo?
>
> Would love to learn more about that:)

## The Problem
I needed a way to recognize and show Github Repo contributors in the README file of a project for more visibility. The [all-contributors-cli](https://allcontributors.org/docs/en/cli/overview) is a CLI tool that fits my needs. However, the problem is that while the Repo already has a dozen contributors, the CLI tool is not able to recognize any of them before the point of using the tool. 

Running `npx all-contributors-cli init` sets up the Repo with the default settings, but it does not recognize any of the existing contributors. One helpful command provided by the CLI is `npx all-contributors-cli check`. This command checks the Repo for any contributors not yet added in the `.all-contributorsrc` file and prints the list of names. Given that the Repo that I was working on has about 50 contributors, I would have to take the names from the missing list and manually add them via `npx all-contributors-cli add {contributor} {contribution_type}`. This is a tedious task, and I would like to automate this process.

## The Solution
Inspired by the discussions in related GitHub issues, I think a Python script can help. The final script is made up of several functions that are called in the main function. I also included a few options to handle the common use cases in the script that I put together.

### `init`
This function will initialize the Repo with the default settings. The code basically calls `npx all-contributors-cli init`.

```python
import subprocess
import shlex
import sys

def init():
    print("Initialize all-contributors")
    subprocess.run(shlex.split("npx all-contributors-cli init"), shell=True)
```

### `check`
This function will check the Repo for any contributors not yet added in the `.all-contributorsrc` file and use the list to call `npx all-contributors-cli add {contributor} code`. Note that I intentionally ignored `dependabot[bot]` and set the default contribution type to `code`. Change them to suit your needs! There is also a dryrun parameter that is used to control the execution of the add command.

```python
def check(dryrun=False):
    all_contributors_check_result = subprocess.run(
        shlex.split("npx all-contributors-cli check"),
        shell=True,
        stdout=subprocess.PIPE,
    ).stdout.decode("utf-8")

    missing_contributors = all_contributors_check_result.replace(
        "Missing contributors in .all-contributorsrc:\n", ""
    ).strip()
    if missing_contributors in ["", "dependabot[bot]"]:  # ignore dependabot[bot]
        print("No missing contributors")
        return

    default_contribution_type = "code"  # default contribution type
    contributors_to_add = missing_contributors.split(", ")
    if "dependabot[bot]" in contributors_to_add:
        contributors_to_add.remove("dependabot[bot]")  # ignore dependabot[bot]

    print("Update .all-contributorsrc to include all contributors read from Github")
    for contributor in contributors_to_add:
        command = (
            f"npx all-contributors-cli add {contributor} {default_contribution_type}"
        )
        if not dryrun:
            print("run: " + command)
            subprocess.run(shlex.split(command), shell=True)
        else:
            print("dryrun: " + command)
```

### `generate`
This function will update the README file with the list of missing contributors. The code basically calls `npx all-contributors-cli generate`.

```python
def generate():
    print("Update README.md to generate table of contributors")
    subprocess.run(shlex.split("npx all-contributors-cli generate"), shell=True)
    print("Done!")
```

### `main`
This is the main function that calls the other functions in the script. To make things simpler, the script can be run with an argument that determines what gets done.

```python
def main():
    command = sys.argv[1] if len(sys.argv) > 1 else "help"
    if command == "init":
        init()
        check()
        generate()
    elif command == "help":
        print(
            """
        Commands:
        init: initialize all-contributors for the first time (will generate .all-contributorsrc)
         - python add-all-contributors.py init
        add: add missing contributors (when you already have .all-contributorsrc)
         - python add-all-contributors.py add
        dryrun: dryrun add missing contributors (test without adding)
         - python add-all-contributors.py dryrun
        """
        )
    elif command == "add":
        check()
        generate()
    elif command == "dryrun":
        check(True)
    else:
        print("Unknown command: " + command)
        exit(1)
```

The full code is available as a GitHub [Gist](https://gist.github.com/tlylt/28f07c6bbd742e6a6205168a1b486c55)
<iframe style="width:75vh;height:50vh" src="https://gist.github.com/tlylt/28f07c6bbd742e6a6205168a1b486c55.pibb"></iframe>

## Usage
0. Understand [all-contributors](https://allcontributors.org/docs/en/overview) and decide if you want to use it. You should also have recent versions of npm and Python available on your machine.
1. Put the [script](https://gist.github.com/tlylt/28f07c6bbd742e6a6205168a1b486c55) in the root directory of your project.
2. If nothing is done yet (no .all-contributorsrc), run `python add-all-contributors.py init`. Remove the add-all-contributors.py file and that's it!
3. If you already have .all-contributorsrc file, run `python add-all-contributors.py add`. Remove the add-all-contributors.py file and that's it!
4. If you would like to see a help message, run `python add-all-contributors.py`.
5. If you would like to see who will be added without actually doing it, run `python add-all-contributors.py dryrun`. (Assuming the repository already have a .all-contributorsrc file)

## Conclusion
Even though I "solved" my own problem, the comments and discussions on this topic/issue gave me the idea to write this script. So, thanks to random people on the internet for sharing!