@mokoconsulting/mcp-mokosuite 1.1.0-dev.372 → 1.1.0

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.
@@ -7,8 +7,8 @@
7
7
  # INGROUP: MokoCLI.Release
8
8
  # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
9
9
  # PATH: /.mokogit/workflows/auto-release.yml
10
- # VERSION: 06.02.00
11
- # BRIEF: Universal build & release � detects platform from metadata API
10
+ # VERSION: 06.07.00
11
+ # BRIEF: Universal build & release � detects platform from metadata API. BUMP-ON-DEV: base version + CHANGELOG promoted on dev pre-promotion (promote-rc), main publishes --bump none --no-commit so it never diverges from dev.
12
12
  #
13
13
  # +=======================================================================+
14
14
  # | UNIVERSAL BUILD & RELEASE PIPELINE |
@@ -152,6 +152,18 @@ jobs:
152
152
  fetch-depth: 1
153
153
  submodules: recursive
154
154
 
155
+ - name: "Require Makefile (auto-release gate)"
156
+ # RC promotion is part of auto-release; it must also refuse to run without
157
+ # a Makefile (see the release job for rationale). Post-checkout step because
158
+ # a job-level if: cannot inspect the workspace.
159
+ run: |
160
+ if [ ! -f Makefile ] && [ ! -f makefile ] && [ ! -f GNUmakefile ]; then
161
+ echo "::error::auto-release (RC promote) requires a Makefile at the repository root — none found. Aborting."
162
+ echo "## RC promote blocked: no Makefile at repo root" >> "$GITHUB_STEP_SUMMARY"
163
+ exit 1
164
+ fi
165
+ echo "Makefile present — RC promote may proceed."
166
+
155
167
  - name: Setup MokoCLI tools
156
168
  env:
157
169
  MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
@@ -195,6 +207,38 @@ jobs:
195
207
  echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
196
208
  fi
197
209
 
210
+ - name: "Bump base version + promote changelog on dev (pre-promotion)"
211
+ env:
212
+ # Contents-API writes to protected dev need the admin-bypass token
213
+ # (github.token cannot bypass branch protection). Same token/primitive
214
+ # as the changelog persist below (#510).
215
+ MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
216
+ run: |
217
+ # "Bump on dev" (release-engineering inversion): move the base-version
218
+ # bump AND the CHANGELOG [Unreleased] promotion to dev BEFORE the
219
+ # promotion, so the rc cut (force-pushed from dev below) and the eventual
220
+ # main INHERIT the final version + promoted changelog. Nothing new is
221
+ # minted on main, so dev stays a clean superset and dev->main stops
222
+ # conflicting on Makefile/manifest/package.json/CHANGELOG.
223
+ #
224
+ # Only dev-headed promotions bump minor. fix/patch/hotfix/bugfix already
225
+ # carried their patch bump from auto-bump.yml on the branch — do NOT
226
+ # double-bump them (mirrors the release job's bump-level gate). Only runs
227
+ # for a real dev promotion (PR head == dev), never for a workflow_dispatch
228
+ # promote-rc (no PR head) — which is a manual re-cut of the current dev.
229
+ HEAD_REF="${{ github.event.pull_request.head.ref }}"
230
+ if [ "$HEAD_REF" != "dev" ]; then
231
+ echo "PR head '${HEAD_REF:-<none>}' is not dev — skipping dev pre-bump (fix/hotfix or manual re-cut)."
232
+ exit 0
233
+ fi
234
+ API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
235
+ # release_bump_dev.php: minor-bump the base on dev + promote CHANGELOG on
236
+ # dev, both via the Contents API. Idempotent + monotonic (never moves dev
237
+ # down), so a PR re-synchronize that re-runs this step is a safe no-op.
238
+ php ${MOKO_CLI}/release_bump_dev.php \
239
+ --path . --branch dev --bump minor \
240
+ --token "${MOKOGIT_TOKEN}" --api-base "${API_BASE}"
241
+
198
242
  - name: Rename branch to rc
199
243
  env:
200
244
  MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
@@ -261,8 +305,14 @@ jobs:
261
305
  - name: Publish RC release
262
306
  continue-on-error: true
