Automatically Manage Outside Collaborators Organization-wide
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:
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:
The architecture relies on this repository acting as the central dashboard where:
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:
user06
), if there exists therepo_name_1
,user06
ends up with "write"
permissions instead of "triage"
, as it should have been insteadlab_xyz/group02
.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 convention !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.
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:
OUTSIDE_COLLABORATORS_TOKEN
where to store the admin PAT.groups
.repos
and add up the entries according to your needs.templates
into the repository while preserving the files paths.✨ You are finally good to go! Remember to manage the update of the outside collaborators through pull requests.
We hope that you will find this workflow helpful!
Contributions that improve the automation are more than welcome.
This repository is maintained by:
![]() |
@pattacini |