Manual Sync of labels #5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 | |
needs: get_labels | |
env: | |
GH_TOKEN: ${{ secrets.LAB_PAT }} | |
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: read label details | |
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 | |
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 | 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 |