Skip to content

Commit 72b25f4

Browse files
authored
Merge pull request #4 from borghei/fix/workflow-parse-errors
fix(ci): resolve parse errors in smart-sync and pr-issue-auto-close workflows
2 parents 710378a + 8f6ed0f commit 72b25f4

File tree

2 files changed

+100
-97
lines changed

2 files changed

+100
-97
lines changed

.github/workflows/pr-issue-auto-close.yml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,10 @@ jobs:
2525
if [ -f ".github/WORKFLOW_KILLSWITCH" ]; then
2626
STATUS=$(grep "STATUS:" .github/WORKFLOW_KILLSWITCH | awk '{print $2}')
2727
if [ "$STATUS" = "DISABLED" ]; then
28-
echo "🛑 Workflows disabled by kill switch"
28+
echo "Workflows disabled by kill switch"
2929
exit 0
3030
fi
3131
fi
32-
- name: Checkout repository
33-
uses: actions/checkout@v4
3432
3533
- name: Extract linked issues from PR body
3634
id: extract_issues
@@ -123,15 +121,17 @@ jobs:
123121
owner: context.repo.owner,
124122
repo: context.repo.repo,
125123
issue_number: parseInt(issueNumber),
126-
body: `Completed via PR #${prNumber}
127-
128-
PR: ${prTitle}
129-
URL: ${prUrl}
130-
Merged by: ${merger}
131-
132-
This issue has been resolved and the changes have been merged into main.
133-
134-
Automatically closed via PR merge automation`
124+
body: [
125+
`Completed via PR #${prNumber}`,
126+
'',
127+
`PR: ${prTitle}`,
128+
`URL: ${prUrl}`,
129+
`Merged by: ${merger}`,
130+
'',
131+
'This issue has been resolved and the changes have been merged into main.',
132+
'',
133+
'Automatically closed via PR merge automation'
134+
].join('\n')
135135
});
136136
137137
// Close the issue

.github/workflows/smart-sync.yml

Lines changed: 88 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,24 @@ name: Smart Bidirectional Sync
44
'on':
55
issues:
66
types: [labeled, closed, reopened]
7-
projects_v2_item:
8-
types: [edited]
7+
workflow_dispatch:
8+
inputs:
9+
direction:
10+
description: 'Sync direction (project-to-issue requires PROJECTS_TOKEN secret)'
11+
required: true
12+
type: choice
13+
options:
14+
- project-to-issue
15+
- issue-to-project
16+
issue_number:
17+
description: 'Issue number to sync'
18+
required: true
19+
type: string
920

1021
# Prevent sync loops with debouncing
1122
concurrency:
12-
group: smart-sync-${{ github.event.issue.number || github.event.projects_v2_item.node_id }}
13-
cancel-in-progress: true # Cancel pending runs (debouncing effect)
23+
group: smart-sync-${{ github.event.issue.number || github.event.inputs.issue_number || github.run_id }}
24+
cancel-in-progress: true
1425

1526
jobs:
1627
determine-direction:
@@ -19,7 +30,6 @@ jobs:
1930
permissions:
2031
contents: read
2132
issues: read
22-
id-token: write
2333

2434
outputs:
2535
should_sync: ${{ steps.check.outputs.should_sync }}
@@ -45,29 +55,32 @@ jobs:
4555
# Check which event triggered this workflow
4656
if [ "${{ github.event_name }}" = "issues" ]; then
4757
# Issue event → sync to project board
48-
echo "direction=issue-to-project" >> $GITHUB_OUTPUT
49-
echo "issue_number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
58+
echo "direction=issue-to-project" >> "$GITHUB_OUTPUT"
59+
echo "issue_number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT"
5060
5161
# Only sync on status label changes or state changes
52-
if [[ "${{ github.event.action }}" == "labeled" && "${{ github.event.label.name }}" == status:* ]] || \
53-
[ "${{ github.event.action }}" = "closed" ] || \
54-
[ "${{ github.event.action }}" = "reopened" ]; then
55-
echo "should_sync=true" >> $GITHUB_OUTPUT
56-
echo "✅ Will sync: Issue #${{ github.event.issue.number }} → Project Board"
62+
ACTION="${{ github.event.action }}"
63+
LABEL="${{ github.event.label.name }}"
64+
if { [ "$ACTION" = "labeled" ] && echo "$LABEL" | grep -q "^status:"; } || \
65+
[ "$ACTION" = "closed" ] || \
66+
[ "$ACTION" = "reopened" ]; then
67+
echo "should_sync=true" >> "$GITHUB_OUTPUT"
68+
echo "Will sync: Issue #${{ github.event.issue.number }} to Project Board"
5769
else
58-
echo "should_sync=false" >> $GITHUB_OUTPUT
59-
echo "⏭️ Skipping: Not a status change or state change"
70+
echo "should_sync=false" >> "$GITHUB_OUTPUT"
71+
echo "Skipping: Not a status change or state change"
6072
fi
6173
62-
elif [ "${{ github.event_name }}" = "projects_v2_item" ]; then
63-
# Project event → sync to issue
64-
echo "direction=project-to-issue" >> $GITHUB_OUTPUT
65-
echo "should_sync=true" >> $GITHUB_OUTPUT
66-
echo "✅ Will sync: Project Board → Issue"
74+
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
75+
# Manual trigger
76+
echo "direction=${{ github.event.inputs.direction }}" >> "$GITHUB_OUTPUT"
77+
echo "issue_number=${{ github.event.inputs.issue_number }}" >> "$GITHUB_OUTPUT"
78+
echo "should_sync=true" >> "$GITHUB_OUTPUT"
79+
echo "Will sync: Manual trigger (${{ github.event.inputs.direction }})"
6780
6881
else
69-
echo "should_sync=false" >> $GITHUB_OUTPUT
70-
echo "⚠️ Unknown event type"
82+
echo "should_sync=false" >> "$GITHUB_OUTPUT"
83+
echo "Unknown event type"
7184
fi
7285
7386
rate-limit-check:
@@ -136,7 +149,6 @@ jobs:
136149
permissions:
137150
contents: read
138151
issues: read
139-
id-token: write
140152

