Unfortunately, GitHub does not provide (yet) a centralized way to manage outside collaborators within an organization, although this feature is very much requested. As of now, outside collaborators can be handled only at the level of the single repositories, having the main drawback of spreading in many places the knowledge of who can access what.
This undesired effect poses a problem of maintenance. If an external group of developers collaborates to multiple repositories within an organization, what happens if the group evolves over time with new additions or with members who leave? What if it is required to change their access permissions? In short, one must visit all the repositories where such a collaboration takes place for checking and updating the corresponding information.
A possible workaround foresees to invite outside collaborators to join a dedicated organization team. This way, we can take advantage of the perks we all know:
- teams are maintained within the central settings of the organization
- teams can be mentioned
Nonetheless, being a formal member of the organization may give the outside collaborator privileges when it comes
down to some specific access policies. For instance, if the base permissions of the organization
is set to "Read"
instead of "None"
, then that collaborator will be able to clone and pull all repositories,
private ones included!
Also, keeping the clear separation among organization members and outside collaborators is certainly advantageous if we consider that we will prevent outside developers from inheriting future upcoming functionalities that GitHub will design for org members and that can turn out to be disruptive when assigned to "unintended" members.
A different solution to the problem is to implement an automated workflow for handling outside collaborators within an organization from a central "dashboard" repo.
We make use of the following components:
- GitHub Actions for carrying out in the cloud the necessary jobs underlying the automation.
- GitHub REST API for querying and setting the information related to the outside collaborators.
The architecture relies on this repository acting as the central dashboard where:
- "groups" of outside collaborators (let's call them groups to differentiate from org teams) can be set up and modified using the mechanism of pull-requests.
- permissions to access those repositories where the collaboration takes place ("automated repositories") are stored and used to override the standard methodology.
The "outside collaborators groups" are defined in YAML files under groups
as collections of outside
collaborators' usernames.
Here's a simple example:
lab_xyz/group01:
- "user01"
- "user02"
- "user03"
lab_xyz/group02:
- "user04"
- "user05"
- "user06"
generic-group:
- "user07"
- "user08"
Likewise, access permissions to automated repositories are defined in YAML files under repos
as in the
following example:
repo_name_1:
lab_xyz/group01:
type: "group"
permissions: "read"
lab_xyz/group02:
type: "group"
permissions: "triage"
user06:
type: "user"
permissions: "write"
repo_name_2:
lab_abc/group01:
type: "group"
permissions: "write"
user07:
type: "user"
permissions: "maintain"
Upon updating those YAML files in the default branch via forks and pull requests or upon a manual trigger, a GitHub workflow propagates the changes to the automated repositories. In detail, for each automated repo, the outside collaborators are automatically invited, removed or updated with the requested permissions.
Importantly, the YAML files can be modified via pull-requests, enabling the representatives responsible for the outside collaborators (who are generally external to the organization) to keep their groups up-to-date. In addition, pull-requests have to be reviewed by org members, thus ensuring that the process can run securely.
Pay attention to the following points:
- The name of the automated repositories in the YAML files shall not contain the organization (i.e. the repository owner).
- With specific keys, entries can represent groups but also individuals (e.g.
user06
), if there exists the requirement to deal with single outside collaborators within the repository. - Handling of outside collaborators on an individual basis takes over groups: e.g. for the repo
repo_name_1
, the useruser06
ends up with"write"
permissions instead of"triage"
, as it should have been instead for being a member oflab_xyz/group02
. - If a user belongs to multiple groups that are all assigned to a single specific repository, then that user will end up receiving permissions according to how those groups get sequentially processed by the automation. To get around this, just handle that user on an individual basis.
- The managing of outside collaborators of an automated repo will be always overridden by the automatic workflow. Instead, org members can be still added/removed manually as inside collaborators.
- You can use YAML anchors and aliases to streamline the content of the files.
Anyone posting a message in an issue or a PR of a org repository where the outside collaborators automation is established can mention a group using the following bash-like convention:
$group-name
${group-name}
For example, if user01
posts:
Hey ${lab_xyz/group01} 👋🏻
I've got an exciting news to share with you!
Then, GitHub will reply with:
>Hey lab_xyz/group01 👋🏻
>I've got an exciting news to share with you!
@user01 wanted to notify the following collaborators:
@user02 @user03
To avoid cluttering the thread, the original triggering message is quoted only up to a given extent.
Removing entries from repos
When a repo entry gets removed from repos
, the subsequent action won't be able to perform
any cleanup of the corresponding repository as the entry is simply missing and thus the action won't
find it out. To circumvent this, leave the entry empty for one round to give the action the possibility
to perform the required cleanup. Soon afterward, the entry can be safely removed.
There are other smarter ways to get it done automatically (e.g. by comparing HEAD
against HEAD~
)
but this is actually the simplest. Also, consider that if an entry is no longer declared, then it is
semantically correct that the automation does no longer handle it.
Obviously, one can also perform a manual cleanup straight away.
Pending invitations self-expire after a few days.
It may happen that certain invitees do not want to join the intended repositories as external collaborators. In this regard, we do not clean up automatically stale invitations to use them as indicators that we shall not spam those invitees with continuous requests at every automation run.
However, it might be convenient sometimes to clean up stale invitations on demand. To this end,
one can rely on the manually-triggered workflow Delete Invitations
.
Follow the quick guide below if you want to install this automation in your organization:
- Create a copy of this dashboard repository in your organization account. To use the mentioning mechanism, it is required to keep this repo public.
- Make sure that only org admins can manage the dashboard repository.
- One org admin is required to create a personal access token (PAT) with full repo scope.
- Ceate in the dashboard a secret called
OUTSIDE_COLLABORATORS_TOKEN
where to store the admin PAT. This PAT will allow the automation to modify the organization repositories. - You may consider enforcing the use of a GitHub environment to improve security. Jobs referring to environments can start only if approved by specific reviewers.
- Edit the initial content of
groups
. - For each single repo of your org you aim to apply automation to, do:
- Create the corresponding file in
repos
and add up the entries according to your needs. - Optionally, if you aim to enable the mentioning mechanism, copy out the
content of
templates
into the repository while preserving the files paths.
- Create the corresponding file in
✨ You are finally good to go! Remember to manage the update of the outside collaborators through pull requests.
- We are required to comply with the GitHub API rate limit rules. In case we hit such a limit, the automation will wait for the reset to take place. To alleviate this, one can follow @JeroenKnoops's suggestion and use a GitHub App to perform API calls.
- The dashboard repository is required to be public in order to enable the mentioning mechanism. See FAQ for more details.
- Pending known bugs 🐛
We hope that you will find this workflow helpful!
Contributions that improve the automation are more than welcome.
This repository is maintained by:
@pattacini |