Public Access
Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e5a3b902c | |||
| 08130d1bf3 | |||
| 4009d68a7a | |||
| 2312c95dba | |||
| 7a06bcf403 | |||
| a8baf355c4 | |||
| 9520ce3bb8 | |||
| 120dbe3be2 | |||
| 657e6013de | |||
| c7a26c1ce1 | |||
| bdbabd9886 | |||
| e061537404 | |||
| 4637dab159 | |||
| b885f12ece | |||
| e99881ad65 | |||
| fd3c847fb1 | |||
| 424fe658ba | |||
| 0ff87f3631 | |||
| 3164f21ba9 | |||
| 713cb61efc | |||
| fa342a278b | |||
| d91baf996e | |||
| 2100a3bec7 | |||
| 8d772df3e7 | |||
| 3972e4fb84 | |||
| 86b8449eba | |||
| 601451867d | |||
| 09ffcdce49 | |||
| a49e0d7b2a | |||
| bc1a111775 | |||
| 98c41c3a53 | |||
| 52ebcde1ff | |||
| ded17c34b8 | |||
| 1b79de20a9 | |||
| bba1cbc845 | |||
| 3f956f5661 | |||
| 9036d20898 | |||
| 7c1edcf813 | |||
| fec5a48b4a | |||
| 468b962aaa | |||
| cc66a79fea | |||
| 660ae5bed4 | |||
| 5ffaf95d20 | |||
| ddfb479bc2 | |||
| 13e2729124 | |||
| 05ee59d75e | |||
| c4bb08073b | |||
| b32556fdef | |||
| c57a7e4b96 | |||
| 5846ccc3e8 | |||
| 6ba0208571 | |||
| 759b04e088 | |||
| 4dfa2d9722 | |||
| 590ad7337a | |||
| c607966e83 | |||
| 97f2226288 | |||
| b1f2c391cb | |||
| 7e4d88eea5 | |||
| 9fdd6c5cf9 | |||
| 113af457d9 | |||
| abd67f6c43 | |||
| d3dce5d24e | |||
| c9592d8e78 | |||
| 6bf6251705 | |||
| 520acd4fee | |||
| a6c4fe0688 | |||
| 3c420284e3 | |||
| 66543c14d3 | |||
| d9908e264a | |||
| 9206f716e1 | |||
| 84633e22e5 | |||
| 510d1248ab | |||
| 8e30fa54b3 | |||
| 1aaec7d8ff | |||
| 5efdaf6c05 | |||
| 448e11568b | |||
| dce0fe0994 | |||
| 2dc115327b | |||
| 4715dba7fc | |||
| 5ab1782715 | |||
| 08db79da17 | |||
| c94564cec6 | |||
| ac52a697e5 | |||
| 57b458b79a | |||
| 2a84b38194 | |||
| 9d9b78b4e9 | |||
| 092543db1c | |||
| 2286514c59 | |||
| 6993f30513 | |||
| 0b30cc5a6d | |||
| 7b0c239dd2 | |||
| cf278cf914 | |||
| fdeacc73c5 | |||
| 117bba7765 | |||
| 65a11a19ac | |||
| fae2eab41b | |||
| f91b05287a | |||
| 43fd64a15b | |||
| 15074fb0ae | |||
| 415b5403be | |||
| 84fdb39d20 | |||
| 44b9e991b5 | |||
| 9b647b2540 | |||
| cedf10bdff | |||
| 8422292b46 | |||
| b1f3487a43 | |||
| c8f346e069 | |||
| 0da62a99e3 | |||
| 60c8309067 | |||
| 12f7ab7e30 | |||
| ccff69d48c | |||
| 8ab44feed5 | |||
| 483a22a13c | |||
| a3b1fb1f11 | |||
| 5e69242320 | |||
| a183787db6 | |||
| 13ab870418 | |||
| 8fd324f60e | |||
| 3ebfe2da8a | |||
| c87125ab10 | |||
| 4e3e9b7899 | |||
| 5a918e2d1f | |||
| 2d856c8846 | |||
| 200956b91a | |||
| bb24a58903 | |||
| 8cea26d748 | |||
| eb557302ce | |||
| 7e555eacf0 | |||
| 1d897ba115 | |||
| 8e6aaaff88 | |||
| fef27eb5d1 | |||
| 6d8e09827d |
@@ -124,7 +124,8 @@ jobs:
|
||||
"rule_name": "main",
|
||||
"enable_push": true,
|
||||
"enable_push_whitelist": true,
|
||||
"push_whitelist_usernames": ["jmiller"],
|
||||
"push_whitelist_usernames": ["jmiller", "moko-deploy"],
|
||||
"push_whitelist_actions_user": true,
|
||||
"enable_force_push": false,
|
||||
"enable_force_push_allowlist": false,
|
||||
"force_push_allowlist_usernames": [],
|
||||
@@ -140,10 +141,12 @@ jobs:
|
||||
"rule_name": "dev",
|
||||
"enable_push": true,
|
||||
"enable_push_whitelist": true,
|
||||
"push_whitelist_usernames": ["jmiller"],
|
||||
"enable_force_push": false,
|
||||
"enable_force_push_allowlist": false,
|
||||
"force_push_allowlist_usernames": [],
|
||||
"push_whitelist_usernames": ["jmiller", "moko-deploy"],
|
||||
"push_whitelist_actions_user": true,
|
||||
"enable_force_push": true,
|
||||
"enable_force_push_allowlist": true,
|
||||
"force_push_allowlist_usernames": ["jmiller", "moko-deploy"],
|
||||
"force_push_allowlist_actions_user": true,
|
||||
"enable_merge_whitelist": false,
|
||||
"required_approvals": 0,
|
||||
"block_on_rejected_reviews": false,
|
||||
@@ -154,10 +157,12 @@ jobs:
|
||||
"rule_name": "rc",
|
||||
"enable_push": true,
|
||||
"enable_push_whitelist": true,
|
||||
"push_whitelist_usernames": ["jmiller"],
|
||||
"enable_force_push": false,
|
||||
"enable_force_push_allowlist": false,
|
||||
"force_push_allowlist_usernames": [],
|
||||
"push_whitelist_usernames": ["jmiller", "moko-deploy"],
|
||||
"push_whitelist_actions_user": true,
|
||||
"enable_force_push": true,
|
||||
"enable_force_push_allowlist": true,
|
||||
"force_push_allowlist_usernames": ["jmiller", "moko-deploy"],
|
||||
"force_push_allowlist_actions_user": true,
|
||||
"enable_merge_whitelist": false,
|
||||
"required_approvals": 0,
|
||||
"block_on_rejected_reviews": false,
|
||||
@@ -168,7 +173,8 @@ jobs:
|
||||
"rule_name": "beta",
|
||||
"enable_push": true,
|
||||
"enable_push_whitelist": true,
|
||||
"push_whitelist_usernames": ["jmiller"],
|
||||
"push_whitelist_usernames": ["jmiller", "moko-deploy"],
|
||||
"push_whitelist_actions_user": true,
|
||||
"enable_force_push": false,
|
||||
"enable_force_push_allowlist": false,
|
||||
"force_push_allowlist_usernames": [],
|
||||
@@ -182,7 +188,8 @@ jobs:
|
||||
"rule_name": "alpha",
|
||||
"enable_push": true,
|
||||
"enable_push_whitelist": true,
|
||||
"push_whitelist_usernames": ["jmiller"],
|
||||
"push_whitelist_usernames": ["jmiller", "moko-deploy"],
|
||||
"push_whitelist_actions_user": true,
|
||||
"enable_force_push": false,
|
||||
"enable_force_push_allowlist": false,
|
||||
"force_push_allowlist_usernames": [],
|
||||
|
||||
@@ -1,66 +1,66 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogitea/workflows/auto-bump.yml
|
||||
# VERSION: 09.02.00
|
||||
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
|
||||
|
||||
name: "Universal: Auto Version Bump"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- rc
|
||||
- 'feature/**'
|
||||
- 'patch/**'
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
bump:
|
||||
name: Version Bump
|
||||
runs-on: release
|
||||
if: >-
|
||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
||||
!contains(github.event.head_commit.message, '[skip bump]') &&
|
||||
!startsWith(github.event.head_commit.message, 'Merge pull request')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup mokocli tools
|
||||
run: |
|
||||
if ! command -v composer &> /dev/null; then
|
||||
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
|
||||
fi
|
||||
if [ -d "/opt/mokocli/cli" ]; then
|
||||
echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
|
||||
else
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
|
||||
/tmp/mokocli
|
||||
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
|
||||
echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Bump version
|
||||
run: |
|
||||
php ${MOKO_CLI}/version_auto_bump.php \
|
||||
--path . --branch "${GITHUB_REF_NAME}" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogitea/workflows/auto-bump.yml
|
||||
# VERSION: 09.02.00
|
||||
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
|
||||
|
||||
name: "Universal: Auto Version Bump"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- rc
|
||||
- 'feature/**'
|
||||
- 'patch/**'
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
bump:
|
||||
name: Version Bump
|
||||
runs-on: release
|
||||
if: >-
|
||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
||||
!contains(github.event.head_commit.message, '[skip bump]') &&
|
||||
!startsWith(github.event.head_commit.message, 'Merge pull request')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup mokocli tools
|
||||
run: |
|
||||
if ! command -v composer &> /dev/null; then
|
||||
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
|
||||
fi
|
||||
if [ -d "/opt/mokocli/cli" ]; then
|
||||
echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
|
||||
else
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
|
||||
/tmp/mokocli
|
||||
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
|
||||
echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Bump version
|
||||
run: |
|
||||
php ${MOKO_CLI}/version_auto_bump.php \
|
||||
--path . --branch "${GITHUB_REF_NAME}" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
|
||||
# PATH: /templates/workflows/universal/auto-release.yml.template
|
||||
# VERSION: 05.00.00
|
||||
# VERSION: 05.01.00
|
||||
# BRIEF: Universal build & release � detects platform from manifest.xml
|
||||
#
|
||||
# +=======================================================================+
|
||||
@@ -27,9 +27,18 @@ name: "Universal: Build & Release"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, closed]
|
||||
types: [opened, synchronize, closed]
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.mokogitea/workflows/**'
|
||||
- '*.md'
|
||||
- 'wiki/**'
|
||||
- '.editorconfig'
|
||||
- '.gitignore'
|
||||
- '.gitattributes'
|
||||
- '.gitmessage'
|
||||
- 'LICENSE'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
action:
|
||||
@@ -43,7 +52,7 @@ on:
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||
|
||||
@@ -55,9 +64,14 @@ jobs:
|
||||
promote-rc:
|
||||
name: Promote to RC
|
||||
runs-on: release
|
||||
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
|
||||
if: >-
|
||||
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
|
||||
!startsWith(github.event.repository.name, 'Template-') &&
|
||||
(
|
||||
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
|
||||
(github.event.action == 'synchronize' && github.event.pull_request.merged != true) ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
|
||||
)
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -65,6 +79,7 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 1
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup mokocli tools
|
||||
env:
|
||||
@@ -80,7 +95,7 @@ jobs:
|
||||
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
|
||||
fi
|
||||
rm -rf /tmp/mokocli
|
||||
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
|
||||
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoCLI.git
|
||||
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
|
||||
cd /tmp/mokocli
|
||||
composer install --no-dev --no-interaction --quiet
|
||||
@@ -89,11 +104,39 @@ jobs:
|
||||
|
||||
- name: Rename branch to rc
|
||||
run: |
|
||||
php ${MOKO_CLI}/branch_rename.php \
|
||||
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
||||
--pr "${{ github.event.pull_request.number }}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
AUTH="Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
FROM="${{ github.event.pull_request.head.ref || 'dev' }}"
|
||||
PR="${{ github.event.pull_request.number }}"
|
||||
|
||||
# Resolve the source branch HEAD commit.
|
||||
SRC_JSON=$(curl -sf -H "$AUTH" "${API_BASE}/branches/${FROM}") \
|
||||
|| { echo "::error::Source branch ${FROM} not found"; exit 1; }
|
||||
SRC_SHA=$(printf '%s' "$SRC_JSON" | python3 -c "import sys, json; print(json.load(sys.stdin)['commit']['id'])" 2>/dev/null || true)
|
||||
[ -n "$SRC_SHA" ] || { echo "::error::Could not resolve HEAD of ${FROM}"; exit 1; }
|
||||
|
||||
# Point rc at the source commit. If rc already exists (a protected branch that
|
||||
# cannot be deleted), force-update its ref in place instead of delete+recreate:
|
||||
# deleting a protected branch fails, which then makes the recreate return HTTP 409.
|
||||
if curl -sf -o /dev/null -H "$AUTH" "${API_BASE}/branches/rc"; then
|
||||
echo "rc exists - force-updating to ${FROM} (${SRC_SHA})"
|
||||
curl -sf -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
|
||||
"${API_BASE}/git/refs/heads/rc" -d "{\"sha\":\"${SRC_SHA}\",\"force\":true}" \
|
||||
|| { echo "::error::Failed to force-update rc (CI token needs force-push on the protected rc branch)"; exit 1; }
|
||||
else
|
||||
echo "Creating rc from ${FROM}"
|
||||
curl -sf -X POST -H "$AUTH" -H "Content-Type: application/json" \
|
||||
"${API_BASE}/branches" -d "{\"new_branch_name\":\"rc\",\"old_branch_name\":\"${FROM}\"}" \
|
||||
|| { echo "::error::Failed to create rc from ${FROM}"; exit 1; }
|
||||
fi
|
||||
|
||||
# Repoint the PR at rc, then delete the old source branch (non-fatal).
|
||||
if [ -n "$PR" ]; then
|
||||
curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
|
||||
"${API_BASE}/pulls/${PR}" -d '{"head":"rc"}' >/dev/null || true
|
||||
fi
|
||||
curl -s -X DELETE -H "$AUTH" "${API_BASE}/branches/${FROM}" >/dev/null || true
|
||||
echo "Renamed ${FROM} -> rc"
|
||||
|
||||
- name: Checkout rc and configure git
|
||||
run: |
|
||||
@@ -111,7 +154,7 @@ jobs:
|
||||
|
||||
- name: Update RC release notes from CHANGELOG.md
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
# Extract [Unreleased] section from changelog
|
||||
@@ -153,9 +196,13 @@ jobs:
|
||||
release:
|
||||
name: Build & Release Pipeline
|
||||
runs-on: release
|
||||
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
|
||||
if: >-
|
||||
github.event.pull_request.merged == true ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
|
||||
!startsWith(github.event.repository.name, 'Template-') &&
|
||||
(
|
||||
github.event.pull_request.merged == true ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
|
||||
)
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -163,6 +210,7 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Configure git for bot pushes
|
||||
run: |
|
||||
@@ -198,7 +246,7 @@ jobs:
|
||||
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
|
||||
fi
|
||||
rm -rf /tmp/mokocli
|
||||
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
|
||||
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoCLI.git
|
||||
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
|
||||
cd /tmp/mokocli
|
||||
composer install --no-dev --no-interaction --quiet
|
||||
@@ -259,7 +307,7 @@ jobs:
|
||||
!startsWith(steps.platform.outputs.platform, 'joomla')
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
SEMVER_TAG="v${VERSION}"
|
||||
|
||||
@@ -284,7 +332,7 @@ jobs:
|
||||
|
||||
- name: Update release notes and promote changelog
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
# Get the stable release info (version and ID)
|
||||
@@ -353,7 +401,7 @@ jobs:
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/release_mirror.php \
|
||||
--version "$VERSION" --tag "$RELEASE_TAG" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||
@@ -382,7 +430,7 @@ jobs:
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
# Delete rc branch (ephemeral — created by promote-rc)
|
||||
@@ -406,7 +454,7 @@ jobs:
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
BRANCH_NAME="version/${VERSION}"
|
||||
@@ -427,7 +475,7 @@ jobs:
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/version_reset_dev.php \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
|
||||
--branch dev --path . 2>&1 || true
|
||||
@@ -453,5 +501,5 @@ jobs:
|
||||
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Release | [View](${MOKOGITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
@@ -33,7 +33,8 @@ jobs:
|
||||
run: |
|
||||
BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
||||
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
|
||||
# URL-encode the branch name's slashes (no PHP dependency on the runner)
|
||||
ENCODED=$(printf '%s' "${BRANCH}" | sed 's|/|%2F|g')
|
||||
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Enforce branch protection rules across all org repos.
|
||||
# Runs weekly and on manual dispatch.
|
||||
|
||||
name: "Org: Enforce Branch Protections"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * 1' # Every Monday at 6am UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Dry run (show changes without applying)'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
enforce:
|
||||
name: Enforce Branch Protections
|
||||
runs-on: release
|
||||
|
||||
steps:
|
||||
- name: Checkout MokoCLI
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
|
||||
- name: Setup PHP
|
||||
run: |
|
||||
if ! command -v php > /dev/null 2>&1; then
|
||||
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-curl > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
- name: Run branch protection enforcement
|
||||
run: |
|
||||
DRY_RUN=""
|
||||
if [ "${{ inputs.dry_run }}" = "true" ]; then
|
||||
DRY_RUN="--dry-run"
|
||||
fi
|
||||
php cli/branch_protect_org.php \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
--org "MokoConsulting" \
|
||||
$DRY_RUN
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Branch Protection Enforcement" >> $GITHUB_STEP_SUMMARY
|
||||
echo "All repos checked for main, dev, rc, beta, alpha protections" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Push whitelist: jmiller only" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -6,13 +6,19 @@
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.CI
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
||||
# PATH: /.gitea/workflows/ci-generic.yml
|
||||
# PATH: /.mokogitea/workflows/ci-generic.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js)
|
||||
|
||||
name: "Generic: Project CI"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
- dev/**
|
||||
- rc/**
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
@@ -26,6 +32,8 @@ jobs:
|
||||
lint:
|
||||
name: Lint & Validate
|
||||
runs-on: ubuntu-latest
|
||||
# Skip on template repos (Template-*) — they hold placeholder scaffolding, not buildable source.
|
||||
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -124,6 +132,9 @@ jobs:
|
||||
name: Tests
|
||||
runs-on: ubuntu-latest
|
||||
needs: lint
|
||||
# Run only when lint succeeded; always() forces evaluation so a skipped
|
||||
# lint (e.g. template repos) skips this job cleanly instead of hanging.
|
||||
if: ${{ always() && needs.lint.result == 'success' }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogitea/workflows/ci-issue-reporter.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Reusable workflow — creates/updates a Gitea issue when a CI gate fails.
|
||||
# Clones MokoCLI and runs cli/ci_issue_reporter.sh.
|
||||
|
||||
name: "Universal: CI Issue Reporter"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
gate:
|
||||
description: "CI gate name (e.g. PR Validation, Repository Health)"
|
||||
required: true
|
||||
type: string
|
||||
details:
|
||||
description: "Human-readable failure description"
|
||||
required: true
|
||||
type: string
|
||||
severity:
|
||||
description: "error or warning"
|
||||
required: false
|
||||
type: string
|
||||
default: "error"
|
||||
workflow:
|
||||
description: "Workflow name for the issue title"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
secrets:
|
||||
MOKOGITEA_TOKEN:
|
||||
required: true
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
report:
|
||||
name: "Report: ${{ inputs.gate }}"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone MokoCLI
|
||||
env:
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
git clone --depth 1 --filter=blob:none --sparse "${MOKOGITEA_URL}/MokoConsulting/MokoCLI.git" /tmp/mokocli
|
||||
cd /tmp/mokocli && git sparse-checkout set cli/ci_issue_reporter.sh
|
||||
|
||||
- name: Report CI failure
|
||||
env:
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
run: |
|
||||
chmod +x /tmp/mokocli/cli/ci_issue_reporter.sh
|
||||
/tmp/mokocli/cli/ci_issue_reporter.sh \
|
||||
--gate "${{ inputs.gate }}" \
|
||||
--details "${{ inputs.details }}" \
|
||||
--severity "${{ inputs.severity }}" \
|
||||
--workflow "${{ inputs.workflow }}"
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
|
||||
- name: "PHPCS (PSR-12)"
|
||||
run: |
|
||||
vendor/bin/phpcs --standard=phpcs.xml --report=summary --warning-severity=0 lib/ validate/ automation/ 2>&1 || {
|
||||
vendor/bin/phpcs --standard=phpcs.xml --report=summary --warning-severity=0 lib/ validate/ 2>&1 || {
|
||||
echo "::error::PHPCS found coding standard violations"
|
||||
echo "### PHPCS" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Coding standard violations detected. Run \`composer phpcs\` locally." >> $GITHUB_STEP_SUMMARY
|
||||
@@ -453,8 +453,8 @@ jobs:
|
||||
needs.governance.result == 'failure' ||
|
||||
needs.templates.result == 'failure')
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
run: |
|
||||
chmod +x automation/ci-issue-reporter.sh
|
||||
REPORTER="./automation/ci-issue-reporter.sh"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Maintenance
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# PATH: /.gitea/workflows/cleanup.yml
|
||||
# PATH: /.mokogitea/workflows/cleanup.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
|
||||
|
||||
@@ -21,7 +21,7 @@ permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
@@ -33,30 +33,30 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
|
||||
- name: Delete merged branches
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
echo "=== Merged Branch Cleanup ==="
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
|
||||
# List branches via API
|
||||
BRANCHES=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
|
||||
BRANCHES=$(curl -sS -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/branches?limit=50" | jq -r '.[].name')
|
||||
|
||||
DELETED=0
|
||||
for BRANCH in $BRANCHES; do
|
||||
# Skip protected branches
|
||||
case "$BRANCH" in
|
||||
main|master|develop|release/*|hotfix/*) continue ;;
|
||||
main|master|dev|develop|rc|beta|alpha|release|release/*|production|stable|staging|hotfix/*|version/*) continue ;;
|
||||
esac
|
||||
|
||||
# Check if branch is merged into main
|
||||
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
|
||||
echo " Deleting merged branch: ${BRANCH}"
|
||||
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
|
||||
curl -sS -X DELETE -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/branches/${BRANCH}" 2>/dev/null || true
|
||||
DELETED=$((DELETED + 1))
|
||||
fi
|
||||
@@ -66,20 +66,20 @@ jobs:
|
||||
|
||||
- name: Clean old workflow runs
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
echo "=== Workflow Run Cleanup ==="
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
# Get old completed runs
|
||||
RUNS=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
|
||||
RUNS=$(curl -sS -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/actions/runs?status=completed&limit=50" | \
|
||||
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
|
||||
|
||||
DELETED=0
|
||||
for RUN_ID in $RUNS; do
|
||||
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
|
||||
curl -sS -X DELETE -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
|
||||
DELETED=$((DELETED + 1))
|
||||
done
|
||||
|
||||
@@ -13,7 +13,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
@@ -49,18 +49,18 @@ jobs:
|
||||
- name: Verify Gitea registry
|
||||
run: |
|
||||
echo "Gitea Composer registry auto-publishes from tags."
|
||||
echo "Package available at: ${GITEA_URL}/api/packages/MokoConsulting/composer"
|
||||
echo "Package available at: ${MOKOGITEA_URL}/api/packages/MokoConsulting/composer"
|
||||
echo "Install: composer require mokoconsulting/mokocli"
|
||||
|
||||
# Packagist — notify of new version
|
||||
# Packagist — notify of new version (points to GitHub mirror which Packagist can access)
|
||||
- name: Notify Packagist
|
||||
if: secrets.PACKAGIST_TOKEN != ''
|
||||
if: ${{ secrets.PACKAGIST_TOKEN != '' }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
echo "Notifying Packagist of version ${VERSION}..."
|
||||
curl -sf -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"repository":{"url":"https://git.mokoconsulting.tech/MokoConsulting/mokocli"}}' \
|
||||
-d '{"repository":{"url":"https://github.com/mokoconsulting-tech/mokocli"}}' \
|
||||
"https://packagist.org/api/update-package?username=mokoconsulting&apiToken=${{ secrets.PACKAGIST_TOKEN }}" \
|
||||
&& echo "Packagist notified" \
|
||||
|| echo "::warning::Packagist notification failed (package may not be registered yet)"
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Deploy
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
|
||||
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
|
||||
# VERSION: 04.07.00
|
||||
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
|
||||
|
||||
name: "Universal: Deploy to Dev (Manual)"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
clear_remote:
|
||||
description: 'Delete all remote files before uploading'
|
||||
required: false
|
||||
default: 'false'
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: SFTP Deploy to Dev
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Setup PHP
|
||||
run: |
|
||||
php -v && composer --version
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
|
||||
/tmp/mokostandards-api 2>/dev/null || true
|
||||
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
|
||||
cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Check FTP configuration
|
||||
id: check
|
||||
env:
|
||||
HOST: ${{ vars.DEV_FTP_HOST }}
|
||||
PATH_VAR: ${{ vars.DEV_FTP_PATH }}
|
||||
PORT: ${{ vars.DEV_FTP_PORT }}
|
||||
run: |
|
||||
if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then
|
||||
echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy"
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
echo "host=$HOST" >> "$GITHUB_OUTPUT"
|
||||
|
||||
REMOTE="${PATH_VAR%/}"
|
||||
echo "remote=$REMOTE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
[ -z "$PORT" ] && PORT="22"
|
||||
echo "port=$PORT" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Deploy via SFTP
|
||||
if: steps.check.outputs.skip != 'true'
|
||||
env:
|
||||
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
|
||||
SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
|
||||
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
|
||||
run: |
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; }
|
||||
|
||||
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
|
||||
"${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \
|
||||
> /tmp/sftp-config.json
|
||||
|
||||
if [ -n "$SFTP_KEY" ]; then
|
||||
echo "$SFTP_KEY" > /tmp/deploy_key
|
||||
chmod 600 /tmp/deploy_key
|
||||
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
|
||||
else
|
||||
printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json
|
||||
fi
|
||||
|
||||
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
|
||||
[ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote)
|
||||
|
||||
PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/mokostandards-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
|
||||
else
|
||||
php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||
fi
|
||||
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check.outputs.skip }}" = "true" ]; then
|
||||
echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -5,7 +5,7 @@
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Automation
|
||||
# VERSION: 01.00.00
|
||||
# VERSION: 09.43.00
|
||||
# BRIEF: Auto-create feature branch when an issue is opened
|
||||
|
||||
name: "Universal: Issue Branch"
|
||||
@@ -19,7 +19,7 @@ permissions:
|
||||
issues: write
|
||||
|
||||
env:
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
create-branch:
|
||||
@@ -28,8 +28,8 @@ jobs:
|
||||
steps:
|
||||
- name: Create branch and comment
|
||||
run: |
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
ISSUE_NUM="${{ github.event.issue.number }}"
|
||||
ISSUE_TITLE="${{ github.event.issue.title }}"
|
||||
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
echo "Created branch: ${BRANCH}"
|
||||
|
||||
# Comment on issue with branch link
|
||||
REPO_URL="${GITEA_URL}/${{ github.repository }}"
|
||||
REPO_URL="${MOKOGITEA_URL}/${{ github.repository }}"
|
||||
BODY="Branch created: [\`${BRANCH}\`](${REPO_URL}/src/branch/${BRANCH})\n\n\`\`\`bash\ngit fetch origin\ngit checkout ${BRANCH}\n\`\`\`"
|
||||
|
||||
curl -sf -X POST \
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Notifications
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# PATH: /.gitea/workflows/notify.yml
|
||||
# PATH: /.mokogitea/workflows/notify.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Push notifications via ntfy on release success or workflow failure
|
||||
|
||||
@@ -15,9 +15,9 @@ name: "Universal: Notifications"
|
||||
on:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- "Joomla Build & Release"
|
||||
- "Joomla Extension CI"
|
||||
- "Deploy"
|
||||
- "Universal: Build & Release"
|
||||
- "Joomla: Extension CI"
|
||||
- "Generic: Project CI"
|
||||
types:
|
||||
- completed
|
||||
|
||||
|
||||
+528
-534
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||
# VERSION: 05.01.00
|
||||
# VERSION: 05.02.00
|
||||
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
|
||||
|
||||
name: "Universal: Pre-Release"
|
||||
@@ -48,9 +48,13 @@ jobs:
|
||||
build:
|
||||
name: "Build Pre-Release (${{ inputs.stability || github.ref_name }})"
|
||||
runs-on: release
|
||||
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
github.event_name == 'push'
|
||||
!startsWith(github.event.repository.name, 'Template-') &&
|
||||
(
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
github.event_name == 'push'
|
||||
)
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -59,6 +63,11 @@ jobs:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
ref: ${{ github.ref_name }}
|
||||
submodules: recursive
|
||||
|
||||
- name: Update submodules to main
|
||||
run: |
|
||||
git submodule foreach --quiet 'git checkout main && git pull --quiet origin main' 2>/dev/null || true
|
||||
|
||||
- name: Setup mokocli tools
|
||||
env:
|
||||
|
||||
@@ -29,12 +29,20 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Rename branch
|
||||
env:
|
||||
BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||
REPO: ${{ github.repository }}
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||
set -euo pipefail
|
||||
# BRANCH is attacker-controlled (PR head ref). Strict allowlist before ANY use.
|
||||
if ! printf '%s' "$BRANCH" | grep -Eq '^rc/[A-Za-z0-9._/-]+$'; then
|
||||
echo "::error::Refusing unsafe branch name: $BRANCH"; exit 1
|
||||
fi
|
||||
SUFFIX="${BRANCH#rc/}"
|
||||
DEV_BRANCH="dev/${SUFFIX}"
|
||||
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${REPO}/branches"
|
||||
|
||||
# Create dev/ branch from rc/ branch
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
|
||||
@@ -42,25 +50,22 @@ jobs:
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"new_branch_name\": \"${DEV_BRANCH}\", \"old_branch_name\": \"${BRANCH}\"}" \
|
||||
"${API}" 2>/dev/null || true)
|
||||
|
||||
if [ "$STATUS" = "201" ]; then
|
||||
echo "Created branch: ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Created branch: ${DEV_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"
|
||||
exit 1
|
||||
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"; exit 1
|
||||
fi
|
||||
|
||||
# Delete rc/ branch
|
||||
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
|
||||
# Read BRANCH from the environment inside PHP (getenv, no string interpolation -> no PHP injection)
|
||||
ENCODED=$(php -r 'echo rawurlencode(getenv("BRANCH"));')
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
"${API}/${ENCODED}" 2>/dev/null || true)
|
||||
|
||||
if [ "$STATUS" = "204" ]; then
|
||||
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Deleted branch: ${BRANCH}" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "::warning::Failed to delete ${BRANCH} (HTTP ${STATUS})"
|
||||
fi
|
||||
|
||||
echo "### RC Reverted" >> $GITHUB_STEP_SUMMARY
|
||||
echo "${BRANCH} → ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### RC Reverted" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "${BRANCH} → ${DEV_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,82 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# PATH: /.gitea/workflows/security-audit.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Dependency vulnerability scanning for composer and npm packages
|
||||
|
||||
name: "Universal: Security Audit"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * 1' # Weekly on Monday at 06:00 UTC
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'composer.json'
|
||||
- 'composer.lock'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
|
||||
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }}
|
||||
|
||||
jobs:
|
||||
audit:
|
||||
name: Dependency Audit
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Composer audit
|
||||
if: hashFiles('composer.lock') != ''
|
||||
run: |
|
||||
echo "=== Composer Security Audit ==="
|
||||
if ! command -v composer &> /dev/null; then
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq php-cli composer >/dev/null 2>&1
|
||||
fi
|
||||
composer audit --format=plain 2>&1 | tee /tmp/composer-audit.txt
|
||||
RESULT=$?
|
||||
if [ $RESULT -ne 0 ]; then
|
||||
echo "::warning::Composer vulnerabilities found"
|
||||
echo "composer_vulnerable=true" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "No known vulnerabilities in composer dependencies"
|
||||
fi
|
||||
|
||||
- name: NPM audit
|
||||
if: hashFiles('package-lock.json') != ''
|
||||
run: |
|
||||
echo "=== NPM Security Audit ==="
|
||||
npm audit --production 2>&1 | tee /tmp/npm-audit.txt || true
|
||||
if npm audit --production 2>&1 | grep -q "found 0 vulnerabilities"; then
|
||||
echo "No known vulnerabilities in npm dependencies"
|
||||
else
|
||||
echo "::warning::NPM vulnerabilities found"
|
||||
echo "npm_vulnerable=true" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Notify on vulnerabilities
|
||||
if: env.composer_vulnerable == 'true' || env.npm_vulnerable == 'true'
|
||||
run: |
|
||||
REPO="${{ github.event.repository.name }}"
|
||||
curl -sS \
|
||||
-H "Title: ${REPO} has vulnerable dependencies" \
|
||||
-H "Tags: lock,warning" \
|
||||
-H "Priority: high" \
|
||||
-d "Security audit found vulnerabilities. Review dependency updates." \
|
||||
"${NTFY_URL}/${NTFY_TOPIC}" || true
|
||||
@@ -22,7 +22,7 @@ permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Sync Workflows to Repos
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '.mokogitea/workflows/**'
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout mokocli
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: MokoConsulting/mokocli
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
|
||||
- name: Setup PHP
|
||||
uses: https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/raw/branch/main/actions/setup-php@v1
|
||||
with:
|
||||
php-version: '8.1'
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --no-dev --no-interaction
|
||||
|
||||
- name: Sync workflows to generic repos
|
||||
run: php automation/bulk_sync.php --platform generic --org MokoConsulting --workflows-only --auto-merge --token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
env:
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow.Template
|
||||
# INGROUP: MokoStandards.CI
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
|
||||
# PATH: /.mokogitea/workflows/version-set.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Set or reset the extension version across all version-bearing files
|
||||
|
||||
name: "Joomla: Set Version"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version number (e.g. 01.00.00)"
|
||||
required: true
|
||||
type: string
|
||||
branch:
|
||||
description: "Branch to update (default: current)"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
set-version:
|
||||
name: Set Version to ${{ inputs.version }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Validate version format
|
||||
run: |
|
||||
VERSION="${{ inputs.version }}"
|
||||
if ! echo "$VERSION" | grep -qP '^\d{2}\.\d{2}\.\d{2}$'; then
|
||||
echo "::error::Invalid version format '${VERSION}' — expected XX.YY.ZZ (e.g. 01.00.00)"
|
||||
exit 1
|
||||
fi
|
||||
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN || github.token }}
|
||||
ref: ${{ inputs.branch || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Update manifest version
|
||||
run: |
|
||||
MANIFEST=""
|
||||
for XML_FILE in $(find . -maxdepth 3 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
|
||||
if grep -q "<extension" "$XML_FILE" 2>/dev/null; then
|
||||
MANIFEST="$XML_FILE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "::warning::No Joomla extension manifest found — skipping manifest update"
|
||||
else
|
||||
OLD_VER=$(grep -oP '<version>\K[^<]+' "$MANIFEST" | head -1)
|
||||
sed -i "s|<version>${OLD_VER}</version>|<version>${VERSION}</version>|" "$MANIFEST"
|
||||
echo "Manifest: ${OLD_VER} → ${VERSION} (${MANIFEST})"
|
||||
fi
|
||||
|
||||
- name: Update README.md version
|
||||
run: |
|
||||
if [ -f "README.md" ]; then
|
||||
if grep -qP '^\s*VERSION:\s*\d' README.md; then
|
||||
sed -i -E "s/(VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" README.md
|
||||
echo "README.md version updated to ${VERSION}"
|
||||
else
|
||||
echo "::warning::No VERSION line found in README.md — skipping"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Update CHANGELOG.md
|
||||
run: |
|
||||
if [ -f "CHANGELOG.md" ]; then
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
# Check if this version already has an entry
|
||||
if grep -q "^\#\# \[${VERSION}\]" CHANGELOG.md; then
|
||||
echo "CHANGELOG.md already has entry for ${VERSION} — skipping"
|
||||
else
|
||||
# Insert new version entry after [Unreleased] or at the top after header
|
||||
if grep -q '^\#\# \[Unreleased\]' CHANGELOG.md; then
|
||||
sed -i "/^\#\# \[Unreleased\]/a\\\\n## [${VERSION}] --- ${DATE}" CHANGELOG.md
|
||||
else
|
||||
sed -i "/^\# Changelog/a\\\\n## [Unreleased]\n\n## [${VERSION}] --- ${DATE}" CHANGELOG.md
|
||||
fi
|
||||
echo "CHANGELOG.md: added entry for ${VERSION}"
|
||||
fi
|
||||
else
|
||||
echo "::warning::No CHANGELOG.md found — skipping"
|
||||
fi
|
||||
|
||||
- name: Update FILE INFORMATION blocks
|
||||
run: |
|
||||
# Update VERSION in file header blocks (# VERSION: XX.YY.ZZ)
|
||||
find . -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" -o -name "*.php" -o -name "*.md" \) \
|
||||
-not -path "./.git/*" -not -path "./vendor/*" -print0 2>/dev/null | \
|
||||
while IFS= read -r -d '' FILE; do
|
||||
if head -20 "$FILE" | grep -qP '^\s*#?\s*VERSION:\s*\d{2}\.\d{2}\.\d{2}'; then
|
||||
sed -i -E "s/(#?\s*VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" "$FILE"
|
||||
echo "Updated FILE INFORMATION VERSION in ${FILE}"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Commit and push
|
||||
run: |
|
||||
git config user.name "Moko Consulting [bot]"
|
||||
git config user.email "hello@mokoconsulting.tech"
|
||||
git add -A
|
||||
if git diff --cached --quiet; then
|
||||
echo "No version changes detected — nothing to commit"
|
||||
else
|
||||
git commit -m "chore: set version to ${VERSION} [skip bump]
|
||||
|
||||
Authored-by: Moko Consulting"
|
||||
git push
|
||||
echo "### Version Set" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Version updated to \`${VERSION}\` on branch \`${GITHUB_REF_NAME}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -13,6 +13,7 @@
|
||||
name: "Universal: Workflow Sync Trigger"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
@@ -26,8 +27,9 @@ jobs:
|
||||
name: Sync workflows to live repos
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event.pull_request.merged == true &&
|
||||
!contains(github.event.pull_request.title, '[skip sync]')
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.pull_request.merged == true &&
|
||||
!contains(github.event.pull_request.title, '[skip sync]'))
|
||||
|
||||
steps:
|
||||
- name: Determine platform from repo name
|
||||
@@ -49,8 +51,14 @@ jobs:
|
||||
env:
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
GITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
git clone --depth 1 "${GITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
|
||||
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
git clone --depth 1 "${MOKOGITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
|
||||
|
||||
- name: Install PHP
|
||||
run: |
|
||||
if ! command -v php &> /dev/null; then
|
||||
apt-get update -qq && apt-get install -y -qq php-cli php-json php-curl > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
+9
-6
@@ -4,7 +4,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
FILE INFORMATION
|
||||
DEFGROUP: MokoCli.Root
|
||||
INGROUP: MokoCli
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
PATH: /CHANGELOG.md
|
||||
BRIEF: Release changelog
|
||||
-->
|
||||
@@ -12,12 +12,15 @@ BRIEF: Release changelog
|
||||
# Changelog
|
||||
## [Unreleased]
|
||||
|
||||
## [09.38.00] --- 2026-06-21
|
||||
## [09.43.00] --- 2026-07-05
|
||||
|
||||
## [09.38.00] --- 2026-06-21
|
||||
## [09.42.00] --- 2026-07-04
|
||||
|
||||
## [09.38.00] --- 2026-06-21
|
||||
## [09.42.00] --- 2026-07-04
|
||||
|
||||
## [09.37.00] --- 2026-06-21
|
||||
### Fixed
|
||||
- branch_rename.php: force-update the target branch ref in place instead of delete+recreate, so `moko repo:rename-branch` works when the target (e.g. `rc`) is a protected branch that cannot be deleted — previously the delete silently failed and the recreate returned HTTP 409
|
||||
|
||||
## [09.37.00] --- 2026-06-21
|
||||
## [09.42.00] --- 2026-07-04
|
||||
|
||||
## [09.42.00] --- 2026-07-04
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
FILE INFORMATION
|
||||
DEFGROUP: MokoPlatform.Root
|
||||
INGROUP: MokoPlatform
|
||||
DEFGROUP: MokoCLI.Root
|
||||
INGROUP: MokoCLI
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
PATH: /PLUGIN_SCRIPTS.md
|
||||
BRIEF: Plugin system CLI documentation
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
FILE INFORMATION
|
||||
DEFGROUP: MokoPlatform.Root
|
||||
INGROUP: MokoPlatform
|
||||
DEFGROUP: MokoCLI.Root
|
||||
INGROUP: MokoCLI
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
PATH: /README.md
|
||||
VERSION: 09.38.00
|
||||
VERSION: 09.43.00
|
||||
BRIEF: Project overview and documentation
|
||||
-->
|
||||
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
FILE INFORMATION
|
||||
DEFGROUP: MokoPlatform.Index
|
||||
INGROUP: MokoPlatform.Analysis
|
||||
DEFGROUP: MokoCLI.Index
|
||||
INGROUP: MokoCLI.Analysis
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
PATH: /analysis/index.md
|
||||
BRIEF: Analysis directory index
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.CLI
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.CLI
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/audit_query.php
|
||||
* BRIEF: Search, filter, and export audit logs
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* @package MokoCLI
|
||||
* @subpackage cli
|
||||
* @author Moko Consulting <hello@mokoconsulting.tech>
|
||||
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
||||
* @license GNU General Public License version 3 or later; see LICENSE
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Enforce branch protection rules across all repos in the org.
|
||||
*
|
||||
* Usage:
|
||||
* php cli/branch_protect_org.php --token TOKEN [--org MokoConsulting] [--dry-run]
|
||||
*
|
||||
* Branch flow: feature/* -> dev -> rc -> main
|
||||
* main, dev, rc: push whitelist only (no direct push)
|
||||
* alpha, beta: push whitelist only (pre-release)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$options = getopt('', ['token:', 'org:', 'api-base:', 'dry-run', 'help']);
|
||||
|
||||
if (isset($options['help']) || empty($options['token'])) {
|
||||
echo "Usage: php cli/branch_protect_org.php --token TOKEN [--org ORG] [--api-base URL] [--dry-run]\n";
|
||||
echo "\n";
|
||||
echo "Options:\n";
|
||||
echo " --token Gitea API token (required)\n";
|
||||
echo " --org Organization name (default: MokoConsulting)\n";
|
||||
echo " --api-base API base URL (default: https://git.mokoconsulting.tech/api/v1)\n";
|
||||
echo " --dry-run Show what would be changed without making changes\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$token = $options['token'];
|
||||
$org = $options['org'] ?? 'MokoConsulting';
|
||||
$apiBase = rtrim($options['api-base'] ?? 'https://git.mokoconsulting.tech/api/v1', '/');
|
||||
$dryRun = isset($options['dry-run']);
|
||||
|
||||
// Protected branches and their rules
|
||||
$branchRules = [
|
||||
// Primary branches (flow: feature/* -> dev -> rc -> main)
|
||||
'main' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
'dev' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
'rc' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
'beta' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
'alpha' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
|
||||
// Synonyms (prevent bypass via alternate names)
|
||||
'master' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
'develop' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
'release' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
'production' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
'stable' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
'staging' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
|
||||
];
|
||||
|
||||
function apiRequest(string $method, string $url, string $token, ?array $body = null): array
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Authorization: token ' . $token,
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json',
|
||||
],
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
]);
|
||||
|
||||
if ($body !== null) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
return [
|
||||
'status' => $httpCode,
|
||||
'data' => json_decode($response, true) ?: [],
|
||||
];
|
||||
}
|
||||
|
||||
// 1. List all org repos
|
||||
echo "Fetching repos for {$org}...\n";
|
||||
$page = 1;
|
||||
$repos = [];
|
||||
|
||||
do {
|
||||
$result = apiRequest('GET', "{$apiBase}/orgs/{$org}/repos?limit=50&page={$page}", $token);
|
||||
$batch = $result['data'];
|
||||
$repos = array_merge($repos, $batch);
|
||||
$page++;
|
||||
} while (count($batch) === 50);
|
||||
|
||||
echo sprintf("Found %d repos\n\n", count($repos));
|
||||
|
||||
$summary = ['protected' => 0, 'added' => 0, 'skipped' => 0, 'errors' => 0];
|
||||
|
||||
foreach ($repos as $repo) {
|
||||
$repoName = $repo['name'];
|
||||
|
||||
if ($repo['archived'] ?? false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get existing protections
|
||||
$existing = apiRequest('GET', "{$apiBase}/repos/{$org}/{$repoName}/branch_protections", $token);
|
||||
$existingNames = array_map(fn($p) => $p['branch_name'] ?? '', $existing['data'] ?: []);
|
||||
|
||||
$added = [];
|
||||
$skipped = [];
|
||||
|
||||
foreach ($branchRules as $branch => $rules) {
|
||||
if (in_array($branch, $existingNames, true)) {
|
||||
$skipped[] = $branch;
|
||||
$summary['skipped']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
$added[] = $branch;
|
||||
$summary['added']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$body = array_merge($rules, ['branch_name' => $branch]);
|
||||
$result = apiRequest('POST', "{$apiBase}/repos/{$org}/{$repoName}/branch_protections", $token, $body);
|
||||
|
||||
if ($result['status'] >= 200 && $result['status'] < 300) {
|
||||
$added[] = $branch;
|
||||
$summary['added']++;
|
||||
} elseif ($result['status'] === 422) {
|
||||
$skipped[] = $branch;
|
||||
$summary['skipped']++;
|
||||
} else {
|
||||
$added[] = "{$branch}(ERR:{$result['status']})";
|
||||
$summary['errors']++;
|
||||
}
|
||||
}
|
||||
|
||||
$summary['protected']++;
|
||||
|
||||
if (!empty($added)) {
|
||||
$prefix = $dryRun ? '[DRY-RUN] ' : '';
|
||||
echo sprintf(" %s%-35s added: %s\n", $prefix, $repoName, implode(', ', $added));
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
echo sprintf("Summary: %d repos, %d rules added, %d already existed, %d errors\n",
|
||||
$summary['protected'], $summary['added'], $summary['skipped'], $summary['errors']);
|
||||
|
||||
if ($dryRun) {
|
||||
echo "\n(Dry run - no changes made)\n";
|
||||
}
|
||||
+37
-17
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/branch_rename.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Rename a git branch via Gitea API (create new, update PR, delete old)
|
||||
*/
|
||||
|
||||
@@ -64,26 +64,46 @@ class BranchRenameCli extends CliFramework
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Step 2: Delete target branch if it already exists
|
||||
// Step 2: Point the target branch at the source commit.
|
||||
// If the target already exists (e.g. a protected `rc` branch that cannot be
|
||||
// deleted), force-update its ref in place instead of delete+recreate: deleting a
|
||||
// protected branch fails, which then makes the recreate return HTTP 409.
|
||||
$sourceSha = '';
|
||||
if (isset($check['body']['commit']['id']) && is_string($check['body']['commit']['id'])) {
|
||||
$sourceSha = $check['body']['commit']['id'];
|
||||
}
|
||||
$targetCheck = $this->apiRequest('GET', "{$apiBase}/branches/{$to}", $headers);
|
||||
if ($targetCheck['code'] === 200) {
|
||||
echo "Target branch '{$to}' already exists — deleting\n";
|
||||
echo "Target branch '{$to}' already exists - force-updating to {$from}
|
||||
";
|
||||
if (!$this->dryRun) {
|
||||
$this->apiRequest('DELETE', "{$apiBase}/branches/{$to}", $headers);
|
||||
if ($sourceSha === '') {
|
||||
$this->log('ERROR', "Cannot resolve HEAD commit of source '{$from}'");
|
||||
return 1;
|
||||
}
|
||||
$ref = $this->apiRequest('PATCH', "{$apiBase}/git/refs/heads/{$to}", $headers, [
|
||||
'sha' => $sourceSha,
|
||||
'force' => true,
|
||||
]);
|
||||
if ($ref['code'] < 200 || $ref['code'] >= 300) {
|
||||
$this->log('ERROR', "Failed to force-update '{$to}': HTTP {$ref['code']} (needs force-push perm)");
|
||||
$this->log('ERROR', json_encode($ref['body']));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Create new branch from source
|
||||
echo "Creating branch: {$to} (from {$from})\n";
|
||||
if (!$this->dryRun) {
|
||||
$create = $this->apiRequest('POST', "{$apiBase}/branches", $headers, [
|
||||
'new_branch_name' => $to,
|
||||
'old_branch_name' => $from,
|
||||
]);
|
||||
if ($create['code'] < 200 || $create['code'] >= 300) {
|
||||
$this->log('ERROR', "Failed to create branch '{$to}': HTTP {$create['code']}");
|
||||
$this->log('ERROR', json_encode($create['body']));
|
||||
return 1;
|
||||
} else {
|
||||
echo "Creating branch: {$to} (from {$from})
|
||||
";
|
||||
if (!$this->dryRun) {
|
||||
$create = $this->apiRequest('POST', "{$apiBase}/branches", $headers, [
|
||||
'new_branch_name' => $to,
|
||||
'old_branch_name' => $from,
|
||||
]);
|
||||
if ($create['code'] < 200 || $create['code'] >= 300) {
|
||||
$this->log('ERROR', "Failed to create branch '{$to}': HTTP {$create['code']}");
|
||||
$this->log('ERROR', json_encode($create['body']));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/bulk_workflow_push.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Push a workflow file to all governed repos via the Gitea Contents API
|
||||
*/
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/bulk_workflow_trigger.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Trigger a workflow across multiple repos at once
|
||||
*/
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Automation.CI
|
||||
# INGROUP: mokocli.CLI
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCLI
|
||||
# PATH: /cli/ci_issue_reporter.sh
|
||||
# VERSION: 10.00.00
|
||||
# BRIEF: Creates or updates a Gitea issue when a CI gate fails.
|
||||
# Deduplicates by searching open issues with the "ci-auto" label
|
||||
# whose title matches the gate. If a matching issue exists, a comment
|
||||
# is appended instead of opening a duplicate.
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Defaults ────────────────────────────────────────────────────────────────
|
||||
MOKOGITEA_URL="${MOKOGITEA_URL:-https://git.mokoconsulting.tech}"
|
||||
MOKOGITEA_TOKEN="${MOKOGITEA_TOKEN:-}"
|
||||
REPO="${GITHUB_REPOSITORY:-}"
|
||||
RUN_URL="${GITHUB_SERVER_URL:-${MOKOGITEA_URL}}/${REPO}/actions/runs/${GITHUB_RUN_ID:-0}"
|
||||
LABEL_NAME="ci-auto"
|
||||
LABEL_COLOR="#e11d48"
|
||||
|
||||
GATE=""
|
||||
DETAILS=""
|
||||
SEVERITY="error"
|
||||
WORKFLOW=""
|
||||
|
||||
# ── Parse arguments ─────────────────────────────────────────────────────────
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: ci_issue_reporter.sh --gate NAME --details TEXT [OPTIONS]
|
||||
|
||||
Required:
|
||||
--gate CI gate name (e.g. "Code Quality", "Self-Health")
|
||||
--details Human-readable failure description
|
||||
|
||||
Optional:
|
||||
--severity "error" (default) or "warning"
|
||||
--workflow Workflow name for the issue title
|
||||
--repo owner/repo (default: \$GITHUB_REPOSITORY)
|
||||
--run-url URL to the CI run (auto-detected from env)
|
||||
--token Gitea API token (default: \$MOKOGITEA_TOKEN)
|
||||
--url Gitea base URL (default: \$MOKOGITEA_URL)
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--gate) GATE="$2"; shift 2 ;;
|
||||
--details) DETAILS="$2"; shift 2 ;;
|
||||
--severity) SEVERITY="$2"; shift 2 ;;
|
||||
--workflow) WORKFLOW="$2"; shift 2 ;;
|
||||
--repo) REPO="$2"; shift 2 ;;
|
||||
--run-url) RUN_URL="$2"; shift 2 ;;
|
||||
--token) MOKOGITEA_TOKEN="$2"; shift 2 ;;
|
||||
--url) MOKOGITEA_URL="$2"; shift 2 ;;
|
||||
-h|--help) usage ;;
|
||||
*) echo "Unknown option: $1"; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z "$GATE" ]] && { echo "ERROR: --gate is required"; usage; }
|
||||
[[ -z "$DETAILS" ]] && { echo "ERROR: --details is required"; usage; }
|
||||
[[ -z "$MOKOGITEA_TOKEN" ]] && { echo "ERROR: MOKOGITEA_TOKEN not set"; exit 1; }
|
||||
[[ -z "$REPO" ]] && { echo "ERROR: GITHUB_REPOSITORY not set"; exit 1; }
|
||||
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${REPO}"
|
||||
|
||||
# ── Build title ─────────────────────────────────────────────────────────────
|
||||
if [[ -n "$WORKFLOW" ]]; then
|
||||
TITLE="[CI] ${WORKFLOW}: ${GATE} failed"
|
||||
else
|
||||
TITLE="[CI] ${GATE} failed"
|
||||
fi
|
||||
|
||||
# ── Ensure label exists ─────────────────────────────────────────────────────
|
||||
ensure_label() {
|
||||
local exists
|
||||
exists=$(curl -sf -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/labels" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$exists" == "200" ]]; then
|
||||
local found
|
||||
found=$(curl -sf \
|
||||
-H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/labels" 2>/dev/null \
|
||||
| grep -o "\"name\":\"${LABEL_NAME}\"" || true)
|
||||
|
||||
if [[ -z "$found" ]]; then
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/labels" \
|
||||
-d "{\"name\":\"${LABEL_NAME}\",\"color\":\"${LABEL_COLOR}\",\"description\":\"Auto-created by CI issue reporter\"}" \
|
||||
> /dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Search for existing open issue ──────────────────────────────────────────
|
||||
find_existing_issue() {
|
||||
local query
|
||||
query=$(printf '%s' "[CI] ${GATE}" | sed 's/ /%20/g; s/\[/%5B/g; s/\]/%5D/g')
|
||||
|
||||
local response
|
||||
response=$(curl -sf \
|
||||
-H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/issues?type=issues&state=open&labels=${LABEL_NAME}&q=${query}&limit=5" \
|
||||
2>/dev/null || echo "[]")
|
||||
|
||||
echo "$response" \
|
||||
| grep -oP '"number":\s*\K[0-9]+' \
|
||||
| head -1
|
||||
}
|
||||
|
||||
# ── Build issue body ────────────────────────────────────────────────────────
|
||||
build_body() {
|
||||
local severity_badge
|
||||
if [[ "$SEVERITY" == "error" ]]; then
|
||||
severity_badge="**Severity:** Error"
|
||||
else
|
||||
severity_badge="**Severity:** Warning"
|
||||
fi
|
||||
|
||||
cat <<BODY
|
||||
## CI Gate Failure: ${GATE}
|
||||
|
||||
${severity_badge}
|
||||
**Workflow:** ${WORKFLOW:-unknown}
|
||||
**Branch:** ${GITHUB_REF_NAME:-unknown}
|
||||
**Commit:** \`${GITHUB_SHA:0:8}\`
|
||||
**Run:** [View CI run](${RUN_URL})
|
||||
|
||||
### Details
|
||||
|
||||
${DETAILS}
|
||||
|
||||
### Resolution
|
||||
|
||||
Fix the issue described above and push a new commit. This issue will be closed automatically when the gate passes, or can be closed manually.
|
||||
|
||||
---
|
||||
*Auto-created by [ci-issue-reporter](${MOKOGITEA_URL}/MokoConsulting/MokoCLI/src/branch/main/cli/ci_issue_reporter.sh)*
|
||||
BODY
|
||||
}
|
||||
|
||||
# ── Build comment body (for existing issues) ────────────────────────────────
|
||||
build_comment() {
|
||||
cat <<COMMENT
|
||||
### CI failure recurrence
|
||||
|
||||
**Branch:** ${GITHUB_REF_NAME:-unknown}
|
||||
**Commit:** \`${GITHUB_SHA:0:8}\`
|
||||
**Run:** [View CI run](${RUN_URL})
|
||||
|
||||
${DETAILS}
|
||||
COMMENT
|
||||
}
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────────────
|
||||
ensure_label
|
||||
|
||||
EXISTING=$(find_existing_issue)
|
||||
|
||||
if [[ -n "$EXISTING" ]]; then
|
||||
COMMENT_BODY=$(build_comment)
|
||||
COMMENT_JSON=$(printf '%s' "$COMMENT_BODY" | python3 -c "
|
||||
import sys, json
|
||||
print(json.dumps({'body': sys.stdin.read()}))" 2>/dev/null)
|
||||
|
||||
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
|
||||
-H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues/${EXISTING}/comments" \
|
||||
-d "${COMMENT_JSON}" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$HTTP" == "201" ]]; then
|
||||
echo "Commented on existing issue #${EXISTING}"
|
||||
else
|
||||
echo "WARNING: Failed to comment on issue #${EXISTING} (HTTP ${HTTP})"
|
||||
fi
|
||||
else
|
||||
ISSUE_BODY=$(build_body)
|
||||
ISSUE_JSON=$(python3 -c "
|
||||
import sys, json
|
||||
body = sys.stdin.read()
|
||||
print(json.dumps({
|
||||
'title': sys.argv[1],
|
||||
'body': body,
|
||||
'labels': []
|
||||
}))" "$TITLE" <<< "$ISSUE_BODY" 2>/dev/null)
|
||||
|
||||
RESPONSE=$(curl -sf -X POST \
|
||||
-H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues" \
|
||||
-d "${ISSUE_JSON}" 2>/dev/null || echo "{}")
|
||||
|
||||
ISSUE_NUM=$(echo "$RESPONSE" | grep -oP '"number":\s*\K[0-9]+' | head -1)
|
||||
|
||||
if [[ -n "$ISSUE_NUM" ]]; then
|
||||
LABEL_ID=$(curl -sf \
|
||||
-H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/labels" 2>/dev/null \
|
||||
| grep -oP "\"id\":\s*\K[0-9]+(?=[^}]*\"name\":\s*\"${LABEL_NAME}\")" \
|
||||
| head -1 || true)
|
||||
|
||||
if [[ -n "$LABEL_ID" ]]; then
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues/${ISSUE_NUM}/labels" \
|
||||
-d "{\"labels\":[${LABEL_ID}]}" \
|
||||
> /dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
echo "Created issue #${ISSUE_NUM}: ${TITLE}"
|
||||
else
|
||||
echo "WARNING: Failed to create issue"
|
||||
echo "Response: ${RESPONSE}"
|
||||
fi
|
||||
fi
|
||||
@@ -12,7 +12,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/client_dashboard.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Generate unified client dashboard HTML
|
||||
*/
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/client_inventory.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Discover and list all client-waas repos with their server configuration status
|
||||
*/
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/client_provision.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Provision a new client environment end-to-end
|
||||
*/
|
||||
|
||||
@@ -104,7 +104,7 @@ class ClientProvisionCli extends CliFramework
|
||||
'variables' => 'setVariables',
|
||||
'secrets' => 'setSecrets',
|
||||
'monitoring' => 'setupMonitoring',
|
||||
'summary' => 'printSummary',
|
||||
'summary' => 'printProvisioningSummary',
|
||||
];
|
||||
|
||||
$exitCode = 0;
|
||||
@@ -383,7 +383,7 @@ class ClientProvisionCli extends CliFramework
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function printSummary(): int
|
||||
private function printProvisioningSummary(): int
|
||||
{
|
||||
$vars = $this->config['variables'] ?? [];
|
||||
$secrets = $this->config['secrets'] ?? [];
|
||||
|
||||
@@ -95,6 +95,10 @@ class CreateProjectCli extends CliFramework
|
||||
|
||||
$repoRoot = dirname(__DIR__, 2);
|
||||
$templatesDir = "{$repoRoot}/templates/projects";
|
||||
if (!is_dir($templatesDir)) {
|
||||
$this->log('ERROR', "Project templates directory not found: {$templatesDir}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$repos = [];
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.CLI
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.CLI
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/deploy_joomla.php
|
||||
* BRIEF: Smart Joomla deploy — routes files to correct server directories by extension type
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/grafana_dashboard.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Manage Grafana dashboards via API
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/joomla_build.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Build a Joomla extension ZIP from manifest — all types supported
|
||||
* NOTE: Called by pre-release and auto-release workflows.
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/joomla_metadata_validate.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Validate MokoGitea repo metadata against Joomla extension manifest XML
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/manifest_detect.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Auto-detect manifest fields from source files and optionally push to API
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/manifest_integrity.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Cross-check manifest API fields against repo contents across the org
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/manifest_licensing.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Ensure licensing tags (updateservers, dlid) in Joomla extension manifests
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/manifest_read.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Read repo metadata from Gitea manifest API, auto-detect the rest
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/platform_detect.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Auto-detect repository platform type and optionally update manifest
|
||||
*/
|
||||
|
||||
|
||||
+1
-14
@@ -40,7 +40,6 @@ class ReleaseCli extends CliFramework
|
||||
$bulkSyncFile = file_exists("{$repoRoot}/.mokogitea/workflows/bulk-repo-sync.yml")
|
||||
? "{$repoRoot}/.mokogitea/workflows/bulk-repo-sync.yml"
|
||||
: "{$repoRoot}/.github/workflows/bulk-repo-sync.yml";
|
||||
$cleanupFile = "{$repoRoot}/templates/workflows/shared/repository-cleanup.yml.template";
|
||||
|
||||
// -- Step 1: Read current version --
|
||||
$readme = "{$repoRoot}/README.md";
|
||||
@@ -131,19 +130,7 @@ class ReleaseCli extends CliFramework
|
||||
file_put_contents($bulkSyncFile, $bulkContent);
|
||||
}
|
||||
|
||||
// -- Step 5: Update repository-cleanup.yml current branch --
|
||||
echo "Updating repository-cleanup.yml -> chore/sync-mokostandards-v{$minorVersion}\n";
|
||||
if (!$this->dryRun) {
|
||||
$cleanupContent = file_get_contents($cleanupFile);
|
||||
$cleanupContent = preg_replace(
|
||||
'/CURRENT="chore\/sync-mokostandards-v[^"]*"/',
|
||||
"CURRENT=\"chore/sync-mokostandards-v{$minorVersion}\"",
|
||||
$cleanupContent
|
||||
);
|
||||
file_put_contents($cleanupFile, $cleanupContent);
|
||||
}
|
||||
|
||||
// -- Step 6: Commit changes --
|
||||
// -- Step 5: Commit changes --
|
||||
if (!$this->dryRun) {
|
||||
echo "Committing...\n";
|
||||
passthru("cd {$repoRoot} && git add -A && git commit -m \"chore(release): prepare {$currentVersion} release [skip ci]\"");
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/release_cascade.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Cascade release zip to all lower stability channels
|
||||
*/
|
||||
|
||||
|
||||
+79
-1
@@ -270,12 +270,90 @@ class ReleasePackageCli extends CliFramework
|
||||
}
|
||||
}
|
||||
|
||||
// Check if sub-source is itself a Joomla package (nested package)
|
||||
$nestedPkgManifests = glob("{$subSourceDir}/pkg_*.xml") ?: [];
|
||||
$isNestedPackage = !empty($nestedPkgManifests) && is_dir("{$subSourceDir}/packages");
|
||||
|
||||
$subZip = new \ZipArchive();
|
||||
if ($subZip->open($subZipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
|
||||
$this->log('ERROR', "Failed to create sub-package ZIP: {$subZipPath}");
|
||||
continue;
|
||||
}
|
||||
$this->addDirToZip($subZip, $subSourceDir, '', $this->excludePatterns);
|
||||
|
||||
if ($isNestedPackage) {
|
||||
// Build nested package: zip each sub-extension, then assemble
|
||||
echo " Building nested package: {$subName}\n";
|
||||
$nestedPkgDirs = glob("{$subSourceDir}/packages/*", GLOB_ONLYDIR) ?: [];
|
||||
|
||||
$nestedManifested = [];
|
||||
foreach ($nestedPkgManifests as $npmf) {
|
||||
$npmXml = @simplexml_load_file($npmf);
|
||||
if ($npmXml && isset($npmXml->files)) {
|
||||
foreach ($npmXml->files->file as $fn) {
|
||||
$nzn = pathinfo((string) $fn, PATHINFO_FILENAME);
|
||||
if (!empty($nzn)) {
|
||||
$nestedManifested[$nzn] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use a subdirectory to avoid filename collisions with outer ZIPs
|
||||
$nestedOutputDir = "{$outputDir}/nested_{$subName}";
|
||||
@mkdir($nestedOutputDir, 0755, true);
|
||||
|
||||
foreach ($nestedPkgDirs as $npd) {
|
||||
$nestedSubName = basename($npd);
|
||||
if (!empty($nestedManifested) && !isset($nestedManifested[$nestedSubName])) {
|
||||
continue;
|
||||
}
|
||||
$nestedSubZipPath = "{$nestedOutputDir}/{$nestedSubName}.zip";
|
||||
$nsZip = new \ZipArchive();
|
||||
if ($nsZip->open($nestedSubZipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
|
||||
continue;
|
||||
}
|
||||
$this->addDirToZip($nsZip, $npd, '', $this->excludePatterns);
|
||||
$nsZip->close();
|
||||
$subZip->addFile($nestedSubZipPath, "packages/{$nestedSubName}.zip");
|
||||
echo " Nested sub: {$nestedSubName}.zip\n";
|
||||
}
|
||||
|
||||
// Patch nested manifest: ensure folder="packages" so Joomla finds sub-ZIPs
|
||||
foreach ($nestedPkgManifests as $npmf) {
|
||||
$npmContent = file_get_contents($npmf);
|
||||
if ($npmContent !== false
|
||||
&& strpos($npmContent, '<files>') !== false
|
||||
&& strpos($npmContent, 'folder="packages"') === false
|
||||
) {
|
||||
$npmContent = str_replace('<files>', '<files folder="packages">', $npmContent);
|
||||
file_put_contents($npmf, $npmContent);
|
||||
echo " Fixed: added folder=\"packages\" to " . basename($npmf) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Add top-level files (manifest, script, etc.) with exclude filter
|
||||
$nestedTopFiles = array_merge(
|
||||
glob("{$subSourceDir}/*.xml") ?: [],
|
||||
glob("{$subSourceDir}/*.php") ?: []
|
||||
);
|
||||
foreach ($nestedTopFiles as $ntf) {
|
||||
if (!$this->isExcluded(basename($ntf), $this->excludePatterns)) {
|
||||
$subZip->addFile($ntf, basename($ntf));
|
||||
}
|
||||
}
|
||||
|
||||
// Add all top-level directories except packages/
|
||||
$nestedTopDirs = glob("{$subSourceDir}/*", GLOB_ONLYDIR) ?: [];
|
||||
foreach ($nestedTopDirs as $ntd) {
|
||||
$ndName = basename($ntd);
|
||||
if ($ndName === 'packages') {
|
||||
continue;
|
||||
}
|
||||
$this->addDirToZip($subZip, $ntd, $ndName, $this->excludePatterns);
|
||||
}
|
||||
} else {
|
||||
$this->addDirToZip($subZip, $subSourceDir, '', $this->excludePatterns);
|
||||
}
|
||||
$subZip->close();
|
||||
|
||||
$zip->addFile($subZipPath, "packages/{$subName}.zip");
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/release_publish.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Publish a release and create copies for all lesser stability streams.
|
||||
*/
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/scaffold_client.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings
|
||||
*/
|
||||
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: mokocli.CLI
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/theme_vars_check.php
|
||||
* BRIEF: Validate a client MokoOnyx theme package — required CSS variables
|
||||
* (derived dynamically from the MokoOnyx standard theme) are defined in
|
||||
* the client's light/dark custom CSS, required files exist, the manifest
|
||||
* is sane, and (optionally) the repo's Gitea metadata is set.
|
||||
*
|
||||
* Standalone (no CliFramework dependency) so it runs even when the shared
|
||||
* framework autoloader is unavailable.
|
||||
*
|
||||
* Usage:
|
||||
* php theme_vars_check.php --path . --reference /tmp/mokoonyx [--github-output]
|
||||
* [--api-base <url> --repo <owner/repo> --token <token>]
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$opts = getopt('', ['path:', 'reference:', 'github-output', 'api-base:', 'repo:', 'token:']);
|
||||
$path = isset($opts['path']) && is_string($opts['path']) && $opts['path'] !== '' ? $opts['path'] : '.';
|
||||
$ref = isset($opts['reference']) && is_string($opts['reference']) ? $opts['reference'] : '';
|
||||
$gh = array_key_exists('github-output', $opts);
|
||||
|
||||
$root = realpath($path);
|
||||
if ($root === false) { $root = $path; }
|
||||
|
||||
$src = is_dir("$root/src") ? "$root/src" : (is_dir("$root/source") ? "$root/source" : null);
|
||||
if ($src === null) {
|
||||
fwrite(STDERR, "ERROR: no src/ or source/ directory under $root\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$errors = 0;
|
||||
$summary = [];
|
||||
$fail = function (string $msg) use (&$errors, &$summary): void {
|
||||
echo " [FAIL] $msg\n";
|
||||
$summary[] = "FAIL: $msg";
|
||||
$errors++;
|
||||
};
|
||||
$ok = function (string $msg): void { echo " [ok] $msg\n"; };
|
||||
$note = function (string $msg): void { echo " [note] $msg\n"; };
|
||||
|
||||
/** Extract the set of CSS custom-property names DEFINED in a CSS string. */
|
||||
$definedVars = static function (string $css): array {
|
||||
// Matches "--name:" at a declaration position (not var(--name) uses).
|
||||
preg_match_all('/(?:^|[\s;{])(--[a-z0-9_-]+)\s*:/i', $css, $m);
|
||||
return array_values(array_unique(array_map('strtolower', $m[1] ?? [])));
|
||||
};
|
||||
|
||||
/** Find the MokoOnyx standard theme file for a mode inside a reference checkout. */
|
||||
$findStandard = static function (string $ref, string $mode): ?string {
|
||||
if ($ref === '') { return null; }
|
||||
foreach ([
|
||||
"$ref/source/media/css/theme/$mode.standard.css",
|
||||
"$ref/media/templates/site/mokoonyx/css/theme/$mode.standard.css",
|
||||
"$ref/$mode.standard.css",
|
||||
] as $cand) {
|
||||
if (is_file($cand)) { return $cand; }
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
$cssDir = "$src/media/templates/site/mokoonyx/css";
|
||||
$themeDir = "$cssDir/theme";
|
||||
$manifest = "$src/templateDetails.xml";
|
||||
$customs = ['light' => "$themeDir/light.custom.css", 'dark' => "$themeDir/dark.custom.css"];
|
||||
|
||||
echo "MokoOnyx theme validation: $src\n\n";
|
||||
|
||||
// 1) Required files -------------------------------------------------------
|
||||
echo "=== Required files ===\n";
|
||||
$requiredFiles = [
|
||||
'templateDetails.xml' => $manifest,
|
||||
'theme/light.custom.css' => $customs['light'],
|
||||
'theme/dark.custom.css' => $customs['dark'],
|
||||
'user.css' => "$cssDir/user.css",
|
||||
];
|
||||
foreach ($requiredFiles as $label => $file) {
|
||||
is_file($file) ? $ok("$label present") : $fail("missing required file: $label");
|
||||
}
|
||||
|
||||
// 1b) Forbidden committed files ------------------------------------------
|
||||
echo "\n=== Forbidden files ===\n";
|
||||
$scan = static function (string $dir, array $skip) use (&$scan): array {
|
||||
$out = [];
|
||||
foreach (scandir($dir) ?: [] as $e) {
|
||||
if ($e === '.' || $e === '..') { continue; }
|
||||
$p = "$dir/$e";
|
||||
if (is_dir($p)) {
|
||||
if (in_array($e, $skip, true)) { continue; }
|
||||
$out = array_merge($out, $scan($p, $skip));
|
||||
} else {
|
||||
$out[] = $p;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
};
|
||||
$forbidden = [];
|
||||
if (is_dir("$root/.claude")) { $forbidden[] = '.claude/'; }
|
||||
foreach ($scan($root, ['.git', 'vendor', 'node_modules']) as $f) {
|
||||
$b = basename($f);
|
||||
$rel = ltrim(str_replace('\\', '/', substr($f, strlen($root))), '/');
|
||||
if ($b === '.mcp.json' || $b === 'TODO.md'
|
||||
|| fnmatch('.mcp_*.json', $b) || fnmatch('sftp-config*.json', $b)
|
||||
|| fnmatch('*.min.css', $b) || fnmatch('*.min.js', $b)) {
|
||||
$forbidden[] = $rel;
|
||||
}
|
||||
}
|
||||
$forbidden = array_values(array_unique($forbidden));
|
||||
if ($forbidden) {
|
||||
$shown = array_slice($forbidden, 0, 10);
|
||||
$more = count($forbidden) - count($shown);
|
||||
$fail('forbidden file(s) committed: ' . implode(', ', $shown) . ($more > 0 ? " (+$more more)" : ''));
|
||||
} else {
|
||||
$ok('no forbidden files committed');
|
||||
}
|
||||
|
||||
// 2) CSS variables — required set derived from the MokoOnyx standard theme
|
||||
echo "\n=== CSS variables vs MokoOnyx standard theme ===\n";
|
||||
if ($ref === '') {
|
||||
$note('no --reference given; skipping variable parity check');
|
||||
} else {
|
||||
foreach ($customs as $mode => $customFile) {
|
||||
$std = $findStandard($ref, $mode);
|
||||
if ($std === null) {
|
||||
$fail("$mode: standard theme not found under reference '$ref'");
|
||||
continue;
|
||||
}
|
||||
if (!is_file($customFile)) {
|
||||
continue; // already reported missing
|
||||
}
|
||||
$required = $definedVars((string) file_get_contents($std));
|
||||
$defined = $definedVars((string) file_get_contents($customFile));
|
||||
$missing = array_values(array_diff($required, $defined));
|
||||
if ($missing) {
|
||||
$shown = array_slice($missing, 0, 15);
|
||||
$more = count($missing) - count($shown);
|
||||
$fail("$mode mode missing " . count($missing) . '/' . count($required)
|
||||
. ' standard variable(s): ' . implode(', ', $shown) . ($more > 0 ? " (+$more more)" : ''));
|
||||
} else {
|
||||
$ok("$mode mode defines all " . count($required) . ' standard variables');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Manifest sanity ------------------------------------------------------
|
||||
echo "\n=== Manifest (templateDetails.xml) ===\n";
|
||||
if (is_file($manifest)) {
|
||||
$xml = @simplexml_load_file($manifest);
|
||||
if ($xml === false) {
|
||||
$fail('templateDetails.xml is not well-formed XML');
|
||||
} else {
|
||||
$version = isset($xml->version) ? trim((string) $xml->version) : '';
|
||||
$version !== '' ? $ok("version $version") : $fail('<version> is missing or empty');
|
||||
|
||||
$server = isset($xml->updateservers->server) ? trim((string) $xml->updateservers->server) : '';
|
||||
if ($server === '') {
|
||||
$fail('<updateservers><server> is missing');
|
||||
} elseif (strpos($server, '/raw/branch/') !== false) {
|
||||
$fail('update server uses a legacy raw/branch URL; use the dynamic MokoGitea feed');
|
||||
} else {
|
||||
$ok("update server: $server");
|
||||
}
|
||||
|
||||
isset($xml->dlid) ? $ok('<dlid> license-key field present')
|
||||
: $fail('<dlid prefix="dlid=" suffix=""/> is missing');
|
||||
|
||||
// Element naming convention (MokoOnyx client theme file package)
|
||||
$type = trim((string) ($xml['type'] ?? ''));
|
||||
$element = isset($xml->element) ? trim((string) $xml->element) : '';
|
||||
if ($type === 'file' && $element !== '') {
|
||||
strpos($element, 'file_mokoonyx_') === 0
|
||||
? $ok("element naming ok ($element)")
|
||||
: $fail("element '$element' should start with 'file_mokoonyx_' for a MokoOnyx client theme package");
|
||||
}
|
||||
|
||||
// Required manifest fields
|
||||
foreach (['name', 'element', 'author', 'creationDate'] as $field) {
|
||||
(isset($xml->$field) && trim((string) $xml->$field) !== '')
|
||||
? $ok("<$field> present")
|
||||
: $fail("<$field> is missing or empty");
|
||||
}
|
||||
|
||||
// scriptfile (if declared) must exist
|
||||
if (isset($xml->scriptfile)) {
|
||||
$sf = trim((string) $xml->scriptfile);
|
||||
is_file("$src/$sf") ? $ok("scriptfile '$sf' present")
|
||||
: $fail("<scriptfile> references '$sf' which is not in src/");
|
||||
}
|
||||
|
||||
// Fileset integrity — every referenced file/folder must exist in src/
|
||||
if (isset($xml->fileset)) {
|
||||
$missingFs = [];
|
||||
foreach ($xml->fileset->files as $files) {
|
||||
$folder = trim((string) ($files['folder'] ?? ''));
|
||||
$baseDir = $folder !== '' ? "$src/$folder" : $src;
|
||||
foreach ($files->filename as $fn) {
|
||||
$rel = ($folder !== '' ? "$folder/" : '') . trim((string) $fn);
|
||||
if (!is_file("$baseDir/" . trim((string) $fn))) { $missingFs[] = $rel; }
|
||||
}
|
||||
foreach ($files->folder as $fd) {
|
||||
$rel = ($folder !== '' ? "$folder/" : '') . trim((string) $fd) . '/';
|
||||
if (!is_dir("$baseDir/" . trim((string) $fd))) { $missingFs[] = $rel; }
|
||||
}
|
||||
}
|
||||
if ($missingFs) {
|
||||
$shown = array_slice($missingFs, 0, 10);
|
||||
$more = count($missingFs) - count($shown);
|
||||
$fail('fileset references ' . count($missingFs) . ' missing path(s): '
|
||||
. implode(', ', $shown) . ($more > 0 ? " (+$more more)" : ''));
|
||||
} else {
|
||||
$ok('all fileset paths exist in src/');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4) Repository metadata via Gitea API (optional) -------------------------
|
||||
$apiBase = isset($opts['api-base']) && is_string($opts['api-base']) ? rtrim($opts['api-base'], '/') : '';
|
||||
$repoSlug = isset($opts['repo']) && is_string($opts['repo']) ? trim($opts['repo']) : '';
|
||||
$token = isset($opts['token']) && is_string($opts['token']) ? trim($opts['token']) : '';
|
||||
if ($apiBase !== '' && $repoSlug !== '' && $token !== '') {
|
||||
echo "\n=== Repository metadata (Gitea API) ===\n";
|
||||
$url = "$apiBase/repos/$repoSlug";
|
||||
$ctx = stream_context_create(['http' => [
|
||||
'method' => 'GET',
|
||||
'header' => "Authorization: token $token\r\nAccept: application/json\r\n",
|
||||
'ignore_errors' => true,
|
||||
'timeout' => 15,
|
||||
]]);
|
||||
$resp = @file_get_contents($url, false, $ctx);
|
||||
$data = $resp !== false ? json_decode($resp, true) : null;
|
||||
if (!is_array($data) || !isset($data['name'])) {
|
||||
$fail("could not read repo metadata from Gitea API ($url)");
|
||||
} else {
|
||||
trim((string) ($data['description'] ?? '')) !== '' ? $ok('description set')
|
||||
: $fail('repo description is empty');
|
||||
trim((string) ($data['website'] ?? '')) !== '' ? $ok('website set: ' . $data['website'])
|
||||
: $fail('repo website is empty');
|
||||
$topics = $data['topics'] ?? [];
|
||||
(is_array($topics) && count($topics) > 0) ? $ok(count($topics) . ' topic(s) set')
|
||||
: $fail('repo has no topics');
|
||||
|
||||
// Required topics
|
||||
$requiredTopics = ['joomla', 'client-waas', 'mokoonyx'];
|
||||
$haveTopics = array_map('strtolower', is_array($topics) ? $topics : []);
|
||||
$missingTopics = array_values(array_diff($requiredTopics, $haveTopics));
|
||||
$missingTopics
|
||||
? $fail('missing required topic(s): ' . implode(', ', $missingTopics))
|
||||
: $ok('required topics present (' . implode(', ', $requiredTopics) . ')');
|
||||
|
||||
((string) ($data['default_branch'] ?? '')) === 'main' ? $ok('default branch is main')
|
||||
: $fail("default branch is '" . ($data['default_branch'] ?? '') . "' (expected main)");
|
||||
|
||||
// Branch protection on main
|
||||
$bresp = @file_get_contents("$apiBase/repos/$repoSlug/branches/main", false, $ctx);
|
||||
$bdata = $bresp !== false ? json_decode($bresp, true) : null;
|
||||
if (is_array($bdata) && array_key_exists('protected', $bdata)) {
|
||||
$bdata['protected'] ? $ok('main branch is protected')
|
||||
: $fail('main branch is not protected');
|
||||
} else {
|
||||
$note('could not read branch protection state for main');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "\n=== Repository metadata (Gitea API) ===\n";
|
||||
$note('API args not provided; skipping repo metadata check');
|
||||
}
|
||||
|
||||
// Output / exit -----------------------------------------------------------
|
||||
echo "\n";
|
||||
if ($gh && ($f = getenv('GITHUB_STEP_SUMMARY'))) {
|
||||
$md = "## MokoOnyx Theme Validation\n\n";
|
||||
$md .= $errors === 0
|
||||
? "All checks passed.\n"
|
||||
: implode("\n", array_map(static fn ($l) => "- $l", $summary)) . "\n";
|
||||
@file_put_contents($f, $md, FILE_APPEND);
|
||||
}
|
||||
|
||||
if ($errors > 0) {
|
||||
echo "FAILED: $errors issue(s) found.\n";
|
||||
exit(1);
|
||||
}
|
||||
echo "All MokoOnyx theme validation checks passed.\n";
|
||||
exit(0);
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/updates_xml_sync.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Sync updates.xml to target branches via Gitea API
|
||||
* NOTE: Called by pre-release and auto-release workflows after updates.xml
|
||||
* is modified on the current branch. Pushes the file to other branches
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/version_auto_bump.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash
|
||||
*/
|
||||
|
||||
|
||||
+22
-3
@@ -214,7 +214,8 @@ class VersionBumpCli extends CliFramework
|
||||
$newContent = preg_replace(
|
||||
$xmlPattern,
|
||||
'${1}' . $newBase . '</version>',
|
||||
$content
|
||||
$content,
|
||||
1
|
||||
);
|
||||
} else {
|
||||
$xmlPattern = '#<version>\d{2}\.\d{2}\.\d{2}'
|
||||
@@ -222,9 +223,13 @@ class VersionBumpCli extends CliFramework
|
||||
$newContent = preg_replace(
|
||||
$xmlPattern,
|
||||
"<version>{$newFull}</version>",
|
||||
$content
|
||||
$content,
|
||||
1
|
||||
);
|
||||
}
|
||||
if ($newContent !== null) {
|
||||
$newContent = preg_replace('#(\s*<version>[^<]*</version>)\s*<version>[^<]*</version>#', '$1', $newContent);
|
||||
}
|
||||
if ($newContent !== null && $newContent !== $content) {
|
||||
file_put_contents($xmlFile, $newContent);
|
||||
$updatedFiles[] = substr($xmlFile, strlen($root) + 1);
|
||||
@@ -234,6 +239,20 @@ class VersionBumpCli extends CliFramework
|
||||
if (!empty($updatedFiles)) {
|
||||
fwrite(STDERR, "Updated " . count($updatedFiles) . " Joomla manifest(s): " . implode(', ', $updatedFiles) . "\n");
|
||||
}
|
||||
// Joomla schema version: create empty SQL update file if sql/updates/mysql/ exists
|
||||
$sqlUpdateDirs = array_merge(
|
||||
SourceResolver::globSource($root, 'packages/*/sql/updates/mysql'),
|
||||
SourceResolver::globSource($root, 'sql/updates/mysql'),
|
||||
glob("{$root}/sql/updates/mysql") ?: []
|
||||
);
|
||||
$sqlUpdateDirs = array_unique(array_filter($sqlUpdateDirs, 'is_dir'));
|
||||
foreach ($sqlUpdateDirs as $sqlDir) {
|
||||
$sqlFile = "{$sqlDir}/{$newBase}.sql";
|
||||
if (!file_exists($sqlFile)) {
|
||||
file_put_contents($sqlFile, "/* {$newBase} — no schema changes */\n");
|
||||
fwrite(STDERR, "Created SQL update file: " . substr($sqlFile, strlen($root) + 1) . "\n");
|
||||
}
|
||||
}
|
||||
$packageJsonFile = "{$root}/package.json";
|
||||
if (file_exists($packageJsonFile)) {
|
||||
$pkgContent = file_get_contents($packageJsonFile);
|
||||
@@ -370,7 +389,7 @@ class VersionBumpCli extends CliFramework
|
||||
/**
|
||||
* Scan git release tags for the highest version across all channels.
|
||||
*
|
||||
* Checks release names like "MokoSuiteClient (VERSION: 09.38.00)" in
|
||||
* Checks release names like "MokoSuiteClient (VERSION: 09.43.00)" in
|
||||
* git tags (stable, release-candidate, development, etc.) to find the
|
||||
* highest version that has been released on any channel.
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/version_check.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Validate version consistency across README, manifests, and sub-packages
|
||||
*/
|
||||
|
||||
@@ -131,9 +131,11 @@ class VersionCheckCli extends CliFramework
|
||||
$updated = preg_replace(
|
||||
$vPat,
|
||||
"<version>{$highestVersion}</version>",
|
||||
$content
|
||||
$content,
|
||||
1
|
||||
);
|
||||
if ($updated !== null) {
|
||||
$updated = preg_replace('#(\s*<version>[^<]*</version>)\s*<version>[^<]*</version>#', '$1', $updated);
|
||||
file_put_contents($mokoManifest, $updated);
|
||||
} echo " Fixed: .mokogitea/manifest.xml -> {$highestVersion}\n";
|
||||
}
|
||||
@@ -185,8 +187,9 @@ class VersionCheckCli extends CliFramework
|
||||
if (!file_exists($file)) {
|
||||
continue;
|
||||
} $content = file_get_contents($file);
|
||||
$updated = preg_replace('#<version>[^<]*</version>#', "<version>{$highestVersion}</version>", $content);
|
||||
$updated = preg_replace('#<version>[^<]*</version>#', "<version>{$highestVersion}</version>", $content, 1);
|
||||
if ($updated !== null) {
|
||||
$updated = preg_replace('#(\s*<version>[^<]*</version>)\s*<version>[^<]*</version>#', '$1', $updated);
|
||||
file_put_contents($file, $updated);
|
||||
} echo " Fixed: {$source} -> {$highestVersion}\n";
|
||||
}
|
||||
|
||||
@@ -164,8 +164,12 @@ class VersionSetPlatformCli extends CliFramework
|
||||
$updated = preg_replace(
|
||||
'|<version>[^<]*</version>|',
|
||||
"<version>{$version}</version>",
|
||||
$content
|
||||
$content,
|
||||
1
|
||||
);
|
||||
if ($updated !== null) {
|
||||
$updated = preg_replace('#(\s*<version>[^<]*</version>)\s*<version>[^<]*</version>#', '$1', $updated);
|
||||
}
|
||||
if ($updated !== null && $updated !== $content) {
|
||||
file_put_contents($file, $updated);
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/wiki_sync.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Sync select wiki pages from mokocli to all template repos
|
||||
*/
|
||||
|
||||
|
||||
+128
-4
@@ -10,7 +10,7 @@
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/workflow_sync.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Sync workflows from Generic → platform templates → live repos based on manifest.platform
|
||||
*/
|
||||
|
||||
@@ -42,9 +42,13 @@ class WorkflowSyncCli extends CliFramework
|
||||
'joomla' => ['deploy-manual.yml'],
|
||||
];
|
||||
|
||||
/** Prefix for custom workflows preserved during orphan cleanup. */
|
||||
private const CUSTOM_PREFIX = 'custom-';
|
||||
|
||||
private int $updated = 0;
|
||||
private int $created = 0;
|
||||
private int $skipped = 0;
|
||||
private int $deleted = 0;
|
||||
private int $errors = 0;
|
||||
|
||||
protected function configure(): void
|
||||
@@ -56,6 +60,7 @@ class WorkflowSyncCli extends CliFramework
|
||||
$this->addArgument('--branch', 'Target branch (default: main)', 'main');
|
||||
$this->addArgument('--phase', 'Phase to run: all, templates, repos (default: all)', 'all');
|
||||
$this->addArgument('--platform-filter', 'Only sync repos matching this platform', '');
|
||||
$this->addArgument('--delete-orphans', 'Delete workflows not in template (preserves custom-* and custom/)', false);
|
||||
}
|
||||
|
||||
protected function run(): int
|
||||
@@ -114,7 +119,7 @@ class WorkflowSyncCli extends CliFramework
|
||||
|
||||
echo "\n";
|
||||
$this->log('INFO', "Done: {$this->created} created, {$this->updated} updated, "
|
||||
. "{$this->skipped} skipped, {$this->errors} error(s).");
|
||||
. "{$this->deleted} deleted, {$this->skipped} skipped, {$this->errors} error(s).");
|
||||
|
||||
return $this->errors > 0 ? 1 : 0;
|
||||
}
|
||||
@@ -275,14 +280,15 @@ class WorkflowSyncCli extends CliFramework
|
||||
|
||||
foreach ($workflows as $workflow) {
|
||||
$filename = $workflow['name'];
|
||||
$destPath = '.mokogitea/workflows/' . $filename;
|
||||
$label = "{$repoFullName}/{$filename}";
|
||||
|
||||
// Skip platform-excluded workflows
|
||||
if (in_array($filename, self::PLATFORM_EXCLUDES[$platform] ?? [], true)) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $label, 'EXCLUDED (platform)');
|
||||
$this->skipped++;
|
||||
continue;
|
||||
}
|
||||
$destPath = '.mokogitea/workflows/' . $filename;
|
||||
$label = "{$repoFullName}/{$filename}";
|
||||
|
||||
// Get source content from template
|
||||
$sourceContent = $this->getFileContent(
|
||||
@@ -303,6 +309,14 @@ class WorkflowSyncCli extends CliFramework
|
||||
$destPath, $sourceContent, $branch, $commitMsg, $label
|
||||
);
|
||||
}
|
||||
|
||||
// Delete orphan workflows if enabled
|
||||
if ($this->getArgument('--delete-orphans', false)) {
|
||||
$templateNames = array_map(fn($w) => $w['name'], $workflows);
|
||||
$this->deleteOrphanWorkflows(
|
||||
$giteaUrl, $token, $org, $repoName, $branch, $templateNames, $platform
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
@@ -406,6 +420,116 @@ class WorkflowSyncCli extends CliFramework
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete workflows in a repo that are NOT in the template and NOT custom.
|
||||
*
|
||||
* Protected from deletion:
|
||||
* - Files matching template workflow names
|
||||
* - Files with `custom-` prefix (convention for repo-specific workflows)
|
||||
* - Directories named `custom` (future: subfolder discovery)
|
||||
* - Platform-excluded workflows
|
||||
*/
|
||||
private function deleteOrphanWorkflows(
|
||||
string $giteaUrl,
|
||||
string $token,
|
||||
string $org,
|
||||
string $repoName,
|
||||
string $branch,
|
||||
array $templateNames,
|
||||
string $platform
|
||||
): void {
|
||||
$repoWorkflows = $this->listWorkflows($giteaUrl, $token, $org, $repoName, $branch);
|
||||
if ($repoWorkflows === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$platformExcludes = self::PLATFORM_EXCLUDES[$platform] ?? [];
|
||||
|
||||
foreach ($repoWorkflows as $workflow) {
|
||||
$name = $workflow['name'];
|
||||
|
||||
// Keep if it's in the template
|
||||
if (in_array($name, $templateNames, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep if it has the custom- prefix
|
||||
if (str_starts_with($name, self::CUSTOM_PREFIX)) {
|
||||
$label = "{$org}/{$repoName}/{$name}";
|
||||
fprintf(STDERR, "%-45s | %s\n", $label, 'KEPT (custom)');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep if it's platform-excluded (legitimately skipped during sync)
|
||||
if (in_array($name, $platformExcludes, true)) {
|
||||
$label = "{$org}/{$repoName}/{$name}";
|
||||
fprintf(STDERR, "%-45s | %s\n", $label, 'KEPT (platform-excluded)');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Delete orphan
|
||||
$filePath = '.mokogitea/workflows/' . $name;
|
||||
$label = "{$org}/{$repoName}/{$name}";
|
||||
|
||||
if ($this->dryRun) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $label, 'WOULD DELETE');
|
||||
$this->deleted++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$deleted = $this->deleteFile($giteaUrl, $token, $org, $repoName, $filePath, $branch);
|
||||
if ($deleted) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $label, 'DELETED');
|
||||
$this->deleted++;
|
||||
} else {
|
||||
fprintf(STDERR, "%-45s | %s\n", $label, 'ERROR (delete)');
|
||||
$this->errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file from a repo via the Gitea Contents API.
|
||||
*/
|
||||
private function deleteFile(
|
||||
string $giteaUrl,
|
||||
string $token,
|
||||
string $org,
|
||||
string $repoName,
|
||||
string $filePath,
|
||||
string $branch
|
||||
): bool {
|
||||
// Get SHA first
|
||||
$existing = $this->apiRequest(
|
||||
$giteaUrl, $token, 'GET',
|
||||
"/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}?ref={$branch}"
|
||||
);
|
||||
|
||||
if ($existing['code'] !== 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = json_decode($existing['body'], true);
|
||||
$sha = $data['sha'] ?? '';
|
||||
if ($sha === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$payload = json_encode([
|
||||
'sha' => $sha,
|
||||
'message' => "chore: delete orphan workflow {$filePath} [skip ci]",
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
$response = $this->apiRequest(
|
||||
$giteaUrl, $token, 'DELETE',
|
||||
"/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}",
|
||||
$payload
|
||||
);
|
||||
|
||||
return $response['code'] === 200;
|
||||
}
|
||||
|
||||
/**
|
||||
* List workflow files in a repo's .mokogitea/workflows/ directory.
|
||||
*/
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@
|
||||
],
|
||||
"scripts": {
|
||||
"test": "phpunit",
|
||||
"phpcs": "phpcs --standard=phpcs.xml lib/ validate/ automation/",
|
||||
"phpstan": "phpstan analyse -c phpstan.neon lib/ validate/ automation/",
|
||||
"phpcs": "phpcs --standard=phpcs.xml lib/ validate/",
|
||||
"phpstan": "phpstan analyse -c phpstan.neon lib/ validate/",
|
||||
"psalm": "psalm --config=psalm.xml",
|
||||
"check": [
|
||||
"@phpcs",
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Deploy
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Deploy
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /deploy/backup-before-deploy.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Snapshot Joomla directories before deployment for rollback capability
|
||||
*/
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Deploy
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Deploy
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /deploy/deploy-and-verify.php
|
||||
* BRIEF: Deploy with automatic health check and rollback on failure
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Deploy
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Deploy
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /deploy/deploy-dolibarr.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Deploy Dolibarr module files to a remote server via SFTP/rsync
|
||||
*/
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Deploy
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Deploy
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /deploy/deploy-joomla.php
|
||||
* BRIEF: Smart Joomla deploy — routes files to correct Joomla directories based on XML manifest
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Deploy
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Deploy
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /deploy/deploy-sftp.php
|
||||
* BRIEF: Deploy a repository src/ directory to a remote web server via SFTP
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Deploy
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Deploy
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /deploy/health-check.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Post-deploy health check — verify a Joomla site is responding correctly
|
||||
*/
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Deploy
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Deploy
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /deploy/rollback-joomla.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Rollback a Joomla deployment by restoring from a pre-deploy snapshot
|
||||
*/
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Deploy
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Deploy
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /deploy/sync-joomla.php
|
||||
* VERSION: 09.38.00
|
||||
* VERSION: 09.43.00
|
||||
* BRIEF: Sync Joomla site directories between two servers via rsync over SSH
|
||||
*/
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Fix
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Fix
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /fix/fix_line_endings.php
|
||||
* BRIEF: CLI script to normalise CRLF/CR to LF in tracked source files
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Fix
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Fix
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /fix/fix_permissions.php
|
||||
* BRIEF: CLI script to normalise file permissions (dirs 755, files 644, scripts 755)
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Fix
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Fix
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /fix/fix_tabs.php
|
||||
* BRIEF: CLI script to convert tabs to spaces in tracked source files
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Scripts.Fix
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Scripts.Fix
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /fix/fix_trailing_spaces.php
|
||||
* BRIEF: CLI script to remove trailing whitespace from tracked source files
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
FILE INFORMATION
|
||||
DEFGROUP: MokoPlatform.Index
|
||||
INGROUP: MokoPlatform.Fix
|
||||
DEFGROUP: MokoCLI.Index
|
||||
INGROUP: MokoCLI.Fix
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
PATH: /fix/index.md
|
||||
BRIEF: Fix directory index
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
FILE INFORMATION
|
||||
DEFGROUP: MokoPlatform.Root
|
||||
INGROUP: MokoPlatform
|
||||
DEFGROUP: MokoCLI.Root
|
||||
INGROUP: MokoCLI
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
PATH: /index.md
|
||||
BRIEF: Scripts directory index
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Lib
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Lib
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/CliBase.php
|
||||
* BRIEF: Standalone base CLI class for scripts that do not use CliFramework
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Lib
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Lib
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Common.php
|
||||
* BRIEF: Common utility functions for scripts
|
||||
|
||||
@@ -9,8 +9,8 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Plugins
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Plugins
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/AbstractProjectPlugin.php
|
||||
* BRIEF: Abstract base class for project plugins
|
||||
@@ -23,7 +23,7 @@ namespace MokoCli;
|
||||
*
|
||||
* Provides common functionality for all project type plugins
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 1.0.0
|
||||
*/
|
||||
abstract class AbstractProjectPlugin implements ProjectPluginInterface
|
||||
|
||||
@@ -9,8 +9,8 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.API
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.API
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/ApiClient.php
|
||||
* BRIEF: HTTP API client library
|
||||
@@ -31,7 +31,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.00.04
|
||||
* @author mokocli Team
|
||||
* @license GPL-3.0-or-later
|
||||
|
||||
@@ -22,13 +22,13 @@ declare(strict_types=1);
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Audit
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Audit
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/AuditLogger.php
|
||||
* BRIEF: Enterprise audit logging
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.00.04
|
||||
* @author mokocli Team
|
||||
* @license GPL-3.0-or-later
|
||||
|
||||
@@ -10,13 +10,13 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Checkpoint
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Checkpoint
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/CheckpointManager.php
|
||||
* BRIEF: Checkpoint manager for resumable operations
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.00.04
|
||||
* @author mokocli Team
|
||||
* @license GPL-3.0-or-later
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.CLI
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.CLI
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/CliFramework.php
|
||||
* BRIEF: CliFramework — unified base class for all mokocli CLI scripts
|
||||
|
||||
@@ -9,8 +9,8 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Config
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Config
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/Config.php
|
||||
* BRIEF: Configuration manager
|
||||
@@ -32,7 +32,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.00.04
|
||||
* @author mokocli Team
|
||||
* @license GPL-3.0-or-later
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/ConfigValidator.php
|
||||
* BRIEF: Validate project config against plugin JSON schema
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Enterprise
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/EnterpriseReadinessValidator.php
|
||||
* BRIEF: Enterprise readiness validation library
|
||||
|
||||
@@ -17,13 +17,13 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Recovery
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Recovery
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/ErrorRecovery.php
|
||||
* BRIEF: Error recovery framework
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.00.04
|
||||
* @author mokocli Team
|
||||
* @license GPL-3.0-or-later
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise
|
||||
* INGROUP: MokoPlatform.Lib
|
||||
* DEFGROUP: MokoCLI.Enterprise
|
||||
* INGROUP: MokoCLI.Lib
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/FileFixUtility.php
|
||||
* BRIEF: Utility class for fixing file formatting issues (line endings, permissions, tabs, trailing spaces)
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Platform
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Platform
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/GitHubAdapter.php
|
||||
* BRIEF: GitHub implementation of GitPlatformAdapter
|
||||
@@ -31,7 +31,7 @@ use RuntimeException;
|
||||
* - Topics: PUT with {"names": [...]}
|
||||
* - Workflow dir: .github/workflows
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @since 04.06.10
|
||||
* @see GitPlatformAdapter
|
||||
*/
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Platform
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Platform
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/GitPlatformAdapter.php
|
||||
* BRIEF: Interface defining all git platform operations for GitHub/Gitea abstraction
|
||||
@@ -25,7 +25,7 @@ namespace MokoCli;
|
||||
* Implementations exist for GitHub (GitHubAdapter) and Gitea (MokoGiteaAdapter),
|
||||
* allowing scripts to work against either platform transparently.
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.06.10
|
||||
*/
|
||||
interface GitPlatformAdapter
|
||||
|
||||
@@ -9,8 +9,8 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Validation
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Validation
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/InputValidator.php
|
||||
* BRIEF: Input validation library
|
||||
@@ -31,7 +31,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.00.04
|
||||
* @author mokocli Team
|
||||
* @license GPL-3.0-or-later
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Enterprise
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/ManifestParser.php
|
||||
* BRIEF: Parser for the XML-based manifest.xml repository manifest
|
||||
@@ -28,7 +28,7 @@ use SimpleXMLElement;
|
||||
* Reads, writes, and validates the manifest.xml repository manifest.
|
||||
* The file uses XML format (no file extension) and lives at .mokogitea/manifest.xml.
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.07.00
|
||||
*/
|
||||
class ManifestParser
|
||||
|
||||
@@ -9,8 +9,8 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Metrics
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Metrics
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/MetricsCollector.php
|
||||
* BRIEF: Metrics collection framework
|
||||
@@ -46,7 +46,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.00.04
|
||||
* @author mokocli Team
|
||||
* @license GPL-3.0-or-later
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Platform
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Platform
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/MokoGiteaAdapter.php
|
||||
* BRIEF: Gitea implementation of GitPlatformAdapter
|
||||
@@ -33,7 +33,7 @@ use RuntimeException;
|
||||
* - Branch protection: flat API (not rulesets)
|
||||
* - Workflow dir: .mokogitea/workflows
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @since 04.06.10
|
||||
* @see GitPlatformAdapter
|
||||
*/
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise
|
||||
* INGROUP: MokoPlatform.Lib
|
||||
* DEFGROUP: MokoCLI.Enterprise
|
||||
* INGROUP: MokoCLI.Lib
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/PackageBuilder.php
|
||||
* BRIEF: Builds release packages for generic, Dolibarr module, and Joomla component projects
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Platform
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Platform
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/PlatformAdapterFactory.php
|
||||
* BRIEF: Factory for creating platform-specific GitPlatformAdapter instances
|
||||
@@ -33,7 +33,7 @@ use RuntimeException;
|
||||
* $repos = $adapter->listOrgRepos('mokoconsulting-tech');
|
||||
* ```
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 04.06.10
|
||||
*
|
||||
* @since 04.00.00
|
||||
|
||||
@@ -9,8 +9,8 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Plugins
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Plugins
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/PluginFactory.php
|
||||
* BRIEF: Plugin factory for project type detection
|
||||
@@ -23,7 +23,7 @@ namespace MokoCli;
|
||||
*
|
||||
* Provides convenient methods for plugin instantiation with dependency injection
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 1.0.0
|
||||
*/
|
||||
class PluginFactory
|
||||
|
||||
@@ -9,8 +9,8 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Plugins
|
||||
* INGROUP: MokoPlatform.Enterprise
|
||||
* DEFGROUP: MokoCLI.Enterprise.Plugins
|
||||
* INGROUP: MokoCLI.Enterprise
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/PluginRegistry.php
|
||||
* BRIEF: Plugin registry for available project plugins
|
||||
@@ -35,7 +35,7 @@ use MokoCli\Plugins\McpServerPlugin;
|
||||
*
|
||||
* Manages plugin discovery, registration, and lifecycle
|
||||
*
|
||||
* @package MokoPlatform\Enterprise
|
||||
* @package MokoCLI\Enterprise
|
||||
* @version 1.0.0
|
||||
*/
|
||||
class PluginRegistry
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Plugins
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Enterprise.Plugins
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/Plugins/ApiPlugin.php
|
||||
* BRIEF: Enterprise plugin for API/Microservices projects
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Plugins
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Enterprise.Plugins
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/Plugins/DocumentationPlugin.php
|
||||
* BRIEF: Enterprise plugin for documentation projects
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Plugins
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Enterprise.Plugins
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/Plugins/DolibarrPlugin.php
|
||||
* BRIEF: Enterprise plugin for Dolibarr modules
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Plugins
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Enterprise.Plugins
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/Plugins/GenericPlugin.php
|
||||
* BRIEF: Enterprise plugin for generic projects
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoPlatform.Enterprise.Plugins
|
||||
* INGROUP: MokoPlatform
|
||||
* DEFGROUP: MokoCLI.Enterprise.Plugins
|
||||
* INGROUP: MokoCLI
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /lib/Enterprise/Plugins/JoomlaPlugin.php
|
||||
* BRIEF: Enterprise plugin for Joomla projects
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user