@datarailsshared/dr_renderer 1.5.76 → 1.5.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,915 @@
1
+ # AI Coder Jira Integration Workflow
2
+ #
3
+ # This workflow integrates AI-powered code generation with Jira issues.
4
+ # It can be triggered by Jira webhooks, GitHub issues, or manual dispatch.
5
+ #
6
+ # MANDATORY REPOSITORY SECRETS:
7
+ # ==============================
8
+ # - OPENAI_API_KEY: OpenAI API key for AI code generation (required for Codex)
9
+ # - ANTHROPIC_API_KEY: Anthropic API key for Claude Code (required for Claude)
10
+ # - JIRA_EMAIL: Email address for Jira API authentication
11
+ # - JIRA_API_TOKEN: Jira API token for authentication
12
+ # - JIRA_BASE_URL: Base URL for Jira instance (e.g., https://yourcompany.atlassian.net)
13
+ # If not provided, will be derived from payload baseUrl
14
+ # WORKFLOW INPUTS (for manual dispatch):
15
+ # ======================================
16
+ # - jira_issue_key: Jira issue key (default: DR-38896)
17
+ # - jira_issue_summary: Brief description of the task
18
+ # - ai_provider: AI provider to use (codex or claude) - default: codex
19
+ #
20
+ # TRIGGER EVENTS:
21
+ # ===============
22
+ # 1. workflow_dispatch: Manual trigger with optional inputs
23
+ # 2. repository_dispatch: Triggered by Jira webhook (event_type: jira_trigger)
24
+ # 3. issues: Triggered when GitHub issue gets 'ai-coder' label
25
+ #
26
+
27
+ name: ai-coder-jira
28
+
29
+ on:
30
+ workflow_dispatch:
31
+ inputs:
32
+ jira_issue_key:
33
+ description: "Jira issue key (if dispatching manually)"
34
+ required: false
35
+ default: DR-38896
36
+ jira_issue_summary:
37
+ description: "Remove unreferenced import in Badge"
38
+ required: false
39
+ ai_provider:
40
+ description: "AI provider to use"
41
+ required: false
42
+ default: codex
43
+ type: choice
44
+ options:
45
+ - codex
46
+ - claude
47
+ repository_dispatch:
48
+ types: [jira_trigger] # Matches "event_type" in your JSON
49
+ issues:
50
+ types: [labeled] # Run when a GH issue gets 'ai-coder'
51
+
52
+ jobs:
53
+ run-ai-agent:
54
+ if: |
55
+ (github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'ai-coder')) ||
56
+ (github.event_name == 'repository_dispatch' && github.event.action == 'jira_trigger') ||
57
+ (github.event_name == 'workflow_dispatch')
58
+ runs-on: ubuntu-latest
59
+
60
+ permissions:
61
+ contents: write
62
+ pull-requests: write
63
+ issues: write
64
+
65
+ steps:
66
+ - uses: actions/checkout@v4
67
+ with:
68
+ fetch-depth: 0
69
+
70
+ - name: Set up Node
71
+ uses: actions/setup-node@v4
72
+ with:
73
+ node-version: 22
74
+
75
+ - name: Create GitHub App token
76
+ id: app-token
77
+ uses: actions/create-github-app-token@v2
78
+ with:
79
+ app-id: ${{ secrets.GH_APP_ID }}
80
+ private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
81
+
82
+ - name: Set token as env var
83
+ run: echo "GH_TOKEN=${{ steps.app-token.outputs.token }}" >> $GITHUB_ENV
84
+
85
+ - name: Debug environment
86
+ run: |
87
+ echo "Event name: ${{ github.event_name }}"
88
+ if [ "${{ github.event_name }}" = "repository_dispatch" ]; then
89
+ echo "Triggered from Jira via repository_dispatch"
90
+ echo "Jira issueKey: ${{ github.event.client_payload.jira.issueKey }}"
91
+ echo "Jira summary: ${{ github.event.client_payload.jira.summary }}"
92
+ echo "AI Provider: ${{ github.event.client_payload.ai_provider || 'codex' }}"
93
+ elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
94
+ echo "Manual dispatch"
95
+ echo "Jira key: ${{ github.event.inputs.jira_issue_key }}"
96
+ echo "Jira summary: ${{ github.event.inputs.jira_issue_summary }}"
97
+ echo "AI Provider: ${{ github.event.inputs.ai_provider }}"
98
+ else
99
+ echo "Triggered from GitHub Issue"
100
+ echo "Issue title: ${{ github.event.issue.title }}"
101
+ echo "Issue number: ${{ github.event.issue.number }}"
102
+ echo "Issue URL: ${{ github.event.issue.html_url }}"
103
+ echo "Labels: ${{ toJson(github.event.issue.labels.*.name) }}"
104
+ echo "AI Provider: codex (default for issue triggers)"
105
+ fi
106
+ echo "Repository: ${{ github.repository }}"
107
+
108
+ - name: Determine AI provider
109
+ run: |
110
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
111
+ AI_PROVIDER="${{ github.event.inputs.ai_provider }}"
112
+ elif [ "${{ github.event_name }}" = "repository_dispatch" ]; then
113
+ AI_PROVIDER="${{ github.event.client_payload.ai_provider }}"
114
+ else
115
+ AI_PROVIDER="codex" # Default for issue triggers
116
+ fi
117
+
118
+ # Default to codex if not specified
119
+ AI_PROVIDER="${AI_PROVIDER:-codex}"
120
+
121
+ echo "AI_PROVIDER=$AI_PROVIDER" >> $GITHUB_ENV
122
+ echo "✅ Using AI provider: $AI_PROVIDER"
123
+
124
+ - name: Validate secrets (will fallback to payload baseUrl if needed)
125
+ run: |
126
+ fail=0
127
+
128
+ # Check AI provider specific secrets
129
+ if [ "${{ env.AI_PROVIDER }}" = "claude" ]; then
130
+ if [ -z "${{ secrets.ANTHROPIC_API_KEY }}" ]; then
131
+ echo "❌ Error: ANTHROPIC_API_KEY not set (required for Claude)"
132
+ fail=1
133
+ else
134
+ echo "✅ ANTHROPIC_API_KEY present"
135
+ fi
136
+ else
137
+ if [ -z "${{ secrets.OPENAI_API_KEY }}" ]; then
138
+ echo "❌ Error: OPENAI_API_KEY not set (required for Codex)"
139
+ fail=1
140
+ else
141
+ echo "✅ OPENAI_API_KEY present"
142
+ fi
143
+ fi
144
+
145
+ if [ -z "${{ secrets.OPENAI_API_KEY }}" ]; then echo "❌ Error: OPENAI_API_KEY not set"; fail=1; fi
146
+ # Jira API secrets preferred; but API base can fallback to payload origin later if JIRA_BASE_URL is empty
147
+ if [ -z "${{ secrets.JIRA_EMAIL }}" ]; then echo "❌ Error: JIRA_EMAIL not set"; fail=1; fi
148
+ if [ -z "${{ secrets.JIRA_API_TOKEN }}" ]; then echo "❌ Error: JIRA_API_TOKEN not set"; fail=1; fi
149
+ if [ $fail -eq 1 ]; then exit 1; fi
150
+ echo "✅ Required secrets present (JIRA_BASE_URL is optional if payload provides baseUrl)"
151
+
152
+ - name: Install AI CLI
153
+ run: |
154
+ if [ "${{ env.AI_PROVIDER }}" = "claude" ]; then
155
+ echo "Installing Claude Code CLI..."
156
+ MAX_ATTEMPTS=15
157
+ ATTEMPT=1
158
+
159
+ while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
160
+ echo "Attempt $ATTEMPT of $MAX_ATTEMPTS"
161
+
162
+ if npm install -g @anthropic-ai/claude-code; then
163
+ echo "✅ Claude Code CLI installed successfully on attempt $ATTEMPT"
164
+ break
165
+ else
166
+ echo "❌ Claude Code CLI install failed on attempt $ATTEMPT"
167
+ if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
168
+ echo "All install attempts failed"
169
+ exit 1
170
+ fi
171
+ sleep 15
172
+ ATTEMPT=$((ATTEMPT + 1))
173
+ fi
174
+ done
175
+ echo "CLI=claude" >> $GITHUB_ENV
176
+ echo "CLI_COMMAND=claude code" >> $GITHUB_ENV
177
+ else
178
+ echo "Installing Codex CLI..."
179
+ MAX_ATTEMPTS=15
180
+ ATTEMPT=1
181
+
182
+ while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
183
+ echo "Attempt $ATTEMPT of $MAX_ATTEMPTS"
184
+
185
+ if npm i -g @openai/codex; then
186
+ echo "✅ Codex CLI installed successfully on attempt $ATTEMPT"
187
+ break
188
+ else
189
+ echo "❌ Codex CLI install failed on attempt $ATTEMPT"
190
+ if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
191
+ echo "All install attempts failed"
192
+ exit 1
193
+ fi
194
+ sleep 15
195
+ ATTEMPT=$((ATTEMPT + 1))
196
+ fi
197
+ done
198
+ echo "CLI=codex" >> $GITHUB_ENV
199
+ echo "CLI_COMMAND=codex" >> $GITHUB_ENV
200
+ fi
201
+
202
+ - name: Verify CLI installation
203
+ run: |
204
+ if [ "${{ env.AI_PROVIDER }}" = "claude" ]; then
205
+ if command -v claude >/dev/null 2>&1; then
206
+ echo "✅ Claude CLI available"
207
+ claude --version || echo "ℹ️ Could not get claude version"
208
+ else
209
+ echo "❌ Claude CLI not found after installation"
210
+ exit 1
211
+ fi
212
+ else
213
+ if command -v codex >/dev/null 2>&1; then
214
+ echo "✅ Codex CLI available"
215
+ codex --version || echo "ℹ️ Could not get codex version"
216
+ else
217
+ echo "❌ Codex CLI not found after installation"
218
+ exit 1
219
+ fi
220
+ fi
221
+
222
+ - id: vars
223
+ shell: bash
224
+ run: |
225
+ set -euo pipefail
226
+ EVENT="${{ github.event_name }}"
227
+
228
+ JIRA_ISSUE_URL=""
229
+ if [ "$EVENT" = "repository_dispatch" ]; then
230
+ ISSUE_KEY="${{ github.event.client_payload.jira.issueKey }}"
231
+ TITLE="${{ github.event.client_payload.jira.summary }}"
232
+ elif [ "$EVENT" = "workflow_dispatch" ]; then
233
+ ISSUE_KEY="${{ github.event.inputs.jira_issue_key }}"
234
+ TITLE="${{ github.event.inputs.jira_issue_summary }}"
235
+ fi
236
+
237
+ TITLE="${TITLE:-Automated task}"
238
+ RAW_DESC="${RAW_DESC:-No description provided.}"
239
+
240
+ DESC_CLEANED="$(echo "$RAW_DESC" | tr '\n' ' ' | sed 's/"/'\''/g')"
241
+ SAFE_TITLE="$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9-' '-' | sed 's/^-*\|-*$//g')"
242
+ BRANCH="${ISSUE_KEY}-${SAFE_TITLE}"
243
+
244
+ echo "ISSUE_KEY=$ISSUE_KEY" >> $GITHUB_ENV
245
+ echo "TITLE=$TITLE" >> $GITHUB_ENV
246
+ echo "RAW_DESC=$RAW_DESC" >> $GITHUB_ENV
247
+ echo "DESC=$DESC_CLEANED" >> $GITHUB_ENV
248
+ echo "BRANCH=$BRANCH" >> $GITHUB_ENV
249
+
250
+
251
+ # Derive Jira API base (origin) from secret OR payload URL
252
+ # Prefer secret JIRA_BASE_URL; else derive "https://your-domain.atlassian.net" from JIRA_ISSUE_URL
253
+ SECRET_BASE="${{ secrets.JIRA_BASE_URL }}"
254
+ if [ -n "$SECRET_BASE" ]; then
255
+ API_BASE="$SECRET_BASE"
256
+ else
257
+ if [ -n "$JIRA_ISSUE_URL" ]; then
258
+ API_BASE="$(echo "$JIRA_ISSUE_URL" | awk -F/ '{print $1"//"$3}')"
259
+ else
260
+ API_BASE=""
261
+ fi
262
+ fi
263
+
264
+ # Construct the full Jira ticket URL
265
+ JIRA_ISSUE_URL=""
266
+ if [ -n "$API_BASE" ] && [ -n "$ISSUE_KEY" ]; then
267
+ # Only construct Jira URL for actual Jira issues (not GitHub issues)
268
+ JIRA_ISSUE_URL="${API_BASE}/browse/${ISSUE_KEY}"
269
+ echo "✅ Constructed Jira URL: $JIRA_ISSUE_URL"
270
+ else
271
+ echo "ℹ️ No Jira URL constructed (API_BASE='$API_BASE', ISSUE_KEY='$ISSUE_KEY')"
272
+ fi
273
+ echo "JIRA_ISSUE_URL=$JIRA_ISSUE_URL" >> $GITHUB_ENV
274
+ echo "API_BASE=$API_BASE" >> $GITHUB_ENV
275
+
276
+ - name: Jira – Transition to In Progress (if looks like a Jira key)
277
+ if: (startsWith(env.ISSUE_KEY, 'DR-') == true || startsWith(env.ISSUE_KEY, 'CA-') == true) && env.API_BASE != ''
278
+ env:
279
+ ISSUE_KEY: ${{ env.ISSUE_KEY }}
280
+ API_BASE: ${{ env.API_BASE }}
281
+ JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
282
+ JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
283
+ run: |
284
+ set -euo pipefail
285
+
286
+ echo "🔧 Starting Jira transition to 'In Progress'"
287
+ echo " ISSUE_KEY: $ISSUE_KEY"
288
+ echo " API_BASE: $API_BASE"
289
+ echo " JIRA_EMAIL: ${JIRA_EMAIL:0:5}***@${JIRA_EMAIL#*@}"
290
+ echo " Token length: ${#JIRA_API_TOKEN} chars"
291
+
292
+ # Install jq if needed
293
+ if ! command -v jq >/dev/null 2>&1; then
294
+ echo "📦 Installing jq..."
295
+ sudo apt-get update && sudo apt-get install -y jq >/dev/null
296
+ fi
297
+
298
+ # Fetch available transitions with error handling
299
+ echo "📡 Fetching available transitions for $ISSUE_KEY..."
300
+ TRANSITIONS_URL="$API_BASE/rest/api/3/issue/$ISSUE_KEY/transitions"
301
+ echo " Request URL: $TRANSITIONS_URL"
302
+
303
+ HTTP_STATUS=$(curl -sS -X GET \
304
+ --url "$TRANSITIONS_URL" \
305
+ --user "$JIRA_EMAIL:$JIRA_API_TOKEN" \
306
+ --header 'Accept: application/json' \
307
+ -w '%{http_code}' \
308
+ -o transitions.json)
309
+
310
+ echo " HTTP Status: $HTTP_STATUS"
311
+
312
+ if [ "$HTTP_STATUS" != "200" ]; then
313
+ echo "❌ Failed to fetch transitions (HTTP $HTTP_STATUS)"
314
+ echo "Response body:"
315
+ cat transitions.json
316
+ exit 1
317
+ fi
318
+
319
+ # Show raw response for debugging
320
+ echo "📋 Raw transitions response (first 500 chars):"
321
+ head -c 500 transitions.json; echo
322
+
323
+ # Validate JSON structure
324
+ if ! jq -e '.transitions' transitions.json >/dev/null 2>&1; then
325
+ echo "❌ Invalid JSON structure - missing .transitions array"
326
+ echo "Full response:"
327
+ cat transitions.json
328
+ exit 1
329
+ fi
330
+
331
+ # Show all available transitions
332
+ echo "📋 Available transitions:"
333
+ jq -r '.transitions[] | " - \(.name) (id: \(.id)) -> \(.to.name)"' transitions.json || echo " Failed to parse transitions"
334
+
335
+ # Try to find "In Progress" transition with various name patterns
336
+ TRANSITIONS_JSON="$(cat transitions.json)"
337
+
338
+ echo "🔍 Looking for 'In Progress' transition..."
339
+ INPROG_ID="$(echo "$TRANSITIONS_JSON" | jq -r '.transitions[] | select(.name=="In Progress") | .id' 2>/dev/null || echo "")"
340
+
341
+ if [ -z "$INPROG_ID" ] || [ "$INPROG_ID" = "null" ]; then
342
+ echo " Exact 'In Progress' not found, trying alternatives..."
343
+
344
+ # Try alternative names
345
+ INPROG_ID="$(echo "$TRANSITIONS_JSON" | jq -r '.transitions[] | select(.name=="Start Progress") | .id' 2>/dev/null || echo "")"
346
+ [ -z "$INPROG_ID" ] && INPROG_ID="$(echo "$TRANSITIONS_JSON" | jq -r '.transitions[] | select(.name=="Start Work") | .id | head -n 1' 2>/dev/null || echo "")"
347
+ [ -z "$INPROG_ID" ] && INPROG_ID="$(echo "$TRANSITIONS_JSON" | jq -r '.transitions[] | select(.to.name=="In Progress") | .id | head -n 1' 2>/dev/null || echo "")"
348
+
349
+ if [ -z "$INPROG_ID" ] || [ "$INPROG_ID" = "null" ]; then
350
+ echo "⚠️ Could not find any 'In Progress' transition with these patterns:"
351
+ echo " - 'In Progress'"
352
+ echo " - 'Start Progress'"
353
+ echo " - 'Start Work'"
354
+ echo " - Any transition leading to 'In Progress' status"
355
+ echo " Available transitions listed above. Skipping transition."
356
+ exit 0
357
+ fi
358
+ fi
359
+
360
+ echo "✅ Found 'In Progress' transition with ID: $INPROG_ID"
361
+
362
+ # Apply the transition
363
+ echo "⚡ Applying transition id=$INPROG_ID to $ISSUE_KEY"
364
+ TRANSITION_URL="$API_BASE/rest/api/3/issue/$ISSUE_KEY/transitions"
365
+ TRANSITION_DATA="{\"transition\":{\"id\":\"$INPROG_ID\"}}"
366
+
367
+ echo " POST URL: $TRANSITION_URL"
368
+ echo " POST Data: $TRANSITION_DATA"
369
+
370
+ TRANSITION_STATUS=$(curl -sS -X POST \
371
+ --url "$TRANSITION_URL" \
372
+ --user "$JIRA_EMAIL:$JIRA_API_TOKEN" \
373
+ --header 'Content-Type: application/json' \
374
+ --data "$TRANSITION_DATA" \
375
+ -w '%{http_code}' \
376
+ -o transition_response.json)
377
+
378
+ echo " Transition HTTP Status: $TRANSITION_STATUS"
379
+
380
+ if [ "$TRANSITION_STATUS" = "204" ]; then
381
+ echo "✅ Successfully transitioned $ISSUE_KEY to 'In Progress'"
382
+ else
383
+ echo "❌ Transition failed (HTTP $TRANSITION_STATUS)"
384
+ echo "Response body:"
385
+ cat transition_response.json 2>/dev/null || echo "No response body"
386
+ exit 1
387
+ fi
388
+
389
+ - name: Fetch Jira issue (debug & export)
390
+ env:
391
+ ISSUE_KEY: ${{ env.ISSUE_KEY }}
392
+ JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
393
+ JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
394
+ API_BASE: ${{ env.API_BASE }}
395
+ run: |
396
+ set -euo pipefail
397
+
398
+ echo "ISSUE_KEY=${ISSUE_KEY}"
399
+ echo "API_BASE host: $(echo "$API_BASE" | sed -E 's#https?://([^/]+)/?.*#\1#')"
400
+ if [ -z "${ISSUE_KEY:-}" ]; then
401
+ echo "::error::ISSUE_KEY is empty"; exit 1
402
+ fi
403
+
404
+ if ! command -v jq >/dev/null 2>&1; then
405
+ echo "jq not found; installing"
406
+ sudo apt-get update -y && sudo apt-get install -y jq
407
+ fi
408
+
409
+ HTTP_STATUS=$(curl -sS -X GET \
410
+ --url "$API_BASE/rest/api/3/issue/$ISSUE_KEY?fields=summary,description&expand=renderedFields" \
411
+ --user "$JIRA_EMAIL:$JIRA_API_TOKEN" \
412
+ --header 'Accept: application/json' \
413
+ -D headers.txt -o issue.json -w '%{http_code}')
414
+
415
+ echo "HTTP_STATUS=$HTTP_STATUS"
416
+ echo "Raw JSON response (first 500 chars):"
417
+ head -c 500 issue.json; echo
418
+
419
+ if [ "$HTTP_STATUS" != "200" ]; then
420
+ echo "::error::Jira GET /issue returned HTTP $HTTP_STATUS"
421
+ echo "Error response:"
422
+ cat issue.json
423
+ exit 1
424
+ fi
425
+
426
+ # Check if the response has the expected structure
427
+ echo "Checking JSON structure..."
428
+ if ! jq -e '.fields' issue.json >/dev/null 2>&1; then
429
+ echo "::error::Invalid JSON structure - missing .fields"
430
+ cat issue.json
431
+ exit 1
432
+ fi
433
+
434
+ # Extract summary with null safety
435
+ SUMMARY="$(jq -r 'if .fields.summary then .fields.summary else "No summary available" end' issue.json)"
436
+ echo "Extracted SUMMARY: $SUMMARY"
437
+
438
+ # Extract description with comprehensive null handling
439
+ RENDERED="$(jq -r 'if .renderedFields and .renderedFields.description then .renderedFields.description else "" end' issue.json)"
440
+ if [ -n "$RENDERED" ] && [ "$RENDERED" != "null" ]; then
441
+ echo "Using renderedFields.description (HTML)."
442
+ DESC="$(printf '%s' "$RENDERED" \
443
+ | sed -e 's/<[^>]*>//g' \
444
+ -e 's/&nbsp;/ /g' -e 's/&amp;/\&/g' -e 's/&lt;/</g' -e 's/&gt;/>/g')"
445
+ else
446
+ echo "renderedFields.description missing or null; trying to extract from ADF format."
447
+ # More robust ADF extraction with null safety
448
+ DESC="$(jq -r '
449
+ if .fields.description and .fields.description.content then
450
+ [.fields.description.content[] |
451
+ if .content then
452
+ [.content[] | if .text then .text else empty end]
453
+ else empty end] |
454
+ flatten | join("\n")
455
+ else
456
+ "No description available"
457
+ end
458
+ ' issue.json 2>/dev/null || echo "No description available")"
459
+ fi
460
+
461
+ # Ensure DESC is not null or empty
462
+ if [ -z "$DESC" ] || [ "$DESC" = "null" ]; then
463
+ DESC="No description provided"
464
+ fi
465
+
466
+ echo "DESC length: $(printf '%s' "$DESC" | wc -c)"
467
+ echo "DESC preview (first 200 chars):"
468
+ printf '%s' "$DESC" | head -c 200; echo
469
+
470
+ # Export to env (multi-line safe)
471
+ echo "ISSUE_KEY=$ISSUE_KEY" >> "$GITHUB_ENV"
472
+ { echo "SUMMARY<<EOF"; printf '%s\n' "$SUMMARY"; echo "EOF"; } >> "$GITHUB_ENV"
473
+ { echo "DESC<<EOF"; printf '%s\n' "$DESC"; echo "EOF"; } >> "$GITHUB_ENV"
474
+
475
+
476
+ - name: Resolve base & branch
477
+ run: |
478
+ set -euo pipefail
479
+ BASE="${{ github.event.repository.default_branch }}" # e.g. main
480
+ BR="${{ env.BRANCH }}" # optional preset
481
+
482
+ # If not provided or equals BASE, synthesize a unique branch
483
+ if [ -z "${BR:-}" ] || [ "$BR" = "$BASE" ]; then
484
+ BR="codex/${ISSUE_KEY:-issue}-${GITHUB_RUN_ID}"
485
+ fi
486
+
487
+ echo "BASE=$BASE" >> "$GITHUB_ENV"
488
+ echo "BRANCH=$BR" >> "$GITHUB_ENV"
489
+ echo "Resolved BASE=$BASE, BRANCH=$BR"
490
+
491
+ # (Re)checkout the base branch to ensure correct diff target
492
+ - uses: actions/checkout@v4
493
+ with:
494
+ ref: ${{ env.BASE }}
495
+ fetch-depth: 0
496
+
497
+ - name: Create or update branch
498
+ env:
499
+ BRANCH: ${{ env.BRANCH }}
500
+ run: |
501
+ # Fetch all remote branches
502
+ git fetch origin
503
+
504
+ # Check if remote branch exists
505
+ if git ls-remote --heads origin "${BRANCH}" | grep -q "${BRANCH}"; then
506
+ echo "Branch ${BRANCH} exists on remote, checking it out and pulling"
507
+ git checkout -B "${BRANCH}" origin/"${BRANCH}"
508
+ git pull origin "${BRANCH}"
509
+ else
510
+ echo "Branch ${BRANCH} doesn't exist on remote, creating new branch"
511
+ git checkout -B "${BRANCH}"
512
+ fi
513
+
514
+ - name: Implement with AI
515
+ env:
516
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
517
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
518
+ run: |
519
+ set -euo pipefail
520
+
521
+ # Build PROMPT
522
+ PROMPT=$(cat <<EOF
523
+ ${ISSUE_KEY}: ${SUMMARY}
524
+
525
+ Details:
526
+ ${DESC}
527
+ EOF
528
+ )
529
+
530
+ echo "PROMPT length: $(printf '%s' "$PROMPT" | wc -c)"
531
+ echo "---- PROMPT preview (first 20 lines) ----"
532
+ printf '%s\n' "$PROMPT" | sed -n '1,20p'
533
+ echo "----------------------------------------"
534
+
535
+ AI_OUTPUT_FILE="ai_output_${ISSUE_KEY}_${GITHUB_RUN_ID}.txt"
536
+
537
+ if [ "${{ env.AI_PROVIDER }}" = "claude" ]; then
538
+ echo "Running Claude Code..."
539
+ if claude --dangerously-skip-permissions -p "$PROMPT" > "$AI_OUTPUT_FILE" 2>&1; then
540
+ echo "✅ Claude Code completed successfully"
541
+ AI_SUCCESS=true
542
+ else
543
+ echo "❌ Claude Code failed"
544
+ AI_SUCCESS=false
545
+ fi
546
+ else
547
+ echo "Running Codex..."
548
+ if codex exec --full-auto "$PROMPT" > "$AI_OUTPUT_FILE" 2>&1; then
549
+ echo "✅ Codex completed successfully"
550
+ AI_SUCCESS=true
551
+ else
552
+ echo "❌ Codex failed"
553
+ AI_SUCCESS=false
554
+ fi
555
+ fi
556
+
557
+ AI_OUTPUT=$(cat "$AI_OUTPUT_FILE")
558
+ echo "---- AI OUTPUT (first 50 lines) ----"
559
+ head -50 "$AI_OUTPUT_FILE"
560
+ echo "---- END OUTPUT PREVIEW ----"
561
+
562
+ echo "AI_SUCCESS=$AI_SUCCESS" >> $GITHUB_ENV
563
+
564
+ AI_OUTPUT_PREVIEW=$(head -50 "$AI_OUTPUT_FILE")
565
+ ESCAPED_PREVIEW=$(echo "$AI_OUTPUT_PREVIEW" | jq -Rs .)
566
+ echo "AI_OUTPUT_PREVIEW=$ESCAPED_PREVIEW" >> $GITHUB_ENV
567
+ echo "AI_OUTPUT_FILE=$AI_OUTPUT_FILE" >> $GITHUB_ENV
568
+
569
+ if [ "$AI_SUCCESS" = "false" ]; then
570
+ exit 1
571
+ fi
572
+
573
+ - name: Upload AI Output as Artifact
574
+ if: always()
575
+ uses: actions/upload-artifact@v4
576
+ with:
577
+ name: ${{ env.AI_PROVIDER }}-output-${{ env.ISSUE_KEY }}-${{ github.run_id }}
578
+ path: ${{ env.AI_OUTPUT_FILE }}
579
+ retention-days: 30
580
+
581
+ - name: Move AI Output to Temp Directory
582
+ if: always()
583
+ run: |
584
+ # Create temp directory if it doesn't exist
585
+ mkdir -p /tmp/ai-artifacts
586
+
587
+ # Move the file to temp directory
588
+ if [ -f "${{ env.AI_OUTPUT_FILE }}" ]; then
589
+ TEMP_FILE="/tmp/ai-artifacts/$(basename "${{ env.AI_OUTPUT_FILE }}")"
590
+ mv "${{ env.AI_OUTPUT_FILE }}" "$TEMP_FILE"
591
+ echo "📁 Moved file to: $TEMP_FILE"
592
+
593
+ # Update environment variable with new path
594
+ echo "AI_OUTPUT_FILE=$TEMP_FILE" >> $GITHUB_ENV
595
+ echo "✅ Updated AI_OUTPUT_FILE environment variable"
596
+ else
597
+ echo "❌ File not found: ${{ env.AI_OUTPUT_FILE }}"
598
+ fi
599
+
600
+ - name: Configure Git & Commit changes (if any)
601
+ env:
602
+ ISSUE_KEY: ${{ env.ISSUE_KEY }}
603
+ TITLE: ${{ env.TITLE }}
604
+ BRANCH: ${{ env.BRANCH }}
605
+ run: |
606
+ git config user.name "GitHub Actions Bot"
607
+ git config user.email "github-actions@github.com"
608
+
609
+ if git diff --quiet; then
610
+ echo "ℹ️ No changes to commit."
611
+ # Still need to push the empty branch for PR creation
612
+ git push --set-upstream origin "${BRANCH}"
613
+ else
614
+ git add -A
615
+ git commit -m "${ISSUE_KEY}: ${TITLE} (via ${{ env.AI_PROVIDER }})"
616
+ git push --set-upstream origin "${BRANCH}"
617
+ echo "✅ Committed and pushed changes"
618
+ fi
619
+
620
+ - name: Create label if it doesn't exist
621
+ run: |
622
+ # Create the ai-coder label if it doesn't exist
623
+ gh label create "ai-coder" --description "AI-generated code" --color "1f883d" || echo "Label already exists"
624
+
625
+ - name: Create or Update PR with gh CLI
626
+ run: |
627
+ # Check if PR already exists for this branch
628
+ EXISTING_PR=$(gh pr list --head "${{ env.BRANCH }}" --base "${{ env.BASE }}" --json number --jq '.[0].number')
629
+
630
+ if [ "$EXISTING_PR" == "null" ] || [ -z "$EXISTING_PR" ]; then
631
+ echo "Creating new PR"
632
+ gh pr create \
633
+ --head "${{ env.BRANCH }}" \
634
+ --title "${{ env.ISSUE_KEY }}: ${{ env.TITLE }}" \
635
+ --body "🤖 AI-generated implementation for **${{ env.ISSUE_KEY }}** using **${{ env.AI_PROVIDER }}**.
636
+
637
+ - Branch: \`${{ env.BRANCH }}\`
638
+ - Trigger: \`${{ github.event_name }}\`
639
+ - AI Provider: \`${{ env.AI_PROVIDER }}\`
640
+ - Jira: ${{ env.JIRA_ISSUE_URL }}
641
+ - AI Output Artifact: [Download from Actions](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" \
642
+ --label "ai-coder" \
643
+ --base "${{ env.BASE }}"
644
+ else
645
+ echo "Updating existing PR #$EXISTING_PR"
646
+ gh pr edit $EXISTING_PR \
647
+ --title "${{ env.ISSUE_KEY }}: ${{ env.TITLE }}" \
648
+ --body "🤖 AI-generated implementation for **${{ env.ISSUE_KEY }}** using **${{ env.AI_PROVIDER }}**.
649
+
650
+ - Branch: \`${{ env.BRANCH }}\`
651
+ - Trigger: \`${{ github.event_name }}\`
652
+ - AI Provider: \`${{ env.AI_PROVIDER }}\`
653
+ - Jira: ${{ env.JIRA_ISSUE_URL }}
654
+ - AI Output Artifact: [Download from Actions](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" \
655
+ --add-label "ai-coder"
656
+ echo "Updated PR: ${{ github.server_url }}/${{ github.repository }}/pull/$EXISTING_PR"
657
+ fi
658
+
659
+ - name: Upload AI Output to Jira as Attachment
660
+ if: success() && (startsWith(env.ISSUE_KEY, 'DR-') == true || startsWith(env.ISSUE_KEY, 'CA-') == true) && env.API_BASE != ''
661
+ env:
662
+ JIRA_BASE_URL: ${{ env.API_BASE }}
663
+ JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
664
+ JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
665
+ ISSUE_KEY: ${{ env.ISSUE_KEY }}
666
+ run: |
667
+ echo "Uploading ${{ env.AI_PROVIDER }} output to Jira as attachment..."
668
+
669
+ ATTACHMENT_FILE="${{ env.AI_OUTPUT_FILE }}"
670
+
671
+ if [ -f "$ATTACHMENT_FILE" ]; then
672
+ echo "Uploading $ATTACHMENT_FILE to Jira issue $ISSUE_KEY"
673
+
674
+ curl -X POST \
675
+ -H "X-Atlassian-Token: no-check" \
676
+ -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
677
+ -F "file=@$ATTACHMENT_FILE" \
678
+ "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/attachments"
679
+
680
+ echo "✅ Attachment uploaded successfully"
681
+ else
682
+ echo "❌ Codex output file not found: $ATTACHMENT_FILE"
683
+ fi
684
+
685
+ - name: Update Jira on Success
686
+ if: success() && (startsWith(env.ISSUE_KEY, 'DR-') == true || startsWith(env.ISSUE_KEY, 'CA-') == true) && env.API_BASE != ''
687
+ env:
688
+ JIRA_BASE_URL: ${{ env.API_BASE }}
689
+ JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
690
+ JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
691
+ ISSUE_KEY: ${{ env.ISSUE_KEY }}
692
+ AI_OUTPUT_PREVIEW: ${{ env.AI_OUTPUT_PREVIEW }}
693
+ run: |
694
+ echo "Updating Jira ticket to Done status and adding comment..."
695
+
696
+ PR_URL="https://github.com/${{ github.repository }}/pull/$(gh pr list --head ${{ env.BRANCH }} --json number --jq '.[0].number')"
697
+
698
+ # Create the comment using jq for proper JSON escaping
699
+ COMMENT_JSON=$(jq -n \
700
+ --arg pr_url "$PR_URL" \
701
+ --arg ai_provider "${{ env.AI_PROVIDER }}" \
702
+ --arg preview "$AI_OUTPUT_PREVIEW" \
703
+ '{
704
+ "body": {
705
+ "type": "doc",
706
+ "version": 1,
707
+ "content": [
708
+ {
709
+ "type": "paragraph",
710
+ "content": [
711
+ {
712
+ "type": "text",
713
+ "text": ("🤖 AI Coding Agent Results - Success! (using " + $ai_provider + ")"),
714
+ "marks": [{"type": "strong"}]
715
+ }
716
+ ]
717
+ },
718
+ {
719
+ "type": "paragraph",
720
+ "content": [
721
+ {
722
+ "type": "text",
723
+ "text": "Pull Request: "
724
+ },
725
+ {
726
+ "type": "text",
727
+ "text": $pr_url,
728
+ "marks": [{"type": "link", "attrs": {"href": $pr_url}}]
729
+ }
730
+ ]
731
+ },
732
+ {
733
+ "type": "paragraph",
734
+ "content": [
735
+ {
736
+ "type": "text",
737
+ "text": "Full output attached to this issue. Preview (first 50 lines):"
738
+ }
739
+ ]
740
+ },
741
+ {
742
+ "type": "codeBlock",
743
+ "attrs": {"language": "text"},
744
+ "content": [
745
+ {
746
+ "type": "text",
747
+ "text": $preview
748
+ }
749
+ ]
750
+ }
751
+ ]
752
+ }
753
+ }')
754
+
755
+ curl -X POST \
756
+ -H "Content-Type: application/json" \
757
+ -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
758
+ "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/comment" \
759
+ -d "$COMMENT_JSON"
760
+
761
+ # Transition to Done
762
+ TRANSITIONS=$(curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
763
+ "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/transitions")
764
+
765
+ DONE_TRANSITION_ID=$(echo "$TRANSITIONS" | jq -r '.transitions[] | select(.name == "Done" or .to.name == "Done") | .id' | head -n 1)
766
+
767
+ if [ -n "$DONE_TRANSITION_ID" ] && [ "$DONE_TRANSITION_ID" != "null" ]; then
768
+ echo "Transitioning to Done (ID: $DONE_TRANSITION_ID)"
769
+ curl -X POST \
770
+ -H "Content-Type: application/json" \
771
+ -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
772
+ "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/transitions" \
773
+ -d "{
774
+ \"transition\": {
775
+ \"id\": \"$DONE_TRANSITION_ID\"
776
+ }
777
+ }"
778
+ else
779
+ echo "❌ Could not find Done transition"
780
+ fi
781
+
782
+ - name: Update Jira on Failure
783
+ if: failure() && (startsWith(env.ISSUE_KEY, 'DR-') == true || startsWith(env.ISSUE_KEY, 'CA-') == true) && env.API_BASE != ''
784
+ env:
785
+ JIRA_BASE_URL: ${{ env.API_BASE }}
786
+ JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
787
+ JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
788
+ ISSUE_KEY: ${{ env.ISSUE_KEY }}
789
+ AI_OUTPUT_PREVIEW: ${{ env.AI_OUTPUT_PREVIEW }}
790
+ run: |
791
+ echo "Updating Jira ticket to Blocked status and adding error comment..."
792
+
793
+ ATTACHMENT_FILE="${{ env.AI_OUTPUT_FILE }}"
794
+ if [ -f "$ATTACHMENT_FILE" ]; then
795
+ echo "Uploading error output to Jira as attachment..."
796
+ curl -X POST \
797
+ -H "X-Atlassian-Token: no-check" \
798
+ -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
799
+ -F "file=@$ATTACHMENT_FILE" \
800
+ "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/attachments" || echo "Failed to upload attachment"
801
+ fi
802
+
803
+ # Add comment with error details
804
+ WORKFLOW_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
805
+
806
+ # Create the comment using jq for proper JSON escaping
807
+ COMMENT_JSON=$(jq -n \
808
+ --arg workflow_url "$WORKFLOW_URL" \
809
+ --arg ai_provider "${{ env.AI_PROVIDER }}" \
810
+ --arg preview "$AI_OUTPUT_PREVIEW" \
811
+ '{
812
+ "body": {
813
+ "type": "doc",
814
+ "version": 1,
815
+ "content": [
816
+ {
817
+ "type": "paragraph",
818
+ "content": [
819
+ {
820
+ "type": "text",
821
+ "text": ("❌ AI Coding Agent Failed (using " + $ai_provider + ")"),
822
+ "marks": [{"type": "strong"}]
823
+ }
824
+ ]
825
+ },
826
+ {
827
+ "type": "paragraph",
828
+ "content": [
829
+ {
830
+ "type": "text",
831
+ "text": "Workflow run: "
832
+ },
833
+ {
834
+ "type": "text",
835
+ "text": $workflow_url,
836
+ "marks": [{"type": "link", "attrs": {"href": $workflow_url}}]
837
+ }
838
+ ]
839
+ },
840
+ {
841
+ "type": "paragraph",
842
+ "content": [
843
+ {
844
+ "type": "text",
845
+ "text": "Error output (first 50 lines, full output attached):"
846
+ }
847
+ ]
848
+ },
849
+ {
850
+ "type": "codeBlock",
851
+ "attrs": {"language": "text"},
852
+ "content": [
853
+ {
854
+ "type": "text",
855
+ "text": $preview
856
+ }
857
+ ]
858
+ }
859
+ ]
860
+ }
861
+ }')
862
+
863
+ curl -X POST \
864
+ -H "Content-Type: application/json" \
865
+ -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
866
+ "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/comment" \
867
+ -d "$COMMENT_JSON"
868
+
869
+ # Get available transitions for debugging
870
+ echo "Fetching available transitions..."
871
+ TRANSITIONS=$(curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
872
+ "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/transitions")
873
+
874
+ echo "Debug: Available transitions:"
875
+ echo "$TRANSITIONS" | jq '.transitions[] | {id: .id, name: .name, to_name: .to.name}'
876
+
877
+ # Try to find blocked transition with multiple approaches
878
+ BLOCKED_TRANSITION_ID=$(echo "$TRANSITIONS" | jq -r '
879
+ .transitions[] |
880
+ select(
881
+ (.name | ascii_downcase | contains("block")) or
882
+ (.to.name | ascii_downcase | contains("block")) or
883
+ (.name | ascii_downcase | contains("fail")) or
884
+ (.to.name | ascii_downcase | contains("fail")) or
885
+ (.name | ascii_downcase | contains("pause")) or
886
+ (.to.name | ascii_downcase | contains("pause")) or
887
+ (.name | ascii_downcase | contains("stop")) or
888
+ (.to.name | ascii_downcase | contains("stop"))
889
+ ) |
890
+ .id' | head -1)
891
+
892
+ if [ -n "$BLOCKED_TRANSITION_ID" ] && [ "$BLOCKED_TRANSITION_ID" != "null" ]; then
893
+ echo "Found transition ID: $BLOCKED_TRANSITION_ID"
894
+ echo "Transitioning to Blocked status..."
895
+
896
+ TRANSITION_RESULT=$(curl -s -X POST \
897
+ -H "Content-Type: application/json" \
898
+ -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
899
+ "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/transitions" \
900
+ -d "{
901
+ \"transition\": {
902
+ \"id\": \"$BLOCKED_TRANSITION_ID\"
903
+ }
904
+ }")
905
+
906
+ if [ $? -eq 0 ]; then
907
+ echo "✅ Successfully transitioned issue to blocked status"
908
+ else
909
+ echo "❌ Failed to transition issue: $TRANSITION_RESULT"
910
+ fi
911
+ else
912
+ echo "❌ Could not find a suitable transition. Available transitions:"
913
+ echo "$TRANSITIONS" | jq -r '.transitions[] | "\(.id): \(.name) -> \(.to.name)"'
914
+ echo "❌ Please check your Jira workflow configuration for transitions containing: block, fail, pause, or stop"
915
+ fi
@@ -0,0 +1,82 @@
1
+ name: AI-CODER-CALLER-FULL
2
+ on:
3
+ workflow_dispatch:
4
+ inputs:
5
+ prompt:
6
+ description: 'Base64 encoded prompt'
7
+ required: true
8
+ type: string
9
+ webhook_uuid:
10
+ description: 'Webhook uuid param'
11
+ required: true
12
+ type: string
13
+ create_pr:
14
+ description: 'create pr'
15
+ required: true
16
+ type: boolean
17
+ title:
18
+ description: 'Base64 encoded title for the PR'
19
+ required: true
20
+ type: string
21
+ default: ""
22
+ reviewer:
23
+ description: 'Optional reviewer username for the PR'
24
+ required: false
25
+ type: string
26
+ default: ""
27
+ ai_provider:
28
+ description: 'AI provider to use (claude or codex)'
29
+ required: false
30
+ type: string
31
+ default: "claude"
32
+ repository_dispatch:
33
+ types: [ai-coder-proxy]
34
+
35
+ jobs:
36
+ prepare-inputs:
37
+ runs-on: ubuntu-latest
38
+ outputs:
39
+ prompt: ${{ steps.parse.outputs.prompt }}
40
+ webhook_uuid: ${{ steps.parse.outputs.webhook_uuid }}
41
+ create_pr: ${{ steps.parse.outputs.create_pr }}
42
+ title: ${{ steps.parse.outputs.title }}
43
+ reviewer: ${{ steps.parse.outputs.reviewer }}
44
+ ai_provider: ${{ steps.parse.outputs.ai_provider }}
45
+ steps:
46
+ - name: Parse inputs
47
+ id: parse
48
+ run: |
49
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
50
+ echo "prompt=${{ github.event.inputs.prompt }}" >> $GITHUB_OUTPUT
51
+ echo "webhook_uuid=${{ github.event.inputs.webhook_uuid }}" >> $GITHUB_OUTPUT
52
+ echo "create_pr=${{ github.event.inputs.create_pr }}" >> $GITHUB_OUTPUT
53
+ echo "title=${{ github.event.inputs.title }}" >> $GITHUB_OUTPUT
54
+ echo "reviewer=${{ github.event.inputs.reviewer }}" >> $GITHUB_OUTPUT
55
+ echo "ai_provider=${{ github.event.inputs.ai_provider }}" >> $GITHUB_OUTPUT
56
+ elif [ "${{ github.event_name }}" = "repository_dispatch" ]; then
57
+ echo "prompt=${{ github.event.client_payload.prompt }}" >> $GITHUB_OUTPUT
58
+ echo "webhook_uuid=${{ github.event.client_payload.webhook_uuid }}" >> $GITHUB_OUTPUT
59
+ echo "create_pr=${{ github.event.client_payload.create_pr }}" >> $GITHUB_OUTPUT
60
+ echo "title=${{ github.event.client_payload.title }}" >> $GITHUB_OUTPUT
61
+ echo "reviewer=${{ github.event.client_payload.reviewer }}" >> $GITHUB_OUTPUT
62
+ echo "ai_provider=${{ github.event.client_payload.ai_provider || 'claude' }}" >> $GITHUB_OUTPUT
63
+ fi
64
+
65
+ call-ai-coder:
66
+ needs: prepare-inputs
67
+ uses: Datarails/dr_github_reusable/.github/workflows/ai-coder-n8n-reusable.yml@DR-38894-Automation
68
+ with:
69
+ prompt: ${{ needs.prepare-inputs.outputs.prompt }}
70
+ webhook_uuid: ${{ needs.prepare-inputs.outputs.webhook_uuid }}
71
+ create_pr: ${{ needs.prepare-inputs.outputs.create_pr == 'true' }}
72
+ title: ${{ needs.prepare-inputs.outputs.title }}
73
+ reviewer: ${{ needs.prepare-inputs.outputs.reviewer }}
74
+ ai_provider: ${{ needs.prepare-inputs.outputs.ai_provider }}
75
+ secrets:
76
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
77
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
78
+ GH_APP_ID: ${{ secrets.GH_APP_ID }}
79
+ GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
80
+ N8N_WEBHOOK_URL: ${{ secrets.N8N_WEBHOOK_URL }}
81
+ GH_PER_TOKEN_TEST: ${{ secrets.GH_PER_TOKEN_TEST }}
82
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datarailsshared/dr_renderer",
3
- "version": "1.5.76",
3
+ "version": "1.5.88",
4
4
  "description": "DataRails charts and tables renderer",
5
5
  "keywords": [
6
6
  "datarails",
@@ -989,7 +989,8 @@ let getHighchartsRenderer = function ($, document, Highcharts, default_colors, h
989
989
  tempAr[temp_len - 1] = highchartsRenderer.getFieldName(colAttrs[j]);
990
990
  }
991
991
 
992
- tempAr = tempAr.concat(lodash.map(colKeys, function (row) {
992
+ const formattedColKeys = highchartsRenderer.getFormattedColKeys(pivotData, null);
993
+ tempAr = tempAr.concat(lodash.map(formattedColKeys, function (row) {
993
994
  return row[j];
994
995
  }));
995
996