Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 }}
Expand Down Expand Up @@ -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

Expand Down
165 changes: 165 additions & 0 deletions .github/workflows/keep-alive.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# 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 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the PR_BODY below don't indicate that an empty commit is being pushed.

#
# Manual trigger:
# This workflow can also be triggered manually via workflow_dispatch if needed.
name: Keep Scheduled Workflows Alive

on:
schedule:
- cron: "0 0 1 * *"
workflow_dispatch:

permissions:
contents: write
pull-requests: write
issues: read

jobs:
keep-alive:
name: Keep repository GitHub Actions active
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: Git checkout
uses: actions/checkout@v6
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"
Comment on lines +55 to +56
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
echo "Days since last activity: $days_diff"
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: |
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
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"
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"
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"
gh pr create \
--head "$branch_name" \
--base ${{ github.event.repository.default_branch }} \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The step above has a fallback to main, which doesn't appear here. Is it redundant?

--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"
fi

# Store PR number for next step
echo "pr_number=$pr_number" >> $GITHUB_OUTPUT
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
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm no bash expert, but this looks like an assignment to me. According to https://stackoverflow.com/a/4277753, the typical syntax uses double brackets:

if [[ "$pr_state" == "CLOSED" ]]

echo "Reopening PR #$pr_number"
gh pr reopen "$pr_number"
sleep 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to sleep?

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"