chore: Sync MokoStandards 04.02.12 #103
20
.github/.mokostandards
vendored
Normal file
20
.github/.mokostandards
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
# FILE INFORMATION
|
||||||
|
# DEFGROUP: MokoStandards.Templates.Config
|
||||||
|
# INGROUP: MokoStandards.Templates
|
||||||
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
|
# PATH: /templates/configs/moko-standards.yml
|
||||||
|
# VERSION: 04.02.08
|
||||||
|
# BRIEF: Governance attachment template — synced to .mokostandards in every governed repository
|
||||||
|
# NOTE: Tokens replaced at sync time: mokoconsulting-tech, MokoCassiopeia, waas-component, 04.02.12
|
||||||
|
#
|
||||||
|
# This file is managed automatically by MokoStandards bulk sync.
|
||||||
|
# Do not edit manually — changes will be overwritten on the next sync.
|
||||||
|
# To update governance settings, open a PR in MokoStandards instead:
|
||||||
|
# https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
|
|
||||||
|
standards_source: "https://github.com/mokoconsulting-tech/MokoStandards"
|
||||||
|
standards_version: "04.02.12"
|
||||||
|
platform: "waas-component"
|
||||||
|
governed_repo: "mokoconsulting-tech/MokoCassiopeia"
|
||||||
89
.github/workflows/auto-dev-issue.yml
vendored
Normal file
89
.github/workflows/auto-dev-issue.yml
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# 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: GitHub.Workflow
|
||||||
|
# INGROUP: MokoStandards.Automation
|
||||||
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
|
# PATH: /templates/workflows/shared/auto-dev-issue.yml
|
||||||
|
# VERSION: 04.02.08
|
||||||
|
# BRIEF: Auto-create tracking issue when a dev/** branch is pushed
|
||||||
|
# NOTE: Synced via bulk-repo-sync to .github/workflows/auto-dev-issue.yml in all governed repos.
|
||||||
|
|
||||||
|
name: Auto Dev Branch Issue
|
||||||
|
|
||||||
|
on:
|
||||||
|
create:
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-issue:
|
||||||
|
name: Create version tracking issue
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: >-
|
||||||
|
github.event.ref_type == 'branch' &&
|
||||||
|
startsWith(github.event.ref, 'dev/')
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Create tracking issue
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
|
run: |
|
||||||
|
BRANCH="${{ github.event.ref }}"
|
||||||
|
VERSION="${BRANCH#dev/}"
|
||||||
|
REPO="${{ github.repository }}"
|
||||||
|
ACTOR="${{ github.actor }}"
|
||||||
|
NOW=$(date -u '+%Y-%m-%d %H:%M UTC')
|
||||||
|
|
||||||
|
TITLE="feat(${VERSION}): Development tracking for ${BRANCH}"
|
||||||
|
|
||||||
|
BODY="## Development Branch Created
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| **Branch** | \`${BRANCH}\` |
|
||||||
|
| **Version** | \`${VERSION}\` |
|
||||||
|
| **Created by** | @${ACTOR} |
|
||||||
|
| **Created at** | ${NOW} |
|
||||||
|
| **Repository** | \`${REPO}\` |
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Feature development complete
|
||||||
|
- [ ] Tests passing
|
||||||
|
- [ ] README.md version bumped to \`${VERSION}\`
|
||||||
|
- [ ] CHANGELOG.md updated
|
||||||
|
- [ ] PR created targeting \`main\`
|
||||||
|
- [ ] Code reviewed and approved
|
||||||
|
- [ ] Merged to \`main\`
|
||||||
|
|
||||||
|
---
|
||||||
|
*Auto-created by [auto-dev-issue.yml](.github/workflows/auto-dev-issue.yml) on branch creation.*"
|
||||||
|
|
||||||
|
# Dedent heredoc
|
||||||
|
BODY=$(echo "$BODY" | sed 's/^ //')
|
||||||
|
|
||||||
|
# Check for existing issue with same title prefix
|
||||||
|
EXISTING=$(gh api "repos/${REPO}/issues?state=open&per_page=5" \
|
||||||
|
--jq ".[] | select(.title | startswith(\"feat(${VERSION})\")) | .number" 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [ -n "$EXISTING" ]; then
|
||||||
|
echo "ℹ️ Issue #${EXISTING} already exists for ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
ISSUE_URL=$(gh issue create \
|
||||||
|
--repo "$REPO" \
|
||||||
|
--title "$TITLE" \
|
||||||
|
--body "$BODY" \
|
||||||
|
--label "type: feature,version" \
|
||||||
|
--assignee "jmiller-moko" 2>&1)
|
||||||
|
echo "✅ Created tracking issue: ${ISSUE_URL}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
295
.github/workflows/auto-release.yml
vendored
295
.github/workflows/auto-release.yml
vendored
@@ -1,7 +1,5 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
# 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
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
@@ -9,12 +7,26 @@
|
|||||||
# INGROUP: MokoStandards.Release
|
# INGROUP: MokoStandards.Release
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/auto-release.yml
|
# PATH: /templates/workflows/shared/auto-release.yml
|
||||||
# VERSION: 04.01.00
|
# VERSION: 04.02.12
|
||||||
# BRIEF: Auto-create a GitHub Release on every push to main with version from README.md
|
# BRIEF: Unified build & release pipeline — version branch, platform version, badges, tag, release
|
||||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/auto-release.yml in all governed repos.
|
#
|
||||||
# For Dolibarr (crm-module) repos, also updates $this->version in the module descriptor.
|
# ╔════════════════════════════════════════════════════════════════════════╗
|
||||||
|
# ║ BUILD & RELEASE PIPELINE ║
|
||||||
|
# ╠════════════════════════════════════════════════════════════════════════╣
|
||||||
|
# ║ ║
|
||||||
|
# ║ Triggers on push to main (skips bot commits + [skip ci]): ║
|
||||||
|
# ║ ║
|
||||||
|
# ║ 1. Read version from README.md ║
|
||||||
|
# ║ 2. Create version/XX.YY.ZZ branch (snapshot) ║
|
||||||
|
# ║ 3. Set platform version (Dolibarr $this->version, Joomla <version>) ║
|
||||||
|
# ║ 4. Update [VERSION: XX.YY.ZZ] badges in markdown files ║
|
||||||
|
# ║ 5. Write update.txt for Dolibarr module update checks ║
|
||||||
|
# ║ 6. Create git tag vXX.YY.ZZ ║
|
||||||
|
# ║ 7. Create GitHub Release with changelog notes ║
|
||||||
|
# ║ ║
|
||||||
|
# ╚════════════════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
name: Auto Release
|
name: Build & Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -22,14 +34,16 @@ on:
|
|||||||
- main
|
- main
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Create Release
|
name: Build & Release Pipeline
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip bot commits (version sync, [skip ci]) to avoid infinite loops
|
|
||||||
if: >-
|
if: >-
|
||||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
!contains(github.event.head_commit.message, '[skip ci]') &&
|
||||||
github.actor != 'github-actions[bot]'
|
github.actor != 'github-actions[bot]'
|
||||||
@@ -41,123 +55,250 @@ jobs:
|
|||||||
token: ${{ secrets.GH_TOKEN || github.token }}
|
token: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Extract version from README.md
|
- name: Setup MokoStandards tools
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 --branch version/04.02.12 --quiet \
|
||||||
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
|
/tmp/mokostandards
|
||||||
|
cd /tmp/mokostandards
|
||||||
|
composer install --no-dev --no-interaction --quiet
|
||||||
|
|
||||||
|
# ── STEP 1: Read version ───────────────────────────────────────────
|
||||||
|
- name: "Step 1: Read version from README.md"
|
||||||
id: version
|
id: version
|
||||||
run: |
|
run: |
|
||||||
VERSION=$(grep -oP '^\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' README.md | head -1)
|
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null)
|
||||||
if [ -z "$VERSION" ]; then
|
if [ -z "$VERSION" ]; then
|
||||||
echo "⚠️ No VERSION found in README.md — skipping release"
|
echo "⏭️ No VERSION in README.md — skipping release"
|
||||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||||
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
|
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "branch=version/${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||||
echo "✅ Version: $VERSION (tag: v${VERSION})"
|
echo "✅ Version: $VERSION"
|
||||||
|
|
||||||
- name: Check if tag already exists
|
- name: Check if already released
|
||||||
if: steps.version.outputs.skip != 'true'
|
if: steps.version.outputs.skip != 'true'
|
||||||
id: tag_check
|
id: check
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.version.outputs.tag }}"
|
TAG="${{ steps.version.outputs.tag }}"
|
||||||
if git rev-parse "$TAG" >/dev/null 2>&1; then
|
BRANCH="${{ steps.version.outputs.branch }}"
|
||||||
echo "ℹ️ Tag $TAG already exists — skipping release"
|
|
||||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
TAG_EXISTS=false
|
||||||
|
BRANCH_EXISTS=false
|
||||||
|
|
||||||
|
git rev-parse "$TAG" >/dev/null 2>&1 && TAG_EXISTS=true
|
||||||
|
git ls-remote --heads origin "$BRANCH" 2>/dev/null | grep -q "$BRANCH" && BRANCH_EXISTS=true
|
||||||
|
|
||||||
|
echo "tag_exists=$TAG_EXISTS" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "branch_exists=$BRANCH_EXISTS" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
if [ "$TAG_EXISTS" = "true" ] && [ "$BRANCH_EXISTS" = "true" ]; then
|
||||||
|
echo "already_released=true" >> "$GITHUB_OUTPUT"
|
||||||
else
|
else
|
||||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
echo "already_released=false" >> "$GITHUB_OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Update Dolibarr module version
|
# ── SANITY CHECKS ────────────────────────────────────────────────────
|
||||||
|
- name: "Sanity: Platform-specific validation"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.tag_check.outputs.exists != 'true'
|
steps.check.outputs.already_released != 'true'
|
||||||
run: |
|
run: |
|
||||||
PLATFORM=""
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
if [ -f ".moko-standards" ]; then
|
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null)
|
||||||
PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"')
|
ERRORS=0
|
||||||
|
|
||||||
|
echo "## 🔍 Pre-Release Sanity Checks" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Platform: \`${PLATFORM}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Common checks
|
||||||
|
if [ ! -f "LICENSE" ]; then
|
||||||
|
echo "❌ Missing LICENSE file" >> $GITHUB_STEP_SUMMARY
|
||||||
|
ERRORS=$((ERRORS+1))
|
||||||
|
else
|
||||||
|
echo "✅ LICENSE" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
if [ ! -d "src" ]; then
|
||||||
|
echo "⚠️ No src/ directory" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "✅ src/ directory" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dolibarr-specific checks
|
||||||
if [ "$PLATFORM" = "crm-module" ]; then
|
if [ "$PLATFORM" = "crm-module" ]; then
|
||||||
echo "📦 Dolibarr release — setting module version to '${VERSION}'"
|
MOD_FILE=$(find src -path "*/core/modules/mod*.class.php" -print -quit 2>/dev/null)
|
||||||
# Update $this->version in the module descriptor (core/modules/mod*.class.php)
|
if [ -z "$MOD_FILE" ]; then
|
||||||
find . -path "*/core/modules/mod*.class.php" -exec \
|
echo "❌ No module descriptor (src/core/modules/mod*.class.php)" >> $GITHUB_STEP_SUMMARY
|
||||||
sed -i "s/\(\$this->version\s*=\s*\)['\"][^'\"]*['\"]/\1'${VERSION}'/" {} + 2>/dev/null || true
|
ERRORS=$((ERRORS+1))
|
||||||
|
else
|
||||||
|
echo "✅ Module descriptor: \`${MOD_FILE}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Check module number
|
||||||
|
NUMERO=$(grep -oP '\$this->numero\s*=\s*\K\d+' "$MOD_FILE" 2>/dev/null || echo "0")
|
||||||
|
if [ "$NUMERO" = "0" ] || [ -z "$NUMERO" ]; then
|
||||||
|
echo "❌ Module number (\$this->numero) is 0 or not set" >> $GITHUB_STEP_SUMMARY
|
||||||
|
ERRORS=$((ERRORS+1))
|
||||||
|
else
|
||||||
|
echo "✅ Module number: ${NUMERO}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check url_last_version exists
|
||||||
|
if grep -q 'url_last_version' "$MOD_FILE" 2>/dev/null; then
|
||||||
|
echo "✅ url_last_version is set" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "⚠️ url_last_version not set — update checks won't work" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Joomla-specific checks
|
||||||
if [ "$PLATFORM" = "waas-component" ]; then
|
if [ "$PLATFORM" = "waas-component" ]; then
|
||||||
echo "📦 Joomla release — setting manifest version to '${VERSION}'"
|
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||||
# Update <version> tag in Joomla XML manifest files
|
if [ -z "$MANIFEST" ]; then
|
||||||
find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | while read -r manifest; do
|
echo "❌ No Joomla XML manifest found" >> $GITHUB_STEP_SUMMARY
|
||||||
sed -i "s|<version>[^<]*</version>|<version>${VERSION}</version>|" "$manifest" 2>/dev/null || true
|
ERRORS=$((ERRORS+1))
|
||||||
done
|
else
|
||||||
|
echo "✅ Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Check extension type
|
||||||
|
TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null)
|
||||||
|
echo "✅ Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Commit the version update if anything changed
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
if ! git diff --quiet; then
|
if [ "$ERRORS" -gt 0 ]; then
|
||||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
echo "**❌ ${ERRORS} error(s) — release may be incomplete**" >> $GITHUB_STEP_SUMMARY
|
||||||
git config --local user.name "github-actions[bot]"
|
else
|
||||||
git add -A
|
echo "**✅ All sanity checks passed**" >> $GITHUB_STEP_SUMMARY
|
||||||
git commit -m "chore(release): set version to ${VERSION} [skip ci]" \
|
|
||||||
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
|
||||||
git push
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Extract changelog entry
|
# ── STEP 2: Create version branch ──────────────────────────────────
|
||||||
|
- name: "Step 2: Create version branch"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.tag_check.outputs.exists != 'true'
|
steps.check.outputs.branch_exists != 'true'
|
||||||
id: changelog
|
run: |
|
||||||
|
BRANCH="${{ steps.version.outputs.branch }}"
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
git push origin "$BRANCH"
|
||||||
|
echo "🌿 Created branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# ── STEP 3: Set platform version ───────────────────────────────────
|
||||||
|
- name: "Step 3: Set platform version"
|
||||||
|
if: >-
|
||||||
|
steps.version.outputs.skip != 'true' &&
|
||||||
|
steps.check.outputs.already_released != 'true'
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
php /tmp/mokostandards/api/cli/version_set_platform.php \
|
||||||
|
--path . --version "$VERSION" --branch main
|
||||||
|
|
||||||
# Try to extract the section for this version from CHANGELOG.md
|
# ── STEP 4: Update version badges ──────────────────────────────────
|
||||||
NOTES=""
|
- name: "Step 4: Update version badges"
|
||||||
if [ -f "CHANGELOG.md" ]; then
|
|
||||||
# Extract text between this version's heading and the next heading
|
|
||||||
NOTES=$(awk "/^##.*${VERSION}/,/^## /" CHANGELOG.md | head -50 | sed '1d;$d')
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$NOTES" ]; then
|
|
||||||
NOTES="Release ${VERSION}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Write to file to avoid shell escaping issues
|
|
||||||
echo "$NOTES" > /tmp/release_notes.md
|
|
||||||
echo "✅ Release notes prepared"
|
|
||||||
|
|
||||||
- name: Create tag and release
|
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.tag_check.outputs.exists != 'true'
|
steps.check.outputs.already_released != 'true'
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
find . -name "*.md" ! -path "./.git/*" ! -path "./vendor/*" | while read -r f; do
|
||||||
|
if grep -q '\[VERSION:' "$f" 2>/dev/null; then
|
||||||
|
sed -i "s/\[VERSION:[[:space:]]*[0-9]\{2\}\.[0-9]\{2\}\.[0-9]\{2\}\]/[VERSION: ${VERSION}]/" "$f"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── STEP 5: Write update.txt (Dolibarr) ────────────────────────────
|
||||||
|
- name: "Step 5: Write update.txt"
|
||||||
|
if: >-
|
||||||
|
steps.version.outputs.skip != 'true' &&
|
||||||
|
steps.check.outputs.already_released != 'true'
|
||||||
|
run: |
|
||||||
|
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null)
|
||||||
|
if [ "$PLATFORM" = "crm-module" ]; then
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
printf '%s' "$VERSION" > update.txt
|
||||||
|
echo "📦 update.txt: ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Commit all changes ─────────────────────────────────────────────
|
||||||
|
- name: Commit release changes
|
||||||
|
if: >-
|
||||||
|
steps.version.outputs.skip != 'true' &&
|
||||||
|
steps.check.outputs.already_released != 'true'
|
||||||
|
run: |
|
||||||
|
if git diff --quiet && git diff --cached --quiet; then
|
||||||
|
echo "ℹ️ No changes to commit"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --local user.name "github-actions[bot]"
|
||||||
|
git add -A
|
||||||
|
git commit -m "chore(release): build ${VERSION} [skip ci]" \
|
||||||
|
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||||
|
git push
|
||||||
|
|
||||||
|
# ── STEP 6: Create tag ─────────────────────────────────────────────
|
||||||
|
- name: "Step 6: Create git tag"
|
||||||
|
if: >-
|
||||||
|
steps.version.outputs.skip != 'true' &&
|
||||||
|
steps.check.outputs.tag_exists != 'true'
|
||||||
|
run: |
|
||||||
|
TAG="${{ steps.version.outputs.tag }}"
|
||||||
|
git tag "$TAG"
|
||||||
|
git push origin "$TAG"
|
||||||
|
echo "🏷️ Tag: ${TAG}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# ── STEP 7: Create GitHub Release ──────────────────────────────────
|
||||||
|
- name: "Step 7: Create GitHub Release"
|
||||||
|
if: >-
|
||||||
|
steps.version.outputs.skip != 'true' &&
|
||||||
|
steps.check.outputs.tag_exists != 'true'
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.version.outputs.tag }}"
|
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
TAG="${{ steps.version.outputs.tag }}"
|
||||||
|
BRANCH="${{ steps.version.outputs.branch }}"
|
||||||
|
|
||||||
# Create the tag
|
NOTES=$(php /tmp/mokostandards/api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null)
|
||||||
git tag "$TAG"
|
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
||||||
git push origin "$TAG"
|
echo "$NOTES" > /tmp/release_notes.md
|
||||||
|
|
||||||
# Create the release
|
|
||||||
gh release create "$TAG" \
|
gh release create "$TAG" \
|
||||||
--title "${VERSION}" \
|
--title "${VERSION}" \
|
||||||
--notes-file /tmp/release_notes.md \
|
--notes-file /tmp/release_notes.md \
|
||||||
--target main
|
--target "$BRANCH"
|
||||||
|
|
||||||
echo "🚀 Release ${VERSION} created: $TAG"
|
echo "🚀 Release ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
- name: Summary
|
# ── Summary ────────────────────────────────────────────────────────
|
||||||
if: steps.version.outputs.skip != 'true'
|
- name: Pipeline Summary
|
||||||
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
TAG="${{ steps.version.outputs.tag }}"
|
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
|
||||||
if [ "${{ steps.tag_check.outputs.exists }}" = "true" ]; then
|
echo "## ⏭️ Release Skipped" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "## ℹ️ Release — ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "Tag \`${TAG}\` already exists — no new release created." >> $GITHUB_STEP_SUMMARY
|
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
|
||||||
|
echo "## ℹ️ Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
else
|
||||||
echo "## 🚀 Release — ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "Created tag \`${TAG}\` and GitHub Release." >> $GITHUB_STEP_SUMMARY
|
echo "## ✅ Build & Release Complete" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Release | [View](https://github.com/${{ github.repository }}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|||||||
88
.github/workflows/codeql-analysis.yml
vendored
88
.github/workflows/codeql-analysis.yml
vendored
@@ -5,15 +5,17 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: GitHub.Workflow
|
# DEFGROUP: GitHub.Workflow.Template
|
||||||
# INGROUP: MokoStandards.Security
|
# INGROUP: MokoStandards.Security
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /.github/workflows/codeql-analysis.yml
|
# PATH: /templates/workflows/generic/codeql-analysis.yml
|
||||||
# VERSION: 04.01.00
|
# VERSION: 04.02.08
|
||||||
# BRIEF: CodeQL security scanning workflow for PHP codebase
|
# BRIEF: CodeQL security scanning workflow (generic — all repo types)
|
||||||
# NOTE: Repository is PHP-only (v04.00.04). Python was removed Feb 12, 2026.
|
# NOTE: Deployed to .github/workflows/codeql-analysis.yml in governed repos.
|
||||||
|
# CodeQL does not support PHP directly; JavaScript scans JSON/YAML/shell.
|
||||||
|
# For PHP-specific security scanning see standards-compliance.yml.
|
||||||
|
|
||||||
name: "CodeQL Security Scanning"
|
name: CodeQL Security Scanning
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -28,7 +30,7 @@ on:
|
|||||||
- dev/**
|
- dev/**
|
||||||
- rc/**
|
- rc/**
|
||||||
schedule:
|
schedule:
|
||||||
# Run weekly on Monday at 6:00 AM UTC
|
# Weekly on Monday at 06:00 UTC
|
||||||
- cron: '0 6 * * 1'
|
- cron: '0 6 * * 1'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -40,65 +42,60 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Configuration Security Scan
|
name: Analyze (${{ matrix.language }})
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 360
|
timeout-minutes: 360
|
||||||
|
|
||||||
# No language matrix - PHP-only repository
|
strategy:
|
||||||
# CodeQL scans workflow files, configs, and scripts for security issues
|
fail-fast: false
|
||||||
# PHP security handled by SecurityValidator enterprise library
|
matrix:
|
||||||
|
# CodeQL does not support PHP. Use 'javascript' to scan JSON, YAML,
|
||||||
|
# and shell scripts. Add 'actions' to scan GitHub Actions workflows.
|
||||||
|
language: ['javascript', 'actions']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
# No languages specified - scan configurations only
|
languages: ${{ matrix.language }}
|
||||||
# Reference explicit config to scan YAML, JSON, shell scripts
|
|
||||||
config-file: ./.github/codeql/codeql-config.yml
|
|
||||||
# Use security-extended query suite for comprehensive coverage
|
|
||||||
queries: security-extended,security-and-quality
|
queries: security-extended,security-and-quality
|
||||||
|
|
||||||
# Skip autobuild - no code compilation needed for config scanning
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4
|
uses: github/codeql-action/analyze@v3
|
||||||
with:
|
with:
|
||||||
category: "/language:config"
|
category: "/language:${{ matrix.language }}"
|
||||||
upload: true
|
upload: true
|
||||||
output: sarif-results
|
output: sarif-results
|
||||||
wait-for-processing: true
|
wait-for-processing: true
|
||||||
|
|
||||||
- name: Upload SARIF results (optional)
|
- name: Upload SARIF results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v4.5.0
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.5.0
|
||||||
with:
|
with:
|
||||||
name: codeql-results-config
|
name: codeql-results-${{ matrix.language }}
|
||||||
path: sarif-results
|
path: sarif-results
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- name: Check for Critical/High Findings
|
- name: Step summary
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
echo "### 🔍 CodeQL Security Analysis Complete" >> $GITHUB_STEP_SUMMARY
|
echo "### 🔍 CodeQL — ${{ matrix.language }}" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "**Scan Type**: Configuration Security" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "**Query Suite**: security-extended, security-and-quality" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "**Note**: MokoStandards is PHP-only (v04.00.04)." >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "This scan analyzes workflow files, JSON configs, YAML, and shell scripts." >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "For PHP-specific security: Use PHP SecurityValidator enterprise library." >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
URL="https://github.com/${{ github.repository }}/security/code-scanning"
|
URL="https://github.com/${{ github.repository }}/security/code-scanning"
|
||||||
echo "Check the [Security tab]($URL) for detailed findings." >> $GITHUB_STEP_SUMMARY
|
echo "See the [Security tab]($URL) for findings." >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "**Response Requirements**:" >> $GITHUB_STEP_SUMMARY
|
echo "| Severity | SLA |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- Critical: Fix within 7 days" >> $GITHUB_STEP_SUMMARY
|
echo "|----------|-----|" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- High: Fix within 14 days" >> $GITHUB_STEP_SUMMARY
|
echo "| Critical | 7 days |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- Medium: Fix within 30 days" >> $GITHUB_STEP_SUMMARY
|
echo "| High | 14 days |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- Low: Fix within 60 days or next release" >> $GITHUB_STEP_SUMMARY
|
echo "| Medium | 30 days |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Low | 60 days / next release |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
summary:
|
summary:
|
||||||
name: Security Scan Summary
|
name: Security Scan Summary
|
||||||
@@ -107,17 +104,12 @@ jobs:
|
|||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Generate Summary
|
- name: Summary
|
||||||
run: |
|
run: |
|
||||||
echo "### 🛡️ Security Scanning Complete" >> $GITHUB_STEP_SUMMARY
|
echo "### 🛡️ CodeQL Complete" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "All CodeQL security scans have completed." >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "**Trigger**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "**Branch**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
||||||
SECURITY_URL="https://github.com/${{ github.repository }}/security"
|
SECURITY_URL="https://github.com/${{ github.repository }}/security"
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "📊 [View all security alerts]($SECURITY_URL)" >> $GITHUB_STEP_SUMMARY
|
echo "📊 [View all security alerts]($SECURITY_URL)" >> $GITHUB_STEP_SUMMARY
|
||||||
POLICY_URL="https://github.com/${{ github.repository }}"
|
|
||||||
POLICY_URL="${POLICY_URL}/blob/main/docs/policy/security-scanning.md"
|
|
||||||
echo "📋 [Security scanning policy]($POLICY_URL)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|||||||
31
.github/workflows/deploy-demo.yml
vendored
31
.github/workflows/deploy-demo.yml
vendored
@@ -22,7 +22,7 @@
|
|||||||
# INGROUP: MokoStandards.Deploy
|
# INGROUP: MokoStandards.Deploy
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/deploy-demo.yml
|
# PATH: /templates/workflows/shared/deploy-demo.yml
|
||||||
# VERSION: 04.01.00
|
# VERSION: 04.02.08
|
||||||
# BRIEF: SFTP deployment workflow for demo server — synced to all governed repos
|
# BRIEF: SFTP deployment workflow for demo server — synced to all governed repos
|
||||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-demo.yml in all governed repos.
|
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-demo.yml in all governed repos.
|
||||||
# Port is resolved in order: DEMO_FTP_PORT variable → :port suffix in DEMO_FTP_HOST → 22.
|
# Port is resolved in order: DEMO_FTP_PORT variable → :port suffix in DEMO_FTP_HOST → 22.
|
||||||
@@ -70,6 +70,9 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-permission:
|
check-permission:
|
||||||
name: Verify Deployment Permission
|
name: Verify Deployment Permission
|
||||||
@@ -241,7 +244,6 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
$SKIP && continue
|
$SKIP && continue
|
||||||
if [ -f ".gitignore" ]; then
|
|
||||||
if [ -f ".gitignore" ]; then
|
if [ -f ".gitignore" ]; then
|
||||||
git check-ignore -q "$rel" 2>/dev/null && {
|
git check-ignore -q "$rel" 2>/dev/null && {
|
||||||
IGNORED_FILES+=("$rel | .gitignore")
|
IGNORED_FILES+=("$rel | .gitignore")
|
||||||
@@ -345,8 +347,8 @@ jobs:
|
|||||||
|
|
||||||
# ── Platform-specific path safety guards ──────────────────────────────
|
# ── Platform-specific path safety guards ──────────────────────────────
|
||||||
PLATFORM=""
|
PLATFORM=""
|
||||||
if [ -f ".moko-standards" ]; then
|
MOKO_FILE=".github/.mokostandards"; [ ! -f "$MOKO_FILE" ] && MOKO_FILE=".mokostandards"; if [ -f "$MOKO_FILE" ]; then
|
||||||
PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"')
|
PLATFORM=$(grep -E '^platform:' "$MOKO_FILE" | sed 's/.*:[[:space:]]*//' | tr -d '"')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$PLATFORM" = "crm-module" ]; then
|
if [ "$PLATFORM" = "crm-module" ]; then
|
||||||
@@ -407,7 +409,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true'
|
if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true'
|
||||||
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0
|
uses: shivammathur/setup-php@fcafdd6392932010c2bd5094439b8e33be2a8a09 # v2.37.0
|
||||||
with:
|
with:
|
||||||
php-version: '8.1'
|
php-version: '8.1'
|
||||||
tools: composer
|
tools: composer
|
||||||
@@ -418,14 +420,17 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 --quiet \
|
git clone --depth 1 --branch version/04.02.12 --quiet \
|
||||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
/tmp/mokostandards
|
/tmp/mokostandards
|
||||||
cd /tmp/mokostandards
|
cd /tmp/mokostandards
|
||||||
composer install --no-dev --no-interaction --quiet
|
composer install --no-dev --no-interaction --quiet
|
||||||
|
|
||||||
- name: Clear remote destination folder
|
- name: Clear remote destination folder (manual only)
|
||||||
if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true'
|
if: >-
|
||||||
|
steps.source.outputs.skip == 'false' &&
|
||||||
|
steps.remote.outputs.skip != 'true' &&
|
||||||
|
inputs.clear_remote == true
|
||||||
env:
|
env:
|
||||||
SFTP_HOST: ${{ steps.conn.outputs.host }}
|
SFTP_HOST: ${{ steps.conn.outputs.host }}
|
||||||
SFTP_PORT: ${{ steps.conn.outputs.port }}
|
SFTP_PORT: ${{ steps.conn.outputs.port }}
|
||||||
@@ -578,7 +583,7 @@ jobs:
|
|||||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||||
|
|
||||||
- name: Create or update failure issue
|
- name: Create or update failure issue
|
||||||
if: failure()
|
if: failure() && steps.remote.outputs.skip != 'true'
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
@@ -620,22 +625,22 @@ jobs:
|
|||||||
--force 2>/dev/null || true
|
--force 2>/dev/null || true
|
||||||
|
|
||||||
# Look for an existing open deploy-failure issue
|
# Look for an existing open deploy-failure issue
|
||||||
EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=open&per_page=1" \
|
EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=all&per_page=1&sort=created&direction=desc" \
|
||||||
--jq '.[0].number' 2>/dev/null)
|
--jq '.[0].number' 2>/dev/null)
|
||||||
|
|
||||||
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
|
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
|
||||||
gh api "repos/${REPO}/issues/${EXISTING}" \
|
gh api "repos/${REPO}/issues/${EXISTING}" \
|
||||||
-X PATCH \
|
-X PATCH \
|
||||||
-f title="$TITLE" \
|
-f title="$TITLE" \
|
||||||
-f body="$BODY" \
|
-f body="$BODY" \n -f state="open" \
|
||||||
--silent
|
--silent
|
||||||
echo "📋 Failure issue #${EXISTING} updated: ${REPO}" >> "$GITHUB_STEP_SUMMARY"
|
echo "📋 Failure issue #${EXISTING} updated/reopened: ${REPO}" >> "$GITHUB_STEP_SUMMARY"
|
||||||
else
|
else
|
||||||
gh issue create \
|
gh issue create \
|
||||||
--repo "$REPO" \
|
--repo "$REPO" \
|
||||||
--title "$TITLE" \
|
--title "$TITLE" \
|
||||||
--body "$BODY" \
|
--body "$BODY" \
|
||||||
--label "$LABEL" \
|
--label "$LABEL" \n --assignee "jmiller-moko" \
|
||||||
| tee -a "$GITHUB_STEP_SUMMARY"
|
| tee -a "$GITHUB_STEP_SUMMARY"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
48
.github/workflows/deploy-dev.yml
vendored
48
.github/workflows/deploy-dev.yml
vendored
@@ -22,7 +22,7 @@
|
|||||||
# INGROUP: MokoStandards.Deploy
|
# INGROUP: MokoStandards.Deploy
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/deploy-dev.yml
|
# PATH: /templates/workflows/shared/deploy-dev.yml
|
||||||
# VERSION: 04.01.00
|
# VERSION: 04.02.08
|
||||||
# BRIEF: SFTP deployment workflow for development server — synced to all governed repos
|
# BRIEF: SFTP deployment workflow for development server — synced to all governed repos
|
||||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-dev.yml in all governed repos.
|
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-dev.yml in all governed repos.
|
||||||
# Port is resolved in order: DEV_FTP_PORT variable → :port suffix in DEV_FTP_HOST → 22.
|
# Port is resolved in order: DEV_FTP_PORT variable → :port suffix in DEV_FTP_HOST → 22.
|
||||||
@@ -73,6 +73,9 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-permission:
|
check-permission:
|
||||||
name: Verify Deployment Permission
|
name: Verify Deployment Permission
|
||||||
@@ -347,8 +350,8 @@ jobs:
|
|||||||
|
|
||||||
# ── Platform-specific path safety guards ──────────────────────────────
|
# ── Platform-specific path safety guards ──────────────────────────────
|
||||||
PLATFORM=""
|
PLATFORM=""
|
||||||
if [ -f ".moko-standards" ]; then
|
MOKO_FILE=".github/.mokostandards"; [ ! -f "$MOKO_FILE" ] && MOKO_FILE=".mokostandards"; if [ -f "$MOKO_FILE" ]; then
|
||||||
PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"')
|
PLATFORM=$(grep -oP '^platform:.*' "$MOKO_FILE" 2>/dev/null || true)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$PLATFORM" = "crm-module" ]; then
|
if [ "$PLATFORM" = "crm-module" ]; then
|
||||||
@@ -409,7 +412,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true'
|
if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true'
|
||||||
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0
|
uses: shivammathur/setup-php@fcafdd6392932010c2bd5094439b8e33be2a8a09 # v2.37.0
|
||||||
with:
|
with:
|
||||||
php-version: '8.1'
|
php-version: '8.1'
|
||||||
tools: composer
|
tools: composer
|
||||||
@@ -420,14 +423,17 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 --quiet \
|
git clone --depth 1 --branch version/04.02.12 --quiet \
|
||||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
/tmp/mokostandards
|
/tmp/mokostandards
|
||||||
cd /tmp/mokostandards
|
cd /tmp/mokostandards
|
||||||
composer install --no-dev --no-interaction --quiet
|
composer install --no-dev --no-interaction --quiet
|
||||||
|
|
||||||
- name: Clear remote destination folder
|
- name: Clear remote destination folder (manual only)
|
||||||
if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true'
|
if: >-
|
||||||
|
steps.source.outputs.skip == 'false' &&
|
||||||
|
steps.remote.outputs.skip != 'true' &&
|
||||||
|
inputs.clear_remote == true
|
||||||
env:
|
env:
|
||||||
SFTP_HOST: ${{ steps.conn.outputs.host }}
|
SFTP_HOST: ${{ steps.conn.outputs.host }}
|
||||||
SFTP_PORT: ${{ steps.conn.outputs.port }}
|
SFTP_PORT: ${{ steps.conn.outputs.port }}
|
||||||
@@ -574,24 +580,8 @@ jobs:
|
|||||||
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
|
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── For Dolibarr (crm-module): set version to "development" before deploy ─
|
# Set platform version to "development" before deploy (Dolibarr + Joomla)
|
||||||
PLATFORM=""
|
php /tmp/mokostandards/api/cli/version_set_platform.php --path . --version development
|
||||||
if [ -f ".moko-standards" ]; then
|
|
||||||
PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"')
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$PLATFORM" = "crm-module" ]; then
|
|
||||||
echo "📦 Dolibarr dev deploy — setting module version to 'development'"
|
|
||||||
find "$SOURCE_DIR" -path "*/core/modules/mod*.class.php" -exec \
|
|
||||||
sed -i "s/\(\$this->version\s*=\s*\)['\"][^'\"]*['\"]/\1'development'/" {} + 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$PLATFORM" = "waas-component" ]; then
|
|
||||||
echo "📦 Joomla dev deploy — setting manifest version to 'development'"
|
|
||||||
find "$SOURCE_DIR" -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | while read -r manifest; do
|
|
||||||
sed -i "s|<version>[^<]*</version>|<version>development</version>|" "$manifest" 2>/dev/null || true
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||||
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
|
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
|
||||||
@@ -641,22 +631,22 @@ jobs:
|
|||||||
--force 2>/dev/null || true
|
--force 2>/dev/null || true
|
||||||
|
|
||||||
# Look for an existing open deploy-failure issue
|
# Look for an existing open deploy-failure issue
|
||||||
EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=open&per_page=1" \
|
EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=all&per_page=1&sort=created&direction=desc" \
|
||||||
--jq '.[0].number' 2>/dev/null)
|
--jq '.[0].number' 2>/dev/null)
|
||||||
|
|
||||||
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
|
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
|
||||||
gh api "repos/${REPO}/issues/${EXISTING}" \
|
gh api "repos/${REPO}/issues/${EXISTING}" \
|
||||||
-X PATCH \
|
-X PATCH \
|
||||||
-f title="$TITLE" \
|
-f title="$TITLE" \
|
||||||
-f body="$BODY" \
|
-f body="$BODY" \n -f state="open" \
|
||||||
--silent
|
--silent
|
||||||
echo "📋 Failure issue #${EXISTING} updated: ${REPO}" >> "$GITHUB_STEP_SUMMARY"
|
echo "📋 Failure issue #${EXISTING} updated/reopened: ${REPO}" >> "$GITHUB_STEP_SUMMARY"
|
||||||
else
|
else
|
||||||
gh issue create \
|
gh issue create \
|
||||||
--repo "$REPO" \
|
--repo "$REPO" \
|
||||||
--title "$TITLE" \
|
--title "$TITLE" \
|
||||||
--body "$BODY" \
|
--body "$BODY" \
|
||||||
--label "$LABEL" \
|
--label "$LABEL" \n --assignee "jmiller-moko" \
|
||||||
| tee -a "$GITHUB_STEP_SUMMARY"
|
| tee -a "$GITHUB_STEP_SUMMARY"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
30
.github/workflows/deploy-rs.yml
vendored
30
.github/workflows/deploy-rs.yml
vendored
@@ -22,7 +22,7 @@
|
|||||||
# INGROUP: MokoStandards.Deploy
|
# INGROUP: MokoStandards.Deploy
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/deploy-rs.yml
|
# PATH: /templates/workflows/shared/deploy-rs.yml
|
||||||
# VERSION: 04.01.00
|
# VERSION: 04.02.08
|
||||||
# BRIEF: SFTP deployment workflow for release staging server — synced to all governed repos
|
# BRIEF: SFTP deployment workflow for release staging server — synced to all governed repos
|
||||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-rs.yml in all governed repos.
|
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-rs.yml in all governed repos.
|
||||||
# Port is resolved in order: RS_FTP_PORT variable → :port suffix in RS_FTP_HOST → 22.
|
# Port is resolved in order: RS_FTP_PORT variable → :port suffix in RS_FTP_HOST → 22.
|
||||||
@@ -70,6 +70,9 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-permission:
|
check-permission:
|
||||||
name: Verify Deployment Permission
|
name: Verify Deployment Permission
|
||||||
@@ -344,8 +347,8 @@ jobs:
|
|||||||
|
|
||||||
# ── Platform-specific path safety guards ──────────────────────────────
|
# ── Platform-specific path safety guards ──────────────────────────────
|
||||||
PLATFORM=""
|
PLATFORM=""
|
||||||
if [ -f ".moko-standards" ]; then
|
MOKO_FILE=".github/.mokostandards"; [ ! -f "$MOKO_FILE" ] && MOKO_FILE=".mokostandards"; if [ -f "$MOKO_FILE" ]; then
|
||||||
PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"')
|
PLATFORM=$(grep -E '^platform:' "$MOKO_FILE" | sed 's/.*:[[:space:]]*//' | tr -d '"')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# RS deployment: no path restrictions for any platform
|
# RS deployment: no path restrictions for any platform
|
||||||
@@ -387,7 +390,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true'
|
if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true'
|
||||||
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0
|
uses: shivammathur/setup-php@fcafdd6392932010c2bd5094439b8e33be2a8a09 # v2.37.0
|
||||||
with:
|
with:
|
||||||
php-version: '8.1'
|
php-version: '8.1'
|
||||||
tools: composer
|
tools: composer
|
||||||
@@ -398,14 +401,17 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 --quiet \
|
git clone --depth 1 --branch version/04.02.12 --quiet \
|
||||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
/tmp/mokostandards
|
/tmp/mokostandards
|
||||||
cd /tmp/mokostandards
|
cd /tmp/mokostandards
|
||||||
composer install --no-dev --no-interaction --quiet
|
composer install --no-dev --no-interaction --quiet
|
||||||
|
|
||||||
- name: Clear remote destination folder
|
- name: Clear remote destination folder (manual only)
|
||||||
if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true'
|
if: >-
|
||||||
|
steps.source.outputs.skip == 'false' &&
|
||||||
|
steps.remote.outputs.skip != 'true' &&
|
||||||
|
inputs.clear_remote == true
|
||||||
env:
|
env:
|
||||||
SFTP_HOST: ${{ steps.conn.outputs.host }}
|
SFTP_HOST: ${{ steps.conn.outputs.host }}
|
||||||
SFTP_PORT: ${{ steps.conn.outputs.port }}
|
SFTP_PORT: ${{ steps.conn.outputs.port }}
|
||||||
@@ -558,7 +564,7 @@ jobs:
|
|||||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||||
|
|
||||||
- name: Create or update failure issue
|
- name: Create or update failure issue
|
||||||
if: failure()
|
if: failure() && steps.remote.outputs.skip != 'true'
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
@@ -599,8 +605,8 @@ jobs:
|
|||||||
--description "Automated deploy failure tracking" \
|
--description "Automated deploy failure tracking" \
|
||||||
--force 2>/dev/null || true
|
--force 2>/dev/null || true
|
||||||
|
|
||||||
# Look for an existing open deploy-failure issue
|
# Look for an existing deploy-failure issue (any state — reopen if closed)
|
||||||
EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=open&per_page=1" \
|
EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=all&per_page=1&sort=created&direction=desc" \
|
||||||
--jq '.[0].number' 2>/dev/null)
|
--jq '.[0].number' 2>/dev/null)
|
||||||
|
|
||||||
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
|
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
|
||||||
@@ -608,14 +614,16 @@ jobs:
|
|||||||
-X PATCH \
|
-X PATCH \
|
||||||
-f title="$TITLE" \
|
-f title="$TITLE" \
|
||||||
-f body="$BODY" \
|
-f body="$BODY" \
|
||||||
|
-f state="open" \
|
||||||
--silent
|
--silent
|
||||||
echo "📋 Failure issue #${EXISTING} updated: ${REPO}" >> "$GITHUB_STEP_SUMMARY"
|
echo "📋 Failure issue #${EXISTING} updated/reopened: ${REPO}" >> "$GITHUB_STEP_SUMMARY"
|
||||||
else
|
else
|
||||||
gh issue create \
|
gh issue create \
|
||||||
--repo "$REPO" \
|
--repo "$REPO" \
|
||||||
--title "$TITLE" \
|
--title "$TITLE" \
|
||||||
--body "$BODY" \
|
--body "$BODY" \
|
||||||
--label "$LABEL" \
|
--label "$LABEL" \
|
||||||
|
--assignee "jmiller-moko" \
|
||||||
| tee -a "$GITHUB_STEP_SUMMARY"
|
| tee -a "$GITHUB_STEP_SUMMARY"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
# INGROUP: MokoStandards.Firewall
|
# INGROUP: MokoStandards.Firewall
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/enterprise-firewall-setup.yml
|
# PATH: /templates/workflows/shared/enterprise-firewall-setup.yml
|
||||||
# VERSION: 04.01.00
|
# VERSION: 04.02.08
|
||||||
# BRIEF: Enterprise firewall configuration — generates outbound allow-rules including SFTP deployment server
|
# BRIEF: Enterprise firewall configuration — generates outbound allow-rules including SFTP deployment server
|
||||||
# NOTE: Reads DEV_FTP_HOST / DEV_FTP_PORT variables to include SFTP egress rules alongside HTTPS rules.
|
# NOTE: Reads DEV_FTP_HOST / DEV_FTP_PORT variables to include SFTP egress rules alongside HTTPS rules.
|
||||||
|
|
||||||
|
|||||||
89
.github/workflows/repo_health.yml
vendored
89
.github/workflows/repo_health.yml
vendored
@@ -10,8 +10,8 @@
|
|||||||
# INGROUP: MokoStandards.Validation
|
# INGROUP: MokoStandards.Validation
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /.github/workflows/repo_health.yml
|
# PATH: /.github/workflows/repo_health.yml
|
||||||
# VERSION: 04.01.00
|
# VERSION: 04.04.01
|
||||||
# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts.
|
# BRIEF: Dolibarr module health checks — validates release config, module descriptor, repo artifacts, and scripts governance.
|
||||||
# NOTE: Field is user-managed.
|
# NOTE: Field is user-managed.
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
profile:
|
profile:
|
||||||
description: Which configuration profile to validate. release checks SFTP variables used by release pipeline. scripts checks baseline script prerequisites. repo runs repository health only. al[...]
|
description: 'Validation profile: all, release, scripts, or repo'
|
||||||
required: true
|
required: true
|
||||||
default: all
|
default: all
|
||||||
type: choice
|
type: choice
|
||||||
@@ -39,19 +39,7 @@ on:
|
|||||||
- scripts
|
- scripts
|
||||||
- repo
|
- repo
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
|
||||||
- .github/workflows/**
|
|
||||||
- scripts/**
|
|
||||||
- docs/**
|
|
||||||
- dev/**
|
|
||||||
push:
|
push:
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- .github/workflows/**
|
|
||||||
- scripts/**
|
|
||||||
- docs/**
|
|
||||||
- dev/**
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -68,10 +56,10 @@ env:
|
|||||||
|
|
||||||
# Repo health policy
|
# Repo health policy
|
||||||
# Files are listed as-is; directories must end with a trailing slash.
|
# Files are listed as-is; directories must end with a trailing slash.
|
||||||
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.github/workflows/,src/
|
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.github/workflows/
|
||||||
REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/
|
REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,docs/,update.txt
|
||||||
REPO_DISALLOWED_DIRS:
|
REPO_DISALLOWED_DIRS:
|
||||||
REPO_DISALLOWED_FILES: TODO.md,todo.md
|
REPO_DISALLOWED_FILES: TODO.md,todo.md,update.json
|
||||||
|
|
||||||
# Extended checks toggles
|
# Extended checks toggles
|
||||||
EXTENDED_CHECKS: "true"
|
EXTENDED_CHECKS: "true"
|
||||||
@@ -82,6 +70,7 @@ env:
|
|||||||
WORKFLOWS_DIR: .github/workflows
|
WORKFLOWS_DIR: .github/workflows
|
||||||
SHELLCHECK_PATTERN: '*.sh'
|
SHELLCHECK_PATTERN: '*.sh'
|
||||||
SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml'
|
SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml'
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
access_check:
|
access_check:
|
||||||
@@ -412,6 +401,15 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Source directory: src/ or htdocs/ (either is valid)
|
||||||
|
if [ -d "src" ]; then
|
||||||
|
SOURCE_DIR="src"
|
||||||
|
elif [ -d "htdocs" ]; then
|
||||||
|
SOURCE_DIR="htdocs"
|
||||||
|
else
|
||||||
|
missing_required+=("src/ or htdocs/ (source directory required)")
|
||||||
|
fi
|
||||||
|
|
||||||
IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}"
|
IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}"
|
||||||
IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}"
|
IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}"
|
||||||
IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"
|
IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"
|
||||||
@@ -561,6 +559,61 @@ jobs:
|
|||||||
} >> "${GITHUB_STEP_SUMMARY}"
|
} >> "${GITHUB_STEP_SUMMARY}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Dolibarr-specific checks ──────────────────────────────────────
|
||||||
|
dolibarr_findings=()
|
||||||
|
|
||||||
|
# Module descriptor: src/core/modules/mod*.class.php
|
||||||
|
MOD_FILE="$(find src htdocs -path '*/core/modules/mod*.class.php' -print -quit 2>/dev/null || true)"
|
||||||
|
if [ -z "${MOD_FILE}" ]; then
|
||||||
|
dolibarr_findings+=("Module descriptor not found (src/core/modules/mod*.class.php)")
|
||||||
|
else
|
||||||
|
# Check $this->numero is set and non-zero
|
||||||
|
if ! grep -qP '\$this->numero\s*=\s*[1-9]' "${MOD_FILE}"; then
|
||||||
|
dolibarr_findings+=("Module descriptor: \$this->numero not set or is zero")
|
||||||
|
fi
|
||||||
|
# Check $this->version is not hardcoded (should be set by workflow)
|
||||||
|
if grep -qP "\\\$this->version\s*=\s*'[0-9]" "${MOD_FILE}"; then
|
||||||
|
dolibarr_findings+=("Module descriptor: \$this->version appears hardcoded (should be set by deploy/release workflow)")
|
||||||
|
fi
|
||||||
|
# Check url_last_version points to update.txt
|
||||||
|
if grep -qP 'url_last_version.*update\.json' "${MOD_FILE}"; then
|
||||||
|
dolibarr_findings+=("Module descriptor: url_last_version points to update.json (must be update.txt)")
|
||||||
|
fi
|
||||||
|
# Check url_last_version contains /main/ for main branch
|
||||||
|
CURRENT_BRANCH="${GITHUB_REF_NAME:-main}"
|
||||||
|
if [ "${CURRENT_BRANCH}" = "main" ] && ! grep -qP 'url_last_version.*\/main\/' "${MOD_FILE}"; then
|
||||||
|
dolibarr_findings+=("Module descriptor: url_last_version does not reference /main/ branch")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Source README should exist (Dolibarr module store requirement)
|
||||||
|
if [ -n "${SOURCE_DIR:-}" ] && [ ! -f "${SOURCE_DIR}/README.md" ]; then
|
||||||
|
dolibarr_findings+=("${SOURCE_DIR}/README.md missing (required for Dolibarr module store)")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# update.txt should exist in root (created by auto-release)
|
||||||
|
if [ ! -f 'update.txt' ]; then
|
||||||
|
dolibarr_findings+=("update.txt missing in root (created by auto-release workflow)")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${#dolibarr_findings[@]}" -gt 0 ]; then
|
||||||
|
{
|
||||||
|
printf '%s\n' '### Dolibarr module checks'
|
||||||
|
printf '%s\n' '| Check | Status |'
|
||||||
|
printf '%s\n' '|---|---|'
|
||||||
|
for f in "${dolibarr_findings[@]}"; do
|
||||||
|
printf '%s\n' "| ${f} | Warning |"
|
||||||
|
done
|
||||||
|
printf '\n'
|
||||||
|
} >> "${GITHUB_STEP_SUMMARY}"
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf '%s\n' '### Dolibarr module checks'
|
||||||
|
printf '%s\n' 'All Dolibarr-specific checks passed.'
|
||||||
|
printf '\n'
|
||||||
|
} >> "${GITHUB_STEP_SUMMARY}"
|
||||||
|
fi
|
||||||
|
|
||||||
extended_enabled="${EXTENDED_CHECKS:-true}"
|
extended_enabled="${EXTENDED_CHECKS:-true}"
|
||||||
extended_findings=()
|
extended_findings=()
|
||||||
|
|
||||||
|
|||||||
361
.github/workflows/repository-cleanup.yml
vendored
361
.github/workflows/repository-cleanup.yml
vendored
@@ -9,36 +9,58 @@
|
|||||||
# INGROUP: MokoStandards.Maintenance
|
# INGROUP: MokoStandards.Maintenance
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/repository-cleanup.yml
|
# PATH: /templates/workflows/shared/repository-cleanup.yml
|
||||||
# VERSION: 04.01.00
|
# VERSION: 04.02.08
|
||||||
# BRIEF: One-time repository cleanup — reset labels, strip issue template headers, delete old branches
|
# BRIEF: Recurring repository maintenance — labels, branches, workflows, logs, doc indexes
|
||||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/repository-cleanup.yml in all governed repos.
|
# NOTE: Synced via bulk-repo-sync to .github/workflows/repository-cleanup.yml in all governed repos.
|
||||||
# Run manually via workflow_dispatch. Safe to re-run — all operations are idempotent.
|
# Runs on the 1st and 15th of each month at 6:00 AM UTC, and on manual dispatch.
|
||||||
|
|
||||||
name: Repository Cleanup
|
name: Repository Cleanup
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 6 1,15 * *'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
reset_labels:
|
reset_labels:
|
||||||
description: 'Delete ALL existing labels and recreate the standard 54-label set'
|
description: 'Delete ALL existing labels and recreate the standard set'
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
clean_branches:
|
||||||
|
description: 'Delete old chore/sync-mokostandards-* branches'
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
clean_branches:
|
clean_workflows:
|
||||||
description: 'Delete old chore/sync-mokostandards-* branches (keeps current versioned branch only)'
|
description: 'Delete orphaned workflow runs (cancelled, stale)'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
clean_logs:
|
||||||
|
description: 'Delete workflow run logs older than 30 days'
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
fix_templates:
|
fix_templates:
|
||||||
description: 'Strip copyright comment blocks from issue templates'
|
description: 'Strip copyright comment blocks from issue templates'
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
|
rebuild_indexes:
|
||||||
|
description: 'Rebuild docs/ index files'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
delete_closed_issues:
|
||||||
|
description: 'Delete issues that have been closed for more than 30 days'
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
issues: write
|
issues: write
|
||||||
|
actions: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cleanup:
|
cleanup:
|
||||||
name: Repository Cleanup
|
name: Repository Maintenance
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -46,12 +68,18 @@ jobs:
|
|||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GH_TOKEN || github.token }}
|
token: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Check actor permission
|
- name: Check actor permission
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
ACTOR="${{ github.actor }}"
|
ACTOR="${{ github.actor }}"
|
||||||
|
# Schedule triggers use github-actions[bot]
|
||||||
|
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||||
|
echo "✅ Scheduled run — authorized"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
AUTHORIZED_USERS="jmiller-moko github-actions[bot]"
|
AUTHORIZED_USERS="jmiller-moko github-actions[bot]"
|
||||||
for user in $AUTHORIZED_USERS; do
|
for user in $AUTHORIZED_USERS; do
|
||||||
if [ "$ACTOR" = "$user" ]; then
|
if [ "$ACTOR" = "$user" ]; then
|
||||||
@@ -66,9 +94,90 @@ jobs:
|
|||||||
*) echo "❌ Admin or maintain required"; exit 1 ;;
|
*) echo "❌ Admin or maintain required"; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# ── Determine which tasks to run ─────────────────────────────────────
|
||||||
|
# On schedule: run all tasks with safe defaults (labels NOT reset)
|
||||||
|
# On dispatch: use input toggles
|
||||||
|
- name: Set task flags
|
||||||
|
id: tasks
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||||
|
echo "reset_labels=false" >> $GITHUB_OUTPUT
|
||||||
|
echo "clean_branches=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "clean_workflows=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "clean_logs=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "fix_templates=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "rebuild_indexes=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "delete_closed_issues=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "reset_labels=${{ inputs.reset_labels }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "clean_branches=${{ inputs.clean_branches }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "clean_workflows=${{ inputs.clean_workflows }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "clean_logs=${{ inputs.clean_logs }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "fix_templates=${{ inputs.fix_templates }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "rebuild_indexes=${{ inputs.rebuild_indexes }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "delete_closed_issues=${{ inputs.delete_closed_issues }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── DELETE RETIRED WORKFLOWS (always runs) ────────────────────────────
|
||||||
|
- name: Delete retired workflow files
|
||||||
|
run: |
|
||||||
|
echo "## 🗑️ Retired Workflow Cleanup" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
RETIRED=(
|
||||||
|
".github/workflows/build.yml"
|
||||||
|
".github/workflows/code-quality.yml"
|
||||||
|
".github/workflows/release-cycle.yml"
|
||||||
|
".github/workflows/release-pipeline.yml"
|
||||||
|
".github/workflows/branch-cleanup.yml"
|
||||||
|
".github/workflows/auto-update-changelog.yml"
|
||||||
|
".github/workflows/enterprise-issue-manager.yml"
|
||||||
|
".github/workflows/flush-actions-cache.yml"
|
||||||
|
".github/workflows/mokostandards-script-runner.yml"
|
||||||
|
".github/workflows/unified-ci.yml"
|
||||||
|
".github/workflows/unified-platform-testing.yml"
|
||||||
|
".github/workflows/reusable-build.yml"
|
||||||
|
".github/workflows/reusable-ci-validation.yml"
|
||||||
|
".github/workflows/reusable-deploy.yml"
|
||||||
|
".github/workflows/reusable-php-quality.yml"
|
||||||
|
".github/workflows/reusable-platform-testing.yml"
|
||||||
|
".github/workflows/reusable-project-detector.yml"
|
||||||
|
".github/workflows/reusable-release.yml"
|
||||||
|
".github/workflows/reusable-script-executor.yml"
|
||||||
|
".github/workflows/rebuild-docs-indexes.yml"
|
||||||
|
".github/workflows/setup-project-v2.yml"
|
||||||
|
".github/workflows/sync-docs-to-project.yml"
|
||||||
|
".github/workflows/release.yml"
|
||||||
|
".github/workflows/sync-changelogs.yml"
|
||||||
|
".github/workflows/version_branch.yml"
|
||||||
|
"update.json"
|
||||||
|
".github/workflows/auto-version-branch.yml"
|
||||||
|
)
|
||||||
|
|
||||||
|
DELETED=0
|
||||||
|
for wf in "${RETIRED[@]}"; do
|
||||||
|
if [ -f "$wf" ]; then
|
||||||
|
git rm "$wf" 2>/dev/null || rm -f "$wf"
|
||||||
|
echo " Deleted: \`$(basename $wf)\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
DELETED=$((DELETED+1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$DELETED" -gt 0 ]; then
|
||||||
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --local user.name "github-actions[bot]"
|
||||||
|
git add -A
|
||||||
|
git commit -m "chore: delete ${DELETED} retired workflow file(s) [skip ci]" \
|
||||||
|
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||||
|
git push
|
||||||
|
echo "✅ ${DELETED} retired workflow(s) deleted" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "✅ No retired workflows found" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
# ── LABEL RESET ──────────────────────────────────────────────────────
|
# ── LABEL RESET ──────────────────────────────────────────────────────
|
||||||
- name: Reset labels to standard set
|
- name: Reset labels to standard set
|
||||||
if: inputs.reset_labels == true
|
if: steps.tasks.outputs.reset_labels == 'true'
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
@@ -76,23 +185,16 @@ jobs:
|
|||||||
echo "## 🏷️ Label Reset" >> $GITHUB_STEP_SUMMARY
|
echo "## 🏷️ Label Reset" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# Delete all existing labels
|
|
||||||
echo "Deleting existing labels..."
|
|
||||||
DELETED=0
|
|
||||||
gh api "repos/${REPO}/labels?per_page=100" --paginate --jq '.[].name' | while read -r label; do
|
gh api "repos/${REPO}/labels?per_page=100" --paginate --jq '.[].name' | while read -r label; do
|
||||||
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$label', safe=''))")
|
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$label', safe=''))")
|
||||||
gh api -X DELETE "repos/${REPO}/labels/${ENCODED}" --silent 2>/dev/null && DELETED=$((DELETED+1)) || true
|
gh api -X DELETE "repos/${REPO}/labels/${ENCODED}" --silent 2>/dev/null || true
|
||||||
done
|
done
|
||||||
echo "Deleted existing labels" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
# Create the standard 54-label set
|
|
||||||
echo "Creating standard labels..."
|
|
||||||
CREATED=0
|
|
||||||
while IFS='|' read -r name color description; do
|
while IFS='|' read -r name color description; do
|
||||||
[ -z "$name" ] && continue
|
[ -z "$name" ] && continue
|
||||||
gh api "repos/${REPO}/labels" \
|
gh api "repos/${REPO}/labels" \
|
||||||
-f name="$name" -f color="$color" -f description="$description" \
|
-f name="$name" -f color="$color" -f description="$description" \
|
||||||
--silent 2>/dev/null && CREATED=$((CREATED+1)) || true
|
--silent 2>/dev/null || true
|
||||||
done << 'LABELS'
|
done << 'LABELS'
|
||||||
joomla|7F52FF|Joomla extension or component
|
joomla|7F52FF|Joomla extension or component
|
||||||
dolibarr|FF6B6B|Dolibarr module or extension
|
dolibarr|FF6B6B|Dolibarr module or extension
|
||||||
@@ -125,6 +227,7 @@ jobs:
|
|||||||
type: enhancement|84B6EB|Enhancement to existing feature
|
type: enhancement|84B6EB|Enhancement to existing feature
|
||||||
type: refactor|F9D0C4|Code refactoring
|
type: refactor|F9D0C4|Code refactoring
|
||||||
type: chore|FEF2C0|Maintenance tasks
|
type: chore|FEF2C0|Maintenance tasks
|
||||||
|
type: version|0E8A16|Version-related change
|
||||||
status: pending|FBCA04|Pending action or decision
|
status: pending|FBCA04|Pending action or decision
|
||||||
status: in-progress|0E8A16|Currently being worked on
|
status: in-progress|0E8A16|Currently being worked on
|
||||||
status: blocked|B60205|Blocked by another issue or dependency
|
status: blocked|B60205|Blocked by another issue or dependency
|
||||||
@@ -149,45 +252,85 @@ jobs:
|
|||||||
version-drift|FFA500|Version mismatch detected
|
version-drift|FFA500|Version mismatch detected
|
||||||
deploy-failure|CC0000|Automated deploy failure tracking
|
deploy-failure|CC0000|Automated deploy failure tracking
|
||||||
template-validation-failure|D73A4A|Template workflow validation failure
|
template-validation-failure|D73A4A|Template workflow validation failure
|
||||||
|
version|0E8A16|Version bump or release
|
||||||
LABELS
|
LABELS
|
||||||
|
|
||||||
echo "✅ Standard labels created" >> $GITHUB_STEP_SUMMARY
|
echo "✅ Standard labels created" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# ── BRANCH CLEANUP ───────────────────────────────────────────────────
|
# ── BRANCH CLEANUP ───────────────────────────────────────────────────
|
||||||
- name: Delete old sync branches
|
- name: Delete old sync branches
|
||||||
if: inputs.clean_branches == true
|
if: steps.tasks.outputs.clean_branches == 'true'
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
REPO="${{ github.repository }}"
|
REPO="${{ github.repository }}"
|
||||||
CURRENT="chore/sync-mokostandards-v04.01.00"
|
CURRENT="chore/sync-mokostandards-v04.02.12"
|
||||||
echo "## 🌿 Branch Cleanup" >> $GITHUB_STEP_SUMMARY
|
echo "## 🌿 Branch Cleanup" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
DELETED=0
|
FOUND=false
|
||||||
gh api "repos/${REPO}/branches?per_page=100" --jq '.[].name' | \
|
gh api "repos/${REPO}/branches?per_page=100" --jq '.[].name' | \
|
||||||
grep "^chore/sync-mokostandards" | \
|
grep "^chore/sync-mokostandards" | \
|
||||||
grep -v "^${CURRENT}$" | while read -r branch; do
|
grep -v "^${CURRENT}$" | while read -r branch; do
|
||||||
# Close any open PRs on this branch
|
|
||||||
gh pr list --repo "$REPO" --head "$branch" --state open --json number --jq '.[].number' 2>/dev/null | while read -r pr; do
|
gh pr list --repo "$REPO" --head "$branch" --state open --json number --jq '.[].number' 2>/dev/null | while read -r pr; do
|
||||||
gh pr close "$pr" --repo "$REPO" --comment "Superseded by \`${CURRENT}\`" 2>/dev/null || true
|
gh pr close "$pr" --repo "$REPO" --comment "Superseded by \`${CURRENT}\`" 2>/dev/null || true
|
||||||
echo " Closed PR #${pr}" >> $GITHUB_STEP_SUMMARY
|
echo " Closed PR #${pr}" >> $GITHUB_STEP_SUMMARY
|
||||||
done
|
done
|
||||||
# Delete the branch
|
|
||||||
gh api -X DELETE "repos/${REPO}/git/refs/heads/${branch}" --silent 2>/dev/null || true
|
gh api -X DELETE "repos/${REPO}/git/refs/heads/${branch}" --silent 2>/dev/null || true
|
||||||
echo " Deleted: \`${branch}\`" >> $GITHUB_STEP_SUMMARY
|
echo " Deleted: \`${branch}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
FOUND=true
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$FOUND" != "true" ]; then
|
||||||
|
echo "✅ No old sync branches found" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── WORKFLOW RUN CLEANUP ─────────────────────────────────────────────
|
||||||
|
- name: Clean up workflow runs
|
||||||
|
if: steps.tasks.outputs.clean_workflows == 'true'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
|
run: |
|
||||||
|
REPO="${{ github.repository }}"
|
||||||
|
echo "## 🔄 Workflow Run Cleanup" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
DELETED=0
|
||||||
|
# Delete cancelled and stale workflow runs
|
||||||
|
for status in cancelled stale; do
|
||||||
|
gh api "repos/${REPO}/actions/runs?status=${status}&per_page=100" \
|
||||||
|
--jq '.workflow_runs[].id' 2>/dev/null | while read -r run_id; do
|
||||||
|
gh api -X DELETE "repos/${REPO}/actions/runs/${run_id}" --silent 2>/dev/null || true
|
||||||
|
DELETED=$((DELETED+1))
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "✅ Cleaned cancelled/stale workflow runs" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# ── LOG CLEANUP ──────────────────────────────────────────────────────
|
||||||
|
- name: Delete old workflow run logs
|
||||||
|
if: steps.tasks.outputs.clean_logs == 'true'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
|
run: |
|
||||||
|
REPO="${{ github.repository }}"
|
||||||
|
CUTOFF=$(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
||||||
|
echo "## 📋 Log Cleanup" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Deleting logs older than: ${CUTOFF}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
DELETED=0
|
||||||
|
gh api "repos/${REPO}/actions/runs?created=<${CUTOFF}&per_page=100" \
|
||||||
|
--jq '.workflow_runs[].id' 2>/dev/null | while read -r run_id; do
|
||||||
|
gh api -X DELETE "repos/${REPO}/actions/runs/${run_id}/logs" --silent 2>/dev/null || true
|
||||||
DELETED=$((DELETED+1))
|
DELETED=$((DELETED+1))
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$DELETED" -eq 0 ] 2>/dev/null; then
|
echo "✅ Cleaned old workflow run logs" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "✅ No old sync branches found" >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "✅ Cleanup complete" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── ISSUE TEMPLATE FIX ──────────────────────────────────────────────
|
# ── ISSUE TEMPLATE FIX ──────────────────────────────────────────────
|
||||||
- name: Strip copyright headers from issue templates
|
- name: Strip copyright headers from issue templates
|
||||||
if: inputs.fix_templates == true
|
if: steps.tasks.outputs.fix_templates == 'true'
|
||||||
run: |
|
run: |
|
||||||
echo "## 📋 Issue Template Cleanup" >> $GITHUB_STEP_SUMMARY
|
echo "## 📋 Issue Template Cleanup" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -214,26 +357,158 @@ jobs:
|
|||||||
echo "✅ No templates need cleaning" >> $GITHUB_STEP_SUMMARY
|
echo "✅ No templates need cleaning" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── SELF-DELETE ─────────────────────────────────────────────────────
|
# ── REBUILD DOC INDEXES ─────────────────────────────────────────────
|
||||||
- name: Delete this workflow (one-time use)
|
- name: Rebuild docs/ index files
|
||||||
if: success()
|
if: steps.tasks.outputs.rebuild_indexes == 'true'
|
||||||
|
run: |
|
||||||
|
echo "## 📚 Documentation Index Rebuild" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
if [ ! -d "docs" ]; then
|
||||||
|
echo "⏭️ No docs/ directory — skipping" >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
UPDATED=0
|
||||||
|
# Generate index.md for each docs/ subdirectory
|
||||||
|
find docs -type d | while read -r dir; do
|
||||||
|
INDEX="${dir}/index.md"
|
||||||
|
FILES=$(find "$dir" -maxdepth 1 -name "*.md" ! -name "index.md" -printf "- [%f](./%f)\n" 2>/dev/null | sort)
|
||||||
|
if [ -z "$FILES" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > "$INDEX" << INDEXEOF
|
||||||
|
# $(basename "$dir")
|
||||||
|
|
||||||
|
## Documents
|
||||||
|
|
||||||
|
${FILES}
|
||||||
|
|
||||||
|
---
|
||||||
|
*Auto-generated by repository-cleanup workflow*
|
||||||
|
INDEXEOF
|
||||||
|
# Dedent
|
||||||
|
sed -i 's/^ //' "$INDEX"
|
||||||
|
UPDATED=$((UPDATED+1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$UPDATED" -gt 0 ]; then
|
||||||
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --local user.name "github-actions[bot]"
|
||||||
|
git add docs/
|
||||||
|
if ! git diff --cached --quiet; then
|
||||||
|
git commit -m "docs: rebuild documentation indexes [skip ci]" \
|
||||||
|
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||||
|
git push
|
||||||
|
echo "✅ ${UPDATED} index file(s) rebuilt and committed" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "✅ All indexes already up to date" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✅ No indexes to rebuild" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── VERSION DRIFT DETECTION ──────────────────────────────────────────
|
||||||
|
- name: Check for version drift
|
||||||
|
run: |
|
||||||
|
echo "## 📦 Version Drift Check" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
if [ ! -f "README.md" ]; then
|
||||||
|
echo "⏭️ No README.md — skipping" >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
README_VERSION=$(grep -oP '^\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' README.md 2>/dev/null | head -1)
|
||||||
|
if [ -z "$README_VERSION" ]; then
|
||||||
|
echo "⚠️ No VERSION found in README.md FILE INFORMATION block" >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "**README version:** \`${README_VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
DRIFT=0
|
||||||
|
CHECKED=0
|
||||||
|
|
||||||
|
# Check all files with FILE INFORMATION blocks
|
||||||
|
while IFS= read -r -d '' file; do
|
||||||
|
FILE_VERSION=$(grep -oP '^\s*\*?\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' "$file" 2>/dev/null | head -1)
|
||||||
|
[ -z "$FILE_VERSION" ] && continue
|
||||||
|
CHECKED=$((CHECKED+1))
|
||||||
|
if [ "$FILE_VERSION" != "$README_VERSION" ]; then
|
||||||
|
echo " ⚠️ \`${file}\`: \`${FILE_VERSION}\` (expected \`${README_VERSION}\`)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
DRIFT=$((DRIFT+1))
|
||||||
|
fi
|
||||||
|
done < <(find . -maxdepth 4 -type f \( -name "*.php" -o -name "*.md" -o -name "*.yml" \) ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -print0 2>/dev/null)
|
||||||
|
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
if [ "$DRIFT" -gt 0 ]; then
|
||||||
|
echo "⚠️ **${DRIFT}** file(s) out of ${CHECKED} have version drift" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Run \`sync-version-on-merge\` workflow or update manually" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "✅ All ${CHECKED} file(s) match README version \`${README_VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── PROTECT CUSTOM WORKFLOWS ────────────────────────────────────────
|
||||||
|
- name: Ensure custom workflow directory exists
|
||||||
|
run: |
|
||||||
|
echo "## 🔧 Custom Workflows" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
if [ ! -d ".github/workflows/custom" ]; then
|
||||||
|
mkdir -p .github/workflows/custom
|
||||||
|
cat > .github/workflows/custom/README.md << 'CWEOF'
|
||||||
|
# Custom Workflows
|
||||||
|
|
||||||
|
Place repo-specific workflows here. Files in this directory are:
|
||||||
|
- **Never overwritten** by MokoStandards bulk sync
|
||||||
|
- **Never deleted** by the repository-cleanup workflow
|
||||||
|
- Safe for custom CI, notifications, or repo-specific automation
|
||||||
|
|
||||||
|
Synced workflows live in `.github/workflows/` (parent directory).
|
||||||
|
CWEOF
|
||||||
|
sed -i 's/^ //' .github/workflows/custom/README.md
|
||||||
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --local user.name "github-actions[bot]"
|
||||||
|
git add .github/workflows/custom/
|
||||||
|
if ! git diff --cached --quiet; then
|
||||||
|
git commit -m "chore: create .github/workflows/custom/ for repo-specific workflows [skip ci]" \
|
||||||
|
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||||
|
git push
|
||||||
|
echo "✅ Created \`.github/workflows/custom/\` directory" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
CUSTOM_COUNT=$(find .github/workflows/custom -name "*.yml" -o -name "*.yaml" 2>/dev/null | wc -l)
|
||||||
|
echo "✅ Custom workflow directory exists (${CUSTOM_COUNT} workflow(s))" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── DELETE CLOSED ISSUES ──────────────────────────────────────────────
|
||||||
|
- name: Delete old closed issues
|
||||||
|
if: steps.tasks.outputs.delete_closed_issues == 'true'
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
echo "## 🗑️ Self-Cleanup" >> $GITHUB_STEP_SUMMARY
|
REPO="${{ github.repository }}"
|
||||||
|
CUTOFF=$(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
||||||
|
echo "## 🗑️ Closed Issue Cleanup" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Deleting issues closed before: ${CUTOFF}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
WORKFLOW_FILE=".github/workflows/repository-cleanup.yml"
|
DELETED=0
|
||||||
if [ -f "$WORKFLOW_FILE" ]; then
|
gh api "repos/${REPO}/issues?state=closed&since=1970-01-01T00:00:00Z&per_page=100&sort=updated&direction=asc" \
|
||||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
--jq ".[] | select(.closed_at < \"${CUTOFF}\") | .number" 2>/dev/null | while read -r num; do
|
||||||
git config --local user.name "github-actions[bot]"
|
# Lock and close with "not_planned" to mark as cleaned up
|
||||||
git rm "$WORKFLOW_FILE"
|
gh api "repos/${REPO}/issues/${num}/lock" -X PUT -f lock_reason="resolved" --silent 2>/dev/null || true
|
||||||
git commit -m "chore: remove repository-cleanup.yml after successful run [skip ci]" \
|
echo " Locked issue #${num}" >> $GITHUB_STEP_SUMMARY
|
||||||
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
DELETED=$((DELETED+1))
|
||||||
git push
|
done
|
||||||
echo "✅ Workflow file deleted — it will not appear in future syncs" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
if [ "$DELETED" -eq 0 ] 2>/dev/null; then
|
||||||
|
echo "✅ No old closed issues found" >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
else
|
||||||
echo "ℹ️ Workflow file already removed" >> $GITHUB_STEP_SUMMARY
|
echo "✅ Locked ${DELETED} old closed issue(s)" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Summary
|
- name: Summary
|
||||||
@@ -241,4 +516,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "---" >> $GITHUB_STEP_SUMMARY
|
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "*Run by @${{ github.actor }} via workflow_dispatch*" >> $GITHUB_STEP_SUMMARY
|
echo "*Run by @${{ github.actor }} — trigger: ${{ github.event_name }}*" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
2355
.github/workflows/standards-compliance.yml
vendored
2355
.github/workflows/standards-compliance.yml
vendored
File diff suppressed because it is too large
Load Diff
35
.github/workflows/sync-version-on-merge.yml
vendored
35
.github/workflows/sync-version-on-merge.yml
vendored
@@ -9,7 +9,7 @@
|
|||||||
# INGROUP: MokoStandards.Automation
|
# INGROUP: MokoStandards.Automation
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/sync-version-on-merge.yml
|
# PATH: /templates/workflows/shared/sync-version-on-merge.yml
|
||||||
# VERSION: 04.01.00
|
# VERSION: 04.02.08
|
||||||
# BRIEF: Auto-bump patch version on every push to main and propagate to all file headers
|
# BRIEF: Auto-bump patch version on every push to main and propagate to all file headers
|
||||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/sync-version-on-merge.yml in all governed repos.
|
# NOTE: Synced via bulk-repo-sync to .github/workflows/sync-version-on-merge.yml in all governed repos.
|
||||||
# README.md is the single source of truth for the repository version.
|
# README.md is the single source of truth for the repository version.
|
||||||
@@ -32,6 +32,9 @@ permissions:
|
|||||||
contents: write
|
contents: write
|
||||||
issues: write
|
issues: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync-version:
|
sync-version:
|
||||||
name: Propagate README version
|
name: Propagate README version
|
||||||
@@ -45,7 +48,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up PHP
|
- name: Set up PHP
|
||||||
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0
|
uses: shivammathur/setup-php@fcafdd6392932010c2bd5094439b8e33be2a8a09 # v2.37.0
|
||||||
with:
|
with:
|
||||||
php-version: '8.1'
|
php-version: '8.1'
|
||||||
tools: composer
|
tools: composer
|
||||||
@@ -55,7 +58,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 --quiet \
|
git clone --depth 1 --branch version/04.02.12 --quiet \
|
||||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
/tmp/mokostandards
|
/tmp/mokostandards
|
||||||
cd /tmp/mokostandards
|
cd /tmp/mokostandards
|
||||||
@@ -64,31 +67,20 @@ jobs:
|
|||||||
- name: Auto-bump patch version
|
- name: Auto-bump patch version
|
||||||
if: ${{ github.event_name == 'push' && github.actor != 'github-actions[bot]' }}
|
if: ${{ github.event_name == 'push' && github.actor != 'github-actions[bot]' }}
|
||||||
run: |
|
run: |
|
||||||
# If README.md was part of this push, the author already bumped the version — skip.
|
|
||||||
if git diff --name-only HEAD~1 HEAD 2>/dev/null | grep -q '^README\.md$'; then
|
if git diff --name-only HEAD~1 HEAD 2>/dev/null | grep -q '^README\.md$'; then
|
||||||
echo "README.md changed in this push — skipping auto-bump"
|
echo "README.md changed in this push — skipping auto-bump"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CURRENT=$(grep -oP '^\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' README.md | head -1)
|
RESULT=$(php /tmp/mokostandards/api/cli/version_bump.php --path .) || {
|
||||||
if [ -z "$CURRENT" ]; then
|
echo "⚠️ Could not bump version — skipping"
|
||||||
echo "⚠️ No VERSION found in README.md — skipping auto-bump"
|
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
}
|
||||||
|
echo "Auto-bumping patch: $RESULT"
|
||||||
# Increment the patch component (zero-padded to 2 digits)
|
|
||||||
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
|
|
||||||
MINOR=$(echo "$CURRENT" | cut -d. -f2)
|
|
||||||
PATCH=$(echo "$CURRENT" | cut -d. -f3)
|
|
||||||
NEW_PATCH=$(printf '%02d' $(( 10#$PATCH + 1 )))
|
|
||||||
NEW_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}"
|
|
||||||
|
|
||||||
echo "Auto-bumping patch: $CURRENT → $NEW_VERSION"
|
|
||||||
sed -i "s/^\(\s*VERSION:\s*\)${CURRENT}/\1${NEW_VERSION}/" README.md
|
|
||||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
git config --local user.name "github-actions[bot]"
|
git config --local user.name "github-actions[bot]"
|
||||||
git add README.md
|
git add README.md
|
||||||
git commit -m "chore(version): auto-bump patch ${CURRENT} → ${NEW_VERSION} [skip ci]" \
|
git commit -m "chore(version): auto-bump patch ${RESULT} [skip ci]" \
|
||||||
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||||
git push
|
git push
|
||||||
|
|
||||||
@@ -96,7 +88,7 @@ jobs:
|
|||||||
id: readme_version
|
id: readme_version
|
||||||
run: |
|
run: |
|
||||||
git pull --ff-only 2>/dev/null || true
|
git pull --ff-only 2>/dev/null || true
|
||||||
VERSION=$(grep -oP '^\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' README.md | head -1)
|
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null)
|
||||||
if [ -z "$VERSION" ]; then
|
if [ -z "$VERSION" ]; then
|
||||||
echo "⚠️ No VERSION in README.md — skipping propagation"
|
echo "⚠️ No VERSION in README.md — skipping propagation"
|
||||||
echo "skip=true" >> $GITHUB_OUTPUT
|
echo "skip=true" >> $GITHUB_OUTPUT
|
||||||
@@ -119,8 +111,9 @@ jobs:
|
|||||||
- name: Commit updated files
|
- name: Commit updated files
|
||||||
if: ${{ steps.readme_version.outputs.skip != 'true' && inputs.dry_run != true }}
|
if: ${{ steps.readme_version.outputs.skip != 'true' && inputs.dry_run != true }}
|
||||||
run: |
|
run: |
|
||||||
|
git pull --ff-only 2>/dev/null || true
|
||||||
if git diff --quiet; then
|
if git diff --quiet; then
|
||||||
echo "ℹ️ No version changes needed"
|
echo "ℹ️ No version changes needed — already up to date"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
VERSION="${{ steps.readme_version.outputs.version }}"
|
VERSION="${{ steps.readme_version.outputs.version }}"
|
||||||
|
|||||||
20
.mokostandards
Normal file
20
.mokostandards
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
# FILE INFORMATION
|
||||||
|
# DEFGROUP: MokoStandards.Templates.Config
|
||||||
|
# INGROUP: MokoStandards.Templates
|
||||||
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
|
# PATH: /templates/configs/moko-standards.yml
|
||||||
|
# VERSION: 04.02.08
|
||||||
|
# BRIEF: Governance attachment template — synced to .mokostandards in every governed repository
|
||||||
|
# NOTE: Tokens replaced at sync time: mokoconsulting-tech, MokoCassiopeia, waas-component, 04.02.12
|
||||||
|
#
|
||||||
|
# This file is managed automatically by MokoStandards bulk sync.
|
||||||
|
# Do not edit manually — changes will be overwritten on the next sync.
|
||||||
|
# To update governance settings, open a PR in MokoStandards instead:
|
||||||
|
# https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
|
|
||||||
|
standards_source: "https://github.com/mokoconsulting-tech/MokoStandards"
|
||||||
|
standards_version: "04.02.12"
|
||||||
|
platform: "waas-component"
|
||||||
|
governed_repo: "mokoconsulting-tech/MokoCassiopeia"
|
||||||
@@ -1,98 +1,87 @@
|
|||||||
<!--
|
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
|
|
||||||
This file is part of a Moko Consulting project.
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
SPDX-LICENSE-IDENTIFIER: GPL-3.0-or-later
|
||||||
|
|
||||||
# FILE INFORMATION
|
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
|
||||||
DEFGROUP: Joomla.Template
|
|
||||||
INGROUP: MokoCassiopeia.Governance
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
|
||||||
FILE: CODE_OF_CONDUCT.md
|
You should have received a copy of the GNU General Public License (./LICENSE.md).
|
||||||
VERSION: 03.06.03
|
|
||||||
BRIEF: Contributor code of conduct for the MokoCassiopeia project.
|
# FILE INFORMATION
|
||||||
PATH: /CODE_OF_CONDUCT.md
|
DEFGROUP:
|
||||||
NOTE: This document defines behavioral expectations and enforcement processes.
|
INGROUP: Project.Documentation
|
||||||
|
REPO:
|
||||||
|
VERSION: 04.02.08
|
||||||
|
PATH: ./CODE_OF_CONDUCT.md
|
||||||
|
BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default
|
||||||
-->
|
-->
|
||||||
|
# Code of Conduct
|
||||||
|
|
||||||
## Code of Conduct
|
## 1. Purpose
|
||||||
|
|
||||||
This Code of Conduct establishes expectations for behavior within the MokoCassiopeia project community. The objective is to maintain a professional, inclusive, and respectful environment aligned with open source governance best practices.
|
The purpose of this Code of Conduct is to ensure a safe, inclusive, and respectful environment for all contributors and participants in Moko Consulting projects. This applies to all interactions, whether in repositories, issue trackers, documentation, meetings, or community spaces.
|
||||||
|
|
||||||
## Scope
|
## 2. Our Standards
|
||||||
|
|
||||||
This Code of Conduct applies to all project spaces, including:
|
Participants are expected to uphold behaviors that strengthen our community, including:
|
||||||
|
|
||||||
* GitHub repositories, issues, pull requests, discussions, and security advisories.
|
Demonstrating empathy and respect toward others.
|
||||||
* Project documentation, workflows, and release processes.
|
Being inclusive of diverse viewpoints and backgrounds.
|
||||||
* Any communication channels officially associated with the project.
|
Gracefully accepting constructive feedback.
|
||||||
|
Prioritizing collaboration over conflict.
|
||||||
|
Showing professionalism in all interactions.
|
||||||
|
|
||||||
## Our Standards
|
### Unacceptable behavior includes:
|
||||||
|
|
||||||
Participants are expected to:
|
Harassment, discrimination, or derogatory comments.
|
||||||
|
Threatening or violent language or actions.
|
||||||
|
Disruptive, aggressive, or intentionally harmful behavior.
|
||||||
|
Publishing others’ private information without permission.
|
||||||
|
Any behavior that violates applicable laws.
|
||||||
|
|
||||||
* Communicate professionally and respectfully.
|
## 3. Responsibilities of Maintainers
|
||||||
* Provide constructive feedback focused on technical merit and project objectives.
|
|
||||||
* Respect differing viewpoints, experience levels, and backgrounds.
|
|
||||||
* Follow documented contribution, security, and governance policies.
|
|
||||||
|
|
||||||
Unacceptable behavior includes:
|
Maintainers are responsible for:
|
||||||
|
|
||||||
* Harassment, discrimination, or exclusionary conduct.
|
Clarifying acceptable behavior.
|
||||||
* Personal attacks, insults, or inflammatory comments.
|
Taking appropriate corrective action when unacceptable behavior occurs.
|
||||||
* Publishing private information without consent.
|
Removing, editing, or rejecting contributions that violate this Code.
|
||||||
* Disruptive behavior that materially interferes with project operations.
|
Temporarily or permanently banning contributors who engage in repeated or severe violations.
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
## 4. Scope
|
||||||
|
|
||||||
Project maintainers are responsible for:
|
This Code applies to:
|
||||||
|
|
||||||
* Clarifying standards when questions arise.
|
All Moko Consulting repositories.
|
||||||
* Taking appropriate and proportionate corrective action when violations occur.
|
All documentation and collaboration platforms.
|
||||||
* Maintaining confidentiality to the extent practical during investigations.
|
Public and private communication related to project activities.
|
||||||
|
Any representation of Moko Consulting in online or offline spaces.
|
||||||
|
|
||||||
## Reporting
|
## 5. Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported through:
|
Instances of misconduct may be reported to:
|
||||||
|
**[hello@mokoconsulting.tech](mailto:hello@mokoconsulting.tech)**
|
||||||
|
|
||||||
* Email: `hello@mokoconsulting.tech` with subject `CODE OF CONDUCT: MokoCassiopeia`.
|
All reports will be reviewed and investigated promptly and fairly. Maintainers are obligated to maintain confidentiality where possible.
|
||||||
|
|
||||||
Reports should include relevant context, links, screenshots, or other supporting information.
|
Consequences may include:
|
||||||
|
|
||||||
## Enforcement Guidelines
|
A warning.
|
||||||
|
Required training or mediation.
|
||||||
|
Temporary or permanent bans.
|
||||||
|
Escalation to legal authorities when required.
|
||||||
|
|
||||||
Corrective actions may include, but are not limited to:
|
## 6. Acknowledgements
|
||||||
|
|
||||||
* Private warning or request for corrective action.
|
This Code of Conduct is inspired by widely adopted community guidelines, including the Contributor Covenant and major open-source collaboration standards.
|
||||||
* Temporary or permanent restriction from project participation.
|
|
||||||
* Removal of content that violates this Code of Conduct.
|
|
||||||
|
|
||||||
Decisions are made based on impact, severity, and pattern of behavior.
|
## 7. Related Documents
|
||||||
|
|
||||||
## No Retaliation
|
[Governance Guide](./docs-governance.md)
|
||||||
|
[Contributor Guide](./docs-contributing.md)
|
||||||
|
[Documentation Index](./docs-index.md)
|
||||||
|
|
||||||
Retaliation against individuals who report concerns in good faith is not tolerated. Any retaliatory behavior will be treated as a separate violation.
|
This Code of Conduct is a living document and may be updated following the established Change Management process.
|
||||||
|
|
||||||
## Jurisdiction
|
|
||||||
|
|
||||||
This project is managed from Tennessee, USA. This statement is informational and does not constitute legal advice.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Metadata
|
|
||||||
|
|
||||||
* **Document:** CODE_OF_CONDUCT.md
|
|
||||||
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
|
|
||||||
* **Path:** /CODE_OF_CONDUCT.md
|
|
||||||
* **Owner:** Moko Consulting
|
|
||||||
* **Version:** 03.06.00
|
|
||||||
* **Status:** Active
|
|
||||||
* **Effective Date:** 2025-12-18
|
|
||||||
* **Last Reviewed:** 2025-12-18
|
|
||||||
|
|
||||||
## Revision History
|
|
||||||
|
|
||||||
| Date | Change Summary | Author |
|
|
||||||
| ---------- | ----------------------------------------------------------------------------- | --------------- |
|
|
||||||
| 2025-12-18 | Initial publication of contributor conduct standards and enforcement process. | Moko Consulting |
|
|
||||||
|
|||||||
158
CONTRIBUTING.md
158
CONTRIBUTING.md
@@ -1,145 +1,55 @@
|
|||||||
<!--
|
<!--
|
||||||
Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
|
||||||
This file is part of a Moko Consulting project.
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License (./LICENSE).
|
||||||
|
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Template
|
DEFGROUP: {{DEFGROUP}}
|
||||||
INGROUP: MokoCassiopeia.Governance
|
INGROUP: Project.Documentation
|
||||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||||
FILE: CONTRIBUTING.md
|
VERSION: 04.02.12
|
||||||
VERSION: 03.06.03
|
PATH: ./CONTRIBUTING.md
|
||||||
BRIEF: Contribution guidelines for the MokoCassiopeia project.
|
BRIEF: How to contribute; commit, PR, testing and security policies
|
||||||
PATH: /CONTRIBUTING.md
|
-->
|
||||||
NOTE: This document defines contribution workflow, standards, and governance alignment.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Contributing
|
# Contributing
|
||||||
|
|
||||||
This document defines how to contribute to the MokoCassiopeia project. The goal is to ensure changes are reviewable, auditable, and aligned with project governance and release processes.
|
Thank you for your interest in contributing to this project!
|
||||||
|
|
||||||
## Scope
|
This repository is governed by **[MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards)** — the authoritative source of coding standards, workflows, and policies for all Moko Consulting repositories.
|
||||||
|
|
||||||
These guidelines apply to all contributions, including:
|
## Quick Start
|
||||||
|
|
||||||
* Source code changes
|
1. **Fork** the repository
|
||||||
* Documentation updates
|
2. **Branch** from `main` using `dev/XX.YY.ZZ/description` format
|
||||||
* Bug reports and enhancement proposals
|
3. **Follow** [MokoStandards coding conventions](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md)
|
||||||
|
4. **Commit** using [conventional commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `docs:`, `chore:`, etc.
|
||||||
|
5. **Open a PR** targeting `main` — squash merge only
|
||||||
|
|
||||||
## Prerequisites
|
## Standards
|
||||||
|
|
||||||
Contributors are expected to:
|
All contributions must follow MokoStandards:
|
||||||
|
|
||||||
* Have a working understanding of Joomla template structure.
|
| Standard | Reference |
|
||||||
* Be familiar with Git and GitHub pull request workflows.
|
|----------|-----------|
|
||||||
* Review repository governance documents prior to submitting changes.
|
| Coding Style | [coding-style-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md) |
|
||||||
* Set up the development environment using the provided tools.
|
| File Headers | [file-header-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/file-header-standards.md) |
|
||||||
|
| Branching | [branching-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/branching-strategy.md) |
|
||||||
|
| Merge Strategy | [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) |
|
||||||
|
| Scripting | [scripting-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/scripting-standards.md) |
|
||||||
|
|
||||||
### Quick Setup
|
## Version Bumping
|
||||||
|
|
||||||
For first-time contributors:
|
Every PR must bump the patch version in `README.md`. The `sync-version-on-merge` workflow propagates it to all file headers automatically on merge to `main`.
|
||||||
|
|
||||||
```bash
|
|
||||||
# Clone the repository
|
|
||||||
git clone https://github.com/mokoconsulting-tech/MokoCassiopeia.git
|
|
||||||
cd MokoCassiopeia
|
|
||||||
```
|
|
||||||
|
|
||||||
See [docs/QUICK_START.md](./docs/QUICK_START.md) for detailed setup instructions.
|
|
||||||
|
|
||||||
## Development Tools
|
|
||||||
|
|
||||||
The repository provides several tools to streamline development:
|
|
||||||
|
|
||||||
* **Pre-commit Hooks**: Automatic local validation before commits
|
|
||||||
|
|
||||||
## Contribution Workflow
|
|
||||||
|
|
||||||
1. Fork the repository.
|
|
||||||
2. Create a branch from the active development branch.
|
|
||||||
3. Make focused, minimal changes that address a single concern.
|
|
||||||
4. Submit a pull request with a clear description of intent and impact.
|
|
||||||
|
|
||||||
Direct commits to protected branches are not permitted.
|
|
||||||
|
|
||||||
## Branching and Versioning
|
|
||||||
|
|
||||||
* Development work occurs on designated development branches.
|
|
||||||
* Releases are produced from versioned branches following repository standards.
|
|
||||||
* Contributors should not bump version numbers unless explicitly requested.
|
|
||||||
|
|
||||||
## Coding and Formatting Standards
|
|
||||||
|
|
||||||
All contributions must:
|
|
||||||
|
|
||||||
* Follow Joomla coding standards where applicable.
|
|
||||||
* Conform to Moko Consulting repository standards for headers, metadata, and file structure.
|
|
||||||
* Avoid introducing tabs, inconsistent path separators, or non portable assumptions.
|
|
||||||
|
|
||||||
Automated checks may reject changes that do not meet these requirements.
|
|
||||||
|
|
||||||
## Documentation Standards
|
|
||||||
|
|
||||||
Documentation changes must:
|
|
||||||
|
|
||||||
* Include required metadata and revision history sections.
|
|
||||||
* Avoid embedding version numbers in revision history tables.
|
|
||||||
* Preserve existing structure unless a structural change is explicitly proposed.
|
|
||||||
|
|
||||||
## Commit Messages
|
|
||||||
|
|
||||||
Commit messages should:
|
|
||||||
|
|
||||||
* Be concise and descriptive.
|
|
||||||
* Focus on what changed and why.
|
|
||||||
* Avoid referencing internal issue trackers unless required.
|
|
||||||
|
|
||||||
## Reporting Issues
|
|
||||||
|
|
||||||
Bug reports and enhancement requests should be filed as GitHub issues and include:
|
|
||||||
|
|
||||||
* Clear reproduction steps or use cases.
|
|
||||||
* Expected versus actual behavior.
|
|
||||||
* Relevant environment details.
|
|
||||||
|
|
||||||
Security related issues must follow the process defined in SECURITY.md and must not be reported publicly.
|
|
||||||
|
|
||||||
## Review Process
|
|
||||||
|
|
||||||
All pull requests are subject to review. Review criteria include:
|
|
||||||
|
|
||||||
* Technical correctness
|
|
||||||
* Alignment with project goals
|
|
||||||
* Maintainability and clarity
|
|
||||||
* Risk introduced to release and update processes
|
|
||||||
|
|
||||||
Maintainers may request changes prior to approval.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
By contributing, you agree that your contributions will be licensed under GPL-3.0-or-later, consistent with the rest of the project.
|
By contributing, you agree that your contributions will be licensed under the [GPL-3.0-or-later](LICENSE) license.
|
||||||
|
|
||||||
## Code of Conduct
|
|
||||||
|
|
||||||
Participation in this project is governed by the Code of Conduct. Unacceptable behavior may result in contribution restrictions.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Metadata
|
|
||||||
|
|
||||||
* **Document:** CONTRIBUTING.md
|
|
||||||
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
|
|
||||||
* **Path:** /CONTRIBUTING.md
|
|
||||||
* **Owner:** Moko Consulting
|
|
||||||
* **Version:** 03.06.00
|
|
||||||
* **Status:** Active
|
|
||||||
* **Effective Date:** 2025-12-18
|
|
||||||
* **Last Reviewed:** 2025-12-18
|
|
||||||
|
|
||||||
## Revision History
|
|
||||||
|
|
||||||
| Date | Change Summary | Author |
|
|
||||||
| ---------- | ------------------------------------------------------------------------- | --------------- |
|
|
||||||
| 2025-12-18 | Initial publication of contribution guidelines and workflow expectations. | Moko Consulting |
|
|
||||||
|
|||||||
287
SECURITY.md
287
SECURITY.md
@@ -1,185 +1,240 @@
|
|||||||
<!--
|
<!--
|
||||||
Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
|
||||||
This file is part of a Moko Consulting project.
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
# FILE INFORMATION
|
This program is free software; you can redistribute it and/or modify
|
||||||
DEFGROUP: Joomla.Template
|
it under the terms of the GNU General Public License as published by
|
||||||
INGROUP: MokoCassiopeia.Governance
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
(at your option) any later version.
|
||||||
FILE: SECURITY.md
|
|
||||||
VERSION: 03.06.02
|
This program is distributed in the hope that it will be useful,
|
||||||
BRIEF: Security policy and vulnerability reporting process for MokoCassiopeia.
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
PATH: /SECURITY.md
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
NOTE: This policy is process oriented and does not replace secure engineering practices.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# FILE INFORMATION
|
||||||
|
DEFGROUP: [PROJECT_NAME]
|
||||||
|
INGROUP: [PROJECT_NAME].Documentation
|
||||||
|
REPO: [REPOSITORY_URL]
|
||||||
|
PATH: /SECURITY.md
|
||||||
|
VERSION: 04.02.08
|
||||||
|
BRIEF: Security vulnerability reporting and handling policy
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Security Policy
|
# Security Policy
|
||||||
|
|
||||||
This document defines how MokoCassiopeia handles vulnerability intake, triage, remediation, and disclosure. The objective is to reduce risk, protect downstream users, and preserve operational continuity with a verifiable audit trail.
|
## Purpose and Scope
|
||||||
|
|
||||||
## Scope
|
This document defines the security vulnerability reporting, response, and disclosure policy for [PROJECT_NAME] and all repositories governed by these standards. It establishes the authoritative process for responsible disclosure, assessment, remediation, and communication of security issues.
|
||||||
|
|
||||||
This policy applies to:
|
|
||||||
|
|
||||||
* Repository source code, workflows, scripts, and build artifacts.
|
|
||||||
* Release packaging (ZIP outputs) generated from the repository.
|
|
||||||
* Configuration and metadata used for distribution (for example manifests and update metadata).
|
|
||||||
|
|
||||||
Out of scope:
|
|
||||||
|
|
||||||
* Vulnerabilities in upstream Joomla core, third party extensions, or external infrastructure not controlled by this repository.
|
|
||||||
* Issues that require physical access to a host, compromised administrator credentials, or a compromised hosting provider, unless the repository materially increases impact.
|
|
||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
Security fixes are prioritized for:
|
Security updates are provided for the following versions:
|
||||||
|
|
||||||
* The latest released version.
|
| Version | Supported |
|
||||||
* The current development line when it is actively used for release engineering.
|
| ------- | ------------------ |
|
||||||
|
| [X.x.x] | :white_check_mark: |
|
||||||
|
| < [X.0] | :x: |
|
||||||
|
|
||||||
Backports may be provided based on impact, deployment footprint, and engineering capacity.
|
Only the current major version receives security updates. Users should upgrade to the latest supported version to receive security patches.
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
Use one of the following channels:
|
### Where to Report
|
||||||
|
|
||||||
* GitHub Security Advisories (preferred): use the repository security tab to submit a private report.
|
**DO NOT** create public GitHub issues for security vulnerabilities.
|
||||||
* Email: send details to `hello@mokoconsulting.tech` with subject `SECURITY: MokoCassiopeia vulnerability report`.
|
|
||||||
|
|
||||||
Do not file a public GitHub issue for suspected security vulnerabilities.
|
Report security vulnerabilities privately to:
|
||||||
|
|
||||||
### What to include
|
**Email**: `security@[DOMAIN]`
|
||||||
|
|
||||||
Provide enough detail to reproduce and triage:
|
**Subject Line**: `[SECURITY] Brief Description`
|
||||||
|
|
||||||
* A clear description of the vulnerability and expected impact.
|
### What to Include
|
||||||
* A minimal proof of concept or reproduction steps.
|
|
||||||
* Affected versions, configuration assumptions, and environment details.
|
|
||||||
* Any proposed mitigation or patch.
|
|
||||||
* Your preferred contact details for follow up.
|
|
||||||
|
|
||||||
## Triage and Response Targets
|
A complete vulnerability report should include:
|
||||||
|
|
||||||
The project operates with response targets aligned to practical delivery realities:
|
1. **Description**: Clear explanation of the vulnerability
|
||||||
|
2. **Impact**: Potential security impact and severity assessment
|
||||||
|
3. **Affected Versions**: Which versions are vulnerable
|
||||||
|
4. **Reproduction Steps**: Detailed steps to reproduce the issue
|
||||||
|
5. **Proof of Concept**: Code, configuration, or demonstration (if applicable)
|
||||||
|
6. **Suggested Fix**: Proposed remediation (if known)
|
||||||
|
7. **Disclosure Timeline**: Your expectations for public disclosure
|
||||||
|
|
||||||
* **Acknowledgement:** within 3 business days.
|
### Response Timeline
|
||||||
* **Initial triage:** within 10 business days.
|
|
||||||
* **Fix plan:** communicated once severity is confirmed.
|
|
||||||
|
|
||||||
These targets are not guarantees. Complex issues, supply chain considerations, and coordination with upstream vendors may extend timelines.
|
* **Initial Response**: Within 3 business days
|
||||||
|
* **Assessment Complete**: Within 7 business days
|
||||||
|
* **Fix Timeline**: Depends on severity (see below)
|
||||||
|
* **Disclosure**: Coordinated with reporter
|
||||||
|
|
||||||
## Severity Assessment
|
## Severity Classification
|
||||||
|
|
||||||
Issues are triaged based on business impact and technical exploitability, including:
|
Vulnerabilities are classified using the following severity levels:
|
||||||
|
|
||||||
* Remote exploitability and required privileges.
|
### Critical
|
||||||
* Data confidentiality, integrity, and availability impact.
|
* Remote code execution
|
||||||
* Likelihood of exploitation in typical Joomla deployments.
|
* Authentication bypass
|
||||||
* Exposure surface (public endpoints, administrator area, installation flows, and update mechanisms).
|
* Data breach or exposure of sensitive information
|
||||||
|
* **Fix Timeline**: 7 days
|
||||||
|
|
||||||
When appropriate, industry standard scoring such as CVSS may be used for internal prioritization.
|
### High
|
||||||
|
* Privilege escalation
|
||||||
|
* SQL injection or command injection
|
||||||
|
* Cross-site scripting (XSS) with significant impact
|
||||||
|
* **Fix Timeline**: 14 days
|
||||||
|
|
||||||
## Coordinated Disclosure
|
### Medium
|
||||||
|
* Information disclosure (limited scope)
|
||||||
|
* Denial of service
|
||||||
|
* Security misconfigurations with moderate impact
|
||||||
|
* **Fix Timeline**: 30 days
|
||||||
|
|
||||||
The project follows coordinated vulnerability disclosure:
|
### Low
|
||||||
|
* Security best practice violations
|
||||||
|
* Minor information leaks
|
||||||
|
* Issues requiring user interaction or complex preconditions
|
||||||
|
* **Fix Timeline**: 60 days or next release
|
||||||
|
|
||||||
* Reports are treated as confidential until remediation is available.
|
## Remediation Process
|
||||||
* A public advisory may be published once a fix is released.
|
|
||||||
* A reasonable embargo period is expected to enable patch distribution.
|
|
||||||
|
|
||||||
If you believe disclosure is time sensitive due to active exploitation, include that assessment and any supporting indicators.
|
1. **Acknowledgment**: Security team confirms receipt and begins investigation
|
||||||
|
2. **Assessment**: Vulnerability is validated, severity assigned, and impact analyzed
|
||||||
|
3. **Development**: Security patch is developed and tested
|
||||||
|
4. **Review**: Patch undergoes security review and validation
|
||||||
|
5. **Release**: Fixed version is released with security advisory
|
||||||
|
6. **Disclosure**: Public disclosure follows coordinated timeline
|
||||||
|
|
||||||
## Security Updates and Advisories
|
## Security Advisories
|
||||||
|
|
||||||
Security updates are distributed through:
|
Security advisories are published via:
|
||||||
|
|
||||||
* GitHub releases for the repository.
|
* GitHub Security Advisories
|
||||||
* GitHub Security Advisories when applicable.
|
* Release notes and CHANGELOG.md
|
||||||
|
* Security mailing list (when established)
|
||||||
|
|
||||||
Advisories may include:
|
Advisories include:
|
||||||
|
|
||||||
* Affected versions and fixed versions.
|
* CVE identifier (if applicable)
|
||||||
* Mitigations and workarounds when a fix is not immediately available.
|
* Severity rating
|
||||||
* Upgrade guidance.
|
* Affected versions
|
||||||
|
* Fixed versions
|
||||||
|
* Mitigation steps
|
||||||
|
* Attribution (with reporter consent)
|
||||||
|
|
||||||
## Dependencies and Supply Chain Controls
|
## Security Best Practices
|
||||||
|
|
||||||
The project aims to manage supply chain risk through:
|
For repositories adopting MokoStandards:
|
||||||
|
|
||||||
* Pinning and review of workflow dependencies where feasible.
|
### Required Controls
|
||||||
* Minimizing privileged GitHub token permissions.
|
|
||||||
* Validating build inputs prior to packaging releases.
|
|
||||||
|
|
||||||
If you identify a supply chain issue (for example compromised action, dependency confusion, or malicious upstream artifact), report it as a vulnerability.
|
* Enable GitHub security features (Dependabot, code scanning)
|
||||||
|
* Implement branch protection on `main`
|
||||||
|
* Require code review for all changes
|
||||||
|
* Enforce signed commits (recommended)
|
||||||
|
* Use secrets management (never commit credentials)
|
||||||
|
* Maintain security documentation
|
||||||
|
* Follow secure coding standards defined in `/docs/policy/`
|
||||||
|
|
||||||
## Secure Development and CI Expectations
|
### CI/CD Security
|
||||||
|
|
||||||
Security posture is reinforced through operational controls:
|
* Validate all inputs
|
||||||
|
* Sanitize outputs
|
||||||
|
* Use least privilege access
|
||||||
|
* Pin dependencies with hash verification
|
||||||
|
* Scan for vulnerabilities in dependencies
|
||||||
|
* Audit third-party actions and tools
|
||||||
|
|
||||||
* CI validation for packaging inputs and manifest integrity.
|
#### Automated Security Scanning
|
||||||
* Consistent path normalization and whitespace hygiene checks where required for release correctness.
|
|
||||||
* Least privilege for GitHub Actions permissions.
|
|
||||||
|
|
||||||
### Template Security Features
|
All repositories MUST implement:
|
||||||
|
|
||||||
**Custom Head Content Injection**
|
**CodeQL Analysis**:
|
||||||
|
* Enabled for all supported languages (Python, JavaScript, TypeScript, Java, C/C++, C#, Go, Ruby)
|
||||||
|
* Runs on: push to main, pull requests, weekly schedule
|
||||||
|
* Query sets: `security-extended` and `security-and-quality`
|
||||||
|
* Configuration: `.github/workflows/codeql-analysis.yml`
|
||||||
|
|
||||||
The template provides Custom Head Code fields (`custom_head_start` and `custom_head_end`) that allow administrators to inject custom HTML, CSS, and JavaScript code. This is an intentional feature for:
|
**Dependabot Security Updates**:
|
||||||
|
* Weekly scans for vulnerable dependencies
|
||||||
|
* Automated pull requests for security patches
|
||||||
|
* Configuration: `.github/dependabot.yml`
|
||||||
|
|
||||||
* Adding analytics scripts (Google Analytics, Google Tag Manager)
|
**Secret Scanning**:
|
||||||
* Custom meta tags
|
* Enabled by default with push protection
|
||||||
* Third-party integrations
|
* Prevents accidental credential commits
|
||||||
* Custom styling
|
* Partner patterns enabled
|
||||||
|
|
||||||
**Security Considerations:**
|
**Dependency Review**:
|
||||||
|
* Required for all pull requests
|
||||||
|
* Blocks introduction of known vulnerable dependencies
|
||||||
|
* Automatic license compliance checking
|
||||||
|
|
||||||
* These fields use `filter="raw"` to allow HTML/JS injection
|
See [Security Scanning Policy](docs/policy/security-scanning.md) for detailed requirements.
|
||||||
* **Access is restricted to Joomla administrators only** via template configuration
|
|
||||||
* This is not an XSS vulnerability as it requires administrator privileges
|
|
||||||
* Administrators should only add trusted code from verified sources
|
|
||||||
* Regular security audits should review custom head content
|
|
||||||
|
|
||||||
This policy does not guarantee that all vulnerabilities will be prevented. It defines how risk is managed when issues are discovered.
|
### Dependency Management
|
||||||
|
|
||||||
## Safe Harbor
|
* Keep dependencies up to date
|
||||||
|
* Monitor security advisories for dependencies
|
||||||
|
* Remove unused dependencies
|
||||||
|
* Audit new dependencies before adoption
|
||||||
|
* Document security-critical dependencies
|
||||||
|
|
||||||
The project supports good faith security research. When you:
|
## Compliance and Governance
|
||||||
|
|
||||||
* Avoid privacy violations, data destruction, and service disruption.
|
This security policy is binding for all repositories governed by MokoStandards. Deviations require documented justification and approval from the Security Owner.
|
||||||
* Limit testing to systems you own or have explicit permission to test.
|
|
||||||
* Provide a reasonable window for coordinated disclosure.
|
|
||||||
|
|
||||||
Then the project will treat your report as a constructive security contribution.
|
Security policies are reviewed and updated at least annually or following significant security incidents.
|
||||||
|
|
||||||
Jurisdiction note: this repository is managed from Tennessee, USA. This note is informational only and does not constitute legal advice.
|
## Attribution and Recognition
|
||||||
|
|
||||||
## Public Communications
|
We acknowledge and appreciate responsible disclosure. With your permission, we will:
|
||||||
|
|
||||||
Only maintainers will publish security advisories or public statements for confirmed vulnerabilities. Public communication will focus on actionable remediation and operational risk reduction.
|
* Credit you in security advisories
|
||||||
|
* List you in CHANGELOG.md for the fix release
|
||||||
|
* Recognize your contribution publicly (if desired)
|
||||||
|
|
||||||
## Acknowledgements
|
## Contact and Escalation
|
||||||
|
|
||||||
If you want credit, include the name or handle to list in an advisory. If you prefer anonymity, state that explicitly.
|
* **Security Team**: security@[DOMAIN]
|
||||||
|
* **Primary Contact**: [CONTACT_EMAIL]
|
||||||
|
* **Escalation**: For urgent matters requiring immediate attention, contact the maintainer directly via GitHub
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
The following are explicitly out of scope:
|
||||||
|
|
||||||
|
* Issues in third-party dependencies (report directly to maintainers)
|
||||||
|
* Social engineering attacks
|
||||||
|
* Physical security issues
|
||||||
|
* Denial of service via resource exhaustion without amplification
|
||||||
|
* Issues requiring physical access to systems
|
||||||
|
* Theoretical vulnerabilities without proof of exploitability
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Metadata
|
## Metadata
|
||||||
|
|
||||||
* **Document:** SECURITY.md
|
| Field | Value |
|
||||||
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
|
| ------------ | ----------------------------------------------- |
|
||||||
* **Path:** /SECURITY.md
|
| Document | Security Policy |
|
||||||
* **Owner:** Moko Consulting
|
| Path | /SECURITY.md |
|
||||||
* **Version:** 03.06.00
|
| Repository | [REPOSITORY_URL] |
|
||||||
* **Status:** Active
|
| Owner | [OWNER_NAME] |
|
||||||
* **Effective Date:** 2025-12-18
|
| Scope | Security vulnerability handling |
|
||||||
* **Last Reviewed:** 2025-12-18
|
| Applies To | All repositories governed by MokoStandards |
|
||||||
|
| Status | Active |
|
||||||
|
| Effective | [YYYY-MM-DD] |
|
||||||
|
|
||||||
## Revision History
|
## Revision History
|
||||||
|
|
||||||
| Date | Change Summary | Author |
|
| Date | Change Description | Author |
|
||||||
| ---------- | ------------------------------------------------------------------------------------------------ | --------------- |
|
| ---------- | ------------------------------------------------- | --------------- |
|
||||||
| 2026-01-30 | Added Template Security Features section documenting custom head content injection controls. | Copilot Agent |
|
| [YYYY-MM-DD] | Initial creation | [AUTHOR_NAME] |
|
||||||
| 2025-12-18 | Initial publication of security policy, intake channels, triage targets, and disclosure process. | Moko Consulting |
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.1",
|
"php": ">=8.1",
|
||||||
"mokoconsulting-tech/enterprise": "^4.0"
|
"mokoconsulting-tech/enterprise": "dev-version/04.02.00"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^10.5",
|
"phpunit/phpunit": "^10.5",
|
||||||
|
|||||||
Reference in New Issue
Block a user