Release reminders #15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| on: | |
| schedule: | |
| - cron: '0 9 * * 1' # Every Monday at 9am UTC | |
| workflow_dispatch: | |
| name: Release reminders | |
| jobs: | |
| release-reminders: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Generate token from GitHub App | |
| id: app-token | |
| uses: tibdex/github-app-token@v2 | |
| with: | |
| app_id: ${{ secrets.APP_ID }} | |
| private_key: ${{ secrets.APP_PRIVATE_KEY }} | |
| - name: Check packages and create reminders | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| run: | | |
| # Get repos from r-universe + extras | |
| REPOS=$(curl -s "https://epiforecasts.r-universe.dev/api/packages" | jq -r '.[]._upstream // empty' | sed 's|https://github.com/||') | |
| REPOS="$REPOS $(grep 'repo:' _data/software-extras.yml | sed 's/.*: //')" | |
| for repo in $REPOS; do | |
| echo "Checking $repo..." | |
| # Get last release info | |
| RELEASE_INFO=$(gh release view --repo "$repo" --json tagName,publishedAt 2>/dev/null || echo '{}') | |
| LAST_TAG=$(echo "$RELEASE_INFO" | jq -r '.tagName // empty') | |
| LAST_DATE=$(echo "$RELEASE_INFO" | jq -r '.publishedAt // empty') | |
| if [ -n "$LAST_TAG" ]; then | |
| # Has releases - skip if released within last 60 days | |
| if [ -n "$LAST_DATE" ]; then | |
| DAYS_SINCE=$(( ($(date +%s) - $(date -d "$LAST_DATE" +%s)) / 86400 )) | |
| if [ "$DAYS_SINCE" -lt 60 ]; then | |
| echo " Released $DAYS_SINCE days ago, skipping" | |
| continue | |
| fi | |
| fi | |
| # Check commits since last release (need >1 because version bump after release counts as 1) | |
| # Filter out dependabot commits and their merge commits - only count real changes | |
| COMMITS_DATA=$(gh api "repos/$repo/compare/$LAST_TAG...HEAD" 2>/dev/null || echo '{"ahead_by":0,"commits":[]}') | |
| COMMITS=$(echo "$COMMITS_DATA" | jq '.ahead_by') | |
| REAL_COMMITS=$(echo "$COMMITS_DATA" | jq '[.commits[] | select( | |
| .author.login != "dependabot[bot]" and | |
| (.commit.message | test("^Bump "; "i") | not) and | |
| (.commit.message | test("dependabot"; "i") | not) | |
| )] | length') | |
| if [ "$REAL_COMMITS" -lt 2 ]; then | |
| echo " Only $REAL_COMMITS non-bot commit(s) since last release (${COMMITS} total), skipping" | |
| continue | |
| fi | |
| RELEASE_LINE="**Last release:** $LAST_TAG ($DAYS_SINCE days ago)" | |
| COMMITS_LINE="**Commits since:** $REAL_COMMITS (excl. bot commits) ([view diff](https://github.com/$repo/compare/$LAST_TAG...HEAD))" | |
| else | |
| # No releases yet - check if repo has any commits at all | |
| COMMITS=$(gh api "repos/$repo" --jq '.size' 2>/dev/null || echo "0") | |
| if [ "$COMMITS" = "0" ]; then | |
| echo " Empty repo, skipping" | |
| continue | |
| fi | |
| RELEASE_LINE="**Last release:** None yet" | |
| COMMITS_LINE="" | |
| DAYS_SINCE="N/A" | |
| fi | |
| # Skip if there's already an open release reminder issue | |
| MONTH=$(date +'%B %Y') | |
| EXISTING=$(gh issue list --repo "$repo" --state open --json title --jq '[.[] | select(.title | test("Release reminder"))] | length' 2>/dev/null || echo "0") | |
| if [ "$EXISTING" != "0" ]; then | |
| echo " Open release reminder already exists, skipping" | |
| continue | |
| fi | |
| # Get open bugs | |
| BUGS=$(gh issue list --repo "$repo" --label bug --state open --json number,title --jq '.[] | "- #\(.number) \(.title)"' 2>/dev/null || echo "") | |
| if [ -z "$BUGS" ]; then | |
| BUGS="None" | |
| fi | |
| # Get open PRs | |
| PRS=$(gh pr list --repo "$repo" --state open --json number,title --jq '.[] | "- #\(.number) \(.title)"' 2>/dev/null || echo "") | |
| if [ -z "$PRS" ]; then | |
| PRS="None" | |
| fi | |
| # Detect package type | |
| HAS_DESCRIPTION=$(gh api "repos/$repo/contents/DESCRIPTION" --jq '.name' 2>/dev/null || echo "") | |
| HAS_PROJECT_TOML=$(gh api "repos/$repo/contents/Project.toml" --jq '.name' 2>/dev/null || echo "") | |
| # Create reminder issue | |
| echo " Creating reminder for $repo" | |
| { | |
| echo "$RELEASE_LINE" | |
| [ -n "$COMMITS_LINE" ] && echo "$COMMITS_LINE" | |
| echo "**What's new:** [NEWS.md](https://github.com/$repo/blob/HEAD/NEWS.md)" | |
| echo "" | |
| echo "**Open bugs:**" | |
| echo "$BUGS" | |
| echo "" | |
| echo "**Open PRs:**" | |
| echo "$PRS" | |
| echo "" | |
| echo "## To release" | |
| if [ -n "$HAS_DESCRIPTION" ]; then | |
| echo 'Run `usethis::use_release_issue()` for the full checklist.' | |
| elif [ -n "$HAS_PROJECT_TOML" ]; then | |
| echo '1. Bump version in `Project.toml`' | |
| echo '2. Comment `@JuliaRegistrator register` on the commit' | |
| else | |
| echo 'Create a GitHub release.' | |
| fi | |
| echo "" | |
| echo "*Close this issue if not releasing this cycle.*" | |
| echo "" | |
| echo "---" | |
| echo "*Created by the [release reminders workflow](https://github.com/epiforecasts/epiforecasts.github.io/blob/main/.github/workflows/release-reminders.yaml).*" | |
| } > /tmp/release-body.md | |
| # Ensure 'release' label exists, create issue without label if it fails | |
| gh label create release --repo "$repo" --description "Release tracking" --color "0E8A16" 2>/dev/null || true | |
| gh issue create --repo "$repo" \ | |
| --title "Release reminder - $MONTH" \ | |
| --label "release" \ | |
| --body-file /tmp/release-body.md 2>/dev/null \ | |
| || gh issue create --repo "$repo" \ | |
| --title "Release reminder - $MONTH" \ | |
| --body-file /tmp/release-body.md \ | |
| || echo " Failed to create issue for $repo" | |
| done |