Skip to content

Commit 21b957c

Browse files
Debugging
1 parent 123a435 commit 21b957c

1 file changed

Lines changed: 99 additions & 87 deletions

File tree

.github/workflows/cleanup-workflows.yml

Lines changed: 99 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -55,99 +55,76 @@ jobs:
5555
echo "EOF"
5656
} >> "$GITHUB_OUTPUT"
5757
58-
- name: Workflows on github
59-
id: github
58+
- name: Filter for deleted workflows
59+
id: deleted
6060
env:
6161
GH_TOKEN: ${{ github.token }}
6262
run: |
63-
# Note that we filter by `.github` path prefix to ensure we only get locally defined workflows.
64-
#
65-
# Examples of non-local workflows are `dependabot` and `copilot` which have paths:
66-
# - dynamic/dependabot/dependabot-updates
67-
# - dynamic/copilot-pull-request-reviewer/copilot-pull-request-reviewer
68-
WORKFLOWS=$(gh workflow list \
69-
--all \
70-
--json path \
71-
--jq '.[] | select(.path | startswith(".github")) | .path' \
72-
)
73-
printf "%s\n" $WORKFLOWS
74-
{
75-
echo "workflows<<EOF"
76-
echo "$WORKFLOWS"
77-
echo "EOF"
78-
} >> "$GITHUB_OUTPUT"
63+
set -euo pipefail
7964
80-
- name: Filter for deleted workflows
81-
id: deleted
82-
run: |
83-
# Union of `main` and `next` workflows.
84-
EXISTING_FILES=$( \
85-
printf "%s\n%s\n" \
65+
# Union of `main` and `next` workflows as a JSON array of strings (paths)
66+
EXISTING=$(printf "%s\n%s\n" \
8667
"${{ steps.main.outputs.workflows }}" \
8768
"${{ steps.next.outputs.workflows }}" \
8869
)
89-
EXISTING_FILES=$(echo "$EXISTING_FILES" | sort -u)
90-
printf "%s\n" $EXISTING_FILES
91-
92-
# Find deleted workflows as the items in `WORKFLOWS` but not in the union of main and next.
93-
# This assumes that _all_ items in main and next are present in `WORKFLOWS`.
94-
DELETED_FILES=$( \
95-
printf "%s\n%s\n" \
96-
"$EXISTING_FILES" \
97-
"${{ steps.github.outputs.workflows }}" \
70+
EXISTING=$(echo "$EXISTING" | sort -u | jq -R . | jq -s .)
71+
72+
echo "Existing workflows:"
73+
echo "$EXISTING"
74+
75+
# Get workflows currently on GitHub as JSON array of objects
76+
GITHUB=$(gh workflow list --all --json path,name,id)
77+
GITHUB=$(echo "$GITHUB" | jq -c '[.[] | select(.path | startswith(".github/"))]')
78+
79+
echo "Workflows on GitHub:"
80+
echo "$GITHUB"
81+
82+
# Find deleted workflows: present on GitHub but not in main/next
83+
DELETED=$(echo "$GITHUB" | jq -c \
84+
--argjson existing "$EXISTING" '
85+
map(select(.path as $p | $existing | index($p) | not))
86+
'
9887
)
99-
DELETED_FILES=$(echo "$DELETED_FILES" | sort | uniq -u)
100-
printf "%s\n" $DELETED_FILES
88+
89+
echo "Deleted workflows:"
90+
echo "$DELETED"
91+
92+
# Output to GitHub Actions
10193
{
10294
echo "workflows<<EOF"
103-
echo "$DELETED_FILES"
95+
echo "$DELETED"
10496
echo "EOF"
10597
} >> "$GITHUB_OUTPUT"
10698
10799
- name: Delete runs from deleted workflows
108100
env:
109101
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
110102
MODE: ${{ inputs.mode }}
111-
DELETED_WORKFLOWS: ${{ steps.deleted.outputs.workflows }}
103+
WORKFLOWS: ${{ steps.deleted.outputs.workflows }}
112104
OWNER: ${{ github.repository_owner }}
113105
REPO: ${{ github.repository }}
114106
run: |
115107
set -euo pipefail
116108
109+
if [ -z "$WORKFLOWS" ]; then
110+
echo "No workflows to delete."
111+
exit 0
112+
fi
113+
117114
TOTAL_AFFECTED=0
118115
SUMMARY=()
119-
120-
# Read workflows into an array for indexing
121-
mapfile -t WORKFLOWS_ARRAY <<< "$DELETED_WORKFLOWS"
122-
TOTAL_WORKFLOWS=${#WORKFLOWS_ARRAY[@]}
123-
124-
# Convert workflow paths to workflow names
125-
WORKFLOW_NAMES_ARRAY=()
126-
for wf_path in "${WORKFLOWS_ARRAY[@]}"; do
127-
# Extract 'name' from YAML, fallback to filename
128-
name=$(yq -r '.name // ""' "$wf_path" 2>/dev/null || true)
129-
if [ -z "$name" ]; then
130-
name=$(basename "$wf_path")
131-
fi
132-
WORKFLOW_NAMES_ARRAY+=("$name")
133-
done
134-
135-
# Determine max workflow name length for alignment
136-
MAX_WF_LENGTH=0
137-
for wf in "${WORKFLOW_NAMES_ARRAY[@]}"; do
138-
len=${#wf}
139-
(( len > MAX_WF_LENGTH )) && MAX_WF_LENGTH=$len
140-
done
141-
(( MAX_WF_LENGTH < 30 )) && MAX_WF_LENGTH=30 # minimum width
116+
CURRENT_INDEX=0
142117
143118
# Column widths
144119
INDEX_WIDTH=9
120+
MAX_WF_LENGTH=30
145121
WORKFLOW_COUNT_WIDTH=14
146122
GLOBAL_TOTAL_WIDTH=12
147-
148-
# Total table width
149123
TOTAL_WIDTH=$(( INDEX_WIDTH + 3 + MAX_WF_LENGTH + 3 + WORKFLOW_COUNT_WIDTH + 3 + GLOBAL_TOTAL_WIDTH ))
150124
125+
# Count total workflows for progress display
126+
TOTAL_WORKFLOWS=$(echo "$WORKFLOWS" | jq -r '. | length')
127+
151128
# Function to print dynamic table headers padded with '=' and spaces
152129
print_header() {
153130
local name="$1"
@@ -173,48 +150,51 @@ jobs:
173150
"$WORKFLOW_COUNT_WIDTH" "$(printf '%.0s-' $(seq 1 $WORKFLOW_COUNT_WIDTH))" \
174151
"$GLOBAL_TOTAL_WIDTH" "$(printf '%.0s-' $(seq 1 $GLOBAL_TOTAL_WIDTH))"
175152
176-
# Process each workflow by name
177-
for i in "${!WORKFLOW_NAMES_ARRAY[@]}"; do
153+
# Loop over deleted workflows JSON
154+
echo "$WORKFLOWS" | jq -c '.[]' | while read -r wf; do
155+
CURRENT_INDEX=$((CURRENT_INDEX + 1))
156+
WORKFLOW_NAME=$(echo "$wf" | jq -r '.name')
157+
WORKFLOW_ID=$(echo "$wf" | jq -r '.databaseId')
158+
WORKFLOW_PATH=$(echo "$wf" | jq -r '.path')
178159
179-
WORKFLOW_NAME=${WORKFLOW_NAMES_ARRAY[$i]}
180-
[ -z "$WORKFLOW_NAME" ] && continue
181-
CURRENT_INDEX=$((i + 1))
182-
WORKFLOW_COUNT=0
183-
184-
WORKFLOW_ID=$(gh workflow list --all --json id,name \
185-
--jq ".[] | select(.name==\"$WORKFLOW_NAME\") | .id")
160+
# Safety checks
161+
if [ -z "$WORKFLOW_NAME" ]; then
162+
echo "::error title=Workflow name empty::Resolved workflow name is empty at index $CURRENT_INDEX"
163+
exit 1
164+
fi
165+
if [ -z "$WORKFLOW_ID" ]; then
166+
echo "::error title=Workflow ID missing::Workflow '$WORKFLOW_NAME' (path: $WORKFLOW_PATH) has no ID"
167+
exit 1
168+
fi
186169
187-
# GraphQL pagination variables
188-
AFTER_CURSOR=null
170+
WORKFLOW_COUNT=0
171+
AFTER_CURSOR=""
189172
173+
# Paginate over workflow runs
190174
while true; do
191175
RESPONSE=$(gh api graphql -F workflowId="$WORKFLOW_ID" -F after="$AFTER_CURSOR" \
192176
-f query='query($workflowId: ID!, $after: String) {
193177
node(id: $workflowId) {
194178
... on Workflow {
195179
runs(first: 100, after: $after) {
196-
pageInfo {
197-
hasNextPage
198-
endCursor
199-
}
200-
nodes {
201-
databaseId
202-
}
180+
pageInfo { hasNextPage endCursor }
181+
nodes { databaseId }
203182
}
204183
}
205184
}
206185
}')
207-
RUN_IDS=$(echo "$RESPONSE" | jq -r '.data.repository.workflowRuns.nodes[].databaseId')
208-
HAS_NEXT=$(echo "$RESPONSE" | jq -r '.data.repository.workflowRuns.pageInfo.hasNextPage')
209-
AFTER_CURSOR=$(echo "$RESPONSE" | jq -r '.data.repository.workflowRuns.pageInfo.endCursor')
186+
187+
RUN_IDS=$(echo "$RESPONSE" | jq -r '.data.node.runs.nodes[].databaseId')
188+
HAS_NEXT=$(echo "$RESPONSE" | jq -r '.data.node.runs.pageInfo.hasNextPage')
189+
AFTER_CURSOR=$(echo "$RESPONSE" | jq -r '.data.node.runs.pageInfo.endCursor')
210190
211191
[ -z "$RUN_IDS" ] && break
212192
213193
BATCH_COUNT=$(echo "$RUN_IDS" | wc -l | tr -d ' ')
214194
WORKFLOW_COUNT=$((WORKFLOW_COUNT + BATCH_COUNT))
215195
TOTAL_AFFECTED=$((TOTAL_AFFECTED + BATCH_COUNT))
216196
217-
# Print progress line
197+
# Print progress
218198
printf "%*s | %-*s | %*s | %*s\n" \
219199
"$INDEX_WIDTH" "[$CURRENT_INDEX/$TOTAL_WORKFLOWS]" \
220200
"$MAX_WF_LENGTH" "$WORKFLOW_NAME" \
@@ -223,13 +203,13 @@ jobs:
223203
224204
if [ "$MODE" = "execute" ]; then
225205
for RUN_ID in $RUN_IDS; do
226-
echo | gh run delete "$RUN_ID" >/dev/null
206+
gh run delete "$RUN_ID" >/dev/null
227207
done
228208
fi
229209
230210
[ "$HAS_NEXT" != "true" ] && break
231211
232-
# TEMPORARY break for testing large workflows
212+
# TEMPORARY safety break
233213
if [ "$WORKFLOW_COUNT" -gt 200 ]; then
234214
echo " ⚠️ Temporary break: workflow count exceeded 200, stopping early."
235215
break
@@ -249,4 +229,36 @@ jobs:
249229
printf "%*s-+-%-*s-+-%-*s-+-%-*s\n" \
250230
"$INDEX_WIDTH" "$(printf '%.0s-' $(seq 1 $INDEX_WIDTH))" \
251231
"$MAX_WF_LENGTH" "$(printf '%.0s-' $(seq 1 $MAX_WF_LENGTH))" \
252-
"$WORKFLOW_COUNT_WIDT
232+
"$WORKFLOW_COUNT_WIDTH" "$(printf '%.0s-' $(seq 1 $WORKFLOW_COUNT_WIDTH))" \
233+
"$GLOBAL_TOTAL_WIDTH" "$(printf '%.0s-' $(seq 1 $GLOBAL_TOTAL_WIDTH))"
234+
235+
# Print summary rows
236+
for entry in "${SUMMARY[@]}"; do
237+
wf="${entry%%|*}"
238+
count="${entry##*|}"
239+
printf "%*s | %-*s | %*s | %-*s\n" \
240+
"$INDEX_WIDTH" "" \
241+
"$MAX_WF_LENGTH" "$wf" \
242+
"$WORKFLOW_COUNT_WIDTH" "$count" \
243+
"$GLOBAL_TOTAL_WIDTH" ""
244+
done
245+
246+
# Footer separator
247+
printf "%*s-+-%-*s-+-%-*s-+-%-*s\n" \
248+
"$INDEX_WIDTH" "$(printf '%.0s-' $(seq 1 $INDEX_WIDTH))" \
249+
"$MAX_WF_LENGTH" "$(printf '%.0s-' $(seq 1 $MAX_WF_LENGTH))" \
250+
"$WORKFLOW_COUNT_WIDTH" "$(printf '%.0s-' $(seq 1 $WORKFLOW_COUNT_WIDTH))" \
251+
"$GLOBAL_TOTAL_WIDTH" "$(printf '%.0s-' $(seq 1 $GLOBAL_TOTAL_WIDTH))"
252+
253+
# TOTAL row
254+
printf "%*s | %-*s | %*s | %-*s\n" \
255+
"$INDEX_WIDTH" "" \
256+
"$MAX_WF_LENGTH" "TOTAL" \
257+
"$WORKFLOW_COUNT_WIDTH" "$TOTAL_AFFECTED" \
258+
"$GLOBAL_TOTAL_WIDTH" ""
259+
260+
if [ "$MODE" = "dry run" ]; then
261+
echo "Dry run complete. No runs were deleted."
262+
else
263+
echo "Cleanup complete."
264+
fi

0 commit comments

Comments
 (0)