Skip to content

Instantly share code, notes, and snippets.

@asheliahut
Last active May 21, 2025 18:35
Show Gist options
  • Save asheliahut/bad00e79d6317d72fd2184268819822c to your computer and use it in GitHub Desktop.
Save asheliahut/bad00e79d6317d72fd2184268819822c to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -euo pipefail
#---------------- CONFIGURATION ----------------#
GITHUB_ORG="your-org-here" # Replace with your GitHub organization
MAX_PRS=0 # Maximum number of PRs to create (0 for unlimited)
SEARCH_LIMIT=1000 # Limit for search results
BRANCH_NAME="fix/update-workflow-always-conditions"
PR_TITLE="Replace always() with !cancelled() in workflows"
PR_BODY="This PR replaces all uses of always() in GitHub workflows with \${{ !cancelled() }}."
COMMIT_MESSAGE="chore: replace always() with !cancelled() in workflows"
# Patterns to locate repos
SEARCH_PATTERNS=(
'always()'
'${{ always() }}'
'if: always'
)
# Branch priority order
BRANCH_PRIORITIES=("main" "develop" "master")
WORKFLOW_DIR=".github/workflows"
#---------------- PREP ----------------#
if ! gh auth status &>/dev/null; then
echo "🚨 Please run 'gh auth login' first." >&2
exit 1
fi
TMP=$(mktemp -d)
cd "$TMP"
OWNER_FLAG="--owner=$GITHUB_ORG"
#---------------- SEARCH ----------------#
search_repos() {
local term=$1
>&2 echo "🔍 Searching for '$term' in workflows…"
gh search code "$term" \
$OWNER_FLAG \
--language=yml \
--limit $SEARCH_LIMIT \
--json repository \
--jq '.[].repository.nameWithOwner' \
| sort -u
}
> repos.txt
for pat in "${SEARCH_PATTERNS[@]}"; do
search_repos "$pat" >> repos.txt
done
mapfile -t ALL_REPOS < <(sort -u repos.txt)
if [ ${#ALL_REPOS[@]} -eq 0 ]; then
echo "✅ No matching workflows found." >&2
exit 0
fi
if (( MAX_PRS>0 && ${#ALL_REPOS[@]}>MAX_PRS )); then
ALL_REPOS=( "${ALL_REPOS[@]:0:MAX_PRS}" )
fi
echo "🚀 Will process ${#ALL_REPOS[@]} repositories:"
printf " • %s\n" "${ALL_REPOS[@]}"
#---------------- CHECK EXISTING PRS ----------------#
check_existing_pr() {
local repo=$1
local branch=$2
local count
count=$(gh pr list --repo "$repo" --state all --head "$branch" --json number --jq 'length')
[ "$count" -gt 0 ]
}
#---------------- PROCESS ----------------#
PR_COUNT=0
SKIPPED_COUNT=0
for repo in "${ALL_REPOS[@]}"; do
echo
echo "=== $repo ==="
if check_existing_pr "$repo" "$BRANCH_NAME"; then
echo " ⏭️ Skipping: PR already exists."
SKIPPED_COUNT=$((SKIPPED_COUNT+1))
continue
fi
if ! gh repo clone "$repo"; then
echo "❌ Clone failed—skipping." >&2
continue
fi
cd "${repo##*/}"
# Get the default branch for the repository
default_branch=$(git symbolic-ref --short HEAD)
echo " 📌 Default branch: $default_branch"
# First try to use the default branch
if git show-ref --quiet "refs/remotes/origin/$default_branch"; then
target="$default_branch"
else
# If default branch doesn't exist remotely (unusual), check priority branches
target=""
for branch in "${BRANCH_PRIORITIES[@]}"; do
if git show-ref --quiet "refs/remotes/origin/$branch"; then
target="$branch"
echo " ⚠️ Using '$target' instead of default '$default_branch'." >&2
break
fi
done
# If still no target, fall back to the local HEAD (which should be default_branch)
if [ -z "$target" ]; then
target="$default_branch"
echo " ⚠️ Using local default branch '$target' as no priority branches exist remotely." >&2
fi
fi
git checkout -b "$BRANCH_NAME" "origin/$target"
CHANGED=false
WORKFLOW_FILES=()
while IFS= read -r file; do
WORKFLOW_FILES+=("$file")
done < <(find "$WORKFLOW_DIR" -type f \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null)
for file in "${WORKFLOW_FILES[@]}"; do
if grep -q 'always()' "$file"; then
echo " ✏ Patching $file"
cp "$file" "${file}.before"
# 1) Wrap bare `if: always()` (preserve indent & trailing spaces)
sed -i -E \
's|^([[:space:]]*if:[[:space:]]*)always\(\)([[:space:]]*)$|\1\${{ !cancelled() }}\2|' \
"$file"
# 2) Replace standalone `${{ always() }}` → `${{ !cancelled() }}`
sed -i -E \
's|\$\{\{\s*always\(\)\s*\}\}|\${{ !cancelled() }}|g' \
"$file"
# 3) Swap any remaining `always()` inside expressions → `!cancelled()`
sed -i -E \
's|always\(\)|!cancelled()|g' \
"$file"
if ! cmp -s "${file}.before" "$file"; then
echo " - Changes applied"
git add "$file"
CHANGED=true
else
echo " - No net changes"
fi
rm "${file}.before"
fi
done
if [ "$CHANGED" = true ]; then
git commit -m "$COMMIT_MESSAGE"
git push -u origin "$BRANCH_NAME"
gh pr create --title "$PR_TITLE" --body "$PR_BODY" --base "$target"
echo " 🎉 Created PR"
PR_COUNT=$((PR_COUNT+1))
else
echo " ✅ No replacements needed"
fi
cd "$TMP"
done
echo
echo "🎉 Done—created $PR_COUNT PR(s), skipped $SKIPPED_COUNT repo(s)."
rm -rf "$TMP"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment