Compare commits
61 Commits
fb5461b661
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| eaa97e6406 | |||
| beed1e628a | |||
| d11ca3d85b | |||
| d7fdd99f68 | |||
| 405261a123 | |||
| a93794f1ba | |||
| 0a095f9a6b | |||
| fe3644204a | |||
| 5a8bb1bd6e | |||
| 34ef05bd6e | |||
| c893f5ac33 | |||
| e177971462 | |||
| a28236d120 | |||
| 51e599acef | |||
| c73675234b | |||
| 16ff7e611e | |||
| 030f057ab4 | |||
| fec5464c17 | |||
| 2723fbf0e7 | |||
| e9dcd48e44 | |||
| 1bd170c77f | |||
| 1e18d6bcb8 | |||
| 35befccf06 | |||
| d012bb900b | |||
| 2ab332161d | |||
| a9acb2d27c | |||
| e609a4e205 | |||
| dbf4cdef9a | |||
| 24d5238b64 | |||
| 4ccefec2dc | |||
| 5a8b18ea8d | |||
| 9dbf790e1a | |||
| a07d93b6fc | |||
| 415e58d06c | |||
| d8ec7b5ba0 | |||
| e882425f04 | |||
| 3171fb3ef0 | |||
| 6cd46f0b7f | |||
| 48ae7c1e88 | |||
| 63a2640254 | |||
| 6ec2202c6e | |||
| 8f7cce051b | |||
| f426f21f2e | |||
| 2ee5a55ec5 | |||
| a04040533c | |||
| d5541abf22 | |||
| 8ae829ad89 | |||
| 5815ad040f | |||
| 96b6db73a9 | |||
| 8eb3e310cf | |||
| eca475c6e3 | |||
| 92822303ef | |||
| 9649fb55cf | |||
| 8f39017b59 | |||
| bd18642045 | |||
| 820e968e1a | |||
| a5cd566dea | |||
| b5599579a7 | |||
| 61a232dfc6 | |||
| a45bf42335 | |||
| 77a1ae3977 |
@@ -6,13 +6,13 @@
|
||||
-->
|
||||
<moko-platform xmlns="https://standards.mokoconsulting.tech/moko-platform/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://standards.mokoconsulting.tech/moko-platform/1.0 https://git.mokoconsulting.tech/MokoConsulting/moko-platform/raw/branch/main/definitions/manifest-schema.xsd"
|
||||
xsi:schemaLocation="https://standards.mokoconsulting.tech/moko-platform/1.0 https://git.mokoconsulting.tech/MokoConsulting/moko-platform/raw/branch/main/templates/schemas/manifest-schema.xsd"
|
||||
schema-version="1.0">
|
||||
<identity>
|
||||
<name>moko-platform</name>
|
||||
<org>MokoConsulting</org>
|
||||
<description>Enterprise automation, validation, sync, and governance engine for all Moko Consulting repositories</description>
|
||||
<version>09.01.00</version>
|
||||
<version>09.02.05</version>
|
||||
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
||||
</identity>
|
||||
<governance>
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# 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
|
||||
|
||||
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.GA_TOKEN }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup moko-platform 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/moko-platform/cli" ]; then
|
||||
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
|
||||
else
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
|
||||
/tmp/moko-platform-api
|
||||
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
||||
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Bump version
|
||||
run: |
|
||||
BUMP=$(php ${MOKO_CLI}/version_bump.php --path . 2>&1) || true
|
||||
echo "$BUMP"
|
||||
|
||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) || true
|
||||
[ -z "$VERSION" ] && { echo "No version found — skipping"; exit 0; }
|
||||
|
||||
# Propagate to platform manifests
|
||||
php ${MOKO_CLI}/version_set_platform.php \
|
||||
--path . --version "$VERSION" --branch dev 2>/dev/null || true
|
||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||
|
||||
# Commit if anything changed
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo "No version changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git commit -m "chore(version): patch bump to ${VERSION} [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||
git push origin dev
|
||||
echo "Bumped to ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -31,6 +31,15 @@ on:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
action:
|
||||
description: 'Action to perform'
|
||||
required: false
|
||||
type: choice
|
||||
default: release
|
||||
options:
|
||||
- release
|
||||
- promote-rc
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
@@ -47,8 +56,8 @@ jobs:
|
||||
name: Promote Pre-Release to RC
|
||||
runs-on: release
|
||||
if: >-
|
||||
github.event.action == 'opened' &&
|
||||
github.event.pull_request.draft == true
|
||||
(github.event.action == 'opened' && github.event.pull_request.draft == true) ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -78,7 +87,7 @@ jobs:
|
||||
--from auto --to release-candidate \
|
||||
--token "${{ secrets.GA_TOKEN }}" \
|
||||
--api-base "${API_BASE}" \
|
||||
--branch "${{ github.event.pull_request.head.ref }}"
|
||||
--branch "${{ github.event.pull_request.head.ref || 'dev' }}"
|
||||
|
||||
- name: Cascade lesser channels
|
||||
continue-on-error: true
|
||||
@@ -100,7 +109,8 @@ jobs:
|
||||
name: Build & Release Pipeline
|
||||
runs-on: release
|
||||
if: >-
|
||||
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
|
||||
github.event.pull_request.merged == true ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -170,16 +180,15 @@ jobs:
|
||||
echo "::notice::No RC release — full build pipeline"
|
||||
fi
|
||||
|
||||
- name: "Step 1b: Bump version"
|
||||
- name: "Step 1b: Minor bump version"
|
||||
id: bump
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.rc.outputs.promote != 'true'
|
||||
run: |
|
||||
MOKO_API="/tmp/moko-platform-api/cli"
|
||||
BUMP=$(php ${MOKO_API}/version_bump.php --path . --minor)
|
||||
VERSION=$(echo "$BUMP" | grep -oP '\d{2}\.\d{2}\.\d{2}$' || true)
|
||||
[ -z "$VERSION" ] && VERSION=$(php ${MOKO_API}/version_read.php --path .)
|
||||
php ${MOKO_API}/version_bump.php --path . --minor 2>&1 || true
|
||||
VERSION=$(php ${MOKO_API}/version_read.php --path .)
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "Bumped to: ${VERSION}"
|
||||
|
||||
@@ -209,95 +218,8 @@ jobs:
|
||||
steps.check.outputs.already_released != 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
ERRORS=0
|
||||
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
MANIFEST="${{ steps.platform.outputs.manifest }}"
|
||||
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
|
||||
echo "## Pre-Release Sanity Checks (${PLATFORM})" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- Version drift check (must pass before release) --------
|
||||
README_VER=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' README.md 2>/dev/null | head -1)
|
||||
if [ "$README_VER" != "$VERSION" ]; then
|
||||
echo "- Version drift: README says \`${README_VER}\` but releasing \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "- Version consistent: \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Check CHANGELOG version matches
|
||||
CL_VER=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' CHANGELOG.md 2>/dev/null | head -1)
|
||||
if [ -n "$CL_VER" ] && [ "$CL_VER" != "$VERSION" ]; then
|
||||
echo "- CHANGELOG drift: \`${CL_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
fi
|
||||
|
||||
# Check composer.json version if present
|
||||
if [ -f "composer.json" ]; then
|
||||
COMP_VER=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' composer.json 2>/dev/null | head -1)
|
||||
if [ -n "$COMP_VER" ] && [ "$COMP_VER" != "$VERSION" ]; then
|
||||
echo "- composer.json drift: \`${COMP_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Common checks
|
||||
if [ ! -f "LICENSE" ]; then
|
||||
echo "- Missing LICENSE file" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "- LICENSE present" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ ! -d "src" ] && [ ! -d "htdocs" ]; then
|
||||
echo "- Warning: No src/ or htdocs/ directory" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- Source directory present" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# -- Platform-specific checks --------
|
||||
case "$PLATFORM" in
|
||||
joomla)
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
XML_VER=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
|
||||
if [ -n "$XML_VER" ] && [ "$XML_VER" != "$VERSION" ]; then
|
||||
echo "- Manifest drift: \`${XML_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "- Manifest version: \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" 2>/dev/null)
|
||||
echo "- Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- No Joomla XML manifest (WaaS site)" >> $GITHUB_STEP_SUMMARY
|
||||
fi ;;
|
||||
dolibarr)
|
||||
if [ -n "$MOD_FILE" ]; then
|
||||
MOD_VER=$(sed -n "s/.*\\\$this->version = '\([^']*\)'.*/\1/p" "$MOD_FILE" 2>/dev/null | head -1)
|
||||
if [ -n "$MOD_VER" ] && [ "$MOD_VER" != "$VERSION" ]; then
|
||||
echo "- Module drift: \`${MOD_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "- Module version: \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
else
|
||||
echo "- No mod*.class.php found" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
fi
|
||||
if [ ! -f "update.txt" ]; then
|
||||
echo "- Missing update.txt" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
fi ;;
|
||||
*) echo "- Generic platform � no manifest checks" >> $GITHUB_STEP_SUMMARY ;;
|
||||
esac
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "$ERRORS" -gt 0 ]; then
|
||||
echo "**${ERRORS} error(s) — release may be incomplete**" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "**All sanity checks passed**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
php /tmp/moko-platform-api/cli/release_validate.php \
|
||||
--path . --version "$VERSION" --output-summary --github-output || true
|
||||
|
||||
# -- STEP 2: Create or update version/XX.YY archive branch ---------------
|
||||
# Always runs — every version change on main archives to version/XX.YY
|
||||
@@ -337,25 +259,19 @@ jobs:
|
||||
php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true
|
||||
php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true
|
||||
|
||||
- name: "Step 5: Write update stream"
|
||||
# Step 5 (updates.xml) moved after Step 8 to include SHA-256 checksum
|
||||
|
||||
- name: "Step 4b: Promote and prune CHANGELOG"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.platform.outputs.platform == 'joomla'
|
||||
steps.check.outputs.already_released != 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
|
||||
# Fetch latest updates.xml from main so preserve logic has all channels
|
||||
GA_TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
curl -sf -H "Authorization: token ${GA_TOKEN}" \
|
||||
"${API}/contents/updates.xml?ref=main" 2>/dev/null | \
|
||||
python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin)['content']).decode())" \
|
||||
> updates.xml 2>/dev/null || true
|
||||
|
||||
php /tmp/moko-platform-api/cli/updates_xml_build.php \
|
||||
--path . --version "${VERSION}" --stability stable \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
--github-output
|
||||
MOKO_API="/tmp/moko-platform-api/cli"
|
||||
if [ -f "CHANGELOG.md" ]; then
|
||||
php ${MOKO_API}/changelog_promote.php --path . --version "$VERSION" 2>&1 || true
|
||||
php ${MOKO_API}/changelog_prune.php --path . --keep 5 2>&1 || true
|
||||
fi
|
||||
|
||||
- name: Commit release changes
|
||||
if: >-
|
||||
@@ -398,7 +314,7 @@ jobs:
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.rc.outputs.promote == 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php /tmp/moko-platform-api/cli/release_promote.php \
|
||||
--from release-candidate --to stable \
|
||||
@@ -415,250 +331,93 @@ jobs:
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
BRANCH="${{ steps.version.outputs.branch }}"
|
||||
MAJOR="${{ steps.version.outputs.major }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php /tmp/moko-platform-api/cli/release_create.php \
|
||||
--path . --version "$VERSION" --tag "$RELEASE_TAG" \
|
||||
--token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --branch main
|
||||
echo "Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Reuse metadata from Step 5 (single source of truth)
|
||||
EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}"
|
||||
EXT_NAME="${{ steps.updates.outputs.ext_name }}"
|
||||
EXT_TYPE="${{ steps.updates.outputs.ext_type }}"
|
||||
EXT_FOLDER="${{ steps.updates.outputs.ext_folder }}"
|
||||
|
||||
# Fallbacks if Step 5 was skipped
|
||||
if [ -z "$EXT_ELEMENT" ]; then
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
fi
|
||||
[ -z "$EXT_NAME" ] && EXT_NAME="${GITEA_REPO}"
|
||||
|
||||
NOTES=$(php /tmp/moko-platform-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null)
|
||||
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
||||
|
||||
# Build release name: "Pretty Name VERSION (type_element-VERSION)"
|
||||
# Strip existing type prefix to prevent duplication
|
||||
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
|
||||
TYPE_PREFIX=""
|
||||
case "${EXT_TYPE}" in
|
||||
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
|
||||
module) TYPE_PREFIX="mod_" ;;
|
||||
component) TYPE_PREFIX="com_" ;;
|
||||
template) TYPE_PREFIX="tpl_" ;;
|
||||
library) TYPE_PREFIX="lib_" ;;
|
||||
package) TYPE_PREFIX="pkg_" ;;
|
||||
esac
|
||||
RELEASE_NAME="${EXT_NAME} ${VERSION} (${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION})"
|
||||
|
||||
# Delete existing release if present (overwrite, not append)
|
||||
EXISTING=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null || true)
|
||||
EXISTING_ID=$(echo "$EXISTING" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$EXISTING_ID" ]; then
|
||||
curl -sS -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${EXISTING_ID}" 2>/dev/null || true
|
||||
curl -sS -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/tags/${RELEASE_TAG}" 2>/dev/null || true
|
||||
echo "Deleted previous stable release (id: ${EXISTING_ID})"
|
||||
fi
|
||||
|
||||
# Create fresh release
|
||||
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/releases" \
|
||||
-d "$(python3 -c "import json; print(json.dumps({
|
||||
'tag_name': '${RELEASE_TAG}',
|
||||
'name': '${RELEASE_NAME}',
|
||||
'body': '''## ${VERSION} ($(date +%Y-%m-%d))\n${NOTES}''',
|
||||
'target_commitish': '${BRANCH}'
|
||||
}))")"
|
||||
echo "Release created: ${RELEASE_NAME}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- STEP 8: Build Joomla install ZIP + SHA-256 checksum ------------------
|
||||
- name: "Step 8: Build package and update checksum"
|
||||
# -- STEP 8: Build packages and upload to release ----------------------------
|
||||
- name: "Step 8: Build package and upload"
|
||||
id: package
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.rc.outputs.promote != 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
REPO="${{ github.repository }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php /tmp/moko-platform-api/cli/release_package.php \
|
||||
--path . --version "$VERSION" --tag "$RELEASE_TAG" \
|
||||
--token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --output /tmp || true
|
||||
|
||||
# All ZIPs upload to the major release tag (vXX)
|
||||
RELEASE_JSON=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null || true)
|
||||
RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
echo "No release ${RELEASE_TAG} found — skipping ZIP upload"
|
||||
exit 0
|
||||
# -- STEP 5: Write update stream (after build so SHA-256 is available) -----
|
||||
- name: "Step 5: Write update stream"
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||
|
||||
# Fetch latest updates.xml from main so preserve logic has all channels
|
||||
GA_TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
curl -sf -H "Authorization: token ${GA_TOKEN}" \
|
||||
"${API}/contents/updates.xml?ref=main" 2>/dev/null | \
|
||||
python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin)['content']).decode())" \
|
||||
> updates.xml 2>/dev/null || true
|
||||
|
||||
SHA_FLAG=""
|
||||
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
|
||||
|
||||
php /tmp/moko-platform-api/cli/updates_xml_build.php \
|
||||
--path . --version "${VERSION}" --stability stable \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
${SHA_FLAG} --github-output
|
||||
|
||||
# Commit updates.xml if changed
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add updates.xml
|
||||
git commit -m "chore: update stable channel ${VERSION} [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||
git push origin HEAD 2>&1 || true
|
||||
fi
|
||||
|
||||
# Find extension element name from manifest
|
||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)
|
||||
[ -z "$MANIFEST" ] && exit 0
|
||||
|
||||
# Reuse element from Step 5, with same fallback chain
|
||||
EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}"
|
||||
if [ -z "$EXT_ELEMENT" ]; then
|
||||
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(sed -n 's/.*plugin="\([^"]*\)".*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
fi
|
||||
# ZIP name: type_folder_element-VERSION (e.g. plg_system_mokojgdpc-01.01.00.zip)
|
||||
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
# For packages, prefer <packagename> over filename-derived element
|
||||
if [ "$EXT_TYPE" = "package" ]; then
|
||||
PKG_NAME=$(sed -n 's/.*<packagename>\([^<]*\)<\/packagename>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
|
||||
[ -n "$PKG_NAME" ] && EXT_ELEMENT="$PKG_NAME"
|
||||
fi
|
||||
# Strip existing type prefix to prevent duplication (e.g. pkg_mokowaas → mokowaas)
|
||||
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
|
||||
TYPE_PREFIX=""
|
||||
case "${EXT_TYPE}" in
|
||||
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
|
||||
module) TYPE_PREFIX="mod_" ;;
|
||||
component) TYPE_PREFIX="com_" ;;
|
||||
template) TYPE_PREFIX="tpl_" ;;
|
||||
library) TYPE_PREFIX="lib_" ;;
|
||||
package) TYPE_PREFIX="pkg_" ;;
|
||||
esac
|
||||
ZIP_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip"
|
||||
TAR_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.tar.gz"
|
||||
|
||||
# -- Build install packages from src/ ----------------------------
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/"; exit 0; }
|
||||
|
||||
# ZIP package (type-aware via moko-platform PHP API)
|
||||
php /tmp/moko-platform-api/cli/joomla_build.php --path . --version "${VERSION}" --output /tmp
|
||||
# Match the expected ZIP_NAME for upload
|
||||
BUILT_ZIP=$(ls /tmp/${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip 2>/dev/null | head -1 || true)
|
||||
if [ -n "$BUILT_ZIP" ] && [ "$BUILT_ZIP" != "/tmp/${ZIP_NAME}" ]; then
|
||||
mv "$BUILT_ZIP" "/tmp/${ZIP_NAME}"
|
||||
fi
|
||||
|
||||
# tar.gz package (flat source archive)
|
||||
tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" --exclude='.ftpignore' --exclude='sftp-config*' --exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
|
||||
|
||||
ZIP_SIZE=$(stat -c%s "/tmp/${ZIP_NAME}" 2>/dev/null || stat -f%z "/tmp/${ZIP_NAME}" 2>/dev/null || echo "unknown")
|
||||
TAR_SIZE=$(stat -c%s "/tmp/${TAR_NAME}" 2>/dev/null || stat -f%z "/tmp/${TAR_NAME}" 2>/dev/null || echo "unknown")
|
||||
|
||||
# -- Calculate SHA-256 for both ----------------------------------
|
||||
SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
|
||||
SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1)
|
||||
|
||||
# -- Get existing assets for cleanup --------------------------------
|
||||
ASSETS=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]")
|
||||
|
||||
# -- Create per-file .sha256 checksum files -------------------------
|
||||
echo "${SHA256_ZIP} ${ZIP_NAME}" > "/tmp/${ZIP_NAME}.sha256"
|
||||
echo "${SHA256_TAR} ${TAR_NAME}" > "/tmp/${TAR_NAME}.sha256"
|
||||
|
||||
# -- Upload packages + checksums to release tag --------------------
|
||||
for ASSET in "${ZIP_NAME}" "${TAR_NAME}" "${ZIP_NAME}.sha256" "${TAR_NAME}.sha256"; do
|
||||
[ ! -f "/tmp/${ASSET}" ] && continue
|
||||
# Delete existing asset with same name
|
||||
ASSET_ID=$(echo "$ASSETS" | python3 -c "
|
||||
import sys,json
|
||||
assets = json.load(sys.stdin)
|
||||
for a in assets:
|
||||
if a['name'] == '${ASSET}':
|
||||
print(a['id']); break
|
||||
" 2>/dev/null || true)
|
||||
[ -n "$ASSET_ID" ] && curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
|
||||
# Upload
|
||||
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"/tmp/${ASSET}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${ASSET}" > /dev/null 2>&1 || true
|
||||
done
|
||||
|
||||
# updates.xml already handled by Step 5 (updates_xml_build.php with preserve logic)
|
||||
|
||||
echo "### Packages" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Package | Size | SHA-256 |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|---------|------|---------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| \`${ZIP_NAME}\` | ${ZIP_SIZE} | \`${SHA256_ZIP}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| \`${TAR_NAME}\` | ${TAR_SIZE} | \`${SHA256_TAR}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Release | \`${RELEASE_TAG}\` | |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Download | [${ZIP_NAME}](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}) |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- STEP 8b: Update release description with changelog ----------------------
|
||||
- name: "Step 8b: Update release body"
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
MOKO_CLI="/tmp/moko-platform-api/cli"
|
||||
|
||||
php ${MOKO_CLI}/release_body_update.php \
|
||||
php /tmp/moko-platform-api/cli/release_body_update.php \
|
||||
--path . --version "${VERSION}" --tag "${RELEASE_TAG}" \
|
||||
--token "${{ secrets.GA_TOKEN }}" \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
2>/dev/null || {
|
||||
# Fallback: simple body update if CLI not available
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null | \
|
||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then
|
||||
BODY="## ${VERSION} ($(date +%Y-%m-%d))\n\nChecksum files attached as \`*.sha256\` assets."
|
||||
curl -sf -X PATCH -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}" \
|
||||
-d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
2>&1 || true
|
||||
echo "Release body updated" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
|
||||
- name: "Step 9: Mirror release to GitHub"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.version.outputs.stability == 'stable' &&
|
||||
secrets.GH_TOKEN != ''
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
MAJOR="${{ steps.version.outputs.major }}"
|
||||
BRANCH="${{ steps.version.outputs.branch }}"
|
||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||
|
||||
NOTES=$(php /tmp/moko-platform-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null || true)
|
||||
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
||||
echo "$NOTES" > /tmp/release_notes.md
|
||||
|
||||
EXISTING=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/tags/$RELEASE_TAG" 2>/dev/null | jq -r ".tag_name // empty" || true)
|
||||
|
||||
if [ -z "$EXISTING" ]; then
|
||||
gh release create "$RELEASE_TAG" \
|
||||
--repo "$GH_REPO" \
|
||||
--title "v${MAJOR} (latest: ${VERSION})" \
|
||||
--notes-file /tmp/release_notes.md \
|
||||
--target "$BRANCH" || true
|
||||
else
|
||||
gh release edit "$RELEASE_TAG" \
|
||||
--repo "$GH_REPO" \
|
||||
--title "v${MAJOR} (latest: ${VERSION})" || true
|
||||
fi
|
||||
|
||||
# Upload assets to GitHub mirror
|
||||
for PKG in /tmp/${EXT_ELEMENT:-pkg}-${VERSION}.*; do
|
||||
if [ -f "$PKG" ]; then
|
||||
_RELID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/tags/$RELEASE_TAG" 2>/dev/null | jq -r ".id // empty")
|
||||
[ -n "$_RELID" ] && curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" -H "Content-Type: application/octet-stream" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/${_RELID}/assets?name=$(basename $PKG)" --data-binary "@$PKG" > /dev/null 2>&1 || true
|
||||
fi
|
||||
done
|
||||
echo "GitHub mirror updated: ${GH_REPO} ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php /tmp/moko-platform-api/cli/release_mirror.php \
|
||||
--version "$VERSION" --tag "$RELEASE_TAG" \
|
||||
--token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--gh-token "${{ secrets.GH_TOKEN }}" --gh-repo "$GH_REPO" \
|
||||
--branch main 2>&1 || true
|
||||
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- STEP 10: Sync main branch to GitHub mirror ----------------------------
|
||||
- name: "Step 10: Push main to GitHub mirror"
|
||||
@@ -709,28 +468,35 @@ jobs:
|
||||
|
||||
echo "Dev branch reset from main (keeps dev ahead after release)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
|
||||
# -- Dolibarr post-release: Reset dev version -----------------------------
|
||||
- name: "Dolibarr: Reset dev version"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.platform.outputs.platform == 'dolibarr' &&
|
||||
steps.platform.outputs.mod_file != ''
|
||||
- name: "Step 12: Create version branch from main"
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
|
||||
ENCODED_PATH=$(echo "$MOD_FILE" | sed 's|^\./||' | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))")
|
||||
FILE_RESP=$(curl -sf -H "Authorization: token ${TOKEN}" "${API_BASE}/contents/${ENCODED_PATH}?ref=dev" 2>/dev/null || true)
|
||||
FILE_SHA=$(echo "$FILE_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
|
||||
FILE_CONTENT=$(echo "$FILE_RESP" | python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin).get('content','')).decode())" 2>/dev/null || true)
|
||||
if [ -n "$FILE_SHA" ] && [ -n "$FILE_CONTENT" ]; then
|
||||
UPDATED=$(echo "$FILE_CONTENT" | sed "s/\$this->version = '[^']*'/\$this->version = 'development'/")
|
||||
ENCODED=$(echo "$UPDATED" | base64 -w0)
|
||||
curl -sf -X PUT -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/contents/${ENCODED_PATH}" \
|
||||
-d "$(jq -n --arg content \"$ENCODED\" --arg sha \"$FILE_SHA\" --arg msg \"chore(version): reset dev version [skip ci]\" --arg branch \"dev\" '{content:$content,sha:$sha,message:$msg,branch:$branch}')" > /dev/null 2>&1 || true
|
||||
fi
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
BRANCH_NAME="version/${VERSION}"
|
||||
MAIN_SHA=$(git rev-parse HEAD)
|
||||
|
||||
# Delete old version branch if it exists (same version re-release)
|
||||
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}"
|
||||
|
||||
# Create version/XX.YY.ZZ from main
|
||||
curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed"
|
||||
|
||||
echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
|
||||
|
||||
# -- Dolibarr post-release: Reset dev version -----------------------------
|
||||
- name: "Post-release: Reset dev version"
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php /tmp/moko-platform-api/cli/version_reset_dev.php \
|
||||
--token "${{ secrets.GA_TOKEN }}" --api-base "${API_BASE}" \
|
||||
--branch dev --path . 2>&1 || true
|
||||
|
||||
# -- Summary --------------------------------------------------------------
|
||||
- name: Pipeline Summary
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /.mokogitea/workflows/branch-cleanup.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Delete feature branches after PR merge
|
||||
|
||||
name: "Branch Cleanup"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Delete merged branch
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.head.ref != 'dev' &&
|
||||
github.event.pull_request.head.ref != 'main'
|
||||
|
||||
steps:
|
||||
- name: Delete source branch
|
||||
run: |
|
||||
BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
||||
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${BRANCH}', safe=''))")
|
||||
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
-H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API}/${ENCODED}" 2>/dev/null || true)
|
||||
|
||||
if [ "$STATUS" = "204" ]; then
|
||||
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
elif [ "$STATUS" = "404" ]; then
|
||||
echo "Branch already deleted: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "::warning::Failed to delete branch ${BRANCH} (HTTP ${STATUS})"
|
||||
fi
|
||||
@@ -1,139 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Deploy
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# 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 moko-platform 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}/moko-platform.git" \
|
||||
/tmp/moko-platform-api 2>/dev/null || true
|
||||
if [ -d "/tmp/moko-platform-api" ] && [ -f "/tmp/moko-platform-api/composer.json" ]; then
|
||||
cd /tmp/moko-platform-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/moko-platform-api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/moko-platform-api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/moko-platform-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
|
||||
else
|
||||
php /tmp/moko-platform-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||
fi
|
||||
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
|
||||
|
||||
- name: Post-deploy health check
|
||||
if: success() && steps.check.outputs.skip != 'true'
|
||||
run: |
|
||||
if [ -f "deploy/health-check.php" ]; then
|
||||
SITE_URL="${{ vars.DEV_SITE_URL }}"
|
||||
if [ -n "$SITE_URL" ]; then
|
||||
php deploy/health-check.php --url "$SITE_URL" --checks http --timeout 30 || echo "::warning::Health check failed after deploy"
|
||||
else
|
||||
echo "DEV_SITE_URL not configured, skipping health check"
|
||||
fi
|
||||
fi
|
||||
|
||||
- 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
|
||||
@@ -1,314 +1,223 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||
# VERSION: 05.01.00
|
||||
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
|
||||
|
||||
name: "Universal: Pre-Release"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
stability:
|
||||
description: 'Pre-release channel'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- development
|
||||
- alpha
|
||||
- beta
|
||||
- release-candidate
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
GITEA_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 }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
|
||||
runs-on: release
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
|
||||
- name: Setup tools
|
||||
run: |
|
||||
# Update moko-platform CLI tools if available; install PHP if missing
|
||||
if command -v moko-platform-update &> /dev/null; then
|
||||
moko-platform-update
|
||||
elif [ -d "/opt/moko-platform" ]; then
|
||||
cd /opt/moko-platform && git pull origin main --quiet 2>/dev/null || true
|
||||
else
|
||||
if ! command -v php &> /dev/null; then
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl >/dev/null 2>&1
|
||||
fi
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
|
||||
/tmp/moko-platform-api
|
||||
fi
|
||||
# Set MOKO_CLI to whichever path exists
|
||||
if [ -d "/opt/moko-platform/cli" ]; then
|
||||
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Detect platform
|
||||
id: platform
|
||||
run: |
|
||||
php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
||||
|
||||
- name: Resolve metadata and bump version
|
||||
id: meta
|
||||
run: |
|
||||
STABILITY="${{ inputs.stability || 'development' }}"
|
||||
|
||||
case "$STABILITY" in
|
||||
development) SUFFIX="-dev"; TAG="development" ;;
|
||||
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
|
||||
beta) SUFFIX="-beta"; TAG="beta" ;;
|
||||
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
|
||||
esac
|
||||
|
||||
# Patch bump via CLI tool
|
||||
php ${MOKO_CLI}/version_bump.php --path .
|
||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
|
||||
[ -z "$VERSION" ] && VERSION="00.00.01"
|
||||
|
||||
php ${MOKO_CLI}/version_set_platform.php \
|
||||
--path . --version "$VERSION" --branch "${{ github.ref_name }}" 2>/dev/null || true
|
||||
|
||||
# Verify version consistency across all files
|
||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||
|
||||
# Commit version bump
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
||||
git push origin HEAD 2>&1
|
||||
}
|
||||
|
||||
# Auto-detect element via manifest_element.php
|
||||
php ${MOKO_CLI}/manifest_element.php \
|
||||
--path . --version "$VERSION" --stability "$STABILITY" \
|
||||
--repo "${GITEA_REPO}" --github-output
|
||||
|
||||
# Read back element outputs
|
||||
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
|
||||
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
||||
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
||||
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
||||
|
||||
- name: Build package
|
||||
run: |
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
if [ ! -d "$SOURCE_DIR" ]; then
|
||||
echo "::error::No src/ or htdocs/ directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MANIFEST="${{ steps.meta.outputs.manifest }}"
|
||||
EXT_TYPE=""
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
fi
|
||||
|
||||
EXCLUDES="sftp-config* .ftpignore *.ppk *.pem *.key .env* *.local .build-trigger"
|
||||
|
||||
mkdir -p build/package
|
||||
|
||||
if [ "$EXT_TYPE" = "package" ] && [ -d "${SOURCE_DIR}/packages" ]; then
|
||||
echo "=== Building Joomla PACKAGE (multi-extension) ==="
|
||||
for ext_dir in "${SOURCE_DIR}"/packages/*/; do
|
||||
[ ! -d "$ext_dir" ] && continue
|
||||
EXT_NAME=$(basename "$ext_dir")
|
||||
echo " Packaging sub-extension: ${EXT_NAME}"
|
||||
cd "$ext_dir"
|
||||
zip -r "../../build/package/${EXT_NAME}.zip" . -x $EXCLUDES
|
||||
cd "$OLDPWD"
|
||||
done
|
||||
for f in "${SOURCE_DIR}"/*.xml "${SOURCE_DIR}"/*.php; do
|
||||
[ -f "$f" ] && cp "$f" build/package/
|
||||
done
|
||||
else
|
||||
echo "=== Building standard extension ==="
|
||||
rsync -a \
|
||||
--exclude='sftp-config*' \
|
||||
--exclude='.ftpignore' \
|
||||
--exclude='*.ppk' \
|
||||
--exclude='*.pem' \
|
||||
--exclude='*.key' \
|
||||
--exclude='.env*' \
|
||||
--exclude='*.local' \
|
||||
--exclude='.build-trigger' \
|
||||
"${SOURCE_DIR}/" build/package/
|
||||
fi
|
||||
|
||||
- name: Create ZIP
|
||||
id: zip
|
||||
run: |
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
cd build/package
|
||||
zip -r "../${ZIP_NAME}" .
|
||||
cd ..
|
||||
|
||||
SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1)
|
||||
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
|
||||
echo "ZIP: ${ZIP_NAME} (SHA: ${SHA256:0:16}...)"
|
||||
|
||||
- name: Create or replace Gitea release
|
||||
id: release
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
BRANCH=$(git branch --show-current)
|
||||
|
||||
BODY="## ${VERSION} ($(date +%Y-%m-%d))
|
||||
**Channel:** ${STABILITY}
|
||||
**SHA-256:** \`${SHA256}\`"
|
||||
|
||||
# Delete existing release
|
||||
EXISTING_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
|
||||
"${API}/releases/tags/${TAG}" | jq -r '.id // empty' 2>/dev/null)
|
||||
if [ -n "$EXISTING_ID" ]; then
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API}/releases/${EXISTING_ID}" 2>/dev/null || true
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API}/tags/${TAG}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Create release
|
||||
RELEASE_ID=$(curl -sS -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/releases" \
|
||||
-d "$(jq -n \
|
||||
--arg tag "$TAG" \
|
||||
--arg target "$BRANCH" \
|
||||
--arg name "${EXT_ELEMENT} ${VERSION} (${STABILITY})" \
|
||||
--arg body "$BODY" \
|
||||
'{tag_name: $tag, target_commitish: $target, name: $name, body: $body, prerelease: true}'
|
||||
)" | jq -r '.id')
|
||||
|
||||
echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Upload ZIP
|
||||
curl -sS -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \
|
||||
--data-binary "@build/${ZIP_NAME}"
|
||||
|
||||
echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})"
|
||||
|
||||
- name: Update updates.xml
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
echo "No updates.xml -- skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
php ${MOKO_CLI}/updates_xml_build.php \
|
||||
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}"
|
||||
|
||||
# Commit and push
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git add updates.xml
|
||||
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
||||
git push origin HEAD 2>&1 || echo "WARNING: push failed"
|
||||
fi
|
||||
|
||||
- name: "Sync updates.xml to all branches"
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
|
||||
for BRANCH in main dev; do
|
||||
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
|
||||
echo "Syncing updates.xml -> ${BRANCH}"
|
||||
git fetch origin "${BRANCH}" 2>/dev/null || continue
|
||||
git checkout "origin/${BRANCH}" -- . 2>/dev/null || continue
|
||||
git checkout "${CURRENT_BRANCH}" -- updates.xml
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git add updates.xml
|
||||
git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
|
||||
git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
|
||||
fi
|
||||
git checkout "${CURRENT_BRANCH}" 2>/dev/null
|
||||
done
|
||||
|
||||
- name: "Delete lesser pre-release channels (cascade)"
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
|
||||
php ${MOKO_CLI}/release_cascade.php \
|
||||
--stability "${{ steps.meta.outputs.stability }}" \
|
||||
--token "${TOKEN}" \
|
||||
--api-base "${API_BASE}"
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||
# VERSION: 05.01.00
|
||||
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
|
||||
|
||||
name: "Universal: Pre-Release"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
stability:
|
||||
description: 'Pre-release channel'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- development
|
||||
- alpha
|
||||
- beta
|
||||
- release-candidate
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
GITEA_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 }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
|
||||
runs-on: release
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
|
||||
- name: Setup moko-platform tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
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
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||
/tmp/moko-platform-api
|
||||
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
||||
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Detect platform
|
||||
id: platform
|
||||
run: |
|
||||
php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
||||
|
||||
- name: Resolve metadata and bump version
|
||||
id: meta
|
||||
run: |
|
||||
STABILITY="${{ inputs.stability || 'development' }}"
|
||||
|
||||
# Map stability to Gitea release tag
|
||||
case "$STABILITY" in
|
||||
development) TAG="development" ;;
|
||||
alpha) TAG="alpha" ;;
|
||||
beta) TAG="beta" ;;
|
||||
release-candidate) TAG="release-candidate" ;;
|
||||
esac
|
||||
|
||||
# Read current version (includes suffix from manifest, e.g. 01.02.14-dev)
|
||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
|
||||
[ -z "$VERSION" ] && VERSION="00.00.01"
|
||||
|
||||
php ${MOKO_CLI}/version_set_platform.php \
|
||||
--path . --version "$VERSION" --branch "${{ github.ref_name }}" 2>/dev/null || true
|
||||
|
||||
# Verify version consistency across all files
|
||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||
|
||||
# Commit version bump
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
||||
git push origin HEAD 2>&1
|
||||
}
|
||||
|
||||
# Auto-detect element via manifest_element.php
|
||||
php ${MOKO_CLI}/manifest_element.php \
|
||||
--path . --version "$VERSION" --stability "$STABILITY" \
|
||||
--repo "${GITEA_REPO}" --github-output
|
||||
|
||||
# Read back element outputs
|
||||
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
|
||||
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION} ==="
|
||||
|
||||
- name: Create release
|
||||
id: release
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/release_create.php \
|
||||
--path . --version "$VERSION" --tag "$TAG" \
|
||||
--token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --branch dev --prerelease
|
||||
|
||||
- name: Build package and upload
|
||||
id: package
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/release_package.php \
|
||||
--path . --version "$VERSION" --tag "$TAG" \
|
||||
--token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --output /tmp || true
|
||||
|
||||
- name: Update updates.xml
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
echo "No updates.xml -- skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SHA_FLAG=""
|
||||
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
|
||||
|
||||
php ${MOKO_CLI}/updates_xml_build.php \
|
||||
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
${SHA_FLAG}
|
||||
|
||||
# Commit and push
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git add updates.xml
|
||||
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
||||
git push origin HEAD 2>&1 || echo "WARNING: push failed"
|
||||
fi
|
||||
|
||||
- name: "Sync updates.xml to all branches"
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
|
||||
for BRANCH in main dev; do
|
||||
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
|
||||
echo "Syncing updates.xml -> ${BRANCH}"
|
||||
git fetch origin "${BRANCH}" 2>/dev/null || continue
|
||||
git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue
|
||||
git checkout "${CURRENT_BRANCH}" -- updates.xml
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git add updates.xml
|
||||
git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
|
||||
git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
|
||||
fi
|
||||
git checkout "${CURRENT_BRANCH}" 2>/dev/null
|
||||
done
|
||||
|
||||
- name: "Delete lesser pre-release channels (cascade)"
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
|
||||
php ${MOKO_CLI}/release_cascade.php \
|
||||
--stability "${{ steps.meta.outputs.stability }}" \
|
||||
--token "${TOKEN}" \
|
||||
--api-base "${API_BASE}"
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -182,14 +182,8 @@ jobs:
|
||||
PHP_TAG=""
|
||||
[ -n "$PHP_MINIMUM" ] && PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>"
|
||||
|
||||
# Version suffix for non-stable
|
||||
DISPLAY_VERSION="$VERSION"
|
||||
case "$STABILITY" in
|
||||
development) DISPLAY_VERSION="${VERSION}-dev" ;;
|
||||
alpha) DISPLAY_VERSION="${VERSION}-alpha" ;;
|
||||
beta) DISPLAY_VERSION="${VERSION}-beta" ;;
|
||||
rc) DISPLAY_VERSION="${VERSION}-rc" ;;
|
||||
esac
|
||||
# VERSION already includes suffix from manifest (e.g. 01.02.14-dev)
|
||||
# No separate DISPLAY_VERSION needed
|
||||
|
||||
MAJOR=$(echo "$VERSION" | awk -F. '{print $1}')
|
||||
|
||||
@@ -202,7 +196,7 @@ jobs:
|
||||
*) RELEASE_TAG="v${MAJOR}" ;;
|
||||
esac
|
||||
|
||||
PACKAGE_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.zip"
|
||||
PACKAGE_NAME="${EXT_ELEMENT}-${VERSION}.zip"
|
||||
DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
||||
INFO_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
@@ -211,7 +205,7 @@ jobs:
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
if [ -d "$SOURCE_DIR" ]; then
|
||||
EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*"
|
||||
TAR_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
|
||||
TAR_NAME="${EXT_ELEMENT}-${VERSION}.tar.gz"
|
||||
|
||||
cd "$SOURCE_DIR"
|
||||
zip -r "/tmp/${PACKAGE_NAME}" . -x $EXCLUDES
|
||||
@@ -234,7 +228,7 @@ jobs:
|
||||
"${API_BASE}/releases" \
|
||||
-d "$(python3 -c "import json; print(json.dumps({
|
||||
'tag_name': '${RELEASE_TAG}',
|
||||
'name': '${RELEASE_TAG} (${DISPLAY_VERSION})',
|
||||
'name': '${RELEASE_TAG} (${VERSION})',
|
||||
'body': '${STABILITY} release',
|
||||
'prerelease': True,
|
||||
'target_commitish': 'main'
|
||||
@@ -371,7 +365,7 @@ jobs:
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git add updates.xml
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore: update updates.xml (${STABILITY}: ${DISPLAY_VERSION}) [skip ci]" \
|
||||
git commit -m "chore: update updates.xml (${STABILITY}: ${VERSION}) [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||
git push
|
||||
}
|
||||
@@ -417,61 +411,6 @@ jobs:
|
||||
echo "::error::could not get updates.xml SHA from main — file may not exist on main yet" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: SFTP deploy to dev server
|
||||
if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
|
||||
env:
|
||||
DEV_HOST: ${{ vars.DEV_FTP_HOST }}
|
||||
DEV_PATH: ${{ vars.DEV_FTP_PATH }}
|
||||
DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
|
||||
DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
|
||||
DEV_PORT: ${{ vars.DEV_FTP_PORT }}
|
||||
DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
|
||||
DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
|
||||
run: |
|
||||
# -- Permission check: admin or maintain role required --------
|
||||
ACTOR="${{ github.actor }}"
|
||||
REPO="${{ github.repository }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \
|
||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read")
|
||||
case "$PERMISSION" in
|
||||
admin|maintain|write) ;;
|
||||
*)
|
||||
echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
[ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
|
||||
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && exit 0
|
||||
|
||||
PORT="${DEV_PORT:-22}"
|
||||
REMOTE="${DEV_PATH%/}"
|
||||
[ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
|
||||
|
||||
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
|
||||
"$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
|
||||
if [ -n "$DEV_KEY" ]; then
|
||||
echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
|
||||
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
|
||||
else
|
||||
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
|
||||
fi
|
||||
|
||||
PLATFORM=$(php /tmp/moko-platform/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/moko-platform/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/moko-platform/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
elif [ -f "/tmp/moko-platform/deploy/deploy-sftp.php" ]; then
|
||||
php /tmp/moko-platform/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
fi
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Validate updates.xml integrity
|
||||
run: |
|
||||
ERRORS=0
|
||||
@@ -655,6 +594,6 @@ jobs:
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Version | \`${DISPLAY_VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Element | \`${EXT_ELEMENT}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Download | [ZIP](${DOWNLOAD_URL}) |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -18,6 +18,60 @@ Version format: `XX.YY.ZZ` (zero-padded semver).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- `branch-cleanup.yml`: auto-delete merged feature branches after PR merge — synced to all 47 repos
|
||||
- `governance.yml`: lightweight YAML schema replacing HCL definition files for repo governance config
|
||||
- `auto-bump.yml`: auto patch-bump version on every push to dev
|
||||
|
||||
### Changed
|
||||
- **Definitions removed**: deleted `definitions/` directory (63,602 lines of HCL) — Template repos are now the canonical source for platform-specific files
|
||||
- **Template path migration**: `templates/gitea/` → `templates/mokogitea/`, all `.github/` references → `.mokogitea/` across definitions and sync tools
|
||||
- `version_bump.php`: preserves version suffix (e.g. `-dev`) through bumps — moko manifest is now the single source of truth for the full version string (#191)
|
||||
- `version_read.php`: accepts suffix from moko manifest (was stripping it)
|
||||
- `update-server.yml`: removed `DISPLAY_VERSION` — derives filename directly from manifest version (#191)
|
||||
- `pre-release.yml`: removed `SUFFIX` variable — version string already includes suffix
|
||||
- `push_files.php`: detects platform from manifest.xml via API instead of local sync definition files
|
||||
- `auto_detect_platform.php`: gracefully handles missing schema directory
|
||||
- `DefinitionParser.php`: deleted — no longer needed
|
||||
- `manifest-schema.xsd`: moved from `definitions/` to `templates/schemas/`
|
||||
|
||||
### Removed
|
||||
- `definitions/default/` — 10 HCL definition files (generic, joomla, dolibarr, platform, standards, etc.)
|
||||
- `definitions/sync/` — 48 auto-generated sync tracking files
|
||||
- `lib/Enterprise/DefinitionParser.php` — HCL parser (replaced by Template repo sourcing)
|
||||
- Redundant bump from pre-release.yml (handled by auto-bump)
|
||||
- 47 merged feature branches cleaned up from remote
|
||||
|
||||
## [09.02.00] - 2026-05-26
|
||||
|
||||
### Added
|
||||
- **Release promotion pipeline**: draft PR → RC promotion, merged PR → RC-to-stable (skip rebuild)
|
||||
- **7 new CLI tools**: `manifest_element.php`, `release_create.php`, `release_package.php`, `release_promote.php`, `release_mirror.php`, `version_reset_dev.php`, `ManifestReader.php`
|
||||
- `version_bump.php` / `version_read.php`: support for `package.json` (Node.js) and `pyproject.toml` (Python)
|
||||
- `version_bump.php`: now writes bumped version to all sources (README, manifests, Dolibarr mod, composer.json, package.json, pyproject.toml)
|
||||
- `release_cascade.php`: `--version` flag for version-aware deletion of stale releases
|
||||
- `release_validate.php`: auto-detect platform from manifest.xml, `--github-output` flag, source dir check
|
||||
- `updates_xml_build.php`: supports non-Joomla platforms via manifest.xml detection
|
||||
- `release_package.php`: reads entry-point from manifest.xml for source dir resolution
|
||||
- `auto-release.yml`: `workflow_dispatch` with `promote-rc` action as fallback for MokoGitea#220
|
||||
- `update-server.yml`: now universal — pushed to all 69+ repos (Joomla, Dolibarr, generic, MCP)
|
||||
- `ManifestReader.php`: shared typed accessor for `.mokogitea/manifest.xml`
|
||||
- Universal workflow cascade: Template-Generic → other templates → all repos via `bulk_sync.php`
|
||||
- Wiki: UPDATE_SERVER standard page on moko-platform and all template repos
|
||||
- PHPDoc added to 4 classes missing class-level docs
|
||||
|
||||
### Changed
|
||||
- `auto-release.yml`: 761 → 490 lines — replaced all inline bash with CLI tool calls
|
||||
- `pre-release.yml`: 389 → 314 lines — replaced inline logic with `manifest_read.php`, `manifest_element.php`, `updates_xml_build.php`
|
||||
- Removed `paths` filter from workflow triggers (enables Go, Node.js, generic repo compatibility)
|
||||
- `RepositorySynchronizer.php`: fixed template repo names, `.mokogitea/workflows` path, universal workflow sync
|
||||
- Template-Generic is now the single source of truth for universal workflows
|
||||
|
||||
### Fixed
|
||||
- `release_cascade.php` in `auto-release.yml`: was using `--org`/`--repo` flags instead of `--api-base`
|
||||
- `pre-release.yml`: updates.xml sync was checking out entire branch tree instead of just `updates.xml`
|
||||
- MokoWaaS#48: Joomla 6 typed event API fix for `plg_webservices_mokowaas`
|
||||
|
||||
## [09.00.00] - 2026-05-26
|
||||
|
||||
### Added
|
||||
|
||||
@@ -44,8 +44,7 @@ composer check
|
||||
| `lib/Enterprise/` | Core library — CliFramework, ApiClient, adapters, validators |
|
||||
| `lib/Enterprise/Plugins/` | 11 platform plugins (Joomla, Dolibarr, Node.js, Python, etc.) |
|
||||
| `deploy/` | SFTP deployment scripts (Joomla, Dolibarr, health checks) |
|
||||
| `definitions/` | Repository structure definitions (HCL format) |
|
||||
| `templates/` | Workflow templates, config templates, docs templates |
|
||||
| `templates/` | Universal templates, configs, governance schema |
|
||||
| `.mokogitea/workflows/` | CI/CD workflows (Gitea Actions) |
|
||||
| `bin/moko` | Unified CLI dispatcher — runs any tool via `php bin/moko <command>` |
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ DEFGROUP: MokoStandards.Root
|
||||
INGROUP: MokoStandards
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
PATH: /README.md
|
||||
VERSION: 09.01.00
|
||||
VERSION: 09.02.05
|
||||
BRIEF: Project overview and documentation
|
||||
-->
|
||||
|
||||
|
||||
+25
-43
@@ -26,7 +26,6 @@ use MokoEnterprise\{
|
||||
AuditLogger,
|
||||
CliFramework,
|
||||
Config,
|
||||
DefinitionParser,
|
||||
GitPlatformAdapter,
|
||||
MetricsCollector,
|
||||
PlatformAdapterFactory,
|
||||
@@ -59,7 +58,6 @@ class PushFiles extends CliFramework
|
||||
private ApiClient $api;
|
||||
private GitPlatformAdapter $adapter;
|
||||
private AuditLogger $logger;
|
||||
private DefinitionParser $defParser;
|
||||
private ProjectTypeDetector $typeDetector;
|
||||
|
||||
/**
|
||||
@@ -154,7 +152,6 @@ class PushFiles extends CliFramework
|
||||
$this->adapter = PlatformAdapterFactory::create($config);
|
||||
$this->api = $this->adapter->getApiClient();
|
||||
$this->logger = new AuditLogger('push_files');
|
||||
$this->defParser = new DefinitionParser();
|
||||
$this->typeDetector = new ProjectTypeDetector($this->logger);
|
||||
|
||||
$platform = $this->adapter->getPlatformName();
|
||||
@@ -198,43 +195,24 @@ class PushFiles extends CliFramework
|
||||
$platform = $this->detectRepoPlatform($org, $repo);
|
||||
$this->log(" {$repo}: platform = {$platform}", 'INFO');
|
||||
|
||||
// Build a destination→source lookup from the definition
|
||||
$defEntries = $this->defParser->parseForPlatform($platform, $repoRoot);
|
||||
$destToSource = [];
|
||||
foreach ($defEntries as $entry) {
|
||||
$destToSource[$entry['destination']] = $entry['source'];
|
||||
}
|
||||
|
||||
$resolved = [];
|
||||
foreach ($files as $fileSpec) {
|
||||
if (str_contains($fileSpec, ':')) {
|
||||
// Raw source:destination pair
|
||||
[$src, $dest] = explode(':', $fileSpec, 2);
|
||||
$srcAbs = rtrim($repoRoot, '/') . '/' . ltrim($src, '/');
|
||||
if (!file_exists($srcAbs)) {
|
||||
$this->log(" ⚠️ Source not found for {$repo}: {$src}", 'WARN');
|
||||
continue;
|
||||
}
|
||||
$resolved[] = ['source' => $srcAbs, 'destination' => $dest];
|
||||
$this->log(" ✓ {$dest} (raw: {$src})", 'INFO');
|
||||
} else {
|
||||
// Destination path — look up in definition
|
||||
$dest = ltrim($fileSpec, '/');
|
||||
if (isset($destToSource[$dest])) {
|
||||
$src = $destToSource[$dest];
|
||||
$srcAbs = str_starts_with($src, '/')
|
||||
? $src
|
||||
: rtrim($repoRoot, '/') . '/' . ltrim($src, '/');
|
||||
if (!file_exists($srcAbs)) {
|
||||
$this->log(" ⚠️ Template not found for {$repo}: {$src}", 'WARN');
|
||||
continue;
|
||||
}
|
||||
$resolved[] = ['source' => $srcAbs, 'destination' => $dest];
|
||||
$this->log(" ✓ {$dest}", 'INFO');
|
||||
} else {
|
||||
$this->log(" ⚠️ {$dest} not found in {$platform} definition for {$repo}", 'WARN');
|
||||
}
|
||||
// Same path as source and destination
|
||||
$src = $fileSpec;
|
||||
$dest = $fileSpec;
|
||||
}
|
||||
$dest = ltrim($dest, '/');
|
||||
$srcAbs = rtrim($repoRoot, '/') . '/' . ltrim($src, '/');
|
||||
if (!file_exists($srcAbs)) {
|
||||
$this->log(" ⚠️ Source not found for {$repo}: {$src}", 'WARN');
|
||||
continue;
|
||||
}
|
||||
$resolved[] = ['source' => $srcAbs, 'destination' => $dest];
|
||||
$this->log(" ✓ {$dest}", 'INFO');
|
||||
}
|
||||
|
||||
if (!empty($resolved)) {
|
||||
@@ -246,24 +224,28 @@ class PushFiles extends CliFramework
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect platform for a repo by checking its sync def file, falling back
|
||||
* to the live GitHub API detection used by bulk_sync.
|
||||
* Detect platform for a repo via manifest or live detection.
|
||||
*/
|
||||
private function detectRepoPlatform(string $org, string $repo): string
|
||||
{
|
||||
// Check local sync def first — fastest path
|
||||
$defDir = dirname(__DIR__) . '/definitions/sync';
|
||||
$defFile = "{$defDir}/{$repo}.def.tf";
|
||||
if (file_exists($defFile)) {
|
||||
$content = file_get_contents($defFile) ?: '';
|
||||
if (preg_match('/detected_platform\s*=\s*"([^"]+)"/', $content, $m)) {
|
||||
return $m[1];
|
||||
// Read platform from repo's .mokogitea/manifest.xml via API
|
||||
try {
|
||||
$manifestData = $this->adapter->getFileContent($org, $repo, '.mokogitea/manifest.xml', 'main');
|
||||
if (!empty($manifestData)) {
|
||||
$xml = @simplexml_load_string($manifestData);
|
||||
if ($xml !== false) {
|
||||
$platform = (string)($xml->governance->platform ?? '');
|
||||
if (!empty($platform)) {
|
||||
return $platform;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Fall through to local detection
|
||||
}
|
||||
|
||||
// Fall back to live detection
|
||||
try {
|
||||
$repoData = $this->api->get("/repos/{$org}/{$repo}");
|
||||
$result = $this->typeDetector->detect('.');
|
||||
return $result['type'] ?? 'default';
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -132,6 +132,10 @@ const COMMAND_MAP = [
|
||||
'release:mirror' => 'cli/release_mirror.php',
|
||||
'release:package' => 'cli/release_package.php',
|
||||
|
||||
// Changelog
|
||||
'changelog:promote' => 'cli/changelog_promote.php',
|
||||
'changelog:prune' => 'cli/changelog_prune.php',
|
||||
|
||||
// Version management
|
||||
'version:read' => 'cli/version_read.php',
|
||||
'version:bump' => 'cli/version_bump.php',
|
||||
|
||||
+1
-13
@@ -118,19 +118,7 @@ if (!$dryRun) {
|
||||
echo " (dry-run) would archive {$org}/{$repoName}\n";
|
||||
}
|
||||
|
||||
// ── Step 5: Remove sync definition ──────────────────────────────────────
|
||||
echo "Step 5: Removing sync definition...\n";
|
||||
$defFile = "{$repoRoot}/definitions/sync/{$repoName}.def.tf";
|
||||
if (file_exists($defFile)) {
|
||||
if (!$dryRun) {
|
||||
unlink($defFile);
|
||||
echo " Removed: {$defFile}\n";
|
||||
} else {
|
||||
echo " (dry-run) would remove {$defFile}\n";
|
||||
}
|
||||
} else {
|
||||
echo " No sync definition found\n";
|
||||
}
|
||||
// ── Step 5: (removed — sync definitions no longer used) ─────────────────
|
||||
|
||||
// ── Step 6: Create archival record ──────────────────────────────────────
|
||||
echo "Step 6: Creating archival record...\n";
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/changelog_prune.php
|
||||
* BRIEF: Prune old CHANGELOG.md entries — keeps [Unreleased] + last N releases
|
||||
*
|
||||
* Usage:
|
||||
* php changelog_prune.php --path /repo --keep 5
|
||||
* php changelog_prune.php --path /repo --keep 3 --dry-run
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$keep = 5;
|
||||
$dryRun = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--keep' && isset($argv[$i + 1])) $keep = (int)$argv[$i + 1];
|
||||
if ($arg === '--dry-run') $dryRun = true;
|
||||
if ($arg === '--help') {
|
||||
echo "changelog_prune — Keep [Unreleased] + last N versioned entries\n\n";
|
||||
echo "Usage: php changelog_prune.php --path . --keep 5 [--dry-run]\n\n";
|
||||
echo "Options:\n";
|
||||
echo " --path Repository path (default: .)\n";
|
||||
echo " --keep Number of versioned releases to keep (default: 5)\n";
|
||||
echo " --dry-run Preview without writing\n";
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
$changelog = realpath($path) . '/CHANGELOG.md';
|
||||
if (!file_exists($changelog)) {
|
||||
fwrite(STDERR, "No CHANGELOG.md found at {$path}\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$content = file_get_contents($changelog);
|
||||
$lines = explode("\n", $content);
|
||||
|
||||
// Split into sections by ## headings
|
||||
$sections = [];
|
||||
$current = [];
|
||||
$currentHeading = null;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
if (preg_match('/^## /', $line)) {
|
||||
if ($currentHeading !== null) {
|
||||
$sections[] = ['heading' => $currentHeading, 'lines' => $current];
|
||||
}
|
||||
$currentHeading = $line;
|
||||
$current = [$line];
|
||||
} else {
|
||||
$current[] = $line;
|
||||
}
|
||||
}
|
||||
if ($currentHeading !== null) {
|
||||
$sections[] = ['heading' => $currentHeading, 'lines' => $current];
|
||||
}
|
||||
|
||||
// Find the header (everything before the first ## section)
|
||||
$header = [];
|
||||
$contentLines = explode("\n", $content);
|
||||
foreach ($contentLines as $line) {
|
||||
if (preg_match('/^## /', $line)) {
|
||||
break;
|
||||
}
|
||||
$header[] = $line;
|
||||
}
|
||||
|
||||
// Separate [Unreleased] from versioned sections
|
||||
$unreleased = null;
|
||||
$versioned = [];
|
||||
|
||||
foreach ($sections as $section) {
|
||||
if (preg_match('/\[Unreleased\]/i', $section['heading'])) {
|
||||
$unreleased = $section;
|
||||
} else {
|
||||
$versioned[] = $section;
|
||||
}
|
||||
}
|
||||
|
||||
$totalVersioned = count($versioned);
|
||||
$pruned = $totalVersioned - $keep;
|
||||
|
||||
if ($pruned <= 0) {
|
||||
echo "CHANGELOG has {$totalVersioned} versioned entries — nothing to prune (keeping {$keep})\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Keep only the first N versioned sections
|
||||
$keptVersioned = array_slice($versioned, 0, $keep);
|
||||
$droppedVersioned = array_slice($versioned, $keep);
|
||||
|
||||
// Report
|
||||
echo "CHANGELOG: {$totalVersioned} versioned entries found\n";
|
||||
echo " Keeping: {$keep} most recent\n";
|
||||
echo " Pruning: {$pruned} old entries\n";
|
||||
|
||||
foreach ($droppedVersioned as $section) {
|
||||
$heading = trim($section['heading']);
|
||||
echo " - {$heading}\n";
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
echo "\n(dry-run) No changes written\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Rebuild the file
|
||||
$output = implode("\n", $header);
|
||||
|
||||
if ($unreleased !== null) {
|
||||
$output .= implode("\n", $unreleased['lines']) . "\n";
|
||||
}
|
||||
|
||||
foreach ($keptVersioned as $section) {
|
||||
$output .= implode("\n", $section['lines']) . "\n";
|
||||
}
|
||||
|
||||
// Clean up excessive blank lines at end
|
||||
$output = rtrim($output) . "\n";
|
||||
|
||||
file_put_contents($changelog, $output);
|
||||
echo "\nCHANGELOG pruned: removed {$pruned} old entries\n";
|
||||
exit(0);
|
||||
+188
-188
@@ -1,188 +1,188 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/client_health_check.php
|
||||
* BRIEF: Verify a client site's update server, installed version, and release availability
|
||||
*
|
||||
* Usage:
|
||||
* php client_health_check.php --update-url URL
|
||||
* php client_health_check.php --path /repo --github-output
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (reads update server URL from manifest)
|
||||
* --update-url Update server XML URL (overrides manifest)
|
||||
* --site-url Live site URL for version checking via Joomla API (optional)
|
||||
* --api-token Joomla API token for site-url (optional)
|
||||
* --github-output Export results to $GITHUB_OUTPUT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$updateUrl = null;
|
||||
$siteUrl = null;
|
||||
$apiToken = null;
|
||||
$ghOutput = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--update-url' && isset($argv[$i + 1])) $updateUrl = $argv[$i + 1];
|
||||
if ($arg === '--site-url' && isset($argv[$i + 1])) $siteUrl = $argv[$i + 1];
|
||||
if ($arg === '--api-token' && isset($argv[$i + 1])) $apiToken = $argv[$i + 1];
|
||||
if ($arg === '--github-output') $ghOutput = true;
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
$checks = [];
|
||||
|
||||
// ── Resolve update server URL from manifest ─────────────────────────────
|
||||
if ($updateUrl === null) {
|
||||
$searchDirs = ["{$root}/src", $root];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) continue;
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
$xml = file_get_contents($f);
|
||||
if (preg_match('/<server[^>]*>([^<]+)<\/server>/', $xml, $m)) {
|
||||
$updateUrl = trim($m[1]);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($updateUrl === null) {
|
||||
fwrite(STDERR, "No update server URL found. Use --update-url or provide a manifest with <updateservers>.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Update server: {$updateUrl}\n\n";
|
||||
|
||||
// ── Check 1: Update server accessible ───────────────────────────────────
|
||||
echo "--- Update Server ---\n";
|
||||
$ch = curl_init($updateUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTPHEADER => ['User-Agent: MokoHealthCheck/1.0'],
|
||||
]);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200 && !empty($response)) {
|
||||
echo " PASS: HTTP {$httpCode}, " . strlen($response) . " bytes\n";
|
||||
$checks['update_server'] = 'pass';
|
||||
} else {
|
||||
echo " FAIL: HTTP {$httpCode}\n";
|
||||
$checks['update_server'] = 'fail';
|
||||
}
|
||||
|
||||
// ── Check 2: Parse updates.xml for stable version ───────────────────────
|
||||
$stableVersion = null;
|
||||
$downloadUrl = null;
|
||||
|
||||
if (!empty($response)) {
|
||||
$sections = preg_split('/<update>/', $response);
|
||||
foreach ($sections as $section) {
|
||||
if (strpos($section, '<tag>stable</tag>') !== false) {
|
||||
if (preg_match('/<version>([^<]+)<\/version>/', $section, $m)) {
|
||||
$stableVersion = $m[1];
|
||||
}
|
||||
if (preg_match('/<downloadurl[^>]*>([^<]+)<\/downloadurl>/', $section, $m)) {
|
||||
$downloadUrl = trim($m[1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($stableVersion === null && preg_match('/<version>([^<]+)<\/version>/', $response, $m)) {
|
||||
$stableVersion = $m[1];
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n--- Stable Release ---\n";
|
||||
if ($stableVersion !== null) {
|
||||
echo " Version: {$stableVersion}\n";
|
||||
$checks['stable_version'] = $stableVersion;
|
||||
} else {
|
||||
echo " FAIL: Could not parse stable version\n";
|
||||
$checks['stable_version'] = 'fail';
|
||||
}
|
||||
|
||||
// ── Check 3: Download URL accessible ────────────────────────────────────
|
||||
if ($downloadUrl !== null) {
|
||||
echo "\n--- Download URL ---\n";
|
||||
$ch = curl_init($downloadUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_NOBODY => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
]);
|
||||
curl_exec($ch);
|
||||
$dlCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$dlSize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
|
||||
curl_close($ch);
|
||||
|
||||
if ($dlCode === 200) {
|
||||
$sizeKb = $dlSize > 0 ? round($dlSize / 1024) . 'KB' : 'unknown size';
|
||||
echo " PASS: HTTP {$dlCode}, {$sizeKb}\n";
|
||||
$checks['download'] = 'pass';
|
||||
} else {
|
||||
echo " FAIL: HTTP {$dlCode}\n";
|
||||
$checks['download'] = 'fail';
|
||||
}
|
||||
}
|
||||
|
||||
// ── Check 4: Site version (optional) ────────────────────────────────────
|
||||
if ($siteUrl !== null && $apiToken !== null) {
|
||||
echo "\n--- Site Version ---\n";
|
||||
$apiUrl = rtrim($siteUrl, '/') . '/api/index.php/v1/extensions?filter[type]=file';
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Joomla-Token: {$apiToken}",
|
||||
'Accept: application/json',
|
||||
],
|
||||
]);
|
||||
$siteResponse = curl_exec($ch);
|
||||
$siteCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($siteCode === 200) {
|
||||
echo " API accessible (HTTP {$siteCode})\n";
|
||||
$checks['site_api'] = 'pass';
|
||||
} else {
|
||||
echo " WARN: Site API returned HTTP {$siteCode}\n";
|
||||
$checks['site_api'] = 'warn';
|
||||
}
|
||||
}
|
||||
|
||||
// ── Summary ─────────────────────────────────────────────────────────────
|
||||
echo "\n=== Health Check Summary ===\n";
|
||||
$failed = 0;
|
||||
foreach ($checks as $name => $result) {
|
||||
$icon = ($result === 'fail') ? 'FAIL' : (($result === 'warn') ? 'WARN' : 'OK');
|
||||
if ($result === 'fail') $failed++;
|
||||
echo " {$icon}: {$name} = {$result}\n";
|
||||
}
|
||||
|
||||
if ($ghOutput) {
|
||||
$ghFile = getenv('GITHUB_OUTPUT');
|
||||
if ($ghFile) {
|
||||
file_put_contents($ghFile, "health_status=" . ($failed > 0 ? 'fail' : 'pass') . "\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "health_version=" . ($stableVersion ?? 'unknown') . "\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "health_failures={$failed}\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
exit($failed > 0 ? 1 : 0);
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/client_health_check.php
|
||||
* BRIEF: Verify a client site's update server, installed version, and release availability
|
||||
*
|
||||
* Usage:
|
||||
* php client_health_check.php --update-url URL
|
||||
* php client_health_check.php --path /repo --github-output
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (reads update server URL from manifest)
|
||||
* --update-url Update server XML URL (overrides manifest)
|
||||
* --site-url Live site URL for version checking via Joomla API (optional)
|
||||
* --api-token Joomla API token for site-url (optional)
|
||||
* --github-output Export results to $GITHUB_OUTPUT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$updateUrl = null;
|
||||
$siteUrl = null;
|
||||
$apiToken = null;
|
||||
$ghOutput = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--update-url' && isset($argv[$i + 1])) $updateUrl = $argv[$i + 1];
|
||||
if ($arg === '--site-url' && isset($argv[$i + 1])) $siteUrl = $argv[$i + 1];
|
||||
if ($arg === '--api-token' && isset($argv[$i + 1])) $apiToken = $argv[$i + 1];
|
||||
if ($arg === '--github-output') $ghOutput = true;
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
$checks = [];
|
||||
|
||||
// ── Resolve update server URL from manifest ─────────────────────────────
|
||||
if ($updateUrl === null) {
|
||||
$searchDirs = ["{$root}/src", $root];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) continue;
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
$xml = file_get_contents($f);
|
||||
if (preg_match('/<server[^>]*>([^<]+)<\/server>/', $xml, $m)) {
|
||||
$updateUrl = trim($m[1]);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($updateUrl === null) {
|
||||
fwrite(STDERR, "No update server URL found. Use --update-url or provide a manifest with <updateservers>.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Update server: {$updateUrl}\n\n";
|
||||
|
||||
// ── Check 1: Update server accessible ───────────────────────────────────
|
||||
echo "--- Update Server ---\n";
|
||||
$ch = curl_init($updateUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTPHEADER => ['User-Agent: MokoHealthCheck/1.0'],
|
||||
]);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200 && !empty($response)) {
|
||||
echo " PASS: HTTP {$httpCode}, " . strlen($response) . " bytes\n";
|
||||
$checks['update_server'] = 'pass';
|
||||
} else {
|
||||
echo " FAIL: HTTP {$httpCode}\n";
|
||||
$checks['update_server'] = 'fail';
|
||||
}
|
||||
|
||||
// ── Check 2: Parse updates.xml for stable version ───────────────────────
|
||||
$stableVersion = null;
|
||||
$downloadUrl = null;
|
||||
|
||||
if (!empty($response)) {
|
||||
$sections = preg_split('/<update>/', $response);
|
||||
foreach ($sections as $section) {
|
||||
if (strpos($section, '<tag>stable</tag>') !== false) {
|
||||
if (preg_match('/<version>([^<]+)<\/version>/', $section, $m)) {
|
||||
$stableVersion = $m[1];
|
||||
}
|
||||
if (preg_match('/<downloadurl[^>]*>([^<]+)<\/downloadurl>/', $section, $m)) {
|
||||
$downloadUrl = trim($m[1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($stableVersion === null && preg_match('/<version>([^<]+)<\/version>/', $response, $m)) {
|
||||
$stableVersion = $m[1];
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n--- Stable Release ---\n";
|
||||
if ($stableVersion !== null) {
|
||||
echo " Version: {$stableVersion}\n";
|
||||
$checks['stable_version'] = $stableVersion;
|
||||
} else {
|
||||
echo " FAIL: Could not parse stable version\n";
|
||||
$checks['stable_version'] = 'fail';
|
||||
}
|
||||
|
||||
// ── Check 3: Download URL accessible ────────────────────────────────────
|
||||
if ($downloadUrl !== null) {
|
||||
echo "\n--- Download URL ---\n";
|
||||
$ch = curl_init($downloadUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_NOBODY => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
]);
|
||||
curl_exec($ch);
|
||||
$dlCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$dlSize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
|
||||
curl_close($ch);
|
||||
|
||||
if ($dlCode === 200) {
|
||||
$sizeKb = $dlSize > 0 ? round($dlSize / 1024) . 'KB' : 'unknown size';
|
||||
echo " PASS: HTTP {$dlCode}, {$sizeKb}\n";
|
||||
$checks['download'] = 'pass';
|
||||
} else {
|
||||
echo " FAIL: HTTP {$dlCode}\n";
|
||||
$checks['download'] = 'fail';
|
||||
}
|
||||
}
|
||||
|
||||
// ── Check 4: Site version (optional) ────────────────────────────────────
|
||||
if ($siteUrl !== null && $apiToken !== null) {
|
||||
echo "\n--- Site Version ---\n";
|
||||
$apiUrl = rtrim($siteUrl, '/') . '/api/index.php/v1/extensions?filter[type]=file';
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Joomla-Token: {$apiToken}",
|
||||
'Accept: application/json',
|
||||
],
|
||||
]);
|
||||
$siteResponse = curl_exec($ch);
|
||||
$siteCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($siteCode === 200) {
|
||||
echo " API accessible (HTTP {$siteCode})\n";
|
||||
$checks['site_api'] = 'pass';
|
||||
} else {
|
||||
echo " WARN: Site API returned HTTP {$siteCode}\n";
|
||||
$checks['site_api'] = 'warn';
|
||||
}
|
||||
}
|
||||
|
||||
// ── Summary ─────────────────────────────────────────────────────────────
|
||||
echo "\n=== Health Check Summary ===\n";
|
||||
$failed = 0;
|
||||
foreach ($checks as $name => $result) {
|
||||
$icon = ($result === 'fail') ? 'FAIL' : (($result === 'warn') ? 'WARN' : 'OK');
|
||||
if ($result === 'fail') $failed++;
|
||||
echo " {$icon}: {$name} = {$result}\n";
|
||||
}
|
||||
|
||||
if ($ghOutput) {
|
||||
$ghFile = getenv('GITHUB_OUTPUT');
|
||||
if ($ghFile) {
|
||||
file_put_contents($ghFile, "health_status=" . ($failed > 0 ? 'fail' : 'pass') . "\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "health_version=" . ($stableVersion ?? 'unknown') . "\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "health_failures={$failed}\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
exit($failed > 0 ? 1 : 0);
|
||||
|
||||
+136
-136
@@ -1,136 +1,136 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/joomla_compat_check.php
|
||||
* BRIEF: Check if extension targetplatform regex matches the latest Joomla version
|
||||
*
|
||||
* Usage:
|
||||
* php joomla_compat_check.php --path /repo
|
||||
* php joomla_compat_check.php --path /repo --github-output
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (default: .)
|
||||
* --github-output Export results to $GITHUB_OUTPUT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$ghOutput = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--github-output') $ghOutput = true;
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
|
||||
// ── Find manifest and extract targetplatform ────────────────────────────
|
||||
$manifest = null;
|
||||
$searchDirs = ["{$root}/src", $root];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) continue;
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
$xml = file_get_contents($f);
|
||||
if (strpos($xml, '<extension') !== false && strpos($xml, 'targetplatform') !== false) {
|
||||
$manifest = $f;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($manifest === null) {
|
||||
fwrite(STDERR, "No manifest with targetplatform found\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$xml = file_get_contents($manifest);
|
||||
$relManifest = str_replace($root . '/', '', $manifest);
|
||||
|
||||
// Extract targetplatform version regex
|
||||
$targetRegex = '';
|
||||
if (preg_match('/targetplatform[^>]*version="([^"]+)"/', $xml, $m)) {
|
||||
$targetRegex = $m[1];
|
||||
}
|
||||
|
||||
if (empty($targetRegex)) {
|
||||
echo "No targetplatform version found in {$relManifest}\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Manifest: {$relManifest}\n";
|
||||
echo "Target regex: {$targetRegex}\n";
|
||||
|
||||
// ── Fetch latest Joomla version ─────────────────────────────────────────
|
||||
$joomlaVersions = [];
|
||||
$updateUrl = 'https://update.joomla.org/core/sts/list_sts.xml';
|
||||
$updateXml = @file_get_contents($updateUrl);
|
||||
|
||||
if ($updateXml === false) {
|
||||
// Fallback: try the LTS feed
|
||||
$updateUrl = 'https://update.joomla.org/core/list.xml';
|
||||
$updateXml = @file_get_contents($updateUrl);
|
||||
}
|
||||
|
||||
if ($updateXml !== false) {
|
||||
// Parse all version entries
|
||||
preg_match_all('/<version>([^<]+)<\/version>/', $updateXml, $matches);
|
||||
$joomlaVersions = $matches[1] ?? [];
|
||||
}
|
||||
|
||||
if (empty($joomlaVersions)) {
|
||||
echo "WARNING: Could not fetch Joomla versions from update server\n";
|
||||
echo "Tested URL: {$updateUrl}\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Sort and get latest
|
||||
usort($joomlaVersions, 'version_compare');
|
||||
$latestJoomla = end($joomlaVersions);
|
||||
|
||||
echo "Latest Joomla: {$latestJoomla}\n";
|
||||
|
||||
// ── Test compatibility ──────────────────────────────────────────────────
|
||||
// The targetplatform regex uses Joomla's regex format
|
||||
// Common patterns: "5\.[0-9]+" or "((5.[0-9])|(6.[0-9]))"
|
||||
$compatible = @preg_match("/{$targetRegex}/", $latestJoomla);
|
||||
|
||||
if ($compatible === false) {
|
||||
echo "ERROR: Invalid regex in targetplatform: {$targetRegex}\n";
|
||||
$result = 'error';
|
||||
} elseif ($compatible === 1) {
|
||||
echo "PASS: Joomla {$latestJoomla} matches targetplatform regex\n";
|
||||
$result = 'pass';
|
||||
} else {
|
||||
// Check which major versions are supported
|
||||
$supported = [];
|
||||
foreach (['5.0', '5.1', '5.2', '5.3', '5.4', '6.0', '6.1', '6.2', '7.0'] as $v) {
|
||||
if (@preg_match("/{$targetRegex}/", $v)) {
|
||||
$supported[] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
echo "WARN: Joomla {$latestJoomla} does NOT match targetplatform regex\n";
|
||||
echo "Supported versions: " . implode(', ', $supported) . "\n";
|
||||
echo "Consider updating targetplatform to include Joomla {$latestJoomla}\n";
|
||||
$result = 'warn';
|
||||
}
|
||||
|
||||
// ── Export ───────────────────────────────────────────────────────────────
|
||||
if ($ghOutput) {
|
||||
$ghFile = getenv('GITHUB_OUTPUT');
|
||||
if ($ghFile) {
|
||||
file_put_contents($ghFile, "compat_result={$result}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "compat_joomla={$latestJoomla}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "compat_regex={$targetRegex}\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
exit($result === 'error' ? 1 : 0);
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/joomla_compat_check.php
|
||||
* BRIEF: Check if extension targetplatform regex matches the latest Joomla version
|
||||
*
|
||||
* Usage:
|
||||
* php joomla_compat_check.php --path /repo
|
||||
* php joomla_compat_check.php --path /repo --github-output
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (default: .)
|
||||
* --github-output Export results to $GITHUB_OUTPUT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$ghOutput = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--github-output') $ghOutput = true;
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
|
||||
// ── Find manifest and extract targetplatform ────────────────────────────
|
||||
$manifest = null;
|
||||
$searchDirs = ["{$root}/src", $root];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) continue;
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
$xml = file_get_contents($f);
|
||||
if (strpos($xml, '<extension') !== false && strpos($xml, 'targetplatform') !== false) {
|
||||
$manifest = $f;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($manifest === null) {
|
||||
fwrite(STDERR, "No manifest with targetplatform found\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$xml = file_get_contents($manifest);
|
||||
$relManifest = str_replace($root . '/', '', $manifest);
|
||||
|
||||
// Extract targetplatform version regex
|
||||
$targetRegex = '';
|
||||
if (preg_match('/targetplatform[^>]*version="([^"]+)"/', $xml, $m)) {
|
||||
$targetRegex = $m[1];
|
||||
}
|
||||
|
||||
if (empty($targetRegex)) {
|
||||
echo "No targetplatform version found in {$relManifest}\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Manifest: {$relManifest}\n";
|
||||
echo "Target regex: {$targetRegex}\n";
|
||||
|
||||
// ── Fetch latest Joomla version ─────────────────────────────────────────
|
||||
$joomlaVersions = [];
|
||||
$updateUrl = 'https://update.joomla.org/core/sts/list_sts.xml';
|
||||
$updateXml = @file_get_contents($updateUrl);
|
||||
|
||||
if ($updateXml === false) {
|
||||
// Fallback: try the LTS feed
|
||||
$updateUrl = 'https://update.joomla.org/core/list.xml';
|
||||
$updateXml = @file_get_contents($updateUrl);
|
||||
}
|
||||
|
||||
if ($updateXml !== false) {
|
||||
// Parse all version entries
|
||||
preg_match_all('/<version>([^<]+)<\/version>/', $updateXml, $matches);
|
||||
$joomlaVersions = $matches[1] ?? [];
|
||||
}
|
||||
|
||||
if (empty($joomlaVersions)) {
|
||||
echo "WARNING: Could not fetch Joomla versions from update server\n";
|
||||
echo "Tested URL: {$updateUrl}\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Sort and get latest
|
||||
usort($joomlaVersions, 'version_compare');
|
||||
$latestJoomla = end($joomlaVersions);
|
||||
|
||||
echo "Latest Joomla: {$latestJoomla}\n";
|
||||
|
||||
// ── Test compatibility ──────────────────────────────────────────────────
|
||||
// The targetplatform regex uses Joomla's regex format
|
||||
// Common patterns: "5\.[0-9]+" or "((5.[0-9])|(6.[0-9]))"
|
||||
$compatible = @preg_match("/{$targetRegex}/", $latestJoomla);
|
||||
|
||||
if ($compatible === false) {
|
||||
echo "ERROR: Invalid regex in targetplatform: {$targetRegex}\n";
|
||||
$result = 'error';
|
||||
} elseif ($compatible === 1) {
|
||||
echo "PASS: Joomla {$latestJoomla} matches targetplatform regex\n";
|
||||
$result = 'pass';
|
||||
} else {
|
||||
// Check which major versions are supported
|
||||
$supported = [];
|
||||
foreach (['5.0', '5.1', '5.2', '5.3', '5.4', '6.0', '6.1', '6.2', '7.0'] as $v) {
|
||||
if (@preg_match("/{$targetRegex}/", $v)) {
|
||||
$supported[] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
echo "WARN: Joomla {$latestJoomla} does NOT match targetplatform regex\n";
|
||||
echo "Supported versions: " . implode(', ', $supported) . "\n";
|
||||
echo "Consider updating targetplatform to include Joomla {$latestJoomla}\n";
|
||||
$result = 'warn';
|
||||
}
|
||||
|
||||
// ── Export ───────────────────────────────────────────────────────────────
|
||||
if ($ghOutput) {
|
||||
$ghFile = getenv('GITHUB_OUTPUT');
|
||||
if ($ghFile) {
|
||||
file_put_contents($ghFile, "compat_result={$result}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "compat_joomla={$latestJoomla}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "compat_regex={$targetRegex}\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
exit($result === 'error' ? 1 : 0);
|
||||
|
||||
+51
-2
@@ -157,6 +157,26 @@ function giteaUploadAsset(string $url, string $token, string $filePath): int
|
||||
return $httpCode;
|
||||
}
|
||||
|
||||
// ── Read platform from .mokogitea/manifest.xml ───────────────────────────────
|
||||
|
||||
$detectedPlatform = 'generic';
|
||||
$detectedEntryPoint = '';
|
||||
$mokoManifest = "{$root}/.mokogitea/manifest.xml";
|
||||
if (file_exists($mokoManifest)) {
|
||||
$mokoXml = @simplexml_load_file($mokoManifest);
|
||||
if ($mokoXml !== false) {
|
||||
$rawPlatform = (string)($mokoXml->governance->platform ?? '');
|
||||
if ($rawPlatform !== '') {
|
||||
$detectedPlatform = match ($rawPlatform) {
|
||||
'waas-component' => 'joomla',
|
||||
'crm-module' => 'dolibarr',
|
||||
default => $rawPlatform,
|
||||
};
|
||||
}
|
||||
$detectedEntryPoint = (string)($mokoXml->build->{"entry-point"} ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Detect element metadata from manifest XML ────────────────────────────────
|
||||
|
||||
$extElement = '';
|
||||
@@ -257,9 +277,19 @@ echo "TAR: {$baseName}.tar.gz\n";
|
||||
// ── Find source directory ────────────────────────────────────────────────────
|
||||
|
||||
$sourceDir = null;
|
||||
if (is_dir("{$root}/src")) {
|
||||
|
||||
// Use entry-point from manifest.xml if available
|
||||
if ($detectedEntryPoint !== '') {
|
||||
$entryDir = rtrim(dirname($detectedEntryPoint) === '.' ? $detectedEntryPoint : dirname($detectedEntryPoint), '/');
|
||||
if (is_dir("{$root}/{$entryDir}")) {
|
||||
$sourceDir = "{$root}/{$entryDir}";
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to common directories
|
||||
if ($sourceDir === null && is_dir("{$root}/src")) {
|
||||
$sourceDir = "{$root}/src";
|
||||
} elseif (is_dir("{$root}/htdocs")) {
|
||||
} elseif ($sourceDir === null && is_dir("{$root}/htdocs")) {
|
||||
$sourceDir = "{$root}/htdocs";
|
||||
}
|
||||
|
||||
@@ -383,6 +413,17 @@ if ($isJoomlaPackage) {
|
||||
}
|
||||
}
|
||||
|
||||
// Include top-level directories (e.g. language/) that aren't packages/
|
||||
$topLevelDirs = glob("{$sourceDir}/*", GLOB_ONLYDIR) ?: [];
|
||||
foreach ($topLevelDirs as $tlDir) {
|
||||
$dirName = basename($tlDir);
|
||||
if ($dirName === 'packages') {
|
||||
continue;
|
||||
}
|
||||
addDirToZip($zip, $tlDir, $dirName, $excludePatterns);
|
||||
echo " Included dir: {$dirName}/\n";
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
echo "ZIP created: {$zipFile}\n";
|
||||
} else {
|
||||
@@ -444,6 +485,14 @@ file_put_contents($tarSha, "{$tarHash} {$baseName}.tar.gz\n");
|
||||
|
||||
echo "SHA-256 (ZIP): {$zipHash}\n";
|
||||
echo "SHA-256 (TAR): {$tarHash}\n";
|
||||
echo "sha256_zip={$zipHash}\n";
|
||||
echo "zip_name={$baseName}.zip\n";
|
||||
|
||||
// Write to GITHUB_OUTPUT if available
|
||||
$ghOutput = getenv('GITHUB_OUTPUT');
|
||||
if ($ghOutput) {
|
||||
file_put_contents($ghOutput, "sha256_zip={$zipHash}\nzip_name={$baseName}.zip\n", FILE_APPEND);
|
||||
}
|
||||
|
||||
// ── Get release ID from tag ──────────────────────────────────────────────────
|
||||
|
||||
|
||||
+209
-209
@@ -1,209 +1,209 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/theme_lint.php
|
||||
* BRIEF: Lint theme files — CSS syntax, image sizes, hardcoded URLs
|
||||
*
|
||||
* Usage:
|
||||
* php theme_lint.php --path /repo
|
||||
* php theme_lint.php --path /repo --max-image-kb 500
|
||||
* php theme_lint.php --path /repo --github-output
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (default: .)
|
||||
* --max-image-kb Maximum image file size in KB (default: 500)
|
||||
* --github-output Export results to $GITHUB_OUTPUT
|
||||
* --strict Exit 1 on any warning (default: only on errors)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$maxImageKb = 500;
|
||||
$ghOutput = false;
|
||||
$strict = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--max-image-kb' && isset($argv[$i + 1])) $maxImageKb = (int)$argv[$i + 1];
|
||||
if ($arg === '--github-output') $ghOutput = true;
|
||||
if ($arg === '--strict') $strict = true;
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
$errors = 0;
|
||||
$warnings = 0;
|
||||
|
||||
// ── Find source directory ───────────────────────────────────────────────
|
||||
$srcDir = null;
|
||||
foreach (['src', 'htdocs'] as $d) {
|
||||
if (is_dir("{$root}/{$d}")) { $srcDir = "{$root}/{$d}"; break; }
|
||||
}
|
||||
if ($srcDir === null) {
|
||||
fwrite(STDERR, "No src/ or htdocs/ directory in {$root}\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Theme Lint: {$srcDir}\n\n";
|
||||
|
||||
// ── Check 1: CSS syntax validation ──────────────────────────────────────
|
||||
echo "--- CSS Syntax ---\n";
|
||||
$cssFiles = findFiles($srcDir, '*.css');
|
||||
$cssMinFiles = findFiles($srcDir, '*.min.css');
|
||||
$cssToCheck = array_diff($cssFiles, $cssMinFiles);
|
||||
|
||||
if (empty($cssToCheck)) {
|
||||
echo " No CSS files to check\n";
|
||||
} else {
|
||||
foreach ($cssToCheck as $file) {
|
||||
$content = file_get_contents($file);
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
|
||||
// Check for unmatched braces
|
||||
$openBraces = substr_count($content, '{');
|
||||
$closeBraces = substr_count($content, '}');
|
||||
if ($openBraces !== $closeBraces) {
|
||||
echo " ERROR: {$relPath}: unmatched braces (open={$openBraces}, close={$closeBraces})\n";
|
||||
$errors++;
|
||||
}
|
||||
|
||||
// Check for empty rules
|
||||
if (preg_match_all('/\{[\s]*\}/', $content, $m)) {
|
||||
$count = count($m[0]);
|
||||
echo " WARN: {$relPath}: {$count} empty rule(s)\n";
|
||||
$warnings++;
|
||||
}
|
||||
|
||||
// Check for !important abuse (more than 10 in one file)
|
||||
$importantCount = substr_count($content, '!important');
|
||||
if ($importantCount > 10) {
|
||||
echo " WARN: {$relPath}: {$importantCount} !important declarations (consider refactoring)\n";
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors === 0) {
|
||||
echo " OK: " . count($cssToCheck) . " CSS file(s) checked\n";
|
||||
}
|
||||
}
|
||||
|
||||
// ── Check 2: Image file sizes ───────────────────────────────────────────
|
||||
echo "\n--- Image Sizes (max {$maxImageKb}KB) ---\n";
|
||||
$imageExts = ['*.jpg', '*.jpeg', '*.png', '*.gif', '*.webp', '*.svg', '*.bmp'];
|
||||
$images = [];
|
||||
foreach ($imageExts as $ext) {
|
||||
$images = array_merge($images, findFiles($srcDir, $ext));
|
||||
}
|
||||
// Also check root images/ directory
|
||||
if (is_dir("{$root}/images")) {
|
||||
foreach ($imageExts as $ext) {
|
||||
$images = array_merge($images, findFiles("{$root}/images", $ext));
|
||||
}
|
||||
}
|
||||
|
||||
$oversized = 0;
|
||||
$totalSize = 0;
|
||||
foreach ($images as $file) {
|
||||
$size = filesize($file);
|
||||
$totalSize += $size;
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
$sizeKb = round($size / 1024);
|
||||
|
||||
if ($sizeKb > $maxImageKb) {
|
||||
echo " WARN: {$relPath}: {$sizeKb}KB (exceeds {$maxImageKb}KB limit)\n";
|
||||
$oversized++;
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
|
||||
$totalMb = round($totalSize / 1024 / 1024, 1);
|
||||
echo " " . count($images) . " image(s), {$totalMb}MB total";
|
||||
if ($oversized > 0) {
|
||||
echo ", {$oversized} oversized";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// ── Check 3: Hardcoded URLs in CSS/JS ───────────────────────────────────
|
||||
echo "\n--- Hardcoded URLs ---\n";
|
||||
$codeFiles = array_merge(
|
||||
findFiles($srcDir, '*.css'),
|
||||
findFiles($srcDir, '*.js')
|
||||
);
|
||||
// Exclude minified files
|
||||
$codeFiles = array_filter($codeFiles, function($f) {
|
||||
return !preg_match('/\.min\.(css|js)$/', $f);
|
||||
});
|
||||
|
||||
$urlPatterns = [
|
||||
'/https?:\/\/clarksvillefurs\.com/' => 'hardcoded production URL',
|
||||
'/https?:\/\/[a-z]+\.dev\.mokoconsulting\.tech/' => 'hardcoded dev URL',
|
||||
'/https?:\/\/localhost/' => 'localhost reference',
|
||||
];
|
||||
|
||||
$urlIssues = 0;
|
||||
foreach ($codeFiles as $file) {
|
||||
$content = file_get_contents($file);
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
|
||||
foreach ($urlPatterns as $pattern => $desc) {
|
||||
if (preg_match_all($pattern, $content, $matches)) {
|
||||
$count = count($matches[0]);
|
||||
echo " WARN: {$relPath}: {$count} {$desc}\n";
|
||||
$urlIssues++;
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($urlIssues === 0) {
|
||||
echo " OK: No hardcoded URLs found\n";
|
||||
}
|
||||
|
||||
// ── Summary ─────────────────────────────────────────────────────────────
|
||||
echo "\n=== Summary ===\n";
|
||||
echo "Errors: {$errors}\n";
|
||||
echo "Warnings: {$warnings}\n";
|
||||
|
||||
if ($ghOutput) {
|
||||
$ghFile = getenv('GITHUB_OUTPUT');
|
||||
if ($ghFile) {
|
||||
file_put_contents($ghFile, "lint_errors={$errors}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "lint_warnings={$warnings}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "lint_images=" . count($images) . "\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "lint_css=" . count($cssToCheck) . "\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors > 0) {
|
||||
exit(1);
|
||||
}
|
||||
if ($strict && $warnings > 0) {
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
|
||||
// ── Helper: recursively find files matching a glob pattern ──────────────
|
||||
function findFiles(string $dir, string $pattern): array
|
||||
{
|
||||
$results = [];
|
||||
if (!is_dir($dir)) return $results;
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if (fnmatch($pattern, $file->getFilename())) {
|
||||
$results[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/theme_lint.php
|
||||
* BRIEF: Lint theme files — CSS syntax, image sizes, hardcoded URLs
|
||||
*
|
||||
* Usage:
|
||||
* php theme_lint.php --path /repo
|
||||
* php theme_lint.php --path /repo --max-image-kb 500
|
||||
* php theme_lint.php --path /repo --github-output
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (default: .)
|
||||
* --max-image-kb Maximum image file size in KB (default: 500)
|
||||
* --github-output Export results to $GITHUB_OUTPUT
|
||||
* --strict Exit 1 on any warning (default: only on errors)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$maxImageKb = 500;
|
||||
$ghOutput = false;
|
||||
$strict = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--max-image-kb' && isset($argv[$i + 1])) $maxImageKb = (int)$argv[$i + 1];
|
||||
if ($arg === '--github-output') $ghOutput = true;
|
||||
if ($arg === '--strict') $strict = true;
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
$errors = 0;
|
||||
$warnings = 0;
|
||||
|
||||
// ── Find source directory ───────────────────────────────────────────────
|
||||
$srcDir = null;
|
||||
foreach (['src', 'htdocs'] as $d) {
|
||||
if (is_dir("{$root}/{$d}")) { $srcDir = "{$root}/{$d}"; break; }
|
||||
}
|
||||
if ($srcDir === null) {
|
||||
fwrite(STDERR, "No src/ or htdocs/ directory in {$root}\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Theme Lint: {$srcDir}\n\n";
|
||||
|
||||
// ── Check 1: CSS syntax validation ──────────────────────────────────────
|
||||
echo "--- CSS Syntax ---\n";
|
||||
$cssFiles = findFiles($srcDir, '*.css');
|
||||
$cssMinFiles = findFiles($srcDir, '*.min.css');
|
||||
$cssToCheck = array_diff($cssFiles, $cssMinFiles);
|
||||
|
||||
if (empty($cssToCheck)) {
|
||||
echo " No CSS files to check\n";
|
||||
} else {
|
||||
foreach ($cssToCheck as $file) {
|
||||
$content = file_get_contents($file);
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
|
||||
// Check for unmatched braces
|
||||
$openBraces = substr_count($content, '{');
|
||||
$closeBraces = substr_count($content, '}');
|
||||
if ($openBraces !== $closeBraces) {
|
||||
echo " ERROR: {$relPath}: unmatched braces (open={$openBraces}, close={$closeBraces})\n";
|
||||
$errors++;
|
||||
}
|
||||
|
||||
// Check for empty rules
|
||||
if (preg_match_all('/\{[\s]*\}/', $content, $m)) {
|
||||
$count = count($m[0]);
|
||||
echo " WARN: {$relPath}: {$count} empty rule(s)\n";
|
||||
$warnings++;
|
||||
}
|
||||
|
||||
// Check for !important abuse (more than 10 in one file)
|
||||
$importantCount = substr_count($content, '!important');
|
||||
if ($importantCount > 10) {
|
||||
echo " WARN: {$relPath}: {$importantCount} !important declarations (consider refactoring)\n";
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors === 0) {
|
||||
echo " OK: " . count($cssToCheck) . " CSS file(s) checked\n";
|
||||
}
|
||||
}
|
||||
|
||||
// ── Check 2: Image file sizes ───────────────────────────────────────────
|
||||
echo "\n--- Image Sizes (max {$maxImageKb}KB) ---\n";
|
||||
$imageExts = ['*.jpg', '*.jpeg', '*.png', '*.gif', '*.webp', '*.svg', '*.bmp'];
|
||||
$images = [];
|
||||
foreach ($imageExts as $ext) {
|
||||
$images = array_merge($images, findFiles($srcDir, $ext));
|
||||
}
|
||||
// Also check root images/ directory
|
||||
if (is_dir("{$root}/images")) {
|
||||
foreach ($imageExts as $ext) {
|
||||
$images = array_merge($images, findFiles("{$root}/images", $ext));
|
||||
}
|
||||
}
|
||||
|
||||
$oversized = 0;
|
||||
$totalSize = 0;
|
||||
foreach ($images as $file) {
|
||||
$size = filesize($file);
|
||||
$totalSize += $size;
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
$sizeKb = round($size / 1024);
|
||||
|
||||
if ($sizeKb > $maxImageKb) {
|
||||
echo " WARN: {$relPath}: {$sizeKb}KB (exceeds {$maxImageKb}KB limit)\n";
|
||||
$oversized++;
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
|
||||
$totalMb = round($totalSize / 1024 / 1024, 1);
|
||||
echo " " . count($images) . " image(s), {$totalMb}MB total";
|
||||
if ($oversized > 0) {
|
||||
echo ", {$oversized} oversized";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// ── Check 3: Hardcoded URLs in CSS/JS ───────────────────────────────────
|
||||
echo "\n--- Hardcoded URLs ---\n";
|
||||
$codeFiles = array_merge(
|
||||
findFiles($srcDir, '*.css'),
|
||||
findFiles($srcDir, '*.js')
|
||||
);
|
||||
// Exclude minified files
|
||||
$codeFiles = array_filter($codeFiles, function($f) {
|
||||
return !preg_match('/\.min\.(css|js)$/', $f);
|
||||
});
|
||||
|
||||
$urlPatterns = [
|
||||
'/https?:\/\/clarksvillefurs\.com/' => 'hardcoded production URL',
|
||||
'/https?:\/\/[a-z]+\.dev\.mokoconsulting\.tech/' => 'hardcoded dev URL',
|
||||
'/https?:\/\/localhost/' => 'localhost reference',
|
||||
];
|
||||
|
||||
$urlIssues = 0;
|
||||
foreach ($codeFiles as $file) {
|
||||
$content = file_get_contents($file);
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
|
||||
foreach ($urlPatterns as $pattern => $desc) {
|
||||
if (preg_match_all($pattern, $content, $matches)) {
|
||||
$count = count($matches[0]);
|
||||
echo " WARN: {$relPath}: {$count} {$desc}\n";
|
||||
$urlIssues++;
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($urlIssues === 0) {
|
||||
echo " OK: No hardcoded URLs found\n";
|
||||
}
|
||||
|
||||
// ── Summary ─────────────────────────────────────────────────────────────
|
||||
echo "\n=== Summary ===\n";
|
||||
echo "Errors: {$errors}\n";
|
||||
echo "Warnings: {$warnings}\n";
|
||||
|
||||
if ($ghOutput) {
|
||||
$ghFile = getenv('GITHUB_OUTPUT');
|
||||
if ($ghFile) {
|
||||
file_put_contents($ghFile, "lint_errors={$errors}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "lint_warnings={$warnings}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "lint_images=" . count($images) . "\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "lint_css=" . count($cssToCheck) . "\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors > 0) {
|
||||
exit(1);
|
||||
}
|
||||
if ($strict && $warnings > 0) {
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
|
||||
// ── Helper: recursively find files matching a glob pattern ──────────────
|
||||
function findFiles(string $dir, string $pattern): array
|
||||
{
|
||||
$results = [];
|
||||
if (!is_dir($dir)) return $results;
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if (fnmatch($pattern, $file->getFilename())) {
|
||||
$results[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
+286
-194
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
@@ -42,174 +43,255 @@ $outputFile = null;
|
||||
$githubOutput = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--version' && isset($argv[$i + 1])) $version = $argv[$i + 1];
|
||||
if ($arg === '--stability' && isset($argv[$i + 1])) $stability = $argv[$i + 1];
|
||||
if ($arg === '--sha' && isset($argv[$i + 1])) $sha = $argv[$i + 1];
|
||||
if ($arg === '--gitea-url' && isset($argv[$i + 1])) $giteaUrl = $argv[$i + 1];
|
||||
if ($arg === '--org' && isset($argv[$i + 1])) $org = $argv[$i + 1];
|
||||
if ($arg === '--repo' && isset($argv[$i + 1])) $repo = $argv[$i + 1];
|
||||
if ($arg === '--output' && isset($argv[$i + 1])) $outputFile = $argv[$i + 1];
|
||||
if ($arg === '--github-output') $githubOutput = true;
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) {
|
||||
$path = $argv[$i + 1];
|
||||
}
|
||||
if ($arg === '--version' && isset($argv[$i + 1])) {
|
||||
$version = $argv[$i + 1];
|
||||
}
|
||||
if ($arg === '--stability' && isset($argv[$i + 1])) {
|
||||
$stability = $argv[$i + 1];
|
||||
}
|
||||
if ($arg === '--sha' && isset($argv[$i + 1])) {
|
||||
$sha = $argv[$i + 1];
|
||||
}
|
||||
if ($arg === '--gitea-url' && isset($argv[$i + 1])) {
|
||||
$giteaUrl = $argv[$i + 1];
|
||||
}
|
||||
if ($arg === '--org' && isset($argv[$i + 1])) {
|
||||
$org = $argv[$i + 1];
|
||||
}
|
||||
if ($arg === '--repo' && isset($argv[$i + 1])) {
|
||||
$repo = $argv[$i + 1];
|
||||
}
|
||||
if ($arg === '--output' && isset($argv[$i + 1])) {
|
||||
$outputFile = $argv[$i + 1];
|
||||
}
|
||||
if ($arg === '--github-output') {
|
||||
$githubOutput = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($version === null) {
|
||||
fwrite(STDERR, "Usage: updates_xml_build.php --path . --version XX.YY.ZZ [--stability stable] [--sha SHA]\n");
|
||||
exit(1);
|
||||
fwrite(STDERR, "Usage: updates_xml_build.php --path . --version XX.YY.ZZ [--stability stable] [--sha SHA]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
|
||||
// -- Read platform from .mokogitea/manifest.xml --------------------------------
|
||||
$detectedPlatform = 'joomla'; // default for backward compat
|
||||
$detectedName = $repo;
|
||||
$detectedPackageType = '';
|
||||
$mokoManifest = "{$root}/.mokogitea/manifest.xml";
|
||||
if (file_exists($mokoManifest)) {
|
||||
$mokoXml = @simplexml_load_file($mokoManifest);
|
||||
if ($mokoXml !== false) {
|
||||
$rawPlatform = (string)($mokoXml->governance->platform ?? '');
|
||||
if ($rawPlatform !== '') {
|
||||
$detectedPlatform = match ($rawPlatform) {
|
||||
'waas-component' => 'joomla',
|
||||
'crm-module' => 'dolibarr',
|
||||
default => $rawPlatform,
|
||||
};
|
||||
}
|
||||
$detectedName = (string)($mokoXml->identity->name ?? $repo);
|
||||
$detectedPackageType = (string)($mokoXml->build->{"package-type"} ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
// -- Locate Joomla manifest ---------------------------------------------------
|
||||
$manifest = null;
|
||||
|
||||
// Priority: pkg_*.xml in src/ > any extension XML in src/ > any in root
|
||||
$candidates = glob("{$root}/src/pkg_*.xml") ?: [];
|
||||
foreach ($candidates as $f) {
|
||||
if (strpos(file_get_contents($f), '<extension') !== false) {
|
||||
$manifest = $f;
|
||||
break;
|
||||
}
|
||||
if (strpos(file_get_contents($f), '<extension') !== false) {
|
||||
$manifest = $f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($manifest === null) {
|
||||
$searchDirs = ["{$root}/src", "{$root}"];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) continue;
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
if (strpos(file_get_contents($f), '<extension') !== false) {
|
||||
$manifest = $f;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$searchDirs = ["{$root}/src", "{$root}"];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) {
|
||||
continue;
|
||||
}
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
if (strpos(file_get_contents($f), '<extension') !== false) {
|
||||
$manifest = $f;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($manifest === null) {
|
||||
fwrite(STDERR, "No Joomla XML manifest found in {$root}\n");
|
||||
exit(1);
|
||||
if ($manifest === null && $detectedPlatform === 'joomla') {
|
||||
fwrite(STDERR, "No Joomla XML manifest found in {$root}\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// -- Parse extension metadata -------------------------------------------------
|
||||
$xml = file_get_contents($manifest);
|
||||
|
||||
// Extract fields via regex (more portable than SimpleXML for malformed manifests)
|
||||
$extName = '';
|
||||
if (preg_match('/<name>([^<]+)<\/name>/', $xml, $m)) $extName = $m[1];
|
||||
|
||||
$extType = '';
|
||||
if (preg_match('/<extension[^>]*type="([^"]+)"/', $xml, $m)) $extType = $m[1];
|
||||
|
||||
$extElement = '';
|
||||
if (preg_match('/<element>([^<]+)<\/element>/', $xml, $m)) $extElement = $m[1];
|
||||
// For packages, prefer <packagename> to avoid pkg_pkg_ duplication
|
||||
if (empty($extElement) && preg_match('/<packagename>([^<]+)<\/packagename>/', $xml, $m)) $extElement = $m[1];
|
||||
if (empty($extElement) && preg_match('/plugin="([^"]+)"/', $xml, $m)) $extElement = $m[1];
|
||||
if (empty($extElement) && preg_match('/module="([^"]+)"/', $xml, $m)) $extElement = $m[1];
|
||||
if (empty($extElement)) {
|
||||
$fname = strtolower(pathinfo($manifest, PATHINFO_FILENAME));
|
||||
if (in_array($fname, ['templatedetails', 'manifest'])) {
|
||||
$extElement = strtolower(str_replace([' ', '-'], '', $repo ?: basename($root)));
|
||||
} else {
|
||||
$extElement = $fname;
|
||||
}
|
||||
}
|
||||
// Strip existing type prefix to prevent duplication (e.g. pkg_mokowaas → mokowaas)
|
||||
$extElement = preg_replace('/^(pkg_|com_|mod_|plg_\w+_|tpl_|lib_)/', '', $extElement);
|
||||
|
||||
$extClient = '';
|
||||
if (preg_match('/<extension[^>]*client="([^"]+)"/', $xml, $m)) $extClient = $m[1];
|
||||
|
||||
$extFolder = '';
|
||||
if (preg_match('/<extension[^>]*group="([^"]+)"/', $xml, $m)) $extFolder = $m[1];
|
||||
|
||||
$targetPlatform = '';
|
||||
if (preg_match('/(<targetplatform[^\/]*\/>)/', $xml, $m)) $targetPlatform = $m[1];
|
||||
if (empty($targetPlatform)) {
|
||||
$targetPlatform = '<targetplatform name="joomla" version="(5|6)\..*" />';
|
||||
}
|
||||
|
||||
$phpMinimum = '';
|
||||
if (preg_match('/<php_minimum>([^<]+)<\/php_minimum>/', $xml, $m)) $phpMinimum = $m[1];
|
||||
|
||||
if ($manifest !== null) {
|
||||
// Joomla manifest found — parse extension metadata from it
|
||||
$xml = file_get_contents($manifest);
|
||||
|
||||
if (preg_match('/<name>([^<]+)<\/name>/', $xml, $m)) {
|
||||
$extName = $m[1];
|
||||
}
|
||||
if (preg_match('/<extension[^>]*type="([^"]+)"/', $xml, $m)) {
|
||||
$extType = $m[1];
|
||||
}
|
||||
if (preg_match('/<element>([^<]+)<\/element>/', $xml, $m)) {
|
||||
$extElement = $m[1];
|
||||
}
|
||||
if (empty($extElement) && preg_match('/<packagename>([^<]+)<\/packagename>/', $xml, $m)) {
|
||||
$extElement = $m[1];
|
||||
}
|
||||
if (empty($extElement) && preg_match('/plugin="([^"]+)"/', $xml, $m)) {
|
||||
$extElement = $m[1];
|
||||
}
|
||||
if (empty($extElement) && preg_match('/module="([^"]+)"/', $xml, $m)) {
|
||||
$extElement = $m[1];
|
||||
}
|
||||
if (empty($extElement)) {
|
||||
$fname = strtolower(pathinfo($manifest, PATHINFO_FILENAME));
|
||||
if (in_array($fname, ['templatedetails', 'manifest'])) {
|
||||
$extElement = strtolower(str_replace([' ', '-'], '', $repo ?: basename($root)));
|
||||
} else {
|
||||
$extElement = $fname;
|
||||
}
|
||||
}
|
||||
$extElement = preg_replace('/^(pkg_|com_|mod_|plg_\w+_|tpl_|lib_)/', '', $extElement);
|
||||
|
||||
if (preg_match('/<extension[^>]*client="([^"]+)"/', $xml, $m)) {
|
||||
$extClient = $m[1];
|
||||
}
|
||||
if (preg_match('/<extension[^>]*group="([^"]+)"/', $xml, $m)) {
|
||||
$extFolder = $m[1];
|
||||
}
|
||||
if (preg_match('/(<targetplatform[^\/]*\/>)/', $xml, $m)) {
|
||||
$targetPlatform = $m[1];
|
||||
}
|
||||
if (empty($targetPlatform)) {
|
||||
$targetPlatform = '<targetplatform name="joomla" version="(5|6)\..*" />';
|
||||
}
|
||||
if (preg_match('/<php_minimum>([^<]+)<\/php_minimum>/', $xml, $m)) {
|
||||
$phpMinimum = $m[1];
|
||||
}
|
||||
} else {
|
||||
// Non-Joomla platform — derive metadata from .mokogitea/manifest.xml
|
||||
$extName = $detectedName ?: ($repo ?: basename($root));
|
||||
$extElement = strtolower(str_replace([' ', '-'], '', $extName));
|
||||
$extType = $detectedPackageType ?: 'generic';
|
||||
$targetPlatform = "<targetplatform name=\"{$detectedPlatform}\" version=\".*\" />";
|
||||
}
|
||||
|
||||
// Resolve language key names (e.g. PLG_SYSTEM_MOKOJOOMTOS)
|
||||
if (preg_match('/^[A-Z_]+$/', $extName)) {
|
||||
$iniFiles = [];
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
foreach ($iterator as $file) {
|
||||
if (preg_match('/\.sys\.ini$/i', $file->getFilename())) {
|
||||
$iniFiles[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
foreach ($iniFiles as $ini) {
|
||||
$content = file_get_contents($ini);
|
||||
if (preg_match('/^' . preg_quote($extName, '/') . '="([^"]+)"/m', $content, $m)) {
|
||||
$extName = $m[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$iniFiles = [];
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
foreach ($iterator as $file) {
|
||||
if (preg_match('/\.sys\.ini$/i', $file->getFilename())) {
|
||||
$iniFiles[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
foreach ($iniFiles as $ini) {
|
||||
$content = file_get_contents($ini);
|
||||
if (preg_match('/^' . preg_quote($extName, '/') . '="([^"]+)"/m', $content, $m)) {
|
||||
$extName = $m[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallbacks
|
||||
if (empty($extName)) $extName = $repo ?: basename($root);
|
||||
if (empty($extType)) $extType = 'component';
|
||||
if (empty($extName)) {
|
||||
$extName = $repo ?: basename($root);
|
||||
}
|
||||
if (empty($extType)) {
|
||||
$extType = 'component';
|
||||
}
|
||||
|
||||
// -- Build type prefix --------------------------------------------------------
|
||||
$typePrefix = '';
|
||||
switch ($extType) {
|
||||
case 'plugin': $typePrefix = "plg_{$extFolder}_"; break;
|
||||
case 'module': $typePrefix = 'mod_'; break;
|
||||
case 'component': $typePrefix = 'com_'; break;
|
||||
case 'template': $typePrefix = 'tpl_'; break;
|
||||
case 'library': $typePrefix = 'lib_'; break;
|
||||
case 'package': $typePrefix = 'pkg_'; break;
|
||||
case 'plugin':
|
||||
$typePrefix = "plg_{$extFolder}_";
|
||||
break;
|
||||
case 'module':
|
||||
$typePrefix = 'mod_';
|
||||
break;
|
||||
case 'component':
|
||||
$typePrefix = 'com_';
|
||||
break;
|
||||
case 'template':
|
||||
$typePrefix = 'tpl_';
|
||||
break;
|
||||
case 'library':
|
||||
$typePrefix = 'lib_';
|
||||
break;
|
||||
case 'package':
|
||||
$typePrefix = 'pkg_';
|
||||
break;
|
||||
}
|
||||
|
||||
// -- Export to GITHUB_OUTPUT if requested -------------------------------------
|
||||
if ($githubOutput) {
|
||||
$ghOutput = getenv('GITHUB_OUTPUT');
|
||||
$lines = [
|
||||
"ext_element={$extElement}",
|
||||
"ext_name={$extName}",
|
||||
"ext_type={$extType}",
|
||||
"ext_folder={$extFolder}",
|
||||
"type_prefix={$typePrefix}",
|
||||
];
|
||||
if ($ghOutput) {
|
||||
file_put_contents($ghOutput, implode("\n", $lines) . "\n", FILE_APPEND);
|
||||
fwrite(STDERR, "Exported " . count($lines) . " fields to GITHUB_OUTPUT\n");
|
||||
} else {
|
||||
foreach ($lines as $line) echo "{$line}\n";
|
||||
}
|
||||
$ghOutput = getenv('GITHUB_OUTPUT');
|
||||
$lines = [
|
||||
"ext_element={$extElement}",
|
||||
"ext_name={$extName}",
|
||||
"ext_type={$extType}",
|
||||
"ext_folder={$extFolder}",
|
||||
"type_prefix={$typePrefix}",
|
||||
];
|
||||
if ($ghOutput) {
|
||||
file_put_contents($ghOutput, implode("\n", $lines) . "\n", FILE_APPEND);
|
||||
fwrite(STDERR, "Exported " . count($lines) . " fields to GITHUB_OUTPUT\n");
|
||||
} else {
|
||||
foreach ($lines as $line) {
|
||||
echo "{$line}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Stability suffix map -----------------------------------------------------
|
||||
$stabilitySuffixMap = [
|
||||
'stable' => '',
|
||||
'rc' => '-rc',
|
||||
'beta' => '-beta',
|
||||
'alpha' => '-alpha',
|
||||
'development' => '-dev',
|
||||
'stable' => '',
|
||||
'rc' => '-rc',
|
||||
'beta' => '-beta',
|
||||
'alpha' => '-alpha',
|
||||
'development' => '-dev',
|
||||
];
|
||||
|
||||
// Joomla <tags><tag> values — maps to Joomla's stabilityTagToInteger()
|
||||
$stabilityTagMap = [
|
||||
'stable' => 'stable',
|
||||
'rc' => 'rc',
|
||||
'beta' => 'beta',
|
||||
'alpha' => 'alpha',
|
||||
'development' => 'dev',
|
||||
'stable' => 'stable',
|
||||
'rc' => 'rc',
|
||||
'beta' => 'beta',
|
||||
'alpha' => 'alpha',
|
||||
'development' => 'dev',
|
||||
];
|
||||
|
||||
// Gitea release tag names (used in download/info URLs)
|
||||
$releaseTagMap = [
|
||||
'stable' => 'stable',
|
||||
'rc' => 'release-candidate',
|
||||
'beta' => 'beta',
|
||||
'alpha' => 'alpha',
|
||||
'development' => 'development',
|
||||
'stable' => 'stable',
|
||||
'rc' => 'release-candidate',
|
||||
'beta' => 'beta',
|
||||
'alpha' => 'alpha',
|
||||
'development' => 'development',
|
||||
];
|
||||
|
||||
// -- Build update entries -----------------------------------------------------
|
||||
@@ -221,70 +303,78 @@ $primaryVersion = $version . $primarySuffix;
|
||||
// to installed extensions. Without it, extension_id=0 in #__updates.
|
||||
$clientTag = '';
|
||||
if (!empty($extClient)) {
|
||||
$clientTag = " <client>{$extClient}</client>";
|
||||
$clientTag = " <client>{$extClient}</client>";
|
||||
} else {
|
||||
$clientTag = ' <client>site</client>';
|
||||
$clientTag = ' <client>site</client>';
|
||||
}
|
||||
|
||||
// Build folder tag
|
||||
$folderTag = '';
|
||||
if (!empty($extFolder) && $extType === 'plugin') {
|
||||
$folderTag = " <folder>{$extFolder}</folder>";
|
||||
$folderTag = " <folder>{$extFolder}</folder>";
|
||||
}
|
||||
|
||||
// PHP minimum tag
|
||||
$phpTag = '';
|
||||
if (!empty($phpMinimum)) {
|
||||
$phpTag = " <php_minimum>{$phpMinimum}</php_minimum>";
|
||||
$phpTag = " <php_minimum>{$phpMinimum}</php_minimum>";
|
||||
}
|
||||
|
||||
// SHA tag
|
||||
$shaTag = '';
|
||||
if (!empty($sha)) {
|
||||
$shaTag = " <sha256>{$sha}</sha256>";
|
||||
$shaTag = " <sha256>{$sha}</sha256>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a single <update> entry for a given stability tag
|
||||
*/
|
||||
function buildEntry(
|
||||
string $tagName,
|
||||
string $entryVersion,
|
||||
string $entryDownloadUrl,
|
||||
string $extName,
|
||||
string $extElement,
|
||||
string $extType,
|
||||
string $clientTag,
|
||||
string $folderTag,
|
||||
string $infoUrl,
|
||||
string $targetPlatform,
|
||||
string $phpTag,
|
||||
string $shaTag
|
||||
string $tagName,
|
||||
string $entryVersion,
|
||||
string $entryDownloadUrl,
|
||||
string $extName,
|
||||
string $extElement,
|
||||
string $extType,
|
||||
string $clientTag,
|
||||
string $folderTag,
|
||||
string $infoUrl,
|
||||
string $targetPlatform,
|
||||
string $phpTag,
|
||||
string $shaTag
|
||||
): string {
|
||||
$lines = [];
|
||||
$lines[] = ' <update>';
|
||||
$lines[] = " <name>{$extName}</name>";
|
||||
$lines[] = " <description>{$extName} update</description>";
|
||||
// Element in updates.xml must match what Joomla stores in #__extensions
|
||||
// For packages: pkg_elementname. For plugins: elementname (folder handles grouping).
|
||||
$dbElement = ($extType === 'package') ? "pkg_{$extElement}" : $extElement;
|
||||
$lines[] = " <element>{$dbElement}</element>";
|
||||
$lines[] = " <type>{$extType}</type>";
|
||||
$lines[] = " <version>{$entryVersion}</version>";
|
||||
if (!empty($clientTag)) $lines[] = $clientTag;
|
||||
if (!empty($folderTag)) $lines[] = $folderTag;
|
||||
$lines[] = " <tags><tag>{$tagName}</tag></tags>";
|
||||
$lines[] = " <infourl title=\"{$extName}\">{$infoUrl}</infourl>";
|
||||
$lines[] = ' <downloads>';
|
||||
$lines[] = " <downloadurl type=\"full\" format=\"zip\">{$entryDownloadUrl}</downloadurl>";
|
||||
$lines[] = ' </downloads>';
|
||||
if (!empty($shaTag)) $lines[] = $shaTag;
|
||||
$lines[] = " {$targetPlatform}";
|
||||
if (!empty($phpTag)) $lines[] = $phpTag;
|
||||
$lines[] = ' <maintainer>Moko Consulting</maintainer>';
|
||||
$lines[] = ' <maintainerurl>https://mokoconsulting.tech</maintainerurl>';
|
||||
$lines[] = ' </update>';
|
||||
return implode("\n", $lines);
|
||||
$lines = [];
|
||||
$lines[] = ' <update>';
|
||||
$lines[] = " <name>{$extName}</name>";
|
||||
$lines[] = " <description>{$extName} update</description>";
|
||||
// Element in updates.xml must match what Joomla stores in #__extensions
|
||||
// For packages: pkg_elementname. For plugins: elementname (folder handles grouping).
|
||||
$dbElement = ($extType === 'package') ? "pkg_{$extElement}" : $extElement;
|
||||
$lines[] = " <element>{$dbElement}</element>";
|
||||
$lines[] = " <type>{$extType}</type>";
|
||||
$lines[] = " <version>{$entryVersion}</version>";
|
||||
if (!empty($clientTag)) {
|
||||
$lines[] = $clientTag;
|
||||
}
|
||||
if (!empty($folderTag)) {
|
||||
$lines[] = $folderTag;
|
||||
}
|
||||
$lines[] = " <tags><tag>{$tagName}</tag></tags>";
|
||||
$lines[] = " <infourl title=\"{$extName}\">{$infoUrl}</infourl>";
|
||||
$lines[] = ' <downloads>';
|
||||
$lines[] = " <downloadurl type=\"full\" format=\"zip\">{$entryDownloadUrl}</downloadurl>";
|
||||
$lines[] = ' </downloads>';
|
||||
if (!empty($shaTag)) {
|
||||
$lines[] = $shaTag;
|
||||
}
|
||||
$lines[] = " {$targetPlatform}";
|
||||
if (!empty($phpTag)) {
|
||||
$lines[] = $phpTag;
|
||||
}
|
||||
$lines[] = ' <maintainer>Moko Consulting</maintainer>';
|
||||
$lines[] = ' <maintainerurl>https://mokoconsulting.tech</maintainerurl>';
|
||||
$lines[] = ' </update>';
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
// -- Determine which channels to write ----------------------------------------
|
||||
@@ -295,7 +385,9 @@ function buildEntry(
|
||||
// When dev releases, only dev is updated; everything else is preserved.
|
||||
$allChannels = ['development', 'alpha', 'beta', 'rc', 'stable'];
|
||||
$stabilityIndex = array_search($stability === 'development' ? 'development' : $stability, $allChannels);
|
||||
if ($stabilityIndex === false) $stabilityIndex = 4; // default to stable
|
||||
if ($stabilityIndex === false) {
|
||||
$stabilityIndex = 4; // default to stable
|
||||
}
|
||||
|
||||
// Write entries for the current channel AND all lower channels (cascade down)
|
||||
// All cascaded entries point to the CURRENT release (the highest stability being built)
|
||||
@@ -306,25 +398,25 @@ $channelDownloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$giteaTag}/
|
||||
$channelInfoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$giteaTag}";
|
||||
|
||||
for ($i = 0; $i <= $stabilityIndex; $i++) {
|
||||
$channelName = $allChannels[$i];
|
||||
$joomlaTag = $stabilityTagMap[$channelName] ?? $channelName;
|
||||
// Only attach SHA to the primary channel entry
|
||||
$entrySha = ($i === $stabilityIndex) ? $shaTag : '';
|
||||
$channelName = $allChannels[$i];
|
||||
$joomlaTag = $stabilityTagMap[$channelName] ?? $channelName;
|
||||
// Only attach SHA to the primary channel entry
|
||||
$entrySha = ($i === $stabilityIndex) ? $shaTag : '';
|
||||
|
||||
$entries[] = buildEntry(
|
||||
$joomlaTag,
|
||||
$channelVersion,
|
||||
$channelDownloadUrl,
|
||||
$extName,
|
||||
$extElement,
|
||||
$extType,
|
||||
$clientTag,
|
||||
$folderTag,
|
||||
$channelInfoUrl,
|
||||
$targetPlatform,
|
||||
$phpTag,
|
||||
$entrySha
|
||||
);
|
||||
$entries[] = buildEntry(
|
||||
$joomlaTag,
|
||||
$channelVersion,
|
||||
$channelDownloadUrl,
|
||||
$extName,
|
||||
$extElement,
|
||||
$extType,
|
||||
$clientTag,
|
||||
$folderTag,
|
||||
$channelInfoUrl,
|
||||
$targetPlatform,
|
||||
$phpTag,
|
||||
$entrySha
|
||||
);
|
||||
}
|
||||
|
||||
// -- Preserve existing entries for channels not being updated -----------------
|
||||
@@ -332,27 +424,27 @@ $dest = $outputFile ?? "{$root}/updates.xml";
|
||||
$preservedEntries = [];
|
||||
|
||||
if (file_exists($dest)) {
|
||||
$existingXml = @simplexml_load_file($dest);
|
||||
if ($existingXml) {
|
||||
// Joomla tags we're writing — don't preserve these
|
||||
$writtenChannels = [];
|
||||
for ($i = 0; $i <= $stabilityIndex; $i++) {
|
||||
$writtenChannels[] = $stabilityTagMap[$allChannels[$i]] ?? $allChannels[$i];
|
||||
}
|
||||
// Also match legacy/alternate tag names (e.g. 'development' = 'dev')
|
||||
$writtenChannels[] = 'development'; // alias for 'dev'
|
||||
$existingXml = @simplexml_load_file($dest);
|
||||
if ($existingXml) {
|
||||
// Joomla tags we're writing — don't preserve these
|
||||
$writtenChannels = [];
|
||||
for ($i = 0; $i <= $stabilityIndex; $i++) {
|
||||
$writtenChannels[] = $stabilityTagMap[$allChannels[$i]] ?? $allChannels[$i];
|
||||
}
|
||||
// Also match legacy/alternate tag names (e.g. 'development' = 'dev')
|
||||
$writtenChannels[] = 'development'; // alias for 'dev'
|
||||
|
||||
foreach ($existingXml->update as $existingUpdate) {
|
||||
$existingTag = '';
|
||||
if (isset($existingUpdate->tags->tag)) {
|
||||
$existingTag = (string) $existingUpdate->tags->tag;
|
||||
}
|
||||
// Keep entries for channels we're NOT overwriting
|
||||
if (!empty($existingTag) && !in_array($existingTag, $writtenChannels, true)) {
|
||||
$preservedEntries[] = ' ' . trim($existingUpdate->asXML());
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($existingXml->update as $existingUpdate) {
|
||||
$existingTag = '';
|
||||
if (isset($existingUpdate->tags->tag)) {
|
||||
$existingTag = (string) $existingUpdate->tags->tag;
|
||||
}
|
||||
// Keep entries for channels we're NOT overwriting
|
||||
if (!empty($existingTag) && !in_array($existingTag, $writtenChannels, true)) {
|
||||
$preservedEntries[] = ' ' . trim($existingUpdate->asXML());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Write updates.xml --------------------------------------------------------
|
||||
|
||||
+81
-101
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
@@ -10,7 +9,7 @@
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/version_bump.php
|
||||
* BRIEF: Auto-increment version — manifest.xml is canonical, also updates README.md and Joomla XML
|
||||
* BRIEF: Auto-increment version — manifest.xml is canonical, cascades to all XML and MD files
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
@@ -18,27 +17,23 @@ declare(strict_types=1);
|
||||
$path = '.';
|
||||
$type = 'patch'; // patch | minor | major
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) {
|
||||
$path = $argv[$i + 1];
|
||||
}
|
||||
if ($arg === '--minor') {
|
||||
$type = 'minor';
|
||||
}
|
||||
if ($arg === '--major') {
|
||||
$type = 'major';
|
||||
}
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--minor') $type = 'minor';
|
||||
if ($arg === '--major') $type = 'major';
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
|
||||
// -- 1. Read version from .mokogitea/manifest.xml (canonical) --
|
||||
$mokoVersion = null;
|
||||
$mokoSuffix = '';
|
||||
$mokoManifest = "{$root}/.mokogitea/manifest.xml";
|
||||
$mokoContent = '';
|
||||
if (file_exists($mokoManifest)) {
|
||||
$mokoContent = file_get_contents($mokoManifest);
|
||||
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})</version>|', $mokoContent, $m)) {
|
||||
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(?:-([a-z]+))?</version>|', $mokoContent, $m)) {
|
||||
$mokoVersion = $m[1];
|
||||
$mokoSuffix = isset($m[2]) ? $m[2] : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +50,7 @@ if (file_exists($readme)) {
|
||||
|
||||
// -- 3. Fallback: Joomla manifest XML --
|
||||
$manifestVersion = null;
|
||||
$manifestSuffix = '';
|
||||
$manifestFiles = array_merge(
|
||||
glob("{$root}/src/pkg_*.xml") ?: [],
|
||||
glob("{$root}/src/*.xml") ?: [],
|
||||
@@ -68,10 +64,12 @@ foreach ($manifestFiles as $xmlFile) {
|
||||
if (strpos($xmlContent, '<extension') === false && strpos($xmlContent, '<version>') === false) {
|
||||
continue;
|
||||
}
|
||||
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(?:-[a-z]+)?</version>|', $xmlContent, $xm)) {
|
||||
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(-[a-z]+)?</version>|', $xmlContent, $xm)) {
|
||||
$candidate = $xm[1];
|
||||
if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) {
|
||||
$manifestVersion = $candidate;
|
||||
// Preserve the suffix from the manifest (e.g. dev, rc) — strip leading dash
|
||||
$manifestSuffix = ltrim($xm[2] ?? '', '-');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,123 +100,105 @@ $patch = (int)$parts[3];
|
||||
$old = sprintf('%02d.%02d.%02d', $major, $minor, $patch);
|
||||
|
||||
switch ($type) {
|
||||
case 'major':
|
||||
$major++;
|
||||
$minor = 0;
|
||||
$patch = 0;
|
||||
break;
|
||||
case 'minor':
|
||||
$minor++;
|
||||
$patch = 0;
|
||||
break;
|
||||
case 'major': $major++; $minor = 0; $patch = 0; break;
|
||||
case 'minor': $minor++; $patch = 0; break;
|
||||
default:
|
||||
$patch++;
|
||||
if ($patch > 99) {
|
||||
$minor++;
|
||||
$patch = 0;
|
||||
}
|
||||
if ($minor > 99) {
|
||||
$major++;
|
||||
$minor = 0;
|
||||
}
|
||||
if ($patch > 99) { $minor++; $patch = 0; }
|
||||
if ($minor > 99) { $major++; $minor = 0; }
|
||||
break;
|
||||
}
|
||||
|
||||
$new = sprintf('%02d.%02d.%02d', $major, $minor, $patch);
|
||||
|
||||
// -- Update .mokogitea/manifest.xml (canonical target) --
|
||||
// -- Determine suffix to preserve (moko manifest takes priority, then Joomla) --
|
||||
$suffix = !empty($mokoSuffix) ? $mokoSuffix : (!empty($manifestSuffix) ? $manifestSuffix : '');
|
||||
$newFull = $suffix !== '' ? "{$new}-{$suffix}" : $new;
|
||||
|
||||
// -- Update .mokogitea/manifest.xml (canonical — preserves suffix) --
|
||||
if (file_exists($mokoManifest) && !empty($mokoContent)) {
|
||||
if (preg_match('|<version>\d{2}\.\d{2}\.\d{2}</version>|', $mokoContent)) {
|
||||
// Replace existing version tag
|
||||
$updated = preg_replace(
|
||||
'|<version>\d{2}\.\d{2}\.\d{2}</version>|',
|
||||
"<version>{$new}</version>",
|
||||
$mokoContent,
|
||||
1
|
||||
);
|
||||
} else {
|
||||
// Insert <version> before <license> (per schema order) or as last child of <identity>
|
||||
if (strpos($mokoContent, '<license') !== false) {
|
||||
$updated = preg_replace(
|
||||
'|(\s*<license)|',
|
||||
"\n <version>{$new}</version>\$1",
|
||||
$mokoContent,
|
||||
1
|
||||
);
|
||||
} elseif (strpos($mokoContent, '</identity>') !== false) {
|
||||
$updated = preg_replace(
|
||||
'|(</identity>)|',
|
||||
" <version>{$new}</version>\n \$1",
|
||||
$mokoContent,
|
||||
1
|
||||
);
|
||||
} else {
|
||||
$updated = $mokoContent;
|
||||
}
|
||||
}
|
||||
$updated = preg_replace(
|
||||
'|<version>\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?</version>|',
|
||||
"<version>{$newFull}</version>",
|
||||
$mokoContent,
|
||||
1
|
||||
);
|
||||
file_put_contents($mokoManifest, $updated);
|
||||
}
|
||||
|
||||
// -- Update README.md --
|
||||
if (file_exists($readme) && !empty($readmeContent)) {
|
||||
$updated = preg_replace(
|
||||
'/(VERSION:\s*)\d{2}\.\d{2}\.\d{2}/m',
|
||||
'${1}' . $new,
|
||||
'/(VERSION:\s*)\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?/m',
|
||||
'${1}' . $newFull,
|
||||
$readmeContent,
|
||||
1
|
||||
);
|
||||
file_put_contents($readme, $updated);
|
||||
}
|
||||
|
||||
// ── Update manifest XML files ────────────────────────────────────────────────
|
||||
foreach ($manifestFiles as $xmlFile) {
|
||||
$xmlContent = file_get_contents($xmlFile);
|
||||
if (strpos($xmlContent, '<extension') === false && strpos($xmlContent, '<version>') === false) {
|
||||
continue;
|
||||
}
|
||||
$updatedXml = preg_replace(
|
||||
'|<version>\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?</version>|',
|
||||
"<version>{$new}</version>",
|
||||
$xmlContent
|
||||
);
|
||||
if ($updatedXml !== $xmlContent) {
|
||||
file_put_contents($xmlFile, $updatedXml);
|
||||
// -- Cascade to ALL Joomla extension XML manifests --
|
||||
$xmlPatterns = [
|
||||
"{$root}/src/pkg_*.xml",
|
||||
"{$root}/src/*.xml",
|
||||
"{$root}/src/packages/*/*.xml",
|
||||
"{$root}/*.xml",
|
||||
];
|
||||
|
||||
$updatedFiles = [];
|
||||
foreach ($xmlPatterns as $pattern) {
|
||||
foreach (glob($pattern) ?: [] as $xmlFile) {
|
||||
$content = file_get_contents($xmlFile);
|
||||
// Only update files that have an <extension> tag (Joomla manifests)
|
||||
if (strpos($content, '<extension') === false) {
|
||||
continue;
|
||||
}
|
||||
$newContent = preg_replace(
|
||||
'|<version>\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?</version>|',
|
||||
"<version>{$newFull}</version>",
|
||||
$content
|
||||
);
|
||||
if ($newContent !== $content) {
|
||||
file_put_contents($xmlFile, $newContent);
|
||||
$updatedFiles[] = substr($xmlFile, strlen($root) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Update Dolibarr mod*.class.php ───────────────────────────────────────────
|
||||
$modFiles = array_merge(
|
||||
glob("{$root}/src/core/modules/mod*.class.php") ?: [],
|
||||
glob("{$root}/htdocs/core/modules/mod*.class.php") ?: []
|
||||
);
|
||||
foreach ($modFiles as $modFile) {
|
||||
$modContent = file_get_contents($modFile);
|
||||
if (strpos($modContent, 'extends DolibarrModules') === false) {
|
||||
continue;
|
||||
}
|
||||
$updatedMod = preg_replace(
|
||||
'/(\$this->version\s*=\s*)[\'"][^\'"]*[\'"]/',
|
||||
"\${1}'{$new}'",
|
||||
$modContent
|
||||
if (!empty($updatedFiles)) {
|
||||
fwrite(STDERR, "Updated " . count($updatedFiles) . " Joomla manifest(s): " . implode(', ', $updatedFiles) . "\n");
|
||||
}
|
||||
|
||||
// -- Update package.json (Node.js / MCP) --
|
||||
$packageJsonFile = "{$root}/package.json";
|
||||
if (file_exists($packageJsonFile)) {
|
||||
$pkgContent = file_get_contents($packageJsonFile);
|
||||
$updatedPkg = preg_replace(
|
||||
'/("version"\s*:\s*")\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?(")/m',
|
||||
'${1}' . $newFull . '${2}',
|
||||
$pkgContent
|
||||
);
|
||||
if ($updatedMod !== $modContent) {
|
||||
file_put_contents($modFile, $updatedMod);
|
||||
if ($updatedPkg !== $pkgContent) {
|
||||
file_put_contents($packageJsonFile, $updatedPkg);
|
||||
fwrite(STDERR, "Updated package.json\n");
|
||||
}
|
||||
}
|
||||
|
||||
// ── Update composer.json ─────────────────────────────────────────────────────
|
||||
$composerFile = "{$root}/composer.json";
|
||||
if (file_exists($composerFile)) {
|
||||
$composerContent = file_get_contents($composerFile);
|
||||
$updatedComposer = preg_replace(
|
||||
'/("version"\s*:\s*")\d{2}\.\d{2}\.\d{2}(")/m',
|
||||
'${1}' . $new . '${2}',
|
||||
$composerContent
|
||||
// -- Update pyproject.toml (Python) --
|
||||
$pyprojectFile = "{$root}/pyproject.toml";
|
||||
if (file_exists($pyprojectFile)) {
|
||||
$pyContent = file_get_contents($pyprojectFile);
|
||||
$updatedPy = preg_replace(
|
||||
'/^(version\s*=\s*")\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?(")/m',
|
||||
'${1}' . $newFull . '${2}',
|
||||
$pyContent
|
||||
);
|
||||
if ($updatedComposer !== $composerContent) {
|
||||
file_put_contents($composerFile, $updatedComposer);
|
||||
if ($updatedPy !== $pyContent) {
|
||||
file_put_contents($pyprojectFile, $updatedPy);
|
||||
fwrite(STDERR, "Updated pyproject.toml\n");
|
||||
}
|
||||
}
|
||||
|
||||
echo "{$old} → {$new}\n";
|
||||
$oldFull = $suffix !== '' ? "{$old}-{$suffix}" : $old;
|
||||
echo "{$oldFull} -> {$newFull}\n";
|
||||
exit(0);
|
||||
|
||||
+233
-233
@@ -1,233 +1,233 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/version_bump_remote.php
|
||||
* BRIEF: Bump version in manifest XML and CHANGELOG.md on a remote branch via Gitea API
|
||||
*
|
||||
* Usage:
|
||||
* php version_bump_remote.php --path . --branch dev --bump minor --token TOKEN --api-base URL
|
||||
* php version_bump_remote.php --path . --branch dev --bump patch --token TOKEN --api-base URL
|
||||
* php version_bump_remote.php --path . --branch dev --bump minor --no-changelog --token TOKEN --api-base URL
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (reads current version from local manifest)
|
||||
* --branch Target branch to bump (required, e.g. dev)
|
||||
* --bump Bump type: patch | minor | major (default: minor)
|
||||
* --token Gitea API token (or GA_TOKEN env var)
|
||||
* --api-base Gitea API base URL for the repo
|
||||
* --no-changelog Skip CHANGELOG.md bump
|
||||
* --repo Repository path (owner/repo) for API base construction
|
||||
* --gitea-url Gitea instance URL (default: env GITEA_URL)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$branch = null;
|
||||
$bumpType = 'minor';
|
||||
$token = null;
|
||||
$apiBase = null;
|
||||
$noChangelog = false;
|
||||
$repo = null;
|
||||
$giteaUrl = null;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--branch' && isset($argv[$i + 1])) $branch = $argv[$i + 1];
|
||||
if ($arg === '--bump' && isset($argv[$i + 1])) $bumpType = $argv[$i + 1];
|
||||
if ($arg === '--token' && isset($argv[$i + 1])) $token = $argv[$i + 1];
|
||||
if ($arg === '--api-base' && isset($argv[$i + 1])) $apiBase = $argv[$i + 1];
|
||||
if ($arg === '--no-changelog') $noChangelog = true;
|
||||
if ($arg === '--repo' && isset($argv[$i + 1])) $repo = $argv[$i + 1];
|
||||
if ($arg === '--gitea-url' && isset($argv[$i + 1])) $giteaUrl = $argv[$i + 1];
|
||||
}
|
||||
|
||||
if ($token === null) $token = getenv('GA_TOKEN') ?: getenv('GITEA_TOKEN') ?: null;
|
||||
if ($giteaUrl === null) $giteaUrl = getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech';
|
||||
|
||||
if ($apiBase === null && $repo !== null) {
|
||||
$apiBase = rtrim($giteaUrl, '/') . '/api/v1/repos/' . $repo;
|
||||
}
|
||||
|
||||
if ($branch === null || $token === null || $apiBase === null) {
|
||||
fwrite(STDERR, "Usage: version_bump_remote.php --branch BRANCH --token TOKEN --api-base URL [--bump minor|patch|major]\n");
|
||||
fwrite(STDERR, " or: version_bump_remote.php --branch BRANCH --token TOKEN --repo owner/repo\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
|
||||
// ── Read current version from local manifest ────────────────────────────
|
||||
$version = null;
|
||||
$manifestFile = null;
|
||||
|
||||
$searchDirs = ["{$root}/src", $root];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) continue;
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
$xml = file_get_contents($f);
|
||||
if (strpos($xml, '<extension') !== false || strpos($xml, '<version>') !== false) {
|
||||
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})</version>|', $xml, $m)) {
|
||||
if ($version === null || version_compare($m[1], $version, '>')) {
|
||||
$version = $m[1];
|
||||
$manifestFile = basename($f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($version === null) {
|
||||
fwrite(STDERR, "No version found in manifest XML\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// ── Compute next version ────────────────────────────────────────────────
|
||||
if (!preg_match('/^(\d{2})\.(\d{2})\.(\d{2})$/', $version, $parts)) {
|
||||
fwrite(STDERR, "Invalid version format: {$version}\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$major = (int)$parts[1];
|
||||
$minor = (int)$parts[2];
|
||||
$patch = (int)$parts[3];
|
||||
|
||||
switch ($bumpType) {
|
||||
case 'major': $major++; $minor = 0; $patch = 0; break;
|
||||
case 'minor': $minor++; $patch = 0; break;
|
||||
default: $patch++; break;
|
||||
}
|
||||
|
||||
$nextVersion = sprintf('%02d.%02d.%02d', $major, $minor, $patch);
|
||||
echo "{$version} -> {$nextVersion} ({$branch})\n";
|
||||
|
||||
// ── Helper: Gitea API request ───────────────────────────────────────────
|
||||
function giteaApi(string $method, string $url, string $token, ?string $body = null): ?array
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: token {$token}",
|
||||
'Content-Type: application/json',
|
||||
],
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
]);
|
||||
if ($body !== null) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode >= 400 || $response === false) {
|
||||
return null;
|
||||
}
|
||||
return json_decode($response, true) ?: [];
|
||||
}
|
||||
|
||||
// ── Helper: Update a file on a remote branch ────────────────────────────
|
||||
function updateRemoteFile(
|
||||
string $apiBase,
|
||||
string $token,
|
||||
string $filePath,
|
||||
string $branch,
|
||||
callable $transform,
|
||||
string $commitMessage
|
||||
): bool {
|
||||
$url = "{$apiBase}/contents/{$filePath}?ref={$branch}";
|
||||
$file = giteaApi('GET', $url, $token);
|
||||
if ($file === null || !isset($file['sha']) || !isset($file['content'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$content = base64_decode($file['content']);
|
||||
$newContent = $transform($content);
|
||||
|
||||
if ($newContent === $content) {
|
||||
fwrite(STDERR, " {$filePath}: no changes needed\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
$payload = json_encode([
|
||||
'content' => base64_encode($newContent),
|
||||
'sha' => $file['sha'],
|
||||
'message' => $commitMessage,
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
$result = giteaApi('PUT', "{$apiBase}/contents/{$filePath}", $token, $payload);
|
||||
if ($result === null) {
|
||||
fwrite(STDERR, " {$filePath}: failed to update\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
echo " {$filePath}: updated on {$branch}\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Update manifest XML on the remote branch ────────────────────────────
|
||||
$manifestPaths = [];
|
||||
if ($manifestFile !== null) {
|
||||
$manifestPaths[] = "src/{$manifestFile}";
|
||||
}
|
||||
$manifestPaths = array_merge($manifestPaths, [
|
||||
'src/templateDetails.xml',
|
||||
'src/manifest.xml',
|
||||
]);
|
||||
|
||||
$manifestUpdated = false;
|
||||
foreach ($manifestPaths as $mPath) {
|
||||
$result = updateRemoteFile(
|
||||
$apiBase, $token, $mPath, $branch,
|
||||
function (string $content) use ($version, $nextVersion): string {
|
||||
return str_replace(
|
||||
"<version>{$version}</version>",
|
||||
"<version>{$nextVersion}</version>",
|
||||
$content
|
||||
);
|
||||
},
|
||||
"chore(version): bump {$version} -> {$nextVersion} [skip ci]"
|
||||
);
|
||||
if ($result) {
|
||||
$manifestUpdated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$manifestUpdated) {
|
||||
fwrite(STDERR, "WARNING: could not update manifest on {$branch}\n");
|
||||
}
|
||||
|
||||
// ── Update CHANGELOG.md on the remote branch ────────────────────────────
|
||||
if (!$noChangelog) {
|
||||
updateRemoteFile(
|
||||
$apiBase, $token, 'CHANGELOG.md', $branch,
|
||||
function (string $content) use ($version, $nextVersion): string {
|
||||
$content = str_replace("VERSION: {$version}", "VERSION: {$nextVersion}", $content);
|
||||
|
||||
if (strpos($content, '[Unreleased]') === false
|
||||
&& strpos($content, "## [{$nextVersion}]") === false
|
||||
) {
|
||||
$marker = "## [{$version}]";
|
||||
if (strpos($content, $marker) !== false) {
|
||||
$unreleased = "## [{$nextVersion}] - Unreleased\n\n### Added\n\n### Changed\n\n### Fixed\n\n";
|
||||
$content = str_replace($marker, $unreleased . $marker, $content);
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
},
|
||||
"chore(version): bump CHANGELOG {$version} -> {$nextVersion} [skip ci]"
|
||||
);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/version_bump_remote.php
|
||||
* BRIEF: Bump version in manifest XML and CHANGELOG.md on a remote branch via Gitea API
|
||||
*
|
||||
* Usage:
|
||||
* php version_bump_remote.php --path . --branch dev --bump minor --token TOKEN --api-base URL
|
||||
* php version_bump_remote.php --path . --branch dev --bump patch --token TOKEN --api-base URL
|
||||
* php version_bump_remote.php --path . --branch dev --bump minor --no-changelog --token TOKEN --api-base URL
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (reads current version from local manifest)
|
||||
* --branch Target branch to bump (required, e.g. dev)
|
||||
* --bump Bump type: patch | minor | major (default: minor)
|
||||
* --token Gitea API token (or GA_TOKEN env var)
|
||||
* --api-base Gitea API base URL for the repo
|
||||
* --no-changelog Skip CHANGELOG.md bump
|
||||
* --repo Repository path (owner/repo) for API base construction
|
||||
* --gitea-url Gitea instance URL (default: env GITEA_URL)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$branch = null;
|
||||
$bumpType = 'minor';
|
||||
$token = null;
|
||||
$apiBase = null;
|
||||
$noChangelog = false;
|
||||
$repo = null;
|
||||
$giteaUrl = null;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--branch' && isset($argv[$i + 1])) $branch = $argv[$i + 1];
|
||||
if ($arg === '--bump' && isset($argv[$i + 1])) $bumpType = $argv[$i + 1];
|
||||
if ($arg === '--token' && isset($argv[$i + 1])) $token = $argv[$i + 1];
|
||||
if ($arg === '--api-base' && isset($argv[$i + 1])) $apiBase = $argv[$i + 1];
|
||||
if ($arg === '--no-changelog') $noChangelog = true;
|
||||
if ($arg === '--repo' && isset($argv[$i + 1])) $repo = $argv[$i + 1];
|
||||
if ($arg === '--gitea-url' && isset($argv[$i + 1])) $giteaUrl = $argv[$i + 1];
|
||||
}
|
||||
|
||||
if ($token === null) $token = getenv('GA_TOKEN') ?: getenv('GITEA_TOKEN') ?: null;
|
||||
if ($giteaUrl === null) $giteaUrl = getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech';
|
||||
|
||||
if ($apiBase === null && $repo !== null) {
|
||||
$apiBase = rtrim($giteaUrl, '/') . '/api/v1/repos/' . $repo;
|
||||
}
|
||||
|
||||
if ($branch === null || $token === null || $apiBase === null) {
|
||||
fwrite(STDERR, "Usage: version_bump_remote.php --branch BRANCH --token TOKEN --api-base URL [--bump minor|patch|major]\n");
|
||||
fwrite(STDERR, " or: version_bump_remote.php --branch BRANCH --token TOKEN --repo owner/repo\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
|
||||
// ── Read current version from local manifest ────────────────────────────
|
||||
$version = null;
|
||||
$manifestFile = null;
|
||||
|
||||
$searchDirs = ["{$root}/src", $root];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) continue;
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
$xml = file_get_contents($f);
|
||||
if (strpos($xml, '<extension') !== false || strpos($xml, '<version>') !== false) {
|
||||
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})</version>|', $xml, $m)) {
|
||||
if ($version === null || version_compare($m[1], $version, '>')) {
|
||||
$version = $m[1];
|
||||
$manifestFile = basename($f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($version === null) {
|
||||
fwrite(STDERR, "No version found in manifest XML\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// ── Compute next version ────────────────────────────────────────────────
|
||||
if (!preg_match('/^(\d{2})\.(\d{2})\.(\d{2})$/', $version, $parts)) {
|
||||
fwrite(STDERR, "Invalid version format: {$version}\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$major = (int)$parts[1];
|
||||
$minor = (int)$parts[2];
|
||||
$patch = (int)$parts[3];
|
||||
|
||||
switch ($bumpType) {
|
||||
case 'major': $major++; $minor = 0; $patch = 0; break;
|
||||
case 'minor': $minor++; $patch = 0; break;
|
||||
default: $patch++; break;
|
||||
}
|
||||
|
||||
$nextVersion = sprintf('%02d.%02d.%02d', $major, $minor, $patch);
|
||||
echo "{$version} -> {$nextVersion} ({$branch})\n";
|
||||
|
||||
// ── Helper: Gitea API request ───────────────────────────────────────────
|
||||
function giteaApi(string $method, string $url, string $token, ?string $body = null): ?array
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: token {$token}",
|
||||
'Content-Type: application/json',
|
||||
],
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
]);
|
||||
if ($body !== null) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode >= 400 || $response === false) {
|
||||
return null;
|
||||
}
|
||||
return json_decode($response, true) ?: [];
|
||||
}
|
||||
|
||||
// ── Helper: Update a file on a remote branch ────────────────────────────
|
||||
function updateRemoteFile(
|
||||
string $apiBase,
|
||||
string $token,
|
||||
string $filePath,
|
||||
string $branch,
|
||||
callable $transform,
|
||||
string $commitMessage
|
||||
): bool {
|
||||
$url = "{$apiBase}/contents/{$filePath}?ref={$branch}";
|
||||
$file = giteaApi('GET', $url, $token);
|
||||
if ($file === null || !isset($file['sha']) || !isset($file['content'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$content = base64_decode($file['content']);
|
||||
$newContent = $transform($content);
|
||||
|
||||
if ($newContent === $content) {
|
||||
fwrite(STDERR, " {$filePath}: no changes needed\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
$payload = json_encode([
|
||||
'content' => base64_encode($newContent),
|
||||
'sha' => $file['sha'],
|
||||
'message' => $commitMessage,
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
$result = giteaApi('PUT', "{$apiBase}/contents/{$filePath}", $token, $payload);
|
||||
if ($result === null) {
|
||||
fwrite(STDERR, " {$filePath}: failed to update\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
echo " {$filePath}: updated on {$branch}\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Update manifest XML on the remote branch ────────────────────────────
|
||||
$manifestPaths = [];
|
||||
if ($manifestFile !== null) {
|
||||
$manifestPaths[] = "src/{$manifestFile}";
|
||||
}
|
||||
$manifestPaths = array_merge($manifestPaths, [
|
||||
'src/templateDetails.xml',
|
||||
'src/manifest.xml',
|
||||
]);
|
||||
|
||||
$manifestUpdated = false;
|
||||
foreach ($manifestPaths as $mPath) {
|
||||
$result = updateRemoteFile(
|
||||
$apiBase, $token, $mPath, $branch,
|
||||
function (string $content) use ($version, $nextVersion): string {
|
||||
return str_replace(
|
||||
"<version>{$version}</version>",
|
||||
"<version>{$nextVersion}</version>",
|
||||
$content
|
||||
);
|
||||
},
|
||||
"chore(version): bump {$version} -> {$nextVersion} [skip ci]"
|
||||
);
|
||||
if ($result) {
|
||||
$manifestUpdated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$manifestUpdated) {
|
||||
fwrite(STDERR, "WARNING: could not update manifest on {$branch}\n");
|
||||
}
|
||||
|
||||
// ── Update CHANGELOG.md on the remote branch ────────────────────────────
|
||||
if (!$noChangelog) {
|
||||
updateRemoteFile(
|
||||
$apiBase, $token, 'CHANGELOG.md', $branch,
|
||||
function (string $content) use ($version, $nextVersion): string {
|
||||
$content = str_replace("VERSION: {$version}", "VERSION: {$nextVersion}", $content);
|
||||
|
||||
if (strpos($content, '[Unreleased]') === false
|
||||
&& strpos($content, "## [{$nextVersion}]") === false
|
||||
) {
|
||||
$marker = "## [{$version}]";
|
||||
if (strpos($content, $marker) !== false) {
|
||||
$unreleased = "## [{$nextVersion}] - Unreleased\n\n### Added\n\n### Changed\n\n### Fixed\n\n";
|
||||
$content = str_replace($marker, $unreleased . $marker, $content);
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
},
|
||||
"chore(version): bump CHANGELOG {$version} -> {$nextVersion} [skip ci]"
|
||||
);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
+39
-12
@@ -30,7 +30,7 @@ if (file_exists($mokoManifest)) {
|
||||
$xml = @simplexml_load_file($mokoManifest);
|
||||
if ($xml !== false) {
|
||||
$v = (string)($xml->identity->version ?? '');
|
||||
if (preg_match('/^\d{2}\.\d{2}\.\d{2}$/', $v)) {
|
||||
if (preg_match('/^\d{2}\.\d{2}\.\d{2}(-[a-z]+)?$/', $v)) {
|
||||
$mokoVersion = $v;
|
||||
}
|
||||
}
|
||||
@@ -66,33 +66,60 @@ foreach ($manifestFiles as $xmlFile) {
|
||||
if (strpos($xmlContent, '<extension') === false && strpos($xmlContent, '<version>') === false) {
|
||||
continue;
|
||||
}
|
||||
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(?:-[a-z]+)?</version>|', $xmlContent, $xm)) {
|
||||
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?)</version>|', $xmlContent, $xm)) {
|
||||
$candidate = $xm[1];
|
||||
if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) {
|
||||
$candidateBase = preg_replace('/-[a-z]+$/', '', $candidate);
|
||||
$currentBase = $manifestVersion ? preg_replace('/-[a-z]+$/', '', $manifestVersion) : null;
|
||||
if ($currentBase === null || version_compare($candidateBase, $currentBase, '>')) {
|
||||
$manifestVersion = $candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- 4. Fallback: package.json (Node.js / MCP) --
|
||||
$packageJsonVersion = null;
|
||||
$packageJsonFile = "{$root}/package.json";
|
||||
if (file_exists($packageJsonFile)) {
|
||||
$pkgData = json_decode(file_get_contents($packageJsonFile), true);
|
||||
if (isset($pkgData['version']) && preg_match('/^\d{2}\.\d{2}\.\d{2}$/', $pkgData['version'])) {
|
||||
$packageJsonVersion = $pkgData['version'];
|
||||
}
|
||||
}
|
||||
|
||||
// -- 5. Fallback: pyproject.toml (Python) --
|
||||
$pyprojectVersion = null;
|
||||
$pyprojectFile = "{$root}/pyproject.toml";
|
||||
if (file_exists($pyprojectFile)) {
|
||||
$pyContent = file_get_contents($pyprojectFile);
|
||||
if (preg_match('/^version\s*=\s*"(\d{2}\.\d{2}\.\d{2})"/m', $pyContent, $pm)) {
|
||||
$pyprojectVersion = $pm[1];
|
||||
}
|
||||
}
|
||||
|
||||
// -- Output the higher version --
|
||||
$candidates = array_filter([
|
||||
$readmeVersion,
|
||||
$manifestVersion,
|
||||
$packageJsonVersion,
|
||||
$pyprojectVersion,
|
||||
]);
|
||||
|
||||
$version = null;
|
||||
if ($readmeVersion !== null && $manifestVersion !== null) {
|
||||
$version = version_compare($manifestVersion, $readmeVersion, '>') ? $manifestVersion : $readmeVersion;
|
||||
} elseif ($manifestVersion !== null) {
|
||||
$version = $manifestVersion;
|
||||
} elseif ($readmeVersion !== null) {
|
||||
$version = $readmeVersion;
|
||||
foreach ($candidates as $candidate) {
|
||||
if ($version === null || version_compare($candidate, $version, '>')) {
|
||||
$version = $candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if ($version === null) {
|
||||
fwrite(STDERR, "No version found in manifest.xml, README.md, or Joomla XML\n");
|
||||
fwrite(STDERR, "No version found in manifest.xml, README.md, Joomla XML, package.json, or pyproject.toml\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// -- Backfill: if manifest.xml exists but lacks <version>, insert it --
|
||||
if ($mokoVersion === null && file_exists($mokoManifest)) {
|
||||
if (file_exists($mokoManifest)) {
|
||||
$content = file_get_contents($mokoManifest);
|
||||
if (!preg_match('|<version>\d{2}\.\d{2}\.\d{2}</version>|', $content)) {
|
||||
if (!preg_match('|<version>\d{2}\.\d{2}\.\d{2}(-[a-z]+)?</version>|', $content)) {
|
||||
if (strpos($content, '<license') !== false) {
|
||||
$content = preg_replace(
|
||||
'|(\s*<license)|',
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
/**
|
||||
* Client Repository Structure Definition
|
||||
* Standard repository structure for managed Joomla client sites (WaaS)
|
||||
*
|
||||
* This is NOT a Joomla extension — it's a full managed client site with
|
||||
* deployment configs, monitoring, SFTP settings, and sync workflows.
|
||||
* The src/ directory mirrors the Joomla site's public_html.
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Client Site"
|
||||
description = "Managed Joomla client site — full site structure, not an extension"
|
||||
repository_type = "client"
|
||||
platform = "client"
|
||||
last_updated = "2026-05-09T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "01.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
detection_hints = [
|
||||
"scripts/sftp-config/",
|
||||
"scripts/sync-dev-to-live.sh",
|
||||
"monitoring/grafana/",
|
||||
"src/administrator/",
|
||||
"src/components/",
|
||||
"src/plugins/",
|
||||
"src/templates/",
|
||||
"src/media/templates/site/mokoonyx/"
|
||||
]
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Client site overview and deployment info"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Release history"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "GPL-3.0-or-later license file"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/LICENSE"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
extension = ""
|
||||
description = "Build and deployment targets (includes minify)"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "PHP dependencies"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = ""
|
||||
description = "Git ignore rules (must include *.min.css, *.min.js, TODO.md)"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Joomla site public_html mirror — deployed via SFTP"
|
||||
required = true
|
||||
purpose = "Contains the full Joomla site directory structure"
|
||||
subdirectories = [
|
||||
{ name = "administrator", path = "src/administrator", description = "Joomla admin", required = true },
|
||||
{ name = "components", path = "src/components", description = "Frontend components", required = true },
|
||||
{ name = "plugins", path = "src/plugins", description = "Plugins", required = true },
|
||||
{ name = "modules", path = "src/modules", description = "Modules", required = true },
|
||||
{ name = "templates", path = "src/templates", description = "Templates", required = true },
|
||||
{ name = "media", path = "src/media", description = "Media assets", required = true },
|
||||
{ name = "images", path = "src/images", description = "Site images", required = false },
|
||||
{ name = "language", path = "src/language", description = "Language files", required = false },
|
||||
{ name = "libraries", path = "src/libraries", description = "Libraries", required = false },
|
||||
{ name = "layouts", path = "src/layouts", description = "Layouts", required = false }
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Deployment, sync, and monitoring scripts"
|
||||
required = true
|
||||
purpose = "Contains SFTP configs, sync scripts, and monitoring"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "sftp-config"
|
||||
path = "scripts/sftp-config"
|
||||
description = "SFTP connection configs (dev + live)"
|
||||
required = true
|
||||
files = [
|
||||
{
|
||||
name = "sftp-config.dev.json"
|
||||
extension = "json"
|
||||
description = "Dev server SFTP connection"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
},
|
||||
{
|
||||
name = "sftp-config.rs.json"
|
||||
extension = "json"
|
||||
description = "Live/release server SFTP connection"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "monitoring"
|
||||
path = "monitoring"
|
||||
description = "Grafana dashboard templates"
|
||||
required = true
|
||||
purpose = "Contains Panopticon-style Grafana dashboard JSON"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "grafana"
|
||||
path = "monitoring/grafana"
|
||||
description = "Grafana dashboard JSON templates"
|
||||
required = true
|
||||
files = [
|
||||
{
|
||||
name = "client-joomla-dashboard.json"
|
||||
extension = "json"
|
||||
description = "Panopticon-style Grafana dashboard template"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/monitoring/client-joomla-dashboard.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".gitea"
|
||||
path = ".gitea"
|
||||
description = "Gitea configuration"
|
||||
required = true
|
||||
purpose = "Contains Gitea Actions workflows"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".gitea/workflows"
|
||||
description = "Gitea Actions CI/CD workflows"
|
||||
required = true
|
||||
files = [
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-release on merge to main"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deploy src/ to servers via SFTP"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
},
|
||||
{
|
||||
name = "add-endpoint.yml"
|
||||
extension = "yml"
|
||||
description = "Add monitoring endpoint to sites.json"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
output "client_structure" {
|
||||
description = "Client site repository structure definition"
|
||||
value = local.repository_structure
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
{
|
||||
"schemaVersion": "1.0",
|
||||
"metadata": {
|
||||
"name": "Default Repository Structure",
|
||||
"description": "Default repository structure applicable to all repository types with minimal requirements",
|
||||
"repositoryType": "library",
|
||||
"platform": "multi-platform",
|
||||
"lastUpdated": "2026-01-16T00:00:00Z",
|
||||
"maintainer": "Moko Consulting"
|
||||
},
|
||||
"structure": {
|
||||
"rootFiles": [
|
||||
{
|
||||
"name": "README.md",
|
||||
"extension": "md",
|
||||
"description": "Project overview and documentation",
|
||||
"requirementStatus": "required",
|
||||
"audience": "general",
|
||||
"template": "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
"name": "LICENSE",
|
||||
"extension": "",
|
||||
"description": "License file (GPL-3.0-or-later)",
|
||||
"requirementStatus": "required",
|
||||
"audience": "general",
|
||||
"template": "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
"name": "CHANGELOG.md",
|
||||
"extension": "md",
|
||||
"description": "Version history and changes",
|
||||
"requirementStatus": "required",
|
||||
"audience": "general",
|
||||
"template": "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
"name": "CONTRIBUTING.md",
|
||||
"extension": "md",
|
||||
"description": "Contribution guidelines",
|
||||
"requirementStatus": "required",
|
||||
"audience": "contributor",
|
||||
"template": "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
"name": "SECURITY.md",
|
||||
"extension": "md",
|
||||
"description": "Security policy and vulnerability reporting",
|
||||
"requirementStatus": "required",
|
||||
"audience": "general",
|
||||
"template": "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
"name": "CODE_OF_CONDUCT.md",
|
||||
"extension": "md",
|
||||
"description": "Community code of conduct",
|
||||
"requirementStatus": "suggested",
|
||||
"audience": "contributor",
|
||||
"template": "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
"name": ".gitignore",
|
||||
"extension": "gitignore",
|
||||
"description": "Git ignore patterns",
|
||||
"requirementStatus": "required",
|
||||
"alwaysOverwrite": false,
|
||||
"audience": "developer"
|
||||
},
|
||||
{
|
||||
"name": ".gitattributes",
|
||||
"extension": "gitattributes",
|
||||
"description": "Git attributes configuration",
|
||||
"requirementStatus": "required",
|
||||
"audience": "developer"
|
||||
},
|
||||
{
|
||||
"name": ".editorconfig",
|
||||
"extension": "editorconfig",
|
||||
"description": "Editor configuration for consistent coding style",
|
||||
"requirementStatus": "required",
|
||||
"alwaysOverwrite": false,
|
||||
"audience": "developer"
|
||||
},
|
||||
{
|
||||
"name": "Makefile",
|
||||
"description": "Build automation",
|
||||
"requirementStatus": "suggested",
|
||||
"audience": "developer"
|
||||
},
|
||||
{
|
||||
"name": "renovate.json",
|
||||
"extension": "json",
|
||||
"description": "Renovate dependency management configuration",
|
||||
"requirementStatus": "required",
|
||||
"alwaysOverwrite": false,
|
||||
"audience": "developer",
|
||||
"template": "templates/configs/renovate.json"
|
||||
}
|
||||
],
|
||||
"directories": [
|
||||
{
|
||||
"name": "docs",
|
||||
"path": "docs",
|
||||
"description": "Documentation directory",
|
||||
"requirementStatus": "required",
|
||||
"purpose": "Contains comprehensive project documentation",
|
||||
"files": [
|
||||
{
|
||||
"name": "index.md",
|
||||
"extension": "md",
|
||||
"description": "Documentation index",
|
||||
"requirementStatus": "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "scripts",
|
||||
"path": "scripts",
|
||||
"description": "Build and automation scripts",
|
||||
"requirementStatus": "required",
|
||||
"purpose": "Contains scripts for building, testing, and deploying"
|
||||
},
|
||||
{
|
||||
"name": "src",
|
||||
"path": "src",
|
||||
"description": "Source code directory",
|
||||
"requirementStatus": "required",
|
||||
"purpose": "Contains application source code"
|
||||
},
|
||||
{
|
||||
"name": "tests",
|
||||
"path": "tests",
|
||||
"description": "Test files",
|
||||
"requirementStatus": "suggested",
|
||||
"purpose": "Contains unit tests, integration tests, and test fixtures",
|
||||
"subdirectories": [
|
||||
{
|
||||
"name": "unit",
|
||||
"path": "tests/unit",
|
||||
"description": "Unit tests",
|
||||
"requirementStatus": "suggested"
|
||||
},
|
||||
{
|
||||
"name": "integration",
|
||||
"path": "tests/integration",
|
||||
"description": "Integration tests",
|
||||
"requirementStatus": "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": ".github",
|
||||
"path": ".github",
|
||||
"description": "Gitea/GitHub Actions configuration (Gitea reads .github/workflows natively)",
|
||||
"requirementStatus": "required",
|
||||
"purpose": "Contains CI/CD workflows and repository configuration. Gitea is the primary platform; GitHub is backup only.",
|
||||
"subdirectories": [
|
||||
{
|
||||
"name": "workflows",
|
||||
"path": ".github/workflows",
|
||||
"description": "CI/CD workflows (Gitea-primary, GitHub-compatible)",
|
||||
"requirementStatus": "required",
|
||||
"requiredFiles": [
|
||||
"auto-assign.yml",
|
||||
"auto-dev-issue.yml",
|
||||
"auto-release.yml",
|
||||
"branch-freeze.yml",
|
||||
"changelog-validation.yml",
|
||||
"repository-cleanup.yml",
|
||||
"sync-version-on-merge.yml",
|
||||
"cascade-dev.yml",
|
||||
"gitleaks.yml"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "node_modules",
|
||||
"path": "node_modules",
|
||||
"description": "Node.js dependencies (generated)",
|
||||
"requirementStatus": "not-allowed",
|
||||
"purpose": "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
"name": "vendor",
|
||||
"path": "vendor",
|
||||
"description": "PHP dependencies (generated)",
|
||||
"requirementStatus": "not-allowed",
|
||||
"purpose": "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
"name": "build",
|
||||
"path": "build",
|
||||
"description": "Build artifacts (generated)",
|
||||
"requirementStatus": "not-allowed",
|
||||
"purpose": "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
"name": "dist",
|
||||
"path": "dist",
|
||||
"description": "Distribution files (generated)",
|
||||
"requirementStatus": "not-allowed",
|
||||
"purpose": "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,686 +0,0 @@
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
},
|
||||
{
|
||||
name = "renovate.json"
|
||||
extension = "json"
|
||||
description = "Renovate dependency management configuration"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/renovate.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
},
|
||||
{
|
||||
name = "cascade-dev.yml"
|
||||
extension = "yml"
|
||||
description = "Forward-merge main to all open branches (dev, rc/*, beta/*, alpha/*) on push to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "workflows/cascade-dev.yml"
|
||||
},
|
||||
{
|
||||
name = "gitleaks.yml"
|
||||
extension = "yml"
|
||||
description = "Secret scanning — detect leaked credentials, API keys, and tokens using Gitleaks"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "workflows/gitleaks.yml"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 0
|
||||
dismiss_stale_reviews = true
|
||||
block_on_rejected_reviews = true
|
||||
restrict_pushes = true
|
||||
push_whitelist = ["jmiller"]
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
enforce_admins = false
|
||||
},
|
||||
{
|
||||
branch_pattern = "dev"
|
||||
require_pull_request = false
|
||||
required_approvals = 0
|
||||
restrict_pushes = false
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
},
|
||||
{
|
||||
branch_pattern = "rc/*"
|
||||
require_pull_request = false
|
||||
required_approvals = 0
|
||||
restrict_pushes = false
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
},
|
||||
{
|
||||
branch_pattern = "beta/*"
|
||||
require_pull_request = false
|
||||
required_approvals = 0
|
||||
restrict_pushes = false
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
},
|
||||
{
|
||||
branch_pattern = "alpha/*"
|
||||
require_pull_request = false
|
||||
required_approvals = 0
|
||||
restrict_pushes = false
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,331 +0,0 @@
|
||||
/**
|
||||
* .github-private Repository Structure Definition
|
||||
* Org-level private repository containing universal GitHub Actions workflows,
|
||||
* helper scripts, and default issue templates for all governed repositories.
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*
|
||||
* NOTES
|
||||
* ─────
|
||||
* • GitHub reads ISSUE_TEMPLATE/ from this repo as org-wide defaults for any
|
||||
* governed repo that does not supply its own templates.
|
||||
* • Workflows in .github/workflows/ support both standalone execution and
|
||||
* workflow_call so governed repos can invoke them as reusable workflows via
|
||||
* `uses: mokoconsulting-tech/.github-private/.github/workflows/<name>.yml@main`.
|
||||
* • This repo is EXCLUDED from bulk-repo-sync — it manages its own content
|
||||
* independently as GitHub's org-level defaults repo.
|
||||
*/
|
||||
|
||||
locals {
|
||||
github_private_repository_structure = {
|
||||
metadata = {
|
||||
name = ".github-private"
|
||||
description = "Private GitHub org defaults — universal workflows, issue templates, and helper scripts"
|
||||
repository_type = "github-private"
|
||||
platform = "github-private"
|
||||
last_updated = "2026-03-12T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
visibility = "private"
|
||||
sync_priority = -1
|
||||
exclude_from_sync = true
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Repository overview — purpose, contents, and how governed repos use this repo"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
required = true
|
||||
audience = "general"
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
license_type = "GPL-3.0-or-later"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
required = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and private vulnerability reporting"
|
||||
required = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
audience = "contributor"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
required = true
|
||||
audience = "contributor"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Governance policy and decision-making process"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
audience = "general"
|
||||
template = "templates/docs/required/GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
required = true
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitea/.mokostandards"
|
||||
extension = "xml"
|
||||
description = "MokoStandards XML manifest — generated programmatically by RepositorySynchronizer::migrateMokoStandards()"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
template = "managed-by-sync"
|
||||
source_type = "programmatic"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = "ISSUE_TEMPLATE"
|
||||
description = "Org-default issue templates — applied to all governed repos without their own templates"
|
||||
requirement_status = "required"
|
||||
purpose = "GitHub reads ISSUE_TEMPLATE/ from this repo as org-wide defaults"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
extension = "yml"
|
||||
description = "Issue template chooser — disables blank issues and lists contact links"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/github-private/ISSUE_TEMPLATE/config.yml.template"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
extension = "md"
|
||||
description = "Bug report issue template"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
template = "templates/github-private/ISSUE_TEMPLATE/bug_report.md.template"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
extension = "md"
|
||||
description = "Feature request issue template"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
template = "templates/github-private/ISSUE_TEMPLATE/feature_request.md.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Helper scripts used by universal workflows and available as git hooks"
|
||||
requirement_status = "required"
|
||||
purpose = "Reusable Bash utilities for commit-message and PR-title validation"
|
||||
files = [
|
||||
{
|
||||
name = "check-pr-title.sh"
|
||||
extension = "sh"
|
||||
description = "Validates PR title follows conventional-commit format"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/github-private/scripts/check-pr-title.sh.template"
|
||||
},
|
||||
{
|
||||
name = "check-commit-msg.sh"
|
||||
extension = "sh"
|
||||
description = "Validates individual commit messages follow conventional-commit format; usable as a git commit-msg hook"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/github-private/scripts/check-commit-msg.sh.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration for .github-private itself"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains CI workflows for this repo and reusable workflows callable org-wide"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "CI + universal reusable workflows; callable via uses: mokoconsulting-tech/.github-private/.github/workflows/<name>.yml@main"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "stale.yml"
|
||||
extension = "yml"
|
||||
description = "Marks stale issues and pull requests; standalone (schedule) and reusable (workflow_call)"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/github-private/workflows/stale.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-assign.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-assigns PR author and logs CODEOWNERS status; standalone and reusable"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/github-private/workflows/auto-assign.yml.template"
|
||||
},
|
||||
{
|
||||
name = "pr-labeler.yml"
|
||||
extension = "yml"
|
||||
description = "Labels PRs from branch name and validates PR title format; standalone and reusable"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/github-private/workflows/pr-labeler.yml.template"
|
||||
},
|
||||
{
|
||||
name = "welcome.yml"
|
||||
extension = "yml"
|
||||
description = "Posts welcome message on first-time contributor PRs and issues; standalone and reusable"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/github-private/workflows/welcome.yml.template"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT for automation — required for bulk sync and workflow execution"
|
||||
required = true
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_KEY"
|
||||
description = "SSH private key for SFTP dev deployment (preferred); if DEV_FTP_PASSWORD is also set it is used as the key passphrase, with password-only as fallback"
|
||||
required = false
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_PASSWORD"
|
||||
description = "SFTP password for dev deployment; used as SSH key passphrase when DEV_FTP_KEY is also set, and as standalone fallback if key auth fails"
|
||||
required = false
|
||||
scope = "org"
|
||||
note = "At least one of DEV_FTP_KEY or DEV_FTP_PASSWORD must be configured"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "DEV_FTP_HOST"
|
||||
description = "Dev server hostname; may include port suffix (e.g. dev.example.com or dev.example.com:2222)"
|
||||
required = true
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_PATH"
|
||||
description = "Base remote path for SFTP deployment (e.g. /var/www/html)"
|
||||
required = true
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_USERNAME"
|
||||
description = "SFTP username for dev server authentication"
|
||||
required = true
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_PORT"
|
||||
description = "Explicit SFTP port override; if omitted the port is parsed from DEV_FTP_HOST or defaults to 22"
|
||||
required = false
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_PATH_SUFFIX"
|
||||
description = "Per-repo path suffix appended to DEV_FTP_PATH (e.g. /.github-private)"
|
||||
required = false
|
||||
scope = "repo"
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
visibility = "private"
|
||||
has_issues = true
|
||||
has_projects = false
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_squash_merge = true
|
||||
allow_merge_commit = false
|
||||
allow_rebase_merge = true
|
||||
delete_branch_on_merge = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,484 +0,0 @@
|
||||
/**
|
||||
* MCP Server Repository Structure Definition
|
||||
* Standard repository structure for Model Context Protocol (MCP) server projects
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "MCP Server"
|
||||
description = "Standard repository structure for Model Context Protocol (MCP) server projects — TypeScript/Node.js MCP servers that expose external APIs as AI assistant tools"
|
||||
repository_type = "mcp-server"
|
||||
platform = "mcp-server"
|
||||
last_updated = "2026-05-07T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "04.06.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview with tool reference table, install, and configuration"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "package.json"
|
||||
extension = "json"
|
||||
description = "Node.js project manifest — @mokoconsulting scoped, MCP SDK + Zod dependencies"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "tsconfig.json"
|
||||
extension = "json"
|
||||
description = "TypeScript configuration — ES2022 target, Node16 module, strict mode"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "config.example.json"
|
||||
extension = "json"
|
||||
description = "Example multi-connection configuration file"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitmessage"
|
||||
extension = "gitmessage"
|
||||
description = "Conventional commit message template"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation — install, build, dev, clean, setup, start targets"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "TypeScript source code"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains MCP server entry point, API client, config loader, and type definitions"
|
||||
files = [
|
||||
{
|
||||
name = "index.ts"
|
||||
extension = "ts"
|
||||
description = "MCP server entry point — registers all API tools with McpServer"
|
||||
requirement_status = "required"
|
||||
},
|
||||
{
|
||||
name = "client.ts"
|
||||
extension = "ts"
|
||||
description = "HTTP client wrapper for the target API (GET/POST/PUT/DELETE)"
|
||||
requirement_status = "required"
|
||||
},
|
||||
{
|
||||
name = "config.ts"
|
||||
extension = "ts"
|
||||
description = "Configuration loader — reads ~/.{project}.json with multi-connection support"
|
||||
requirement_status = "required"
|
||||
},
|
||||
{
|
||||
name = "types.ts"
|
||||
extension = "ts"
|
||||
description = "TypeScript interfaces for connection, config, and API response types"
|
||||
requirement_status = "required"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Setup and utility scripts"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains interactive setup wizard and repo-specific helpers"
|
||||
files = [
|
||||
{
|
||||
name = "setup.mjs"
|
||||
extension = "mjs"
|
||||
description = "Interactive setup wizard — prompts for API connection details and writes config"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".gitea"
|
||||
path = ".gitea"
|
||||
description = "Gitea-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains Gitea Actions workflows and platform configuration"
|
||||
files = [
|
||||
{
|
||||
name = ".mokostandards"
|
||||
description = "MokoStandards platform declaration — must contain 'platform: mcp-server'"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".gitea/workflows"
|
||||
description = "Gitea Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create release on push to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-assign.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-assign issues and PRs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-assign.yml.template"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/standards-compliance.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "changelog-validation.yml"
|
||||
extension = "yml"
|
||||
description = "CHANGELOG validation on PR"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/changelog-validation.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup of stale branches and workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment to development server"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment to demo server on merge to main"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "copilot-agent.yml"
|
||||
extension = "yml"
|
||||
description = "Copilot agent workflow for automated code review"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/copilot-agent.yml.template"
|
||||
},
|
||||
{
|
||||
name = "mcp-build-test.yml"
|
||||
extension = "yml"
|
||||
description = "MCP server build validation — TypeScript compile, dist verification, tool count"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/mcp/mcp-build-test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "mcp-sdk-check.yml"
|
||||
extension = "yml"
|
||||
description = "Weekly check for MCP SDK and Zod updates — creates issue when new version available"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/mcp/mcp-sdk-check.yml.template"
|
||||
},
|
||||
{
|
||||
name = "mcp-tool-inventory.yml"
|
||||
extension = "yml"
|
||||
description = "Generate tool inventory report on push to main"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/mcp/mcp-tool-inventory.yml.template"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Compiled JavaScript output (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level Gitea PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "Gitea Actions workflows"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "20"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
},
|
||||
{
|
||||
name = "new-tool"
|
||||
color = "5319e7"
|
||||
description = "New MCP tool/endpoint to add"
|
||||
},
|
||||
{
|
||||
name = "api-change"
|
||||
color = "fbca04"
|
||||
description = "Upstream API changed — tool needs update"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,331 +0,0 @@
|
||||
/**
|
||||
* Dolibarr Platform Structure Definition
|
||||
* Standard repository structure for the full Dolibarr ERP/CRM installation
|
||||
*
|
||||
* This is distinct from dolibarr — it defines the ENTIRE Dolibarr platform
|
||||
* (htdocs/, not src/). It does NOT have a module descriptor, numero, or
|
||||
* publish-to-mokodolimods workflow.
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Dolibarr Platform"
|
||||
description = "Full Dolibarr ERP/CRM installation — htdocs/ root, not a module"
|
||||
repository_type = "platform"
|
||||
platform = "dolibarr"
|
||||
last_updated = "2026-03-31T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Developer-focused documentation"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
audience = "contributor"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "GPL-3.0-or-later license file"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/LICENSE"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer package definition"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
},
|
||||
{
|
||||
name = "phpstan.neon"
|
||||
extension = "neon"
|
||||
description = "PHPStan static analysis configuration"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/phpstan.neon"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
extension = ""
|
||||
description = "Build automation targets"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/Makefile"
|
||||
},
|
||||
{
|
||||
name = "src/.ftpignore"
|
||||
extension = ""
|
||||
description = "Files excluded from SFTP deployment"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/ftp_ignore"
|
||||
},
|
||||
{
|
||||
name = ".gitea/.mokostandards"
|
||||
extension = "xml"
|
||||
description = "MokoStandards XML manifest — generated programmatically by RepositorySynchronizer::migrateMokoStandards()"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
template = "managed-by-sync"
|
||||
source_type = "programmatic"
|
||||
},
|
||||
{
|
||||
name = "renovate.json"
|
||||
extension = "json"
|
||||
description = "Renovate dependency management configuration"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/renovate.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "htdocs"
|
||||
path = "htdocs"
|
||||
description = "Dolibarr web root — entire platform"
|
||||
required = true
|
||||
purpose = "Contains the full Dolibarr installation including core, custom modules, and themes"
|
||||
},
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Developer and technical documentation"
|
||||
required = true
|
||||
purpose = "Contains technical documentation"
|
||||
files = [
|
||||
{
|
||||
name = "update-server.md"
|
||||
extension = "md"
|
||||
description = "Dolibarr update server (update.txt) documentation"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/template-update-server-dolibarr.md"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub configuration"
|
||||
required = true
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment to dev server (htdocs/)"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment to demo server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment to release staging server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on minor version"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: retired workflows, stale branches"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue on dev/rc branch creation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repo_health.yml"
|
||||
extension = "yml"
|
||||
description = "Dolibarr platform health checks (shared guardrails, no module-specific checks)"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/dolibarr/repo_health.yml.template"
|
||||
},
|
||||
{
|
||||
name = "cascade-dev.yml"
|
||||
extension = "yml"
|
||||
description = "Forward-merge main to all open branches (dev, rc/*, beta/*, alpha/*) on push to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "workflows/cascade-dev.yml"
|
||||
},
|
||||
{
|
||||
name = "gitleaks.yml"
|
||||
extension = "yml"
|
||||
description = "Secret scanning — detect leaked credentials, API keys, and tokens using Gitleaks"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "workflows/gitleaks.yml"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
},
|
||||
{
|
||||
name = "dolibarr_issue.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/dolibarr_issue.md"
|
||||
},
|
||||
{
|
||||
name = "dolibarr_module_id_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/dolibarr_module_id_request.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
output "crm_platform_structure" {
|
||||
description = "Dolibarr Platform repository structure definition"
|
||||
value = local.repository_structure
|
||||
}
|
||||
@@ -1,763 +0,0 @@
|
||||
/**
|
||||
* MokoStandards Repository Structure Definition
|
||||
* Repository structure definition for the MokoStandards standards and templates repository
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "MokoStandards Repository"
|
||||
description = "Repository structure definition for MokoStandards - organizational standards, templates, and automation"
|
||||
repository_type = "standards"
|
||||
platform = "standards"
|
||||
last_updated = "2026-03-03T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Repository overview and documentation"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
required = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = "CITATION.cff"
|
||||
extension = "cff"
|
||||
description = "Citation file format for academic references"
|
||||
required = true
|
||||
audience = "general"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
required = true
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitmessage"
|
||||
extension = "gitmessage"
|
||||
description = "Git commit message template"
|
||||
required = true
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".git-blame-ignore-revs"
|
||||
extension = "git-blame-ignore-revs"
|
||||
description = "Git blame ignore revisions"
|
||||
requirement_status = "suggested"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".mailmap"
|
||||
extension = "mailmap"
|
||||
description = "Git mailmap for contributor attribution"
|
||||
requirement_status = "suggested"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".eslintrc.json"
|
||||
extension = "json"
|
||||
description = "ESLint configuration for JavaScript"
|
||||
requirement_status = "suggested"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".prettierrc.json"
|
||||
extension = "json"
|
||||
description = "Prettier configuration for code formatting"
|
||||
requirement_status = "suggested"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".markdownlint.json"
|
||||
extension = "json"
|
||||
description = "Markdown linting configuration"
|
||||
requirement_status = "suggested"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".yamllint"
|
||||
extension = "yamllint"
|
||||
description = "YAML linting configuration"
|
||||
requirement_status = "suggested"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".pylintrc"
|
||||
extension = "pylintrc"
|
||||
description = "Python linting configuration"
|
||||
requirement_status = "suggested"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".htmlhintrc"
|
||||
extension = "htmlhintrc"
|
||||
description = "HTML linting configuration"
|
||||
requirement_status = "suggested"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "PHP dependency management"
|
||||
requirement_status = "suggested"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitea/.mokostandards"
|
||||
extension = "xml"
|
||||
description = "MokoStandards XML manifest — generated programmatically by RepositorySynchronizer::migrateMokoStandards()"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "managed-by-sync"
|
||||
source_type = "programmatic"
|
||||
},
|
||||
{
|
||||
name = "renovate.json"
|
||||
extension = "json"
|
||||
description = "Renovate dependency management configuration"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/renovate.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "api"
|
||||
path = "api"
|
||||
description = "API scripts and automation"
|
||||
required = true
|
||||
purpose = "Contains all operational scripts - validation, automation, build, release, etc."
|
||||
subdirectories = [
|
||||
{
|
||||
name = "validate"
|
||||
path = "api/validate"
|
||||
description = "Validation scripts"
|
||||
required = true
|
||||
purpose = "Scripts for validating repository structure, health, and compliance"
|
||||
},
|
||||
{
|
||||
name = "automation"
|
||||
path = "api/automation"
|
||||
description = "Automation scripts"
|
||||
required = true
|
||||
purpose = "Scripts for bulk operations and repository synchronization"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "api/build"
|
||||
description = "Build scripts"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Scripts for building and packaging"
|
||||
},
|
||||
{
|
||||
name = "release"
|
||||
path = "api/release"
|
||||
description = "Release scripts"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Scripts for release management"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "api/tests"
|
||||
description = "Test scripts"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Test scripts and test data"
|
||||
},
|
||||
{
|
||||
name = "maintenance"
|
||||
path = "api/maintenance"
|
||||
description = "Maintenance scripts"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Scripts for repository maintenance tasks"
|
||||
},
|
||||
{
|
||||
name = "definitions"
|
||||
path = "api/definitions"
|
||||
description = "Repository structure definitions"
|
||||
required = true
|
||||
purpose = "HCL/Terraform definition files for different repository types"
|
||||
},
|
||||
{
|
||||
name = "lib"
|
||||
path = "api/lib"
|
||||
description = "Shared libraries"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Shared code libraries and utilities"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation"
|
||||
required = true
|
||||
purpose = "Comprehensive documentation for standards, guides, policies, and references"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "guide"
|
||||
path = "docs/guide"
|
||||
description = "User guides"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "reference"
|
||||
path = "docs/reference"
|
||||
description = "Reference documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "policy"
|
||||
path = "docs/policy"
|
||||
description = "Policies and standards"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "workflows"
|
||||
path = "docs/workflows"
|
||||
description = "Workflow documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
path = "docs/security"
|
||||
description = "Security documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "development"
|
||||
path = "docs/development"
|
||||
description = "Development documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "templates"
|
||||
path = "templates"
|
||||
description = "Template files"
|
||||
required = true
|
||||
purpose = "Template files for workflows, configs, documentation, and projects"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = "templates/workflows"
|
||||
description = "GitHub Actions workflow templates"
|
||||
required = true
|
||||
},
|
||||
{
|
||||
name = "github"
|
||||
path = "templates/github"
|
||||
description = "GitHub configuration templates"
|
||||
required = true
|
||||
},
|
||||
{
|
||||
name = "docs"
|
||||
path = "templates/docs"
|
||||
description = "Documentation templates"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "configs"
|
||||
path = "templates/configs"
|
||||
description = "Configuration file templates"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "licenses"
|
||||
path = "templates/licenses"
|
||||
description = "License templates"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "projects"
|
||||
path = "templates/projects"
|
||||
description = "Project definition templates"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "terraform"
|
||||
path = "templates/terraform"
|
||||
description = "Terraform configuration templates"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "templates/scripts"
|
||||
description = "Script templates"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "logs"
|
||||
path = "logs"
|
||||
description = "Log files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Storage for operation logs, audit trails, and metrics"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "audit"
|
||||
path = "logs/audit"
|
||||
description = "Audit logs"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "automation"
|
||||
path = "logs/automation"
|
||||
description = "Automation logs"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "validation"
|
||||
path = "logs/validation"
|
||||
description = "Validation logs"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
required = true
|
||||
purpose = "GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
required = true
|
||||
files = [
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},,
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "One-time cleanup: reset labels, strip issue template headers, delete old branches — self-deletes after run"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards self-compliance validation"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall rules setup workflow"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
},
|
||||
{
|
||||
name = "cascade-dev.yml"
|
||||
extension = "yml"
|
||||
description = "Forward-merge main to all open branches (dev, rc/*, beta/*, alpha/*) on push to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "workflows/cascade-dev.yml"
|
||||
},
|
||||
{
|
||||
name = "gitleaks.yml"
|
||||
extension = "yml"
|
||||
description = "Secret scanning — detect leaked credentials, API keys, and tokens using Gitleaks"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "workflows/gitleaks.yml"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
files = [
|
||||
{
|
||||
name = "config.tf"
|
||||
extension = "tf"
|
||||
description = "Repository override configuration for bulk sync"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "copilot.yml"
|
||||
extension = "yml"
|
||||
description = "GitHub Copilot configuration — topic list and repo metadata"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "copilot-instructions.md"
|
||||
extension = "md"
|
||||
description = "GitHub Copilot custom instructions for this repository"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "CLAUDE.md"
|
||||
extension = "md"
|
||||
description = "Claude Code context and instructions for this repository"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".checkpoints"
|
||||
path = ".checkpoints"
|
||||
description = "Checkpoint files for long-running operations"
|
||||
requirement_status = "optional"
|
||||
purpose = "Stores checkpoint data for resumable operations"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT for automation — configure in org Actions secrets"
|
||||
required = true
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_KEY"
|
||||
description = "SSH private key for SFTP dev deployment (preferred); if DEV_FTP_PASSWORD is also set it is used as the key passphrase, with password-only as fallback"
|
||||
required = false
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_PASSWORD"
|
||||
description = "SFTP password for dev deployment; used as SSH key passphrase when DEV_FTP_KEY is also set, and as standalone fallback if key auth fails"
|
||||
required = false
|
||||
scope = "org"
|
||||
note = "At least one of DEV_FTP_KEY or DEV_FTP_PASSWORD must be configured"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "STANDARDS_VERSION"
|
||||
description = "Current MokoStandards version"
|
||||
required = false
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_HOST"
|
||||
description = "Dev server hostname; may include port suffix (e.g. dev.example.com or dev.example.com:2222)"
|
||||
required = true
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_PATH"
|
||||
description = "Base remote path for SFTP deployment (e.g. /var/www/html)"
|
||||
required = true
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_USERNAME"
|
||||
description = "SFTP username for dev server authentication"
|
||||
required = true
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_PORT"
|
||||
description = "Explicit SFTP port override; if omitted the port is parsed from DEV_FTP_HOST or defaults to 22"
|
||||
required = false
|
||||
scope = "org"
|
||||
},
|
||||
{
|
||||
name = "DEV_FTP_SUFFIX"
|
||||
description = "Per-repo path suffix appended to DEV_FTP_PATH (e.g. /mokostandards)"
|
||||
required = false
|
||||
scope = "repo"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 0
|
||||
dismiss_stale_reviews = true
|
||||
block_on_rejected_reviews = true
|
||||
restrict_pushes = true
|
||||
push_whitelist = ["jmiller"]
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
enforce_admins = false
|
||||
},
|
||||
{
|
||||
branch_pattern = "dev"
|
||||
require_pull_request = false
|
||||
required_approvals = 0
|
||||
restrict_pushes = false
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
},
|
||||
{
|
||||
branch_pattern = "rc/*"
|
||||
require_pull_request = false
|
||||
required_approvals = 0
|
||||
restrict_pushes = false
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
},
|
||||
{
|
||||
branch_pattern = "beta/*"
|
||||
require_pull_request = false
|
||||
required_approvals = 0
|
||||
restrict_pushes = false
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
},
|
||||
{
|
||||
branch_pattern = "alpha/*"
|
||||
require_pull_request = false
|
||||
required_approvals = 0
|
||||
restrict_pushes = false
|
||||
enable_force_push = true
|
||||
force_push_whitelist = ["jmiller"]
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = true
|
||||
allow_squash_merge = true
|
||||
allow_merge_commit = false
|
||||
allow_rebase_merge = true
|
||||
delete_branch_on_merge = true
|
||||
}
|
||||
|
||||
labels = [
|
||||
{ name = "bulk-sync-success", color = "0e8a16", description = "Bulk sync completed successfully" },
|
||||
{ name = "bulk-sync-failure", color = "d73a4a", description = "Bulk sync failed" },
|
||||
{ name = "standards-update", color = "fbca04", description = "Standards update" },
|
||||
{ name = "template-update", color = "d4c5f9", description = "Template file update" },
|
||||
{ name = "documentation", color = "0075ca", description = "Documentation changes" },
|
||||
{ name = "automation", color = "5319e7", description = "Automation scripts" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<!--
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
FILE INFORMATION
|
||||
DEFGROUP: MokoStandards.Index
|
||||
INGROUP: MokoStandards.Definitions
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
PATH: /definitions/index.md
|
||||
BRIEF: Definitions directory index
|
||||
-->
|
||||
|
||||
# Docs Index: /api/definitions
|
||||
|
||||
## Purpose
|
||||
|
||||
This index provides navigation to documentation within this folder.
|
||||
|
||||
## Documents
|
||||
|
||||
- [README](./README.md)
|
||||
|
||||
## Metadata
|
||||
|
||||
- **Document Type:** index
|
||||
- **Auto-generated:** This file is automatically generated by rebuild_indexes.py
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Author | Change | Notes |
|
||||
| ---------- | ------------------ | ----------------- | ------------------------------------------ |
|
||||
| Auto | rebuild_indexes.py | Automated update | Generated by documentation index automation |
|
||||
@@ -1,682 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/.github-private
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-03-24T19:30:16+00:00
|
||||
* Platform : default-repository
|
||||
* Description: This is the private organization-level configuration repository for mokoconsulting-tech. It provides centralized community health files, templates, and standards that apply across all private repositories in the organization.
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/.github-private"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "This is the private organization-level configuration repository for mokoconsulting-tech. It provides centralized community health files, templates, and standards that apply across all private repositories in the organization."
|
||||
sync_timestamp = "2026-03-24T19:30:16+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 39
|
||||
created_files = 0
|
||||
updated_files = 28
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/ci.yml" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/ci.yml" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/repo-health.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/repo-health.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "ci.yml"
|
||||
extension = "yml"
|
||||
description = "Continuous integration workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "ci.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "ci.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/ci.yml.template"
|
||||
},
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repo-health.yml"
|
||||
extension = "yml"
|
||||
description = "Repository health monitoring"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "repo_health.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "repo-health.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/repo_health.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,733 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/.github
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T21:05:55+00:00
|
||||
* Platform : default-repository
|
||||
* Description:
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/.github"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = ""
|
||||
sync_timestamp = "2026-04-02T21:05:55+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 42
|
||||
updated_files = 5
|
||||
skipped_files = 6
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "created" },
|
||||
{ path = "CONTRIBUTING.md" action = "created" },
|
||||
{ path = "SECURITY.md" action = "created" },
|
||||
{ path = "CODE_OF_CONDUCT.md" action = "created" },
|
||||
{ path = "ROADMAP.md" action = "created" },
|
||||
{ path = "Makefile" action = "created" },
|
||||
{ path = "composer.json" action = "created" },
|
||||
{ path = "docs/index.md" action = "created" },
|
||||
{ path = "docs/INSTALLATION.md" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "created" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "created" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "created" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "created" },
|
||||
{ path = ".github/CODEOWNERS" action = "created" },
|
||||
{ path = "composer.json" action = "enterprise dependency added" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,734 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/Copy-PortablePath
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:20:12+00:00
|
||||
* Platform : default-repository
|
||||
* Description: Copy Portable Path is a lightweight PowerShell utility that adds two context menu items to Windows Explorer — Copy Relative Path and Copy Absolute Path — both using forward slashes for cross-platform compatibility. Supports multiple selections, optional MSYS/WSL /c/... style, and installs per-user without admin rights.
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/Copy-PortablePath"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "Copy Portable Path is a lightweight PowerShell utility that adds two context menu items to Windows Explorer — Copy Relative Path and Copy Absolute Path — both using forward slashes for cross-platform compatibility. Supports multiple selections, optional MSYS/WSL /c/... style, and installs per-user without admin rights."
|
||||
sync_timestamp = "2026-04-02T15:20:12+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 11
|
||||
updated_files = 31
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/Copy-PortablePath/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,400 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/MokoDoliMods
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:19:29+00:00
|
||||
* Platform : crm-platform
|
||||
* Description: The DoliMods is the repository of the Dolibarr ERP CRM modules, developed by the DoliCloud.com team.
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/crm-platform.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/MokoDoliMods"
|
||||
default_branch = "main"
|
||||
detected_platform = "crm-platform"
|
||||
description = "The DoliMods is the repository of the Dolibarr ERP CRM modules, developed by the DoliCloud.com team."
|
||||
sync_timestamp = "2026-04-02T15:19:29+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/crm-platform.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 3
|
||||
updated_files = 40
|
||||
skipped_files = 10
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "CONTRIBUTING.md" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/MokoDoliMods/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Dolibarr Platform Structure Definition
|
||||
* Standard repository structure for the full Dolibarr ERP/CRM installation
|
||||
*
|
||||
* This is distinct from crm-module — it defines the ENTIRE Dolibarr platform
|
||||
* (htdocs/, not src/). It does NOT have a module descriptor, numero, or
|
||||
* publish-to-mokodolimods workflow.
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Dolibarr Platform"
|
||||
description = "Full Dolibarr ERP/CRM installation — htdocs/ root, not a module"
|
||||
repository_type = "crm-platform"
|
||||
platform = "dolibarr"
|
||||
last_updated = "2026-03-31T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Developer-focused documentation"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
audience = "contributor"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "GPL-3.0-or-later license file"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/LICENSE"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer package definition"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
},
|
||||
{
|
||||
name = "phpstan.neon"
|
||||
extension = "neon"
|
||||
description = "PHPStan static analysis configuration"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/phpstan.neon"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
extension = ""
|
||||
description = "Build automation targets"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/Makefile"
|
||||
},
|
||||
{
|
||||
name = "src/.ftpignore"
|
||||
extension = ""
|
||||
description = "Files excluded from SFTP deployment"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/ftp_ignore"
|
||||
},
|
||||
{
|
||||
name = ".mokostandards"
|
||||
extension = ""
|
||||
description = "MokoStandards platform identifier"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/mokostandards.yml.template"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "htdocs"
|
||||
path = "htdocs"
|
||||
description = "Dolibarr web root — entire platform"
|
||||
required = true
|
||||
purpose = "Contains the full Dolibarr installation including core, custom modules, and themes"
|
||||
},
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Developer and technical documentation"
|
||||
required = true
|
||||
purpose = "Contains technical documentation"
|
||||
files = [
|
||||
{
|
||||
name = "update-server.md"
|
||||
extension = "md"
|
||||
description = "Dolibarr update server (update.txt) documentation"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/template-update-server-dolibarr.md"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub configuration"
|
||||
required = true
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment to dev server (htdocs/)"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment to demo server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment to release staging server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on minor version"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: retired workflows, stale branches"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue on dev/rc branch creation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repo_health.yml"
|
||||
extension = "yml"
|
||||
description = "Dolibarr platform health checks (shared guardrails, no module-specific checks)"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/dolibarr/repo_health.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
},
|
||||
{
|
||||
name = "dolibarr_issue.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/dolibarr_issue.md"
|
||||
},
|
||||
{
|
||||
name = "dolibarr_module_id_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/dolibarr_module_id_request.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
output "crm_platform_structure" {
|
||||
description = "Dolibarr Platform repository structure definition"
|
||||
value = local.repository_structure
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,400 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/MokoDolibarr
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:42:39+00:00
|
||||
* Platform : crm-platform
|
||||
* Description: Dolibarr ERP CRM is a modern software package to manage your company or foundation\'s activity (contacts, suppliers, invoices, orders, stocks, agenda, accounting, ...). it\'s an open source Web application (written in PHP) designed for businesses of any sizes, foundations and freelancers.
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/crm-platform.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/MokoDolibarr"
|
||||
default_branch = "main"
|
||||
detected_platform = "crm-platform"
|
||||
description = "Dolibarr ERP CRM is a modern software package to manage your company or foundation\'s activity (contacts, suppliers, invoices, orders, stocks, agenda, accounting, ...). it\'s an open source Web application (written in PHP) designed for businesses of any sizes, foundations and freelancers."
|
||||
sync_timestamp = "2026-04-02T15:42:39+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/crm-platform.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 16
|
||||
updated_files = 29
|
||||
skipped_files = 8
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "CONTRIBUTING.md" action = "created" },
|
||||
{ path = "CODE_OF_CONDUCT.md" action = "created" },
|
||||
{ path = "ROADMAP.md" action = "created" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "created" },
|
||||
{ path = "docs/INSTALLATION.md" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/MokoDolibarr/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Dolibarr Platform Structure Definition
|
||||
* Standard repository structure for the full Dolibarr ERP/CRM installation
|
||||
*
|
||||
* This is distinct from crm-module — it defines the ENTIRE Dolibarr platform
|
||||
* (htdocs/, not src/). It does NOT have a module descriptor, numero, or
|
||||
* publish-to-mokodolimods workflow.
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Dolibarr Platform"
|
||||
description = "Full Dolibarr ERP/CRM installation — htdocs/ root, not a module"
|
||||
repository_type = "crm-platform"
|
||||
platform = "dolibarr"
|
||||
last_updated = "2026-03-31T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Developer-focused documentation"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
audience = "contributor"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "GPL-3.0-or-later license file"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/LICENSE"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer package definition"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
},
|
||||
{
|
||||
name = "phpstan.neon"
|
||||
extension = "neon"
|
||||
description = "PHPStan static analysis configuration"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/phpstan.neon"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
extension = ""
|
||||
description = "Build automation targets"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/Makefile"
|
||||
},
|
||||
{
|
||||
name = "src/.ftpignore"
|
||||
extension = ""
|
||||
description = "Files excluded from SFTP deployment"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/ftp_ignore"
|
||||
},
|
||||
{
|
||||
name = ".mokostandards"
|
||||
extension = ""
|
||||
description = "MokoStandards platform identifier"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/configs/mokostandards.yml.template"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "htdocs"
|
||||
path = "htdocs"
|
||||
description = "Dolibarr web root — entire platform"
|
||||
required = true
|
||||
purpose = "Contains the full Dolibarr installation including core, custom modules, and themes"
|
||||
},
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Developer and technical documentation"
|
||||
required = true
|
||||
purpose = "Contains technical documentation"
|
||||
files = [
|
||||
{
|
||||
name = "update-server.md"
|
||||
extension = "md"
|
||||
description = "Dolibarr update server (update.txt) documentation"
|
||||
required = true
|
||||
always_overwrite = true
|
||||
template = "templates/docs/required/template-update-server-dolibarr.md"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub configuration"
|
||||
required = true
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment to dev server (htdocs/)"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment to demo server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment to release staging server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on minor version"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: retired workflows, stale branches"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue on dev/rc branch creation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repo_health.yml"
|
||||
extension = "yml"
|
||||
description = "Dolibarr platform health checks (shared guardrails, no module-specific checks)"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/dolibarr/repo_health.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
},
|
||||
{
|
||||
name = "dolibarr_issue.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/dolibarr_issue.md"
|
||||
},
|
||||
{
|
||||
name = "dolibarr_module_id_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/dolibarr_module_id_request.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
output "crm_platform_structure" {
|
||||
description = "Dolibarr Platform repository structure definition"
|
||||
value = local.repository_structure
|
||||
}
|
||||
@@ -1,734 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/MokoISOUpdatePortable
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:33:45+00:00
|
||||
* Platform : default-repository
|
||||
* Description: A PortableApp that keeps ISOs of selected systems up to date.
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/MokoISOUpdatePortable"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "A PortableApp that keeps ISOs of selected systems up to date."
|
||||
sync_timestamp = "2026-04-02T15:33:45+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 11
|
||||
updated_files = 31
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/MokoISOUpdatePortable/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,734 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/MokoPerfectPublisher-Discord
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T21:05:10+00:00
|
||||
* Platform : default-repository
|
||||
* Description: A Perfect Publisher plugin to post to Discord
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/MokoPerfectPublisher-Discord"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "A Perfect Publisher plugin to post to Discord"
|
||||
sync_timestamp = "2026-04-02T21:05:10+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 11
|
||||
updated_files = 31
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/MokoPerfectPublisher-Discord/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,734 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/MokoStandards-Template-Client
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:40:35+00:00
|
||||
* Platform : default-repository
|
||||
* Description: A template repo for clients of Moko Consulting
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/MokoStandards-Template-Client"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "A template repo for clients of Moko Consulting"
|
||||
sync_timestamp = "2026-04-02T15:40:35+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 11
|
||||
updated_files = 31
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/MokoStandards-Template-Client/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,734 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/MokoStandards-Template-Generic
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:27:41+00:00
|
||||
* Platform : default-repository
|
||||
* Description: A repo template for a generic coding project according to MokoStandards
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/MokoStandards-Template-Generic"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "A repo template for a generic coding project according to MokoStandards"
|
||||
sync_timestamp = "2026-04-02T15:27:41+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 11
|
||||
updated_files = 31
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/MokoStandards-Template-Generic/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,734 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/MokoTesting
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:23:26+00:00
|
||||
* Platform : default-repository
|
||||
* Description: Testign grond for Moko Consulting
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/MokoTesting"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "Testign grond for Moko Consulting"
|
||||
sync_timestamp = "2026-04-02T15:23:26+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 11
|
||||
updated_files = 31
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/MokoTesting/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,734 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/MokoWinSetup
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:39:49+00:00
|
||||
* Platform : default-repository
|
||||
* Description: A setup script for Windows
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/MokoWinSetup"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "A setup script for Windows"
|
||||
sync_timestamp = "2026-04-02T15:39:49+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 11
|
||||
updated_files = 31
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/MokoWinSetup/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,734 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/PLG_FINDER_MOKOVMSMARTSEARCH
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:33:10+00:00
|
||||
* Platform : default-repository
|
||||
* Description: Virtuemart Smart serachPlugin
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/PLG_FINDER_MOKOVMSMARTSEARCH"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "Virtuemart Smart serachPlugin "
|
||||
sync_timestamp = "2026-04-02T15:33:10+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 11
|
||||
updated_files = 31
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/PLG_FINDER_MOKOVMSMARTSEARCH/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,775 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/client-clarksvillefurs
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-03-30T08:30:51+00:00
|
||||
* Platform : default-repository
|
||||
* Description: The Clarksville Furs implementation of MokoCassiopeia
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/client-clarksvillefurs"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "The Clarksville Furs implementation of MokoCassiopeia"
|
||||
sync_timestamp = "2026-03-30T08:30:51+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 56
|
||||
created_files = 16
|
||||
updated_files = 22
|
||||
skipped_files = 18
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/ci.yml" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "created" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/repo-health.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/ci.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/client-clarksvillefurs/contents/.github/workflows/ci.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/ci.yml does not match 1f2449072e0d3304fb645b2ef45526fcf978783e\",\"documentation_url\":\"https (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/test.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/client-clarksvillefurs/contents/.github/workflows/test.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/test.yml does not match 5c7b0f5639c622d5c910a066fe2415b69a49918d\",\"documentation_url\":\"htt (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/code-quality.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/client-clarksvillefurs/contents/.github/workflows/code-quality.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/code-quality.yml does not match f510c81073d573669477a96a646d3a8383e77b6b\",\"documentation_u (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/client-clarksvillefurs/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 04c7ef91744b3e482f495e592f268a7448dafe74\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/deploy.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/client-clarksvillefurs/contents/.github/workflows/deploy.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/deploy.yml does not match 4895e597d4ee56a976c8792ab80da574bcfa23c8\",\"documentation_url\":\"h (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/repo-health.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/client-clarksvillefurs/contents/.github/workflows/standards-compliance.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/standards-compliance.yml does not match bc0516a1dbc8b2acfcf3189ac28be497c1a8c93e\",\"documen (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "ci.yml"
|
||||
extension = "yml"
|
||||
description = "Continuous integration workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "ci.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "ci.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/ci.yml.template"
|
||||
},
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repo-health.yml"
|
||||
extension = "yml"
|
||||
description = "Repository health monitoring"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "repo_health.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "repo-health.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/repo_health.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,734 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/client-kiddieland
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:41:12+00:00
|
||||
* Platform : default-repository
|
||||
* Description: Client repo for Kiddieland Child Care Center
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/client-kiddieland"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "Client repo for Kiddieland Child Care Center"
|
||||
sync_timestamp = "2026-04-02T15:41:12+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 53
|
||||
created_files = 11
|
||||
updated_files = 31
|
||||
skipped_files = 11
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{ path = "LICENSE" action = "updated" },
|
||||
{ path = "Makefile" action = "updated" },
|
||||
{ path = "composer.json" action = "updated" },
|
||||
{ path = "docs/index.md" action = "updated" },
|
||||
{ path = "docs/INSTALLATION.md" action = "updated" },
|
||||
{ path = ".github/workflows/test.yml" action = "created" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "created" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "created" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/enterprise-firewall-setup.yml" action = "created" },
|
||||
{ path = ".github/deploy-dev.yml" action = "created" },
|
||||
{ path = ".github/deploy-demo.yml" action = "created" },
|
||||
{ path = ".github/deploy-rs.yml" action = "created" },
|
||||
{ path = ".github/sync-version-on-merge.yml" action = "created" },
|
||||
{ path = ".github/auto-release.yml" action = "created" },
|
||||
{ path = ".github/repository-cleanup.yml" action = "created" },
|
||||
{ path = ".github/auto-dev-issue.yml" action = "created" },
|
||||
{ path = ".github/workflows/test.yml" action = "updated" },
|
||||
{ path = ".github/workflows/code-quality.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy.yml" action = "updated" },
|
||||
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
|
||||
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
|
||||
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
|
||||
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-release.yml" action = "updated" },
|
||||
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
|
||||
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
|
||||
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
|
||||
{ path = ".github/CODEOWNERS" action = "updated" },
|
||||
]
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "GOVERNANCE.md" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/codeql-analysis.yml" reason = "API error: Request failed after 3 attempts: Client error: `PUT https://api.github.com/repos/mokoconsulting-tech/client-kiddieland/contents/.github/workflows/codeql-analysis.yml` resulted in a `409 Conflict` response:
|
||||
{\"message\":\".github/workflows/codeql-analysis.yml does not match 3f50896ddc3f73cd5863338e95067692ee0e52e6\",\"documentatio (truncated...)
|
||||
" },
|
||||
{ path = ".github/workflows/release-cycle.yml" reason = "Source file not found" },
|
||||
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
/**
|
||||
* Default Repository Structure Definition
|
||||
* Default repository structure applicable to all repository types with minimal requirements
|
||||
*
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Schema Version: 1.0
|
||||
*/
|
||||
|
||||
locals {
|
||||
repository_structure = {
|
||||
metadata = {
|
||||
name = "Default Repository Structure"
|
||||
description = "Default repository structure applicable to all repository types with minimal requirements"
|
||||
repository_type = "library"
|
||||
platform = "multi-platform"
|
||||
last_updated = "2026-01-16T00:00:00Z"
|
||||
maintainer = "Moko Consulting"
|
||||
version = "05.00.00"
|
||||
schema_version = "1.0"
|
||||
}
|
||||
|
||||
root_files = [
|
||||
{
|
||||
name = "README.md"
|
||||
extension = "md"
|
||||
description = "Project overview and documentation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-README.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "README.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-README.md"
|
||||
},
|
||||
{
|
||||
name = "LICENSE"
|
||||
extension = ""
|
||||
description = "License file (GPL-3.0-or-later)"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/licenses"
|
||||
source_filename = "GPL-3.0"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "LICENSE"
|
||||
create_path = false
|
||||
template = "templates/licenses/GPL-3.0"
|
||||
},
|
||||
{
|
||||
name = "CHANGELOG.md"
|
||||
extension = "md"
|
||||
description = "Version history and changes"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CHANGELOG.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CHANGELOG.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CHANGELOG.md"
|
||||
},
|
||||
{
|
||||
name = "CONTRIBUTING.md"
|
||||
extension = "md"
|
||||
description = "Contribution guidelines"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-CONTRIBUTING.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CONTRIBUTING.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-CONTRIBUTING.md"
|
||||
},
|
||||
{
|
||||
name = "SECURITY.md"
|
||||
extension = "md"
|
||||
description = "Security policy and vulnerability reporting"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-SECURITY.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "SECURITY.md"
|
||||
create_path = false
|
||||
template = "templates/docs/required/template-SECURITY.md"
|
||||
},
|
||||
{
|
||||
name = "CODE_OF_CONDUCT.md"
|
||||
extension = "md"
|
||||
description = "Community code of conduct"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "contributor"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-CODE_OF_CONDUCT.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "CODE_OF_CONDUCT.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-CODE_OF_CONDUCT.md"
|
||||
},
|
||||
{
|
||||
name = "ROADMAP.md"
|
||||
extension = "md"
|
||||
description = "Project roadmap with version goals and milestones"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-ROADMAP.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "ROADMAP.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-ROADMAP.md"
|
||||
},
|
||||
{
|
||||
name = "GOVERNANCE.md"
|
||||
extension = "md"
|
||||
description = "Project governance model and decision-making process"
|
||||
requirement_status = "suggested"
|
||||
always_overwrite = false
|
||||
protected = true
|
||||
audience = "general"
|
||||
source_path = "templates/docs/extra"
|
||||
source_filename = "template-GOVERNANCE.md"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "GOVERNANCE.md"
|
||||
create_path = false
|
||||
template = "templates/docs/extra/template-GOVERNANCE.md"
|
||||
},
|
||||
{
|
||||
name = ".gitignore"
|
||||
extension = "gitignore"
|
||||
description = "Git ignore patterns"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".gitattributes"
|
||||
extension = "gitattributes"
|
||||
description = "Git attributes configuration"
|
||||
requirement_status = "required"
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = ".editorconfig"
|
||||
extension = "editorconfig"
|
||||
description = "Editor configuration for consistent coding style"
|
||||
requirement_status = "required"
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
},
|
||||
{
|
||||
name = "Makefile"
|
||||
description = "Build automation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
audience = "developer"
|
||||
source_path = "templates/makefiles"
|
||||
source_filename = "Makefile.generic.template"
|
||||
source_type = "template"
|
||||
destination_path = "."
|
||||
destination_filename = "Makefile"
|
||||
create_path = false
|
||||
template = "templates/makefiles/Makefile.generic.template"
|
||||
},
|
||||
{
|
||||
name = "composer.json"
|
||||
extension = "json"
|
||||
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
|
||||
required = true
|
||||
always_overwrite = false
|
||||
audience = "developer"
|
||||
template = "templates/configs/composer.generic.json"
|
||||
}
|
||||
]
|
||||
|
||||
directories = [
|
||||
{
|
||||
name = "docs"
|
||||
path = "docs"
|
||||
description = "Documentation directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains comprehensive project documentation"
|
||||
files = [
|
||||
{
|
||||
name = "index.md"
|
||||
extension = "md"
|
||||
description = "Documentation index"
|
||||
requirement_status = "suggested"
|
||||
template = "templates/docs/index.md"
|
||||
},
|
||||
{
|
||||
name = "INSTALLATION.md"
|
||||
extension = "md"
|
||||
description = "Installation and setup instructions"
|
||||
requirement_status = "required"
|
||||
audience = "general"
|
||||
source_path = "templates/docs/required"
|
||||
source_filename = "template-INSTALLATION.md"
|
||||
source_type = "template"
|
||||
destination_path = "docs"
|
||||
destination_filename = "INSTALLATION.md"
|
||||
create_path = true
|
||||
template = "templates/docs/required/template-INSTALLATION.md"
|
||||
},
|
||||
{
|
||||
name = "API.md"
|
||||
extension = "md"
|
||||
description = "API documentation"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "ARCHITECTURE.md"
|
||||
extension = "md"
|
||||
description = "Architecture documentation"
|
||||
requirement_status = "suggested"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "scripts"
|
||||
path = "scripts"
|
||||
description = "Repo-specific scripts — not managed by MokoStandards sync"
|
||||
required = false
|
||||
purpose = "Optional directory for repo-specific build helpers and one-off scripts. MokoStandards tools are installed via Composer (mokoconsulting-tech/enterprise) and called through vendor/bin/."
|
||||
files = [
|
||||
{
|
||||
name = "MokoStandards.override.xml"
|
||||
extension = "xml"
|
||||
description = "MokoStandards sync override configuration"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "src"
|
||||
path = "src"
|
||||
description = "Source code directory"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains application source code"
|
||||
},
|
||||
{
|
||||
name = "tests"
|
||||
path = "tests"
|
||||
description = "Test files"
|
||||
requirement_status = "suggested"
|
||||
purpose = "Contains unit tests, integration tests, and test fixtures"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "unit"
|
||||
path = "tests/unit"
|
||||
description = "Unit tests"
|
||||
requirement_status = "suggested"
|
||||
},
|
||||
{
|
||||
name = "integration"
|
||||
path = "tests/integration"
|
||||
description = "Integration tests"
|
||||
requirement_status = "optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = ".github"
|
||||
path = ".github"
|
||||
description = "GitHub-specific configuration"
|
||||
requirement_status = "required"
|
||||
purpose = "Contains GitHub Actions workflows and configuration"
|
||||
subdirectories = [
|
||||
{
|
||||
name = "workflows"
|
||||
path = ".github/workflows"
|
||||
description = "GitHub Actions workflows"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "test.yml"
|
||||
extension = "yml"
|
||||
description = "Comprehensive testing workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "test.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "test.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/test.yml.template"
|
||||
},
|
||||
{
|
||||
name = "code-quality.yml"
|
||||
extension = "yml"
|
||||
description = "Code quality and linting workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "code-quality.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "code-quality.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/code-quality.yml.template"
|
||||
},
|
||||
{
|
||||
name = "codeql-analysis.yml"
|
||||
extension = "yml"
|
||||
description = "CodeQL security analysis workflow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "codeql-analysis.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "codeql-analysis.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/codeql-analysis.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy.yml"
|
||||
extension = "yml"
|
||||
description = "Deployment workflow"
|
||||
requirement_status = "optional"
|
||||
always_overwrite = true
|
||||
source_path = "templates/workflows/generic"
|
||||
source_filename = "deploy.yml.template"
|
||||
source_type = "template"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "deploy.yml"
|
||||
create_path = true
|
||||
template = "templates/workflows/generic/deploy.yml.template"
|
||||
},
|
||||
{
|
||||
name = "release-cycle.yml"
|
||||
extension = "yml"
|
||||
description = "Release management workflow with automated release flow"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "release-cycle.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "release-cycle.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/release-cycle.yml"
|
||||
},
|
||||
{
|
||||
name = "standards-compliance.yml"
|
||||
extension = "yml"
|
||||
description = "MokoStandards compliance validation"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
source_path = ".github/workflows"
|
||||
source_filename = "standards-compliance.yml"
|
||||
source_type = "copy"
|
||||
destination_path = ".github/workflows"
|
||||
destination_filename = "standards-compliance.yml"
|
||||
create_path = true
|
||||
template = ".github/workflows/standards-compliance.yml"
|
||||
},
|
||||
{
|
||||
name = "enterprise-firewall-setup.yml"
|
||||
extension = "yml"
|
||||
description = "Enterprise firewall configuration for trusted domain access"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-dev.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the development server"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-dev.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-demo.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the demo server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-demo.yml.template"
|
||||
},
|
||||
{
|
||||
name = "deploy-rs.yml"
|
||||
extension = "yml"
|
||||
description = "SFTP deployment of src/ to the release staging server on merge to main"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/deploy-rs.yml.template"
|
||||
},
|
||||
{
|
||||
name = "sync-version-on-merge.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-bump patch version on merge and propagate to all file headers"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-release.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create GitHub Release on push to main with version from README.md"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-release.yml.template"
|
||||
},
|
||||
{
|
||||
name = "repository-cleanup.yml"
|
||||
extension = "yml"
|
||||
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/repository-cleanup.yml.template"
|
||||
},
|
||||
{
|
||||
name = "auto-dev-issue.yml"
|
||||
extension = "yml"
|
||||
description = "Auto-create tracking issue when a dev/** branch is pushed"
|
||||
requirement_status = "required"
|
||||
always_overwrite = true
|
||||
template = "templates/workflows/shared/auto-dev-issue.yml.template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "ISSUE_TEMPLATE"
|
||||
path = ".github/ISSUE_TEMPLATE"
|
||||
description = "GitHub issue templates synced from MokoStandards"
|
||||
requirement_status = "required"
|
||||
files = [
|
||||
{
|
||||
name = "config.yml"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/config.yml"
|
||||
},
|
||||
{
|
||||
name = "adr.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/adr.md"
|
||||
},
|
||||
{
|
||||
name = "bug_report.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
|
||||
},
|
||||
{
|
||||
name = "documentation.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
|
||||
},
|
||||
{
|
||||
name = "enterprise_support.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
|
||||
},
|
||||
{
|
||||
name = "feature_request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
|
||||
},
|
||||
{
|
||||
name = "firewall-request.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
|
||||
},
|
||||
{
|
||||
name = "question.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/question.md"
|
||||
},
|
||||
{
|
||||
name = "request-license.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
|
||||
},
|
||||
{
|
||||
name = "rfc.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
|
||||
},
|
||||
{
|
||||
name = "security.md"
|
||||
always_overwrite = true
|
||||
template = "templates/github/ISSUE_TEMPLATE/security.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name = "node_modules"
|
||||
path = "node_modules"
|
||||
description = "Node.js dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "vendor"
|
||||
path = "vendor"
|
||||
description = "PHP dependencies (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "build"
|
||||
path = "build"
|
||||
description = "Build artifacts (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
},
|
||||
{
|
||||
name = "dist"
|
||||
path = "dist"
|
||||
description = "Distribution files (generated)"
|
||||
requirement_status = "not-allowed"
|
||||
purpose = "Generated directory that should not be committed"
|
||||
}
|
||||
]
|
||||
|
||||
repository_requirements = {
|
||||
secrets = [
|
||||
{
|
||||
name = "GH_TOKEN"
|
||||
description = "Org-level GitHub PAT — configure in org Actions secrets"
|
||||
required = true
|
||||
scope = "organisation"
|
||||
used_in = "GitHub Actions workflows"
|
||||
},
|
||||
{
|
||||
name = "CODECOV_TOKEN"
|
||||
description = "Codecov upload token for code coverage reporting"
|
||||
required = false
|
||||
scope = "repository"
|
||||
used_in = "CI workflow code coverage step"
|
||||
}
|
||||
]
|
||||
|
||||
variables = [
|
||||
{
|
||||
name = "NODE_VERSION"
|
||||
description = "Node.js version for CI/CD"
|
||||
default_value = "18"
|
||||
required = false
|
||||
scope = "repository"
|
||||
},
|
||||
{
|
||||
name = "PYTHON_VERSION"
|
||||
description = "Python version for CI/CD"
|
||||
default_value = "3.9"
|
||||
required = false
|
||||
scope = "repository"
|
||||
}
|
||||
]
|
||||
|
||||
branch_protections = [
|
||||
{
|
||||
branch_pattern = "main"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci", "code-quality"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
},
|
||||
{
|
||||
branch_pattern = "master"
|
||||
require_pull_request = true
|
||||
required_approvals = 1
|
||||
require_code_owner_review = false
|
||||
dismiss_stale_reviews = true
|
||||
require_status_checks = true
|
||||
required_status_checks = ["ci"]
|
||||
enforce_admins = false
|
||||
restrict_pushes = true
|
||||
}
|
||||
]
|
||||
|
||||
repository_settings = {
|
||||
has_issues = true
|
||||
has_projects = true
|
||||
has_wiki = false
|
||||
has_discussions = false
|
||||
allow_merge_commit = true
|
||||
allow_squash_merge = true
|
||||
allow_rebase_merge = false
|
||||
delete_branch_on_merge = true
|
||||
allow_auto_merge = false
|
||||
}
|
||||
|
||||
labels = [
|
||||
{
|
||||
name = "bug"
|
||||
color = "d73a4a"
|
||||
description = "Something isn't working"
|
||||
},
|
||||
{
|
||||
name = "enhancement"
|
||||
color = "a2eeef"
|
||||
description = "New feature or request"
|
||||
},
|
||||
{
|
||||
name = "documentation"
|
||||
color = "0075ca"
|
||||
description = "Improvements or additions to documentation"
|
||||
},
|
||||
{
|
||||
name = "security"
|
||||
color = "ee0701"
|
||||
description = "Security vulnerability or concern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Repository Sync Tracking Definition: mokoconsulting-tech/joomla-api-mcp
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on 2026-04-23T00:00:00+00:00
|
||||
* Platform : default-repository
|
||||
* Description: MCP server for Joomla Web Services API operations
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit api/definitions/default/default-repository.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "mokoconsulting-tech/joomla-api-mcp"
|
||||
default_branch = "main"
|
||||
detected_platform = "default-repository"
|
||||
description = "MCP server for Joomla Web Services API operations"
|
||||
sync_timestamp = "2026-04-23T00:00:00+00:00"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "api/definitions/default/default-repository.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = 0
|
||||
created_files = 0
|
||||
updated_files = 0
|
||||
skipped_files = 0
|
||||
}
|
||||
|
||||
synced_files = []
|
||||
|
||||
skipped_files = [
|
||||
{ path = "README.md" reason = "README — never overwritten" },
|
||||
{ path = "CHANGELOG.md" reason = "CHANGELOG — never overwritten" },
|
||||
{ path = "CONTRIBUTING.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "SECURITY.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "CODE_OF_CONDUCT.md" reason = "Preserved (always_overwrite=false)" },
|
||||
{ path = "ROADMAP.md" reason = "Preserved (always_overwrite=false)" },
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,501 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoStandards.Enterprise
|
||||
* INGROUP: MokoStandards
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /lib/Enterprise/DefinitionParser.php
|
||||
* BRIEF: Parses Terraform HCL repository definition files into a flat sync-file list
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MokoEnterprise;
|
||||
|
||||
/**
|
||||
* Definition Parser
|
||||
*
|
||||
* Parses the Terraform HCL repository definition files stored in
|
||||
* definitions/default/ and returns a flat list of file sync entries.
|
||||
*
|
||||
* File blocks that carry either a `template` field (external file path) or a
|
||||
* `stub_content` heredoc (inline content) are returned — these are the files
|
||||
* that the bulk-sync process should push to remote repositories.
|
||||
*
|
||||
* When both `stub_content` and `template` are present in the same block,
|
||||
* `stub_content` takes priority (the definition file is authoritative).
|
||||
*
|
||||
* Each returned entry is an associative array with one of two shapes:
|
||||
*
|
||||
* External-file entry (legacy, uses `template` path):
|
||||
* 'source' => string — path relative to the MokoStandards repo root
|
||||
* 'destination' => string — path in the target repository
|
||||
* 'always_overwrite' => bool — true: overwrite existing file; false: create-only
|
||||
*
|
||||
* Inline-content entry (uses `stub_content` heredoc):
|
||||
* 'inline_content' => string — rendered template content (ready to push)
|
||||
* 'destination' => string — path in the target repository
|
||||
* 'always_overwrite' => bool — true: overwrite existing file; false: create-only
|
||||
*
|
||||
* @since 04.00.00
|
||||
*/
|
||||
class DefinitionParser
|
||||
{
|
||||
/** Map platform slug → definition file basename */
|
||||
private const PLATFORM_DEFINITION_MAP = [
|
||||
'crm-module' => 'crm-module.tf',
|
||||
'waas-component' => 'waas-component.tf',
|
||||
'generic-repository' => 'generic-repository.tf',
|
||||
'default-repository' => 'default-repository.tf',
|
||||
'standards' => 'standards-repository.tf',
|
||||
];
|
||||
|
||||
/** Default definition used when platform has no specific file */
|
||||
private const FALLBACK_DEFINITION = 'default-repository.tf';
|
||||
|
||||
/** Directory containing the base definition files */
|
||||
private const DEFINITIONS_DIR = 'definitions/default';
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Public API
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parse a definition file by platform slug.
|
||||
*
|
||||
* @param string $platform e.g. 'crm-module', 'waas-component'
|
||||
* @param string $repoRoot Absolute path to the MokoStandards repository root
|
||||
* @return array<int, array{source: string, destination: string, always_overwrite: bool}>
|
||||
*/
|
||||
public function parseForPlatform(string $platform, string $repoRoot): array
|
||||
{
|
||||
$basename = self::PLATFORM_DEFINITION_MAP[$platform] ?? self::FALLBACK_DEFINITION;
|
||||
$path = rtrim($repoRoot, '/') . '/' . self::DEFINITIONS_DIR . '/' . $basename;
|
||||
|
||||
if (!file_exists($path)) {
|
||||
$fallback = rtrim($repoRoot, '/') . '/' . self::DEFINITIONS_DIR . '/' . self::FALLBACK_DEFINITION;
|
||||
if (!file_exists($fallback)) {
|
||||
return [];
|
||||
}
|
||||
$path = $fallback;
|
||||
}
|
||||
|
||||
return $this->parseFile($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a definition file at an explicit filesystem path.
|
||||
*
|
||||
* @param string $filePath Absolute path to the .tf definition file
|
||||
* @return array<int, array{source: string, destination: string, always_overwrite: bool}>
|
||||
*/
|
||||
public function parseFile(string $filePath): array
|
||||
{
|
||||
if (!file_exists($filePath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$content = file_get_contents($filePath);
|
||||
if ($content === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->parse($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse raw HCL content.
|
||||
*
|
||||
* @param string $content Raw .tf file content
|
||||
* @return array<int, array{source: string, destination: string, always_overwrite: bool}>
|
||||
*/
|
||||
public function parse(string $content): array
|
||||
{
|
||||
$entries = [];
|
||||
|
||||
// root_files = [ { ... }, ... ]
|
||||
$rootFilesContent = $this->extractNamedArray($content, 'root_files');
|
||||
if ($rootFilesContent !== null) {
|
||||
$entries = array_merge($entries, $this->parseFileBlocks($rootFilesContent, ''));
|
||||
}
|
||||
|
||||
// directories = [ { ... }, ... ]
|
||||
$dirsContent = $this->extractNamedArray($content, 'directories');
|
||||
if ($dirsContent !== null) {
|
||||
$entries = array_merge($entries, $this->parseDirectories($dirsContent));
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Internal parsing helpers
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Locate `name = [` inside $content and return the content between the
|
||||
* outermost `[` and its matching `]`, or null if not found.
|
||||
*/
|
||||
private function extractNamedArray(string $content, string $name): ?string
|
||||
{
|
||||
$pattern = '/\b' . preg_quote($name, '/') . '\s*=\s*\[/';
|
||||
|
||||
// Build a mask of heredoc regions so the regex doesn't match inside them.
|
||||
// Replace heredoc content with spaces (preserving offsets) before matching.
|
||||
$masked = $content;
|
||||
$len = strlen($content);
|
||||
$i = 0;
|
||||
while ($i < $len - 1) {
|
||||
if ($content[$i] === '<' && $content[$i + 1] === '<') {
|
||||
$heredocEnd = $this->skipHeredoc($content, $i, $len);
|
||||
// Blank out the heredoc region in the masked copy
|
||||
for ($k = $i; $k < $heredocEnd && $k < $len; $k++) {
|
||||
$masked[$k] = ($content[$k] === "\n") ? "\n" : ' ';
|
||||
}
|
||||
$i = $heredocEnd;
|
||||
continue;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
if (!preg_match($pattern, $masked, $match, PREG_OFFSET_CAPTURE)) {
|
||||
return null;
|
||||
}
|
||||
// Position of the `[` at the end of the matched string — use original content
|
||||
$openPos = $match[0][1] + strlen($match[0][0]) - 1;
|
||||
return $this->extractBetweenPair($content, $openPos, '[', ']');
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting at $pos (which must hold $open), walk forward counting depth
|
||||
* until the matching $close is found. Returns the content between them
|
||||
* (exclusive), or null on malformed input.
|
||||
*/
|
||||
private function extractBetweenPair(string $content, int $pos, string $open, string $close): ?string
|
||||
{
|
||||
if (!isset($content[$pos]) || $content[$pos] !== $open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$depth = 0;
|
||||
$start = $pos;
|
||||
$len = strlen($content);
|
||||
|
||||
for ($i = $pos; $i < $len; $i++) {
|
||||
// Skip heredoc regions — they contain unbalanced brackets in markdown/code
|
||||
if ($content[$i] === '<' && isset($content[$i + 1]) && $content[$i + 1] === '<') {
|
||||
$i = $this->skipHeredoc($content, $i, $len) - 1; // -1 because for loop increments
|
||||
continue;
|
||||
}
|
||||
if ($content[$i] === $open) {
|
||||
$depth++;
|
||||
} elseif ($content[$i] === $close) {
|
||||
$depth--;
|
||||
if ($depth === 0) {
|
||||
return substr($content, $start + 1, $i - $start - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null; // unterminated
|
||||
}
|
||||
|
||||
/**
|
||||
* Split $content into top-level `{ … }` blocks (depth 1 only).
|
||||
*
|
||||
* Heredoc sections (`<<-WORD … WORD` and `<<WORD … WORD`) are skipped in
|
||||
* their entirety so that any `{` or `}` characters inside template content
|
||||
* do not corrupt the brace-depth counter.
|
||||
*
|
||||
* @return string[] Each element is the inner content of one block (without outer braces)
|
||||
*/
|
||||
private function splitBlocks(string $content): array
|
||||
{
|
||||
$blocks = [];
|
||||
$depth = 0;
|
||||
$start = null;
|
||||
$len = strlen($content);
|
||||
$i = 0;
|
||||
|
||||
while ($i < $len) {
|
||||
// Detect heredoc: <<WORD or <<-WORD
|
||||
if ($content[$i] === '<' && isset($content[$i + 1]) && $content[$i + 1] === '<') {
|
||||
$i = $this->skipHeredoc($content, $i, $len);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($content[$i] === '{') {
|
||||
if ($depth === 0) {
|
||||
$start = $i;
|
||||
}
|
||||
$depth++;
|
||||
} elseif ($content[$i] === '}') {
|
||||
$depth--;
|
||||
if ($depth === 0 && $start !== null) {
|
||||
$blocks[] = substr($content, $start + 1, $i - $start - 1);
|
||||
$start = null;
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance past a HCL heredoc starting at position $i.
|
||||
*
|
||||
* Supports both `<<WORD` (content-preserving) and `<<-WORD`
|
||||
* (indent-stripping) forms. Returns the index immediately after the
|
||||
* closing delimiter line, or $i + 2 if the heredoc is malformed.
|
||||
*/
|
||||
private function skipHeredoc(string $content, int $i, int $len): int
|
||||
{
|
||||
$j = $i + 2; // skip <<
|
||||
|
||||
// Optional indent-strip marker
|
||||
$stripIndent = false;
|
||||
if (isset($content[$j]) && $content[$j] === '-') {
|
||||
$stripIndent = true;
|
||||
$j++;
|
||||
}
|
||||
|
||||
// Read the delimiter identifier (alphanumeric + underscore)
|
||||
$delimiter = '';
|
||||
while ($j < $len && (ctype_alnum($content[$j]) || $content[$j] === '_')) {
|
||||
$delimiter .= $content[$j];
|
||||
$j++;
|
||||
}
|
||||
|
||||
if ($delimiter === '') {
|
||||
return $i + 2; // Not a real heredoc
|
||||
}
|
||||
|
||||
// Skip optional whitespace and the rest of the opening line
|
||||
while ($j < $len && $content[$j] !== "\n") {
|
||||
$j++;
|
||||
}
|
||||
if ($j < $len) {
|
||||
$j++; // skip the newline after the opening line
|
||||
}
|
||||
|
||||
// Scan line by line until the closing delimiter
|
||||
while ($j < $len) {
|
||||
$lineEnd = strpos($content, "\n", $j);
|
||||
$lineEnd = ($lineEnd === false) ? $len : $lineEnd;
|
||||
|
||||
$line = substr($content, $j, $lineEnd - $j);
|
||||
// For <<- (indent-stripping) form, the terminator may itself be indented;
|
||||
// strip leading whitespace before comparing. For the non-stripping form
|
||||
// (<<), the terminator must be at column 0 — but we still rtrim trailing
|
||||
// whitespace/CR to handle Windows line-endings gracefully.
|
||||
$normalised = $stripIndent ? trim($line) : rtrim($line);
|
||||
if ($normalised === $delimiter) {
|
||||
return $lineEnd + 1;
|
||||
}
|
||||
$j = $lineEnd + 1;
|
||||
}
|
||||
|
||||
return $len; // unterminated heredoc — consume to EOF
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse all file blocks inside a `files = [ … ]` array content,
|
||||
* returning only those that have a `template` field.
|
||||
*
|
||||
* @param string $arrayContent Inner content between the outer `[` and `]`
|
||||
* @param string $dirPath Directory prefix for the destination ('' = repo root)
|
||||
* @return array<int, array{source: string, destination: string, always_overwrite: bool}>
|
||||
*/
|
||||
private function parseFileBlocks(string $arrayContent, string $dirPath): array
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this->splitBlocks($arrayContent) as $block) {
|
||||
$entry = $this->parseFileBlock($block, $dirPath);
|
||||
if ($entry !== null) {
|
||||
$entries[] = $entry;
|
||||
}
|
||||
}
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single file block `{ name = "…", template = "…", … }` or
|
||||
* `{ name = "…", stub_content = <<-EOT … EOT, … }`.
|
||||
*
|
||||
* When a `stub_content` heredoc is present it takes priority over a
|
||||
* `template` file-path reference. Returns null when the block has
|
||||
* neither (structural-only entry that should not be synced).
|
||||
*
|
||||
* @return array{source?: string, inline_content?: string, destination: string, always_overwrite: bool}|null
|
||||
*/
|
||||
private function parseFileBlock(string $block, string $dirPath): ?array
|
||||
{
|
||||
// --- try stub_content heredoc first (preferred) ---
|
||||
$inlineContent = $this->extractHeredoc($block, 'stub_content');
|
||||
|
||||
// --- fall back to stub_content as a quoted string (e.g. "line1\nline2") ---
|
||||
if ($inlineContent === null) {
|
||||
if (preg_match('/\bstub_content\s*=\s*"((?:[^"\\\\]|\\\\.)*)"/', $block, $m)) {
|
||||
$inlineContent = stripcslashes($m[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// --- fall back to external template path ---
|
||||
$source = null;
|
||||
if ($inlineContent === null) {
|
||||
if (!preg_match('/\btemplate\s*=\s*"([^"]+)"/', $block, $m)) {
|
||||
return null; // neither inline content nor template → structural entry
|
||||
}
|
||||
$source = $m[1];
|
||||
}
|
||||
|
||||
// name is required
|
||||
if (!preg_match('/\bname\s*=\s*"([^"]+)"/', $block, $m)) {
|
||||
return null;
|
||||
}
|
||||
$filename = $m[1];
|
||||
|
||||
// destination_filename overrides name
|
||||
if (preg_match('/\bdestination_filename\s*=\s*"([^"]+)"/', $block, $m)) {
|
||||
$filename = $m[1];
|
||||
}
|
||||
|
||||
// destination_path overrides dirPath
|
||||
if (preg_match('/\bdestination_path\s*=\s*"([^"]+)"/', $block, $m)) {
|
||||
$dp = trim($m[1], '/');
|
||||
$destination = ($dp === '' || $dp === '.') ? $filename : "{$dp}/{$filename}";
|
||||
} else {
|
||||
$destination = $dirPath === '' ? $filename : "{$dirPath}/{$filename}";
|
||||
}
|
||||
|
||||
// always_overwrite — default true for all template-driven files
|
||||
$alwaysOverwrite = true;
|
||||
if (preg_match('/\balways_overwrite\s*=\s*(true|false)\b/', $block, $m)) {
|
||||
$alwaysOverwrite = ($m[1] === 'true');
|
||||
}
|
||||
|
||||
// protected — when true, file is never overwritten even with --force
|
||||
$protected = false;
|
||||
if (preg_match('/\bprotected\s*=\s*(true|false)\b/', $block, $m)) {
|
||||
$protected = ($m[1] === 'true');
|
||||
}
|
||||
|
||||
if ($inlineContent !== null) {
|
||||
return [
|
||||
'inline_content' => $inlineContent,
|
||||
'destination' => $destination,
|
||||
'always_overwrite' => $alwaysOverwrite,
|
||||
'protected' => $protected,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'source' => $source,
|
||||
'destination' => $destination,
|
||||
'always_overwrite' => $alwaysOverwrite,
|
||||
'protected' => $protected,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a heredoc value for the given field name from a block string.
|
||||
*
|
||||
* Handles both `<<WORD` (content-preserving) and `<<-WORD`
|
||||
* (indent-stripping) forms. Leading tabs/spaces are stripped uniformly
|
||||
* when the `<<-` form is used, matching HCL semantics.
|
||||
*
|
||||
* Returns null when the field is not found.
|
||||
*/
|
||||
private function extractHeredoc(string $block, string $field): ?string
|
||||
{
|
||||
$pattern = '/\b' . preg_quote($field, '/') . '\s*=\s*<<(-?)(\w+)[ \t]*\r?\n(.*?)\r?\n[ \t]*\2[ \t]*(?:\r?\n|$)/s';
|
||||
if (!preg_match($pattern, $block, $m)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stripIndent = ($m[1] === '-');
|
||||
$rawContent = $m[3];
|
||||
|
||||
if ($stripIndent) {
|
||||
// Determine the minimum leading-whitespace prefix across non-empty lines
|
||||
$lines = explode("\n", $rawContent);
|
||||
$minIndent = PHP_INT_MAX;
|
||||
foreach ($lines as $line) {
|
||||
if (trim($line) === '') {
|
||||
continue;
|
||||
}
|
||||
$indent = strlen($line) - strlen(ltrim($line, " \t"));
|
||||
if ($indent < $minIndent) {
|
||||
$minIndent = $indent;
|
||||
}
|
||||
}
|
||||
if ($minIndent === PHP_INT_MAX) {
|
||||
$minIndent = 0;
|
||||
}
|
||||
// Strip that many characters from the start of each line
|
||||
$lines = array_map(
|
||||
static fn(string $l) => (strlen($l) >= $minIndent) ? substr($l, $minIndent) : $l,
|
||||
$lines
|
||||
);
|
||||
$rawContent = implode("\n", $lines);
|
||||
}
|
||||
|
||||
return $rawContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the `directories = [ … ]` array, descending into every
|
||||
* `subdirectories` block recursively.
|
||||
*
|
||||
* @return array<int, array{source: string, destination: string, always_overwrite: bool}>
|
||||
*/
|
||||
private function parseDirectories(string $dirsArrayContent): array
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this->splitBlocks($dirsArrayContent) as $block) {
|
||||
$entries = array_merge($entries, $this->parseDirectoryBlock($block));
|
||||
}
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process one directory block: extract its path, parse its files, and
|
||||
* recurse into any subdirectories.
|
||||
*
|
||||
* @return array<int, array{source: string, destination: string, always_overwrite: bool}>
|
||||
*/
|
||||
private function parseDirectoryBlock(string $block): array
|
||||
{
|
||||
$entries = [];
|
||||
|
||||
// Determine the path prefix for files inside this directory
|
||||
$dirPath = '';
|
||||
if (preg_match('/\bpath\s*=\s*"([^"]+)"/', $block, $m)) {
|
||||
$dirPath = $m[1];
|
||||
}
|
||||
|
||||
// files = [ … ] inside this directory
|
||||
$filesContent = $this->extractNamedArray($block, 'files');
|
||||
if ($filesContent !== null) {
|
||||
$entries = array_merge($entries, $this->parseFileBlocks($filesContent, $dirPath));
|
||||
}
|
||||
|
||||
// subdirectories = [ … ] — recurse
|
||||
$subdirsContent = $this->extractNamedArray($block, 'subdirectories');
|
||||
if ($subdirsContent !== null) {
|
||||
foreach ($this->splitBlocks($subdirsContent) as $subBlock) {
|
||||
$entries = array_merge($entries, $this->parseDirectoryBlock($subBlock));
|
||||
}
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.Enterprise
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /lib/Enterprise/ManifestReader.php
|
||||
* BRIEF: Read and parse .mokogitea/manifest.xml — shared across all CLI tools
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MokoEnterprise;
|
||||
|
||||
/**
|
||||
* Manifest Reader
|
||||
*
|
||||
* Parses .mokogitea/manifest.xml and provides typed access to all fields.
|
||||
* Used by CLI tools and the Enterprise library to determine platform,
|
||||
* build configuration, and deployment settings from the repository manifest.
|
||||
*
|
||||
* @since 09.01.00
|
||||
*/
|
||||
class ManifestReader
|
||||
{
|
||||
/** @var array<string, string> Parsed manifest fields */
|
||||
private array $fields = [];
|
||||
|
||||
/** @var bool Whether a manifest was found and parsed */
|
||||
private bool $loaded = false;
|
||||
|
||||
/**
|
||||
* Load manifest from a repository root directory.
|
||||
*
|
||||
* @param string $root Repository root path
|
||||
* @return self
|
||||
*/
|
||||
public static function fromPath(string $root): self
|
||||
{
|
||||
$reader = new self();
|
||||
$reader->load($root);
|
||||
return $reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and parse the manifest file.
|
||||
*
|
||||
* @param string $root Repository root path
|
||||
*/
|
||||
public function load(string $root): void
|
||||
{
|
||||
$candidates = [
|
||||
"{$root}/.mokogitea/manifest.xml",
|
||||
"{$root}/.mokogitea/.manifest.xml",
|
||||
"{$root}/.mokogitea/.moko-platform",
|
||||
];
|
||||
|
||||
$manifestFile = null;
|
||||
foreach ($candidates as $candidate) {
|
||||
if (file_exists($candidate)) {
|
||||
$manifestFile = $candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($manifestFile === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$xml = @simplexml_load_file($manifestFile);
|
||||
if ($xml === false) {
|
||||
// Fallback: YAML legacy format
|
||||
$content = file_get_contents($manifestFile);
|
||||
if (preg_match('/^platform:\s*(.+)/m', $content, $m)) {
|
||||
$this->fields['platform'] = trim($m[1], " \t\n\r\"'");
|
||||
}
|
||||
$this->loaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fields = [
|
||||
'name' => (string)($xml->identity->name ?? ''),
|
||||
'org' => (string)($xml->identity->org ?? ''),
|
||||
'description' => (string)($xml->identity->description ?? ''),
|
||||
'license' => (string)($xml->identity->license ?? ''),
|
||||
'license-spdx' => (string)($xml->identity->license['spdx'] ?? ''),
|
||||
'version' => (string)($xml->identity->version ?? ''),
|
||||
'platform' => (string)($xml->governance->platform ?? ''),
|
||||
'standards-version' => (string)($xml->governance->{"standards-version"} ?? ''),
|
||||
'language' => (string)($xml->build->language ?? ''),
|
||||
'package-type' => (string)($xml->build->{"package-type"} ?? ''),
|
||||
'entry-point' => (string)($xml->build->{"entry-point"} ?? ''),
|
||||
'source-dir' => (string)($xml->deploy->{"source-dir"} ?? ''),
|
||||
'remote-subdir' => (string)($xml->deploy->{"remote-subdir"} ?? ''),
|
||||
'dev-host' => (string)($xml->deploy->{"dev-host"} ?? ''),
|
||||
'demo-host' => (string)($xml->deploy->{"demo-host"} ?? ''),
|
||||
];
|
||||
|
||||
// Strip empty values
|
||||
$this->fields = array_filter($this->fields, fn($v) => $v !== '');
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a manifest was found and loaded.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoaded(): bool
|
||||
{
|
||||
return $this->loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single field value.
|
||||
*
|
||||
* @param string $key Field name (e.g. 'platform', 'package-type')
|
||||
* @param string $default Default value if field is missing
|
||||
* @return string
|
||||
*/
|
||||
public function get(string $key, string $default = ''): string
|
||||
{
|
||||
return $this->fields[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the platform slug, normalized to canonical values.
|
||||
*
|
||||
* @return string One of: joomla, dolibarr, generic, mcp, nodejs
|
||||
*/
|
||||
public function getPlatform(): string
|
||||
{
|
||||
$raw = $this->get('platform', 'generic');
|
||||
return match ($raw) {
|
||||
'waas-component' => 'joomla',
|
||||
'crm-module' => 'dolibarr',
|
||||
default => $raw,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source/entry-point directory.
|
||||
*
|
||||
* @param string $root Repository root for existence checking
|
||||
* @return string Resolved source directory path (e.g. 'src', 'htdocs')
|
||||
*/
|
||||
public function getSourceDir(string $root = ''): string
|
||||
{
|
||||
$entryPoint = $this->get('entry-point', '');
|
||||
if ($entryPoint !== '') {
|
||||
// Strip trailing filename (e.g. src/index.ts → src)
|
||||
$dir = rtrim(dirname($entryPoint) === '.' ? $entryPoint : dirname($entryPoint), '/');
|
||||
if ($root === '' || is_dir("{$root}/{$dir}")) {
|
||||
return $dir;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: check common directories
|
||||
if ($root !== '') {
|
||||
if (is_dir("{$root}/src")) {
|
||||
return 'src';
|
||||
}
|
||||
if (is_dir("{$root}/htdocs")) {
|
||||
return 'htdocs';
|
||||
}
|
||||
}
|
||||
|
||||
return 'src';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the package type for build decisions.
|
||||
*
|
||||
* @return string e.g. 'package', 'dolibarr', 'generic', 'mcp-server'
|
||||
*/
|
||||
public function getPackageType(): string
|
||||
{
|
||||
return $this->get('package-type', 'generic');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all parsed fields.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ class MokoStandardsParser
|
||||
public const NAMESPACE_URI = 'https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API';
|
||||
public const STANDARDS_SOURCE = 'https://git.mokoconsulting.tech/MokoConsulting/MokoStandards';
|
||||
|
||||
/** Valid platform slugs — must match definitions/default/*.tf filenames. */
|
||||
/** Valid platform slugs — must match Template-* repo names. */
|
||||
public const VALID_PLATFORMS = [
|
||||
'default-repository',
|
||||
'crm-module',
|
||||
|
||||
@@ -32,7 +32,6 @@ use RuntimeException;
|
||||
*/
|
||||
class RepositorySynchronizer
|
||||
{
|
||||
private const SYNC_DEFINITION_DIR = 'definitions/sync';
|
||||
/** Override file path — resolved at runtime via adapter's getMetadataDir(). */
|
||||
private const SYNC_OVERRIDE_FILE_SUFFIX = 'override.tf';
|
||||
private const STANDARDS_VERSION = '04.06.00';
|
||||
@@ -45,7 +44,6 @@ class RepositorySynchronizer
|
||||
private AuditLogger $logger;
|
||||
private MetricsCollector $metrics;
|
||||
private CheckpointManager $checkpoints;
|
||||
private DefinitionParser $definitionParser;
|
||||
private MokoStandardsParser $manifestParser;
|
||||
|
||||
/**
|
||||
@@ -55,7 +53,6 @@ class RepositorySynchronizer
|
||||
* @param AuditLogger $logger Audit logger
|
||||
* @param MetricsCollector $metrics Metrics collector
|
||||
* @param CheckpointManager|null $checkpoints Checkpoint manager
|
||||
* @param DefinitionParser|null $definitionParser Definition parser
|
||||
* @param GitPlatformAdapter|null $adapter Platform adapter (auto-created from ApiClient if null)
|
||||
*/
|
||||
public function __construct(
|
||||
@@ -63,14 +60,12 @@ class RepositorySynchronizer
|
||||
AuditLogger $logger,
|
||||
MetricsCollector $metrics,
|
||||
?CheckpointManager $checkpoints = null,
|
||||
?DefinitionParser $definitionParser = null,
|
||||
?GitPlatformAdapter $adapter = null
|
||||
) {
|
||||
$this->adapter = $adapter ?? new MokoGiteaAdapter($apiClient);
|
||||
$this->logger = $logger;
|
||||
$this->metrics = $metrics;
|
||||
$this->checkpoints = $checkpoints ?? new CheckpointManager('.checkpoints');
|
||||
$this->definitionParser = $definitionParser ?? new DefinitionParser();
|
||||
$this->manifestParser = new MokoStandardsParser();
|
||||
}
|
||||
|
||||
@@ -237,10 +232,6 @@ class RepositorySynchronizer
|
||||
if ($prNumber) {
|
||||
$this->logger->logInfo("Successfully created PR #{$prNumber} for {$repo}");
|
||||
|
||||
// Generate / update definitions/sync/{repo}.def.tf AFTER the sync so it
|
||||
// reflects exactly what was pushed in this run.
|
||||
$this->generateRepositoryDefinition($org, $repo, $platform, $repoInfo, $summary);
|
||||
|
||||
return (int) $prNumber;
|
||||
}
|
||||
|
||||
@@ -268,136 +259,6 @@ class RepositorySynchronizer
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate / update the repository tracking definition after a successful sync.
|
||||
*
|
||||
* Writes definitions/sync/{repo}.def.tf with:
|
||||
* - the base platform definition as a foundation
|
||||
* - a sync_record block recording what was actually pushed (files created/updated/skipped)
|
||||
* - full timestamps and platform metadata
|
||||
*
|
||||
* @param string $org
|
||||
* @param string $repo
|
||||
* @param string $platform Detected platform slug (e.g. 'dolibarr')
|
||||
* @param array $repoInfo Raw GitHub API repository object
|
||||
* @param array $summary Sync result from createSyncPR: {copied[], skipped[], total}
|
||||
* @return bool
|
||||
*/
|
||||
private function generateRepositoryDefinition(
|
||||
string $org,
|
||||
string $repo,
|
||||
string $platform,
|
||||
array $repoInfo,
|
||||
array $summary
|
||||
): bool {
|
||||
try {
|
||||
$this->logger->logInfo("Writing sync tracking definition for {$org}/{$repo}");
|
||||
|
||||
$timestamp = date('c');
|
||||
$description = addslashes($repoInfo['description'] ?? '');
|
||||
$defaultBranch = $repoInfo['default_branch'] ?? 'main';
|
||||
|
||||
// Resolve repo root relative to this file's location
|
||||
$repoRoot = dirname(dirname(__DIR__));
|
||||
$baseDefPath = "{$repoRoot}/definitions/default/{$platform}.tf";
|
||||
if (!file_exists($baseDefPath)) {
|
||||
$baseDefPath = "{$repoRoot}/definitions/default/generic.tf";
|
||||
}
|
||||
$baseDefinition = file_get_contents($baseDefPath) ?: '';
|
||||
|
||||
// Extract definition version from the source .tf metadata block
|
||||
$definitionVersion = 'unknown';
|
||||
if (preg_match('/\bversion\s*=\s*"([^"]+)"/', $baseDefinition, $vm)) {
|
||||
$definitionVersion = $vm[1];
|
||||
}
|
||||
|
||||
// Cache the nullable sub-arrays once to avoid repeated null-coalescing
|
||||
$copiedItems = $summary['copied'] ?? [];
|
||||
$skippedItems = $summary['skipped'] ?? [];
|
||||
$totalCount = (int) ($summary['total'] ?? 0);
|
||||
|
||||
// Build the synced_files list
|
||||
$syncedEntries = '';
|
||||
foreach ($copiedItems as $item) {
|
||||
$action = addslashes($item['action'] ?? 'synced');
|
||||
$file = addslashes($item['file'] ?? '');
|
||||
$syncedEntries .= " { path = \"{$file}\" action = \"{$action}\" },\n";
|
||||
}
|
||||
|
||||
$skippedEntries = '';
|
||||
foreach ($skippedItems as $item) {
|
||||
$file = addslashes($item['file'] ?? '');
|
||||
$reason = addslashes($item['reason'] ?? '');
|
||||
$skippedEntries .= " { path = \"{$file}\" reason = \"{$reason}\" },\n";
|
||||
}
|
||||
|
||||
$createdCount = count(array_filter($copiedItems, fn($i) => ($i['action'] ?? '') === 'created'));
|
||||
$updatedCount = count(array_filter($copiedItems, fn($i) => ($i['action'] ?? '') === 'updated'));
|
||||
$skippedCount = count($skippedItems);
|
||||
|
||||
// Assemble the definition file using PHP 7.3+ flexible heredoc:
|
||||
// the closing marker is indented, so PHP strips that many leading spaces automatically.
|
||||
$definition = <<<HCL
|
||||
/**
|
||||
* Repository Sync Tracking Definition: {$org}/{$repo}
|
||||
*
|
||||
* Auto-generated by MokoStandards bulk sync on {$timestamp}
|
||||
* Platform : {$platform}
|
||||
* Description: {$description}
|
||||
*
|
||||
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
|
||||
* To change what gets synced, edit definitions/default/{$platform}.tf
|
||||
* and re-run the bulk-repo-sync workflow.
|
||||
*/
|
||||
|
||||
locals {
|
||||
sync_record = {
|
||||
metadata = {
|
||||
repo = "{$org}/{$repo}"
|
||||
default_branch = "{$defaultBranch}"
|
||||
detected_platform = "{$platform}"
|
||||
description = "{$description}"
|
||||
sync_timestamp = "{$timestamp}"
|
||||
source_repo = "mokoconsulting-tech/MokoStandards"
|
||||
base_definition = "definitions/default/{$platform}.tf"
|
||||
}
|
||||
|
||||
sync_stats = {
|
||||
total_files = {$totalCount}
|
||||
created_files = {$createdCount}
|
||||
updated_files = {$updatedCount}
|
||||
skipped_files = {$skippedCount}
|
||||
}
|
||||
|
||||
synced_files = [
|
||||
{$syncedEntries} ]
|
||||
|
||||
skipped_files = [
|
||||
{$skippedEntries} ]
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Base platform definition (reference copy) ----
|
||||
{$baseDefinition}
|
||||
HCL;
|
||||
|
||||
$defFilePath = "{$repoRoot}/" . self::SYNC_DEFINITION_DIR . "/{$repo}.def.tf";
|
||||
|
||||
if (!is_dir(dirname($defFilePath))) {
|
||||
mkdir(dirname($defFilePath), 0755, true);
|
||||
}
|
||||
|
||||
file_put_contents($defFilePath, $definition);
|
||||
$this->logger->logInfo("Wrote sync tracking definition: {$defFilePath}");
|
||||
$this->metrics->increment('definitions_generated');
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logError("Failed to write tracking definition for {$repo}: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect platform from repository info
|
||||
*/
|
||||
@@ -518,7 +379,7 @@ HCL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PR with sync updates driven by the flat entry list from DefinitionParser.
|
||||
* Create a PR with sync updates driven by the file entry list.
|
||||
*
|
||||
* @param string $org
|
||||
* @param string $repo
|
||||
@@ -1245,7 +1106,7 @@ HCL;
|
||||
|
||||
// CODEOWNERS — GitHub only; Gitea doesn't enforce it
|
||||
if ($this->adapter->getPlatformName() === 'github') {
|
||||
$shared[] = ['templates/github/CODEOWNERS', '.github/CODEOWNERS'];
|
||||
$shared[] = ['templates/mokogitea/CODEOWNERS', '.github/CODEOWNERS'];
|
||||
}
|
||||
|
||||
// Platform-specific gitignore (merged, not replaced)
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# governance.yml — Repository governance configuration
|
||||
# Place this file in each Template-* repo to define branch protections,
|
||||
# labels, secrets, variables, and repository settings for governed repos.
|
||||
#
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# Schema Version: 1.0
|
||||
|
||||
schema_version: "1.0"
|
||||
|
||||
branch_protections:
|
||||
- pattern: main
|
||||
require_pull_request: true
|
||||
required_approvals: 0
|
||||
dismiss_stale_reviews: true
|
||||
block_on_rejected_reviews: true
|
||||
restrict_pushes: true
|
||||
push_whitelist: [jmiller]
|
||||
enable_force_push: true
|
||||
force_push_whitelist: [jmiller]
|
||||
enforce_admins: false
|
||||
- pattern: dev
|
||||
require_pull_request: false
|
||||
restrict_pushes: false
|
||||
enable_force_push: true
|
||||
force_push_whitelist: [jmiller]
|
||||
- pattern: "rc/*"
|
||||
require_pull_request: false
|
||||
restrict_pushes: false
|
||||
enable_force_push: true
|
||||
force_push_whitelist: [jmiller]
|
||||
- pattern: "beta/*"
|
||||
require_pull_request: false
|
||||
restrict_pushes: false
|
||||
enable_force_push: true
|
||||
force_push_whitelist: [jmiller]
|
||||
- pattern: "alpha/*"
|
||||
require_pull_request: false
|
||||
restrict_pushes: false
|
||||
enable_force_push: true
|
||||
force_push_whitelist: [jmiller]
|
||||
|
||||
labels:
|
||||
- name: bug
|
||||
color: d73a4a
|
||||
description: "Something isn't working"
|
||||
- name: enhancement
|
||||
color: a2eeef
|
||||
description: "New feature or request"
|
||||
- name: documentation
|
||||
color: "0075ca"
|
||||
description: "Improvements or additions to documentation"
|
||||
- name: security
|
||||
color: ee0701
|
||||
description: "Security vulnerability or concern"
|
||||
|
||||
secrets:
|
||||
- name: GA_TOKEN
|
||||
description: "Gitea API token for automation"
|
||||
required: true
|
||||
scope: org
|
||||
- name: GH_TOKEN
|
||||
description: "GitHub PAT for mirror operations"
|
||||
required: false
|
||||
scope: org
|
||||
|
||||
variables:
|
||||
- name: GITEA_URL
|
||||
description: "Gitea instance URL"
|
||||
default: "https://git.mokoconsulting.tech"
|
||||
scope: org
|
||||
- name: GITEA_ORG
|
||||
description: "Gitea organization name"
|
||||
default: "MokoConsulting"
|
||||
scope: org
|
||||
|
||||
repo_settings:
|
||||
has_issues: true
|
||||
has_projects: true
|
||||
has_wiki: false
|
||||
has_discussions: false
|
||||
allow_merge_commit: true
|
||||
allow_squash_merge: true
|
||||
allow_rebase_merge: false
|
||||
delete_branch_on_merge: true
|
||||
allow_auto_merge: false
|
||||
+1
-1
@@ -9,7 +9,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
DEFGROUP: MokoStandards.Templates.GitHub
|
||||
INGROUP: MokoStandards.Templates
|
||||
REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards
|
||||
PATH: /templates/github/CLAUDE.dolibarr.md.template
|
||||
PATH: /templates/mokogitea/CLAUDE.dolibarr.md.template
|
||||
VERSION: XX.YY.ZZ
|
||||
BRIEF: Claude AI assistant context template for Dolibarr/MokoCRM module repositories
|
||||
NOTE: Synced to .gitea/CLAUDE.md in all Dolibarr/CRM repos via bulk sync.
|
||||
+1
-1
@@ -9,7 +9,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
DEFGROUP: MokoStandards.Templates.GitHub
|
||||
INGROUP: MokoStandards.Templates
|
||||
REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards
|
||||
PATH: /templates/github/CLAUDE.joomla.md.template
|
||||
PATH: /templates/mokogitea/CLAUDE.joomla.md.template
|
||||
VERSION: XX.YY.ZZ
|
||||
BRIEF: Claude AI assistant context template for Joomla/MokoWaaS governed repositories
|
||||
NOTE: Synced to .gitea/CLAUDE.md in all Joomla/WaaS repos via bulk sync.
|
||||
@@ -9,7 +9,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
DEFGROUP: MokoStandards.Templates.GitHub
|
||||
INGROUP: MokoStandards.Templates
|
||||
REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards
|
||||
PATH: /templates/github/CLAUDE.md.template
|
||||
PATH: /templates/mokogitea/CLAUDE.md.template
|
||||
VERSION: XX.YY.ZZ
|
||||
BRIEF: Standard CLAUDE.md template for Moko Consulting governed repositories
|
||||
NOTE: Synced to .gitea/CLAUDE.md in all governed repositories via bulk sync.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user