From f5d404e6be8fbf51d4b46876a8e1bbbef75dd159 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:19:38 +0000 Subject: [PATCH 01/12] Initial plan From 1dc3437e4ea8022acc5ec88162a8a78681f9eb6b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:21:42 +0000 Subject: [PATCH 02/12] Add keep-alive workflow to prevent scheduled workflow deactivation Co-authored-by: TimHess <3947063+TimHess@users.noreply.github.com> --- .github/workflows/keep-alive.yml | 105 +++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 .github/workflows/keep-alive.yml diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml new file mode 100644 index 0000000..074dc7f --- /dev/null +++ b/.github/workflows/keep-alive.yml @@ -0,0 +1,105 @@ +name: Keep Scheduled Workflows Alive + +on: + schedule: + # Run on the 1st of each month (checks for activity in the last 50 days before taking action) + # This conservative frequency ensures we never hit the 60-day GitHub inactivity limit + # even accounting for holidays, weekends, or workflow execution delays + - cron: "0 0 1 * *" + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + issues: read + +jobs: + keep-alive: + name: Keep repository active + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Git checkout + uses: actions/checkout@v4 + + - name: Check for recent activity + id: check_activity + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + # Check for any activity in the last 50 days + cutoff_date=$(date -u -d '50 days ago' '+%Y-%m-%dT%H:%M:%SZ') + echo "Checking for activity since: $cutoff_date" + + # Check for recent commits + recent_commits=$(gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/repos/${{ github.repository }}/commits?since=$cutoff_date&per_page=1" \ + --jq 'length') + + # Check for recent issues/PRs + recent_issues=$(gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/repos/${{ github.repository }}/issues?since=$cutoff_date&per_page=1&state=all" \ + --jq 'length') + + echo "Recent commits: $recent_commits" + echo "Recent issues/PRs: $recent_issues" + + if [ "$recent_commits" -gt 0 ] || [ "$recent_issues" -gt 0 ]; then + echo "Recent activity detected. No action needed." + echo "needs_activity=false" >> $GITHUB_OUTPUT + else + echo "No recent activity. Will create keep-alive activity." + echo "needs_activity=true" >> $GITHUB_OUTPUT + fi + + - name: Create and close PR to maintain activity + if: steps.check_activity.outputs.needs_activity == 'true' + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + # Configure git + git config --local user.name "github-actions[bot]" + git config --local user.email "github-actions[bot]@users.noreply.github.com" + + # Create a unique branch name + branch_name="keep-alive-$(date +%Y%m%d-%H%M%S)" + + # Create a new branch and make an empty commit + git checkout -b "$branch_name" + git commit --allow-empty -m "chore: keep scheduled workflows alive + +This automated commit ensures scheduled workflows remain active. +GitHub disables scheduled workflows after 60 days of repository inactivity." + + git push --set-upstream origin "$branch_name" + + # Create PR + pr_url=$(gh pr create \ + --head "$branch_name" \ + --base main \ + --title "chore: keep scheduled workflows alive" \ + --body "This automated PR is created to prevent scheduled workflows from being disabled due to inactivity. + +GitHub automatically disables scheduled workflows when no repository activity has occurred in 60 days. This PR ensures continuous operation of our scheduled builds and tests. + +This PR contains an empty commit and will be automatically closed without merging.") + + echo "Created PR: $pr_url" + + # Extract PR number from URL + pr_number=$(echo "$pr_url" | grep -o '[0-9]*$') + + # Close the PR immediately + gh pr close "$pr_number" --comment "Closing this keep-alive PR as its purpose (maintaining repository activity) has been fulfilled." + + echo "Closed PR #$pr_number" + + # Clean up the branch + git push origin --delete "$branch_name" From 6a8805488e16c2892515f60cbbb5eaa59848921d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:22:28 +0000 Subject: [PATCH 03/12] Add error handling to keep-alive workflow Co-authored-by: TimHess <3947063+TimHess@users.noreply.github.com> --- .github/workflows/keep-alive.yml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index 074dc7f..cb5ba41 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -38,14 +38,14 @@ jobs: -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "/repos/${{ github.repository }}/commits?since=$cutoff_date&per_page=1" \ - --jq 'length') + --jq 'length' 2>/dev/null || echo "0") # Check for recent issues/PRs recent_issues=$(gh api \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "/repos/${{ github.repository }}/issues?since=$cutoff_date&per_page=1&state=all" \ - --jq 'length') + --jq 'length' 2>/dev/null || echo "0") echo "Recent commits: $recent_commits" echo "Recent issues/PRs: $recent_issues" @@ -96,10 +96,15 @@ This PR contains an empty commit and will be automatically closed without mergin # Extract PR number from URL pr_number=$(echo "$pr_url" | grep -o '[0-9]*$') - # Close the PR immediately - gh pr close "$pr_number" --comment "Closing this keep-alive PR as its purpose (maintaining repository activity) has been fulfilled." - - echo "Closed PR #$pr_number" - - # Clean up the branch - git push origin --delete "$branch_name" + if [ -n "$pr_number" ]; then + # Close the PR immediately + gh pr close "$pr_number" --comment "Closing this keep-alive PR as its purpose (maintaining repository activity) has been fulfilled." + echo "Closed PR #$pr_number" + + # Clean up the branch + git push origin --delete "$branch_name" + echo "Deleted branch: $branch_name" + else + echo "Warning: Could not extract PR number from URL: $pr_url" + echo "The branch $branch_name was created but PR may not have been closed." + fi From 58da803a83148864be907b27aa6cfc75a118f85f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:23:39 +0000 Subject: [PATCH 04/12] Address code review feedback: improve regex, branch handling, and documentation Co-authored-by: TimHess <3947063+TimHess@users.noreply.github.com> --- .github/workflows/keep-alive.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index cb5ba41..a0732e9 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -30,6 +30,7 @@ jobs: shell: bash run: | # Check for any activity in the last 50 days + # Note: date -d is GNU-specific, but this workflow runs on ubuntu-latest which has GNU date cutoff_date=$(date -u -d '50 days ago' '+%Y-%m-%dT%H:%M:%SZ') echo "Checking for activity since: $cutoff_date" @@ -83,7 +84,7 @@ GitHub disables scheduled workflows after 60 days of repository inactivity." # Create PR pr_url=$(gh pr create \ --head "$branch_name" \ - --base main \ + --base ${{ github.event.repository.default_branch }} \ --title "chore: keep scheduled workflows alive" \ --body "This automated PR is created to prevent scheduled workflows from being disabled due to inactivity. @@ -93,8 +94,8 @@ This PR contains an empty commit and will be automatically closed without mergin echo "Created PR: $pr_url" - # Extract PR number from URL - pr_number=$(echo "$pr_url" | grep -o '[0-9]*$') + # Extract PR number from URL (require at least one digit) + pr_number=$(echo "$pr_url" | grep -oE '[0-9]+$') if [ -n "$pr_number" ]; then # Close the PR immediately @@ -106,5 +107,7 @@ This PR contains an empty commit and will be automatically closed without mergin echo "Deleted branch: $branch_name" else echo "Warning: Could not extract PR number from URL: $pr_url" - echo "The branch $branch_name was created but PR may not have been closed." + echo "The branch $branch_name was created but PR may not have been properly closed." + echo "Attempting to clean up branch anyway..." + git push origin --delete "$branch_name" 2>/dev/null || echo "Branch cleanup failed, may need manual cleanup" fi From 5b30f0d00fe193c10c278b9fa5b6499ec08a5cf0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:24:31 +0000 Subject: [PATCH 05/12] Add comprehensive documentation to keep-alive workflow Co-authored-by: TimHess <3947063+TimHess@users.noreply.github.com> --- .github/workflows/keep-alive.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index a0732e9..ffbd04f 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -1,3 +1,24 @@ +# Keep Scheduled Workflows Alive +# +# Purpose: +# GitHub automatically disables scheduled workflows after 60 days of repository inactivity. +# This workflow prevents that by creating minimal activity when needed. +# +# How it works: +# 1. Runs monthly on the 1st of each month +# 2. Checks for any commits, issues, or PRs in the last 50 days +# 3. If no activity is found: +# - Creates a temporary branch with an empty commit +# - Opens a pull request +# - Immediately closes the PR +# - Deletes the temporary branch +# 4. If recent activity exists, does nothing +# +# Manual trigger: +# This workflow can also be triggered manually via workflow_dispatch if needed. +# +# Related issue: https://github.com/SteeltoeOSS/NetCoreToolTemplates/issues/106 +# name: Keep Scheduled Workflows Alive on: From cfa441d835d549dd2010a46f8aa51fb5be3c4f86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:44:58 +0000 Subject: [PATCH 06/12] Simplify keep-alive workflow to reuse same PR every ~59 days Co-authored-by: TimHess <3947063+TimHess@users.noreply.github.com> --- .github/workflows/keep-alive.yml | 152 +++++++++++++++---------------- 1 file changed, 72 insertions(+), 80 deletions(-) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index ffbd04f..5182f74 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -5,14 +5,16 @@ # This workflow prevents that by creating minimal activity when needed. # # How it works: -# 1. Runs monthly on the 1st of each month -# 2. Checks for any commits, issues, or PRs in the last 50 days -# 3. If no activity is found: -# - Creates a temporary branch with an empty commit -# - Opens a pull request -# - Immediately closes the PR -# - Deletes the temporary branch -# 4. If recent activity exists, does nothing +# 1. Runs every 59 days (using approximation via cron) +# 2. Checks if the keep-alive PR exists +# 3. If PR doesn't exist, creates it with a persistent branch +# 4. Reopens the PR (if closed) and immediately closes it again +# 5. This activity prevents workflow deactivation +# +# Benefits: +# - Uses the same PR repeatedly (minimal noise) +# - No human intervention required +# - Runs every ~59 days (close to 60-day limit but safe) # # Manual trigger: # This workflow can also be triggered manually via workflow_dispatch if needed. @@ -23,10 +25,9 @@ name: Keep Scheduled Workflows Alive on: schedule: - # Run on the 1st of each month (checks for activity in the last 50 days before taking action) - # This conservative frequency ensures we never hit the 60-day GitHub inactivity limit - # even accounting for holidays, weekends, or workflow execution delays - - cron: "0 0 1 * *" + # Run every 59 days (approximate using twice per 4 months = ~60 days) + # This is close to GitHub's 60-day limit but provides a safety margin + - cron: "0 0 1 */2 *" workflow_dispatch: permissions: @@ -44,91 +45,82 @@ jobs: - name: Git checkout uses: actions/checkout@v4 - - name: Check for recent activity - id: check_activity + - name: Ensure keep-alive branch and PR exist env: GH_TOKEN: ${{ github.token }} shell: bash run: | - # Check for any activity in the last 50 days - # Note: date -d is GNU-specific, but this workflow runs on ubuntu-latest which has GNU date - cutoff_date=$(date -u -d '50 days ago' '+%Y-%m-%dT%H:%M:%SZ') - echo "Checking for activity since: $cutoff_date" + # Use a fixed branch name for persistence + branch_name="keep-alive-workflow" - # Check for recent commits - recent_commits=$(gh api \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "/repos/${{ github.repository }}/commits?since=$cutoff_date&per_page=1" \ - --jq 'length' 2>/dev/null || echo "0") + # Configure git + git config --local user.name "github-actions[bot]" + git config --local user.email "github-actions[bot]@users.noreply.github.com" - # Check for recent issues/PRs - recent_issues=$(gh api \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "/repos/${{ github.repository }}/issues?since=$cutoff_date&per_page=1&state=all" \ - --jq 'length' 2>/dev/null || echo "0") + # Check if branch exists remotely + if git ls-remote --heads origin "$branch_name" | grep -q "$branch_name"; then + echo "Branch $branch_name already exists" + else + echo "Creating new branch $branch_name" + git checkout -b "$branch_name" + git commit --allow-empty -m "chore: keep scheduled workflows alive + +This branch is used by the keep-alive workflow to maintain repository activity. +GitHub disables scheduled workflows after 60 days of repository inactivity." + git push --set-upstream origin "$branch_name" + fi - echo "Recent commits: $recent_commits" - echo "Recent issues/PRs: $recent_issues" + # Check if PR exists (open or closed) + pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number' 2>/dev/null || echo "") - if [ "$recent_commits" -gt 0 ] || [ "$recent_issues" -gt 0 ]; then - echo "Recent activity detected. No action needed." - echo "needs_activity=false" >> $GITHUB_OUTPUT + if [ -z "$pr_number" ]; then + echo "Creating new PR for keep-alive workflow" + gh pr create \ + --head "$branch_name" \ + --base ${{ github.event.repository.default_branch }} \ + --title "chore: keep scheduled workflows alive" \ + --body "This automated PR maintains repository activity to prevent scheduled workflows from being disabled. + +GitHub automatically disables scheduled workflows after 60 days of repository inactivity. This PR is reopened and closed periodically by the keep-alive workflow to ensure continuous operation. + +**Note:** This PR is managed by automation and will be reopened/closed automatically. Do not delete this PR or its branch." + + # Get the newly created PR number + pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number') + echo "Created PR #$pr_number" else - echo "No recent activity. Will create keep-alive activity." - echo "needs_activity=true" >> $GITHUB_OUTPUT + echo "PR #$pr_number already exists" fi + + # Store PR number for next step + echo "pr_number=$pr_number" >> $GITHUB_OUTPUT + id: setup - - name: Create and close PR to maintain activity - if: steps.check_activity.outputs.needs_activity == 'true' + - name: Reopen and close PR to maintain activity env: GH_TOKEN: ${{ github.token }} shell: bash run: | - # Configure git - git config --local user.name "github-actions[bot]" - git config --local user.email "github-actions[bot]@users.noreply.github.com" + pr_number="${{ steps.setup.outputs.pr_number }}" - # Create a unique branch name - branch_name="keep-alive-$(date +%Y%m%d-%H%M%S)" - - # Create a new branch and make an empty commit - git checkout -b "$branch_name" - git commit --allow-empty -m "chore: keep scheduled workflows alive - -This automated commit ensures scheduled workflows remain active. -GitHub disables scheduled workflows after 60 days of repository inactivity." + if [ -z "$pr_number" ]; then + echo "Error: PR number not found" + exit 1 + fi - git push --set-upstream origin "$branch_name" + # Check PR state + pr_state=$(gh pr view "$pr_number" --json state --jq '.state') + echo "PR #$pr_number is currently: $pr_state" - # Create PR - pr_url=$(gh pr create \ - --head "$branch_name" \ - --base ${{ github.event.repository.default_branch }} \ - --title "chore: keep scheduled workflows alive" \ - --body "This automated PR is created to prevent scheduled workflows from being disabled due to inactivity. - -GitHub automatically disables scheduled workflows when no repository activity has occurred in 60 days. This PR ensures continuous operation of our scheduled builds and tests. - -This PR contains an empty commit and will be automatically closed without merging.") - - echo "Created PR: $pr_url" + # Reopen if closed + if [ "$pr_state" = "CLOSED" ]; then + echo "Reopening PR #$pr_number" + gh pr reopen "$pr_number" + sleep 2 + fi - # Extract PR number from URL (require at least one digit) - pr_number=$(echo "$pr_url" | grep -oE '[0-9]+$') + # Close the PR with a timestamp comment + echo "Closing PR #$pr_number" + gh pr close "$pr_number" --comment "Keep-alive activity: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" - if [ -n "$pr_number" ]; then - # Close the PR immediately - gh pr close "$pr_number" --comment "Closing this keep-alive PR as its purpose (maintaining repository activity) has been fulfilled." - echo "Closed PR #$pr_number" - - # Clean up the branch - git push origin --delete "$branch_name" - echo "Deleted branch: $branch_name" - else - echo "Warning: Could not extract PR number from URL: $pr_url" - echo "The branch $branch_name was created but PR may not have been properly closed." - echo "Attempting to clean up branch anyway..." - git push origin --delete "$branch_name" 2>/dev/null || echo "Branch cleanup failed, may need manual cleanup" - fi + echo "Keep-alive activity completed successfully" From 769babd65dea43aa5b61fb56e7e7bc603860623e Mon Sep 17 00:00:00 2001 From: Tim Hess Date: Fri, 30 Jan 2026 15:16:32 -0600 Subject: [PATCH 07/12] run keep-alive every month - bump a couple other action refs - whitespace cleanup --- .github/workflows/build.yml | 8 ++++--- .github/workflows/keep-alive.yml | 40 +++++++++++++------------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba38298..1aef4ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,9 @@ jobs: 10.0.* - name: Git checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 + with: + persist-credentials: false - name: Restore packages run: dotnet restore --verbosity minimal @@ -204,7 +206,7 @@ jobs: path: packages - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x source-url: ${{ vars.AZURE_ARTIFACTS_FEED_URL }} @@ -235,7 +237,7 @@ jobs: steps: - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index 5182f74..ac30d14 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -5,29 +5,19 @@ # This workflow prevents that by creating minimal activity when needed. # # How it works: -# 1. Runs every 59 days (using approximation via cron) +# 1. Runs on the first day of every month # 2. Checks if the keep-alive PR exists # 3. If PR doesn't exist, creates it with a persistent branch # 4. Reopens the PR (if closed) and immediately closes it again # 5. This activity prevents workflow deactivation # -# Benefits: -# - Uses the same PR repeatedly (minimal noise) -# - No human intervention required -# - Runs every ~59 days (close to 60-day limit but safe) -# # Manual trigger: # This workflow can also be triggered manually via workflow_dispatch if needed. -# -# Related issue: https://github.com/SteeltoeOSS/NetCoreToolTemplates/issues/106 -# name: Keep Scheduled Workflows Alive on: schedule: - # Run every 59 days (approximate using twice per 4 months = ~60 days) - # This is close to GitHub's 60-day limit but provides a safety margin - - cron: "0 0 1 */2 *" + - cron: "0 0 1 * *" workflow_dispatch: permissions: @@ -37,13 +27,15 @@ permissions: jobs: keep-alive: - name: Keep repository active + name: Keep repository GitHub Actions active runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Git checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 + with: + persist-credentials: false - name: Ensure keep-alive branch and PR exist env: @@ -56,7 +48,7 @@ jobs: # Configure git git config --local user.name "github-actions[bot]" git config --local user.email "github-actions[bot]@users.noreply.github.com" - + # Check if branch exists remotely if git ls-remote --heads origin "$branch_name" | grep -q "$branch_name"; then echo "Branch $branch_name already exists" @@ -69,10 +61,10 @@ This branch is used by the keep-alive workflow to maintain repository activity. GitHub disables scheduled workflows after 60 days of repository inactivity." git push --set-upstream origin "$branch_name" fi - + # Check if PR exists (open or closed) pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number' 2>/dev/null || echo "") - + if [ -z "$pr_number" ]; then echo "Creating new PR for keep-alive workflow" gh pr create \ @@ -84,14 +76,14 @@ GitHub disables scheduled workflows after 60 days of repository inactivity." GitHub automatically disables scheduled workflows after 60 days of repository inactivity. This PR is reopened and closed periodically by the keep-alive workflow to ensure continuous operation. **Note:** This PR is managed by automation and will be reopened/closed automatically. Do not delete this PR or its branch." - + # Get the newly created PR number pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number') echo "Created PR #$pr_number" else echo "PR #$pr_number already exists" fi - + # Store PR number for next step echo "pr_number=$pr_number" >> $GITHUB_OUTPUT id: setup @@ -102,25 +94,25 @@ GitHub automatically disables scheduled workflows after 60 days of repository in shell: bash run: | pr_number="${{ steps.setup.outputs.pr_number }}" - + if [ -z "$pr_number" ]; then echo "Error: PR number not found" exit 1 fi - + # Check PR state pr_state=$(gh pr view "$pr_number" --json state --jq '.state') echo "PR #$pr_number is currently: $pr_state" - + # Reopen if closed if [ "$pr_state" = "CLOSED" ]; then echo "Reopening PR #$pr_number" gh pr reopen "$pr_number" sleep 2 fi - + # Close the PR with a timestamp comment echo "Closing PR #$pr_number" gh pr close "$pr_number" --comment "Keep-alive activity: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" - + echo "Keep-alive activity completed successfully" From f0311235110aefc23de10331603560f3d386eb31 Mon Sep 17 00:00:00 2001 From: Tim Hess Date: Fri, 30 Jan 2026 15:27:38 -0600 Subject: [PATCH 08/12] move commit message and pr body to env vars for easier formatting --- .github/workflows/keep-alive.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index ac30d14..81d1e09 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -40,6 +40,17 @@ jobs: - name: Ensure keep-alive branch and PR exist env: GH_TOKEN: ${{ github.token }} + COMMIT_MSG: | + chore: keep scheduled workflows alive + + This branch is used by the keep-alive workflow to maintain repository activity. + GitHub disables scheduled workflows after 60 days of repository inactivity. + PR_BODY: | + This automated PR maintains repository activity to prevent scheduled workflows from being disabled. + + GitHub automatically disables scheduled workflows after 60 days of repository inactivity. This PR is reopened and closed periodically by the keep-alive workflow to ensure continuous operation. + + **Note:** This PR is managed by automation and will be reopened/closed automatically. Do not delete this PR or its branch. shell: bash run: | # Use a fixed branch name for persistence @@ -55,10 +66,7 @@ jobs: else echo "Creating new branch $branch_name" git checkout -b "$branch_name" - git commit --allow-empty -m "chore: keep scheduled workflows alive - -This branch is used by the keep-alive workflow to maintain repository activity. -GitHub disables scheduled workflows after 60 days of repository inactivity." + git commit --allow-empty -m "$COMMIT_MSG" git push --set-upstream origin "$branch_name" fi @@ -71,11 +79,7 @@ GitHub disables scheduled workflows after 60 days of repository inactivity." --head "$branch_name" \ --base ${{ github.event.repository.default_branch }} \ --title "chore: keep scheduled workflows alive" \ - --body "This automated PR maintains repository activity to prevent scheduled workflows from being disabled. - -GitHub automatically disables scheduled workflows after 60 days of repository inactivity. This PR is reopened and closed periodically by the keep-alive workflow to ensure continuous operation. - -**Note:** This PR is managed by automation and will be reopened/closed automatically. Do not delete this PR or its branch." + --body "$PR_BODY" # Get the newly created PR number pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number') From ba20ecb12bbcbebcd7225717030a28c6457ffb70 Mon Sep 17 00:00:00 2001 From: Tim Hess Date: Mon, 2 Feb 2026 13:49:49 -0600 Subject: [PATCH 09/12] add step to check for activity in the last 30 days to reduce noise --- .github/workflows/keep-alive.yml | 49 ++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index 81d1e09..58ad946 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -37,7 +37,44 @@ jobs: with: persist-credentials: false + - name: Check for recent activity + id: check_activity + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + # Check if the repository has been active in the last 30 days + # We check the default branch for recent commits + + DEFAULT_BRANCH="${{ github.event.repository.default_branch }}" + if [ -z "$DEFAULT_BRANCH" ]; then + DEFAULT_BRANCH="main" + fi + + echo "Checking activity on branch: $DEFAULT_BRANCH" + + # Get the date of the last commit on the default branch + last_commit_date=$(gh api "repos/${{ github.repository }}/commits/$DEFAULT_BRANCH" --jq '.commit.committer.date') + echo "Last commit date: $last_commit_date" + + # Calculate days since last commit + last_commit_seconds=$(date -d "$last_commit_date" +%s) + current_seconds=$(date +%s) + days_diff=$(( (current_seconds - last_commit_seconds) / 86400 )) + + echo "Days since last activity: $days_diff" + + # Threshold: 30 days (Run monthly, so if no activity in last month, we should act) + if [ "$days_diff" -lt 30 ]; then + echo "Repository is active enough (less than 30 days since last commit). No keep-alive action needed." + echo "run_keep_alive=false" >> $GITHUB_OUTPUT + else + echo "Repository inactive for $days_diff days. Running keep-alive action." + echo "run_keep_alive=true" >> $GITHUB_OUTPUT + fi + - name: Ensure keep-alive branch and PR exist + if: steps.check_activity.outputs.run_keep_alive == 'true' env: GH_TOKEN: ${{ github.token }} COMMIT_MSG: | @@ -55,7 +92,7 @@ jobs: run: | # Use a fixed branch name for persistence branch_name="keep-alive-workflow" - + # Configure git git config --local user.name "github-actions[bot]" git config --local user.email "github-actions[bot]@users.noreply.github.com" @@ -63,13 +100,18 @@ jobs: # Check if branch exists remotely if git ls-remote --heads origin "$branch_name" | grep -q "$branch_name"; then echo "Branch $branch_name already exists" + git fetch origin "$branch_name" + git checkout "$branch_name" + git pull origin "$branch_name" else echo "Creating new branch $branch_name" git checkout -b "$branch_name" - git commit --allow-empty -m "$COMMIT_MSG" - git push --set-upstream origin "$branch_name" fi + # Create/Update commit to ensure activity + git commit --allow-empty -m "$COMMIT_MSG" + git push origin "$branch_name" + # Check if PR exists (open or closed) pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number' 2>/dev/null || echo "") @@ -93,6 +135,7 @@ jobs: id: setup - name: Reopen and close PR to maintain activity + if: steps.check_activity.outputs.run_keep_alive == 'true' env: GH_TOKEN: ${{ github.token }} shell: bash From 34c7b1f43802cad2e739dccbdc6ea9e7e3ad3850 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:38:23 +0000 Subject: [PATCH 10/12] Clarify why workflow runs every 30 days instead of 60 Co-authored-by: TimHess <3947063+TimHess@users.noreply.github.com> --- .github/workflows/keep-alive.yml | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index 58ad946..b1b6932 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -4,12 +4,20 @@ # GitHub automatically disables scheduled workflows after 60 days of repository inactivity. # This workflow prevents that by creating minimal activity when needed. # +# Important Constraint: +# This workflow MUST run at least every 60 days. If it doesn't run for 60 days, GitHub will +# disable it permanently and it won't be able to run itself anymore. Therefore, we schedule +# it to run every 30 days (monthly) to ensure it always stays active, even if there are +# holidays, weekends, or workflow execution delays. +# # How it works: -# 1. Runs on the first day of every month -# 2. Checks if the keep-alive PR exists -# 3. If PR doesn't exist, creates it with a persistent branch -# 4. Reopens the PR (if closed) and immediately closes it again -# 5. This activity prevents workflow deactivation +# 1. Runs on the first day of every month (~30 days) +# 2. Checks if there's been activity in the last 30 days (commits on default branch) +# 3. If no recent activity, creates/updates a persistent keep-alive PR: +# - Creates a keep-alive branch (if it doesn't exist) +# - Adds an empty commit to the branch +# - Opens/reopens the PR and immediately closes it +# 4. If recent activity exists, skips the keep-alive action to reduce noise # # Manual trigger: # This workflow can also be triggered manually via workflow_dispatch if needed. @@ -17,6 +25,10 @@ name: Keep Scheduled Workflows Alive on: schedule: + # Run on the 1st of every month (approximately every 30 days) + # IMPORTANT: This MUST run at least every 60 days. If 60 days pass without running, + # GitHub will disable this workflow and it won't be able to run itself anymore. + # Running monthly (every ~30 days) ensures this never happens. - cron: "0 0 1 * *" workflow_dispatch: @@ -44,7 +56,8 @@ jobs: shell: bash run: | # Check if the repository has been active in the last 30 days - # We check the default branch for recent commits + # If there's been recent activity, we skip the keep-alive action to reduce noise. + # We check the default branch for recent commits. DEFAULT_BRANCH="${{ github.event.repository.default_branch }}" if [ -z "$DEFAULT_BRANCH" ]; then @@ -64,7 +77,9 @@ jobs: echo "Days since last activity: $days_diff" - # Threshold: 30 days (Run monthly, so if no activity in last month, we should act) + # Threshold: 30 days + # Since this workflow runs monthly, if there's been activity in the last 30 days, + # we don't need to create additional keep-alive activity. if [ "$days_diff" -lt 30 ]; then echo "Repository is active enough (less than 30 days since last commit). No keep-alive action needed." echo "run_keep_alive=false" >> $GITHUB_OUTPUT From bb3a97c8db63f841ae2b54f64c9dac26b445dfce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:56:19 +0000 Subject: [PATCH 11/12] Reduce verbosity, add draft PR flag, and document empty commits Co-authored-by: TimHess <3947063+TimHess@users.noreply.github.com> --- .github/workflows/keep-alive.yml | 78 ++++++++++---------------------- 1 file changed, 23 insertions(+), 55 deletions(-) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index b1b6932..f461eca 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -1,34 +1,19 @@ # Keep Scheduled Workflows Alive # -# Purpose: -# GitHub automatically disables scheduled workflows after 60 days of repository inactivity. -# This workflow prevents that by creating minimal activity when needed. +# GitHub disables scheduled workflows after 60 days of inactivity. This workflow prevents that +# by creating minimal activity when needed. # -# Important Constraint: -# This workflow MUST run at least every 60 days. If it doesn't run for 60 days, GitHub will -# disable it permanently and it won't be able to run itself anymore. Therefore, we schedule -# it to run every 30 days (monthly) to ensure it always stays active, even if there are -# holidays, weekends, or workflow execution delays. +# IMPORTANT: Must run within 60 days or it will disable itself. Runs monthly (~30 days) for safety. # # How it works: -# 1. Runs on the first day of every month (~30 days) -# 2. Checks if there's been activity in the last 30 days (commits on default branch) -# 3. If no recent activity, creates/updates a persistent keep-alive PR: -# - Creates a keep-alive branch (if it doesn't exist) -# - Adds an empty commit to the branch -# - Opens/reopens the PR and immediately closes it -# 4. If recent activity exists, skips the keep-alive action to reduce noise -# -# Manual trigger: -# This workflow can also be triggered manually via workflow_dispatch if needed. +# 1. Monthly: checks for activity in last 30 days +# 2. If inactive: creates/updates persistent draft PR with empty commits +# 3. If active: skips to reduce noise name: Keep Scheduled Workflows Alive on: schedule: - # Run on the 1st of every month (approximately every 30 days) - # IMPORTANT: This MUST run at least every 60 days. If 60 days pass without running, - # GitHub will disable this workflow and it won't be able to run itself anymore. - # Running monthly (every ~30 days) ensures this never happens. + # Monthly (1st of month). Must run within 60 days or workflow disables itself. - cron: "0 0 1 * *" workflow_dispatch: @@ -55,10 +40,7 @@ jobs: GH_TOKEN: ${{ github.token }} shell: bash run: | - # Check if the repository has been active in the last 30 days - # If there's been recent activity, we skip the keep-alive action to reduce noise. - # We check the default branch for recent commits. - + # Skip keep-alive if repository has been active in last 30 days DEFAULT_BRANCH="${{ github.event.repository.default_branch }}" if [ -z "$DEFAULT_BRANCH" ]; then DEFAULT_BRANCH="main" @@ -66,25 +48,20 @@ jobs: echo "Checking activity on branch: $DEFAULT_BRANCH" - # Get the date of the last commit on the default branch last_commit_date=$(gh api "repos/${{ github.repository }}/commits/$DEFAULT_BRANCH" --jq '.commit.committer.date') echo "Last commit date: $last_commit_date" - # Calculate days since last commit last_commit_seconds=$(date -d "$last_commit_date" +%s) current_seconds=$(date +%s) days_diff=$(( (current_seconds - last_commit_seconds) / 86400 )) echo "Days since last activity: $days_diff" - # Threshold: 30 days - # Since this workflow runs monthly, if there's been activity in the last 30 days, - # we don't need to create additional keep-alive activity. if [ "$days_diff" -lt 30 ]; then - echo "Repository is active enough (less than 30 days since last commit). No keep-alive action needed." + echo "Active (< 30 days). No keep-alive needed." echo "run_keep_alive=false" >> $GITHUB_OUTPUT else - echo "Repository inactive for $days_diff days. Running keep-alive action." + echo "Inactive ($days_diff days). Running keep-alive." echo "run_keep_alive=true" >> $GITHUB_OUTPUT fi @@ -95,57 +72,51 @@ jobs: COMMIT_MSG: | chore: keep scheduled workflows alive - This branch is used by the keep-alive workflow to maintain repository activity. - GitHub disables scheduled workflows after 60 days of repository inactivity. + Empty commit to maintain repository activity. + GitHub disables scheduled workflows after 60 days of inactivity. PR_BODY: | - This automated PR maintains repository activity to prevent scheduled workflows from being disabled. + Automated keep-alive to prevent scheduled workflows from being disabled. - GitHub automatically disables scheduled workflows after 60 days of repository inactivity. This PR is reopened and closed periodically by the keep-alive workflow to ensure continuous operation. + GitHub disables scheduled workflows after 60 days of inactivity. This draft PR is periodically updated with empty commits and reopened/closed to maintain activity. - **Note:** This PR is managed by automation and will be reopened/closed automatically. Do not delete this PR or its branch. + Empty commits are used because they create activity without modifying repository content. shell: bash run: | - # Use a fixed branch name for persistence branch_name="keep-alive-workflow" - # Configure git git config --local user.name "github-actions[bot]" git config --local user.email "github-actions[bot]@users.noreply.github.com" - # Check if branch exists remotely if git ls-remote --heads origin "$branch_name" | grep -q "$branch_name"; then - echo "Branch $branch_name already exists" + echo "Branch exists" git fetch origin "$branch_name" git checkout "$branch_name" git pull origin "$branch_name" else - echo "Creating new branch $branch_name" + echo "Creating branch" git checkout -b "$branch_name" fi - # Create/Update commit to ensure activity git commit --allow-empty -m "$COMMIT_MSG" git push origin "$branch_name" - # Check if PR exists (open or closed) pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number' 2>/dev/null || echo "") if [ -z "$pr_number" ]; then - echo "Creating new PR for keep-alive workflow" + echo "Creating draft PR" gh pr create \ + --draft \ --head "$branch_name" \ --base ${{ github.event.repository.default_branch }} \ --title "chore: keep scheduled workflows alive" \ --body "$PR_BODY" - # Get the newly created PR number pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number') echo "Created PR #$pr_number" else - echo "PR #$pr_number already exists" + echo "PR #$pr_number exists" fi - # Store PR number for next step echo "pr_number=$pr_number" >> $GITHUB_OUTPUT id: setup @@ -162,19 +133,16 @@ jobs: exit 1 fi - # Check PR state pr_state=$(gh pr view "$pr_number" --json state --jq '.state') - echo "PR #$pr_number is currently: $pr_state" + echo "PR #$pr_number is $pr_state" - # Reopen if closed if [ "$pr_state" = "CLOSED" ]; then echo "Reopening PR #$pr_number" gh pr reopen "$pr_number" sleep 2 fi - # Close the PR with a timestamp comment echo "Closing PR #$pr_number" - gh pr close "$pr_number" --comment "Keep-alive activity: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" + gh pr close "$pr_number" --comment "Keep-alive: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" - echo "Keep-alive activity completed successfully" + echo "Keep-alive completed" From 4dc69d2bb630b08e1931f8c0dea8a1c90a04bb7d Mon Sep 17 00:00:00 2001 From: Tim Hess Date: Tue, 3 Feb 2026 14:22:44 -0600 Subject: [PATCH 12/12] further comment cleanup --- .github/workflows/keep-alive.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index f461eca..148c8e2 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -3,17 +3,15 @@ # GitHub disables scheduled workflows after 60 days of inactivity. This workflow prevents that # by creating minimal activity when needed. # -# IMPORTANT: Must run within 60 days or it will disable itself. Runs monthly (~30 days) for safety. -# # How it works: -# 1. Monthly: checks for activity in last 30 days -# 2. If inactive: creates/updates persistent draft PR with empty commits -# 3. If active: skips to reduce noise +# 1. Runs on 1st of the month via cron schedule +# 2. Checks for activity in last 30 days +# 3. If inactive: creates/updates persistent draft PR with empty commits +# 4. If active: skips to reduce noise name: Keep Scheduled Workflows Alive on: schedule: - # Monthly (1st of month). Must run within 60 days or workflow disables itself. - cron: "0 0 1 * *" workflow_dispatch: