Skip to content

Commit 22e9bc2

Browse files
authored
Create sub-issues workflow (#3128)
1 parent d11c357 commit 22e9bc2

2 files changed

Lines changed: 181 additions & 0 deletions

File tree

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
name: Create Configlet Sync Issues
2+
3+
on:
4+
workflow_call:
5+
workflow_dispatch:
6+
7+
permissions:
8+
issues: write
9+
contents: read
10+
11+
jobs:
12+
create-sync-issues:
13+
runs-on: ubuntu-24.04
14+
timeout-minutes: 15
15+
16+
env:
17+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18+
PARENT_ISSUE_TITLE: "🚨 configlet sync --test found unsynced tests"
19+
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v4
23+
24+
- name: Fetch configlet
25+
run: ./bin/fetch-configlet
26+
27+
- name: Run configlet sync --tests and capture output
28+
id: sync
29+
shell: bash {0}
30+
run: |
31+
raw_output="$(./bin/configlet sync --tests 2>&1)"
32+
exit_code=$?
33+
printf "exit_code=%d\n" "$exit_code" >> "$GITHUB_OUTPUT"
34+
{
35+
printf "output<<CONFIGLET_EOF\n"
36+
printf "%s\n" "$raw_output"
37+
printf "CONFIGLET_EOF\n"
38+
} >> "$GITHUB_OUTPUT"
39+
printf "configlet exit code: %d\n" "$exit_code"
40+
printf "%s\n" "$raw_output"
41+
42+
- name: Parse exercises with missing tests
43+
id: parse
44+
if: steps.sync.outputs.exit_code != '0'
45+
shell: bash
46+
run: |
47+
output='${{ steps.sync.outputs.output }}'
48+
49+
# Extract exercise slugs from lines like: [warn] dot-dsl: missing 19 test cases
50+
mapfile -t exercises < <(printf "%s\n" "$output" | grep -oP '(?<=\[warn\] )[a-z][a-z0-9-]+(?=: missing \d+ test case)')
51+
52+
if [[ ${#exercises[@]} -eq 0 ]]; then
53+
printf "No exercises with missing tests found in output.\n"
54+
printf "exercises_json=[]\n" >> "$GITHUB_OUTPUT"
55+
exit 0
56+
fi
57+
58+
printf "Found %d exercise(s) with missing tests:\n" "${#exercises[@]}"
59+
printf " - %s\n" "${exercises[@]}"
60+
61+
# Build JSON array of slugs
62+
json="["
63+
for i in "${!exercises[@]}"; do
64+
[[ $i -gt 0 ]] && json+=","
65+
json+="\"${exercises[$i]}\""
66+
done
67+
json+="]"
68+
printf "exercises_json=%s\n" "$json" >> "$GITHUB_OUTPUT"
69+
70+
# Build per-exercise details: slug <TAB> test_name <TAB> uuid (one row per test)
71+
{
72+
printf "details<<DETAILS_EOF\n"
73+
current_slug=""
74+
while IFS= read -r line; do
75+
if [[ "$line" =~ ^\[warn\]\ ([a-z][a-z0-9-]+):\ missing ]]; then
76+
current_slug="${BASH_REMATCH[1]}"
77+
elif [[ -n "$current_slug" && "$line" =~ ^[[:space:]]+-\ (.+)\ \(([a-f0-9-]+)\)$ ]]; then
78+
printf "%s\t%s\t%s\n" "$current_slug" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
79+
elif [[ "$line" =~ ^\[warn\] && ! "$line" =~ missing ]]; then
80+
current_slug=""
81+
fi
82+
done <<< "$output"
83+
printf "DETAILS_EOF\n"
84+
} >> "$GITHUB_OUTPUT"
85+
86+
- name: Find parent tracking issue
87+
id: find_parent
88+
if: steps.sync.outputs.exit_code != '0' && steps.parse.outputs.exercises_json != '[]'
89+
shell: bash
90+
run: |
91+
issue_data=$(gh issue list \
92+
--repo "${{ github.repository }}" \
93+
--search "is:issue is:open in:title \"${PARENT_ISSUE_TITLE}\"" \
94+
--json number \
95+
--jq '.[0].number // empty')
96+
97+
if [[ -z "$issue_data" ]]; then
98+
printf "::warning::Parent issue not found. Run 'Run Configlet Sync' first.\n"
99+
printf "parent_number=\n" >> "$GITHUB_OUTPUT"
100+
else
101+
printf "Found parent issue #%s\n" "$issue_data"
102+
printf "parent_number=%s\n" "$issue_data" >> "$GITHUB_OUTPUT"
103+
fi
104+
105+
- name: Create or update child issues per exercise
106+
if: steps.sync.outputs.exit_code != '0' && steps.parse.outputs.exercises_json != '[]'
107+
shell: bash
108+
env:
109+
EXERCISES_JSON: ${{ steps.parse.outputs.exercises_json }}
110+
DETAILS: ${{ steps.parse.outputs.details }}
111+
PARENT_NUMBER: ${{ steps.find_parent.outputs.parent_number }}
112+
run: |
113+
repo="${{ github.repository }}"
114+
115+
mapfile -t exercises < <(printf "%s\n" "$EXERCISES_JSON" | jq -r '.[]')
116+
117+
for slug in "${exercises[@]}"; do
118+
child_title="[configlet] ${slug}: missing test cases"
119+
120+
# Collect missing tests for this exercise
121+
missing_tests=""
122+
while IFS=$'\t' read -r es_slug test_name uuid; do
123+
[[ "$es_slug" == "$slug" ]] || continue
124+
missing_tests+="- \`${uuid}\` ${test_name}"$'\n'
125+
done <<< "$DETAILS"
126+
127+
parent_ref=""
128+
[[ -n "$PARENT_NUMBER" ]] && parent_ref="Part of #${PARENT_NUMBER}."
129+
130+
# Write body to a temp file to avoid quoting / indentation issues
131+
body_file=$(mktemp)
132+
cat > "$body_file" << ISSUE_BODY_EOF
133+
## Missing test cases for \`${slug}\`
134+
135+
${parent_ref}
136+
137+
The following test cases from [problem-specifications](https://github.com/exercism/problem-specifications/tree/main/exercises/${slug}) are not yet implemented in this track:
138+
139+
${missing_tests}
140+
### How to help
141+
142+
For detailed instructions on how to fetch configlet and update the tests, please see the **"How to do this task"** section in the main tracking issue:
143+
👉 **[Read the instructions here](${{ github.server_url }}/${{ github.repository }}/issues/${PARENT_NUMBER:-"none"})**
144+
145+
_This issue is managed automatically by the [Create Configlet Sync Issues](${{ github.server_url }}/${{ github.repository }}/actions/workflows/create-configlet-sync-issues.yml) workflow._
146+
ISSUE_BODY_EOF
147+
148+
# Check for an existing open child issue
149+
existing=$(gh issue list \
150+
--repo "$repo" \
151+
--search "is:issue is:open in:title \"${child_title}\"" \
152+
--json number \
153+
--jq '.[0].number // empty')
154+
155+
if [[ -z "$existing" ]]; then
156+
printf "Creating child issue for: %s\n" "$slug"
157+
issue_url=$(gh issue create \
158+
--repo "$repo" \
159+
--title "$child_title" \
160+
--body-file "$body_file" \
161+
--label "x:knowledge/elementary,x:module/practice-exercise")
162+
new_number=$(basename "$issue_url")
163+
printf "Created #%s for %s\n" "$new_number" "$slug"
164+
else
165+
printf "Updating existing child issue #%s for: %s\n" "$existing" "$slug"
166+
gh issue edit "$existing" \
167+
--repo "$repo" \
168+
--body-file "$body_file"
169+
fi
170+
171+
rm -f "$body_file"
172+
done
173+
174+
- name: All tests synced — nothing to do
175+
if: steps.sync.outputs.exit_code == '0'
176+
run: printf "✅ All exercises are fully synced. No child issues needed.\n"

.github/workflows/run-configlet-sync.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@ on:
88
jobs:
99
call-gha-workflow:
1010
uses: exercism/github-actions/.github/workflows/configlet-sync.yml@main
11+
12+
create-sync-issues:
13+
needs: call-gha-workflow
14+
uses: ./.github/workflows/create-configlet-sync-issues.yml
15+
secrets: inherit

0 commit comments

Comments
 (0)