55# This workflow prevents that by creating minimal activity when needed.
66#
77# How it works:
8- # 1. Runs monthly on the 1st of each month
9- # 2. Checks for any commits, issues, or PRs in the last 50 days
10- # 3. If no activity is found:
11- # - Creates a temporary branch with an empty commit
12- # - Opens a pull request
13- # - Immediately closes the PR
14- # - Deletes the temporary branch
15- # 4. If recent activity exists, does nothing
8+ # 1. Runs every 59 days (using approximation via cron)
9+ # 2. Checks if the keep-alive PR exists
10+ # 3. If PR doesn't exist, creates it with a persistent branch
11+ # 4. Reopens the PR (if closed) and immediately closes it again
12+ # 5. This activity prevents workflow deactivation
13+ #
14+ # Benefits:
15+ # - Uses the same PR repeatedly (minimal noise)
16+ # - No human intervention required
17+ # - Runs every ~59 days (close to 60-day limit but safe)
1618#
1719# Manual trigger:
1820# This workflow can also be triggered manually via workflow_dispatch if needed.
@@ -23,10 +25,9 @@ name: Keep Scheduled Workflows Alive
2325
2426on :
2527 schedule :
26- # Run on the 1st of each month (checks for activity in the last 50 days before taking action)
27- # This conservative frequency ensures we never hit the 60-day GitHub inactivity limit
28- # even accounting for holidays, weekends, or workflow execution delays
29- - cron : " 0 0 1 * *"
28+ # Run every 59 days (approximate using twice per 4 months = ~60 days)
29+ # This is close to GitHub's 60-day limit but provides a safety margin
30+ - cron : " 0 0 1 */2 *"
3031 workflow_dispatch :
3132
3233permissions :
@@ -44,91 +45,82 @@ jobs:
4445 - name : Git checkout
4546 uses : actions/checkout@v4
4647
47- - name : Check for recent activity
48- id : check_activity
48+ - name : Ensure keep-alive branch and PR exist
4949 env :
5050 GH_TOKEN : ${{ github.token }}
5151 shell : bash
5252 run : |
53- # Check for any activity in the last 50 days
54- # Note: date -d is GNU-specific, but this workflow runs on ubuntu-latest which has GNU date
55- cutoff_date=$(date -u -d '50 days ago' '+%Y-%m-%dT%H:%M:%SZ')
56- echo "Checking for activity since: $cutoff_date"
53+ # Use a fixed branch name for persistence
54+ branch_name="keep-alive-workflow"
5755
58- # Check for recent commits
59- recent_commits=$(gh api \
60- -H "Accept: application/vnd.github+json" \
61- -H "X-GitHub-Api-Version: 2022-11-28" \
62- "/repos/${{ github.repository }}/commits?since=$cutoff_date&per_page=1" \
63- --jq 'length' 2>/dev/null || echo "0")
56+ # Configure git
57+ git config --local user.name "github-actions[bot]"
58+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
6459
65- # Check for recent issues/PRs
66- recent_issues=$(gh api \
67- -H "Accept: application/vnd.github+json" \
68- -H "X-GitHub-Api-Version: 2022-11-28" \
69- "/repos/${{ github.repository }}/issues?since=$cutoff_date&per_page=1&state=all" \
70- --jq 'length' 2>/dev/null || echo "0")
60+ # Check if branch exists remotely
61+ if git ls-remote --heads origin "$branch_name" | grep -q "$branch_name"; then
62+ echo "Branch $branch_name already exists"
63+ else
64+ echo "Creating new branch $branch_name"
65+ git checkout -b "$branch_name"
66+ git commit --allow-empty -m "chore: keep scheduled workflows alive
67+
68+ This branch is used by the keep-alive workflow to maintain repository activity.
69+ GitHub disables scheduled workflows after 60 days of repository inactivity."
70+ git push --set-upstream origin "$branch_name"
71+ fi
7172
72- echo "Recent commits: $recent_commits"
73- echo "Recent issues/PRs: $recent_issues"
73+ # Check if PR exists (open or closed)
74+ pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number' 2>/dev/null || echo "")
7475
75- if [ "$recent_commits" -gt 0 ] || [ "$recent_issues" -gt 0 ]; then
76- echo "Recent activity detected. No action needed."
77- echo "needs_activity=false" >> $GITHUB_OUTPUT
76+ if [ -z "$pr_number" ]; then
77+ echo "Creating new PR for keep-alive workflow"
78+ gh pr create \
79+ --head "$branch_name" \
80+ --base ${{ github.event.repository.default_branch }} \
81+ --title "chore : keep scheduled workflows alive" \
82+ --body "This automated PR maintains repository activity to prevent scheduled workflows from being disabled.
83+
84+ 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.
85+
86+ **Note:** This PR is managed by automation and will be reopened/closed automatically. Do not delete this PR or its branch."
87+
88+ # Get the newly created PR number
89+ pr_number=$(gh pr list --head "$branch_name" --state all --json number --jq '.[0].number')
90+ echo "Created PR # $pr_number"
7891 else
79- echo "No recent activity. Will create keep-alive activity."
80- echo "needs_activity=true" >> $GITHUB_OUTPUT
92+ echo "PR # $pr_number already exists"
8193 fi
94+
95+ # Store PR number for next step
96+ echo "pr_number=$pr_number" >> $GITHUB_OUTPUT
97+ id : setup
8298
83- - name : Create and close PR to maintain activity
84- if : steps.check_activity.outputs.needs_activity == 'true'
99+ - name : Reopen and close PR to maintain activity
85100 env :
86101 GH_TOKEN : ${{ github.token }}
87102 shell : bash
88103 run : |
89- # Configure git
90- git config --local user.name "github-actions[bot]"
91- git config --local user.email "github-actions[bot]@users.noreply.github.com"
104+ pr_number="${{ steps.setup.outputs.pr_number }}"
92105
93- # Create a unique branch name
94- branch_name="keep-alive-$(date +%Y%m%d-%H%M%S)"
95-
96- # Create a new branch and make an empty commit
97- git checkout -b "$branch_name"
98- git commit --allow-empty -m "chore: keep scheduled workflows alive
99-
100- This automated commit ensures scheduled workflows remain active.
101- GitHub disables scheduled workflows after 60 days of repository inactivity."
106+ if [ -z "$pr_number" ]; then
107+ echo "Error: PR number not found"
108+ exit 1
109+ fi
102110
103- git push --set-upstream origin "$branch_name"
111+ # Check PR state
112+ pr_state=$(gh pr view "$pr_number" --json state --jq '.state')
113+ echo "PR #$pr_number is currently: $pr_state"
104114
105- # Create PR
106- pr_url=$(gh pr create \
107- --head "$branch_name" \
108- --base ${{ github.event.repository.default_branch }} \
109- --title "chore : keep scheduled workflows alive" \
110- --body "This automated PR is created to prevent scheduled workflows from being disabled due to inactivity.
111-
112- 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.
113-
114- This PR contains an empty commit and will be automatically closed without merging.")
115-
116- echo "Created PR : $pr_url"
115+ # Reopen if closed
116+ if [ "$pr_state" = "CLOSED" ]; then
117+ echo "Reopening PR #$pr_number"
118+ gh pr reopen "$pr_number"
119+ sleep 2
120+ fi
117121
118- # Extract PR number from URL (require at least one digit)
119- pr_number=$(echo "$pr_url" | grep -oE '[0-9]+$')
122+ # Close the PR with a timestamp comment
123+ echo "Closing PR #$pr_number"
124+ gh pr close "$pr_number" --comment "Keep-alive activity: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
120125
121- if [ -n "$pr_number" ]; then
122- # Close the PR immediately
123- gh pr close "$pr_number" --comment "Closing this keep-alive PR as its purpose (maintaining repository activity) has been fulfilled."
124- echo "Closed PR # $pr_number"
125-
126- # Clean up the branch
127- git push origin --delete "$branch_name"
128- echo "Deleted branch : $branch_name"
129- else
130- echo "Warning : Could not extract PR number from URL: $pr_url"
131- echo "The branch $branch_name was created but PR may not have been properly closed."
132- echo "Attempting to clean up branch anyway..."
133- git push origin --delete "$branch_name" 2>/dev/null || echo "Branch cleanup failed, may need manual cleanup"
134- fi
126+ echo "Keep-alive activity completed successfully"
0 commit comments