Data: Sync maintainers #3446
This file contains hidden or 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
| name: "Data: Sync maintainers" | |
| # Script connects to the contacts database once per hour and updates BOARD_MAINTAINER property in the board config files. | |
| # If there are any changes, it opens a Pull Request | |
| # | |
| # spdx-id: GPL-2.0-or-later | |
| # copyright-owner: @igorpecovnik | |
| on: | |
| schedule: | |
| - cron: "0 * * * *" | |
| workflow_dispatch: | |
| jobs: | |
| Build: | |
| name: "Maintainers sync" | |
| runs-on: ubuntu-latest | |
| if: ${{ github.repository_owner == 'armbian' }} | |
| env: | |
| MAINTAINER_INACTIVE_DAYS: "365" # maintainers idle >= this many days are dropped from CODEOWNERS (kept as BOARD_MAINTAINER) | |
| steps: | |
| - name: "Checkout build repo" | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| repository: armbian/build | |
| # Follow the dispatched ref: the scheduled run is on main (so this is | |
| # 'main' there), but a manual workflow_dispatch from a branch checks out | |
| # THAT branch — so changes to generate_CODEOWNERS.sh / configs can be | |
| # tested end-to-end before merge (was hardcoded 'main', which always ran | |
| # main's files regardless of where you dispatched from). | |
| ref: ${{ github.ref_name }} | |
| fetch-depth: 0 | |
| clean: false | |
| - name: "Install SSH key for storage" | |
| uses: shimataro/ssh-key-action@87a8f067114a8ce263df83e9ed5c849953548bc3 # v2.8.1 | |
| with: | |
| key: ${{ secrets.KEY_UPLOAD }} | |
| known_hosts: ${{ secrets.KNOWN_HOSTS_ARMBIAN_UPLOAD }} | |
| if_key_exists: replace | |
| - name: "Download JSON file" | |
| run: | | |
| # download json that is prepared in https://github.com/armbian/armbian.github.io | |
| curl -o /tmp/armbian_maintainers.json https://github.armbian.com/maintainers.json | |
| - name: "Update maintainers" | |
| run: | | |
| # reset all maintainers so we generate from scratch | |
| sed -i "s/BOARD_MAINTAINER.*/BOARD_MAINTAINER=\"\"/" config/boards/*.{conf,wip,csc,eos,tvb} | |
| # extract values fron JSON | |
| declare -A MAINTAINERS | |
| INACTIVE_DAYS="${MAINTAINER_INACTIVE_DAYS:-365}" | |
| : > /tmp/inactive_maintainers.txt # GitHub usernames idle >= INACTIVE_DAYS (for CODEOWNERS) | |
| { | |
| # By default, bash run the pipe command in subshells | |
| # which make variable can't be assigned to. | |
| # And yes, lastpipe can solve it | |
| # But this is better. | |
| # One jq pass -> a row per maintainer: name, boards, github-username, | |
| # idle-days, joined by US (0x1f). A non-whitespace separator is required: | |
| # IFS=$'\t' read collapses consecutive tabs, so an empty field (e.g. a null | |
| # Maintaining) would shift the columns. Replaces ~4 jq forks per record and | |
| # the inner board loop's var that used to shadow the record var. | |
| while IFS=$'\x1f' read -r NAME BOARD MAINTAINER_GITHUB DAYS; do | |
| # long-inactive: kept in BOARD_MAINTAINER below, recorded for CODEOWNERS drop | |
| if [[ -n "$MAINTAINER_GITHUB" && -n "$DAYS" ]] && (( DAYS >= INACTIVE_DAYS )); then | |
| echo "$MAINTAINER_GITHUB" >> /tmp/inactive_maintainers.txt | |
| echo " ⚠ inactive ${DAYS}d: @${MAINTAINER_GITHUB} (dropped from CODEOWNERS)" | |
| fi | |
| [[ -n "$BOARD" && -n "$MAINTAINER_GITHUB" ]] || continue | |
| echo "- [$NAME](https://github.com/${MAINTAINER_GITHUB})" | |
| while read -r board; do | |
| [[ -n "$board" ]] || continue | |
| echo -e " - $board" | |
| MAINTAINERS["$board"]+="$MAINTAINER_GITHUB " | |
| done < <(echo "$BOARD" | tr ',' '\n' | sort -u) | |
| done < <(jq -r ' | |
| .[] | [ (.First_Name // ""), (.Maintaining // ""), | |
| (.Github | split("/") | .[3] // ""), | |
| (if (.Days_since_last_activity | type) == "number" | |
| then (.Days_since_last_activity | floor | tostring) else "" end) ] | |
| | join("\u001f") | |
| ' /tmp/armbian_maintainers.json) | |
| for cfg in config/boards/*.{conf,wip,csc,eos,tvb}; do | |
| board_name="$(echo "${cfg##*/}" | sed -E 's/\..*//')" | |
| declare -a maintainers | |
| readarray -t maintainers < <(echo "${MAINTAINERS[${board_name}]}" | xargs -n1 | sort -u) | |
| sed -i "s/BOARD_MAINTAINER=.*/BOARD_MAINTAINER=\"${maintainers[*]}\"/" "${cfg}" | |
| done | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: "Mark csc for no maintainer" | |
| run: | | |
| grep BOARD_MAINTAINER=\"\" config/boards/*.{wip,conf} | cut -d":" -f1 | | |
| while read -r line; do | |
| if [[ "${line}" != "${line/.conf/.csc}" ]]; then | |
| mv -v "$line" "${line/.conf/.csc}" | |
| fi | |
| if [[ "${line}" != "${line/.wip/.csc}" ]]; then | |
| mv -v "$line" "${line/.wip/.csc}" | |
| fi | |
| done | |
| - name: "Re-generate CODEOWNERS" | |
| env: | |
| INACTIVE_MAINTAINERS_FILE: /tmp/inactive_maintainers.txt | |
| run: | | |
| ./.github/generate_CODEOWNERS.sh | |
| - name: "Flag CODEOWNERS users without write access" | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -uo pipefail | |
| # Individuals referenced in the generated CODEOWNERS (drop any @org/team refs). | |
| mapfile -t users < <(grep -oE '@[A-Za-z0-9_/-]+' .github/CODEOWNERS \ | |
| | sed 's/^@//' | grep -v '/' | tr 'A-Z' 'a-z' | sort -u) | |
| # Collaborators that actually have push (write) access. A CODEOWNERS entry | |
| # for anyone WITHOUT write access is silently ignored by GitHub — never | |
| # review-requested — so surface them for the maintainers to fix. | |
| collabs="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/collaborators" \ | |
| --jq '.[] | select(.permissions.push) | .login' 2>/dev/null || true)" | |
| have_push="$(tr 'A-Z' 'a-z' <<<"$collabs" | sort -u)" | |
| { | |
| echo "" | |
| echo "### CODEOWNERS users without write access" | |
| echo "" | |
| if [[ -z "$have_push" ]]; then | |
| echo "_Could not read repository collaborators (token lacks access) — check skipped._" | |
| else | |
| missing=() | |
| for u in "${users[@]}"; do | |
| grep -qxF "$u" <<<"$have_push" || missing+=("$u") | |
| done | |
| if (( ${#missing[@]} )); then | |
| echo "These ${#missing[@]} CODEOWNERS entries are ignored by GitHub until granted write access:" | |
| echo "" | |
| printf -- '- @%s\n' "${missing[@]}" | |
| else | |
| echo "_All ${#users[@]} CODEOWNERS users have write access._" | |
| fi | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Create Pull Request | |
| id: cpr | |
| uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| commit-message: '`Automatic` board configs status synchronise' | |
| signoff: false | |
| branch: update-maintainers | |
| delete-branch: true | |
| title: '`Automatic` board configs status synchronise' | |
| body: | | |
| Update maintainers and board status | |
| - synced status from the database | |
| - rename to .`csc` where we don't have anyone | |
| If you want to become a board maintainer, [adjust data here](https://www.armbian.com/update-data/). | |
| Ref: | |
| - [Board Maintainers Procedures and Guidelines](https://docs.armbian.com/Board_Maintainers_Procedures_and_Guidelines/) | |
| - [Contribute](https://docs.armbian.com/Process_Contribute/) | |
| labels: | | |
| Work in progress | |
| #assignees: igorpecovnik | |
| #reviewers: Must be org collaborator | |
| draft: false |