Automatically add all existing GitHub repo contributors with all-contributors-cli

January 10, 2022

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 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.

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.

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.

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.

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

Usage

  1. Understand all-contributors and decide if you want to use it. You should also have recent versions of npm and Python available on your machine.
  2. Put the script in the root directory of your project.
  3. If nothing is done yet (no .all-contributorsrc), run python add-all-contributors.py init. Remove the all-all-contributors.py file and that’s it!
  4. If you already have .all-contributorsrc file, run python add-all-contributors.py add. Remove the all-all-contributors.py file and that’s it!
  5. If you would like to see a help message, run python add-all-contributors.py.
  6. 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!



Profile picture

Written by Liu Yongliang who lives in Singapore. Also on Dev.to, LinkedIn, GitHub

© Copyright 2022 Liu Yongliang