From 2ccc3504e7279647b088a5ba4c396992c6c1d91f Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Thu, 18 Sep 2025 01:26:21 +0200 Subject: [PATCH 1/2] Track committer additions with git history After https://github.com/NixOS/nixpkgs-committers/pull/31, people who received commit access since a year ago were tracked with the date of commit bit reception. While this works, it's not necessary in the long term, because the git history will keep track of when commit bits are given. This change starts using the commit history when possible, which allows avoiding the need for adding addition dates for new committers. --- .github/workflows/retire.yml | 2 ++ members/Enzime | 1 - members/LordGrimmauld | 1 - members/MattSturgeon | 1 - members/SigmaSquadron | 1 - members/jmbaur | 1 - members/m1cr0man | 1 - scripts/retire.sh | 52 +++++++++++++++++++++++++++++++----- scripts/sync.sh | 3 +-- 9 files changed, 49 insertions(+), 14 deletions(-) diff --git a/.github/workflows/retire.yml b/.github/workflows/retire.yml index e45eaee..e311ba8 100644 --- a/.github/workflows/retire.yml +++ b/.github/workflows/retire.yml @@ -34,6 +34,8 @@ jobs: uses: actions/checkout@v4 with: token: ${{ steps.app-token.outputs.token }} + # Fetch full history to determine when committers were added + fetch-depth: 0 - name: Run script # One month plus a bit of leeway run: scripts/retire.sh NixOS nixpkgs nixpkgs-committers members "yesterday 1 month ago" diff --git a/members/Enzime b/members/Enzime index 3fb16d6..e69de29 100644 --- a/members/Enzime +++ b/members/Enzime @@ -1 +0,0 @@ -2025-08-20 diff --git a/members/LordGrimmauld b/members/LordGrimmauld index 3fb16d6..e69de29 100644 --- a/members/LordGrimmauld +++ b/members/LordGrimmauld @@ -1 +0,0 @@ -2025-08-20 diff --git a/members/MattSturgeon b/members/MattSturgeon index 3fb16d6..e69de29 100644 --- a/members/MattSturgeon +++ b/members/MattSturgeon @@ -1 +0,0 @@ -2025-08-20 diff --git a/members/SigmaSquadron b/members/SigmaSquadron index 3fb16d6..e69de29 100644 --- a/members/SigmaSquadron +++ b/members/SigmaSquadron @@ -1 +0,0 @@ -2025-08-20 diff --git a/members/jmbaur b/members/jmbaur index 3fb16d6..e69de29 100644 --- a/members/jmbaur +++ b/members/jmbaur @@ -1 +0,0 @@ -2025-08-20 diff --git a/members/m1cr0man b/members/m1cr0man index 3fb16d6..e69de29 100644 --- a/members/m1cr0man +++ b/members/m1cr0man @@ -1 +0,0 @@ -2025-08-20 diff --git a/scripts/retire.sh b/scripts/retire.sh index bffd73a..cdac339 100755 --- a/scripts/retire.sh +++ b/scripts/retire.sh @@ -37,9 +37,31 @@ DIR=${4:-$(usage)} NOTICE_CUTOFF=${5:-$(usage)} mainBranch=$(git branch --show-current) -newCutoff=$(date --date="1 year ago" +%s) noticeCutoff=$(date --date="$NOTICE_CUTOFF" +%s) +# People that received the commit bit after this date won't be retired +newCutoff=$(date --date="1 year ago" +%s) + +# We need to know when people received their commit bit to avoid retiring them within the first year. +# For now this is done either with the git creation date of the file, or its contents: +# +# | commit bit reception date | file creation date | file contents | +# | -------------------------- | ------------------ | -------------- | +# | A) -∞ - 2024-10-06 | 2025-07-08 | empty | +# | B) 2024-10-07 - 2025-04-22 | 2025-07-08 | reception date | +# | C) 2025-08-13 - ∞ | reception date | empty | +# +# After 2026-04-23 (one year after C started), the file creation date +# for all first-year committers will match the reception date, +# while everybody else will have been a committer for more than one year. +# This means the code can then be simplified to just +# check if the file creation date is in the last year. +# +# For now however, the code needs to check if the file creation date +# is before 2025-07-09 to distinguish between periods A and C, +# so we hardcode that date for the code to use. +createdOnReceptionEpoch=$(date --date=2025-07-09 +%s) + if [[ -z "${PROD:-}" ]]; then tmp=$(git rev-parse --show-toplevel)/.tmp rm -rf "$tmp" @@ -54,12 +76,30 @@ mkdir -p "$DIR" cd "$DIR" for login in *; do - # Don't remove people that have been added recently - if [[ -s "$login" ]]; then - epochAdded=$(date --date="$(<"$login")" +%s) - if (( newCutoff < epochAdded )); then - continue + # Figure out when this person received the commit bit + # Get the unix epoch of the first commit that touched this file + # --first-parent is important to get the time of when the main branch was changed + fileCommitEpoch=$(git log --reverse --first-parent --format=%cd --date=unix -- "$login" | head -1) + if (( fileCommitEpoch < createdOnReceptionEpoch )); then + # If it was created before creation actually matched the reception date + # This branch can be removed after 2026-04-23 + + if [[ -s "$login" ]]; then + # If the file is non-empty it indicates an explicit reception date + receptionEpoch=$(date --date="$(<"$login")" +%s) + else + # Otherwise they received the commit bit more than a year ago (start of unix epoch, 1970) + receptionEpoch=0 fi + else + # Otherwise creation matches reception + receptionEpoch=$fileCommitEpoch + fi + + # If the commit bit was received after the cutoff date, don't retire in any case + if (( newCutoff < receptionEpoch )); then + log "$login became a committer less than 1 year ago, skipping retirement check" + continue fi trace gh api -X GET /repos/"$ORG"/"$ACTIVITY_REPO"/activity \ diff --git a/scripts/sync.sh b/scripts/sync.sh index 15b2271..0fa103b 100755 --- a/scripts/sync.sh +++ b/scripts/sync.sh @@ -17,8 +17,7 @@ gh api /orgs/"$ORG"/teams/"$TEAM"/members --paginate --jq '.[].login' | if [[ -f "$DIR/$login" ]]; then mv "$DIR/$login" "$DIR.new" else - # Keep track of when the user was added - date +%F > "$DIR.new/$login" + touch "$DIR.new/$login" fi done From db0c92b5c0ac201f9cfcff4a34d10b77fe64a87e Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Thu, 18 Sep 2025 02:16:46 +0200 Subject: [PATCH 2/2] Extend and improve testing instructions Now also documents testing of how the commit history is used to infer committer addition times. To make that work, we use a clean members-test directory that is guaranteed to not exist in the upstream repo, so it has a clean history that isn't conflicting. --- scripts/README.md | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/scripts/README.md b/scripts/README.md index 5c0e41f..f99d599 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -36,10 +36,10 @@ Once you have the above setup (or got @infinisil to add yourself to his), you ha This script has no external effects and as such can be easily tested by running: ```bash -scripts/sync.sh infinisil-test-org actors members +scripts/sync.sh infinisil-test-org actors members-test ``` -Check that it synchronises the files in the `members` directory with the team members of the `actors` team. +Check that it synchronises the files in the `members-test` directory with the team members of the `actors` team. ## `retire.sh` @@ -47,16 +47,21 @@ This script has external effects and as such needs a bit more care when testing. ### Setup (important!) -To avoid other users getting pings, ensure that the `members` directory contains only a simulated new user and your own user (simulated to have been added over a year ago), then commit and push it for testing: +To avoid other users getting pings, ensure that the `members-test` directory contains only simulated new users and your own user (simulated to have been added over a year ago), then commit and push it for testing: ```bash me=$(gh api /user --jq .login) git switch -C "test-$me" -rm -rf members -mkdir members -date +%F > "members/github" -date --date="1 year ago 1 day ago" +%F > "members/$me" -git add members +rm -rf members-test +mkdir -p members-test + +touch members-test/"$me" +date +%F > "members-test/new-committer-1" +git add members-test +GIT_COMMITTER_DATE=$(date --date @0) git commit -m testing + +touch "members-test/new-committer-2" +git add members-test git commit -m testing git push -f -u origin HEAD ``` @@ -67,40 +72,40 @@ The following sequence tests all code paths: 1. Run the script with the `active` repo argument to simulate CI running without inactive users: ```bash - scripts/retire.sh infinisil-test-org active nixpkgs-committers members 'yesterday 1 month ago' + scripts/retire.sh infinisil-test-org active nixpkgs-committers members-test 'yesterday 1 month ago' ``` Check that no PR would be opened. 2. Run the script with the `empty` repo argument to simulate CI running with inactive users: ```bash - scripts/retire.sh infinisil-test-org empty nixpkgs-committers members 'yesterday 1 month ago' + scripts/retire.sh infinisil-test-org empty nixpkgs-committers members-test 'yesterday 1 month ago' ``` - Check that it would only create a PR for your own user and not the "github" user before running it again with `PROD=1` to actually do it: + Check that it would only create a PR for your own user and not the "new-committer-1" or "new-committer-2" user before running it again with `PROD=1` to actually do it: ```bash - PROD=1 scripts/retire.sh infinisil-test-org empty nixpkgs-committers members 'yesterday 1 month ago' + PROD=1 scripts/retire.sh infinisil-test-org empty nixpkgs-committers members-test 'yesterday 1 month ago' ``` Check that it created the PR appropriately. You can undo this step by closing the PR. 3. Run it again to simulate CI running again later: ```bash - PROD=1 scripts/retire.sh infinisil-test-org empty nixpkgs-committers members 'yesterday 1 month ago' + PROD=1 scripts/retire.sh infinisil-test-org empty nixpkgs-committers members-test 'yesterday 1 month ago' ``` Check that no other PR is opened. 4. Run it again with `now` as the date to simulate the time interval passing: ```bash - PROD=1 scripts/retire.sh infinisil-test-org empty nixpkgs-committers members now + PROD=1 scripts/retire.sh infinisil-test-org empty nixpkgs-committers members-test now ``` Check that it undrafted the previous PR and posted an appropriate comment. 5. Run it again to simulate CI running again later: ```bash - PROD=1 scripts/retire.sh infinisil-test-org empty nixpkgs-committers members now + PROD=1 scripts/retire.sh infinisil-test-org empty nixpkgs-committers members-test now ``` 6. Reset by marking the PR as a draft again. 7. Run it again with the `active` repo argument to simulate activity during the time interval: ```bash - PROD=1 scripts/retire.sh infinisil-test-org active nixpkgs-committers members now + PROD=1 scripts/retire.sh infinisil-test-org active nixpkgs-committers members-test now ```