263
307
  run: |
308
+ # --bump none (bump-on-dev): the base was already minor-bumped on dev by
309
+ # the pre-promotion step above, and rc was force-pushed FROM that dev, so
310
+ # rc already carries the final base. Bumping again here would double-bump
311
+ # (dev=+1, rc=+2). --no-commit: don't commit a version change back to rc
312
+ # (the rc tip must stay identical to the promoted dev). The RC release
313
+ # still stamps the -rc suffix on the inherited base in-tree.
264
314
  php ${MOKO_CLI}/release_publish.php \
265
- --path . --stability rc --bump minor --branch rc \
315
+ --path . --stability rc --bump none --no-commit --branch rc \
266
316
  --token "${{ github.token }}"
267
317
 
268
318
  - name: Update RC release notes from CHANGELOG.md
@@ -307,7 +357,7 @@ jobs:
307
357
  if: always()
308
358
  run: |
309
359
  echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
310
- echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
360
+ echo "Base bumped + changelog promoted on dev (pre-promotion); rc cut from dev; RC release built" >> $GITHUB_STEP_SUMMARY
311
361
 
312
362
  # ── Merged PR → Build & Release (or promote RC to stable) ─────────────────────────
313
363
  release:
@@ -350,6 +400,20 @@ jobs:
350
400
  fetch-depth: 0
351
401
  submodules: recursive
352
402
 
403
+ - name: "Require Makefile (auto-release gate)"
404
+ # auto-release must not run without a Makefile: the pipeline drives its
405
+ # build/package targets through make, and a release-bearing repo is
406
+ # expected to ship one. Absence is a misconfiguration — fail loudly with
407
+ # a clear message rather than half-releasing. Must be a post-checkout step
408
+ # (a job-level if: cannot inspect the workspace).
409
+ run: |
410
+ if [ ! -f Makefile ] && [ ! -f makefile ] && [ ! -f GNUmakefile ]; then
411
+ echo "::error::auto-release requires a Makefile at the repository root — none found. A release-bearing repo must ship a Makefile (build/package targets). Aborting."
412
+ echo "## Release blocked: no Makefile at repo root" >> "$GITHUB_STEP_SUMMARY"
413
+ exit 1
414
+ fi
415
+ echo "Makefile present — auto-release may proceed."
416
+
353
417
  - name: Configure git for bot pushes
354
418
  env:
355
419
  MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
@@ -422,24 +486,27 @@ jobs:
422
486
  - name: "Determine version bump level"
423
487
  id: bump
424
488
  run: |
425
- # Fix/patch branches: version was already bumped by pre-release, just strip suffix
426
- # Feature/dev branches: bump minor for the new stable release
489
+ # Bump-on-dev: the base version is now bumped on DEV pre-promotion (the
490
+ # promote-rc job's release_bump_dev.php step), so main ALWAYS publishes
491
+ # the inherited version with NO bump. Historically a dev-headed promotion
492
+ # bumped minor here (on main) — that on-main bump is exactly the divergence
493
+ # this change removes. fix/patch/hotfix/bugfix were already 'none' (their
494
+ # patch came from auto-bump.yml on the branch). So every path is 'none' now.
427
495
  HEAD_REF="${{ github.event.pull_request.head.ref || 'dev' }}"
