From de48c2beda9874237caf27a2efbe25474cb17026 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:13 -0500 Subject: [PATCH 01/39] chore: update LICENSE from MokoStandards -- 2.49.1 From ced1460373dba0af9d570534ea68d1acd56a778e Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:14 -0500 Subject: [PATCH 02/39] chore: update SECURITY.md from MokoStandards --- SECURITY.md | 287 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 171 insertions(+), 116 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 9c2b528..777c03f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,185 +1,240 @@ -## 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 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. +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. ## Supported Versions -Security fixes are prioritized for: +Security updates are provided for the following versions: -* The latest released version. -* The current development line when it is actively used for release engineering. +| Version | Supported | +| ------- | ------------------ | +| [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 -Use one of the following channels: +### Where to Report -* GitHub Security Advisories (preferred): use the repository security tab to submit a private report. -* Email: send details to `hello@mokoconsulting.tech` with subject `SECURITY: MokoCassiopeia vulnerability report`. +**DO NOT** create public GitHub issues for security vulnerabilities. -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. -* 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. +### What to Include -## 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. -* **Initial triage:** within 10 business days. -* **Fix plan:** communicated once severity is confirmed. +### Response Timeline -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. -* Data confidentiality, integrity, and availability impact. -* Likelihood of exploitation in typical Joomla deployments. -* Exposure surface (public endpoints, administrator area, installation flows, and update mechanisms). +### Critical +* Remote code execution +* Authentication bypass +* 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. -* A public advisory may be published once a fix is released. -* A reasonable embargo period is expected to enable patch distribution. +## Remediation Process -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 when applicable. +* GitHub Security Advisories +* Release notes and CHANGELOG.md +* Security mailing list (when established) -Advisories may include: +Advisories include: -* Affected versions and fixed versions. -* Mitigations and workarounds when a fix is not immediately available. -* Upgrade guidance. +* CVE identifier (if applicable) +* Severity rating +* 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. -* Minimizing privileged GitHub token permissions. -* Validating build inputs prior to packaging releases. +### Required Controls -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. -* Consistent path normalization and whitespace hygiene checks where required for release correctness. -* Least privilege for GitHub Actions permissions. +#### Automated Security Scanning -### 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) -* Custom meta tags -* Third-party integrations -* Custom styling +**Secret Scanning**: +* Enabled by default with push protection +* Prevents accidental credential commits +* 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 -* **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 +See [Security Scanning Policy](docs/policy/security-scanning.md) for detailed requirements. -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. -* Limit testing to systems you own or have explicit permission to test. -* Provide a reasonable window for coordinated disclosure. +This security policy is binding for all repositories governed by MokoStandards. Deviations require documented justification and approval from the Security Owner. -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 -* **Document:** SECURITY.md -* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia) -* **Path:** /SECURITY.md -* **Owner:** Moko Consulting -* **Version:** 03.06.00 -* **Status:** Active -* **Effective Date:** 2025-12-18 -* **Last Reviewed:** 2025-12-18 +| Field | Value | +| ------------ | ----------------------------------------------- | +| Document | Security Policy | +| Path | /SECURITY.md | +| Repository | [REPOSITORY_URL] | +| Owner | [OWNER_NAME] | +| Scope | Security vulnerability handling | +| Applies To | All repositories governed by MokoStandards | +| Status | Active | +| Effective | [YYYY-MM-DD] | ## Revision History -| Date | Change Summary | Author | -| ---------- | ------------------------------------------------------------------------------------------------ | --------------- | -| 2026-01-30 | Added Template Security Features section documenting custom head content injection controls. | Copilot Agent | -| 2025-12-18 | Initial publication of security policy, intake channels, triage targets, and disclosure process. | Moko Consulting | +| Date | Change Description | Author | +| ---------- | ------------------------------------------------- | --------------- | +| [YYYY-MM-DD] | Initial creation | [AUTHOR_NAME] | -- 2.49.1 From 5acf4dbe0655d7d4c00bc427f4e4a8ad40740fa8 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:14 -0500 Subject: [PATCH 03/39] chore: update CODE_OF_CONDUCT.md from MokoStandards --- CODE_OF_CONDUCT.md | 131 +++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 71 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b0b170d..7086a96 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,98 +1,87 @@ - +# 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. -* Project documentation, workflows, and release processes. -* Any communication channels officially associated with the project. + Demonstrating empathy and respect toward others. + Being inclusive of diverse viewpoints and backgrounds. + 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. -* Provide constructive feedback focused on technical merit and project objectives. -* Respect differing viewpoints, experience levels, and backgrounds. -* Follow documented contribution, security, and governance policies. +## 3. Responsibilities of Maintainers -Unacceptable behavior includes: +Maintainers are responsible for: -* Harassment, discrimination, or exclusionary conduct. -* Personal attacks, insults, or inflammatory comments. -* Publishing private information without consent. -* Disruptive behavior that materially interferes with project operations. + Clarifying acceptable behavior. + Taking appropriate corrective action when unacceptable behavior occurs. + Removing, editing, or rejecting contributions that violate this Code. + 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. -* Taking appropriate and proportionate corrective action when violations occur. -* Maintaining confidentiality to the extent practical during investigations. + All Moko Consulting repositories. + All documentation and collaboration platforms. + 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. -* Temporary or permanent restriction from project participation. -* Removal of content that violates this Code of Conduct. +This Code of Conduct is inspired by widely adopted community guidelines, including the Contributor Covenant and major open-source collaboration standards. -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. - -## 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 | +This Code of Conduct is a living document and may be updated following the established Change Management process. -- 2.49.1 From d0f993d1049f075d5d3488e1df7019ca54304cce Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:15 -0500 Subject: [PATCH 04/39] chore: update CONTRIBUTING.md from MokoStandards --- CONTRIBUTING.md | 189 ++++++++++++++++++++++-------------------------- 1 file changed, 86 insertions(+), 103 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b8f124..d1d9db4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,145 +1,128 @@ + VERSION: 04.04.00 + PATH: ./CONTRIBUTING.md + BRIEF: How to contribute; branch strategy, commit conventions, PR workflow, and release pipeline + --> -## 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 **MokoCassiopeia**! -## 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: +## Branch Strategy -* Source code changes -* Documentation updates -* Bug reports and enhancement proposals +| Branch | Purpose | Deploys To | +|--------|---------|------------| +| `main` | Bleeding edge — all development merges here | CI only | +| `dev/XX.YY.ZZ` | Feature development | Dev server (version: "development") | +| `version/XX.YY.ZZ` | Stable frozen snapshot | Demo + RS servers | -## Prerequisites +### Development Workflow -Contributors are expected to: - -* Have a working understanding of Joomla template structure. -* Be familiar with Git and GitHub pull request workflows. -* Review repository governance documents prior to submitting changes. -* Set up the development environment using the provided tools. - -### Quick Setup - -For first-time contributors: - -```bash -# Clone the repository -git clone https://github.com/mokoconsulting-tech/MokoCassiopeia.git -cd MokoCassiopeia +``` +1. Create branch: git checkout -b dev/XX.YY.ZZ/my-feature +2. Develop + test (dev server auto-deploys on push) +3. Open PR → main (squash merge only) +4. Auto-release (version branch + tag + GitHub Release created automatically) ``` -See [docs/QUICK_START.md](./docs/QUICK_START.md) for detailed setup instructions. +### Branch Naming -## Development Tools +| Prefix | Use | +|--------|-----| +| `dev/XX.YY.ZZ` | Feature development (e.g., `dev/02.00.00/add-extrafields`) | +| `version/XX.YY.ZZ` | Stable release (auto-created, never manually pushed) | +| `chore/` | Automated sync branches (managed by MokoStandards) | -The repository provides several tools to streamline development: +> **Never use** `feature/`, `hotfix/`, or `release/` prefixes — they are not part of the MokoStandards branch strategy. -* **Pre-commit Hooks**: Automatic local validation before commits +## Commit Conventions -## Contribution Workflow +Use [conventional commits](https://www.conventionalcommits.org/): -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. +``` +feat(scope): add new extrafield for invoice tracking +fix(sql): correct column type in llx_mytable +docs(readme): update installation instructions +chore(deps): bump enterprise library to 04.02.30 +``` -Direct commits to protected branches are not permitted. +**Valid types:** `feat` | `fix` | `docs` | `chore` | `ci` | `refactor` | `style` | `test` | `perf` | `revert` | `build` -## Branching and Versioning +## Pull Request Workflow -* 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. +1. **Branch** from `main` using `dev/XX.YY.ZZ/description` format +2. **Bump** the patch version in `README.md` before opening the PR +3. **Title** must be a valid conventional commit subject line +4. **Target** `main` — squash merge only (merge commits are disabled) +5. **CI checks** must pass before merge -## Coding and Formatting Standards +### What Happens on Merge -All contributions must: +When your PR is merged to `main`, these workflows run automatically: -* 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. +1. **sync-version-on-merge** — auto-bumps patch version, propagates to all file headers +2. **auto-release** — creates `version/XX.YY.ZZ` branch, git tag, and GitHub Release +3. **deploy-demo / deploy-rs** — deploys to demo and RS servers (if `src/**` changed) -Automated checks may reject changes that do not meet these requirements. +## Coding Standards -## Documentation Standards +All contributions must follow [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards): -Documentation changes must: +| Standard | Reference | +|----------|-----------| +| Coding Style | [coding-style-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md) | +| File Headers | [file-header-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/file-header-standards.md) | +| Branching | [branch-release-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/branch-release-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) | +| Build & Release | [build-release.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/workflows/build-release.md) | -* 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. +## PR Checklist -## Commit Messages +- [ ] Branch named `dev/XX.YY.ZZ/description` +- [ ] Patch version bumped in `README.md` +- [ ] Conventional commit format for PR title +- [ ] All new files have FILE INFORMATION headers +- [ ] `declare(strict_types=1)` in all PHP files +- [ ] PHPDoc on all public methods +- [ ] Tests pass +- [ ] CHANGELOG.md updated +- [ ] No secrets, tokens, or credentials committed -Commit messages should: +## Custom Workflows -* Be concise and descriptive. -* Focus on what changed and why. -* Avoid referencing internal issue trackers unless required. +Place repo-specific workflows in `.github/workflows/custom/` — they are **never overwritten or deleted** by MokoStandards sync: -## 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. +``` +.github/workflows/ +├── deploy-dev.yml ← Synced from MokoStandards +├── auto-release.yml ← Synced from MokoStandards +└── custom/ ← Your custom workflows (safe) + └── my-custom-ci.yml +``` ## License -By contributing, you agree that your contributions will be licensed under GPL-3.0-or-later, consistent with the rest of the project. - -## Code of Conduct - -Participation in this project is governed by the Code of Conduct. Unacceptable behavior may result in contribution restrictions. +By contributing, you agree that your contributions will be licensed under the [GPL-3.0-or-later](LICENSE) license. --- -## 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 | +*This file is synced from [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). Do not edit directly — changes will be overwritten on the next sync.* -- 2.49.1 From e33c9991439ec38719cca0bd2ff34d94f5b628ea Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:16 -0500 Subject: [PATCH 05/39] chore: update update.xml from MokoStandards -- 2.49.1 From 7422529d17db43fe0d0e7b1980ee663764a4c3b1 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:16 -0500 Subject: [PATCH 06/39] chore: update phpstan.neon from MokoStandards -- 2.49.1 From 8939edc4a35f1b39029d9d470086d61f655a8fdb Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:17 -0500 Subject: [PATCH 07/39] chore: update Makefile from MokoStandards -- 2.49.1 From c926a55c1c375f6bbb19b8cbce91a931631ebae3 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:18 -0500 Subject: [PATCH 08/39] chore: update .gitignore from MokoStandards -- 2.49.1 From 639a5a72301e6acd582986b280cbf1dc9e3c14aa Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:19 -0500 Subject: [PATCH 09/39] chore: update composer.json from MokoStandards --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b60f555..83ea53f 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "prefer-stable": true, "require": { "php": ">=8.1", - "mokoconsulting-tech/enterprise": "^4.0" + "mokoconsulting-tech/enterprise": "dev-version/04.02.00" }, "require-dev": { "phpunit/phpunit": "^10.5", -- 2.49.1 From 763b75dbb051aa04a177fc6ec9651f5b332af5c6 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:19 -0500 Subject: [PATCH 10/39] chore: add .mokostandards from MokoStandards --- .mokostandards | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .mokostandards diff --git a/.mokostandards b/.mokostandards new file mode 100644 index 0000000..a2bf7b4 --- /dev/null +++ b/.mokostandards @@ -0,0 +1,20 @@ +# Copyright (C) 2026 Moko Consulting +# 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.04.00 +# BRIEF: Governance attachment template — synced to .mokostandards in every governed repository +# NOTE: Tokens replaced at sync time: mokoconsulting-tech, MokoCassiopeia, waas-component, 04.04.00 +# +# 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.04.00" +platform: "waas-component" +governed_repo: "mokoconsulting-tech/MokoCassiopeia" -- 2.49.1 From 7a6c36458c6d4ed0b982cd62ba41a4c5189aed5b Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:20 -0500 Subject: [PATCH 11/39] chore: add docs/update-server.md from MokoStandards --- docs/update-server.md | 119 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 docs/update-server.md diff --git a/docs/update-server.md b/docs/update-server.md new file mode 100644 index 0000000..8a297d2 --- /dev/null +++ b/docs/update-server.md @@ -0,0 +1,119 @@ + + +# Joomla Update Server + +[![MokoStandards](https://img.shields.io/badge/MokoStandards-04.04.00-blue)](https://github.com/mokoconsulting-tech/MokoStandards) + +This document explains how `update.xml` is automatically managed for this Joomla extension following the [Joomla Update Server specification](https://docs.joomla.org/Deploying_an_Update_Server). + +## How It Works + +Joomla checks for extension updates by fetching an XML file from the URL defined in the `` tag in the extension's XML manifest. MokoStandards generates this file automatically. + +### Automatic Generation + +| Event | Workflow | `` | `` | +|-------|----------|---------|-------------| +| Merge to `main` | `auto-release.yml` | `stable` | `XX.YY.ZZ` | +| Push to `dev/**` | `deploy-dev.yml` | `development` | `development` | +| Push to `rc/**` | `deploy-dev.yml` | `rc` | `XX.YY.ZZ-rc` | + +### Generated XML Structure + +```xml + + + + Extension Name + Extension Name update + com_extensionname + component + 01.02.03 + site + system + + stable + + https://github.com/.../releases/tag/v01.02.03 + + https://github.com/.../releases/download/v01.02.03/com_ext-01.02.03.zip + + + 8.1 + Moko Consulting + https://mokoconsulting.tech + + +``` + +### Metadata Source + +All metadata is extracted from the extension's XML manifest (`src/*.xml`) at build time: + +| XML Element | Source | Notes | +|-------------|--------|-------| +| `` | `` in manifest | Extension display name | +| `` | `` in manifest | Must match installed extension identifier | +| `` | `type` attribute on `` | `component`, `module`, `plugin`, `library`, `package`, `template` | +| `` | `client` attribute on `` | `site` or `administrator` — **required for plugins and modules** | +| `` | `group` attribute on `` | Plugin group (e.g., `system`, `content`) — **required for plugins** | +| `` | `` in manifest | Falls back to Joomla 4.3+ / 5.x if not specified | +| `` | `` in manifest | Included only if present | + +### Extension Manifest Setup + +Your XML manifest must include an `` tag pointing to the `update.xml` on the `main` branch: + +```xml + + My Extension + com_myextension + + + + https://raw.githubusercontent.com/mokoconsulting-tech/MokoCassiopeia/main/update.xml + + + +``` + +### Branch Lifecycle + +``` +dev/XX.YY.ZZ → rc/XX.YY.ZZ → main → version/XX.YY.ZZ +(development) (rc) (stable) (frozen snapshot) +``` + +1. **Development** (`dev/**`): `update.xml` with `development`, download points to branch archive +2. **Release Candidate** (`rc/**`): `update.xml` with `rc`, version set to `XX.YY.ZZ-rc` +3. **Stable Release** (merge to `main`): `update.xml` with `stable`, download points to GitHub Release asset +4. **Frozen Snapshot** (`version/XX.YY.ZZ`): immutable, never force-pushed + +### Health Checks + +The `repo_health.yml` workflow verifies on every commit: + +- `update.xml` exists in the repository root +- XML manifest exists with `` tag +- ``, ``, ``, `` tags present +- Extension `type` attribute is valid +- Language `.ini` files exist +- `index.html` directory listing protection in `src/`, `src/admin/`, `src/site/` + +--- + +*Managed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). See [docs/workflows/update-server.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/workflows/update-server.md) for the full specification.* -- 2.49.1 From a1777534c2fa5b087c7cc849ebf5e076b1200288 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:21 -0500 Subject: [PATCH 12/39] chore: update .github/copilot.yml from MokoStandards -- 2.49.1 From 3feb51880d3a8485e8ff7dc6de57484260839827 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:21 -0500 Subject: [PATCH 13/39] chore: update .github/copilot-instructions.md from MokoStandards -- 2.49.1 From 01cf4c68cf318887384ee15c5471e26d62a13ecf Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:22 -0500 Subject: [PATCH 14/39] chore: update .github/CLAUDE.md from MokoStandards -- 2.49.1 From d0411bb70adb49877f46e051b8bb6bf62e46eaf0 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:23 -0500 Subject: [PATCH 15/39] chore: update .github/workflows/codeql-analysis.yml from MokoStandards --- .github/workflows/codeql-analysis.yml | 88 ++++++++++++--------------- 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 123a1a7..385d61c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,15 +5,17 @@ # SPDX-License-Identifier: GPL-3.0-or-later # # FILE INFORMATION -# DEFGROUP: GitHub.Workflow +# DEFGROUP: GitHub.Workflow.Template # INGROUP: MokoStandards.Security # REPO: https://github.com/mokoconsulting-tech/MokoStandards -# PATH: /.github/workflows/codeql-analysis.yml -# VERSION: 04.01.00 -# BRIEF: CodeQL security scanning workflow for PHP codebase -# NOTE: Repository is PHP-only (v04.00.04). Python was removed Feb 12, 2026. +# PATH: /templates/workflows/generic/codeql-analysis.yml +# VERSION: 04.04.00 +# 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. +# For PHP-specific security scanning see standards-compliance.yml. -name: "CodeQL Security Scanning" +name: CodeQL Security Scanning on: push: @@ -28,7 +30,7 @@ on: - dev/** - rc/** schedule: - # Run weekly on Monday at 6:00 AM UTC + # Weekly on Monday at 06:00 UTC - cron: '0 6 * * 1' workflow_dispatch: @@ -40,65 +42,60 @@ permissions: jobs: analyze: - name: Configuration Security Scan + name: Analyze (${{ matrix.language }}) runs-on: ubuntu-latest timeout-minutes: 360 - # No language matrix - PHP-only repository - # CodeQL scans workflow files, configs, and scripts for security issues - # PHP security handled by SecurityValidator enterprise library + strategy: + fail-fast: false + 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: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Initialize CodeQL - uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + uses: github/codeql-action/init@v3 with: - # No languages specified - scan configurations only - # Reference explicit config to scan YAML, JSON, shell scripts - config-file: ./.github/codeql/codeql-config.yml - # Use security-extended query suite for comprehensive coverage + languages: ${{ matrix.language }} 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 - uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + uses: github/codeql-action/analyze@v3 with: - category: "/language:config" + category: "/language:${{ matrix.language }}" upload: true output: sarif-results wait-for-processing: true - - name: Upload SARIF results (optional) + - name: Upload SARIF results if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v4.5.0 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.5.0 with: - name: codeql-results-config + name: codeql-results-${{ matrix.language }} path: sarif-results retention-days: 30 - - name: Check for Critical/High Findings + - name: Step summary if: always() run: | - echo "### 🔍 CodeQL Security Analysis Complete" >> $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 "### 🔍 CodeQL — ${{ matrix.language }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY 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 "**Response Requirements**:" >> $GITHUB_STEP_SUMMARY - echo "- Critical: Fix within 7 days" >> $GITHUB_STEP_SUMMARY - echo "- High: Fix within 14 days" >> $GITHUB_STEP_SUMMARY - echo "- Medium: Fix within 30 days" >> $GITHUB_STEP_SUMMARY - echo "- Low: Fix within 60 days or next release" >> $GITHUB_STEP_SUMMARY + echo "| Severity | SLA |" >> $GITHUB_STEP_SUMMARY + echo "|----------|-----|" >> $GITHUB_STEP_SUMMARY + echo "| Critical | 7 days |" >> $GITHUB_STEP_SUMMARY + echo "| High | 14 days |" >> $GITHUB_STEP_SUMMARY + echo "| Medium | 30 days |" >> $GITHUB_STEP_SUMMARY + echo "| Low | 60 days / next release |" >> $GITHUB_STEP_SUMMARY summary: name: Security Scan Summary @@ -107,17 +104,12 @@ jobs: if: always() steps: - - name: Generate Summary + - name: Summary run: | - echo "### 🛡️ Security Scanning 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 "### 🛡️ CodeQL Complete" >> $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" + echo "" >> $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 -- 2.49.1 From d990b14be9e1ded7cce18820a45a566998dbaa36 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:23 -0500 Subject: [PATCH 16/39] chore: update .github/workflows/standards-compliance.yml from MokoStandards --- .github/workflows/standards-compliance.yml | 2355 ++++++++++---------- 1 file changed, 1192 insertions(+), 1163 deletions(-) diff --git a/.github/workflows/standards-compliance.yml b/.github/workflows/standards-compliance.yml index a1ad082..af68869 100644 --- a/.github/workflows/standards-compliance.yml +++ b/.github/workflows/standards-compliance.yml @@ -5,16 +5,41 @@ # INGROUP: MokoStandards.Compliance # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /.github/workflows/standards-compliance.yml -# VERSION: 04.01.00 +# VERSION: 04.04.00 # BRIEF: MokoStandards compliance validation workflow # NOTE: Validates repository structure, documentation, and coding standards name: Standards Compliance +# ╔════════════════════════════════════════════════════════════════════════╗ +# ║ MOKOSTANDARDS COMPLIANCE WORKFLOW ║ +# ╠════════════════════════════════════════════════════════════════════════╣ +# ║ ║ +# ║ 28 checks across 4 priority tiers: ║ +# ║ ║ +# ║ TIER 1 — CRITICAL (must pass) ║ +# ║ secret-scanning, license-compliance, repository-structure, ║ +# ║ coding-standards, version-consistency ║ +# ║ ║ +# ║ TIER 2 — IMPORTANT (should pass) ║ +# ║ workflow-validation, documentation-quality, readme-completeness, ║ +# ║ git-hygiene, script-integrity ║ +# ║ ║ +# ║ TIER 3 — QUALITY (code metrics) ║ +# ║ line-length, file-naming, insecure-patterns, complexity, ║ +# ║ duplication, dead-code ║ +# ║ ║ +# ║ TIER 4 — SUPPLEMENTARY (informational) ║ +# ║ file-size, binary, todo, deps, links, api-docs, accessibility, ║ +# ║ performance, enterprise, health, terraform ║ +# ║ ║ +# ║ File size: warning >15MB, critical >20MB ║ +# ║ Exempt: .mmdb, .woff2, .woff, .ttf, .otf ║ +# ║ ║ +# ╚════════════════════════════════════════════════════════════════════════╝ + env: - ACTIONS_STEP_DEBUG: true - ACTIONS_RUNNER_DEBUG: true - WORKFLOW_VERSION: "04.00.05" + WORKFLOW_VERSION: "04.02.12" # MokoStandards Policy Compliance: # - File formatting: Enforces organizational coding standards @@ -63,22 +88,191 @@ env: on: push: - branches: - - main - - dev/** - - rc/** + branches: [main, dev/**, rc/**, version/**] pull_request: - branches: - - main - - dev/** - - rc/** + branches: [main, dev/**, rc/**] workflow_dispatch: permissions: contents: read pull-requests: write + issues: write + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true jobs: + # ════════════════════════════════════════════════════════════════════════ + # TIER 1 — CRITICAL (must pass, blocks merge) + # ════════════════════════════════════════════════════════════════════════ + secret-scanning: + name: Secret Scanning + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Scan for Secrets + run: | + set -x + echo "## 🔒 Secret Scanning" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Scanning for hardcoded secrets and credentials." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Define secret patterns + VIOLATIONS=0 + + # Check for common secret patterns + echo "### Secret Patterns" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Helper: scan with a pattern, show results with file:line, return count + scan_pattern() { + local label="$1" icon="$2" tmpfile="$3" + local count=0 + if [ -f "$tmpfile" ]; then + count=$(wc -l < "$tmpfile") + fi + if [ "$count" -gt 0 ]; then + echo "${icon} **${label}**: ${count} finding(s)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View locations" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| File | Line | Match |" >> $GITHUB_STEP_SUMMARY + echo "|------|------|-------|" >> $GITHUB_STEP_SUMMARY + head -20 "$tmpfile" | while IFS= read -r line; do + FILE=$(echo "$line" | cut -d: -f1 | sed 's|^\./||') + LINENO=$(echo "$line" | cut -d: -f2) + MATCH=$(echo "$line" | cut -d: -f3- | head -c 80 | sed 's/|/\\|/g') + echo "| \`${FILE}\` | ${LINENO} | \`${MATCH}\` |" >> $GITHUB_STEP_SUMMARY + done + if [ "$count" -gt 20 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "*... and $((count - 20)) more*" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + count)) + fi + } + + # Pattern 1: password/secret assignments + grep -r -n -E "(password|passwd|pwd|secret|api[_-]?key|token).*=.*['\"]" . \ + --include="*.php" --include="*.py" --include="*.js" --include="*.ts" \ + --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null | \ + grep -v -E '(test|example|sample|getenv|getString|getArgument|config\[|/\.\*/|^\s*//|^\s*\*|CREDENTIAL_PATTERNS|SecurityValidator|SECRET_PATTERN|===|!==|ApiClient|str_contains|gen_wrappers)' | \ + grep -v "= ''" | grep -v '= ""' | grep -v '\$this->config' > /tmp/secrets1.txt 2>/dev/null || true + scan_pattern "Secret assignments" "⚠️" /tmp/secrets1.txt + + # Pattern 2: Private keys + grep -r -n "BEGIN.*PRIVATE KEY" . \ + --include="*.pem" --include="*.key" --include="*.txt" \ + --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null > /tmp/secrets2.txt || true + scan_pattern "Private keys" "❌" /tmp/secrets2.txt + + # Pattern 3: AWS keys + grep -r -n -E "AKIA[0-9A-Z]{16}" . \ + --include="*.php" --include="*.py" --include="*.js" --include="*.txt" --include="*.env" \ + --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null > /tmp/secrets3.txt || true + scan_pattern "AWS access keys" "❌" /tmp/secrets3.txt + + # Pattern 4: GitHub tokens + grep -r -n -E "gh[ps]_[a-zA-Z0-9]{36}" . \ + --include="*.php" --include="*.py" --include="*.js" --include="*.txt" --include="*.env" \ + --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null > /tmp/secrets4.txt || true + scan_pattern "GitHub tokens" "❌" /tmp/secrets4.txt + + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$VIOLATIONS" -gt 0 ]; then + echo "**Total Violations**: $VIOLATIONS" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View detected secrets (file paths only)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/secrets*.txt 2>/dev/null | cut -d: -f1 | sort -u >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Action Required**: Remove hardcoded secrets immediately!" >> $GITHUB_STEP_SUMMARY + echo "Use environment variables or secrets management instead." >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "✅ No hardcoded secrets detected" >> $GITHUB_STEP_SUMMARY + fi + + license-compliance: + name: License Header Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check SPDX Headers + run: | + set -x + echo "### SPDX License Header Check" >> $GITHUB_STEP_SUMMARY + + # Count source files with and without SPDX headers + TOTAL_PHP=0 + WITH_SPDX_PHP=0 + + if find . -name "*.php" -type f ! -path "./vendor/*" | head -1 | grep -q .; then + TOTAL_PHP=$(find . -name "*.php" -type f ! -path "./vendor/*" | wc -l) + WITH_SPDX_PHP=$(find . -name "*.php" -type f ! -path "./vendor/*" -exec grep -l "SPDX-License-Identifier" {} \; | wc -l) + fi + + if [ "$TOTAL_PHP" -gt 0 ]; then + PERCENT=$((WITH_SPDX_PHP * 100 / TOTAL_PHP)) + echo "- PHP files: $WITH_SPDX_PHP/$TOTAL_PHP ($PERCENT%) with SPDX headers" >> $GITHUB_STEP_SUMMARY + + if [ "$PERCENT" -lt 80 ]; then + echo "⚠️ Less than 80% of PHP files have SPDX headers" >> $GITHUB_STEP_SUMMARY + else + echo "✅ Good SPDX header coverage" >> $GITHUB_STEP_SUMMARY + fi + fi + + - name: Validate License File + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### License File Validation" >> $GITHUB_STEP_SUMMARY + + if [ ! -f "LICENSE" ]; then + echo "❌ LICENSE file not found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: LICENSE File Missing" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** LICENSE file is required for all MokoStandards-compliant repositories" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Add LICENSE file with appropriate open-source license (GPL-3.0-or-later recommended)" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: LICENSE file not found - This is a critical requirement" + exit 1 + fi + + # Check license type + if grep -qi "GNU GENERAL PUBLIC LICENSE" LICENSE; then + VERSION=$(grep -i "Version 3" LICENSE || echo "") + if [ -n "$VERSION" ]; then + echo "✅ GPL-3.0-or-later license detected" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ GPL license detected but version unclear" >> $GITHUB_STEP_SUMMARY + fi + elif grep -qi "MIT License" LICENSE; then + echo "✅ MIT license detected" >> $GITHUB_STEP_SUMMARY + elif grep -qi "Apache License" LICENSE; then + echo "✅ Apache license detected" >> $GITHUB_STEP_SUMMARY + else + echo "ℹ️ License type could not be automatically detected" >> $GITHUB_STEP_SUMMARY + fi + repository-structure: name: Repository Structure Validation runs-on: ubuntu-latest @@ -206,6 +400,387 @@ jobs: exit 1 fi + coding-standards: + name: Coding Standards Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check for Tab Characters + run: | + set -x + echo "### Tab Character Detection" >> $GITHUB_STEP_SUMMARY + + # Policy: Tabs are DEFAULT. Only check for tabs in files that REQUIRE spaces. + # Languages requiring spaces: YAML, Python, Haskell, F#, CoffeeScript, Nim, JSON, RST + TABS_IN_SPACES_FILES=$(find . -type f \ + \( -name "*.yml" -o -name "*.yaml" \ + -o -name "*.py" \ + -o -name "*.hs" -o -name "*.lhs" \ + -o -name "*.fs" -o -name "*.fsx" -o -name "*.fsi" \ + -o -name "*.coffee" -o -name "*.litcoffee" \ + -o -name "*.nim" -o -name "*.nims" -o -name "*.nimble" \ + -o -name "*.json" \ + -o -name "*.rst" \) \ + ! -path "./vendor/*" \ + ! -path "./node_modules/*" \ + ! -path "./.git/*" \ + -exec grep -l $'\t' {} \; 2>/dev/null | head -10) + + if [ -n "$TABS_IN_SPACES_FILES" ]; then + echo "⚠️ Tab characters found in files that require spaces:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$TABS_IN_SPACES_FILES" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "These languages require spaces (tabs will break): YAML, Python, Haskell, F#, CoffeeScript, Nim, JSON, RST" >> $GITHUB_STEP_SUMMARY + echo "All other files (including .md, .ps1, LICENSE, etc.) may use tabs per MokoStandards policy" >> $GITHUB_STEP_SUMMARY + else + echo "✅ No tabs found in files requiring spaces" >> $GITHUB_STEP_SUMMARY + echo "Note: Tabs are allowed in most files (policy default). Only checked files requiring spaces." >> $GITHUB_STEP_SUMMARY + fi + + - name: Check File Encoding + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### File Encoding Check" >> $GITHUB_STEP_SUMMARY + + # Check for UTF-8 encoding (ASCII is a subset of UTF-8 and is acceptable) + NON_UTF8=$(find . -type f \( -name "*.php" -o -name "*.js" -o -name "*.md" \) \ + ! -path "./vendor/*" \ + ! -path "./node_modules/*" \ + ! -path "./.git/*" \ + -exec file {} \; | grep -v "UTF-8" | grep -v "ASCII" | head -5) + + if [ -n "$NON_UTF8" ]; then + echo "⚠️ Non-UTF-8 files detected:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$NON_UTF8" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + else + echo "✅ All source files appear to be UTF-8 encoded" >> $GITHUB_STEP_SUMMARY + fi + + - name: Check Line Endings + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Line Ending Check" >> $GITHUB_STEP_SUMMARY + + # Check for CRLF line endings + CRLF_FILES=$(find . -type f \( -name "*.php" -o -name "*.js" -o -name "*.md" \) \ + ! -path "./vendor/*" \ + ! -path "./node_modules/*" \ + ! -path "./.git/*" \ + -exec file {} \; | grep "CRLF" | head -5) + + if [ -n "$CRLF_FILES" ]; then + echo "⚠️ Files with CRLF line endings found:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$CRLF_FILES" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "MokoStandards requires LF line endings" >> $GITHUB_STEP_SUMMARY + else + echo "✅ Line endings are consistent (LF)" >> $GITHUB_STEP_SUMMARY + fi + + version-consistency: + name: Version Consistency Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0 + with: + php-version: '8.1' + extensions: json + tools: composer + coverage: none + + - name: Install API Package + run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader + env: + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' + + - name: Run Version Consistency Check + id: version_check + run: | + set -x + echo "## 🔢 Version Consistency Validation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Use PHP enterprise library for version consistency check + if [ -f "vendor/bin/moko" ]; then + php vendor/bin/moko check:version -- --path . --verbose 2>&1 | tee /tmp/version-check.log + EXIT_CODE=${PIPESTATUS[0]} + elif [ -f "/tmp/mokostandards/api/validate/check_version_consistency.php" ]; then + php /tmp/mokostandards/api/validate/check_version_consistency.php --path . --verbose 2>&1 | tee /tmp/version-check.log + EXIT_CODE=${PIPESTATUS[0]} + elif [ -f "api/validate/check_version_consistency.php" ]; then + php api/validate/check_version_consistency.php --path . --verbose 2>&1 | tee /tmp/version-check.log + EXIT_CODE=${PIPESTATUS[0]} + else + echo "⏭️ Install mokoconsulting-tech/enterprise via Composer for version checks" >> $GITHUB_STEP_SUMMARY + exit 0 + fi + + echo '```' >> $GITHUB_STEP_SUMMARY + cat /tmp/version-check.log >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + + if [ "$EXIT_CODE" -eq 0 ]; then + echo "✅ All version numbers are consistent" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Version drift detected" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + + # ════════════════════════════════════════════════════════════════════════ + # TIER 2 — IMPORTANT (should pass) + # ════════════════════════════════════════════════════════════════════════ + workflow-validation: + name: Workflow Configuration Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check Required Workflows + run: | + set -x + echo "### GitHub Actions Workflows" >> $GITHUB_STEP_SUMMARY + + WORKFLOWS_DIR=".github/workflows" + + if [ ! -d "$WORKFLOWS_DIR" ]; then + echo "❌ No workflows directory found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: Workflows Directory Missing" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** .github/workflows directory is required for CI/CD automation" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Create .github/workflows directory and add GitHub Actions workflows" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: .github/workflows directory not found" + exit 1 + fi + + # Check for recommended workflows + CI_FOUND=false + for wf in ci.yml build.yml ci-dolibarr.yml ci-joomla.yml; do + if [ -f "$WORKFLOWS_DIR/$wf" ]; then + echo "✅ CI workflow present ($wf)" >> $GITHUB_STEP_SUMMARY + CI_FOUND=true + break + fi + done + if [ "$CI_FOUND" = "false" ]; then + echo "⚠️ No CI workflow found (ci.yml, build.yml, ci-dolibarr.yml, or ci-joomla.yml)" >> $GITHUB_STEP_SUMMARY + fi + + if [ -f "$WORKFLOWS_DIR/codeql-analysis.yml" ]; then + echo "✅ CodeQL security scanning present" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ CodeQL workflow not found" >> $GITHUB_STEP_SUMMARY + fi + + # Check for MokoStandards-synced workflows + for wf in deploy-dev.yml deploy-demo.yml deploy-rs.yml sync-version-on-merge.yml auto-release.yml standards-compliance.yml enterprise-firewall-setup.yml; do + if [ -f "$WORKFLOWS_DIR/$wf" ]; then + echo "✅ ${wf}" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ ${wf} not found (synced from MokoStandards)" >> $GITHUB_STEP_SUMMARY + fi + done + + - name: Validate Workflow Syntax + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Workflow YAML Syntax" >> $GITHUB_STEP_SUMMARY + + INVALID=0 + for workflow in $(find .github/workflows -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null); do + if [ -f "$workflow" ]; then + if python3 -c "import yaml, sys; yaml.safe_load(open(sys.argv[1]))" "$workflow" 2>/dev/null; then + echo "✅ $(basename $workflow)" >> $GITHUB_STEP_SUMMARY + else + echo "❌ $(basename $workflow) - invalid YAML" >> $GITHUB_STEP_SUMMARY + INVALID=$((INVALID + 1)) + fi + fi + done + + if [ "$INVALID" -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: Invalid Workflow YAML Syntax" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** $INVALID workflow file(s) have invalid YAML syntax" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Fix YAML syntax errors in the marked workflow files" >> $GITHUB_STEP_SUMMARY + echo "**Tool:** Run \`python3 -c \"import yaml; yaml.safe_load(open('.github/workflows/FILE.yml'))\"\` locally" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: $INVALID workflow file(s) with invalid YAML syntax" + exit 1 + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ✅ All Workflow Files Have Valid YAML Syntax" >> $GITHUB_STEP_SUMMARY + echo "" + echo "✅ SUCCESS: All workflow files passed YAML validation" + + - name: Validate CodeQL Configuration + if: hashFiles('.github/workflows/codeql-analysis.yml') != '' + run: | + set -e + echo "" >> $GITHUB_STEP_SUMMARY + echo "### CodeQL Language Configuration" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Inline validation (rewritten from Python to bash for PHP-only architecture) + CODEQL_FILE=".github/workflows/codeql-analysis.yml" + + if [ ! -f "$CODEQL_FILE" ]; then + echo "⚠️ CodeQL workflow file not found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ⚠️ CodeQL Workflow Not Found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** CodeQL workflow file not present - skipping language validation" >> $GITHUB_STEP_SUMMARY + echo "" + echo "⚠️ INFO: CodeQL workflow not found - Skipping validation" + exit 0 + fi + + echo "**CodeQL Configuration Analysis**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Extract configured languages from workflow + LANGUAGES=$(grep -A5 "language:" "$CODEQL_FILE" | grep -oP "(?<=')[^']+(?=')" | tr '\n' ' ' || echo "") + + # Check if this is a configuration-only scan (no languages specified) + if grep -q "category.*language:config" "$CODEQL_FILE"; then + echo "**Scan Type:** Configuration-only (no language matrix)" >> $GITHUB_STEP_SUMMARY + echo "**Status:** ✅ Valid configuration for PHP-only repository" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "This CodeQL workflow scans YAML, JSON, shell scripts for security issues." >> $GITHUB_STEP_SUMMARY + echo "PHP security is handled by SecurityValidator enterprise library." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "✅ SUCCESS: CodeQL configuration-only scan properly configured" + exit 0 + fi + + if [ -z "$LANGUAGES" ]; then + echo "❌ No languages configured in CodeQL workflow" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: CodeQL Languages Not Configured" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** CodeQL workflow exists but has no languages configured" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Configure appropriate languages in codeql-analysis.yml" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: No languages configured in CodeQL workflow" + exit 1 + fi + + echo "**Configured Languages:** $LANGUAGES" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Validate language presence in repository + INVALID_LANGS="" + VALID_LANGS="" + + for LANG in $LANGUAGES; do + case "$LANG" in + python) + # Check for Python files (should be none in v04.00.04) + if find . -name "*.py" -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS python" + echo "✅ Python: Found Python files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS python" + echo "❌ Python: No Python files found (PHP-only repository)" >> $GITHUB_STEP_SUMMARY + fi + ;; + javascript|typescript) + # Check for JS/TS files + if find . \( -name "*.js" -o -name "*.ts" -o -name "*.json" \) -type f ! -path "./.git/*" ! -path "./node_modules/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS $LANG" + echo "✅ $LANG: Found JavaScript/TypeScript/JSON files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS $LANG" + echo "⚠️ $LANG: No JavaScript/TypeScript files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + java) + if find . -name "*.java" -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS java" + echo "✅ Java: Found Java files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS java" + echo "⚠️ Java: No Java files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + go) + if find . -name "*.go" -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS go" + echo "✅ Go: Found Go files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS go" + echo "⚠️ Go: No Go files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + cpp|c) + if find . \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS $LANG" + echo "✅ $LANG: Found C/C++ files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS $LANG" + echo "⚠️ $LANG: No C/C++ files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + ruby) + if find . -name "*.rb" -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS ruby" + echo "✅ Ruby: Found Ruby files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS ruby" + echo "⚠️ Ruby: No Ruby files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + *) + echo "⚠️ $LANG: Unknown language, skipping validation" >> $GITHUB_STEP_SUMMARY + ;; + esac + done + + echo "" >> $GITHUB_STEP_SUMMARY + + # Report results + if [ -n "$INVALID_LANGS" ]; then + echo "**⚠️ Warning:** Some configured languages may not have corresponding files:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "Invalid languages: $INVALID_LANGS" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note:** This is informational. CodeQL will skip languages without source files." >> $GITHUB_STEP_SUMMARY + echo "For PHP repository (v04.00.04), JavaScript language covers JSON/YAML/shell scripts." >> $GITHUB_STEP_SUMMARY + else + echo "✅ **All configured CodeQL languages have corresponding source files**" >> $GITHUB_STEP_SUMMARY + fi + + # Always succeed - this is informational only + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ✅ CodeQL Configuration Validation Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** CodeQL language configuration reviewed successfully" >> $GITHUB_STEP_SUMMARY + echo "" + echo "✅ SUCCESS: CodeQL validation complete" + exit 0 + documentation-quality: name: Documentation Quality Check runs-on: ubuntu-latest @@ -407,92 +982,164 @@ jobs: echo "⚠️ No documentation index (docs/index.md or docs/README.md)" >> $GITHUB_STEP_SUMMARY fi - coding-standards: - name: Coding Standards Check + readme-completeness: + name: README Completeness Check runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Check for Tab Characters + - name: Check README Sections run: | set -x - echo "### Tab Character Detection" >> $GITHUB_STEP_SUMMARY + echo "## 📄 README Completeness Check" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY - # Policy: Tabs are DEFAULT. Only check for tabs in files that REQUIRE spaces. - # Languages requiring spaces: YAML, Python, Haskell, F#, CoffeeScript, Nim, JSON, RST - TABS_IN_SPACES_FILES=$(find . -type f \ - \( -name "*.yml" -o -name "*.yaml" \ - -o -name "*.py" \ - -o -name "*.hs" -o -name "*.lhs" \ - -o -name "*.fs" -o -name "*.fsx" -o -name "*.fsi" \ - -o -name "*.coffee" -o -name "*.litcoffee" \ - -o -name "*.nim" -o -name "*.nims" -o -name "*.nimble" \ - -o -name "*.json" \ - -o -name "*.rst" \) \ - ! -path "./vendor/*" \ - ! -path "./node_modules/*" \ - ! -path "./.git/*" \ - -exec grep -l $'\t' {} \; 2>/dev/null | head -10) - - if [ -n "$TABS_IN_SPACES_FILES" ]; then - echo "⚠️ Tab characters found in files that require spaces:" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "$TABS_IN_SPACES_FILES" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "These languages require spaces (tabs will break): YAML, Python, Haskell, F#, CoffeeScript, Nim, JSON, RST" >> $GITHUB_STEP_SUMMARY - echo "All other files (including .md, .ps1, LICENSE, etc.) may use tabs per MokoStandards policy" >> $GITHUB_STEP_SUMMARY - else - echo "✅ No tabs found in files requiring spaces" >> $GITHUB_STEP_SUMMARY - echo "Note: Tabs are allowed in most files (policy default). Only checked files requiring spaces." >> $GITHUB_STEP_SUMMARY + if [ ! -f "README.md" ]; then + echo "❌ README.md not found" >> $GITHUB_STEP_SUMMARY + exit 1 fi - - name: Check File Encoding + # Required sections + REQUIRED_SECTIONS=("Installation" "Usage" "Contributing" "License") + MISSING=0 + PRESENT=0 + + echo "### Required Sections" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + for section in "${REQUIRED_SECTIONS[@]}"; do + if grep -qi "##.*$section" README.md; then + echo "✅ $section" >> $GITHUB_STEP_SUMMARY + PRESENT=$((PRESENT + 1)) + else + echo "❌ $section" >> $GITHUB_STEP_SUMMARY + MISSING=$((MISSING + 1)) + fi + done + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Completeness**: $PRESENT/${#REQUIRED_SECTIONS[@]} required sections present" >> $GITHUB_STEP_SUMMARY + + if [ "$MISSING" -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Action Required**: Add missing sections to README.md" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + # ============================================================================ + # PHASE 3: Future Enhancements + # ============================================================================ + + git-hygiene: + name: Git Repository Hygiene + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Check .gitignore + run: | + set -x + echo "### .gitignore Validation" >> $GITHUB_STEP_SUMMARY + + if [ ! -f ".gitignore" ]; then + echo "⚠️ .gitignore file not found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ⚠️ Warning: .gitignore Not Found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** .gitignore file is recommended but not required" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation:** Add .gitignore to exclude build artifacts, dependencies, and temporary files" >> $GITHUB_STEP_SUMMARY + echo "" + echo "⚠️ WARNING: .gitignore file not found - Continuing validation" + exit 0 + fi + + # Check for common exclusions + MISSING="" + grep -q "vendor/" .gitignore || MISSING="${MISSING}vendor/ " + grep -q "node_modules/" .gitignore || MISSING="${MISSING}node_modules/ " + + if [ -n "$MISSING" ]; then + echo "⚠️ .gitignore may be missing common exclusions: $MISSING" >> $GITHUB_STEP_SUMMARY + else + echo "✅ .gitignore appears complete" >> $GITHUB_STEP_SUMMARY + fi + + - name: Check for Large Files run: | set -x echo "" >> $GITHUB_STEP_SUMMARY - echo "### File Encoding Check" >> $GITHUB_STEP_SUMMARY + echo "### Large File Detection" >> $GITHUB_STEP_SUMMARY - # Check for UTF-8 encoding (ASCII is a subset of UTF-8 and is acceptable) - NON_UTF8=$(find . -type f \( -name "*.php" -o -name "*.js" -o -name "*.md" \) \ - ! -path "./vendor/*" \ - ! -path "./node_modules/*" \ - ! -path "./.git/*" \ - -exec file {} \; | grep -v "UTF-8" | grep -v "ASCII" | head -5) + # Find files larger than 1MB + LARGE_FILES=$(find . -type f -size +1M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" | head -5) - if [ -n "$NON_UTF8" ]; then - echo "⚠️ Non-UTF-8 files detected:" >> $GITHUB_STEP_SUMMARY + if [ -n "$LARGE_FILES" ]; then + echo "⚠️ Large files detected (>1MB):" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "$NON_UTF8" >> $GITHUB_STEP_SUMMARY + echo "$LARGE_FILES" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "Consider using Git LFS for large binary files" >> $GITHUB_STEP_SUMMARY else - echo "✅ All source files appear to be UTF-8 encoded" >> $GITHUB_STEP_SUMMARY + echo "✅ No unusually large files detected" >> $GITHUB_STEP_SUMMARY fi - - name: Check Line Endings + script-integrity: + name: Script Integrity Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.x' + + - name: Validate Script Integrity + id: script_check run: | set -x + echo "## 🔐 Script Integrity Validation" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "### Line Ending Check" >> $GITHUB_STEP_SUMMARY - # Check for CRLF line endings - CRLF_FILES=$(find . -type f \( -name "*.php" -o -name "*.js" -o -name "*.md" \) \ - ! -path "./vendor/*" \ - ! -path "./node_modules/*" \ - ! -path "./.git/*" \ - -exec file {} \; | grep "CRLF" | head -5) + if [ -f "api/.script-registry.json" ]; then + echo "### Critical Scripts" >> $GITHUB_STEP_SUMMARY + php api/maintenance/update_sha_hashes.php \ + --dry-run --verbose | tee /tmp/script-validation.log - if [ -n "$CRLF_FILES" ]; then - echo "⚠️ Files with CRLF line endings found:" >> $GITHUB_STEP_SUMMARY + EXIT_CODE=$? + + echo "" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "$CRLF_FILES" >> $GITHUB_STEP_SUMMARY + cat /tmp/script-validation.log >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "MokoStandards requires LF line endings" >> $GITHUB_STEP_SUMMARY + + if [ "$EXIT_CODE" -eq 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "✅ All critical scripts validated successfully!" >> $GITHUB_STEP_SUMMARY + exit 0 + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "❌ Script integrity violations detected" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Review validation report and update registry" >> $GITHUB_STEP_SUMMARY + exit 1 + fi else - echo "✅ Line endings are consistent (LF)" >> $GITHUB_STEP_SUMMARY + echo "ℹ️ Script registry not found - skipping integrity check" >> $GITHUB_STEP_SUMMARY + exit 0 fi + + # ════════════════════════════════════════════════════════════════════════ + # TIER 3 — QUALITY (code quality metrics) + # ════════════════════════════════════════════════════════════════════════ line-length-validation: name: Line Length Check runs-on: ubuntu-latest @@ -588,550 +1235,338 @@ jobs: echo "Line length standards help maintain code readability." >> $GITHUB_STEP_SUMMARY echo "Exceptions documented in: \`docs/policy/coding-style-guide.md\`" >> $GITHUB_STEP_SUMMARY - license-compliance: - name: License Header Validation + file-naming-standards: + name: File Naming Standards runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Check SPDX Headers - run: | - set -x - echo "### SPDX License Header Check" >> $GITHUB_STEP_SUMMARY - - # Count source files with and without SPDX headers - TOTAL_PHP=0 - WITH_SPDX_PHP=0 - - if find . -name "*.php" -type f ! -path "./vendor/*" | head -1 | grep -q .; then - TOTAL_PHP=$(find . -name "*.php" -type f ! -path "./vendor/*" | wc -l) - WITH_SPDX_PHP=$(find . -name "*.php" -type f ! -path "./vendor/*" -exec grep -l "SPDX-License-Identifier" {} \; | wc -l) - fi - - if [ "$TOTAL_PHP" -gt 0 ]; then - PERCENT=$((WITH_SPDX_PHP * 100 / TOTAL_PHP)) - echo "- PHP files: $WITH_SPDX_PHP/$TOTAL_PHP ($PERCENT%) with SPDX headers" >> $GITHUB_STEP_SUMMARY - - if [ "$PERCENT" -lt 80 ]; then - echo "⚠️ Less than 80% of PHP files have SPDX headers" >> $GITHUB_STEP_SUMMARY - else - echo "✅ Good SPDX header coverage" >> $GITHUB_STEP_SUMMARY - fi - fi - - - name: Validate License File + - name: Check File Naming run: | set -x + echo "## 📝 File Naming Standards" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "### License File Validation" >> $GITHUB_STEP_SUMMARY - if [ ! -f "LICENSE" ]; then - echo "❌ LICENSE file not found" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### ❌ Validation Failed: LICENSE File Missing" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Error:** LICENSE file is required for all MokoStandards-compliant repositories" >> $GITHUB_STEP_SUMMARY - echo "**Action Required:** Add LICENSE file with appropriate open-source license (GPL-3.0-or-later recommended)" >> $GITHUB_STEP_SUMMARY - echo "" - echo "❌ ERROR: LICENSE file not found - This is a critical requirement" - exit 1 - fi + VIOLATIONS=0 - # Check license type - if grep -qi "GNU GENERAL PUBLIC LICENSE" LICENSE; then - VERSION=$(grep -i "Version 3" LICENSE || echo "") - if [ -n "$VERSION" ]; then - echo "✅ GPL-3.0-or-later license detected" >> $GITHUB_STEP_SUMMARY - else - echo "⚠️ GPL license detected but version unclear" >> $GITHUB_STEP_SUMMARY - fi - elif grep -qi "MIT License" LICENSE; then - echo "✅ MIT license detected" >> $GITHUB_STEP_SUMMARY - elif grep -qi "Apache License" LICENSE; then - echo "✅ Apache license detected" >> $GITHUB_STEP_SUMMARY - else - echo "ℹ️ License type could not be automatically detected" >> $GITHUB_STEP_SUMMARY - fi + # Check PHP files (should be PascalCase for classes) + INVALID_PHP=$(find . -name "*.php" ! -path "./vendor/*" ! -path "./.git/*" ! -regex ".*/[A-Z][a-zA-Z0-9]*\.php" ! -name "index.php" ! -name "functions.php" | wc -l || echo 0) - git-hygiene: - name: Git Repository Hygiene - runs-on: ubuntu-latest + # Check config files (should be kebab-case) + INVALID_CONFIG=$(find . -name "*.yml" -o -name "*.yaml" -o -name "*.json" ! -path "./vendor/*" ! -path "./.git/*" ! -path "./node_modules/*" | grep -E "[A-Z_]" | wc -l || echo 0) - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - - - name: Check .gitignore - run: | - set -x - echo "### .gitignore Validation" >> $GITHUB_STEP_SUMMARY - - if [ ! -f ".gitignore" ]; then - echo "⚠️ .gitignore file not found" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### ⚠️ Warning: .gitignore Not Found" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Status:** .gitignore file is recommended but not required" >> $GITHUB_STEP_SUMMARY - echo "**Recommendation:** Add .gitignore to exclude build artifacts, dependencies, and temporary files" >> $GITHUB_STEP_SUMMARY - echo "" - echo "⚠️ WARNING: .gitignore file not found - Continuing validation" - exit 0 - fi - - # Check for common exclusions - MISSING="" - grep -q "vendor/" .gitignore || MISSING="${MISSING}vendor/ " - grep -q "node_modules/" .gitignore || MISSING="${MISSING}node_modules/ " - - if [ -n "$MISSING" ]; then - echo "⚠️ .gitignore may be missing common exclusions: $MISSING" >> $GITHUB_STEP_SUMMARY - else - echo "✅ .gitignore appears complete" >> $GITHUB_STEP_SUMMARY - fi - - - name: Check for Large Files - run: | - set -x + echo "### Naming Violations" >> $GITHUB_STEP_SUMMARY + echo "- **PHP files not PascalCase**: $INVALID_PHP" >> $GITHUB_STEP_SUMMARY + echo "- **Config files not kebab-case**: $INVALID_CONFIG" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "### Large File Detection" >> $GITHUB_STEP_SUMMARY - # Find files larger than 1MB - LARGE_FILES=$(find . -type f -size +1M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" | head -5) + VIOLATIONS=$((INVALID_PHP + INVALID_CONFIG)) - if [ -n "$LARGE_FILES" ]; then - echo "⚠️ Large files detected (>1MB):" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "$LARGE_FILES" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "Consider using Git LFS for large binary files" >> $GITHUB_STEP_SUMMARY + if [ "$VIOLATIONS" -gt 0 ]; then + echo "⚠️ Found $VIOLATIONS naming convention violation(s)" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Follow naming conventions for consistency" >> $GITHUB_STEP_SUMMARY else - echo "✅ No unusually large files detected" >> $GITHUB_STEP_SUMMARY + echo "✅ File naming conventions followed" >> $GITHUB_STEP_SUMMARY fi - workflow-validation: - name: Workflow Configuration Check + insecure-patterns: + name: Insecure Code Pattern Detection runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Check Required Workflows + - name: Scan for Insecure Patterns run: | set -x - echo "### GitHub Actions Workflows" >> $GITHUB_STEP_SUMMARY + echo "## 🔒 Insecure Code Pattern Detection" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY - WORKFLOWS_DIR=".github/workflows" + VIOLATIONS=0 - if [ ! -d "$WORKFLOWS_DIR" ]; then - echo "❌ No workflows directory found" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### ❌ Validation Failed: Workflows Directory Missing" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Error:** .github/workflows directory is required for CI/CD automation" >> $GITHUB_STEP_SUMMARY - echo "**Action Required:** Create .github/workflows directory and add GitHub Actions workflows" >> $GITHUB_STEP_SUMMARY - echo "" - echo "❌ ERROR: .github/workflows directory not found" - exit 1 + # PHP: SQL injection patterns + if grep -r -n "\\$_\(GET\|POST\|REQUEST\).*mysql_query\|mysqli_query" . --include="*.php" ! -path "./vendor/*" 2>/dev/null > /tmp/sql_inject.txt; then + COUNT=$(wc -l < /tmp/sql_inject.txt) + echo "⚠️ Found $COUNT potential SQL injection pattern(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) fi - # Check for recommended workflows - if [ -f "$WORKFLOWS_DIR/ci.yml" ] || [ -f "$WORKFLOWS_DIR/build.yml" ]; then - echo "✅ CI workflow present" >> $GITHUB_STEP_SUMMARY + # PHP: eval/exec usage + if grep -r -n "eval\|exec\|system\|passthru\|shell_exec" . --include="*.php" ! -path "./vendor/*" 2>/dev/null > /tmp/exec.txt; then + COUNT=$(wc -l < /tmp/exec.txt) + echo "⚠️ Found $COUNT dangerous function call(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) + fi + + # Python: eval usage + if grep -r -n "eval(" . --include="*.py" 2>/dev/null > /tmp/py_eval.txt; then + COUNT=$(wc -l < /tmp/py_eval.txt) + echo "⚠️ Found $COUNT Python eval() usage(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) + fi + + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$VIOLATIONS" -gt 0 ]; then + echo "**Total Violations**: $VIOLATIONS" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Review and secure flagged patterns" >> $GITHUB_STEP_SUMMARY else - echo "⚠️ No CI workflow found (ci.yml or build.yml)" >> $GITHUB_STEP_SUMMARY + echo "✅ No insecure patterns detected" >> $GITHUB_STEP_SUMMARY fi - if [ -f "$WORKFLOWS_DIR/codeql-analysis.yml" ]; then - echo "✅ CodeQL security scanning present" >> $GITHUB_STEP_SUMMARY - else - echo "⚠️ CodeQL workflow not found" >> $GITHUB_STEP_SUMMARY - fi - - - name: Validate Workflow Syntax - run: | - set -x - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Workflow YAML Syntax" >> $GITHUB_STEP_SUMMARY - - INVALID=0 - for workflow in $(find .github/workflows -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null); do - if [ -f "$workflow" ]; then - if python3 -c "import yaml, sys; yaml.safe_load(open(sys.argv[1]))" "$workflow" 2>/dev/null; then - echo "✅ $(basename $workflow)" >> $GITHUB_STEP_SUMMARY - else - echo "❌ $(basename $workflow) - invalid YAML" >> $GITHUB_STEP_SUMMARY - INVALID=$((INVALID + 1)) - fi - fi - done - - if [ "$INVALID" -gt 0 ]; then - echo "" >> $GITHUB_STEP_SUMMARY - echo "### ❌ Validation Failed: Invalid Workflow YAML Syntax" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Error:** $INVALID workflow file(s) have invalid YAML syntax" >> $GITHUB_STEP_SUMMARY - echo "**Action Required:** Fix YAML syntax errors in the marked workflow files" >> $GITHUB_STEP_SUMMARY - echo "**Tool:** Run \`python3 -c \"import yaml; yaml.safe_load(open('.github/workflows/FILE.yml'))\"\` locally" >> $GITHUB_STEP_SUMMARY - echo "" - echo "❌ ERROR: $INVALID workflow file(s) with invalid YAML syntax" - exit 1 - fi - - echo "" >> $GITHUB_STEP_SUMMARY - echo "### ✅ All Workflow Files Have Valid YAML Syntax" >> $GITHUB_STEP_SUMMARY - echo "" - echo "✅ SUCCESS: All workflow files passed YAML validation" - - - name: Validate CodeQL Configuration - if: hashFiles('.github/workflows/codeql-analysis.yml') != '' - run: | - set -e - echo "" >> $GITHUB_STEP_SUMMARY - echo "### CodeQL Language Configuration" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - # Inline validation (rewritten from Python to bash for PHP-only architecture) - CODEQL_FILE=".github/workflows/codeql-analysis.yml" - - if [ ! -f "$CODEQL_FILE" ]; then - echo "⚠️ CodeQL workflow file not found" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### ⚠️ CodeQL Workflow Not Found" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Status:** CodeQL workflow file not present - skipping language validation" >> $GITHUB_STEP_SUMMARY - echo "" - echo "⚠️ INFO: CodeQL workflow not found - Skipping validation" - exit 0 - fi - - echo "**CodeQL Configuration Analysis**" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - # Extract configured languages from workflow - LANGUAGES=$(grep -A5 "language:" "$CODEQL_FILE" | grep -oP "(?<=')[^']+(?=')" | tr '\n' ' ' || echo "") - - # Check if this is a configuration-only scan (no languages specified) - if grep -q "category.*language:config" "$CODEQL_FILE"; then - echo "**Scan Type:** Configuration-only (no language matrix)" >> $GITHUB_STEP_SUMMARY - echo "**Status:** ✅ Valid configuration for PHP-only repository" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "This CodeQL workflow scans YAML, JSON, shell scripts for security issues." >> $GITHUB_STEP_SUMMARY - echo "PHP security is handled by SecurityValidator enterprise library." >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "✅ SUCCESS: CodeQL configuration-only scan properly configured" - exit 0 - fi - - if [ -z "$LANGUAGES" ]; then - echo "❌ No languages configured in CodeQL workflow" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### ❌ Validation Failed: CodeQL Languages Not Configured" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Error:** CodeQL workflow exists but has no languages configured" >> $GITHUB_STEP_SUMMARY - echo "**Action Required:** Configure appropriate languages in codeql-analysis.yml" >> $GITHUB_STEP_SUMMARY - echo "" - echo "❌ ERROR: No languages configured in CodeQL workflow" - exit 1 - fi - - echo "**Configured Languages:** $LANGUAGES" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - # Validate language presence in repository - INVALID_LANGS="" - VALID_LANGS="" - - for LANG in $LANGUAGES; do - case "$LANG" in - python) - # Check for Python files (should be none in v04.00.04) - if find . -name "*.py" -type f ! -path "./.git/*" | grep -q .; then - VALID_LANGS="$VALID_LANGS python" - echo "✅ Python: Found Python files" >> $GITHUB_STEP_SUMMARY - else - INVALID_LANGS="$INVALID_LANGS python" - echo "❌ Python: No Python files found (PHP-only repository)" >> $GITHUB_STEP_SUMMARY - fi - ;; - javascript|typescript) - # Check for JS/TS files - if find . \( -name "*.js" -o -name "*.ts" -o -name "*.json" \) -type f ! -path "./.git/*" ! -path "./node_modules/*" | grep -q .; then - VALID_LANGS="$VALID_LANGS $LANG" - echo "✅ $LANG: Found JavaScript/TypeScript/JSON files" >> $GITHUB_STEP_SUMMARY - else - INVALID_LANGS="$INVALID_LANGS $LANG" - echo "⚠️ $LANG: No JavaScript/TypeScript files found" >> $GITHUB_STEP_SUMMARY - fi - ;; - java) - if find . -name "*.java" -type f ! -path "./.git/*" | grep -q .; then - VALID_LANGS="$VALID_LANGS java" - echo "✅ Java: Found Java files" >> $GITHUB_STEP_SUMMARY - else - INVALID_LANGS="$INVALID_LANGS java" - echo "⚠️ Java: No Java files found" >> $GITHUB_STEP_SUMMARY - fi - ;; - go) - if find . -name "*.go" -type f ! -path "./.git/*" | grep -q .; then - VALID_LANGS="$VALID_LANGS go" - echo "✅ Go: Found Go files" >> $GITHUB_STEP_SUMMARY - else - INVALID_LANGS="$INVALID_LANGS go" - echo "⚠️ Go: No Go files found" >> $GITHUB_STEP_SUMMARY - fi - ;; - cpp|c) - if find . \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -type f ! -path "./.git/*" | grep -q .; then - VALID_LANGS="$VALID_LANGS $LANG" - echo "✅ $LANG: Found C/C++ files" >> $GITHUB_STEP_SUMMARY - else - INVALID_LANGS="$INVALID_LANGS $LANG" - echo "⚠️ $LANG: No C/C++ files found" >> $GITHUB_STEP_SUMMARY - fi - ;; - ruby) - if find . -name "*.rb" -type f ! -path "./.git/*" | grep -q .; then - VALID_LANGS="$VALID_LANGS ruby" - echo "✅ Ruby: Found Ruby files" >> $GITHUB_STEP_SUMMARY - else - INVALID_LANGS="$INVALID_LANGS ruby" - echo "⚠️ Ruby: No Ruby files found" >> $GITHUB_STEP_SUMMARY - fi - ;; - *) - echo "⚠️ $LANG: Unknown language, skipping validation" >> $GITHUB_STEP_SUMMARY - ;; - esac - done - - echo "" >> $GITHUB_STEP_SUMMARY - - # Report results - if [ -n "$INVALID_LANGS" ]; then - echo "**⚠️ Warning:** Some configured languages may not have corresponding files:" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "Invalid languages: $INVALID_LANGS" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Note:** This is informational. CodeQL will skip languages without source files." >> $GITHUB_STEP_SUMMARY - echo "For PHP repository (v04.00.04), JavaScript language covers JSON/YAML/shell scripts." >> $GITHUB_STEP_SUMMARY - else - echo "✅ **All configured CodeQL languages have corresponding source files**" >> $GITHUB_STEP_SUMMARY - fi - - # Always succeed - this is informational only - echo "" >> $GITHUB_STEP_SUMMARY - echo "### ✅ CodeQL Configuration Validation Complete" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Status:** CodeQL language configuration reviewed successfully" >> $GITHUB_STEP_SUMMARY - echo "" - echo "✅ SUCCESS: CodeQL validation complete" - exit 0 - - version-consistency: - name: Version Consistency Check + code-complexity: + name: Code Complexity Analysis runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Set up PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + - name: Setup PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0 with: php-version: '8.1' - extensions: json - tools: composer - coverage: none - - name: Install API Package - run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader - env: - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' - - - name: Run Version Consistency Check - id: version_check + - name: Analyze Complexity run: | set -x - echo "## 🔢 Version Consistency Validation" >> $GITHUB_STEP_SUMMARY + echo "## 📊 Code Complexity Analysis" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - if [ -f "api/validate/check_version_consistency.php" ]; then - php api/validate/check_version_consistency.php --verbose | tee /tmp/version-check.log - EXIT_CODE=$? + PHP_COUNT=$(find . -name "*.php" ! -path "./vendor/*" ! -path "./.git/*" | wc -l) - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - cat /tmp/version-check.log >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + if [ "$PHP_COUNT" -gt 0 ]; then + # Install phploc + wget https://phar.phpunit.de/phploc.phar 2>/dev/null + chmod +x phploc.phar - if [ "$EXIT_CODE" -eq 0 ]; then + echo "### PHP Code Metrics" >> $GITHUB_STEP_SUMMARY + if ./phploc.phar --exclude vendor --exclude .git . 2>&1 | tee /tmp/phploc.txt; then + COMPLEXITY=$(grep "Cyclomatic Complexity" /tmp/phploc.txt | grep "Average" | awk '{print $NF}' || echo "N/A") + echo "**Average Cyclomatic Complexity**: $COMPLEXITY" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "✅ All version numbers are consistent!" >> $GITHUB_STEP_SUMMARY - exit 0 - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "❌ Version mismatches detected - Please update all version references" >> $GITHUB_STEP_SUMMARY - exit 1 + + if [ "$COMPLEXITY" != "N/A" ] && [ $(echo "$COMPLEXITY > 10" | bc -l) -eq 1 ]; then + echo "⚠️ Average complexity exceeds recommended threshold (10)" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Refactor complex functions" >> $GITHUB_STEP_SUMMARY + else + echo "✅ Code complexity within acceptable limits" >> $GITHUB_STEP_SUMMARY + fi fi else - echo "ℹ️ Version consistency check script not found - skipping" >> $GITHUB_STEP_SUMMARY - exit 0 + echo "ℹ️ No PHP files found for complexity analysis" >> $GITHUB_STEP_SUMMARY fi - script-integrity: - name: Script Integrity Validation + code-duplication: + name: Code Duplication Detection runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Set up Python + - name: Setup PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0 + with: + php-version: '8.1' + + - name: Detect Duplicates + run: | + set -x + echo "## 🔁 Code Duplication Detection" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Check if PHP files exist + PHP_COUNT=$(find . -name "*.php" ! -path "./vendor/*" ! -path "./.git/*" | wc -l) + + if [ "$PHP_COUNT" -gt 0 ]; then + echo "### PHP Code Duplication" >> $GITHUB_STEP_SUMMARY + + # Install phpcpd + wget https://phar.phpunit.de/phpcpd.phar 2>/dev/null + chmod +x phpcpd.phar + + # Run duplication detection + if ./phpcpd.phar --exclude vendor --exclude .git . 2>&1 | tee /tmp/phpcpd.txt; then + DUPLICATION=$(grep "Found" /tmp/phpcpd.txt | grep -oE "[0-9]+\.[0-9]+%" | head -1 || echo "0.00%") + echo "📊 **Duplication Rate**: $DUPLICATION" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + DUPLICATION_NUM=$(echo "$DUPLICATION" | sed 's/%//') + if [ $(echo "$DUPLICATION_NUM > 5.0" | bc -l) -eq 1 ]; then + echo "⚠️ Code duplication exceeds 5% threshold" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View duplication details" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/phpcpd.txt >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "✅ Code duplication within acceptable limits (<5%)" >> $GITHUB_STEP_SUMMARY + fi + else + echo "✅ No significant code duplication detected" >> $GITHUB_STEP_SUMMARY + fi + else + echo "ℹ️ No PHP files found for duplication analysis" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note**: This is an informational check to encourage DRY principles." >> $GITHUB_STEP_SUMMARY + + dead-code-detection: + name: Dead Code Detection + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.x' - - name: Validate Script Integrity - id: script_check + - name: Detect Dead Code run: | set -x - echo "## 🔐 Script Integrity Validation" >> $GITHUB_STEP_SUMMARY + echo "## 🗑️ Dead Code Detection" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - if [ -f "api/.script-registry.json" ]; then - echo "### Critical Scripts" >> $GITHUB_STEP_SUMMARY - php api/maintenance/update_sha_hashes.php \ - --dry-run --verbose | tee /tmp/script-validation.log + PY_COUNT=$(find . -name "*.py" ! -path "./vendor/*" ! -path "./.git/*" ! -path "./venv/*" | wc -l) - EXIT_CODE=$? + if [ "$PY_COUNT" -gt 0 ]; then + pip install vulture 2>/dev/null + echo "### Python Dead Code" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - cat /tmp/script-validation.log >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - - if [ "$EXIT_CODE" -eq 0 ]; then - echo "" >> $GITHUB_STEP_SUMMARY - echo "✅ All critical scripts validated successfully!" >> $GITHUB_STEP_SUMMARY - exit 0 - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "❌ Script integrity violations detected" >> $GITHUB_STEP_SUMMARY - echo "**Action Required:** Review validation report and update registry" >> $GITHUB_STEP_SUMMARY - exit 1 + if vulture . --exclude vendor,venv,.git 2>&1 | tee /tmp/vulture.txt; then + DEAD_COUNT=$(wc -l < /tmp/vulture.txt || echo 0) + if [ "$DEAD_COUNT" -gt 0 ]; then + echo "⚠️ Found $DEAD_COUNT potential dead code item(s)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View dead code" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + head -50 /tmp/vulture.txt >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "✅ No dead code detected" >> $GITHUB_STEP_SUMMARY + fi fi else - echo "ℹ️ Script registry not found - skipping integrity check" >> $GITHUB_STEP_SUMMARY - exit 0 + echo "ℹ️ No Python files found for dead code analysis" >> $GITHUB_STEP_SUMMARY fi - enterprise-readiness: - name: Enterprise Readiness Check + + # ════════════════════════════════════════════════════════════════════════ + # TIER 4 — SUPPLEMENTARY (informational) + # ════════════════════════════════════════════════════════════════════════ + file-size-limits: + name: File Size Limits runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Set up PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 - with: - php-version: '8.1' - extensions: json, mbstring - tools: composer - coverage: none - - - name: Install API Package - run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader - env: - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' - - - name: Check Enterprise Readiness - id: enterprise_check + - name: Check File Sizes run: | + set -x + echo "## 📦 File Size Validation" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - if [ -f "api/validate/check_enterprise_readiness.php" ]; then - php api/validate/check_enterprise_readiness.php --verbose | tee /tmp/enterprise-check.log - EXIT_CODE=$? + # Exempt file types (allowed to be large) + EXEMPT="! -name *.mmdb ! -name *.woff2 ! -name *.woff ! -name *.ttf ! -name *.otf" + # Find large files (>15MB warning, >20MB critical) + LARGE_FILES=$(find . -type f -size +15M $EXEMPT ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" 2>/dev/null | wc -l) + HUGE_FILES=$(find . -type f -size +20M $EXEMPT ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" 2>/dev/null | wc -l) + + echo "### Size Thresholds" >> $GITHUB_STEP_SUMMARY + echo "- **Warning**: Files >15MB" >> $GITHUB_STEP_SUMMARY + echo "- **Critical**: Files >20MB" >> $GITHUB_STEP_SUMMARY + echo "- **Exempt**: .mmdb, .woff2, .woff, .ttf, .otf" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$HUGE_FILES" -gt 0 ]; then + echo "❌ **Critical**: Found $HUGE_FILES file(s) exceeding 20MB" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View files >20MB" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - cat /tmp/enterprise-check.log >> $GITHUB_STEP_SUMMARY + find . -type f -size +20M $EXEMPT ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -exec ls -lh {} + 2>/dev/null | awk '{print $5, $9}' >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - - if [ "$EXIT_CODE" -eq 0 ]; then - echo "" >> $GITHUB_STEP_SUMMARY - echo "✅ Repository meets enterprise readiness criteria!" >> $GITHUB_STEP_SUMMARY - exit 0 - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "⚠️ Enterprise readiness issues detected" >> $GITHUB_STEP_SUMMARY - echo "**Note:** This is informational - review recommendations to improve" >> $GITHUB_STEP_SUMMARY - exit 0 # Non-blocking - fi + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Action Required**: Remove or optimize files >20MB" >> $GITHUB_STEP_SUMMARY + exit 1 + elif [ "$LARGE_FILES" -gt 0 ]; then + echo "⚠️ **Warning**: Found $LARGE_FILES file(s) between 15MB and 20MB" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View files >15MB" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + find . -type f -size +15M $EXEMPT ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -exec ls -lh {} + 2>/dev/null | awk '{print $5, $9}' >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Consider optimizing large files" >> $GITHUB_STEP_SUMMARY else - echo "ℹ️ Enterprise readiness check script not found - skipping" >> $GITHUB_STEP_SUMMARY - exit 0 + echo "✅ All files within acceptable size limits" >> $GITHUB_STEP_SUMMARY fi - repository-health: - name: Repository Health Check + binary-file-detection: + name: Binary File Detection runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Set up PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 - with: - php-version: '8.1' - extensions: json, mbstring - tools: composer - coverage: none - - - name: Install API Package - run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader - env: - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' - - - name: Check Repository Health - id: health_check + - name: Detect Binary Files run: | + set -x + echo "## 🔍 Binary File Detection" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - if [ -f "api/validate/check_repo_health.php" ]; then - php api/validate/check_repo_health.php --verbose | tee /tmp/health-check.log - EXIT_CODE=$? + # Find binary files excluding allowed types + BINARIES=$(find . -type f ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" \ + ! -name "*.png" ! -name "*.jpg" ! -name "*.jpeg" ! -name "*.gif" ! -name "*.svg" ! -name "*.ico" \ + ! -name "*.woff" ! -name "*.woff2" ! -name "*.ttf" ! -name "*.eot" \ + -exec file {} \; | grep -v "text" | grep -v "empty" | wc -l || echo 0) + if [ "$BINARIES" -gt 0 ]; then + echo "⚠️ Found $BINARIES non-image binary file(s)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View binary files" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - cat /tmp/health-check.log >> $GITHUB_STEP_SUMMARY + find . -type f ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" \ + ! -name "*.png" ! -name "*.jpg" ! -name "*.jpeg" ! -name "*.gif" ! -name "*.svg" ! -name "*.ico" \ + ! -name "*.woff" ! -name "*.woff2" ! -name "*.ttf" ! -name "*.eot" \ + -exec file {} \; | grep -v "text" | grep -v "empty" | cut -d: -f1 >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - - if [ "$EXIT_CODE" -eq 0 ]; then - echo "" >> $GITHUB_STEP_SUMMARY - echo "✅ Repository health check passed!" >> $GITHUB_STEP_SUMMARY - exit 0 - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "⚠️ Repository health issues detected" >> $GITHUB_STEP_SUMMARY - echo "**Note:** This is informational - review recommendations to improve" >> $GITHUB_STEP_SUMMARY - exit 0 # Non-blocking - fi + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Source control should primarily contain text files" >> $GITHUB_STEP_SUMMARY else - echo "ℹ️ Repository health check script not found - skipping" >> $GITHUB_STEP_SUMMARY - exit 0 + echo "✅ No unexpected binary files detected" >> $GITHUB_STEP_SUMMARY fi + # ============================================================================ + # PHASE 4: Nice to Have Checks + # ============================================================================ + todo-fixme-tracking: name: TODO/FIXME Tracking runs-on: ubuntu-latest @@ -1181,154 +1616,131 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "**Note**: This is an informational check. Technical debt items don't block compliance." >> $GITHUB_STEP_SUMMARY - file-size-limits: - name: File Size Limits + dependency-vulnerabilities: + name: Dependency Vulnerability Scanning runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Check File Sizes + - name: Setup PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0 + with: + php-version: '8.1' + + - name: Setup Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.x' + + - name: Scan Dependencies run: | set -x - echo "## 📦 File Size Validation" >> $GITHUB_STEP_SUMMARY + echo "## 🛡️ Dependency Vulnerability Scanning" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - # Find large files (>1MB) - LARGE_FILES=$(find . -type f -size +1M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" 2>/dev/null | wc -l) - HUGE_FILES=$(find . -type f -size +15M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" 2>/dev/null | wc -l) + VULNERABILITIES=0 - echo "### Size Thresholds" >> $GITHUB_STEP_SUMMARY - echo "- **Warning**: Files >1MB" >> $GITHUB_STEP_SUMMARY - echo "- **Critical**: Files >15MB" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - if [ "$HUGE_FILES" -gt 0 ]; then - echo "❌ **Critical**: Found $HUGE_FILES file(s) exceeding 15MB" >> $GITHUB_STEP_SUMMARY + # PHP Dependencies + if [ -f "composer.json" ]; then + echo "### PHP Dependencies (composer)" >> $GITHUB_STEP_SUMMARY + if composer audit --no-dev 2>&1 | tee /tmp/php_audit.txt; then + echo "✅ No PHP vulnerabilities detected" >> $GITHUB_STEP_SUMMARY + else + VULN_COUNT=$(grep -c "vulnerability" /tmp/php_audit.txt || echo 0) + echo "⚠️ Found $VULN_COUNT PHP vulnerability/vulnerabilities" >> $GITHUB_STEP_SUMMARY + VULNERABILITIES=$((VULNERABILITIES + VULN_COUNT)) + fi echo "" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "View files >15MB" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - find . -type f -size +15M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -exec ls -lh {} + 2>/dev/null | awk '{print $5, $9}' >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Action Required**: Remove or optimize files >15MB" >> $GITHUB_STEP_SUMMARY - exit 1 - elif [ "$LARGE_FILES" -gt 0 ]; then - echo "⚠️ **Warning**: Found $LARGE_FILES file(s) between 1MB and 15MB" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "View files >1MB" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - find . -type f -size +1M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -exec ls -lh {} + 2>/dev/null | awk '{print $5, $9}' >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Recommendation**: Consider optimizing or documenting large files" >> $GITHUB_STEP_SUMMARY - else - echo "✅ All files within acceptable size limits (<1MB)" >> $GITHUB_STEP_SUMMARY fi - secret-scanning: - name: Secret Scanning + # Python Dependencies + if [ -f "requirements.txt" ]; then + echo "### Python Dependencies" >> $GITHUB_STEP_SUMMARY + pip install pip-audit 2>&1 > /dev/null + if pip-audit -r requirements.txt 2>&1 | tee /tmp/py_audit.txt; then + echo "✅ No Python vulnerabilities detected" >> $GITHUB_STEP_SUMMARY + else + VULN_COUNT=$(grep -c "vulnerability" /tmp/py_audit.txt || echo 0) + echo "⚠️ Found $VULN_COUNT Python vulnerability/vulnerabilities" >> $GITHUB_STEP_SUMMARY + VULNERABILITIES=$((VULNERABILITIES + VULN_COUNT)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + fi + + # NPM Dependencies + if [ -f "package.json" ]; then + echo "### NPM Dependencies" >> $GITHUB_STEP_SUMMARY + if npm audit --production 2>&1 | tee /tmp/npm_audit.txt; then + echo "✅ No NPM vulnerabilities detected" >> $GITHUB_STEP_SUMMARY + else + VULN_COUNT=$(grep -c "vulnerability" /tmp/npm_audit.txt || echo 0) + echo "⚠️ Found $VULN_COUNT NPM vulnerability/vulnerabilities" >> $GITHUB_STEP_SUMMARY + VULNERABILITIES=$((VULNERABILITIES + VULN_COUNT)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + fi + + if [ "$VULNERABILITIES" -gt 0 ]; then + echo "**Total Vulnerabilities**: $VULNERABILITIES" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Action Required**: Update vulnerable dependencies" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "✅ No dependency vulnerabilities detected" >> $GITHUB_STEP_SUMMARY + fi + + unused-dependencies: + name: Unused Dependencies Check runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Scan for Secrets + - name: Setup PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0 + with: + php-version: '8.1' + + - name: Check Unused Dependencies run: | set -x - echo "## 🔒 Secret Scanning" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Scanning for hardcoded secrets and credentials." >> $GITHUB_STEP_SUMMARY + echo "## 📦 Unused Dependencies Check" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - # Define secret patterns - VIOLATIONS=0 + if [ -f "composer.json" ]; then + echo "### PHP Dependencies" >> $GITHUB_STEP_SUMMARY - # Check for common secret patterns - echo "### Secret Patterns" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + # Install composer-unused + composer global require icanhazstring/composer-unused 2>/dev/null || true - # Pattern 1: password/secret assignments - # Exclusions: - # test|example|sample - test/example files - # getenv - environment-variable reads - # /\.\*/|^\s*// - regex patterns and commented lines - # CREDENTIAL_PATTERNS|SecurityValidator|SECRET_PATTERN - scanner internals - # ===|!== - strict comparison operators (not assignments) - # ApiClient - constructor calls where token is a variable arg - if grep -r -n -E "(password|passwd|pwd|secret|api[_-]?key|token).*=.*['\"]" . \ - --include="*.php" --include="*.py" --include="*.js" --include="*.ts" \ - --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null | \ - grep -v -E '(test|example|sample|getenv|/\.\*/|^\s*//|CREDENTIAL_PATTERNS|SecurityValidator|SECRET_PATTERN|===|!==|ApiClient)' | \ - grep -v "= ''" | grep -v '= ""' > /tmp/secrets1.txt 2>/dev/null; then - COUNT=$(wc -l < /tmp/secrets1.txt) - if [ "$COUNT" -gt 0 ]; then - echo "⚠️ Found $COUNT potential secret assignment(s)" >> $GITHUB_STEP_SUMMARY - VIOLATIONS=$((VIOLATIONS + COUNT)) + if composer global exec composer-unused 2>&1 | tee /tmp/unused.txt; then + UNUSED_COUNT=$(grep "unused" /tmp/unused.txt | wc -l || echo 0) + if [ "$UNUSED_COUNT" -gt 0 ]; then + echo "⚠️ Found $UNUSED_COUNT unused dependency/dependencies" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View unused dependencies" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/unused.txt >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "✅ No unused dependencies detected" >> $GITHUB_STEP_SUMMARY + fi + else + echo "✅ All dependencies appear to be in use" >> $GITHUB_STEP_SUMMARY fi - fi - - # Pattern 2: Private keys - if grep -r -n "BEGIN.*PRIVATE KEY" . \ - --include="*.pem" --include="*.key" --include="*.txt" \ - --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null > /tmp/secrets2.txt; then - COUNT=$(wc -l < /tmp/secrets2.txt) - if [ "$COUNT" -gt 0 ]; then - echo "❌ Found $COUNT private key file(s)" >> $GITHUB_STEP_SUMMARY - VIOLATIONS=$((VIOLATIONS + COUNT)) - fi - fi - - # Pattern 3: AWS keys - if grep -r -n -E "AKIA[0-9A-Z]{16}" . \ - --include="*.php" --include="*.py" --include="*.js" --include="*.txt" --include="*.env" \ - --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null > /tmp/secrets3.txt; then - COUNT=$(wc -l < /tmp/secrets3.txt) - if [ "$COUNT" -gt 0 ]; then - echo "❌ Found $COUNT potential AWS access key(s)" >> $GITHUB_STEP_SUMMARY - VIOLATIONS=$((VIOLATIONS + COUNT)) - fi - fi - - # Pattern 4: GitHub tokens - if grep -r -n -E "gh[ps]_[a-zA-Z0-9]{36}" . \ - --include="*.php" --include="*.py" --include="*.js" --include="*.txt" --include="*.env" \ - --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null > /tmp/secrets4.txt; then - COUNT=$(wc -l < /tmp/secrets4.txt) - if [ "$COUNT" -gt 0 ]; then - echo "❌ Found $COUNT potential GitHub token(s)" >> $GITHUB_STEP_SUMMARY - VIOLATIONS=$((VIOLATIONS + COUNT)) - fi - fi - - echo "" >> $GITHUB_STEP_SUMMARY - - if [ "$VIOLATIONS" -gt 0 ]; then - echo "**Total Violations**: $VIOLATIONS" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "View detected secrets (file paths only)" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - cat /tmp/secrets*.txt 2>/dev/null | cut -d: -f1 | sort -u >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Action Required**: Remove hardcoded secrets immediately!" >> $GITHUB_STEP_SUMMARY - echo "Use environment variables or secrets management instead." >> $GITHUB_STEP_SUMMARY - exit 1 else - echo "✅ No hardcoded secrets detected" >> $GITHUB_STEP_SUMMARY + echo "ℹ️ No composer.json found" >> $GITHUB_STEP_SUMMARY fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Remove unused dependencies to reduce attack surface" >> $GITHUB_STEP_SUMMARY + broken-link-detection: name: Broken Link Detection runs-on: ubuntu-latest @@ -1418,283 +1830,6 @@ jobs: # PHASE 2: Medium Priority Checks # ============================================================================ - dependency-vulnerabilities: - name: Dependency Vulnerability Scanning - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 - with: - php-version: '8.1' - - - name: Setup Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: '3.x' - - - name: Scan Dependencies - run: | - set -x - echo "## 🛡️ Dependency Vulnerability Scanning" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - VULNERABILITIES=0 - - # PHP Dependencies - if [ -f "composer.json" ]; then - echo "### PHP Dependencies (composer)" >> $GITHUB_STEP_SUMMARY - if composer audit --no-dev 2>&1 | tee /tmp/php_audit.txt; then - echo "✅ No PHP vulnerabilities detected" >> $GITHUB_STEP_SUMMARY - else - VULN_COUNT=$(grep -c "vulnerability" /tmp/php_audit.txt || echo 0) - echo "⚠️ Found $VULN_COUNT PHP vulnerability/vulnerabilities" >> $GITHUB_STEP_SUMMARY - VULNERABILITIES=$((VULNERABILITIES + VULN_COUNT)) - fi - echo "" >> $GITHUB_STEP_SUMMARY - fi - - # Python Dependencies - if [ -f "requirements.txt" ]; then - echo "### Python Dependencies" >> $GITHUB_STEP_SUMMARY - pip install pip-audit 2>&1 > /dev/null - if pip-audit -r requirements.txt 2>&1 | tee /tmp/py_audit.txt; then - echo "✅ No Python vulnerabilities detected" >> $GITHUB_STEP_SUMMARY - else - VULN_COUNT=$(grep -c "vulnerability" /tmp/py_audit.txt || echo 0) - echo "⚠️ Found $VULN_COUNT Python vulnerability/vulnerabilities" >> $GITHUB_STEP_SUMMARY - VULNERABILITIES=$((VULNERABILITIES + VULN_COUNT)) - fi - echo "" >> $GITHUB_STEP_SUMMARY - fi - - # NPM Dependencies - if [ -f "package.json" ]; then - echo "### NPM Dependencies" >> $GITHUB_STEP_SUMMARY - if npm audit --production 2>&1 | tee /tmp/npm_audit.txt; then - echo "✅ No NPM vulnerabilities detected" >> $GITHUB_STEP_SUMMARY - else - VULN_COUNT=$(grep -c "vulnerability" /tmp/npm_audit.txt || echo 0) - echo "⚠️ Found $VULN_COUNT NPM vulnerability/vulnerabilities" >> $GITHUB_STEP_SUMMARY - VULNERABILITIES=$((VULNERABILITIES + VULN_COUNT)) - fi - echo "" >> $GITHUB_STEP_SUMMARY - fi - - if [ "$VULNERABILITIES" -gt 0 ]; then - echo "**Total Vulnerabilities**: $VULNERABILITIES" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Action Required**: Update vulnerable dependencies" >> $GITHUB_STEP_SUMMARY - exit 1 - else - echo "✅ No dependency vulnerabilities detected" >> $GITHUB_STEP_SUMMARY - fi - - code-duplication: - name: Code Duplication Detection - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 - with: - php-version: '8.1' - - - name: Detect Duplicates - run: | - set -x - echo "## 🔁 Code Duplication Detection" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - # Check if PHP files exist - PHP_COUNT=$(find . -name "*.php" ! -path "./vendor/*" ! -path "./.git/*" | wc -l) - - if [ "$PHP_COUNT" -gt 0 ]; then - echo "### PHP Code Duplication" >> $GITHUB_STEP_SUMMARY - - # Install phpcpd - wget https://phar.phpunit.de/phpcpd.phar 2>/dev/null - chmod +x phpcpd.phar - - # Run duplication detection - if ./phpcpd.phar --exclude vendor --exclude .git . 2>&1 | tee /tmp/phpcpd.txt; then - DUPLICATION=$(grep "Found" /tmp/phpcpd.txt | grep -oE "[0-9]+\.[0-9]+%" | head -1 || echo "0.00%") - echo "📊 **Duplication Rate**: $DUPLICATION" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - DUPLICATION_NUM=$(echo "$DUPLICATION" | sed 's/%//') - if [ $(echo "$DUPLICATION_NUM > 5.0" | bc -l) -eq 1 ]; then - echo "⚠️ Code duplication exceeds 5% threshold" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "View duplication details" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - cat /tmp/phpcpd.txt >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - else - echo "✅ Code duplication within acceptable limits (<5%)" >> $GITHUB_STEP_SUMMARY - fi - else - echo "✅ No significant code duplication detected" >> $GITHUB_STEP_SUMMARY - fi - else - echo "ℹ️ No PHP files found for duplication analysis" >> $GITHUB_STEP_SUMMARY - fi - - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Note**: This is an informational check to encourage DRY principles." >> $GITHUB_STEP_SUMMARY - - unused-dependencies: - name: Unused Dependencies Check - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 - with: - php-version: '8.1' - - - name: Check Unused Dependencies - run: | - set -x - echo "## 📦 Unused Dependencies Check" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - if [ -f "composer.json" ]; then - echo "### PHP Dependencies" >> $GITHUB_STEP_SUMMARY - - # Install composer-unused - composer global require icanhazstring/composer-unused 2>/dev/null || true - - if composer global exec composer-unused 2>&1 | tee /tmp/unused.txt; then - UNUSED_COUNT=$(grep "unused" /tmp/unused.txt | wc -l || echo 0) - if [ "$UNUSED_COUNT" -gt 0 ]; then - echo "⚠️ Found $UNUSED_COUNT unused dependency/dependencies" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "View unused dependencies" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - cat /tmp/unused.txt >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - else - echo "✅ No unused dependencies detected" >> $GITHUB_STEP_SUMMARY - fi - else - echo "✅ All dependencies appear to be in use" >> $GITHUB_STEP_SUMMARY - fi - else - echo "ℹ️ No composer.json found" >> $GITHUB_STEP_SUMMARY - fi - - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Recommendation**: Remove unused dependencies to reduce attack surface" >> $GITHUB_STEP_SUMMARY - - readme-completeness: - name: README Completeness Check - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Check README Sections - run: | - set -x - echo "## 📄 README Completeness Check" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - if [ ! -f "README.md" ]; then - echo "❌ README.md not found" >> $GITHUB_STEP_SUMMARY - exit 1 - fi - - # Required sections - REQUIRED_SECTIONS=("Installation" "Usage" "Contributing" "License") - MISSING=0 - PRESENT=0 - - echo "### Required Sections" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - for section in "${REQUIRED_SECTIONS[@]}"; do - if grep -qi "##.*$section" README.md; then - echo "✅ $section" >> $GITHUB_STEP_SUMMARY - PRESENT=$((PRESENT + 1)) - else - echo "❌ $section" >> $GITHUB_STEP_SUMMARY - MISSING=$((MISSING + 1)) - fi - done - - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Completeness**: $PRESENT/${#REQUIRED_SECTIONS[@]} required sections present" >> $GITHUB_STEP_SUMMARY - - if [ "$MISSING" -gt 0 ]; then - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Action Required**: Add missing sections to README.md" >> $GITHUB_STEP_SUMMARY - exit 1 - fi - - # ============================================================================ - # PHASE 3: Future Enhancements - # ============================================================================ - - code-complexity: - name: Code Complexity Analysis - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 - with: - php-version: '8.1' - - - name: Analyze Complexity - run: | - set -x - echo "## 📊 Code Complexity Analysis" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - PHP_COUNT=$(find . -name "*.php" ! -path "./vendor/*" ! -path "./.git/*" | wc -l) - - if [ "$PHP_COUNT" -gt 0 ]; then - # Install phploc - wget https://phar.phpunit.de/phploc.phar 2>/dev/null - chmod +x phploc.phar - - echo "### PHP Code Metrics" >> $GITHUB_STEP_SUMMARY - if ./phploc.phar --exclude vendor --exclude .git . 2>&1 | tee /tmp/phploc.txt; then - COMPLEXITY=$(grep "Cyclomatic Complexity" /tmp/phploc.txt | grep "Average" | awk '{print $NF}' || echo "N/A") - echo "**Average Cyclomatic Complexity**: $COMPLEXITY" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - if [ "$COMPLEXITY" != "N/A" ] && [ $(echo "$COMPLEXITY > 10" | bc -l) -eq 1 ]; then - echo "⚠️ Average complexity exceeds recommended threshold (10)" >> $GITHUB_STEP_SUMMARY - echo "**Recommendation**: Refactor complex functions" >> $GITHUB_STEP_SUMMARY - else - echo "✅ Code complexity within acceptable limits" >> $GITHUB_STEP_SUMMARY - fi - fi - else - echo "ℹ️ No PHP files found for complexity analysis" >> $GITHUB_STEP_SUMMARY - fi - api-documentation: name: API Documentation Coverage runs-on: ubuntu-latest @@ -1728,176 +1863,6 @@ jobs: echo "ℹ️ No public methods found for documentation check" >> $GITHUB_STEP_SUMMARY fi - insecure-patterns: - name: Insecure Code Pattern Detection - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Scan for Insecure Patterns - run: | - set -x - echo "## 🔒 Insecure Code Pattern Detection" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - VIOLATIONS=0 - - # PHP: SQL injection patterns - if grep -r -n "\\$_\(GET\|POST\|REQUEST\).*mysql_query\|mysqli_query" . --include="*.php" ! -path "./vendor/*" 2>/dev/null > /tmp/sql_inject.txt; then - COUNT=$(wc -l < /tmp/sql_inject.txt) - echo "⚠️ Found $COUNT potential SQL injection pattern(s)" >> $GITHUB_STEP_SUMMARY - VIOLATIONS=$((VIOLATIONS + COUNT)) - fi - - # PHP: eval/exec usage - if grep -r -n "eval\|exec\|system\|passthru\|shell_exec" . --include="*.php" ! -path "./vendor/*" 2>/dev/null > /tmp/exec.txt; then - COUNT=$(wc -l < /tmp/exec.txt) - echo "⚠️ Found $COUNT dangerous function call(s)" >> $GITHUB_STEP_SUMMARY - VIOLATIONS=$((VIOLATIONS + COUNT)) - fi - - # Python: eval usage - if grep -r -n "eval(" . --include="*.py" 2>/dev/null > /tmp/py_eval.txt; then - COUNT=$(wc -l < /tmp/py_eval.txt) - echo "⚠️ Found $COUNT Python eval() usage(s)" >> $GITHUB_STEP_SUMMARY - VIOLATIONS=$((VIOLATIONS + COUNT)) - fi - - echo "" >> $GITHUB_STEP_SUMMARY - - if [ "$VIOLATIONS" -gt 0 ]; then - echo "**Total Violations**: $VIOLATIONS" >> $GITHUB_STEP_SUMMARY - echo "**Recommendation**: Review and secure flagged patterns" >> $GITHUB_STEP_SUMMARY - else - echo "✅ No insecure patterns detected" >> $GITHUB_STEP_SUMMARY - fi - - binary-file-detection: - name: Binary File Detection - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Detect Binary Files - run: | - set -x - echo "## 🔍 Binary File Detection" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - # Find binary files excluding allowed types - BINARIES=$(find . -type f ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" \ - ! -name "*.png" ! -name "*.jpg" ! -name "*.jpeg" ! -name "*.gif" ! -name "*.svg" ! -name "*.ico" \ - ! -name "*.woff" ! -name "*.woff2" ! -name "*.ttf" ! -name "*.eot" \ - -exec file {} \; | grep -v "text" | grep -v "empty" | wc -l || echo 0) - - if [ "$BINARIES" -gt 0 ]; then - echo "⚠️ Found $BINARIES non-image binary file(s)" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "View binary files" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - find . -type f ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" \ - ! -name "*.png" ! -name "*.jpg" ! -name "*.jpeg" ! -name "*.gif" ! -name "*.svg" ! -name "*.ico" \ - ! -name "*.woff" ! -name "*.woff2" ! -name "*.ttf" ! -name "*.eot" \ - -exec file {} \; | grep -v "text" | grep -v "empty" | cut -d: -f1 >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Recommendation**: Source control should primarily contain text files" >> $GITHUB_STEP_SUMMARY - else - echo "✅ No unexpected binary files detected" >> $GITHUB_STEP_SUMMARY - fi - - # ============================================================================ - # PHASE 4: Nice to Have Checks - # ============================================================================ - - dead-code-detection: - name: Dead Code Detection - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: '3.x' - - - name: Detect Dead Code - run: | - set -x - echo "## 🗑️ Dead Code Detection" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - PY_COUNT=$(find . -name "*.py" ! -path "./vendor/*" ! -path "./.git/*" ! -path "./venv/*" | wc -l) - - if [ "$PY_COUNT" -gt 0 ]; then - pip install vulture 2>/dev/null - echo "### Python Dead Code" >> $GITHUB_STEP_SUMMARY - - if vulture . --exclude vendor,venv,.git 2>&1 | tee /tmp/vulture.txt; then - DEAD_COUNT=$(wc -l < /tmp/vulture.txt || echo 0) - if [ "$DEAD_COUNT" -gt 0 ]; then - echo "⚠️ Found $DEAD_COUNT potential dead code item(s)" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - echo "View dead code" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - head -50 /tmp/vulture.txt >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY - else - echo "✅ No dead code detected" >> $GITHUB_STEP_SUMMARY - fi - fi - else - echo "ℹ️ No Python files found for dead code analysis" >> $GITHUB_STEP_SUMMARY - fi - - file-naming-standards: - name: File Naming Standards - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Check File Naming - run: | - set -x - echo "## 📝 File Naming Standards" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - VIOLATIONS=0 - - # Check PHP files (should be PascalCase for classes) - INVALID_PHP=$(find . -name "*.php" ! -path "./vendor/*" ! -path "./.git/*" ! -regex ".*/[A-Z][a-zA-Z0-9]*\.php" ! -name "index.php" ! -name "functions.php" | wc -l || echo 0) - - # Check config files (should be kebab-case) - INVALID_CONFIG=$(find . -name "*.yml" -o -name "*.yaml" -o -name "*.json" ! -path "./vendor/*" ! -path "./.git/*" ! -path "./node_modules/*" | grep -E "[A-Z_]" | wc -l || echo 0) - - echo "### Naming Violations" >> $GITHUB_STEP_SUMMARY - echo "- **PHP files not PascalCase**: $INVALID_PHP" >> $GITHUB_STEP_SUMMARY - echo "- **Config files not kebab-case**: $INVALID_CONFIG" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - VIOLATIONS=$((INVALID_PHP + INVALID_CONFIG)) - - if [ "$VIOLATIONS" -gt 0 ]; then - echo "⚠️ Found $VIOLATIONS naming convention violation(s)" >> $GITHUB_STEP_SUMMARY - echo "**Recommendation**: Follow naming conventions for consistency" >> $GITHUB_STEP_SUMMARY - else - echo "✅ File naming conventions followed" >> $GITHUB_STEP_SUMMARY - fi - accessibility-check: name: Accessibility Check runs-on: ubuntu-latest @@ -1980,6 +1945,106 @@ jobs: echo "ℹ️ Not a JavaScript project" >> $GITHUB_STEP_SUMMARY fi + enterprise-readiness: + name: Enterprise Readiness Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0 + with: + php-version: '8.1' + extensions: json, mbstring + tools: composer + coverage: none + + - name: Install API Package + run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader + env: + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' + + - name: Check Enterprise Readiness + id: enterprise_check + run: | + echo "" >> $GITHUB_STEP_SUMMARY + + if [ -f "api/validate/check_enterprise_readiness.php" ]; then + php api/validate/check_enterprise_readiness.php --verbose | tee /tmp/enterprise-check.log + EXIT_CODE=$? + + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/enterprise-check.log >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + + if [ "$EXIT_CODE" -eq 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "✅ Repository meets enterprise readiness criteria!" >> $GITHUB_STEP_SUMMARY + exit 0 + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "⚠️ Enterprise readiness issues detected" >> $GITHUB_STEP_SUMMARY + echo "**Note:** This is informational - review recommendations to improve" >> $GITHUB_STEP_SUMMARY + exit 0 # Non-blocking + fi + else + echo "ℹ️ Enterprise readiness check script not found - skipping" >> $GITHUB_STEP_SUMMARY + exit 0 + fi + + repository-health: + name: Repository Health Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0 + with: + php-version: '8.1' + extensions: json, mbstring + tools: composer + coverage: none + + - name: Install API Package + run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader + env: + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' + + - name: Check Repository Health + id: health_check + run: | + echo "" >> $GITHUB_STEP_SUMMARY + + if [ -f "api/validate/check_repo_health.php" ]; then + php api/validate/check_repo_health.php --verbose | tee /tmp/health-check.log + EXIT_CODE=$? + + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/health-check.log >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + + if [ "$EXIT_CODE" -eq 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "✅ Repository health check passed!" >> $GITHUB_STEP_SUMMARY + exit 0 + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "⚠️ Repository health issues detected" >> $GITHUB_STEP_SUMMARY + echo "**Note:** This is informational - review recommendations to improve" >> $GITHUB_STEP_SUMMARY + exit 0 # Non-blocking + fi + else + echo "ℹ️ Repository health check script not found - skipping" >> $GITHUB_STEP_SUMMARY + exit 0 + fi + terraform-validation: name: Terraform Configuration Validation runs-on: ubuntu-latest @@ -2420,110 +2485,73 @@ jobs: - name: Create tracking issue for standards violations if: failure() && github.event_name == 'push' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const compliancePercent = '${{ needs.compliance-check.outputs.compliance_percentage }}' || '0'; - const failedChecks = []; + env: + GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} + run: | + REPO="${{ github.repository }}" + RUN_URL="${{ github.server_url }}/${REPO}/actions/runs/${{ github.run_id }}" + DATE=$(date -u '+%Y-%m-%d') + SHA="${{ github.sha }}" + ACTOR="${{ github.actor }}" + BRANCH="${{ github.ref_name }}" - // Collect failed checks from job outputs - const jobs = [ - { name: 'File Headers', failed: '${{ needs.compliance-check.outputs.headers_ok }}' !== 'true' }, - { name: 'Required Files', failed: '${{ needs.compliance-check.outputs.required_files_ok }}' !== 'true' }, - { name: 'Documentation', failed: '${{ needs.compliance-check.outputs.documentation_ok }}' !== 'true' }, - { name: 'Code Quality', failed: '${{ needs.compliance-check.outputs.code_quality_ok }}' !== 'true' } - ]; + # Collect failed checks + FAILED="" + [ "${{ needs.repository-structure.result }}" != "success" ] && FAILED="${FAILED}\n- Repository Structure" + [ "${{ needs.documentation-quality.result }}" != "success" ] && FAILED="${FAILED}\n- Documentation Quality" + [ "${{ needs.coding-standards.result }}" != "success" ] && FAILED="${FAILED}\n- Coding Standards" + [ "${{ needs.license-compliance.result }}" != "success" ] && FAILED="${FAILED}\n- License Compliance" + [ "${{ needs.git-hygiene.result }}" != "success" ] && FAILED="${FAILED}\n- Git Hygiene" + [ "${{ needs.workflow-validation.result }}" != "success" ] && FAILED="${FAILED}\n- Workflow Validation" + [ "${{ needs.version-consistency.result }}" != "success" ] && FAILED="${FAILED}\n- Version Consistency" + [ "${{ needs.script-integrity.result }}" != "success" ] && FAILED="${FAILED}\n- Script Integrity" + [ "${{ needs.secret-scanning.result }}" != "success" ] && FAILED="${FAILED}\n- Secret Scanning" + [ "${{ needs.line-length-validation.result }}" != "success" ] && FAILED="${FAILED}\n- Line Length" + [ "${{ needs.file-size-limits.result }}" != "success" ] && FAILED="${FAILED}\n- File Size Limits" + [ "${{ needs.readme-completeness.result }}" != "success" ] && FAILED="${FAILED}\n- README Completeness" - jobs.forEach(job => { - if (job.failed) failedChecks.push(job.name); - }); + if [ -z "$FAILED" ]; then + echo "No failed checks to report" + exit 0 + fi - const body = `## 📋 Standards Compliance Violations Detected + TITLE="[Standards] Compliance violations — ${DATE}" + BODY="## Standards Compliance Violations - **Branch**: \`${context.ref}\` - **Commit**: ${context.sha.substring(0, 7)} - **Triggered by**: @${context.actor} - **Date**: ${new Date().toISOString()} + | Field | Value | + |-------|-------| + | **Branch** | \`${BRANCH}\` | + | **Commit** | \`${SHA:0:7}\` | + | **Actor** | @${ACTOR} | + | **Run** | [View workflow](${RUN_URL}) | - ### Compliance Score: ${compliancePercent}% + ### Failed Checks + $(printf '%b' "$FAILED") - ### ❌ Failed Checks - ${failedChecks.length > 0 ? failedChecks.map(check => `- ${check}`).join('\n') : '_Details available in workflow run_'} + ### Required Actions + 1. Review the [workflow run](${RUN_URL}) for details + 2. Fix each failed check + 3. Push to trigger a new scan - ### 📊 What This Means - Your repository does not meet the required MokoStandards compliance threshold. This can affect: - - Code quality and maintainability - - Team collaboration efficiency - - Automated tooling integration - - Repository discoverability + --- + *Auto-created by standards-compliance workflow*" - ### ✅ Required Actions - 1. Review the [workflow run](${context.payload.repository.html_url}/actions/runs/${context.runId}) for detailed findings - 2. Address each failed check according to MokoStandards documentation - 3. Push changes to trigger a new compliance scan - 4. Ensure compliance reaches 100% before merging + BODY=$(echo "$BODY" | sed 's/^ //') + LABEL="standards-violation" - ### 📚 Resources - - [MokoStandards Documentation](https://github.com/mokoconsulting-tech/MokoStandards) - - [Repository Structure Guide](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/core-structure.md) - - [Documentation Standards](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/document-formatting.md) - - [Coding Standards](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/coding-style-guide.md) + gh label create "$LABEL" --repo "$REPO" --color "D73A4A" --description "Standards compliance failure" --force 2>/dev/null || true - ### 🔄 Resolution - This issue will be automatically closed when compliance reaches 100%. + EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=all&per_page=1&sort=created&direction=desc" \ + --jq '.[0].number' 2>/dev/null) - --- - *This issue was automatically created by the Standards Compliance workflow.* - `; - - // Validate assignees before creating issue - async function validateAssignees(assignees) { - const validAssignees = []; - for (const assignee of assignees) { - try { - await github.rest.users.getByUsername({ username: assignee }); - validAssignees.push(assignee); - console.log(`✓ Validated assignee: ${assignee}`); - } catch (error) { - console.log(`✗ Invalid assignee (skipping): ${assignee} - ${error.message}`); - } - } - return validAssignees; - } - - const requestedAssignees = ['jmiller-moko']; - const validAssignees = await validateAssignees(requestedAssignees); - - // Check for existing open issue - const issues = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - labels: 'standards-violation', - per_page: 1 - }); - - if (issues.data.length > 0) { - // Update existing issue - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issues.data[0].number, - body: `### 🔄 Updated Scan Results\n\n${body}` - }); - console.log(`Updated existing issue #${issues.data[0].number}`); - } else { - // Create new issue with validated assignees - const issue = await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: `[Standards] Compliance Violations - ${new Date().toISOString().split('T')[0]}`, - body: body, - labels: ['standards-violation', 'compliance', 'automation'], - assignees: validAssignees - }); - console.log(`Created new issue #${issue.data.number}`); - } + if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then + gh api "repos/${REPO}/issues/${EXISTING}" -X PATCH \ + -f title="$TITLE" -f body="$BODY" -f state="open" --silent + echo "Updated issue #${EXISTING}" + else + gh issue create --repo "$REPO" --title "$TITLE" --body "$BODY" \ + --label "$LABEL" --assignee "jmiller-moko" + fi # CUSTOMIZATION: # @@ -2532,3 +2560,4 @@ jobs: # 3. Integrate with custom linting tools # 4. Add notification steps for compliance failures # 5. Customize required files/directories for your project type + -- 2.49.1 From f640778885a7bf3388703a9ed7b84be9f0b753aa Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:24 -0500 Subject: [PATCH 17/39] chore: update .github/workflows/enterprise-firewall-setup.yml from MokoStandards --- .github/workflows/enterprise-firewall-setup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/enterprise-firewall-setup.yml b/.github/workflows/enterprise-firewall-setup.yml index a093120..b0c12fc 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 -# VERSION: 04.01.00 +# VERSION: 04.04.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. -- 2.49.1 From a6504c295a831229328297548599734df257df02 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:25 -0500 Subject: [PATCH 18/39] chore: update .github/workflows/deploy-dev.yml from MokoStandards --- .github/workflows/deploy-dev.yml | 116 +++++++++++++++++++++++++------ 1 file changed, 94 insertions(+), 22 deletions(-) diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 5fa6a23..a956ec3 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -22,7 +22,7 @@ # INGROUP: MokoStandards.Deploy # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/deploy-dev.yml -# VERSION: 04.01.00 +# VERSION: 04.04.00 # 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. # Port is resolved in order: DEV_FTP_PORT variable → :port suffix in DEV_FTP_HOST → 22. @@ -49,6 +49,7 @@ on: push: branches: - 'dev/**' + - 'rc/**' - develop - development paths: @@ -57,6 +58,7 @@ on: types: [opened, synchronize, reopened, closed] branches: - 'dev/**' + - 'rc/**' - develop - development paths: @@ -73,6 +75,9 @@ permissions: contents: read pull-requests: write +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + jobs: check-permission: name: Verify Deployment Permission @@ -347,8 +352,8 @@ jobs: # ── Platform-specific path safety guards ────────────────────────────── PLATFORM="" - if [ -f ".moko-standards" ]; then - PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"') + MOKO_FILE=".github/.mokostandards"; [ ! -f "$MOKO_FILE" ] && MOKO_FILE=".mokostandards"; if [ -f "$MOKO_FILE" ]; then + PLATFORM=$(grep -oP '^platform:.*' "$MOKO_FILE" 2>/dev/null || true) fi if [ "$PLATFORM" = "crm-module" ]; then @@ -409,7 +414,7 @@ jobs: - name: Setup PHP 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: php-version: '8.1' tools: composer @@ -420,14 +425,17 @@ jobs: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}' run: | - git clone --depth 1 --quiet \ + git clone --depth 1 --branch version/04.04.00 --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 - - name: Clear remote destination folder - if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true' + - name: Clear remote destination folder (manual only) + if: >- + steps.source.outputs.skip == 'false' && + steps.remote.outputs.skip != 'true' && + inputs.clear_remote == true env: SFTP_HOST: ${{ steps.conn.outputs.host }} SFTP_PORT: ${{ steps.conn.outputs.port }} @@ -574,23 +582,87 @@ jobs: DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD") fi - # ── For Dolibarr (crm-module): set version to "development" before deploy ─ - PLATFORM="" - if [ -f ".moko-standards" ]; then - PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"') + # Set platform version to "development" before deploy (Dolibarr + Joomla) + php /tmp/mokostandards/api/cli/version_set_platform.php --path . --version development + + # Write update files — dev/** = development, rc/** = rc + PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true) + REPO="${{ github.repository }}" + BRANCH="${{ github.ref_name }}" + + # Determine stability tag from branch prefix + STABILITY="development" + VERSION_LABEL="development" + if [[ "$BRANCH" == rc/* ]]; then + STABILITY="rc" + VERSION_LABEL=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null || echo "${BRANCH#rc/}")-rc 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 + printf '%s' "$VERSION_LABEL" > update.txt 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 '/dev/null | while read -r manifest; do - sed -i "s|[^<]*|development|" "$manifest" 2>/dev/null || true - done + MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null | head -1 || true) + if [ -n "$MANIFEST" ]; then + EXT_NAME=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}") + EXT_TYPE=$(grep -oP ']+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component") + EXT_ELEMENT=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml) + EXT_CLIENT=$(grep -oP ']+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "") + EXT_FOLDER=$(grep -oP ']+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "") + TARGET_PLATFORM=$(grep -oP '' "$MANIFEST" 2>/dev/null | head -1 || echo '') + + CLIENT_TAG="" + if [ -n "$EXT_CLIENT" ]; then + CLIENT_TAG="${EXT_CLIENT}" + elif [ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]; then + CLIENT_TAG="site" + fi + + FOLDER_TAG="" + if [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ]; then + FOLDER_TAG="${EXT_FOLDER}" + fi + + DOWNLOAD_URL="https://github.com/${REPO}/archive/refs/heads/${BRANCH}.zip" + + cat > update.xml << 'DEVXML' + + + + EXT_NAME_PH + EXT_NAME_PH STABILITY_PH build + EXT_ELEMENT_PH + EXT_TYPE_PH + VERSION_LABEL_PH + CLIENT_TAG_PH + FOLDER_TAG_PH + + STABILITY_PH + + https://github.com/REPO_PH/tree/BRANCH_PH + + DOWNLOAD_URL_PH + + TARGET_PLATFORM_PH + Moko Consulting + https://mokoconsulting.tech + + +DEVXML + sed -i "s|EXT_NAME_PH|${EXT_NAME}|g" update.xml + sed -i "s|EXT_ELEMENT_PH|${EXT_ELEMENT}|g" update.xml + sed -i "s|EXT_TYPE_PH|${EXT_TYPE}|g" update.xml + sed -i "s|CLIENT_TAG_PH|${CLIENT_TAG}|g" update.xml + sed -i "s|FOLDER_TAG_PH|${FOLDER_TAG}|g" update.xml + sed -i "s|REPO_PH|${REPO}|g" update.xml + sed -i "s|BRANCH_PH|${BRANCH}|g" update.xml + sed -i "s|DOWNLOAD_URL_PH|${DOWNLOAD_URL}|g" update.xml + sed -i "s|TARGET_PLATFORM_PH|${TARGET_PLATFORM}|g" update.xml + sed -i "s|VERSION_LABEL_PH|${VERSION_LABEL}|g" update.xml + sed -i "s|STABILITY_PH|${STABILITY}|g" update.xml + sed -i '/^[[:space:]]*$/d' update.xml + fi fi php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}" @@ -641,22 +713,22 @@ jobs: --force 2>/dev/null || true # 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) if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then gh api "repos/${REPO}/issues/${EXISTING}" \ -X PATCH \ -f title="$TITLE" \ - -f body="$BODY" \ + -f body="$BODY" \n -f state="open" \ --silent - echo "📋 Failure issue #${EXISTING} updated: ${REPO}" >> "$GITHUB_STEP_SUMMARY" + echo "📋 Failure issue #${EXISTING} updated/reopened: ${REPO}" >> "$GITHUB_STEP_SUMMARY" else gh issue create \ --repo "$REPO" \ --title "$TITLE" \ --body "$BODY" \ - --label "$LABEL" \ + --label "$LABEL" \n --assignee "jmiller-moko" \ | tee -a "$GITHUB_STEP_SUMMARY" fi -- 2.49.1 From ade8d4398cc994d81e4140616f796b8de65d0bf1 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:26 -0500 Subject: [PATCH 19/39] chore: update .github/workflows/deploy-demo.yml from MokoStandards --- .github/workflows/deploy-demo.yml | 84 ++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index 184799f..69c5e1d 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -22,7 +22,7 @@ # INGROUP: MokoStandards.Deploy # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/deploy-demo.yml -# VERSION: 04.01.00 +# VERSION: 04.04.00 # 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. # Port is resolved in order: DEMO_FTP_PORT variable → :port suffix in DEMO_FTP_HOST → 22. @@ -70,6 +70,9 @@ permissions: contents: read pull-requests: write +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + jobs: check-permission: name: Verify Deployment Permission @@ -241,7 +244,6 @@ jobs: fi done $SKIP && continue - if [ -f ".gitignore" ]; then if [ -f ".gitignore" ]; then git check-ignore -q "$rel" 2>/dev/null && { IGNORED_FILES+=("$rel | .gitignore") @@ -345,8 +347,8 @@ jobs: # ── Platform-specific path safety guards ────────────────────────────── PLATFORM="" - if [ -f ".moko-standards" ]; then - PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"') + MOKO_FILE=".github/.mokostandards"; [ ! -f "$MOKO_FILE" ] && MOKO_FILE=".mokostandards"; if [ -f "$MOKO_FILE" ]; then + PLATFORM=$(grep -E '^platform:' "$MOKO_FILE" | sed 's/.*:[[:space:]]*//' | tr -d '"') fi if [ "$PLATFORM" = "crm-module" ]; then @@ -407,7 +409,7 @@ jobs: - name: Setup PHP 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: php-version: '8.1' tools: composer @@ -418,14 +420,17 @@ jobs: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}' run: | - git clone --depth 1 --quiet \ + git clone --depth 1 --branch version/04.04.00 --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 - - name: Clear remote destination folder - if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true' + - name: Clear remote destination folder (manual only) + if: >- + steps.source.outputs.skip == 'false' && + steps.remote.outputs.skip != 'true' && + inputs.clear_remote == true env: SFTP_HOST: ${{ steps.conn.outputs.host }} SFTP_PORT: ${{ steps.conn.outputs.port }} @@ -566,6 +571,59 @@ jobs: > /tmp/sftp-config.json fi + # ── Write update files (demo = stable) ───────────────────────────── + PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true) + VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null || echo "unknown") + REPO="${{ github.repository }}" + + if [ "$PLATFORM" = "crm-module" ]; then + printf '%s' "$VERSION" > update.txt + fi + + if [ "$PLATFORM" = "waas-component" ]; then + MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null | head -1 || true) + if [ -n "$MANIFEST" ]; then + EXT_NAME=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}") + EXT_TYPE=$(grep -oP ']+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component") + EXT_ELEMENT=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml) + EXT_CLIENT=$(grep -oP ']+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "") + EXT_FOLDER=$(grep -oP ']+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "") + TARGET_PLATFORM=$(grep -oP '' "$MANIFEST" 2>/dev/null | head -1 || echo '') + + CLIENT_TAG="" + if [ -n "$EXT_CLIENT" ]; then CLIENT_TAG="${EXT_CLIENT}"; elif [ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]; then CLIENT_TAG="site"; fi + FOLDER_TAG="" + if [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ]; then FOLDER_TAG="${EXT_FOLDER}"; fi + + cat > update.xml << 'UPDATEXML' + + + + EXT_NAME_PH + EXT_NAME_PH update + EXT_ELEMENT_PH + EXT_TYPE_PH + VERSION_PH + CLIENT_TAG_PH + FOLDER_TAG_PH + + stable + + https://github.com/REPO_PH + + https://github.com/REPO_PH/releases/download/vVERSION_PH/EXT_ELEMENT_PH-VERSION_PH.zip + + TARGET_PLATFORM_PH + Moko Consulting + https://mokoconsulting.tech + + +UPDATEXML + sed -i "s|EXT_NAME_PH|${EXT_NAME}|g;s|EXT_ELEMENT_PH|${EXT_ELEMENT}|g;s|EXT_TYPE_PH|${EXT_TYPE}|g;s|VERSION_PH|${VERSION}|g;s|CLIENT_TAG_PH|${CLIENT_TAG}|g;s|FOLDER_TAG_PH|${FOLDER_TAG}|g;s|REPO_PH|${REPO}|g;s|TARGET_PLATFORM_PH|${TARGET_PLATFORM}|g" update.xml + sed -i '/^[[:space:]]*$/d' update.xml + fi + fi + # ── Run deploy-sftp.php from MokoStandards ──────────────────────────── DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json) if [ "$USE_PASSPHRASE" = "true" ]; then @@ -578,7 +636,7 @@ jobs: rm -f /tmp/deploy_key /tmp/sftp-config.json - name: Create or update failure issue - if: failure() + if: failure() && steps.remote.outputs.skip != 'true' env: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} run: | @@ -620,22 +678,22 @@ jobs: --force 2>/dev/null || true # 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) if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then gh api "repos/${REPO}/issues/${EXISTING}" \ -X PATCH \ -f title="$TITLE" \ - -f body="$BODY" \ + -f body="$BODY" \n -f state="open" \ --silent - echo "📋 Failure issue #${EXISTING} updated: ${REPO}" >> "$GITHUB_STEP_SUMMARY" + echo "📋 Failure issue #${EXISTING} updated/reopened: ${REPO}" >> "$GITHUB_STEP_SUMMARY" else gh issue create \ --repo "$REPO" \ --title "$TITLE" \ --body "$BODY" \ - --label "$LABEL" \ + --label "$LABEL" \n --assignee "jmiller-moko" \ | tee -a "$GITHUB_STEP_SUMMARY" fi -- 2.49.1 From 7135652410109c8e581d1437fec26f5967dd29d1 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:27 -0500 Subject: [PATCH 20/39] chore: update .github/workflows/deploy-rs.yml from MokoStandards --- .github/workflows/deploy-rs.yml | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deploy-rs.yml b/.github/workflows/deploy-rs.yml index eef55b8..f5e950f 100644 --- a/.github/workflows/deploy-rs.yml +++ b/.github/workflows/deploy-rs.yml @@ -22,7 +22,7 @@ # INGROUP: MokoStandards.Deploy # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/deploy-rs.yml -# VERSION: 04.01.00 +# VERSION: 04.04.00 # 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. # Port is resolved in order: RS_FTP_PORT variable → :port suffix in RS_FTP_HOST → 22. @@ -70,6 +70,9 @@ permissions: contents: read pull-requests: write +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + jobs: check-permission: name: Verify Deployment Permission @@ -344,8 +347,8 @@ jobs: # ── Platform-specific path safety guards ────────────────────────────── PLATFORM="" - if [ -f ".moko-standards" ]; then - PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"') + MOKO_FILE=".github/.mokostandards"; [ ! -f "$MOKO_FILE" ] && MOKO_FILE=".mokostandards"; if [ -f "$MOKO_FILE" ]; then + PLATFORM=$(grep -E '^platform:' "$MOKO_FILE" | sed 's/.*:[[:space:]]*//' | tr -d '"') fi # RS deployment: no path restrictions for any platform @@ -387,7 +390,7 @@ jobs: - name: Setup PHP 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: php-version: '8.1' tools: composer @@ -398,14 +401,17 @@ jobs: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}' run: | - git clone --depth 1 --quiet \ + git clone --depth 1 --branch version/04.04.00 --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 - - name: Clear remote destination folder - if: steps.source.outputs.skip == 'false' && steps.remote.outputs.skip != 'true' + - name: Clear remote destination folder (manual only) + if: >- + steps.source.outputs.skip == 'false' && + steps.remote.outputs.skip != 'true' && + inputs.clear_remote == true env: SFTP_HOST: ${{ steps.conn.outputs.host }} SFTP_PORT: ${{ steps.conn.outputs.port }} @@ -558,7 +564,7 @@ jobs: rm -f /tmp/deploy_key /tmp/sftp-config.json - name: Create or update failure issue - if: failure() + if: failure() && steps.remote.outputs.skip != 'true' env: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} run: | @@ -599,8 +605,8 @@ jobs: --description "Automated deploy failure tracking" \ --force 2>/dev/null || true - # Look for an existing open deploy-failure issue - EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=open&per_page=1" \ + # Look for an existing deploy-failure issue (any state — reopen if closed) + EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=all&per_page=1&sort=created&direction=desc" \ --jq '.[0].number' 2>/dev/null) if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then @@ -608,14 +614,16 @@ jobs: -X PATCH \ -f title="$TITLE" \ -f body="$BODY" \ + -f state="open" \ --silent - echo "📋 Failure issue #${EXISTING} updated: ${REPO}" >> "$GITHUB_STEP_SUMMARY" + echo "📋 Failure issue #${EXISTING} updated/reopened: ${REPO}" >> "$GITHUB_STEP_SUMMARY" else gh issue create \ --repo "$REPO" \ --title "$TITLE" \ --body "$BODY" \ --label "$LABEL" \ + --assignee "jmiller-moko" \ | tee -a "$GITHUB_STEP_SUMMARY" fi -- 2.49.1 From 2372e925f7d5edf09fd7b345fa43ed3bee5b9a82 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:28 -0500 Subject: [PATCH 21/39] chore: update .github/workflows/sync-version-on-merge.yml from MokoStandards --- .github/workflows/sync-version-on-merge.yml | 35 +++++++++------------ 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/.github/workflows/sync-version-on-merge.yml b/.github/workflows/sync-version-on-merge.yml index 56cd2a8..a2b1c68 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 -# VERSION: 04.01.00 +# VERSION: 04.04.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. @@ -32,6 +32,9 @@ permissions: contents: write issues: write +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + jobs: sync-version: name: Propagate README version @@ -45,7 +48,7 @@ jobs: fetch-depth: 0 - name: Set up PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + uses: shivammathur/setup-php@fcafdd6392932010c2bd5094439b8e33be2a8a09 # v2.37.0 with: php-version: '8.1' tools: composer @@ -55,7 +58,7 @@ jobs: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}' run: | - git clone --depth 1 --quiet \ + git clone --depth 1 --branch version/04.04.00 --quiet \ "https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \ /tmp/mokostandards cd /tmp/mokostandards @@ -64,31 +67,20 @@ jobs: - name: Auto-bump patch version if: ${{ github.event_name == 'push' && github.actor != 'github-actions[bot]' }} 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 echo "README.md changed in this push — skipping auto-bump" exit 0 fi - CURRENT=$(grep -oP '^\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' README.md | head -1) - if [ -z "$CURRENT" ]; then - echo "⚠️ No VERSION found in README.md — skipping auto-bump" + RESULT=$(php /tmp/mokostandards/api/cli/version_bump.php --path .) || { + echo "⚠️ Could not bump version — skipping" exit 0 - fi - - # 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 + } + echo "Auto-bumping patch: $RESULT" git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" 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] " git push @@ -96,7 +88,7 @@ jobs: id: readme_version run: | 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 echo "⚠️ No VERSION in README.md — skipping propagation" echo "skip=true" >> $GITHUB_OUTPUT @@ -119,8 +111,9 @@ jobs: - name: Commit updated files if: ${{ steps.readme_version.outputs.skip != 'true' && inputs.dry_run != true }} run: | + git pull --ff-only 2>/dev/null || true if git diff --quiet; then - echo "ℹ️ No version changes needed" + echo "ℹ️ No version changes needed — already up to date" exit 0 fi VERSION="${{ steps.readme_version.outputs.version }}" -- 2.49.1 From 9c4be466fa080a49436832a6c7d27492a0cac392 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:28 -0500 Subject: [PATCH 22/39] chore: update .github/workflows/auto-release.yml from MokoStandards --- .github/workflows/auto-release.yml | 444 +++++++++++++++++++++++------ 1 file changed, 363 insertions(+), 81 deletions(-) diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 0016fd2..7fac9f2 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -1,7 +1,5 @@ # Copyright (C) 2026 Moko Consulting # -# This file is part of a Moko Consulting project. -# # SPDX-License-Identifier: GPL-3.0-or-later # # FILE INFORMATION @@ -9,12 +7,30 @@ # INGROUP: MokoStandards.Release # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/auto-release.yml -# VERSION: 04.01.00 -# BRIEF: Auto-create a GitHub Release on every push to main with version from README.md -# 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. +# VERSION: 04.04.00 +# BRIEF: Unified build & release pipeline — version branch, platform version, badges, tag, release +# +# ╔════════════════════════════════════════════════════════════════════════╗ +# ║ BUILD & RELEASE PIPELINE ║ +# ╠════════════════════════════════════════════════════════════════════════╣ +# ║ ║ +# ║ Triggers on push to main (skips bot commits + [skip ci]): ║ +# ║ ║ +# ║ Every push: ║ +# ║ 1. Read version from README.md ║ +# ║ 3. Set platform version (Dolibarr $this->version, Joomla )║ +# ║ 4. Update [VERSION: XX.YY.ZZ] badges in markdown files ║ +# ║ 5. Write update.txt / update.xml ║ +# ║ 6. Create git tag vXX.YY.ZZ ║ +# ║ 7a. Patch: update existing GitHub Release for this minor ║ +# ║ ║ +# ║ Minor releases only (patch == 00): ║ +# ║ 2. Create version/XX.YY.ZZ branch (immutable snapshot) ║ +# ║ 7b. Create new GitHub Release ║ +# ║ ║ +# ╚════════════════════════════════════════════════════════════════════════╝ -name: Auto Release +name: Build & Release on: push: @@ -22,14 +38,16 @@ on: - main - master +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + permissions: contents: write jobs: release: - name: Create Release + name: Build & Release Pipeline runs-on: ubuntu-latest - # Skip bot commits (version sync, [skip ci]) to avoid infinite loops if: >- !contains(github.event.head_commit.message, '[skip ci]') && github.actor != 'github-actions[bot]' @@ -41,123 +59,387 @@ jobs: token: ${{ secrets.GH_TOKEN || github.token }} 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.04.00 --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 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 - echo "⚠️ No VERSION found in README.md — skipping release" + echo "⏭️ No VERSION in README.md — skipping release" echo "skip=true" >> "$GITHUB_OUTPUT" exit 0 fi echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT" + echo "branch=version/${VERSION}" >> "$GITHUB_OUTPUT" echo "skip=false" >> "$GITHUB_OUTPUT" - echo "✅ Version: $VERSION (tag: v${VERSION})" - - name: Check if tag already exists + # Detect if this is a minor release (patch == 00) + PATCH=$(echo "$VERSION" | awk -F. '{print $3}') + if [ "$PATCH" = "00" ]; then + echo "is_minor=true" >> "$GITHUB_OUTPUT" + echo "✅ Version: $VERSION (minor release — full pipeline)" + else + echo "is_minor=false" >> "$GITHUB_OUTPUT" + echo "✅ Version: $VERSION (patch — platform version + badges only)" + fi + + - name: Check if already released if: steps.version.outputs.skip != 'true' - id: tag_check + id: check run: | TAG="${{ steps.version.outputs.tag }}" - if git rev-parse "$TAG" >/dev/null 2>&1; then - echo "ℹ️ Tag $TAG already exists — skipping release" - echo "exists=true" >> "$GITHUB_OUTPUT" + BRANCH="${{ steps.version.outputs.branch }}" + + 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 - echo "exists=false" >> "$GITHUB_OUTPUT" + echo "already_released=false" >> "$GITHUB_OUTPUT" fi - - name: Update Dolibarr module version + # ── SANITY CHECKS ──────────────────────────────────────────────────── + - name: "Sanity: Platform-specific validation" if: >- steps.version.outputs.skip != 'true' && - steps.tag_check.outputs.exists != 'true' + steps.check.outputs.already_released != 'true' run: | - PLATFORM="" - if [ -f ".moko-standards" ]; then - PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"') + VERSION="${{ steps.version.outputs.version }}" + PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null) + 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 + 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 + MOD_FILE=$(find src -path "*/core/modules/mod*.class.php" -print -quit 2>/dev/null) + if [ -z "$MOD_FILE" ]; then + echo "❌ No module descriptor (src/core/modules/mod*.class.php)" >> $GITHUB_STEP_SUMMARY + 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 + + # Joomla-specific checks + if [ "$PLATFORM" = "waas-component" ]; then + MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null | head -1) + if [ -z "$MANIFEST" ]; then + echo "❌ No Joomla XML manifest found" >> $GITHUB_STEP_SUMMARY + ERRORS=$((ERRORS+1)) + else + echo "✅ Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY + + # Check extension type + TYPE=$(grep -oP ']+type="\K[^"]+' "$MANIFEST" 2>/dev/null) + echo "✅ Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + if [ "$ERRORS" -gt 0 ]; then + echo "**❌ ${ERRORS} error(s) — release may be incomplete**" >> $GITHUB_STEP_SUMMARY + else + echo "**✅ All sanity checks passed**" >> $GITHUB_STEP_SUMMARY + fi + + # ── STEP 2: Create version branch (minor releases only) ───────────── + - name: "Step 2: Create version branch" + if: >- + steps.version.outputs.skip != 'true' && + steps.version.outputs.is_minor == 'true' && + steps.check.outputs.branch_exists != 'true' + 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: | VERSION="${{ steps.version.outputs.version }}" + php /tmp/mokostandards/api/cli/version_set_platform.php \ + --path . --version "$VERSION" --branch main + + # ── STEP 4: Update version badges ────────────────────────────────── + - name: "Step 4: Update version badges" + if: >- + steps.version.outputs.skip != '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 files (Dolibarr: update.txt / Joomla: update.xml) + - name: "Step 5: Write update files" + 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) + VERSION="${{ steps.version.outputs.version }}" + REPO="${{ github.repository }}" if [ "$PLATFORM" = "crm-module" ]; then - echo "📦 Dolibarr release — setting module version to '${VERSION}'" - # Update $this->version in the module descriptor (core/modules/mod*.class.php) - find . -path "*/core/modules/mod*.class.php" -exec \ - sed -i "s/\(\$this->version\s*=\s*\)['\"][^'\"]*['\"]/\1'${VERSION}'/" {} + 2>/dev/null || true + printf '%s' "$VERSION" > update.txt + echo "📦 update.txt: ${VERSION}" >> $GITHUB_STEP_SUMMARY fi if [ "$PLATFORM" = "waas-component" ]; then - echo "📦 Joomla release — setting manifest version to '${VERSION}'" - # Update tag in Joomla XML manifest files - find . -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null | while read -r manifest; do - sed -i "s|[^<]*|${VERSION}|" "$manifest" 2>/dev/null || true - done + # ── Parse extension metadata from XML manifest ────────────── + MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null | head -1) + if [ -z "$MANIFEST" ]; then + echo "⚠️ No Joomla XML manifest found — skipping update.xml" >> $GITHUB_STEP_SUMMARY + else + EXT_NAME=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}") + EXT_TYPE=$(grep -oP ']+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component") + EXT_ELEMENT=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "") + EXT_CLIENT=$(grep -oP ']+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "") + EXT_FOLDER=$(grep -oP ']+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "") + TARGET_PLATFORM=$(grep -oP '' "$MANIFEST" 2>/dev/null | head -1 || echo "") + PHP_MINIMUM=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "") + + # Derive element from manifest filename if not in XML + if [ -z "$EXT_ELEMENT" ]; then + EXT_ELEMENT=$(basename "$MANIFEST" .xml) + fi + + # Build client tag: plugins and frontend modules need site + CLIENT_TAG="" + if [ -n "$EXT_CLIENT" ]; then + CLIENT_TAG="${EXT_CLIENT}" + elif [ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]; then + CLIENT_TAG="site" + fi + + # Build folder tag for plugins (required for Joomla to match the update) + FOLDER_TAG="" + if [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ]; then + FOLDER_TAG="${EXT_FOLDER}" + fi + + # Build targetplatform (fallback to Joomla 4+5 if not in manifest) + if [ -z "$TARGET_PLATFORM" ]; then + TARGET_PLATFORM='' + fi + + # Build php_minimum tag + PHP_TAG="" + if [ -n "$PHP_MINIMUM" ]; then + PHP_TAG="${PHP_MINIMUM}" + fi + + DOWNLOAD_URL="https://github.com/${REPO}/releases/download/v${VERSION}/${EXT_ELEMENT}-${VERSION}.zip" + INFO_URL="https://github.com/${REPO}/releases/tag/v${VERSION}" + + # ── Write update.xml (stable release) ─────────────────────── + cat > update.xml << 'XMLEOF' + + + + EXT_NAME_PH + EXT_NAME_PH update + EXT_ELEMENT_PH + EXT_TYPE_PH + VERSION_PH + CLIENT_TAG_PH + FOLDER_TAG_PH + + stable + + INFO_URL_PH + + DOWNLOAD_URL_PH + + TARGET_PLATFORM_PH + PHP_TAG_PH + Moko Consulting + https://mokoconsulting.tech + + +XMLEOF + # Replace placeholders (avoids heredoc variable expansion issues with XML) + sed -i "s|EXT_NAME_PH|${EXT_NAME}|g" update.xml + sed -i "s|EXT_ELEMENT_PH|${EXT_ELEMENT}|g" update.xml + sed -i "s|EXT_TYPE_PH|${EXT_TYPE}|g" update.xml + sed -i "s|VERSION_PH|${VERSION}|g" update.xml + sed -i "s|CLIENT_TAG_PH|${CLIENT_TAG}|g" update.xml + sed -i "s|FOLDER_TAG_PH|${FOLDER_TAG}|g" update.xml + sed -i "s|INFO_URL_PH|${INFO_URL}|g" update.xml + sed -i "s|DOWNLOAD_URL_PH|${DOWNLOAD_URL}|g" update.xml + sed -i "s|TARGET_PLATFORM_PH|${TARGET_PLATFORM}|g" update.xml + sed -i "s|PHP_TAG_PH|${PHP_TAG}|g" update.xml + # Remove empty placeholder lines + sed -i '/^[[:space:]]*$/d' update.xml + + echo "📦 update.xml: ${VERSION} (stable) — ${EXT_TYPE}/${EXT_ELEMENT}" >> $GITHUB_STEP_SUMMARY + fi fi - # Commit the version update if anything changed - if ! git diff --quiet; 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(release): set version to ${VERSION} [skip ci]" \ - --author="github-actions[bot] " - git push - fi - - - name: Extract changelog entry + # ── Commit all changes ───────────────────────────────────────────── + - name: Commit release changes if: >- steps.version.outputs.skip != 'true' && - steps.tag_check.outputs.exists != 'true' - id: changelog + 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] " + git push - # Try to extract the section for this version from CHANGELOG.md - NOTES="" - 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 + # ── STEP 6: Create tag ───────────────────────────────────────────── + - name: "Step 6: Create git tag" if: >- steps.version.outputs.skip != 'true' && - steps.tag_check.outputs.exists != '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 or update GitHub Release ────────────────────────── + - name: "Step 7: GitHub Release" + if: >- + steps.version.outputs.skip != 'true' && + steps.check.outputs.tag_exists != 'true' env: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} run: | - TAG="${{ steps.version.outputs.tag }}" VERSION="${{ steps.version.outputs.version }}" + TAG="${{ steps.version.outputs.tag }}" + BRANCH="${{ steps.version.outputs.branch }}" + IS_MINOR="${{ steps.version.outputs.is_minor }}" - # Create the tag - git tag "$TAG" - git push origin "$TAG" + # Derive the minor version base (XX.YY.00) + MINOR_BASE=$(echo "$VERSION" | sed 's/\.[0-9]*$/.00/') + MINOR_TAG="v${MINOR_BASE}" - # Create the release - gh release create "$TAG" \ - --title "${VERSION}" \ - --notes-file /tmp/release_notes.md \ - --target main + NOTES=$(php /tmp/mokostandards/api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null) + [ -z "$NOTES" ] && NOTES="Release ${VERSION}" + echo "$NOTES" > /tmp/release_notes.md - echo "🚀 Release ${VERSION} created: $TAG" + if [ "$IS_MINOR" = "true" ]; then + # Minor release: create new GitHub Release + gh release create "$TAG" \ + --title "${VERSION}" \ + --notes-file /tmp/release_notes.md \ + --target "$BRANCH" + echo "🚀 Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY + else + # Patch release: update the existing minor release with new tag + # Find the latest release for this minor version + EXISTING=$(gh release view "$MINOR_TAG" --json tagName -q .tagName 2>/dev/null || true) + if [ -n "$EXISTING" ]; then + # Update existing release body with patch info + CURRENT_NOTES=$(gh release view "$MINOR_TAG" --json body -q .body 2>/dev/null || true) + { + echo "$CURRENT_NOTES" + echo "" + echo "---" + echo "### Patch ${VERSION}" + echo "" + cat /tmp/release_notes.md + } > /tmp/updated_notes.md - - name: Summary - if: steps.version.outputs.skip != 'true' + gh release edit "$MINOR_TAG" \ + --title "${MINOR_BASE} (latest: ${VERSION})" \ + --notes-file /tmp/updated_notes.md + echo "📝 Release updated: ${MINOR_BASE} → patch ${VERSION}" >> $GITHUB_STEP_SUMMARY + else + # No existing minor release found — create one for this patch + gh release create "$TAG" \ + --title "${VERSION}" \ + --notes-file /tmp/release_notes.md + echo "🚀 Release created: ${VERSION} (no minor release found)" >> $GITHUB_STEP_SUMMARY + fi + fi + + # ── Summary ──────────────────────────────────────────────────────── + - name: Pipeline Summary + if: always() run: | VERSION="${{ steps.version.outputs.version }}" - TAG="${{ steps.version.outputs.tag }}" - if [ "${{ steps.tag_check.outputs.exists }}" = "true" ]; then - echo "## ℹ️ Release — ${VERSION}" >> $GITHUB_STEP_SUMMARY - echo "Tag \`${TAG}\` already exists — no new release created." >> $GITHUB_STEP_SUMMARY + if [ "${{ steps.version.outputs.skip }}" = "true" ]; then + echo "## ⏭️ Release Skipped" >> $GITHUB_STEP_SUMMARY + echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY + elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then + echo "## ℹ️ Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY else - echo "## 🚀 Release — ${VERSION}" >> $GITHUB_STEP_SUMMARY - echo "Created tag \`${TAG}\` and GitHub Release." >> $GITHUB_STEP_SUMMARY + echo "" >> $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 -- 2.49.1 From d42729876d476e5cdf6280925bc4326d0ff5539d Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:29 -0500 Subject: [PATCH 23/39] chore: update .github/workflows/repository-cleanup.yml from MokoStandards --- .github/workflows/repository-cleanup.yml | 361 ++++++++++++++++++++--- 1 file changed, 318 insertions(+), 43 deletions(-) diff --git a/.github/workflows/repository-cleanup.yml b/.github/workflows/repository-cleanup.yml index 33c9864..50bc2cf 100644 --- a/.github/workflows/repository-cleanup.yml +++ b/.github/workflows/repository-cleanup.yml @@ -9,36 +9,58 @@ # INGROUP: MokoStandards.Maintenance # REPO: https://github.com/mokoconsulting-tech/MokoStandards # PATH: /templates/workflows/shared/repository-cleanup.yml -# VERSION: 04.01.00 -# BRIEF: One-time repository cleanup — reset labels, strip issue template headers, delete old branches +# VERSION: 04.04.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. -# 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 on: + schedule: + - cron: '0 6 1,15 * *' workflow_dispatch: inputs: 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 default: true - clean_branches: - description: 'Delete old chore/sync-mokostandards-* branches (keeps current versioned branch only)' + clean_workflows: + description: 'Delete orphaned workflow runs (cancelled, stale)' + type: boolean + default: true + clean_logs: + description: 'Delete workflow run logs older than 30 days' type: boolean default: true fix_templates: description: 'Strip copyright comment blocks from issue templates' type: boolean 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: contents: write issues: write + actions: write jobs: cleanup: - name: Repository Cleanup + name: Repository Maintenance runs-on: ubuntu-latest steps: @@ -46,12 +68,18 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: token: ${{ secrets.GH_TOKEN || github.token }} + fetch-depth: 0 - name: Check actor permission env: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} run: | 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]" for user in $AUTHORIZED_USERS; do if [ "$ACTOR" = "$user" ]; then @@ -66,9 +94,90 @@ jobs: *) echo "❌ Admin or maintain required"; exit 1 ;; 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] " + git push + echo "✅ ${DELETED} retired workflow(s) deleted" >> $GITHUB_STEP_SUMMARY + else + echo "✅ No retired workflows found" >> $GITHUB_STEP_SUMMARY + fi + # ── LABEL RESET ────────────────────────────────────────────────────── - name: Reset labels to standard set - if: inputs.reset_labels == true + if: steps.tasks.outputs.reset_labels == 'true' env: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} run: | @@ -76,23 +185,16 @@ jobs: echo "## 🏷️ Label Reset" >> $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 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 - 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 [ -z "$name" ] && continue gh api "repos/${REPO}/labels" \ -f name="$name" -f color="$color" -f description="$description" \ - --silent 2>/dev/null && CREATED=$((CREATED+1)) || true + --silent 2>/dev/null || true done << 'LABELS' joomla|7F52FF|Joomla extension or component dolibarr|FF6B6B|Dolibarr module or extension @@ -125,6 +227,7 @@ jobs: type: enhancement|84B6EB|Enhancement to existing feature type: refactor|F9D0C4|Code refactoring type: chore|FEF2C0|Maintenance tasks + type: version|0E8A16|Version-related change status: pending|FBCA04|Pending action or decision status: in-progress|0E8A16|Currently being worked on status: blocked|B60205|Blocked by another issue or dependency @@ -149,45 +252,85 @@ jobs: version-drift|FFA500|Version mismatch detected deploy-failure|CC0000|Automated deploy failure tracking template-validation-failure|D73A4A|Template workflow validation failure + version|0E8A16|Version bump or release LABELS echo "✅ Standard labels created" >> $GITHUB_STEP_SUMMARY # ── BRANCH CLEANUP ─────────────────────────────────────────────────── - name: Delete old sync branches - if: inputs.clean_branches == true + if: steps.tasks.outputs.clean_branches == 'true' env: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} run: | REPO="${{ github.repository }}" - CURRENT="chore/sync-mokostandards-v04.01.00" + CURRENT="chore/sync-mokostandards-v04.04.00" echo "## 🌿 Branch Cleanup" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - DELETED=0 + FOUND=false gh api "repos/${REPO}/branches?per_page=100" --jq '.[].name' | \ grep "^chore/sync-mokostandards" | \ 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 close "$pr" --repo "$REPO" --comment "Superseded by \`${CURRENT}\`" 2>/dev/null || true echo " Closed PR #${pr}" >> $GITHUB_STEP_SUMMARY done - # Delete the branch gh api -X DELETE "repos/${REPO}/git/refs/heads/${branch}" --silent 2>/dev/null || true 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)) done - if [ "$DELETED" -eq 0 ] 2>/dev/null; then - echo "✅ No old sync branches found" >> $GITHUB_STEP_SUMMARY - else - echo "✅ Cleanup complete" >> $GITHUB_STEP_SUMMARY - fi + echo "✅ Cleaned old workflow run logs" >> $GITHUB_STEP_SUMMARY # ── ISSUE TEMPLATE FIX ────────────────────────────────────────────── - name: Strip copyright headers from issue templates - if: inputs.fix_templates == true + if: steps.tasks.outputs.fix_templates == 'true' run: | echo "## 📋 Issue Template Cleanup" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY @@ -214,26 +357,158 @@ jobs: echo "✅ No templates need cleaning" >> $GITHUB_STEP_SUMMARY fi - # ── SELF-DELETE ───────────────────────────────────────────────────── - - name: Delete this workflow (one-time use) - if: success() + # ── REBUILD DOC INDEXES ───────────────────────────────────────────── + - name: Rebuild docs/ index files + 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] " + 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] " + 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: GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} 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 "Deleting issues closed before: ${CUTOFF}" >> $GITHUB_STEP_SUMMARY - WORKFLOW_FILE=".github/workflows/repository-cleanup.yml" - if [ -f "$WORKFLOW_FILE" ]; then - git config --local user.email "github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - git rm "$WORKFLOW_FILE" - git commit -m "chore: remove repository-cleanup.yml after successful run [skip ci]" \ - --author="github-actions[bot] " - git push - echo "✅ Workflow file deleted — it will not appear in future syncs" >> $GITHUB_STEP_SUMMARY + DELETED=0 + gh api "repos/${REPO}/issues?state=closed&since=1970-01-01T00:00:00Z&per_page=100&sort=updated&direction=asc" \ + --jq ".[] | select(.closed_at < \"${CUTOFF}\") | .number" 2>/dev/null | while read -r num; do + # Lock and close with "not_planned" to mark as cleaned up + gh api "repos/${REPO}/issues/${num}/lock" -X PUT -f lock_reason="resolved" --silent 2>/dev/null || true + echo " Locked issue #${num}" >> $GITHUB_STEP_SUMMARY + DELETED=$((DELETED+1)) + done + + if [ "$DELETED" -eq 0 ] 2>/dev/null; then + echo "✅ No old closed issues found" >> $GITHUB_STEP_SUMMARY else - echo "ℹ️ Workflow file already removed" >> $GITHUB_STEP_SUMMARY + echo "✅ Locked ${DELETED} old closed issue(s)" >> $GITHUB_STEP_SUMMARY fi - name: Summary @@ -241,4 +516,4 @@ jobs: run: | 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 -- 2.49.1 From 9ef832cbe9c5d9577f533ac2207750a04dea88b4 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:30 -0500 Subject: [PATCH 24/39] chore: add .github/workflows/auto-dev-issue.yml from MokoStandards --- .github/workflows/auto-dev-issue.yml | 102 +++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .github/workflows/auto-dev-issue.yml diff --git a/.github/workflows/auto-dev-issue.yml b/.github/workflows/auto-dev-issue.yml new file mode 100644 index 0000000..1f75577 --- /dev/null +++ b/.github/workflows/auto-dev-issue.yml @@ -0,0 +1,102 @@ +# Copyright (C) 2026 Moko Consulting +# +# 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.04.00 +# BRIEF: Auto-create tracking issue when a dev/** or rc/** 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/') || startsWith(github.event.ref, 'rc/')) + + steps: + - name: Create tracking issue + env: + GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} + run: | + BRANCH="${{ github.event.ref }}" + REPO="${{ github.repository }}" + ACTOR="${{ github.actor }}" + NOW=$(date -u '+%Y-%m-%d %H:%M UTC') + + # Determine branch type and version + if [[ "$BRANCH" == rc/* ]]; then + VERSION="${BRANCH#rc/}" + BRANCH_TYPE="Release Candidate" + LABEL_TYPE="type: release" + TITLE_PREFIX="rc" + else + VERSION="${BRANCH#dev/}" + BRANCH_TYPE="Development" + LABEL_TYPE="type: feature" + TITLE_PREFIX="feat" + fi + + TITLE="${TITLE_PREFIX}(${VERSION}): ${BRANCH_TYPE} tracking for ${BRANCH}" + + BODY="## ${BRANCH_TYPE} Branch Created + + | Field | Value | + |-------|-------| + | **Branch** | \`${BRANCH}\` | + | **Version** | \`${VERSION}\` | + | **Type** | ${BRANCH_TYPE} | + | **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(\"${TITLE_PREFIX}(${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 "${LABEL_TYPE},version" \ + --assignee "jmiller-moko" 2>&1) + echo "✅ Created tracking issue: ${ISSUE_URL}" >> $GITHUB_STEP_SUMMARY + fi -- 2.49.1 From 8f546d086d9ce09df3593505f89eff50763cf605 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:31 -0500 Subject: [PATCH 25/39] chore: update .github/workflows/repo_health.yml from MokoStandards --- .github/workflows/repo_health.yml | 86 ++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/.github/workflows/repo_health.yml b/.github/workflows/repo_health.yml index d3daece..1be23dc 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.01.00 +# VERSION: 04.04.00 # BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts. # NOTE: Field is user-managed. # ============================================================================ @@ -29,7 +29,7 @@ on: workflow_dispatch: inputs: 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 default: all type: choice @@ -39,19 +39,7 @@ on: - scripts - repo pull_request: - paths: - - .github/workflows/** - - scripts/** - - docs/** - - dev/** push: - branches: - - main - paths: - - .github/workflows/** - - scripts/** - - docs/** - - dev/** permissions: contents: read @@ -83,6 +71,9 @@ env: SHELLCHECK_PATTERN: '*.sh' SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml' +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + jobs: access_check: name: Access control @@ -561,6 +552,73 @@ jobs: } >> "${GITHUB_STEP_SUMMARY}" fi + # ── Joomla-specific checks ─────────────────────────────────────── + joomla_findings=() + + # XML manifest: find any XML file containing tag)") + else + # Check tag exists + if ! grep -qP '' "${MANIFEST}"; then + joomla_findings+=("XML manifest: tag missing") + fi + # Check extension type attribute + if ! grep -qP 'type="(component|module|plugin|library|package|template|language)"' "${MANIFEST}"; then + joomla_findings+=("XML manifest: type attribute missing or invalid") + fi + # Check tag + if ! grep -qP '' "${MANIFEST}"; then + joomla_findings+=("XML manifest: tag missing") + fi + # Check tag + if ! grep -qP '' "${MANIFEST}"; then + joomla_findings+=("XML manifest: tag missing") + fi + # Check for Joomla 4+ + if ! grep -qP ' missing (required for Joomla 4+)") + fi + fi + + # Language files: check for at least one .ini file + INI_COUNT="$(find . -name '*.ini' -type f 2>/dev/null | wc -l)" + if [ "${INI_COUNT}" -eq 0 ]; then + joomla_findings+=("No .ini language files found") + fi + + # update.xml must exist in root (Joomla update server) + if [ ! -f 'update.xml' ]; then + joomla_findings+=("update.xml missing in root (required for Joomla update server)") + fi + + # index.html files for directory listing protection + INDEX_DIRS=("src" "src/admin" "src/site") + for dir in "${INDEX_DIRS[@]}"; do + if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then + joomla_findings+=("${dir}/index.html missing (directory listing protection)") + fi + done + + if [ "${#joomla_findings[@]}" -gt 0 ]; then + { + printf '%s\n' '### Joomla extension checks' + printf '%s\n' '| Check | Status |' + printf '%s\n' '|---|---|' + for f in "${joomla_findings[@]}"; do + printf '%s\n' "| ${f} | Warning |" + done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + else + { + printf '%s\n' '### Joomla extension checks' + printf '%s\n' 'All Joomla-specific checks passed.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + extended_enabled="${EXTENDED_CHECKS:-true}" extended_findings=() -- 2.49.1 From 2c42c4a64b538f236e3e2f192e76b34f44c8f7f2 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:32 -0500 Subject: [PATCH 26/39] chore: update .github/ISSUE_TEMPLATE/config.yml from MokoStandards -- 2.49.1 From d5a2144e638e4223fcf3b0748a5bbe39602b46ce Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:33 -0500 Subject: [PATCH 27/39] chore: update .github/ISSUE_TEMPLATE/adr.md from MokoStandards -- 2.49.1 From af240605bdc6b4ef7163ba5ee594d4f75a9d4fd5 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:33 -0500 Subject: [PATCH 28/39] chore: update .github/ISSUE_TEMPLATE/bug_report.md from MokoStandards -- 2.49.1 From 273db6e4d9dc83ac3d8d927d07e98cfabbcad2fa Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:34 -0500 Subject: [PATCH 29/39] chore: update .github/ISSUE_TEMPLATE/documentation.md from MokoStandards -- 2.49.1 From d01ac13386283d08e9bdbae79e931f38783bff38 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:35 -0500 Subject: [PATCH 30/39] chore: update .github/ISSUE_TEMPLATE/enterprise_support.md from MokoStandards -- 2.49.1 From 3ceec2bef44c10be35139b78ca65e928bc2b0741 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:35 -0500 Subject: [PATCH 31/39] chore: update .github/ISSUE_TEMPLATE/feature_request.md from MokoStandards -- 2.49.1 From 9500ececbed629b84360e87462364983dd860ac9 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:36 -0500 Subject: [PATCH 32/39] chore: update .github/ISSUE_TEMPLATE/firewall-request.md from MokoStandards --- .github/ISSUE_TEMPLATE/firewall-request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/firewall-request.md b/.github/ISSUE_TEMPLATE/firewall-request.md index 4a43395..38be866 100644 --- a/.github/ISSUE_TEMPLATE/firewall-request.md +++ b/.github/ISSUE_TEMPLATE/firewall-request.md @@ -3,7 +3,7 @@ name: Firewall Request about: Request firewall rule changes or access to external resources title: '[FIREWALL] [Resource Name] - [Brief Description]' labels: ['firewall-request', 'infrastructure', 'security'] -assignees: [] +assignees: ['jmiller-moko'] --- -- 2.49.1 From 4a398f7e1478224d433e36fd89fa63e10fee6052 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:37 -0500 Subject: [PATCH 33/39] chore: update .github/ISSUE_TEMPLATE/question.md from MokoStandards --- .github/ISSUE_TEMPLATE/question.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index e17850b..74df7a0 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -3,7 +3,7 @@ name: Question about: Ask a question about usage, features, or best practices title: '[QUESTION] ' labels: ['question'] -assignees: [] +assignees: ['jmiller-moko'] --- -- 2.49.1 From 7c151f3328c92a8f27e1dd6c81313a1d794e593b Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:37 -0500 Subject: [PATCH 34/39] chore: update .github/ISSUE_TEMPLATE/request-license.md from MokoStandards --- .github/ISSUE_TEMPLATE/request-license.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/request-license.md b/.github/ISSUE_TEMPLATE/request-license.md index 52c3b74..a9c87a7 100644 --- a/.github/ISSUE_TEMPLATE/request-license.md +++ b/.github/ISSUE_TEMPLATE/request-license.md @@ -3,7 +3,7 @@ name: License Request about: Request an organization license for Sublime Text title: '[LICENSE REQUEST] Sublime Text - [Your Name]' labels: ['license-request', 'admin'] -assignees: [] +assignees: ['jmiller-moko'] --- -- 2.49.1 From 84eeddef78ecaa11642664edc44d695c6758c2fd Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:38 -0500 Subject: [PATCH 35/39] chore: update .github/ISSUE_TEMPLATE/rfc.md from MokoStandards -- 2.49.1 From 84a284a0069377d9362b1e62db62d4bc0a08eb71 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:39 -0500 Subject: [PATCH 36/39] chore: update .github/ISSUE_TEMPLATE/security.md from MokoStandards -- 2.49.1 From 8532fa1d02bef91cd29f9e6683037cec3af43685 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:40 -0500 Subject: [PATCH 37/39] chore: update .github/ISSUE_TEMPLATE/joomla_issue.md from MokoStandards -- 2.49.1 From f557f1f0d899934a15306bb56567de503b22e976 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:36:41 -0500 Subject: [PATCH 38/39] chore: migrate .mokostandards to .github/ --- .github/.mokostandards | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/.mokostandards diff --git a/.github/.mokostandards b/.github/.mokostandards new file mode 100644 index 0000000..a2bf7b4 --- /dev/null +++ b/.github/.mokostandards @@ -0,0 +1,20 @@ +# Copyright (C) 2026 Moko Consulting +# 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.04.00 +# BRIEF: Governance attachment template — synced to .mokostandards in every governed repository +# NOTE: Tokens replaced at sync time: mokoconsulting-tech, MokoCassiopeia, waas-component, 04.04.00 +# +# 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.04.00" +platform: "waas-component" +governed_repo: "mokoconsulting-tech/MokoCassiopeia" -- 2.49.1 From 335990a1ec61231cf84f3848a6acbd929519b57c Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Tue, 31 Mar 2026 11:20:34 -0500 Subject: [PATCH 39/39] fix: delete retired workflows and fix duplicate env: [skip ci] Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/repo_health.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/repo_health.yml b/.github/workflows/repo_health.yml index 1be23dc..946349a 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.04.00 +# VERSION: 04.04.01 # BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts. # NOTE: Field is user-managed. # ============================================================================ @@ -56,7 +56,7 @@ env: # Repo health policy # 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_DISALLOWED_DIRS: REPO_DISALLOWED_FILES: TODO.md,todo.md @@ -70,8 +70,6 @@ env: WORKFLOWS_DIR: .github/workflows SHELLCHECK_PATTERN: '*.sh' SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml' - -env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true jobs: @@ -403,6 +401,15 @@ jobs: exit 0 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 optional_files <<< "${REPO_OPTIONAL_FILES}" IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}" @@ -594,7 +601,7 @@ jobs: fi # index.html files for directory listing protection - INDEX_DIRS=("src" "src/admin" "src/site") + INDEX_DIRS=("${SOURCE_DIR}" "${SOURCE_DIR}/admin" "${SOURCE_DIR}/site") for dir in "${INDEX_DIRS[@]}"; do if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then joomla_findings+=("${dir}/index.html missing (directory listing protection)") -- 2.49.1