Skip to content

feat: add pending reviewer ping to auto-update workflow #49

feat: add pending reviewer ping to auto-update workflow

feat: add pending reviewer ping to auto-update workflow #49

name: Auto Update Tool Versions
on:
schedule:
- cron: "0 2 * * 1-5" # 2:00 AM UTC, Monday to Friday
workflow_dispatch: # Allow manual trigger
push:
paths:
- '.github/workflows/auto-update-tools.yml' # Trigger on changes to this workflow for testing
jobs:
check-and-update-versions:
name: Check and update tool versions
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Fetch latest Scarb version from GitHub releases
id: scarb_version
env:
GH_TOKEN: ${{ github.token }}
run: |
latest_version=$(gh api repos/software-mansion/scarb/releases/latest --jq '.tag_name' | sed 's/^v//')
echo "Latest Scarb version found is $latest_version"
echo "LATEST=$latest_version" >> $GITHUB_OUTPUT
- name: Check if Scarb update needed
id: check_scarb
run: |
current_version=$(grep "^SCARB_LATEST_COMPATIBLE_VERSION=" starkup.sh | cut -d'"' -f2)
latest_version=${{ steps.scarb_version.outputs.LATEST }}
echo "Current Scarb version: $current_version"
echo "Latest Scarb version: $latest_version"
if [ "$latest_version" != "$current_version" ]; then
echo "UPDATE_NEEDED=true" >> $GITHUB_OUTPUT
echo "OLD_VERSION=$current_version" >> $GITHUB_OUTPUT
echo "NEW_VERSION=$latest_version" >> $GITHUB_OUTPUT
else
echo "UPDATE_NEEDED=false" >> $GITHUB_OUTPUT
fi
- name: Fetch latest Starknet Foundry version from GitHub releases
id: foundry_version
env:
GH_TOKEN: ${{ github.token }}
run: |
latest_version=$(gh api repos/foundry-rs/starknet-foundry/releases/latest --jq '.tag_name' | sed 's/^v//')
echo "Latest Starknet Foundry version found is $latest_version"
echo "LATEST=$latest_version" >> $GITHUB_OUTPUT
- name: Check if Foundry update needed
id: check_foundry
run: |
current_version=$(grep "^FOUNDRY_LATEST_COMPATIBLE_VERSION=" starkup.sh | cut -d'"' -f2)
latest_version=${{ steps.foundry_version.outputs.LATEST }}
echo "Current Foundry version: $current_version"
echo "Latest Foundry version: $latest_version"
if [ "$latest_version" != "$current_version" ]; then
echo "UPDATE_NEEDED=true" >> $GITHUB_OUTPUT
echo "OLD_VERSION=$current_version" >> $GITHUB_OUTPUT
echo "NEW_VERSION=$latest_version" >> $GITHUB_OUTPUT
else
echo "UPDATE_NEEDED=false" >> $GITHUB_OUTPUT
fi
- name: Fetch latest Cairo Coverage version from GitHub releases
id: coverage_version
env:
GH_TOKEN: ${{ github.token }}
run: |
latest_version=$(gh api repos/software-mansion/cairo-coverage/releases/latest --jq '.tag_name' | sed 's/^v//')
echo "Latest Cairo Coverage version found is $latest_version"
echo "LATEST=$latest_version" >> $GITHUB_OUTPUT
- name: Check if Coverage update needed
id: check_coverage
run: |
current_version=$(grep "^COVERAGE_LATEST_COMPATIBLE_VERSION=" starkup.sh | cut -d'"' -f2)
latest_version=${{ steps.coverage_version.outputs.LATEST }}
echo "Current Coverage version: $current_version"
echo "Latest Coverage version: $latest_version"
if [ "$latest_version" != "$current_version" ]; then
echo "UPDATE_NEEDED=true" >> $GITHUB_OUTPUT
echo "OLD_VERSION=$current_version" >> $GITHUB_OUTPUT
echo "NEW_VERSION=$latest_version" >> $GITHUB_OUTPUT
else
echo "UPDATE_NEEDED=false" >> $GITHUB_OUTPUT
fi
- name: Fetch latest Cairo Profiler version from GitHub releases
id: profiler_version
env:
GH_TOKEN: ${{ github.token }}
run: |
latest_version=$(gh api repos/software-mansion/cairo-profiler/releases/latest --jq '.tag_name' | sed 's/^v//')
echo "Latest Cairo Profiler version found is $latest_version"
echo "LATEST=$latest_version" >> $GITHUB_OUTPUT
- name: Check if Profiler update needed
id: check_profiler
run: |
current_version=$(grep "^PROFILER_LATEST_COMPATIBLE_VERSION=" starkup.sh | cut -d'"' -f2)
latest_version=${{ steps.profiler_version.outputs.LATEST }}
echo "Current Profiler version: $current_version"
echo "Latest Profiler version: $latest_version"
if [ "$latest_version" != "$current_version" ]; then
echo "UPDATE_NEEDED=true" >> $GITHUB_OUTPUT
echo "OLD_VERSION=$current_version" >> $GITHUB_OUTPUT
echo "NEW_VERSION=$latest_version" >> $GITHUB_OUTPUT
else
echo "UPDATE_NEEDED=false" >> $GITHUB_OUTPUT
fi
- name: Fetch Starknet Devnet version from Starknet Foundry .tool-versions
id: devnet_version
env:
GH_TOKEN: ${{ github.token }}
run: |
foundry_version=${{ steps.foundry_version.outputs.LATEST }}
devnet_version=$(curl -sS --fail -H "Authorization: Bearer $GH_TOKEN" https://raw.githubusercontent.com/foundry-rs/starknet-foundry/v${foundry_version}/.tool-versions | grep "^starknet-devnet" | awk '{print $2}')
echo "Starknet Devnet version found in Starknet Foundry v${foundry_version} is $devnet_version"
echo "LATEST=$devnet_version" >> $GITHUB_OUTPUT
- name: Check if Devnet update needed
id: check_devnet
run: |
current_version=$(grep "^DEVNET_LATEST_COMPATIBLE_VERSION=" starkup.sh | cut -d'"' -f2)
latest_version=${{ steps.devnet_version.outputs.LATEST }}
echo "Current Devnet version: $current_version"
echo "Latest Devnet version: $latest_version"
if [ "$latest_version" != "$current_version" ]; then
echo "UPDATE_NEEDED=true" >> $GITHUB_OUTPUT
echo "OLD_VERSION=$current_version" >> $GITHUB_OUTPUT
echo "NEW_VERSION=$latest_version" >> $GITHUB_OUTPUT
else
echo "UPDATE_NEEDED=false" >> $GITHUB_OUTPUT
fi
- name: Determine if any update is needed
id: check_any
run: |
if [ "${{ steps.check_scarb.outputs.UPDATE_NEEDED }}" = "true" ] || [ "${{ steps.check_foundry.outputs.UPDATE_NEEDED }}" = "true" ] || [ "${{ steps.check_coverage.outputs.UPDATE_NEEDED }}" = "true" ] || [ "${{ steps.check_profiler.outputs.UPDATE_NEEDED }}" = "true" ] || [ "${{ steps.check_devnet.outputs.UPDATE_NEEDED }}" = "true" ]; then
echo "UPDATE_NEEDED=true" >> $GITHUB_OUTPUT
else
echo "UPDATE_NEEDED=false" >> $GITHUB_OUTPUT
echo "No updates needed"
fi
- name: Bump starkup version
if: steps.check_any.outputs.UPDATE_NEEDED == 'true'
id: bump_version
run: |
current_script_version=$(grep "^SCRIPT_VERSION=" starkup.sh | cut -d'"' -f2)
echo "Current starkup version: $current_script_version"
IFS='.' read -r major minor patch <<< "$current_script_version"
new_patch=$((patch + 1))
new_script_version="${major}.${minor}.${new_patch}"
echo "New starkup version: $new_script_version"
echo "NEW_SCRIPT_VERSION=$new_script_version" >> $GITHUB_OUTPUT
- name: Read reviewers file
if: steps.check_any.outputs.UPDATE_NEEDED == 'true'
id: read_reviewers
run: |
# Read reviewers before switching branches
# NOTE: Teams (e.g., org/team-name) are not supported due to GitHub Actions permission limitations.
# Only individual GitHub usernames work. Documentation is in MAINTAINING.md.
# Check if .reviewers file exists, fail if it doesn't
if [ ! -f .reviewers ]; then
echo "Error: .reviewers file not found!"
exit 1
fi
echo "Reading reviewers from .reviewers file..."
reviewers=$(grep -v '^#' .reviewers | grep -v '^$' | tr '\n' ',' | sed 's/,$//')
# Check if we have any reviewers after filtering
if [ -z "$reviewers" ]; then
echo "Error: No reviewers found in .reviewers file!"
exit 1
fi
echo "REVIEWERS=$reviewers" >> $GITHUB_OUTPUT
echo "Found reviewers: $reviewers"
- name: Create PR for version update
if: steps.check_any.outputs.UPDATE_NEEDED == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
current_script_version=$(grep "^SCRIPT_VERSION=" starkup.sh | cut -d'"' -f2)
branch_name="auto-update-tools-v${{ steps.bump_version.outputs.NEW_SCRIPT_VERSION }}"
if git ls-remote --heads origin "$branch_name" | grep -q "$branch_name"; then
echo "Branch $branch_name already exists, updating it..."
git fetch origin "$branch_name"
git checkout -b "$branch_name" "origin/$branch_name" || git checkout "$branch_name"
else
echo "Creating new branch $branch_name..."
git checkout -b "$branch_name"
fi
if [ "${{ steps.check_scarb.outputs.UPDATE_NEEDED }}" = "true" ]; then
sed -i "s/SCARB_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_scarb.outputs.OLD_VERSION }}\"/SCARB_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_scarb.outputs.NEW_VERSION }}\"/" starkup.sh
fi
if [ "${{ steps.check_foundry.outputs.UPDATE_NEEDED }}" = "true" ]; then
sed -i "s/FOUNDRY_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_foundry.outputs.OLD_VERSION }}\"/FOUNDRY_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_foundry.outputs.NEW_VERSION }}\"/" starkup.sh
fi
if [ "${{ steps.check_coverage.outputs.UPDATE_NEEDED }}" = "true" ]; then
sed -i "s/COVERAGE_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_coverage.outputs.OLD_VERSION }}\"/COVERAGE_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_coverage.outputs.NEW_VERSION }}\"/" starkup.sh
fi
if [ "${{ steps.check_profiler.outputs.UPDATE_NEEDED }}" = "true" ]; then
sed -i "s/PROFILER_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_profiler.outputs.OLD_VERSION }}\"/PROFILER_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_profiler.outputs.NEW_VERSION }}\"/" starkup.sh
fi
if [ "${{ steps.check_devnet.outputs.UPDATE_NEEDED }}" = "true" ]; then
sed -i "s/DEVNET_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_devnet.outputs.OLD_VERSION }}\"/DEVNET_LATEST_COMPATIBLE_VERSION=\"${{ steps.check_devnet.outputs.NEW_VERSION }}\"/" starkup.sh
fi
sed -i "s/SCRIPT_VERSION=\"${current_script_version}\"/SCRIPT_VERSION=\"${{ steps.bump_version.outputs.NEW_SCRIPT_VERSION }}\"/" starkup.sh
git add starkup.sh
changes_committed=false
if git commit -m "chore: auto-update tool versions to starkup ${{ steps.bump_version.outputs.NEW_SCRIPT_VERSION }}"; then
changes_committed=true
else
echo "No changes to commit"
fi
git push origin "$branch_name" --force
pr_title="Bump tools; Prepare release ${{ steps.bump_version.outputs.NEW_SCRIPT_VERSION }}"
pr_body="This PR updates tool versions and bumps starkup version to \`${{ steps.bump_version.outputs.NEW_SCRIPT_VERSION }}\` for release.\n\n## Changes\n"
if [ "${{ steps.check_scarb.outputs.UPDATE_NEEDED }}" = "true" ]; then
pr_body="${pr_body}- Scarb: \`${{ steps.check_scarb.outputs.OLD_VERSION }}\` → [\`${{ steps.check_scarb.outputs.NEW_VERSION }}\`](https://github.com/software-mansion/scarb/releases/tag/v${{ steps.check_scarb.outputs.NEW_VERSION }})\n"
fi
if [ "${{ steps.check_foundry.outputs.UPDATE_NEEDED }}" = "true" ]; then
pr_body="${pr_body}- Starknet Foundry: \`${{ steps.check_foundry.outputs.OLD_VERSION }}\` → [\`${{ steps.check_foundry.outputs.NEW_VERSION }}\`](https://github.com/foundry-rs/starknet-foundry/releases/tag/v${{ steps.check_foundry.outputs.NEW_VERSION }})\n"
fi
if [ "${{ steps.check_coverage.outputs.UPDATE_NEEDED }}" = "true" ]; then
pr_body="${pr_body}- Cairo Coverage: \`${{ steps.check_coverage.outputs.OLD_VERSION }}\` → [\`${{ steps.check_coverage.outputs.NEW_VERSION }}\`](https://github.com/software-mansion/cairo-coverage/releases/tag/v${{ steps.check_coverage.outputs.NEW_VERSION }})\n"
fi
if [ "${{ steps.check_profiler.outputs.UPDATE_NEEDED }}" = "true" ]; then
pr_body="${pr_body}- Cairo Profiler: \`${{ steps.check_profiler.outputs.OLD_VERSION }}\` → [\`${{ steps.check_profiler.outputs.NEW_VERSION }}\`](https://github.com/software-mansion/cairo-profiler/releases/tag/v${{ steps.check_profiler.outputs.NEW_VERSION }})\n"
fi
if [ "${{ steps.check_devnet.outputs.UPDATE_NEEDED }}" = "true" ]; then
pr_body="${pr_body}- Starknet Devnet: \`${{ steps.check_devnet.outputs.OLD_VERSION }}\` → [\`${{ steps.check_devnet.outputs.NEW_VERSION }}\`](https://github.com/0xSpaceShard/starknet-devnet/releases/tag/v${{ steps.check_devnet.outputs.NEW_VERSION }})\n"
fi
pr_body="${pr_body}- Starkup version: \`${current_script_version}\` → \`${{ steps.bump_version.outputs.NEW_SCRIPT_VERSION }}\`"
pr_body_formatted=$(printf "%b" "$pr_body")
pr_created=false
if gh pr list --head "$branch_name" --json number --jq '.[0].number' | grep -q .; then
pr_number=$(gh pr list --head "$branch_name" --json number --jq '.[0].number')
echo "Updating existing PR #$pr_number..."
gh pr edit "$pr_number" --title "$pr_title" --body "$pr_body_formatted"
else
echo "Creating new PR..."
gh pr create --title "$pr_title" --body "$pr_body_formatted" --label "dependencies"
pr_created=true
fi
# Only request reviews if PR was newly created or changes were committed
# This prevents re-requesting reviews on workflow reruns that don't modify the PR
if [ "$pr_created" = "true" ] || [ "$changes_committed" = "true" ]; then
reviewers="${{ steps.read_reviewers.outputs.REVIEWERS }}"
echo "Requesting reviews from: $reviewers"
gh pr edit --add-reviewer "$reviewers"
else
echo "Skipping reviewer request - no changes were made to the PR"
fi
# Ping pending reviewers if PR already existed (not newly created)
if [ "$pr_created" = "false" ]; then
echo "PR already existed, checking for pending reviewers to ping..."
# Get all requested reviewers (people who haven't submitted a review yet)
pending_reviewers=$(gh pr view "$pr_number" --json reviewRequests --jq '.reviewRequests[].login' 2>/dev/null || echo "")
# Get reviewers who requested changes (not approved)
# State can be: APPROVED, CHANGES_REQUESTED, COMMENTED, DISMISSED, PENDING
changes_requested=$(gh pr view "$pr_number" --json reviews --jq '[.reviews[] | select(.state == "CHANGES_REQUESTED")] | unique_by(.author.login) | .[].author.login' 2>/dev/null || echo "")
# Combine both lists and deduplicate
all_pending=""
for reviewer in $pending_reviewers $changes_requested; do
if [ -n "$reviewer" ] && [[ ! " $all_pending " =~ " $reviewer " ]]; then
all_pending="$all_pending $reviewer"
fi
done
all_pending=$(echo "$all_pending" | xargs) # Trim whitespace
if [ -z "$all_pending" ]; then
echo "No pending reviewers to ping"
else
echo "Pending reviewers: $all_pending"
# Build ping message
ping_message="👋 Friendly reminder: This PR is awaiting your review.\n\n"
for reviewer in $all_pending; do
ping_message="${ping_message}@${reviewer} "
done
ping_message="${ping_message}\n\nPlease take a moment to review when you have time. Thank you!"
# Post comment
echo "Posting ping comment..."
printf "%b" "$ping_message" | gh pr comment "$pr_number" --body-file -
echo "Successfully pinged pending reviewers"
fi
else
echo "PR was newly created, skipping pending reviewer ping"
fi