@mokoconsulting/mcp-mokosuite 1.0.0 → 1.1.0-dev.372
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.
- package/.mokogit/workflows/auto-bump.yml +8 -0
- package/.mokogit/workflows/auto-release.yml +184 -71
- package/.mokogit/workflows/cascade-dev.yml +128 -60
- package/.mokogit/workflows/changelog-validation.yml +41 -25
- package/.mokogit/workflows/ci-issue-reporter.yml +1 -1
- package/.mokogit/workflows/cleanup.yml +3 -3
- package/.mokogit/workflows/custom-workflow-sync-trigger.yml +83 -0
- package/.mokogit/workflows/issue-branch.yml +1 -1
- package/.mokogit/workflows/npm-build-test.yml +103 -19
- package/.mokogit/workflows/npm-prerelease.yml +174 -0
- package/.mokogit/workflows/npm-publish.yml +44 -17
- package/.mokogit/workflows/pr-check.yml +181 -53
- package/.mokogit/workflows/pre-release.yml +174 -35
- package/.mokogit/workflows/rc-revert.yml +1 -1
- package/.mokogit/workflows/version-set.yml +4 -4
- package/AGENTS.md +39 -0
- package/CHANGELOG.md +46 -10
- package/dist/index.js +28 -1
- package/docs/API.md +54 -45
- package/package.json +1 -1
- package/src/index.ts +59 -1
- package/.mokogit/workflows/ci-generic.yml +0 -203
- package/.mokogit/workflows/repo-health.yml +0 -700
- package/.mokogit/workflows/standards-compliance.yml +0 -2507
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# INGROUP: MokoCLI.CI
|
|
8
8
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
|
9
9
|
# PATH: /.mokogit/workflows/pr-check.yml
|
|
10
|
-
# VERSION: 09.
|
|
10
|
+
# VERSION: 09.24.00
|
|
11
11
|
# BRIEF: PR gate — branch policy + code validation before merge
|
|
12
12
|
|
|
13
13
|
name: "Universal: PR Check"
|
|
@@ -23,6 +23,11 @@ permissions:
|
|
|
23
23
|
env:
|
|
24
24
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
25
25
|
|
|
26
|
+
concurrency:
|
|
27
|
+
# storm-reduction: collapse superseded validation runs on the same ref
|
|
28
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
29
|
+
cancel-in-progress: true
|
|
30
|
+
|
|
26
31
|
jobs:
|
|
27
32
|
# ── Branch Policy ──────────────────────────────────────────────────────
|
|
28
33
|
branch-policy:
|
|
@@ -145,7 +150,7 @@ jobs:
|
|
|
145
150
|
steps:
|
|
146
151
|
- name: Remind to update the wiki
|
|
147
152
|
env:
|
|
148
|
-
TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
|
153
|
+
TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
|
|
149
154
|
SERVER: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
|
|
150
155
|
REPO: ${{ github.repository }}
|
|
151
156
|
PR: ${{ github.event.pull_request.number }}
|
|
@@ -160,16 +165,23 @@ jobs:
|
|
|
160
165
|
echo ""
|
|
161
166
|
echo "_Non-blocking reminder._"
|
|
162
167
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
163
|
-
#
|
|
164
|
-
|
|
168
|
+
# Sticky PR comment: create-or-UPDATE a single comment keyed by a hidden
|
|
169
|
+
# marker (no duplicate reminders across pushes). Best-effort, never fails.
|
|
170
|
+
LIST="${SERVER}/api/v1/repos/${REPO}/issues/${PR}/comments"
|
|
171
|
+
EDIT="${SERVER}/api/v1/repos/${REPO}/issues/comments"
|
|
172
|
+
MARKER="<!-- wiki-reminder -->"
|
|
173
|
+
BODY="$(printf '%s\n\n%s' "${MARKER}" "**Wiki reminder:** docs are wiki-first -- if this PR changes behavior, usage, config, or standards, please update the repo wiki before/after merge. _(non-blocking)_")"
|
|
165
174
|
if [ -n "${TOKEN:-}" ] && [ -n "${PR:-}" ]; then
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
CID="$(curl -sf -H "Authorization: token ${TOKEN}" "${LIST}?limit=100" 2>/dev/null \
|
|
176
|
+
| python3 -c "import sys,json; c=[x for x in json.load(sys.stdin) if '${MARKER}' in (x.get('body') or '')]; print(c[-1]['id'] if c else '')" 2>/dev/null || true)"
|
|
177
|
+
PAYLOAD="$(python3 -c "import json,sys; print(json.dumps({'body': sys.stdin.read()}))" <<<"${BODY}")"
|
|
178
|
+
if [ -n "${CID}" ]; then
|
|
179
|
+
curl -sf -X PATCH -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" -d "${PAYLOAD}" "${EDIT}/${CID}" >/dev/null 2>&1 || true
|
|
180
|
+
else
|
|
181
|
+
curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" -d "${PAYLOAD}" "${LIST}" >/dev/null 2>&1 || true
|
|
170
182
|
fi
|
|
171
183
|
fi
|
|
172
|
-
echo "Wiki reminder emitted (non-blocking)."
|
|
184
|
+
echo "Wiki reminder emitted (non-blocking, sticky)."
|
|
173
185
|
|
|
174
186
|
validate:
|
|
175
187
|
name: Validate PR
|
|
@@ -194,6 +206,28 @@ jobs:
|
|
|
194
206
|
fi
|
|
195
207
|
echo "No conflict markers found"
|
|
196
208
|
|
|
209
|
+
- name: Reject tracked updates.xml
|
|
210
|
+
run: |
|
|
211
|
+
# updates.xml is generated and served by the MokoGIT update server
|
|
212
|
+
# (derived from the published stream releases). It must never be
|
|
213
|
+
# committed to the repo. Fail the PR if a tracked copy appears.
|
|
214
|
+
HITS=$(git ls-files | grep -iE '(^|/)updates\.xml$' || true)
|
|
215
|
+
if [ -n "$HITS" ]; then
|
|
216
|
+
echo "::error::updates.xml must not be committed — it is generated by the MokoGIT update server."
|
|
217
|
+
printf '%s\n' "$HITS" | while IFS= read -r f; do
|
|
218
|
+
[ -n "$f" ] && echo "::error file=${f}::Remove ${f} (served by the update server, not stored in-repo)"
|
|
219
|
+
done
|
|
220
|
+
{
|
|
221
|
+
echo "## Blocked: tracked updates.xml"
|
|
222
|
+
echo "The MokoGIT update server generates this file. Remove it from the repo:"
|
|
223
|
+
echo '```'
|
|
224
|
+
printf '%s\n' "$HITS"
|
|
225
|
+
echo '```'
|
|
226
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
227
|
+
exit 1
|
|
228
|
+
fi
|
|
229
|
+
echo "No tracked updates.xml — OK"
|
|
230
|
+
|
|
197
231
|
- name: Detect platform
|
|
198
232
|
id: platform
|
|
199
233
|
run: |
|
|
@@ -237,11 +271,11 @@ jobs:
|
|
|
237
271
|
echo "::error file=${file}::Missing JEXEC guard: ${file}"
|
|
238
272
|
ERRORS=$((ERRORS + 1))
|
|
239
273
|
fi
|
|
240
|
-
done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0)
|
|
274
|
+
done < <(find . -name "*.php" \( -path "*/src/*" -o -path "*/source/*" \) -not -path "./.git/*" -not -path "./vendor/*" -print0)
|
|
241
275
|
if [ "$ERRORS" -gt 0 ]; then
|
|
242
276
|
echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard"
|
|
243
277
|
echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY
|
|
244
|
-
echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY
|
|
278
|
+
echo "${ERRORS} file(s) in src/ or source/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY
|
|
245
279
|
exit 1
|
|
246
280
|
fi
|
|
247
281
|
echo "JEXEC guard: OK"
|
|
@@ -250,7 +284,7 @@ jobs:
|
|
|
250
284
|
if: steps.platform.outputs.platform == 'joomla'
|
|
251
285
|
run: |
|
|
252
286
|
MISSING=0
|
|
253
|
-
SOURCE_DIR="src"
|
|
287
|
+
SOURCE_DIR="src"; [ -d source ] && SOURCE_DIR="source"
|
|
254
288
|
[ ! -d "$SOURCE_DIR" ] && exit 0
|
|
255
289
|
while IFS= read -r dir; do
|
|
256
290
|
if [ ! -f "${dir}/index.html" ]; then
|
|
@@ -348,27 +382,16 @@ jobs:
|
|
|
348
382
|
;;
|
|
349
383
|
esac
|
|
350
384
|
|
|
351
|
-
- name: Check update stream format
|
|
352
|
-
run: |
|
|
353
|
-
PLATFORM="${{ steps.platform.outputs.platform }}"
|
|
354
|
-
case "$PLATFORM" in
|
|
355
|
-
joomla)
|
|
356
|
-
if [ -f "updates.xml" ]; then
|
|
357
|
-
if command -v php &> /dev/null; then
|
|
358
|
-
php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('updates.xml'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::updates.xml is malformed"; exit 1; }
|
|
359
|
-
fi
|
|
360
|
-
echo "updates.xml valid"
|
|
361
|
-
fi
|
|
362
|
-
;;
|
|
363
|
-
esac
|
|
364
|
-
|
|
365
385
|
- name: Validate Joomla language files
|
|
366
386
|
if: steps.platform.outputs.platform == 'joomla'
|
|
367
387
|
run: |
|
|
368
388
|
ERRORS=0
|
|
369
389
|
WARNINGS=0
|
|
370
390
|
|
|
371
|
-
#
|
|
391
|
+
# en-GB / en-US locale parity. This is NON-FATAL: the release packager
|
|
392
|
+
# (mokocli release_package.php) AUTO-CREATES any missing sibling locale
|
|
393
|
+
# file/dir from the "familiar" locale at build time, so a mismatch here
|
|
394
|
+
# is a heads-up, not a blocker. Genuine .ini syntax errors below stay fatal.
|
|
372
395
|
LANG_ROOT=$(find . -path "*/language" -type d -not -path "./.git/*" 2>/dev/null | head -1)
|
|
373
396
|
if [ -z "$LANG_ROOT" ]; then
|
|
374
397
|
echo "No language/ directory found — skipping"
|
|
@@ -376,30 +399,30 @@ jobs:
|
|
|
376
399
|
fi
|
|
377
400
|
|
|
378
401
|
if [ ! -d "$LANG_ROOT/en-GB" ]; then
|
|
379
|
-
echo "::
|
|
380
|
-
|
|
402
|
+
echo "::warning::Missing en-GB language directory (${LANG_ROOT}/en-GB) — the release will auto-create it from en-US"
|
|
403
|
+
WARNINGS=$((WARNINGS + 1))
|
|
381
404
|
fi
|
|
382
405
|
if [ ! -d "$LANG_ROOT/en-US" ]; then
|
|
383
|
-
echo "::
|
|
384
|
-
|
|
406
|
+
echo "::warning::Missing en-US language directory (${LANG_ROOT}/en-US) — the release will auto-create it from en-GB"
|
|
407
|
+
WARNINGS=$((WARNINGS + 1))
|
|
385
408
|
fi
|
|
386
409
|
|
|
387
|
-
#
|
|
410
|
+
# Warn (don't fail) when en-GB and en-US .ini sets differ — auto-healed at release.
|
|
388
411
|
if [ -d "$LANG_ROOT/en-GB" ] && [ -d "$LANG_ROOT/en-US" ]; then
|
|
389
412
|
for GB_INI in "$LANG_ROOT/en-GB"/*.ini; do
|
|
390
413
|
[ ! -f "$GB_INI" ] && continue
|
|
391
414
|
US_INI="$LANG_ROOT/en-US/$(basename "$GB_INI")"
|
|
392
415
|
if [ ! -f "$US_INI" ]; then
|
|
393
|
-
echo "::
|
|
394
|
-
|
|
416
|
+
echo "::warning::$(basename "$GB_INI") exists in en-GB but missing from en-US — the release will auto-create it"
|
|
417
|
+
WARNINGS=$((WARNINGS + 1))
|
|
395
418
|
fi
|
|
396
419
|
done
|
|
397
420
|
for US_INI in "$LANG_ROOT/en-US"/*.ini; do
|
|
398
421
|
[ ! -f "$US_INI" ] && continue
|
|
399
422
|
GB_INI="$LANG_ROOT/en-GB/$(basename "$US_INI")"
|
|
400
423
|
if [ ! -f "$GB_INI" ]; then
|
|
401
|
-
echo "::
|
|
402
|
-
|
|
424
|
+
echo "::warning::$(basename "$US_INI") exists in en-US but missing from en-GB — the release will auto-create it"
|
|
425
|
+
WARNINGS=$((WARNINGS + 1))
|
|
403
426
|
fi
|
|
404
427
|
done
|
|
405
428
|
fi
|
|
@@ -518,7 +541,8 @@ jobs:
|
|
|
518
541
|
|
|
519
542
|
- name: Verify package source
|
|
520
543
|
run: |
|
|
521
|
-
SOURCE_DIR="
|
|
544
|
+
SOURCE_DIR="source"
|
|
545
|
+
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="src"
|
|
522
546
|
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
|
523
547
|
if [ ! -d "$SOURCE_DIR" ]; then
|
|
524
548
|
echo "::warning::No src/ or htdocs/ directory"
|
|
@@ -532,15 +556,19 @@ jobs:
|
|
|
532
556
|
pre-release:
|
|
533
557
|
name: Build RC Package
|
|
534
558
|
runs-on: ubuntu-latest
|
|
535
|
-
needs
|
|
536
|
-
#
|
|
537
|
-
#
|
|
538
|
-
|
|
559
|
+
# Independent job (no `needs:`) — needs-dependent jobs stall unscheduled on
|
|
560
|
+
# the MokoGIT runner (they sit in `waiting` with started_at=epoch and never
|
|
561
|
+
# run, leaving the check permanently pending). The Joomla Extension CI
|
|
562
|
+
# "Build RC Pre-Release" job uses this same independent pattern for exactly
|
|
563
|
+
# this reason. Guard template repos directly; branch policy is enforced by
|
|
564
|
+
# its own required check, and the pre-release workflow re-validates before
|
|
565
|
+
# publishing, so gating here on validate is not load-bearing.
|
|
566
|
+
if: github.event_name == 'pull_request' && !startsWith(github.event.repository.name, 'Template-')
|
|
539
567
|
|
|
540
568
|
steps:
|
|
541
569
|
- name: Trigger RC pre-release
|
|
542
570
|
env:
|
|
543
|
-
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
|
571
|
+
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
|
|
544
572
|
REPO: ${{ github.repository }}
|
|
545
573
|
BRANCH: ${{ github.head_ref }}
|
|
546
574
|
MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
|
|
@@ -552,14 +580,114 @@ jobs:
|
|
|
552
580
|
# ── Issue Reporter ──────────────────────────────────────────────────────
|
|
553
581
|
report-issues:
|
|
554
582
|
name: Report Issues
|
|
555
|
-
needs
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
583
|
+
# No `needs:` — the MokoGIT Actions scheduler does not offer needs-dependent
|
|
584
|
+
# jobs to runners (they stall "waiting" and are reaped by
|
|
585
|
+
# ABANDONED_JOB_TIMEOUT). This job runs independently and polls the run API
|
|
586
|
+
# for the Validate PR result, then files an issue only when it failed.
|
|
587
|
+
if: always()
|
|
588
|
+
runs-on: ubuntu-latest
|
|
589
|
+
steps:
|
|
590
|
+
- name: Resolve Validate PR result (run API)
|
|
591
|
+
id: gate
|
|
592
|
+
env:
|
|
593
|
+
TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
|
|
594
|
+
SERVER: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
|
|
595
|
+
REPO: ${{ github.repository }}
|
|
596
|
+
RUN_ID: ${{ github.run_id }}
|
|
597
|
+
run: |
|
|
598
|
+
set -u
|
|
599
|
+
api="${SERVER}/api/v1/repos/${REPO}/actions/runs/${RUN_ID}/jobs"
|
|
600
|
+
val="unknown"
|
|
601
|
+
for i in $(seq 1 120); do
|
|
602
|
+
resp=$(curl -sf -H "Authorization: token ${TOKEN}" "$api" 2>/dev/null || echo '{}')
|
|
603
|
+
done_n=$(printf '%s' "$resp" | jq -r '[.jobs[]?|select(.name=="Validate PR")|select(.status=="completed")]|length' 2>/dev/null || echo 0)
|
|
604
|
+
if [ "${done_n:-0}" -ge 1 ]; then
|
|
605
|
+
val=$(printf '%s' "$resp" | jq -r '[.jobs[]?|select(.name=="Validate PR")][0].conclusion // "unknown"' 2>/dev/null || echo unknown)
|
|
606
|
+
break
|
|
607
|
+
fi
|
|
608
|
+
sleep 15
|
|
609
|
+
done
|
|
610
|
+
echo "validate=${val}" >> "$GITHUB_OUTPUT"
|
|
611
|
+
echo "Validate PR result: ${val}"
|
|
612
|
+
- name: Clone MokoCLI
|
|
613
|
+
if: steps.gate.outputs.validate == 'failure'
|
|
614
|
+
env:
|
|
615
|
+
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
|
616
|
+
MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
|
|
617
|
+
run: |
|
|
618
|
+
git clone --depth 1 --filter=blob:none --sparse "${MOKOGIT_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
|
|
619
|
+
cd /tmp/mokocli && git sparse-checkout set cli
|
|
620
|
+
- name: Report CI failure
|
|
621
|
+
if: steps.gate.outputs.validate == 'failure'
|
|
622
|
+
env:
|
|
623
|
+
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
|
624
|
+
MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
|
|
625
|
+
# Route inputs through env vars (avoid ${{ }} shell-injection in run:).
|
|
626
|
+
INPUT_GATE: "PR Validation"
|
|
627
|
+
INPUT_DETAILS: "PR validation failed (syntax, manifest, changelog, or source checks). See the CI run for the specific check that failed."
|
|
628
|
+
INPUT_SEVERITY: error
|
|
629
|
+
INPUT_WORKFLOW: "PR Check"
|
|
630
|
+
run: |
|
|
631
|
+
chmod +x /tmp/mokocli/cli/ci_issue_reporter.sh
|
|
632
|
+
/tmp/mokocli/cli/ci_issue_reporter.sh \
|
|
633
|
+
--gate "$INPUT_GATE" \
|
|
634
|
+
--details "$INPUT_DETAILS" \
|
|
635
|
+
--severity "$INPUT_SEVERITY" \
|
|
636
|
+
--workflow "$INPUT_WORKFLOW"
|
|
637
|
+
|
|
638
|
+
# ── PR Check Summary (sticky comment) ──────────────────────────────────────
|
|
639
|
+
# Create-or-update ONE comment (hidden marker) reflecting the gate outcome, so
|
|
640
|
+
# the PR carries a live status line refreshed on every push. Best-effort.
|
|
641
|
+
pr-summary:
|
|
642
|
+
name: PR Check Summary (sticky comment)
|
|
643
|
+
runs-on: ubuntu-latest
|
|
644
|
+
# No `needs:` — poll the run API for gate results (the scheduler does not
|
|
645
|
+
# dispatch needs-dependent jobs; they stall and are reaped).
|
|
646
|
+
if: always()
|
|
647
|
+
steps:
|
|
648
|
+
- name: Upsert sticky summary comment
|
|
649
|
+
env:
|
|
650
|
+
TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
|
|
651
|
+
SERVER: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
|
|
652
|
+
REPO: ${{ github.repository }}
|
|
653
|
+
PR: ${{ github.event.pull_request.number }}
|
|
654
|
+
RUN_ID: ${{ github.run_id }}
|
|
655
|
+
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
656
|
+
HEAD_REF: ${{ github.head_ref }}
|
|
657
|
+
BASE_REF: ${{ github.base_ref }}
|
|
658
|
+
run: |
|
|
659
|
+
set -uo pipefail
|
|
660
|
+
{ [ -n "${TOKEN:-}" ] && [ -n "${PR:-}" ]; } || { echo "no token/PR; skip"; exit 0; }
|
|
661
|
+
# Poll the run API for Branch Policy + Validate PR results (no `needs:`).
|
|
662
|
+
api="${SERVER}/api/v1/repos/${REPO}/actions/runs/${RUN_ID}/jobs"
|
|
663
|
+
BP=unknown; VAL=unknown
|
|
664
|
+
for i in $(seq 1 120); do
|
|
665
|
+
resp=$(curl -sf -H "Authorization: token ${TOKEN}" "$api" 2>/dev/null || echo '{}')
|
|
666
|
+
pend=$(printf '%s' "$resp" | jq -r '[.jobs[]?|select(.name=="Branch Policy" or .name=="Validate PR")|select(.status!="completed")]|length' 2>/dev/null || echo 1)
|
|
667
|
+
tot=$(printf '%s' "$resp" | jq -r '[.jobs[]?|select(.name=="Branch Policy" or .name=="Validate PR")]|length' 2>/dev/null || echo 0)
|
|
668
|
+
if [ "${tot:-0}" -ge 2 ] && [ "${pend:-1}" = "0" ]; then
|
|
669
|
+
BP=$(printf '%s' "$resp" | jq -r '[.jobs[]?|select(.name=="Branch Policy")][0].conclusion // "unknown"')
|
|
670
|
+
VAL=$(printf '%s' "$resp" | jq -r '[.jobs[]?|select(.name=="Validate PR")][0].conclusion // "unknown"')
|
|
671
|
+
break
|
|
672
|
+
fi
|
|
673
|
+
sleep 15
|
|
674
|
+
done
|
|
675
|
+
icon() { case "$1" in success) printf '✅';; failure) printf '❌';; skipped) printf '⏭️';; *) printf '⚪';; esac; }
|
|
676
|
+
MARKER="<!-- pr-check-summary -->"
|
|
677
|
+
BODY="$(printf '%s\n## PR Check Summary\n\n- %s Branch policy (`%s` → `%s`)\n- %s Validation\n\n_Updated %s · [run](%s)_' \
|
|
678
|
+
"${MARKER}" \
|
|
679
|
+
"$(icon "${BP}")" "${HEAD_REF}" "${BASE_REF}" \
|
|
680
|
+
"$(icon "${VAL}")" \
|
|
681
|
+
"$(date -u +%FT%TZ)" "${RUN_URL}")"
|
|
682
|
+
LIST="${SERVER}/api/v1/repos/${REPO}/issues/${PR}/comments"
|
|
683
|
+
EDIT="${SERVER}/api/v1/repos/${REPO}/issues/comments"
|
|
684
|
+
CID="$(curl -sf -H "Authorization: token ${TOKEN}" "${LIST}?limit=100" 2>/dev/null \
|
|
685
|
+
| python3 -c "import sys,json; c=[x for x in json.load(sys.stdin) if '${MARKER}' in (x.get('body') or '')]; print(c[-1]['id'] if c else '')" 2>/dev/null || true)"
|
|
686
|
+
PAYLOAD="$(python3 -c "import json,sys; print(json.dumps({'body': sys.stdin.read()}))" <<<"${BODY}")"
|
|
687
|
+
if [ -n "${CID}" ]; then
|
|
688
|
+
curl -sf -X PATCH -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" -d "${PAYLOAD}" "${EDIT}/${CID}" >/dev/null 2>&1 || true
|
|
689
|
+
echo "updated sticky summary #${CID} on PR #${PR}"
|
|
690
|
+
else
|
|
691
|
+
curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" -d "${PAYLOAD}" "${LIST}" >/dev/null 2>&1 || true
|
|
692
|
+
echo "created sticky summary on PR #${PR}"
|
|
693
|
+
fi
|