428
- case "$HEAD_REF" in
429
- fix/*|patch/*|hotfix/*|bugfix/*) BUMP="none" ;;
430
- *) BUMP="minor" ;;
431
- esac
496
+ BUMP="none"
432
497
  echo "level=${BUMP}" >> "$GITHUB_OUTPUT"
433
- echo "Bump level: ${BUMP} (from branch: ${HEAD_REF})"
498
+ echo "Bump level: ${BUMP} (from branch: ${HEAD_REF}) — base bumped on dev pre-promotion"
434
499
 
435
500
  - name: "Publish stable release"
436
501
  run: |
437
- BUMP_FLAG=""
438
- if [ "${{ steps.bump.outputs.level }}" != "none" ]; then
439
- BUMP_FLAG="--bump ${{ steps.bump.outputs.level }}"
440
- fi
502
+ # Bump-on-dev: --bump none + --no-commit. The base was bumped on dev
503
+ # pre-promotion and inherited into main via the merge, so main neither
504
+ # bumps NOR commits a version change. release_publish still stamps the
505
+ # (unchanged) version in-tree so the built artifact is correct, but the
506
+ # commit/push back to main is suppressed -- this is what removes the
507
+ # main-only `chore(release): build` commit that diverged main from dev.
441
508
  php ${MOKO_CLI}/release_publish.php \
442
- --path . --stability stable ${BUMP_FLAG} --branch main \
509
+ --path . --stability stable --bump none --no-commit --branch main \
443
510
  --token "${{ github.token }}"
444
511
 
445
512
  - name: "Read published version"
@@ -490,7 +557,7 @@ jobs:
490
557
 
491
558
  echo "semver_tag=${SEMVER_TAG}" >> "$GITHUB_OUTPUT"
492
559
 
493
- - name: Update release notes and promote changelog
560
+ - name: Update release notes and reconcile changelog
494
561
  run: |
495
562
  API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
496
563
  TOKEN="${{ github.token }}"
@@ -536,16 +603,23 @@ jobs:
536
603
  echo "Release notes updated from CHANGELOG.md"
537
604
  fi
538
605
 
539
- # Promote [Unreleased] [version] in CHANGELOG.md via the shared mokocli command (#364).
540
- # changelog_promote.php is idempotent (won't re-promote an existing version), merges
541
- # duplicate headings, drops blank release sections, and collapses whitespace — verified
542
- # byte-identical to the former inline promoter across 8 fixtures.
606
+ # Bump-on-dev: the CHANGELOG [Unreleased] -> [version] promotion now happens
607
+ # on DEV pre-promotion (promote-rc's release_bump_dev.php step), so main
608
+ # INHERITS the already-promoted changelog via the merge and this step
609
+ # normally has nothing to do. We keep an idempotent reconcile here as a
610
+ # safety-net for the fix/hotfix->main path (a fix/* PR merges straight to
611
+ # main WITHOUT going through dev/promote-rc, so its [Unreleased] section was
612
+ # never promoted). changelog_promote.php is idempotent (won't re-promote an
613
+ # existing version) and merges/normalizes, so on the dev path it is a no-op;
614
+ # on the fix path it promotes. The commit/push are already non-fatal
615
+ # (`|| true`): nothing to commit on the dev path, and the release itself is
616
+ # already published.
543
617
  if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then
544
618
  php ${MOKO_CLI}/changelog_promote.php --path . --version "$VERSION"
545
619
  git add CHANGELOG.md
546
- git commit -m "chore: promote changelog [Unreleased] → [${VERSION}] [skip ci]" || true
620
+ git commit -m "chore: reconcile changelog [Unreleased] → [${VERSION}] [skip ci]" || true
547
621
  git push origin main || true
548
- echo "Changelog promoted: [Unreleased] [${VERSION}]"
622
+ echo "Changelog reconciled on main for [${VERSION}] (idempotent; no-op on dev path)"
549
623
  fi
550
624
 
551
625
  - name: Dispatch npm publish (bump has persisted to main)
@@ -596,6 +670,50 @@ jobs:
596
670
 
597
671
 
598
672
 
673
+ # -- Post-release: prune superseded-major version branches ---------------
674
+ # Retention policy: keep every version/* branch of the CURRENT major and
675
+ # retire the branches of older majors. No-op until a major bump, so a
676
+ # normal release touches nothing. Same-repo deletes -> per-repo github.token.
677
+ - name: "Step 13: Prune superseded-major version branches (keep current major)"
678
+ if: steps.version.outputs.skip != 'true'
679
+ continue-on-error: true
680
+ run: |
681
+ API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
682
+ TOKEN="${{ github.token }}"
683
+ VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
684
+ CUR_MAJOR="$(printf '%s' "$VERSION" | cut -d. -f1)"
685
+ CUR_MAJOR="$((10#${CUR_MAJOR:-0}))"
686
+ echo "Retention: keep only current major ${CUR_MAJOR} (from ${VERSION})"
687
+
688
+ ALL=""; page=1
689
+ while [ "$page" -le 20 ]; do
690
+ RESP="$(curl -sf -H "Authorization: token ${TOKEN}" "${API_BASE}/branches?limit=50&page=${page}" 2>/dev/null || echo '[]')"
691
+ CHUNK="$(printf '%s' "$RESP" | python3 -c "import sys,json;[print(b['name']) for b in json.load(sys.stdin)]" 2>/dev/null || true)"
692
+ [ -z "$CHUNK" ] && break
693
+ ALL="$(printf '%s\n%s' "$ALL" "$CHUNK")"
694
+ page=$((page+1))
695
+ done
696
+
697
+ PRUNED=0; KEPT=0
698
+ while IFS= read -r br; do
699
+ [ -z "$br" ] && continue
700
+ case "$br" in version/*) ;; *) continue ;; esac
701
+ maj="$(printf '%s' "${br#version/}" | cut -d. -f1)"
702
+ case "$maj" in ''|*[!0-9]*) KEPT=$((KEPT+1)); continue ;; esac
703
+ if [ "$((10#$maj))" -lt "$CUR_MAJOR" ]; then
704
+ curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${br}" 2>/dev/null \
705
+ && { echo "Pruned ${br}"; PRUNED=$((PRUNED+1)); } \
706
+ || echo "WARNING: could not prune ${br}"
707
+ else
708
+ KEPT=$((KEPT+1))
709
+ fi
710
+ done <<< "$ALL"
711
+
712
+ {
713
+ echo "### Version-branch retention"
714
+ echo "Current major \`${CUR_MAJOR}\` — kept ${KEPT}, pruned ${PRUNED} superseded-major branch(es)."
715
+ } >> "$GITHUB_STEP_SUMMARY"
716
+
599
717
  # -- Post-release: Reset dev version -----------------------------
600
718
  - name: "Post-release: Reset dev version"
601
719
  if: steps.version.outputs.skip != 'true'
@@ -645,7 +763,7 @@ jobs:
645
763
  scheduled-safety-net:
646
764
  name: Scheduled release safety-net
647
765
  runs-on: ubuntu-latest
648
- if: github.event_name == 'schedule' && !startsWith(github.event.repository.name, 'Template-')
766
+ if: github.event_name == 'schedule' && !startsWith(github.event.repository.name, 'Template-') && (github.ref_name == 'main' || github.ref_name == 'stable')
649
767
  # Own-repo write: dispatch a release when main has unreleased changes.
650
768
  permissions:
651
769
  contents: read
@@ -19,6 +19,7 @@ on:
19
19
 
20
20
  permissions:
21
21
  contents: write
22
+ actions: write
22
23
 
23
24
  env:
24
25
  MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
@@ -33,11 +34,11 @@ jobs:
33
34
  uses: actions/checkout@v4
34
35
  with:
35
36
  fetch-depth: 0
36
- token: ${{ secrets.MOKOGIT_TOKEN || github.token }}
37
+ token: ${{ github.token }}
37
38
 
38
39
  - name: Delete merged branches
39
40
  env:
40
- MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
41
+ MOKOGIT_TOKEN: ${{ github.token }}
41
42
  run: |
42
43
  echo "=== Merged Branch Cleanup ==="
43
44
  API="${MOKOGIT_URL}/api/v1/repos/${{ github.repository }}"
@@ -66,7 +67,7 @@ jobs:
66
67
 
67
68
  - name: Clean old workflow runs
68
69
  env:
69
- MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
70
+ MOKOGIT_TOKEN: ${{ github.token }}
70
71
  run: |
71
72
  echo "=== Workflow Run Cleanup ==="
72
73
  API="${MOKOGIT_URL}/api/v1/repos/${{ github.repository }}"
@@ -32,7 +32,7 @@ jobs:
32
32
  # injection via a crafted issue title). Pass everything through env so
33
33
  # the values arrive as ordinary shell variables that are not re-parsed.
34
34
  env:
35
- MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
35
+ MOKOGIT_TOKEN: ${{ github.token }}
36
36
  REPO: ${{ github.repository }}
37
37
  ISSUE_NUM: ${{ github.event.issue.number }}
38
38
  ISSUE_TITLE: ${{ github.event.issue.title }}