diff --git a/.github/CLAUDE.md b/.github/CLAUDE.md index e991394..4e7b705 100644 --- a/.github/CLAUDE.md +++ b/.github/CLAUDE.md @@ -122,13 +122,13 @@ BRIEF: One-line description ### Joomla Version Alignment -The version in `README.md` **must always match** the `` tag in `manifest.xml` and the latest entry in `update.xml`. The `make release` command / release workflow updates all three automatically. +The version in `README.md` **must always match** the `` tag in `manifest.xml` and the latest entry in `updates.xml`. The `make release` command / release workflow updates all three automatically. ```xml 01.02.04 - @@ -154,7 +154,7 @@ The version in `README.md` **must always match** the `` tag in `manifes ``` MokoCassiopeia/ ├── manifest.xml # Joomla installer manifest (root — required) -├── update.xml # Update server manifest (root — required, see below) +├── updates.xml # Update server manifest (root — required, see below) ├── site/ # Frontend (site) code │ ├── controller.php │ ├── controllers/ @@ -183,22 +183,22 @@ MokoCassiopeia/ --- -## update.xml — Required in Repo Root +## updates.xml — Required in Repo Root -`update.xml` **must exist at the repository root**. It is the Joomla update server manifest that allows Joomla installations to check for new versions of this extension. +`updates.xml` **must exist at the repository root**. It is the Joomla update server manifest that allows Joomla installations to check for new versions of this extension. The `manifest.xml` must reference it via: ```xml - https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/update.xml + https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/updates.xml ``` **Rules:** -- Every release must prepend a new `` block at the top of `update.xml` — old entries must be preserved below. -- The `` in `update.xml` must exactly match `` in `manifest.xml` and the version in `README.md`. +- Every release must prepend a new `` block at the top of `updates.xml` — old entries must be preserved below. +- The `` in `updates.xml` must exactly match `` in `manifest.xml` and the version in `README.md`. - The `` must be a publicly accessible direct download link (GitHub Releases asset URL). - `` — the backslash is a **literal backslash character** in the XML attribute value; Joomla's update-server parser treats the value as a regular expression, so `\.` matches a literal dot and `[0-9]+` matches one or more digits. Do not double-escape it. @@ -207,8 +207,8 @@ The `manifest.xml` must reference it via: ## manifest.xml Rules - Lives at the repo root as `manifest.xml` (not inside `site/` or `admin/`). -- `` tag must be kept in sync with `README.md` version and `update.xml`. -- Must include `` block pointing to this repo's `update.xml`. +- `` tag must be kept in sync with `README.md` version and `updates.xml`. +- Must include `` block pointing to this repo's `updates.xml`. - Must include `` and `` sections. - Joomla 4.x requires `Moko\{{EXTENSION_NAME}}` for namespaced extensions. @@ -286,8 +286,8 @@ Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `d | Change type | Documentation to update | |-------------|------------------------| | New or renamed PHP class/method | PHPDoc block; `docs/api/` entry | -| New or changed manifest.xml | Update `update.xml` version; bump README.md version | -| New release | Prepend `` block to `update.xml`; update CHANGELOG.md; bump README.md version | +| New or changed manifest.xml | Update `updates.xml` version; bump README.md version | +| New release | Prepend `` block to `updates.xml`; update CHANGELOG.md; bump README.md version | | New or changed workflow | `docs/workflows/.md` | | Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block | | **Every PR** | **Bump the patch version** — increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it | @@ -301,4 +301,4 @@ Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `d - Never add `defined('_JEXEC') or die;` to CLI scripts or model tests — only to web-accessible PHP files - Never hardcode version numbers in body text — update `README.md` and let automation propagate - Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows — always use `secrets.GH_TOKEN` -- Never let `manifest.xml` version, `update.xml` version, and `README.md` version go out of sync +- Never let `manifest.xml` version, `updates.xml` version, and `README.md` version go out of sync diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 93f049a..c70eb15 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,8 +8,26 @@ # Combined with branch protection (require PR reviews), this prevents # unauthorized modifications to workflows, configs, and governance files. -# ── Workflows (synced from MokoStandards — must not be manually edited) ── -/.github/workflows/ @jmiller-moko +# ── Synced workflows (managed by MokoStandards — do not edit manually) ──── +/.github/workflows/deploy-dev.yml @jmiller-moko +/.github/workflows/deploy-demo.yml @jmiller-moko +/.github/workflows/deploy-rs.yml @jmiller-moko +/.github/workflows/auto-release.yml @jmiller-moko +/.github/workflows/auto-dev-issue.yml @jmiller-moko +/.github/workflows/auto-assign.yml @jmiller-moko +/.github/workflows/sync-version-on-merge.yml @jmiller-moko +/.github/workflows/enterprise-firewall-setup.yml @jmiller-moko +/.github/workflows/repository-cleanup.yml @jmiller-moko +/.github/workflows/standards-compliance.yml @jmiller-moko +/.github/workflows/codeql-analysis.yml @jmiller-moko +/.github/workflows/repo_health.yml @jmiller-moko +/.github/workflows/ci-joomla.yml @jmiller-moko +/.github/workflows/update-server.yml @jmiller-moko +/.github/workflows/deploy-manual.yml @jmiller-moko +/.github/workflows/ci-dolibarr.yml @jmiller-moko +/.github/workflows/publish-to-mokodolimods.yml @jmiller-moko +/.github/workflows/changelog-validation.yml @jmiller-moko +# Custom workflows in .github/workflows/ not listed above are repo-owned. # ── GitHub configuration ───────────────────────────────────────────────── /.github/ISSUE_TEMPLATE/ @jmiller-moko @@ -23,7 +41,7 @@ /composer.json @jmiller-moko /phpstan.neon @jmiller-moko /Makefile @jmiller-moko -/.ftp_ignore @jmiller-moko +/.ftpignore @jmiller-moko /.gitignore @jmiller-moko /.gitattributes @jmiller-moko /.editorconfig @jmiller-moko diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e991394..4e7b705 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -122,13 +122,13 @@ BRIEF: One-line description ### Joomla Version Alignment -The version in `README.md` **must always match** the `` tag in `manifest.xml` and the latest entry in `update.xml`. The `make release` command / release workflow updates all three automatically. +The version in `README.md` **must always match** the `` tag in `manifest.xml` and the latest entry in `updates.xml`. The `make release` command / release workflow updates all three automatically. ```xml 01.02.04 - @@ -154,7 +154,7 @@ The version in `README.md` **must always match** the `` tag in `manifes ``` MokoCassiopeia/ ├── manifest.xml # Joomla installer manifest (root — required) -├── update.xml # Update server manifest (root — required, see below) +├── updates.xml # Update server manifest (root — required, see below) ├── site/ # Frontend (site) code │ ├── controller.php │ ├── controllers/ @@ -183,22 +183,22 @@ MokoCassiopeia/ --- -## update.xml — Required in Repo Root +## updates.xml — Required in Repo Root -`update.xml` **must exist at the repository root**. It is the Joomla update server manifest that allows Joomla installations to check for new versions of this extension. +`updates.xml` **must exist at the repository root**. It is the Joomla update server manifest that allows Joomla installations to check for new versions of this extension. The `manifest.xml` must reference it via: ```xml - https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/update.xml + https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/updates.xml ``` **Rules:** -- Every release must prepend a new `` block at the top of `update.xml` — old entries must be preserved below. -- The `` in `update.xml` must exactly match `` in `manifest.xml` and the version in `README.md`. +- Every release must prepend a new `` block at the top of `updates.xml` — old entries must be preserved below. +- The `` in `updates.xml` must exactly match `` in `manifest.xml` and the version in `README.md`. - The `` must be a publicly accessible direct download link (GitHub Releases asset URL). - `` — the backslash is a **literal backslash character** in the XML attribute value; Joomla's update-server parser treats the value as a regular expression, so `\.` matches a literal dot and `[0-9]+` matches one or more digits. Do not double-escape it. @@ -207,8 +207,8 @@ The `manifest.xml` must reference it via: ## manifest.xml Rules - Lives at the repo root as `manifest.xml` (not inside `site/` or `admin/`). -- `` tag must be kept in sync with `README.md` version and `update.xml`. -- Must include `` block pointing to this repo's `update.xml`. +- `` tag must be kept in sync with `README.md` version and `updates.xml`. +- Must include `` block pointing to this repo's `updates.xml`. - Must include `` and `` sections. - Joomla 4.x requires `Moko\{{EXTENSION_NAME}}` for namespaced extensions. @@ -286,8 +286,8 @@ Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `d | Change type | Documentation to update | |-------------|------------------------| | New or renamed PHP class/method | PHPDoc block; `docs/api/` entry | -| New or changed manifest.xml | Update `update.xml` version; bump README.md version | -| New release | Prepend `` block to `update.xml`; update CHANGELOG.md; bump README.md version | +| New or changed manifest.xml | Update `updates.xml` version; bump README.md version | +| New release | Prepend `` block to `updates.xml`; update CHANGELOG.md; bump README.md version | | New or changed workflow | `docs/workflows/.md` | | Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block | | **Every PR** | **Bump the patch version** — increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it | @@ -301,4 +301,4 @@ Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `d - Never add `defined('_JEXEC') or die;` to CLI scripts or model tests — only to web-accessible PHP files - Never hardcode version numbers in body text — update `README.md` and let automation propagate - Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows — always use `secrets.GH_TOKEN` -- Never let `manifest.xml` version, `update.xml` version, and `README.md` version go out of sync +- Never let `manifest.xml` version, `updates.xml` version, and `README.md` version go out of sync diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b94720f..667ae72 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,7 @@ # INGROUP: MokoStandards.Security # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /.github/dependabot.yml -# VERSION: 01.00.00 +# VERSION: 03.09.03 # BRIEF: Dependabot configuration for automated dependency updates and security patches # NOTE: Monitors GitHub Actions for vulnerabilities and keeps ecosystem secure diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml index 3752b66..d0b70f6 100644 --- a/.github/workflows/auto-assign.yml +++ b/.github/workflows/auto-assign.yml @@ -6,7 +6,7 @@ # INGROUP: MokoStandards.Workflows.Shared # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /.github/workflows/auto-assign.yml -# VERSION: 04.05.11 +# VERSION: 04.06.00 # BRIEF: Auto-assign jmiller-moko to unassigned issues and PRs every 15 minutes name: Auto-Assign Issues & PRs diff --git a/.github/workflows/auto-dev-issue.yml b/.github/workflows/auto-dev-issue.yml index 38c6a53..9b5fbe2 100644 --- a/.github/workflows/auto-dev-issue.yml +++ b/.github/workflows/auto-dev-issue.yml @@ -9,7 +9,7 @@ # INGROUP: MokoStandards.Automation # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/auto-dev-issue.yml.template -# VERSION: 04.05.13 +# VERSION: 04.06.00 # BRIEF: Auto-create tracking issue with sub-issues for dev/rc branch workflow # NOTE: Synced via bulk-repo-sync to .github/workflows/auto-dev-issue.yml in all governed repos. @@ -39,7 +39,10 @@ jobs: runs-on: ubuntu-latest if: >- (github.event_name == 'workflow_dispatch') || - (github.event.ref_type == 'branch' && startsWith(github.event.ref, 'rc/')) + (github.event.ref_type == 'branch' && + (startsWith(github.event.ref, 'rc/') || + startsWith(github.event.ref, 'alpha/') || + startsWith(github.event.ref, 'beta/'))) steps: - name: Create tracking issue and sub-issues @@ -62,6 +65,16 @@ jobs: BRANCH_TYPE="Release Candidate" LABEL_TYPE="type: release" TITLE_PREFIX="rc" + elif [[ "$BRANCH" == beta/* ]]; then + VERSION="${BRANCH#beta/}" + BRANCH_TYPE="Beta" + LABEL_TYPE="type: release" + TITLE_PREFIX="beta" + elif [[ "$BRANCH" == alpha/* ]]; then + VERSION="${BRANCH#alpha/}" + BRANCH_TYPE="Alpha" + LABEL_TYPE="type: release" + TITLE_PREFIX="alpha" else VERSION="${BRANCH#dev/}" BRANCH_TYPE="Development" @@ -80,14 +93,20 @@ jobs: exit 0 fi - # ── Define sub-issues for the dev workflow ──────────────────────── + # ── Define sub-issues for the workflow ───────────────────────── if [[ "$BRANCH" == rc/* ]]; then SUB_ISSUES=( "RC Testing|Verify all features work on rc branch|type: test,release-candidate" - "Regression Testing|Run full regression suite before merge to main|type: test,release-candidate" + "Regression Testing|Run full regression suite before merge|type: test,release-candidate" "Version Bump|Bump version in README.md and all headers|type: version,release-candidate" "Changelog Update|Update CHANGELOG.md with release notes|documentation,release-candidate" - "Merge to Main|Create PR from rc branch to main|type: release,needs-review" + "Merge to Version Branch|Create PR to version/XX|type: release,needs-review" + ) + elif [[ "$BRANCH" == alpha/* ]] || [[ "$BRANCH" == beta/* ]]; then + SUB_ISSUES=( + "Testing|Verify features on ${BRANCH_TYPE} branch|type: test,status: in-progress" + "Bug Fixes|Fix issues found during ${BRANCH_TYPE} testing|type: bug,status: pending" + "Promote to Next Stage|Create PR to promote to next release stage|type: release,needs-review" ) else SUB_ISSUES=( @@ -156,22 +175,26 @@ jobs: done fi - # ── RC: Create or update release-candidate release ────────────── - if [[ "$BRANCH" == rc/* ]]; then - RELEASE_TAG="release-candidate" - EXISTING=$(gh release view "$RELEASE_TAG" --json tagName -q .tagName 2>/dev/null || true) + # ── Create or update prerelease for alpha/beta/rc ──────────────── + if [[ "$BRANCH" == rc/* ]] || [[ "$BRANCH" == alpha/* ]] || [[ "$BRANCH" == beta/* ]]; then + case "$BRANCH_TYPE" in + Alpha) RELEASE_TAG="alpha" ;; + Beta) RELEASE_TAG="beta" ;; + "Release Candidate") RELEASE_TAG="release-candidate" ;; + esac + EXISTING=$(gh release view "$RELEASE_TAG" --json tagName -q .tagName 2>/dev/null || true) if [ -z "$EXISTING" ]; then gh release create "$RELEASE_TAG" \ - --title "release-candidate (${VERSION})" \ - --notes "## Release Candidate ${VERSION}\n\nRC branch: \`${BRANCH}\`\nTracking issue: ${PARENT_URL}" \ + --title "${RELEASE_TAG} (${VERSION})" \ + --notes "## ${BRANCH_TYPE} ${VERSION}\n\nBranch: \`${BRANCH}\`\nTracking issue: ${PARENT_URL}" \ --prerelease \ --target main 2>/dev/null || true - echo "RC release created: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY + echo "${BRANCH_TYPE} release created: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY else gh release edit "$RELEASE_TAG" \ - --title "release-candidate (${VERSION})" --prerelease 2>/dev/null || true - echo "RC release updated: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY + --title "${RELEASE_TAG} (${VERSION})" --prerelease 2>/dev/null || true + echo "${BRANCH_TYPE} release updated: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY fi fi diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 3e1caf3..e5d4c49 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -7,7 +7,7 @@ # INGROUP: MokoStandards.Release # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/joomla/auto-release.yml.template -# VERSION: 04.05.13 +# VERSION: 04.06.00 # BRIEF: Joomla build & release — ZIP package, updates.xml, SHA-256 checksum # # +========================================================================+ @@ -316,54 +316,47 @@ jobs: DOWNLOAD_URL="https://github.com/${REPO}/releases/download/v${VERSION}/${EXT_ELEMENT}-${VERSION}.zip" INFO_URL="https://github.com/${REPO}/releases/tag/v${VERSION}" - # -- Build stable entry ────────────────────────────────────── - STABLE_ENTRY=$(cat < - ${EXT_NAME} - ${EXT_NAME} update - ${EXT_ELEMENT} - ${EXT_TYPE} - ${VERSION} - $([ -n "$CLIENT_TAG" ] && echo " ${CLIENT_TAG}") - $([ -n "$FOLDER_TAG" ] && echo " ${FOLDER_TAG}") - - stable - - ${INFO_URL} - - ${DOWNLOAD_URL} - - ${TARGET_PLATFORM} - $([ -n "$PHP_TAG" ] && echo " ${PHP_TAG}") - Moko Consulting - https://mokoconsulting.tech - -XMLEOF -) + # -- Build stable entry to temp file ───────────────────────── + { + printf '%s\n' ' ' + printf '%s\n' " ${EXT_NAME}" + printf '%s\n' " ${EXT_NAME} update" + printf '%s\n' " ${EXT_ELEMENT}" + printf '%s\n' " ${EXT_TYPE}" + printf '%s\n' " ${VERSION}" + [ -n "$CLIENT_TAG" ] && printf '%s\n' " ${CLIENT_TAG}" + [ -n "$FOLDER_TAG" ] && printf '%s\n' " ${FOLDER_TAG}" + printf '%s\n' ' ' + printf '%s\n' ' stable' + printf '%s\n' ' ' + printf '%s\n' " ${INFO_URL}" + printf '%s\n' ' ' + printf '%s\n' " ${DOWNLOAD_URL}" + printf '%s\n' ' ' + printf '%s\n' " ${TARGET_PLATFORM}" + [ -n "$PHP_TAG" ] && printf '%s\n' " ${PHP_TAG}" + printf '%s\n' ' Moko Consulting' + printf '%s\n' ' https://mokoconsulting.tech' + printf '%s\n' ' ' + } > /tmp/stable_entry.xml # -- Write updates.xml preserving dev/rc entries ────────────── - # Extract existing dev and rc entries if present RC_ENTRY="" DEV_ENTRY="" if [ -f "updates.xml" ]; then - RC_ENTRY=$(python3 -c " -import re -with open('updates.xml') as f: c = f.read() -m = re.search(r'( .*?rc.*?)', c, re.DOTALL) -if m: print(m.group(1)) -" 2>/dev/null || true) - DEV_ENTRY=$(python3 -c " -import re -with open('updates.xml') as f: c = f.read() -m = re.search(r'( .*?development.*?)', c, re.DOTALL) -if m: print(m.group(1)) -" 2>/dev/null || true) + printf 'import re\n' > /tmp/extract.py + printf 'with open("updates.xml") as f: c = f.read()\n' >> /tmp/extract.py + printf 'import sys; tag = sys.argv[1]\n' >> /tmp/extract.py + printf 'm = re.search(r"( .*?" + re.escape(tag) + r".*?)", c, re.DOTALL)\n' >> /tmp/extract.py + printf 'if m: print(m.group(1))\n' >> /tmp/extract.py + RC_ENTRY=$(python3 /tmp/extract.py rc 2>/dev/null || true) + DEV_ENTRY=$(python3 /tmp/extract.py development 2>/dev/null || true) fi { printf '%s\n' '' printf '%s\n' '' - echo "$STABLE_ENTRY" + cat /tmp/stable_entry.xml [ -n "$RC_ENTRY" ] && echo "$RC_ENTRY" [ -n "$DEV_ENTRY" ] && echo "$DEV_ENTRY" printf '%s\n' '' @@ -484,7 +477,7 @@ if m: print(m.group(1)) [ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ — skipping package"; exit 0; } cd "$SOURCE_DIR" - zip -r "/tmp/${PACKAGE_NAME}" . + zip -r "/tmp/${PACKAGE_NAME}" . -x '.ftpignore' cd .. FILESIZE=$(stat -c%s "/tmp/${PACKAGE_NAME}" 2>/dev/null || stat -f%z "/tmp/${PACKAGE_NAME}" 2>/dev/null || echo "unknown") diff --git a/.github/workflows/auto-update-sha.yml b/.github/workflows/auto-update-sha.yml index 713393e..bbcfaaf 100644 --- a/.github/workflows/auto-update-sha.yml +++ b/.github/workflows/auto-update-sha.yml @@ -5,7 +5,7 @@ # INGROUP: MokoCassiopeia.Automation # REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia # PATH: /.github/workflows/auto-update-sha.yml -# VERSION: 01.00.00 +# VERSION: 03.09.03 # BRIEF: Automatically update SHA-256 hash in updates.xml after release # NOTE: Ensures updates.xml stays synchronized with release packages diff --git a/.github/workflows/changelog-validation.yml b/.github/workflows/changelog-validation.yml index 9ed880a..e2ec667 100644 --- a/.github/workflows/changelog-validation.yml +++ b/.github/workflows/changelog-validation.yml @@ -9,7 +9,7 @@ # INGROUP: MokoStandards.CI # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/changelog-validation.yml.template -# VERSION: 04.05.13 +# VERSION: 04.06.00 # BRIEF: Validates CHANGELOG.md format and version consistency # NOTE: Deployed to .github/workflows/changelog-validation.yml in governed repos. diff --git a/.github/workflows/ci-joomla.yml b/.github/workflows/ci-joomla.yml index 6281236..01e1edb 100644 --- a/.github/workflows/ci-joomla.yml +++ b/.github/workflows/ci-joomla.yml @@ -9,7 +9,7 @@ # INGROUP: MokoStandards.CI # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/joomla/ci-joomla.yml.template -# VERSION: 04.05.13 +# VERSION: 04.06.00 # BRIEF: CI workflow for Joomla extensions — lint, validate, test # NOTE: Deployed to .github/workflows/ci-joomla.yml in governed Joomla extension repos. diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3abfb02..1639497 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,7 +9,7 @@ # INGROUP: MokoStandards.Security # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/generic/codeql-analysis.yml.template -# VERSION: 04.05.00 +# VERSION: 03.09.03 # BRIEF: CodeQL security scanning workflow (generic — all repo types) # NOTE: Deployed to .github/workflows/codeql-analysis.yml in governed repos. # CodeQL does not support PHP directly; JavaScript scans JSON/YAML/shell. diff --git a/.github/workflows/deploy-manual.yml b/.github/workflows/deploy-manual.yml new file mode 100644 index 0000000..e127f0e --- /dev/null +++ b/.github/workflows/deploy-manual.yml @@ -0,0 +1,132 @@ +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: GitHub.Workflow +# INGROUP: MokoStandards.Deploy +# REPO: https://github.com/mokoconsulting-tech/MokoStandards +# PATH: /templates/workflows/joomla/deploy-manual.yml.template +# VERSION: 04.06.00 +# BRIEF: Manual SFTP deploy to dev server for Joomla repos +# NOTE: Joomla repos use update.xml for distribution. This is for manual +# dev server testing only — triggered via workflow_dispatch. + +name: Deploy to Dev (Manual) + +on: + workflow_dispatch: + inputs: + clear_remote: + description: 'Delete all remote files before uploading' + required: false + default: 'false' + type: boolean + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +permissions: + contents: read + +jobs: + deploy: + name: SFTP Deploy to Dev + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0 + with: + php-version: '8.2' + extensions: json, ssh2 + tools: composer + coverage: none + + - 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 --quiet \ + "https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \ + /tmp/mokostandards 2>/dev/null || true + if [ -d "/tmp/mokostandards" ] && [ -f "/tmp/mokostandards/composer.json" ]; then + cd /tmp/mokostandards && composer install --no-dev --no-interaction --quiet 2>/dev/null || true + fi + + - name: Check FTP configuration + id: check + env: + HOST: ${{ vars.DEV_FTP_HOST }} + PATH_VAR: ${{ vars.DEV_FTP_PATH }} + SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} + PORT: ${{ vars.DEV_FTP_PORT }} + run: | + if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then + echo "DEV_FTP_HOST or DEV_FTP_PATH not configured — cannot deploy" + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "skip=false" >> "$GITHUB_OUTPUT" + echo "host=$HOST" >> "$GITHUB_OUTPUT" + + REMOTE="${PATH_VAR%/}" + [ -n "$SUFFIX" ] && REMOTE="${REMOTE}/${SUFFIX#/}" + echo "remote=$REMOTE" >> "$GITHUB_OUTPUT" + + [ -z "$PORT" ] && PORT="22" + echo "port=$PORT" >> "$GITHUB_OUTPUT" + + - name: Deploy via SFTP + if: steps.check.outputs.skip != 'true' + env: + SFTP_KEY: ${{ secrets.DEV_FTP_KEY }} + SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }} + SFTP_USER: ${{ vars.DEV_FTP_USERNAME }} + run: | + SOURCE_DIR="src" + [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" + [ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ — nothing to deploy"; exit 0; } + + printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \ + "${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \ + > /tmp/sftp-config.json + + if [ -n "$SFTP_KEY" ]; then + echo "$SFTP_KEY" > /tmp/deploy_key + chmod 600 /tmp/deploy_key + printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json + else + printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json + fi + + DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json) + [ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote) + + PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true) + if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards/api/deploy/deploy-joomla.php" ]; then + php /tmp/mokostandards/api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}" + else + php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}" + fi + + rm -f /tmp/deploy_key /tmp/sftp-config.json + + - name: Summary + if: always() + run: | + if [ "${{ steps.check.outputs.skip }}" = "true" ]; then + echo "### Deploy Skipped — FTP not configured" >> $GITHUB_STEP_SUMMARY + else + echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/enterprise-firewall-setup.yml b/.github/workflows/enterprise-firewall-setup.yml index b6cf7ab..46ef7d2 100644 --- a/.github/workflows/enterprise-firewall-setup.yml +++ b/.github/workflows/enterprise-firewall-setup.yml @@ -22,7 +22,7 @@ # INGROUP: MokoStandards.Firewall # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/enterprise-firewall-setup.yml.template -# VERSION: 04.05.13 +# VERSION: 04.06.00 # 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. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ba0908..4af1d59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ # INGROUP: MokoCassiopeia.Release # REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia # PATH: /.github/workflows/release.yml -# VERSION: 01.00.00 +# VERSION: 03.09.03 # BRIEF: Automated release workflow for MokoCassiopeia Joomla template # NOTE: Creates release packages and publishes to GitHub Releases diff --git a/.github/workflows/repo_health.yml b/.github/workflows/repo_health.yml index 885203a..73308be 100644 --- a/.github/workflows/repo_health.yml +++ b/.github/workflows/repo_health.yml @@ -10,7 +10,7 @@ # INGROUP: MokoStandards.Validation # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /.github/workflows/repo_health.yml -# VERSION: 04.05.00 +# VERSION: 04.06.00 # BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts. # NOTE: Field is user-managed. # ============================================================================ diff --git a/.github/workflows/repository-cleanup.yml b/.github/workflows/repository-cleanup.yml index b078061..6fbf6e4 100644 --- a/.github/workflows/repository-cleanup.yml +++ b/.github/workflows/repository-cleanup.yml @@ -9,7 +9,7 @@ # INGROUP: MokoStandards.Maintenance # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/repository-cleanup.yml.template -# VERSION: 04.05.13 +# VERSION: 04.06.00 # 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. # Runs on the 1st and 15th of each month at 6:00 AM UTC, and on manual dispatch. diff --git a/.github/workflows/standards-compliance.yml b/.github/workflows/standards-compliance.yml index 773279d..79aaedd 100644 --- a/.github/workflows/standards-compliance.yml +++ b/.github/workflows/standards-compliance.yml @@ -5,7 +5,7 @@ # INGROUP: MokoStandards.Compliance # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /.github/workflows/standards-compliance.yml -# VERSION: 04.05.00 +# VERSION: 04.06.00 # BRIEF: MokoStandards compliance validation workflow # NOTE: Validates repository structure, documentation, and coding standards diff --git a/.github/workflows/sync-version-on-merge.yml b/.github/workflows/sync-version-on-merge.yml index 052a7ca..60715f6 100644 --- a/.github/workflows/sync-version-on-merge.yml +++ b/.github/workflows/sync-version-on-merge.yml @@ -9,7 +9,7 @@ # INGROUP: MokoStandards.Automation # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/sync-version-on-merge.yml.template -# VERSION: 04.05.13 +# VERSION: 04.06.00 # 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. # README.md is the single source of truth for the repository version. diff --git a/.github/workflows/update-server.yml b/.github/workflows/update-server.yml index 28a6dd7..f0830c2 100644 --- a/.github/workflows/update-server.yml +++ b/.github/workflows/update-server.yml @@ -7,7 +7,7 @@ # INGROUP: MokoStandards.Joomla # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/joomla/update-server.yml.template -# VERSION: 04.05.13 +# VERSION: 04.06.00 # BRIEF: Update Joomla update server XML feed with stable/rc/dev entries # # Writes updates.xml with multiple entries: @@ -23,6 +23,8 @@ on: push: branches: - 'dev/**' + - 'alpha/**' + - 'beta/**' - 'rc/**' paths: - 'src/**' @@ -30,12 +32,14 @@ on: workflow_dispatch: inputs: stability: - description: 'Stability tag (development, rc, stable)' + description: 'Stability tag' required: true default: 'development' type: choice options: - development + - alpha + - beta - rc - stable @@ -75,11 +79,29 @@ jobs: REPO="${{ github.repository }}" VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null || echo "0.0.0") + # Auto-bump patch on alpha/beta/rc branches (not dev — dev bumps manually) + if [[ "$BRANCH" != dev/* ]]; then + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + BUMPED=$(php /tmp/mokostandards/api/cli/version_bump.php --path . 2>/dev/null || true) + if [ -n "$BUMPED" ]; then + VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null || echo "$VERSION") + git add -A + git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \ + --author="github-actions[bot] " 2>/dev/null || true + git push 2>/dev/null || true + fi + fi + # Determine stability from branch or input if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then STABILITY="${{ inputs.stability }}" elif [[ "$BRANCH" == rc/* ]]; then STABILITY="rc" + elif [[ "$BRANCH" == beta/* ]]; then + STABILITY="beta" + elif [[ "$BRANCH" == alpha/* ]]; then + STABILITY="alpha" elif [[ "$BRANCH" == dev/* ]]; then STABILITY="development" else @@ -116,19 +138,23 @@ jobs: # Version suffix for non-stable DISPLAY_VERSION="$VERSION" - [ "$STABILITY" = "rc" ] && DISPLAY_VERSION="${VERSION}-rc" - [ "$STABILITY" = "development" ] && DISPLAY_VERSION="${VERSION}-dev" + case "$STABILITY" in + development) DISPLAY_VERSION="${VERSION}-dev" ;; + alpha) DISPLAY_VERSION="${VERSION}-alpha" ;; + beta) DISPLAY_VERSION="${VERSION}-beta" ;; + rc) DISPLAY_VERSION="${VERSION}-rc" ;; + esac MAJOR=$(echo "$VERSION" | awk -F. '{print $1}') # Each stability level has its own release tag - if [ "$STABILITY" = "rc" ]; then - RELEASE_TAG="release-candidate" - elif [ "$STABILITY" = "development" ]; then - RELEASE_TAG="development" - else - RELEASE_TAG="v${MAJOR}" - fi + case "$STABILITY" in + development) RELEASE_TAG="development" ;; + alpha) RELEASE_TAG="alpha" ;; + beta) RELEASE_TAG="beta" ;; + rc) RELEASE_TAG="release-candidate" ;; + *) RELEASE_TAG="v${MAJOR}" ;; + esac PACKAGE_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.zip" DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}" @@ -139,7 +165,7 @@ jobs: [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" if [ -d "$SOURCE_DIR" ]; then cd "$SOURCE_DIR" - zip -r "/tmp/${PACKAGE_NAME}" . + zip -r "/tmp/${PACKAGE_NAME}" . -x '.ftpignore' cd .. SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1) @@ -157,95 +183,63 @@ jobs: fi # ── Build the new entry ─────────────────────────────────────── - NEW_ENTRY=$(cat < - ${EXT_NAME} - ${EXT_NAME} (${STABILITY}) - ${EXT_ELEMENT} - ${EXT_TYPE} - ${DISPLAY_VERSION} - $([ -n "$CLIENT_TAG" ] && echo " ${CLIENT_TAG}") - $([ -n "$FOLDER_TAG" ] && echo " ${FOLDER_TAG}") - - ${STABILITY} - - ${INFO_URL} - - ${DOWNLOAD_URL} - - $([ -n "$SHA256" ] && echo " sha256:${SHA256}") - ${TARGET_PLATFORM} - $([ -n "$PHP_TAG" ] && echo " ${PHP_TAG}") - Moko Consulting - https://mokoconsulting.tech - -XMLEOF -) + NEW_ENTRY="" + NEW_ENTRY="${NEW_ENTRY} \n" + NEW_ENTRY="${NEW_ENTRY} ${EXT_NAME}\n" + NEW_ENTRY="${NEW_ENTRY} ${EXT_NAME} (${STABILITY})\n" + NEW_ENTRY="${NEW_ENTRY} ${EXT_ELEMENT}\n" + NEW_ENTRY="${NEW_ENTRY} ${EXT_TYPE}\n" + NEW_ENTRY="${NEW_ENTRY} ${DISPLAY_VERSION}\n" + [ -n "$CLIENT_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${CLIENT_TAG}\n" + [ -n "$FOLDER_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${FOLDER_TAG}\n" + NEW_ENTRY="${NEW_ENTRY} \n" + NEW_ENTRY="${NEW_ENTRY} ${STABILITY}\n" + NEW_ENTRY="${NEW_ENTRY} \n" + NEW_ENTRY="${NEW_ENTRY} ${INFO_URL}\n" + NEW_ENTRY="${NEW_ENTRY} \n" + NEW_ENTRY="${NEW_ENTRY} ${DOWNLOAD_URL}\n" + NEW_ENTRY="${NEW_ENTRY} \n" + [ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} sha256:${SHA256}\n" + NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n" + [ -n "$PHP_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${PHP_TAG}\n" + NEW_ENTRY="${NEW_ENTRY} Moko Consulting\n" + NEW_ENTRY="${NEW_ENTRY} https://mokoconsulting.tech\n" + NEW_ENTRY="${NEW_ENTRY} " + + # ── Write new entry to temp file ─────────────────────────────── + printf '%b' "$NEW_ENTRY" > /tmp/new_entry.xml # ── Merge into updates.xml ───────────────────────────────────── if [ ! -f "updates.xml" ]; then - # Create fresh printf '%s\n' '' > updates.xml printf '%s\n' '' >> updates.xml - echo "$NEW_ENTRY" >> updates.xml - printf '%s\n' '' >> updates.xml + cat /tmp/new_entry.xml >> updates.xml + printf '\n%s\n' '' >> updates.xml else - # Remove existing entry for this stability, add new one - # Use python for reliable XML manipulation - python3 -c " -import re, sys - -with open('updates.xml', 'r') as f: - content = f.read() - -# Remove existing entry with this stability tag -pattern = r' .*?${STABILITY}.*?\n?' -content = re.sub(pattern, '', content, flags=re.DOTALL) - -# Insert new entry before -new_entry = '''${NEW_ENTRY}''' -content = content.replace('', new_entry + '\n') - -# Clean up empty lines -content = re.sub(r'\n{3,}', '\n\n', content) - -with open('updates.xml', 'w') as f: - f.write(content) -" 2>/dev/null || { - # Fallback: just rewrite the whole file if python fails - # Keep existing stable entry if present - STABLE_ENTRY="" - if [ "$STABILITY" != "stable" ] && grep -q 'stable' updates.xml; then - STABLE_ENTRY=$(sed -n '//,/<\/update>/{ /stable<\/tag>/,/<\/update>/p; //,/stable<\/tag>/p }' updates.xml | sort -u) - fi - RC_ENTRY="" - if [ "$STABILITY" != "rc" ] && grep -q 'rc' updates.xml; then - RC_ENTRY=$(python3 -c " -import re -with open('updates.xml') as f: c = f.read() -m = re.search(r'(.*?rc.*?)', c, re.DOTALL) -if m: print(m.group(1)) -" 2>/dev/null || true) - fi - DEV_ENTRY="" - if [ "$STABILITY" != "development" ] && grep -q 'development' updates.xml; then - DEV_ENTRY=$(python3 -c " -import re -with open('updates.xml') as f: c = f.read() -m = re.search(r'(.*?development.*?)', c, re.DOTALL) -if m: print(m.group(1)) -" 2>/dev/null || true) - fi - + # Remove existing entry for this stability, insert new one + printf 'import re\nstability = "%s"\n' "${STABILITY}" > /tmp/merge_xml.py + printf 'with open("updates.xml") as f: content = f.read()\n' >> /tmp/merge_xml.py + printf 'with open("/tmp/new_entry.xml") as f: new_entry = f.read()\n' >> /tmp/merge_xml.py + printf 'pattern = r" .*?" + re.escape(stability) + r".*?\\n?"\n' >> /tmp/merge_xml.py + printf 'content = re.sub(pattern, "", content, flags=re.DOTALL)\n' >> /tmp/merge_xml.py + printf 'content = content.replace("", new_entry + "\\n")\n' >> /tmp/merge_xml.py + printf 'content = re.sub(r"\\n{3,}", "\\n\\n", content)\n' >> /tmp/merge_xml.py + printf 'with open("updates.xml", "w") as f: f.write(content)\n' >> /tmp/merge_xml.py + python3 /tmp/merge_xml.py 2>/dev/null || { + # Fallback: rebuild keeping other stability entries { printf '%s\n' '' printf '%s\n' '' - [ -n "$STABLE_ENTRY" ] && echo "$STABLE_ENTRY" - [ -n "$RC_ENTRY" ] && echo "$RC_ENTRY" - [ -n "$DEV_ENTRY" ] && echo "$DEV_ENTRY" - echo "$NEW_ENTRY" - printf '%s\n' '' - } > updates.xml + for TAG in stable rc development; do + [ "$TAG" = "${STABILITY}" ] && continue + if grep -q "${TAG}" updates.xml 2>/dev/null; then + sed -n "//,/<\/update>/{ /${TAG}<\/tag>/p; }" updates.xml + fi + done + cat /tmp/new_entry.xml + printf '\n%s\n' '' + } > /tmp/updates_new.xml + mv /tmp/updates_new.xml updates.xml } fi @@ -259,6 +253,64 @@ if m: print(m.group(1)) git push } + - name: SFTP deploy to dev server + if: contains(github.ref, 'dev/') + env: + DEV_HOST: ${{ vars.DEV_FTP_HOST }} + DEV_PATH: ${{ vars.DEV_FTP_PATH }} + DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} + DEV_USER: ${{ vars.DEV_FTP_USERNAME }} + DEV_PORT: ${{ vars.DEV_FTP_PORT }} + DEV_KEY: ${{ secrets.DEV_FTP_KEY }} + DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }} + GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} + run: | + # ── Permission check: admin or maintain role required ────── + ACTOR="${{ github.actor }}" + REPO="${{ github.repository }}" + PERMISSION=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" \ + --jq '.permission' 2>/dev/null || \ + gh api "repos/${REPO}/collaborators/${ACTOR}" \ + --jq '.role' 2>/dev/null || echo "read") + case "$PERMISSION" in + admin|maintain|write) ;; + *) + echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write" + exit 0 + ;; + esac + + [ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; } + + SOURCE_DIR="src" + [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" + [ ! -d "$SOURCE_DIR" ] && exit 0 + + PORT="${DEV_PORT:-22}" + REMOTE="${DEV_PATH%/}" + [ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}" + + printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \ + "$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json + if [ -n "$DEV_KEY" ]; then + echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key + printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json + else + printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json + fi + + PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true) + if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards/api/deploy/deploy-joomla.php" ]; then + php /tmp/mokostandards/api/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json + elif [ -f "/tmp/mokostandards/api/deploy/deploy-sftp.php" ]; then + php /tmp/mokostandards/api/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json + fi + rm -f /tmp/deploy_key /tmp/sftp-config.json + echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY + + - name: Summary + if: always() + run: | echo "## Joomla Update Server" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY diff --git a/README.md b/README.md index b2c2ca6..5c16e4d 100644 --- a/README.md +++ b/README.md @@ -13,20 +13,14 @@ BRIEF: Documentation for MokoCassiopeia template --> - -[![Version](https://img.shields.io/badge/version-03.09.03-blue.svg?logo=v&logoColor=white)](https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/v03) -[![License](https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&logoColor=white)](LICENSE) -[![Joomla](https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&logoColor=white)](https://www.joomla.org) -[![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&logoColor=white)](https://www.php.net) - -# MokoCassiopeia +# MokoCassiopeia Template **A Modern, Lightweight Joomla Template Based on Cassiopeia** -](https://img.shields.io/badge/version-03.09.04-green.svg)](https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/v03) -: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) -](https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-blue.svg)](https://www.joomla.org) -](https://img.shields.io/badge/PHP-8.1%2B-blue.svg)](https://www.php.net) +[![Version](https://img.shields.io/badge/version-03.09.07-blue.svg?logo=v&logoColor=white)](https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/v03) +[![License](https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&logoColor=white)](LICENSE) +[![Joomla](https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&logoColor=white)](https://www.joomla.org) +[![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&logoColor=white)](https://www.php.net) MokoCassiopeia is a modern, lightweight enhancement layer built on top of Joomla's Cassiopeia template. It adds **Font Awesome 7**, **Bootstrap 5** helpers, an automatic **Table of Contents (TOC)** utility, advanced **Dark Mode** theming, and optional integrations for **Google Tag Manager** and **Google Analytics (GA4)**—all while maintaining minimal core template overrides for maximum upgrade compatibility. diff --git a/src/html/mod_breadcrumbs/default.php b/src/html/mod_breadcrumbs/default.php index fbea28f..96e741e 100644 --- a/src/html/mod_breadcrumbs/default.php +++ b/src/html/mod_breadcrumbs/default.php @@ -10,31 +10,62 @@ /** * Default layout override for mod_breadcrumbs. * Bootstrap 5 breadcrumb with schema.org BreadcrumbList markup. + * Respects showHome, showLast, homeText module settings. */ defined('_JEXEC') or die; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; Factory::getApplication()->getLanguage()->load('mod_breadcrumbs', JPATH_SITE); $suffix = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COMPAT, 'UTF-8'); $headerTag = htmlspecialchars($params->get('header_tag', 'h3'), ENT_COMPAT, 'UTF-8'); $headerClass = htmlspecialchars($params->get('header_class', ''), ENT_COMPAT, 'UTF-8'); +$showHome = $params->get('showHome', 1); +$showLast = $params->get('showLast', 1); +$homeText = $params->get('homeText', '') ?: Text::_('MOD_BREADCRUMBS_HOME'); + +// Build filtered list respecting module settings +$items = []; +$count = count($list); + +foreach ($list as $key => $item) { + // Skip Home item if showHome is off + if ($key === 0 && !$showHome) { + continue; + } + + // Replace Home text if custom homeText is set + if ($key === 0 && $showHome) { + $item->name = $homeText; + } + + // Skip last item if showLast is off + if ($key === $count - 1 && !$showLast) { + continue; + } + + $items[] = $item; +} + +if (empty($items)) { + return; +} ?>