@mokoenterprises/mokossh-mcp 3.8.1 → 3.9.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.
- package/.mokogit/ISSUE_TEMPLATE/config.yml +4 -4
- package/.mokogit/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/.mokogit/ISSUE_TEMPLATE/security.md +1 -1
- package/.mokogit/actions/resolve-source-dir/action.yml +1 -1
- package/.mokogit/workflows/apidocs.yml +6 -6
- package/.mokogit/workflows/auto-assign.yml +1 -1
- package/.mokogit/workflows/auto-dev-issue.yml +2 -2
- package/.mokogit/workflows/auto-release.yml +202 -33
- package/.mokogit/workflows/branch-cleanup.yml +3 -3
- package/.mokogit/workflows/ci-issue-reporter.yml +5 -5
- package/.mokogit/workflows/cleanup.yml +3 -3
- package/.mokogit/workflows/makefile-lint.yml +48 -17
- package/.mokogit/workflows/npm-build-test.yml +8 -8
- package/.mokogit/workflows/npm-sdk-check.yml +1 -1
- package/.mokogit/workflows/npm-tool-inventory.yml +2 -2
- package/.mokogit/workflows/pr-branch-check.yml +92 -92
- package/.mokogit/workflows/pre-release.yml +208 -15
- package/.mokogit/workflows/rc-revert.yml +3 -3
- package/AGENTS.md +3 -3
- package/CHANGELOG.md +1 -1
- package/CONTRIBUTING.md +6 -6
- package/Makefile +3 -2
- package/README.md +20 -20
- package/SECURITY.md +2 -2
- package/package.json +9 -8
- package/source/token-reader.js +1 -1
- package/tools/deploy.mjs +27 -0
- package/.mokogit/workflows/cascade.yml +0 -645
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# Copyright (C) 2026 Moko
|
|
1
|
+
# Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
4
|
#
|
|
5
5
|
# FILE INFORMATION
|
|
6
6
|
# DEFGROUP: MokoGIT.Workflow
|
|
7
7
|
# INGROUP: MokoCLI.Maintenance
|
|
8
|
-
# REPO: https://git.
|
|
8
|
+
# REPO: https://git.mokoenterprises.us/MokoEnterprises/Template-Generic
|
|
9
9
|
# PATH: /.mokogit/workflows/cleanup.yml
|
|
10
10
|
# VERSION: 01.01.00
|
|
11
11
|
# CHANGE (01.01.00): fail-open branch/run cleanup (#430) — the run token may lack REST read on some repos, returning an error OBJECT; `jq '.[]'` on it exit-5'd the job under pipefail (fleet-wide red). Guard type=="array" / `(.workflow_runs // [])` and skip gracefully; cleanup is best-effort housekeeping and must never red the run.
|
|
@@ -23,7 +23,7 @@ permissions:
|
|
|
23
23
|
actions: write
|
|
24
24
|
|
|
25
25
|
env:
|
|
26
|
-
MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.
|
|
26
|
+
MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoenterprises.us' }}
|
|
27
27
|
|
|
28
28
|
jobs:
|
|
29
29
|
cleanup:
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
# Copyright (C) 2026 Moko
|
|
1
|
+
# Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
4
|
#
|
|
5
5
|
# FILE INFORMATION
|
|
6
6
|
# DEFGROUP: MokoGIT.Workflow
|
|
7
7
|
# INGROUP: MokoCLI.Validation
|
|
8
|
-
# REPO: https://git.
|
|
8
|
+
# REPO: https://git.mokoenterprises.us/MokoEnterprises/Template-Generic
|
|
9
9
|
# PATH: /.mokogit/workflows/makefile-lint.yml
|
|
10
|
-
# VERSION: 01.
|
|
11
|
-
#
|
|
10
|
+
# VERSION: 01.05.01
|
|
11
|
+
# CHANGE (01.05.00): Hardened the Scaffold Placeholder Guard against two false-positive classes. (1) Dropped MokonyxExample from LITERAL_ALT -- it is a documented @see example-class reference (e.g. MokonyxExampleInstallerScript) cited fleet-wide, not an un-de-templated scaffold leftover, so it false-positived legit PRs (mirrors the #524 com_example removal). (2) Excluded TRIPLE-brace from MOUSTACHE_RE -- a real placeholder is always exactly double-brace {{X}}, whereas {{{X}}} is Python f-string brace-escaping (e.g. {{{SODIPODI_NS}}} in inkscape-extensions), never a de-template leftover; the regex now uses PCRE (?<!\{)/(?!\}) lookarounds and scan() greps with -P instead of -E.
|
|
12
|
+
# BRIEF: NEW (01.05.01): the Scaffold Placeholder Guard now also EXCLUDES Gitea-fork test-config fixtures `tests/*.ini.tmpl` (and `tests/**/*.ini.tmpl`). Their `{{TEST_*}}`/`{{GITEA_TEST_*}}` moustache tokens are legitimate test-harness templating rendered by Gitea's own test runner, NOT de-template leftovers; they were hard-failing the guard on every MokoGIT (Gitea fork) PR (#2059). PREVIOUS: Makefile sanity lint -- identity fields + release contract (SRC_DIR, PROJECT_VERSION format, BUILD_DIR, required targets, parse). Release-contract checks are WARN by default; set vars.MAKEFILE_LINT_ENFORCE=enforce to hard-fail (JOOMLA_I18N_ENFORCE-style, per-repo opt-in). Also runs a Scaffold Placeholder Guard job that HARD-FAILS when leftover de-template placeholders (exactly-double-brace {{UPPER_SNAKE}} moustaches and fixed literals like example_resources/CHANGEME/myproject) survive in the tracked tree; skipped on Template-* source repos and excludes .mokogit/** + CHANGELOG*. The moustache scan uses PCRE lookarounds so TRIPLE-brace {{{X}}} (Python f-string brace-escaping) is NOT flagged. The bare Template-<Word> content scan, the com_example literal, and the MokonyxExample literal were removed (#524 / 01.05.00) as they false-positived on legitimate standards-repo citations and documented example hints.
|
|
12
13
|
#
|
|
13
14
|
# +========================================================================+
|
|
14
15
|
# | MAKEFILE IDENTITY LINT |
|
|
@@ -300,10 +301,18 @@ jobs:
|
|
|
300
301
|
|
|
301
302
|
# Literal scaffold strings that must never survive de-templating,
|
|
302
303
|
# as a single pre-built alternation (so this definition line is not
|
|
303
|
-
# itself a multi-token target the scanner would trip over).
|
|
304
|
-
#
|
|
305
|
-
#
|
|
306
|
-
|
|
304
|
+
# itself a multi-token target the scanner would trip over).
|
|
305
|
+
#
|
|
306
|
+
# Deliberately NOT included (issue #524):
|
|
307
|
+
# - com_example : a legitimate documentation/example token used in
|
|
308
|
+
# real field hints (e.g. hint="/media/com_example/*.php"), the
|
|
309
|
+
# matching _DESC language strings, and source/build/i18n-allow.txt.
|
|
310
|
+
# It is an illustrative glob, not an un-de-templated scaffold token.
|
|
311
|
+
# - MokonyxExample : a documented @see example-class reference (e.g.
|
|
312
|
+
# MokonyxExampleInstallerScript) cited fleet-wide in installer
|
|
313
|
+
# comments and docs. Like com_example above it is an illustrative
|
|
314
|
+
# reference, not a de-template leftover, so it is likewise excluded.
|
|
315
|
+
LITERAL_ALT='example_resources|CHANGEME|myproject|myorg|your-project'
|
|
307
316
|
|
|
308
317
|
# Regexes applied per tracked file. Kept as distinct patterns so the
|
|
309
318
|
# failure message can name which class of placeholder was hit.
|
|
@@ -311,9 +320,24 @@ jobs:
|
|
|
311
320
|
# {{DISPLAY_NAME}}, {{ENV_PREFIX}}, and the general
|
|
312
321
|
# {{UPPER_SNAKE}} shape. Requires [A-Z0-9_] (at least one [A-Z]),
|
|
313
322
|
# so lowercase app tokens like {{tempFile}} do NOT match.
|
|
314
|
-
#
|
|
315
|
-
|
|
316
|
-
|
|
323
|
+
#
|
|
324
|
+
# The moustache shape plus the explicit literal set are the reliable
|
|
325
|
+
# de-template signals. A bare Template-<Word> word-match was REMOVED
|
|
326
|
+
# (issue #524): it false-positived on every legitimate citation of the
|
|
327
|
+
# standards repos (Template-Joomla #377 in installer comments, README
|
|
328
|
+
# extension tables, @see wiki links, uninstall SQL banners), which are
|
|
329
|
+
# correct references, not leftover scaffolding. A genuinely
|
|
330
|
+
# un-de-templated repo still trips the moustache/literal rules.
|
|
331
|
+
#
|
|
332
|
+
# Triple-brace EXCLUSION (PCRE lookarounds): a real scaffold
|
|
333
|
+
# placeholder is always EXACTLY double-brace -- {{UPPER_SNAKE}}. A
|
|
334
|
+
# TRIPLE-brace {{{X}}} is Python f-string brace-escaping (e.g.
|
|
335
|
+
# {{{SODIPODI_NS}}} in inkscape-extensions, where {{ / }} emit literal
|
|
336
|
+
# braces around an interpolated value), never a de-template leftover.
|
|
337
|
+
# The (?<!\{) / (?!\}) lookarounds reject a {{...}} that is flanked by
|
|
338
|
+
# a third brace, so f-string escapes no longer false-positive. This
|
|
339
|
+
# requires PCRE, so scan() greps with -P (see below).
|
|
340
|
+
MOUSTACHE_RE='(?<!\{)\{\{[A-Z0-9_]*[A-Z][A-Z0-9_]*\}\}(?!\})'
|
|
317
341
|
|
|
318
342
|
# Files to exclude from the scan:
|
|
319
343
|
# - .mokogit/** : carries this workflow (its own token list)
|
|
@@ -321,10 +345,15 @@ jobs:
|
|
|
321
345
|
# - CHANGELOG* : legitimately records historical names.
|
|
322
346
|
# - the moustache/literal patterns' own home is .mokogit, already out.
|
|
323
347
|
# git ls-files honours .gitignore and only lists TRACKED files.
|
|
348
|
+
# - tests/*.ini.tmpl : Gitea-fork test-config fixtures whose
|
|
349
|
+
# {{TEST_*}}/{{GITEA_TEST_*}} moustaches are legitimate test-harness
|
|
350
|
+
# templating (rendered by Gitea's test runner), not scaffold leftovers (#2059).
|
|
324
351
|
mapfile -t FILES < <(git ls-files \
|
|
325
352
|
':(exclude).mokogit/**' \
|
|
326
353
|
':(exclude)CHANGELOG*' \
|
|
327
|
-
':(exclude)**/CHANGELOG*'
|
|
354
|
+
':(exclude)**/CHANGELOG*' \
|
|
355
|
+
':(exclude)tests/*.ini.tmpl' \
|
|
356
|
+
':(exclude)tests/**/*.ini.tmpl')
|
|
328
357
|
|
|
329
358
|
HITS=0
|
|
330
359
|
|
|
@@ -332,19 +361,21 @@ jobs:
|
|
|
332
361
|
local re="$1" label="$2" f
|
|
333
362
|
for f in "${FILES[@]}"; do
|
|
334
363
|
[ -f "$f" ] || continue
|
|
335
|
-
# grep -
|
|
336
|
-
|
|
364
|
+
# grep -nP: line number + PCRE. PCRE is required for the moustache
|
|
365
|
+
# pattern's (?<!\{)/(?!\}) lookarounds (triple-brace f-string
|
|
366
|
+
# exclusion). GNU grep on ubuntu-latest supports -P. The LITERAL_ALT
|
|
367
|
+
# plain a|b|c alternation works identically under -P. Binary skipped.
|
|
368
|
+
grep -nIP "$re" "$f" 2>/dev/null | while IFS= read -r line; do
|
|
337
369
|
echo "::error file=${f}::Leftover scaffold placeholder (${label}): ${f}:${line}"
|
|
338
370
|
done
|
|
339
|
-
if grep -
|
|
340
|
-
grep -
|
|
371
|
+
if grep -qIP "$re" "$f" 2>/dev/null; then
|
|
372
|
+
grep -nIP "$re" "$f" 2>/dev/null | sed "s|^|- [FAIL] ${f}:|" >> "$GITHUB_STEP_SUMMARY"
|
|
341
373
|
HITS=1
|
|
342
374
|
fi
|
|
343
375
|
done
|
|
344
376
|
}
|
|
345
377
|
|
|
346
378
|
scan "$MOUSTACHE_RE" "UPPER_SNAKE moustache placeholder"
|
|
347
|
-
scan "$TEMPLATE_REPO_RE" "Template-<Word> repo reference"
|
|
348
379
|
scan "$LITERAL_ALT" "literal scaffold token"
|
|
349
380
|
|
|
350
381
|
if [ "$HITS" -ne 0 ]; then
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# MCP Server Build & Validation
|
|
2
|
-
# Copyright (C) 2026 Moko
|
|
2
|
+
# Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
4
|
#
|
|
5
5
|
# Builds the MCP server via the centralized MokoCLI build agent
|
|
@@ -32,7 +32,7 @@ permissions:
|
|
|
32
32
|
issues: write # folded-in Wiki Reminder posts a sticky PR comment
|
|
33
33
|
|
|
34
34
|
env:
|
|
35
|
-
MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.
|
|
35
|
+
MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoenterprises.us' }}
|
|
36
36
|
GIT_ORG: ${{ vars.MOKOGIT_ORG || github.repository_owner }}
|
|
37
37
|
GIT_REPO: ${{ vars.MOKOGIT_REPO || github.event.repository.name }}
|
|
38
38
|
|
|
@@ -97,7 +97,7 @@ jobs:
|
|
|
97
97
|
- name: Setup MokoCLI build agent (moko on PATH)
|
|
98
98
|
env:
|
|
99
99
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
|
100
|
-
MOKO_CLONE_HOST: git.
|
|
100
|
+
MOKO_CLONE_HOST: git.mokoenterprises.us/MokoEnterprises
|
|
101
101
|
run: |
|
|
102
102
|
# Reach mokocli deterministically and expose `moko` on PATH (mokocli's cli/ dir)
|
|
103
103
|
# so `moko build|test|lint` resolve. Prefer a pre-installed /opt/mokocli ONLY
|
|
@@ -120,8 +120,8 @@ jobs:
|
|
|
120
120
|
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
|
|
121
121
|
fi
|
|
122
122
|
rm -rf /tmp/mokocli
|
|
123
|
-
REG='{"type":"composer","url":"https://git.
|
|
124
|
-
if composer create-project --no-dev --no-interaction --no-progress --quiet --repository="${REG}"
|
|
123
|
+
REG='{"type":"composer","url":"https://git.mokoenterprises.us/api/packages/MokoEnterprises/composer"}'
|
|
124
|
+
if composer create-project --no-dev --no-interaction --no-progress --quiet --repository="${REG}" mokoenterprises/mokocli /tmp/mokocli >/dev/null 2>&1 && { [ -x /tmp/mokocli/cli/moko ] || [ -f /tmp/mokocli/cli/moko ]; }; then
|
|
125
125
|
echo "Bootstrapped mokocli from the Composer registry (tokenless)."
|
|
126
126
|
else
|
|
127
127
|
echo "Registry bootstrap unavailable; falling back to authenticated git clone."
|
|
@@ -355,7 +355,7 @@ jobs:
|
|
|
355
355
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
356
356
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
357
357
|
PR_CREATED_AT: ${{ github.event.pull_request.created_at }}
|
|
358
|
-
SERVER: ${{ vars.MOKOGIT_URL || 'https://git.
|
|
358
|
+
SERVER: ${{ vars.MOKOGIT_URL || 'https://git.mokoenterprises.us' }}
|
|
359
359
|
REPO: ${{ github.repository }}
|
|
360
360
|
TOKEN: ${{ github.token }}
|
|
361
361
|
DOCS_WIKI_WINDOW: ${{ vars.DOCS_WIKI_WINDOW || 'pr' }}
|
|
@@ -467,7 +467,7 @@ jobs:
|
|
|
467
467
|
- name: Remind to update the wiki
|
|
468
468
|
env:
|
|
469
469
|
TOKEN: ${{ github.token }}
|
|
470
|
-
SERVER: ${{ vars.MOKOGIT_URL || 'https://git.
|
|
470
|
+
SERVER: ${{ vars.MOKOGIT_URL || 'https://git.mokoenterprises.us' }}
|
|
471
471
|
REPO: ${{ github.repository }}
|
|
472
472
|
PR: ${{ github.event.pull_request.number }}
|
|
473
473
|
run: |
|
|
@@ -475,7 +475,7 @@ jobs:
|
|
|
475
475
|
{
|
|
476
476
|
echo "## Wiki Update Reminder"
|
|
477
477
|
echo ""
|
|
478
|
-
echo "Docs are **wiki-first** at
|
|
478
|
+
echo "Docs are **wiki-first** at MokoEnterprises. If this change affects behavior, usage, configuration, or standards, update the repo wiki:"
|
|
479
479
|
echo ""
|
|
480
480
|
echo "- ${SERVER}/${REPO}/wiki"
|
|
481
481
|
echo ""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# MCP SDK Version Check
|
|
2
|
-
# Copyright (C) 2026 Moko
|
|
2
|
+
# Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
4
|
#
|
|
5
5
|
# Weekly check for MCP SDK updates. Creates an issue when a new version
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# MCP Tool Inventory
|
|
2
|
-
# Copyright (C) 2026 Moko
|
|
2
|
+
# Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
4
|
#
|
|
5
5
|
# Generates a tool inventory report on each push to main.
|
|
@@ -18,7 +18,7 @@ permissions:
|
|
|
18
18
|
contents: read
|
|
19
19
|
|
|
20
20
|
env:
|
|
21
|
-
MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.
|
|
21
|
+
MOKOGIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoenterprises.us' }}
|
|
22
22
|
GIT_ORG: ${{ vars.MOKOGIT_ORG || github.repository_owner }}
|
|
23
23
|
GIT_REPO: ${{ vars.MOKOGIT_REPO || github.event.repository.name }}
|
|
24
24
|
|
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
# Copyright (C) 2026 Moko
|
|
2
|
-
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
-
#
|
|
4
|
-
# Enforces branch merge policy:
|
|
5
|
-
# feature/* → dev only
|
|
6
|
-
# fix/* → dev only
|
|
7
|
-
# hotfix/* → dev or main (emergency)
|
|
8
|
-
# dev → main only
|
|
9
|
-
# alpha/* → dev only
|
|
10
|
-
# beta/* → dev only
|
|
11
|
-
# rc/* → main only
|
|
12
|
-
|
|
13
|
-
name: Branch Policy Check
|
|
14
|
-
|
|
15
|
-
on:
|
|
16
|
-
pull_request:
|
|
17
|
-
types: [opened, synchronize, reopened, edited]
|
|
18
|
-
|
|
19
|
-
jobs:
|
|
20
|
-
check-target:
|
|
21
|
-
name: Verify merge target
|
|
22
|
-
runs-on: ubuntu-latest
|
|
23
|
-
steps:
|
|
24
|
-
- name: Check branch policy
|
|
25
|
-
env:
|
|
26
|
-
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
|
27
|
-
run: |
|
|
28
|
-
HEAD="${{ github.head_ref }}"
|
|
29
|
-
BASE="${{ github.base_ref }}"
|
|
30
|
-
|
|
31
|
-
echo "PR: ${HEAD} → ${BASE}"
|
|
32
|
-
|
|
33
|
-
ALLOWED=true
|
|
34
|
-
REASON=""
|
|
35
|
-
|
|
36
|
-
case "$HEAD" in
|
|
37
|
-
feature/*|feat/*)
|
|
38
|
-
if [ "$BASE" != "dev" ]; then
|
|
39
|
-
ALLOWED=false
|
|
40
|
-
REASON="Feature branches must target 'dev', not '${BASE}'"
|
|
41
|
-
fi
|
|
42
|
-
;;
|
|
43
|
-
fix/*|bugfix/*)
|
|
44
|
-
if [ "$BASE" != "dev" ]; then
|
|
45
|
-
ALLOWED=false
|
|
46
|
-
REASON="Fix branches must target 'dev', not '${BASE}'"
|
|
47
|
-
fi
|
|
48
|
-
;;
|
|
49
|
-
hotfix/*)
|
|
50
|
-
if [ "$BASE" != "dev" ] && [ "$BASE" != "$DEFAULT_BRANCH" ]; then
|
|
51
|
-
ALLOWED=false
|
|
52
|
-
REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'"
|
|
53
|
-
fi
|
|
54
|
-
;;
|
|
55
|
-
alpha/*|beta/*)
|
|
56
|
-
if [ "$BASE" != "dev" ]; then
|
|
57
|
-
ALLOWED=false
|
|
58
|
-
REASON="Pre-release branches must target 'dev', not '${BASE}'"
|
|
59
|
-
fi
|
|
60
|
-
;;
|
|
61
|
-
rc/*)
|
|
62
|
-
if [ "$BASE" != "$DEFAULT_BRANCH" ]; then
|
|
63
|
-
ALLOWED=false
|
|
64
|
-
REASON="Release candidate branches must target 'main', not '${BASE}'"
|
|
65
|
-
fi
|
|
66
|
-
;;
|
|
67
|
-
dev)
|
|
68
|
-
if [ "$BASE" != "$DEFAULT_BRANCH" ]; then
|
|
69
|
-
ALLOWED=false
|
|
70
|
-
REASON="Dev branch can only merge into 'main', not '${BASE}'"
|
|
71
|
-
fi
|
|
72
|
-
;;
|
|
73
|
-
esac
|
|
74
|
-
|
|
75
|
-
if [ "$ALLOWED" = false ]; then
|
|
76
|
-
echo "::error::${REASON}"
|
|
77
|
-
echo ""
|
|
78
|
-
echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY
|
|
79
|
-
echo "" >> $GITHUB_STEP_SUMMARY
|
|
80
|
-
echo "${REASON}" >> $GITHUB_STEP_SUMMARY
|
|
81
|
-
echo "" >> $GITHUB_STEP_SUMMARY
|
|
82
|
-
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
|
|
83
|
-
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
|
84
|
-
echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
|
85
|
-
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
|
|
86
|
-
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
|
87
|
-
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
|
88
|
-
exit 1
|
|
89
|
-
fi
|
|
90
|
-
|
|
91
|
-
echo "Branch policy: OK (${HEAD} → ${BASE})"
|
|
92
|
-
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
|
|
1
|
+
# Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
#
|
|
4
|
+
# Enforces branch merge policy:
|
|
5
|
+
# feature/* → dev only
|
|
6
|
+
# fix/* → dev only
|
|
7
|
+
# hotfix/* → dev or main (emergency)
|
|
8
|
+
# dev → main only
|
|
9
|
+
# alpha/* → dev only
|
|
10
|
+
# beta/* → dev only
|
|
11
|
+
# rc/* → main only
|
|
12
|
+
|
|
13
|
+
name: Branch Policy Check
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
pull_request:
|
|
17
|
+
types: [opened, synchronize, reopened, edited]
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
check-target:
|
|
21
|
+
name: Verify merge target
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- name: Check branch policy
|
|
25
|
+
env:
|
|
26
|
+
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
|
27
|
+
run: |
|
|
28
|
+
HEAD="${{ github.head_ref }}"
|
|
29
|
+
BASE="${{ github.base_ref }}"
|
|
30
|
+
|
|
31
|
+
echo "PR: ${HEAD} → ${BASE}"
|
|
32
|
+
|
|
33
|
+
ALLOWED=true
|
|
34
|
+
REASON=""
|
|
35
|
+
|
|
36
|
+
case "$HEAD" in
|
|
37
|
+
feature/*|feat/*)
|
|
38
|
+
if [ "$BASE" != "dev" ]; then
|
|
39
|
+
ALLOWED=false
|
|
40
|
+
REASON="Feature branches must target 'dev', not '${BASE}'"
|
|
41
|
+
fi
|
|
42
|
+
;;
|
|
43
|
+
fix/*|bugfix/*)
|
|
44
|
+
if [ "$BASE" != "dev" ]; then
|
|
45
|
+
ALLOWED=false
|
|
46
|
+
REASON="Fix branches must target 'dev', not '${BASE}'"
|
|
47
|
+
fi
|
|
48
|
+
;;
|
|
49
|
+
hotfix/*)
|
|
50
|
+
if [ "$BASE" != "dev" ] && [ "$BASE" != "$DEFAULT_BRANCH" ]; then
|
|
51
|
+
ALLOWED=false
|
|
52
|
+
REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'"
|
|
53
|
+
fi
|
|
54
|
+
;;
|
|
55
|
+
alpha/*|beta/*)
|
|
56
|
+
if [ "$BASE" != "dev" ]; then
|
|
57
|
+
ALLOWED=false
|
|
58
|
+
REASON="Pre-release branches must target 'dev', not '${BASE}'"
|
|
59
|
+
fi
|
|
60
|
+
;;
|
|
61
|
+
rc/*)
|
|
62
|
+
if [ "$BASE" != "$DEFAULT_BRANCH" ]; then
|
|
63
|
+
ALLOWED=false
|
|
64
|
+
REASON="Release candidate branches must target 'main', not '${BASE}'"
|
|
65
|
+
fi
|
|
66
|
+
;;
|
|
67
|
+
dev)
|
|
68
|
+
if [ "$BASE" != "$DEFAULT_BRANCH" ]; then
|
|
69
|
+
ALLOWED=false
|
|
70
|
+
REASON="Dev branch can only merge into 'main', not '${BASE}'"
|
|
71
|
+
fi
|
|
72
|
+
;;
|
|
73
|
+
esac
|
|
74
|
+
|
|
75
|
+
if [ "$ALLOWED" = false ]; then
|
|
76
|
+
echo "::error::${REASON}"
|
|
77
|
+
echo ""
|
|
78
|
+
echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY
|
|
79
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
80
|
+
echo "${REASON}" >> $GITHUB_STEP_SUMMARY
|
|
81
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
82
|
+
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
|
|
83
|
+
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
|
84
|
+
echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
|
85
|
+
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
|
|
86
|
+
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
|
87
|
+
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
echo "Branch policy: OK (${HEAD} → ${BASE})"
|
|
92
|
+
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
|