From ad528d97f68c5e469384b2e8de43c028ebca44c0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 6 Jul 2026 11:54:41 +0200 Subject: [PATCH 1/4] keepalive: refresh scheduled workflows without forcing a commit GitHub disables scheduled workflow triggers after 60 days without repository activity. The current workaround commits to `.github/cached/keepalive.txt` every three weeks, which forces every downstream fork (e.g. `cygwingitgadget/gitgitgadget-workflows`) to either diverge from upstream or merge the churn back on a schedule. A much cleaner mechanism exists: PUT /repos/{owner}/{repo}/actions/workflows/{id}/enable resets the 60-day timer even when the workflow is already enabled. The trick is documented in https://github.com/PhrozenByte/gh-workflow-immortality, and it works against forks like `cygwingitgadget/gitgitgadget-workflows`, without conflicting with upstream changes. That endpoint is not reachable with the default `GITHUB_TOKEN`, so this reuses the `actions/create-github-app-token` pattern (and the per-fork `GITGITGADGET_GITHUB_APP_*` secrets) that the other workflows in this repo already rely on. Only workflows in state `active` are refreshed; anything a fork has deliberately disabled (e.g. `sync-ref.yml` in the Cygwin fork, which has no sister repo to sync from) stays disabled. Assisted-by: Opus 4.7 Signed-off-by: Johannes Schindelin --- .github/workflows/keepalive.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/keepalive.yml b/.github/workflows/keepalive.yml index d8de889..de6849a 100644 --- a/.github/workflows/keepalive.yml +++ b/.github/workflows/keepalive.yml @@ -57,3 +57,27 @@ jobs: done exit 1 } + - name: Obtain a token to reset the inactivity timer + uses: actions/create-github-app-token@v3 + id: token + with: + app-id: ${{ secrets.GITGITGADGET_GITHUB_APP_ID }} + private-key: ${{ secrets.GITGITGADGET_GITHUB_APP_PRIVATE_KEY }} + - name: Reset the 60-day inactivity timer of every active workflow + env: + GH_TOKEN: ${{ steps.token.outputs.token }} + REPO: ${{ github.repository }} + run: | + ret=0 + gh api "/repos/$REPO/actions/workflows" --paginate \ + --jq '.workflows[] | select(.state == "active") | .path' | + while read -r path + do + name=${path##*/} + echo "Refreshing $name" + gh workflow -R "$REPO" enable "$name" || { + echo "::error::could not refresh $name; please ensure that it is enabled!" >&2 + ret=1 + } + done + exit $ret From 98f2f1b392ce36b4ed95f78f53e137b93fb28448 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 6 Jul 2026 12:58:10 +0200 Subject: [PATCH 2/4] keepalive: force a real state transition to reset the inactivity timer Calling `enable` on a workflow that is already `active` is not documented to reset GitHub's internal 60-day inactivity clock; the REST documentation at https://docs.github.com/en/rest/actions/workflows leaves the semantics of a no-op call unspecified. Bouncing through `active` -> `disabled_manually` -> `active` forces a real state transition and removes the ambiguity. The `&&` chaining is deliberate: GitHub Actions runs shell steps with `set -e`, so if either call fails the loop exits and the step is marked failed loudly, rather than silently leaving a workflow in `disabled_manually`. Assisted-by: Opus 4.7 Signed-off-by: Johannes Schindelin --- .github/workflows/keepalive.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/keepalive.yml b/.github/workflows/keepalive.yml index de6849a..4b76e56 100644 --- a/.github/workflows/keepalive.yml +++ b/.github/workflows/keepalive.yml @@ -75,6 +75,7 @@ jobs: do name=${path##*/} echo "Refreshing $name" + gh workflow -R "$REPO" disable "$name" && gh workflow -R "$REPO" enable "$name" || { echo "::error::could not refresh $name; please ensure that it is enabled!" >&2 ret=1 From 223c1b60b6f40603531d0aa3b657fd1c51e44ba0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 6 Jul 2026 11:57:41 +0200 Subject: [PATCH 3/4] keepalive: stop forcing synthetic commits into every fork Now that the previous commit uses the `PUT .../actions/workflows/{id}/enable` endpoint to reset the 60-day scheduled-workflow inactivity timer directly, the older fallback that invented a commit under `.github/cached/` has no remaining purpose. In every fork it either did nothing or produced a divergent commit that then had to be pushed; both outcomes are strictly worse than the enable-endpoint approach. The upstream-merge step is kept deliberately: its purpose (keeping forks close to upstream) is orthogonal to the inactivity timer, and its push retry loop still applies to the merge result. Assisted-by: Opus 4.7 Signed-off-by: Johannes Schindelin --- .github/workflows/keepalive.yml | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/.github/workflows/keepalive.yml b/.github/workflows/keepalive.yml index 4b76e56..037512f 100644 --- a/.github/workflows/keepalive.yml +++ b/.github/workflows/keepalive.yml @@ -25,28 +25,12 @@ jobs: git fetch https://github.com/gitgitgadget/gitgitgadget-workflows HEAD && if test 0 = $(git rev-list --count HEAD..FETCH_HEAD) then - exit 0 # let the next step create a commit + exit 0 # nothing to merge fi && git merge --no-edit FETCH_HEAD && echo "result=merged" >>$GITHUB_OUTPUT - - name: Create a commit - id: commit - if: steps.merge.outputs.result != 'merged' - run: | - if test workflow_dispatch != '${{ github.event_name }}' && - test 0 -lt $(git rev-list --count --since=3.weeks.ago HEAD) - then - echo "::notice::No need to keep alive, there were commits in the last three weeks" - echo "result=skip-push" >>$GITHUB_OUTPUT - exit 0 - fi && - mkdir -p .github/cached - file='.github/cached/keepalive.txt' - date >$file - git add "$file" - git commit -m "workflow keepalive" - name: Push changes - if: steps.commit.outputs.result != 'skip-push' + if: steps.merge.outputs.result == 'merged' run: | git push origin HEAD Date: Mon, 6 Jul 2026 13:19:03 +0200 Subject: [PATCH 4/4] Remove left-over from the previous `keepalive` strategy This is now no longer needed. Signed-off-by: Johannes Schindelin --- .github/cached/keepalive.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .github/cached/keepalive.txt diff --git a/.github/cached/keepalive.txt b/.github/cached/keepalive.txt deleted file mode 100644 index 131619b..0000000 --- a/.github/cached/keepalive.txt +++ /dev/null @@ -1 +0,0 @@ -Wed Jun 17 21:25:29 UTC 2026