Skip to content

Manual Sync of labels #19

Manual Sync of labels

Manual Sync of labels #19

# The purpose of this workflow is to take all labels
# in this repository (neurobagel/planning) and push them
# to all repositories owned by the neurobagel organization.
#
# This workflow complements the automatic label syncing workflow
# label_sync.yml that is triggered when a label is created or edited.
# The present workflow is probably most useful when we create a new
# repository and then want to make sure that it immediately receives
# all of our standard labels.
#
# The workflow has the following jobs in this order:
# 1. get_labels: Find all labels in the planning repo and store them
# in the output variable "labels" of the job.
# 2. sync_labels: Create a separate job for each label and then within
# that job conduct the following steps:
# 2.1. read_label: Read the description and color of the label via a
# GitHub GRAPHQL API call and store them as environment variables.
# 2.2. sync_label: Find all repositories owned by neurobagel and iterate
# over them, pushing the previously stored label information to each one.
name: Manual Sync of labels
on:
workflow_dispatch
jobs:
get_labels:
name: Get all labels
runs-on: ubuntu-latest
outputs:
labels: ${{ steps.get_labels.outputs.labels }}
steps:
- id: get_labels
env:
GH_TOKEN: ${{ github.TOKEN }}
MAX_LABEL: 100
run: |
label_list=[$(gh label list -R neurobagel/planning -L ${MAX_LABEL} --json name | jq '.[].name' | jq -s -r @csv)]
echo labels: ${label_list}
echo "labels=${label_list}" >> $GITHUB_OUTPUT
sync_labels:
name: Process all the labels
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
needs: get_labels
strategy:
# Note: we cannot make a matrix for the label AND repo at once,
# because the total number of jobs will exceed the maximum allowed by GitHub.
# Therefore we create one job per label here and then iterate over the repositories
# inside of the job directly.
matrix:
label: ${{fromJSON(needs.get_labels.outputs.labels)}}
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.NB_BOT_ID }}
private-key: ${{ secrets.NB_BOT_KEY }}
- name: Set GH_TOKEN
run: echo "GH_TOKEN=${{ steps.generate-token.outputs.token }}" >> $GITHUB_ENV
- name: read label details
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
id: read_label
run: |
# TODO replace hardcoded owner and repo with variables. But note that GITHUB_REPOSITORY
# in https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
# contains both owner and repo: neurobagel/planning, so we'd have to split it first
label_description="$(gh api graphql -F owner="neurobagel" -F name="planning" -F label="${{matrix.label}}" -f query='
query($owner: String!, $name: String!, $label: String!) {
repository(owner: $owner, name: $name) {
label(name: $label) {
description
}
}
}
' --jq '.data.repository.label.description')"
label_color="$(gh api graphql -F owner="neurobagel" -F name="planning" -F label="${{matrix.label}}" -f query='
query($owner: String!, $name: String!, $label: String!) {
repository(owner: $owner, name: $name) {
label(name: $label) {
color
}
}
}
' --jq '.data.repository.label.color')"
echo "description=${label_description}" >> $GITHUB_OUTPUT
echo "color=${label_color}" >> $GITHUB_OUTPUT
# Note that we have to iterate over the repositories inside of the job rather than as
# part of the job matrix because GH only allows 255 matrix jobs at a time.
# The major downside is that if any of the repositories fail, the entire step will fail
# and none of the subsequent repos will be synced.
#
# Github actions run with set -e, i.e. they immediately fail if any step has a non-zero exit status.
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference
# Conveniently, grep has by default a non-zero exit status if no match is found, so we have to escape
# that the entire step to fail and all subsequent repositories to not be synced.
- name: sync label
env:
MAX_REPO: 100
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |
for repo in $(gh repo list $GITHUB_REPOSITORY_OWNER --no-archived -L ${MAX_REPO} --json owner,name --jq '.[] | "\(.owner.login)/\(.name)"');
do
echo -e "\nSyncing label ${{matrix.label}} to ${repo}!"
# We escape grep's nonzero exit status here
out=$(gh label list -R "${repo}" -L 100 | cut -f 1 | grep -w "^${{ matrix.label }}$" || echo "")
if [ -z "${out}" ]; then
# Label missing
echo I am creating label: "${{matrix.label}}" for ${repo}
gh label create -R "${repo}" "${{matrix.label}}" --color "${{ steps.read_label.outputs.color }}" --description "${{ steps.read_label.outputs.description }}"
else
# Label exists missing
echo I am updating label: "${{matrix.label}}" for ${repo}
gh label edit -R "${repo}" "${{matrix.label}}" --color "${{ steps.read_label.outputs.color }}" --description "${{ steps.read_label.outputs.description }}"
fi
done