141153
steps:
142154
- name: Checkout repository
@@ -147,11 +159,12 @@ jobs:
147159
- name: Sync Issue to Project Board
148160
env:
149161
GH_TOKEN: ${{ secrets.PROJECTS_TOKEN }}
162+
ISSUE_TITLE: ${{ github.event.issue.title }}
150163
run: |
151-
echo "# Issue Project Board Sync"
152-
echo "**Issue**: #${{ github.event.issue.number }} \"${{ github.event.issue.title }}\""
153-
echo "**State**: ${{ github.event.issue.state }}"
154-
echo "**Action**: ${{ github.event.action }}"
164+
echo "# Issue to Project Board Sync"
165+
echo "Issue: #${{ github.event.issue.number }} $ISSUE_TITLE"
166+
echo "State: ${{ github.event.issue.state }}"
167+
echo "Action: ${{ github.event.action }}"
155168
156169
# Step 1: Check if in Project
157170
PROJECT_ITEM=$(gh api graphql -f query='
@@ -277,7 +290,6 @@ jobs:
277290
permissions:
278291
contents: read
279292
issues: write
280-
id-token: write
281293

282294
steps:
283295
- name: Checkout repository
@@ -288,53 +300,35 @@ jobs:
288300
- name: Sync Project Board to Issue
289301
env:
290302
GH_TOKEN: ${{ secrets.PROJECTS_TOKEN }}
303+
ISSUE_NUMBER: ${{ needs.determine-direction.outputs.issue_number }}
291304
run: |
292-
echo "# Project Board → Issue Sync"
293-
echo "**Project Item**: ${{ github.event.projects_v2_item.node_id }}"
294-
echo "**Content**: ${{ github.event.projects_v2_item.content_node_id }}"
295-
echo "**Changed By**: @${{ github.event.sender.login }}"
296-
297-
# Step 1: Get Issue Number
298-
CONTENT_ID="${{ github.event.projects_v2_item.content_node_id }}"
299-
300-
ISSUE_DATA=$(gh api graphql -f query='
301-
query {
302-
node(id: "${{ github.event.projects_v2_item.node_id }}") {
303-
... on ProjectV2Item {
304-
content {
305-
... on Issue {
306-
number
307-
url
308-
state
309-
title
310-
}
311-
}
312-
}
313-
}
314-
}
315-
')
316-
317-
ISSUE_NUMBER=$(echo "$ISSUE_DATA" | jq -r '.data.node.content.number')
305+
echo "# Project Board to Issue Sync"
306+
echo "Issue: #$ISSUE_NUMBER"
307+
echo "Triggered by: @${{ github.event.sender.login }}"
318308
319309
if [ -z "$ISSUE_NUMBER" ] || [ "$ISSUE_NUMBER" = "null" ]; then
320-
echo "⏭️ Not an issue (might be PR or other content)"
321-
exit 0
310+
echo "No issue number provided"
311+
exit 1
322312
fi
323313
324-
echo "Issue Number: $ISSUE_NUMBER"
325-
326-
# Step 2: Get Project Status
327-
STATUS=$(gh api graphql -f query='
314+
# Get project item for this issue
315+
PROJECT_ITEM=$(gh api graphql -f query='
328316
query {
329-
node(id: "${{ github.event.projects_v2_item.node_id }}") {
330-
... on ProjectV2Item {
331-
fieldValues(first: 20) {
317+
repository(owner: "borghei", name: "Claude-Skills") {
318+
issue(number: '"$ISSUE_NUMBER"') {
319+
projectItems(first: 10) {
332320
nodes {
333-
... on ProjectV2ItemFieldSingleSelectValue {
334-
name
335-
field {
336-
... on ProjectV2SingleSelectField {
321+
id
322+
project { number }
323+
fieldValues(first: 20) {
324+
nodes {
325+
... on ProjectV2ItemFieldSingleSelectValue {
337326
name
327+
field {
328+
... on ProjectV2SingleSelectField {
329+
name
330+
}
331+
}
338332
}
339333
}
340334
}
@@ -343,16 +337,25 @@ jobs:
343337
}
344338
}
345339
}
346-
' --jq '.data.node.fieldValues.nodes[] | select(.field.name == "Status") | .name')
340+
')
341+
342+
# Extract status from project 9
343+
STATUS=$(echo "$PROJECT_ITEM" | jq -r '
344+
.data.repository.issue.projectItems.nodes[]
345+
| select(.project.number == 9)
346+
| .fieldValues.nodes[]
347+
| select(.field.name == "Status")
348+
| .name
349+
')
347350
348351
if [ -z "$STATUS" ]; then
349-
echo "⏭️ No status field found"
352+
echo "No status field found in project board"
350353
exit 0
351354
fi
352355
353356
echo "Project Status: $STATUS"
354357
355-
# Step 3: Map Status to Label
358+
# Map status to label
356359
case "$STATUS" in
357360
"To triage") NEW_LABEL="status: triage" ;;
358361
"Backlog") NEW_LABEL="status: backlog" ;;
@@ -361,36 +364,36 @@ jobs:
361364
"In Review") NEW_LABEL="status: in-review" ;;
362365
"Done") NEW_LABEL="status: done" ;;
363366
*)
364-
echo "⏭️ Unknown status: $STATUS"
367+
echo "Unknown status: $STATUS"
365368
exit 0
366369
;;
367370
esac
368371
369372
echo "Target Label: $NEW_LABEL"
370373
371-
# Step 4: Update Issue Labels
372-
CURRENT_LABELS=$(gh issue view $ISSUE_NUMBER --json labels --jq '[.labels[].name] | join(",")')
374+
# Update issue labels
375+
CURRENT_LABELS=$(gh issue view "$ISSUE_NUMBER" --json labels \
376+
--jq '[.labels[].name] | join(",")')
373377
374-
# Remove all status: labels
375-
for label in "status: triage" "status: backlog" "status: ready" "status: in-progress" "status: in-review" "status: done"; do
378+
for label in "status: triage" "status: backlog" "status: ready" \
379+
"status: in-progress" "status: in-review" "status: done"; do
376380
if echo "$CURRENT_LABELS" | grep -q "$label"; then
377-
gh issue edit $ISSUE_NUMBER --remove-label "$label" 2>/dev/null || true
381+
gh issue edit "$ISSUE_NUMBER" --remove-label "$label" 2>/dev/null || true
378382
fi
379383
done
380384
381-
# Add new status label
382-
gh issue edit $ISSUE_NUMBER --add-label "$NEW_LABEL"
383-
echo "✅ Label updated to: $NEW_LABEL"
385+
gh issue edit "$ISSUE_NUMBER" --add-label "$NEW_LABEL"
386+
echo "Label updated to: $NEW_LABEL"
384387
385-
# Step 5: Handle Issue State
386-
CURRENT_STATE=$(gh issue view $ISSUE_NUMBER --json state --jq '.state')
388+
# Handle issue state
389+
CURRENT_STATE=$(gh issue view "$ISSUE_NUMBER" --json state --jq '.state')
387390
388391
if [ "$STATUS" = "Done" ] && [ "$CURRENT_STATE" = "OPEN" ]; then
389-
gh issue close $ISSUE_NUMBER --reason completed
390-
echo "Issue closed (moved to Done)"
392+
gh issue close "$ISSUE_NUMBER" --reason completed
393+
echo "Issue closed (moved to Done)"
391394
elif [ "$STATUS" != "Done" ] && [ "$CURRENT_STATE" = "CLOSED" ]; then
392-
gh issue reopen $ISSUE_NUMBER
393-
echo "Issue reopened (moved from Done)"
395+
gh issue reopen "$ISSUE_NUMBER"
396+
echo "Issue reopened (moved from Done)"
394397
fi
395398
396-
echo "Sync complete: Issue #$ISSUE_NUMBER updated to $STATUS"
399+
echo "Sync complete: Issue #$ISSUE_NUMBER updated to $STATUS"

0 commit comments

Comments
 (0)