Skip to content

Instantly share code, notes, and snippets.

@Fleshgrinder
Last active October 6, 2022 14:45
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fleshgrinder/3a1f00b2d10e8ca8612d70a0b5b453a7 to your computer and use it in GitHub Desktop.
Save Fleshgrinder/3a1f00b2d10e8ca8612d70a0b5b453a7 to your computer and use it in GitHub Desktop.
Deletes a GitHub Workflow by deleting all its Runs.
#!/usr/bin/env bash
set -Eeuo pipefail
# There is no button in the interface of GitHub (and as it seems also no API) with which workflows can be deleted.
# They are eventually removed automatically, but in case you want to clean up your Actions UI it is sometimes nice
# to have the possibility to get rid of workflows that are not required anymore. This interactive script allows you
# to achieve exactly that by deleting each individual run of a workflow, after which GitHub deletes the workflow.
declare -ir CONCURRENCY=${CONCURRENCY:-8}
# region https://no-color.org/
if [[ "${NO_COLOR:-}" == '' ]]; then
readonly RESET='\033[0m'
readonly DIM='\033[2m'
readonly RESET_DIM='\033[22m'
readonly BOLD='\033[1m'
readonly RESET_BOLD='\033[22m'
readonly CYAN='\033[36m'
readonly RED='\033[31m'
readonly GREEN='\033[32m'
else
readonly RESET=
readonly DIM=
readonly RESET_DIM=
readonly BOLD=
readonly RESET_BOLD=
readonly CYAN=
readonly RED=
readonly GREEN=
fi
# endregion
dry_run=false
yes=false
while (($# > 0)); do
case "$1" in
-d | --dry-run)
dry_run=true
;;
-h | --help)
cat <<HELP
Delete GitHub Actions Workflows from this repository.
Usage: gh-delete-workflow [option]...
Options:
-d --dry-run Print what would be deleted, but do not delete anything.
-h --help Show this help and exit.
-y --yes I know what I am doing, stop asking me questions!
Environment:
CONCURRENCY=$CONCURRENCY Sets the concurrency level for requests.
HELP
exit 0
;;
-y | --yes)
yes=true
;;
--)
break
;;
-*)
printf -- '%bUnknown option: %s%b\n' "$RED" "$1" "$RESET"
;;
*)
break
;;
esac
shift
done
readonly dry_run yes
if ! command -v gh &>/dev/null; then
printf -- '%bCould not find gh in PATH, make sure it is available: https://cli.github.com/%b\n' "$RED" "$RESET" >&2
exit 1
fi
workflow_names=()
workflow_paths=()
workflow_ids=()
while read -r workflow; do
eval "$workflow"
done < <(gh api '/repos/{owner}/{repo}/actions/workflows' --jq '.workflows |= sort_by(.name) | .workflows[] | "workflow_names+=('"'"'\(.name)'"'"');workflow_paths+=('"'"'\(.path)'"'"');workflow_ids+=(\(.id))"')
case ${#workflow_names[@]} in
0)
echo 'Nothing to delete, this repository does not have any workflows.'
exit 0
;;
1)
readonly workflow_name=${workflow_names[0]}
readonly workflow_path=${workflow_paths[0]}
readonly workflow_id=${workflow_ids[0]}
printf 'There is only one workflow %b%s %b(%s)%b...\n' "$CYAN" "$workflow_name" "$DIM" "$workflow_id" "$RESET"
;;
*)
for ((i = 0; i < ${#workflow_names[@]}; i++)); do
printf -- '%b[%i]%b %s %b%s (%s)%b\n' "$CYAN" $((i + 1)) "$RESET" "${workflow_names[$i]}" "$DIM" "${workflow_paths[$i]}" "${workflow_ids[$i]}" "$RESET_DIM"
done
while :; do
printf -- '%bWhich workflow [1..%i]? ' "$CYAN" ${#workflow_names[@]}
read -r selection
if ((1 <= selection && selection <= ${#workflow_names[@]})); then
printf -- "%b" "$RESET"
((selection--))
readonly selection
break
else
# shellcheck disable=SC2016
printf -- '%bSelection `%s` is invalid, %bMUST%b be one of [1..%i], try again...%b\n' "$RED" "$selection" "$BOLD" "$RESET_BOLD" "${#workflow_names[@]}" "$RESET" >&2
fi
done
readonly workflow_name=${workflow_names[$selection]}
readonly workflow_path=${workflow_paths[$selection]}
readonly workflow_id=${workflow_ids[$selection]}
;;
esac
unset workflow_names workflow_ids
if [[ "$dry_run" == false && "$yes" == false ]]; then
# shellcheck disable=SC2016
printf -- 'Are you sure you want to delete workflow %b%s %b%s (%s)%b? %bYou cannot recover from this!%b [y/n] ' "$CYAN" "$workflow_name" "$DIM" "$workflow_path" "$workflow_id" "$RESET" "$RED" "$RESET"
read -rN1 reply
if [[ "$reply" != y ]]; then
case "$reply" in
n) response='\nOK' ;;
$'\n') response='You did not enter anything' ;;
*) response='\nAssuming you meant n' ;;
esac
printf -- '%b%b, aborting...%b\n' "$RED" "$response" "$RESET"
exit 0
fi
unset reply
echo
fi
i=0
while read -r run_id; do
if [[ "$dry_run" == true ]]; then
printf -- '%b💀 run %s%b\n' "$RED" "$run_id" "$RESET"
else
gh api -X DELETE "/repos/{owner}/{repo}/actions/runs/$run_id" &
fi
if ((++i == CONCURRENCY)); then
i=0
wait
fi
done < <(gh api --paginate "/repos/{owner}/{repo}/actions/workflows/$workflow_id/runs" --jq '.workflow_runs[] | .id')
wait
printf -- '%bDone%b\n' "$GREEN" "$RESET"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment