chore(deps)(deps): bump the github-actions group with 2 updates #3
@@ -1,94 +0,0 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a bug or issue with MokoStandards scripts, schemas, or documentation
|
||||
title: '[BUG] '
|
||||
labels: ['bug']
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Bug Description
|
||||
|
||||
**Brief summary of the issue:**
|
||||
|
||||
|
||||
**Affected component**:
|
||||
- [ ] Script (specify: _____________)
|
||||
- [ ] Schema (specify: _____________)
|
||||
- [ ] Workflow (specify: _____________)
|
||||
- [ ] Documentation (specify: _____________)
|
||||
- [ ] Enterprise library (specify: _____________)
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
What should have happened:
|
||||
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
What actually happened:
|
||||
|
||||
|
||||
## Environment
|
||||
|
||||
**Operating System**:
|
||||
- [ ] Windows (version: _______)
|
||||
- [ ] macOS (version: _______)
|
||||
- [ ] Linux (distribution: _______)
|
||||
|
||||
**Software Versions**:
|
||||
- Python version:
|
||||
- PowerShell version:
|
||||
- Bash version:
|
||||
- Git version:
|
||||
|
||||
**Repository Context**:
|
||||
- Repository type: [ ] Generic [ ] Joomla/WaaS [ ] Dolibarr/CRM
|
||||
- MokoStandards version/commit:
|
||||
|
||||
## Error Messages
|
||||
|
||||
```
|
||||
Paste any error messages, stack traces, or log output here
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||
If applicable, add screenshots to help explain the problem.
|
||||
|
||||
## Additional Context
|
||||
|
||||
Any other relevant information about the problem:
|
||||
|
||||
|
||||
## Potential Solution
|
||||
|
||||
If you have ideas about what might fix the issue:
|
||||
|
||||
|
||||
## Impact
|
||||
|
||||
**Severity**:
|
||||
- [ ] Critical (blocking work, security issue)
|
||||
- [ ] High (significant impact, workaround exists)
|
||||
- [ ] Medium (annoying but manageable)
|
||||
- [ ] Low (minor inconvenience)
|
||||
|
||||
**Affected users/repos**:
|
||||
- [ ] Just me
|
||||
- [ ] My team
|
||||
- [ ] Multiple teams
|
||||
- [ ] All organization repos
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have searched existing issues to avoid duplicates
|
||||
- [ ] I have included all relevant information
|
||||
- [ ] I have tested with the latest version of MokoStandards
|
||||
- [ ] I have included error messages and logs
|
||||
- [ ] I have specified my environment details
|
||||
@@ -1,114 +0,0 @@
|
||||
---
|
||||
name: Dev Branch Tracking
|
||||
about: Manually create a tracking issue for a development branch
|
||||
title: 'Development Branch: dev/XX.YY.ZZ'
|
||||
labels: ['automation', 'version-management', 'dev-branch']
|
||||
assignees: ['copilot', 'jmiller-moko']
|
||||
---
|
||||
|
||||
## Development Branch Created
|
||||
|
||||
A development branch tracking issue for coordinating work on a version branch.
|
||||
|
||||
### Details
|
||||
- **Branch**: `dev/XX.YY.ZZ` (replace with actual branch name)
|
||||
- **Version**: XX.YY.ZZ (replace with actual version)
|
||||
- **Created**: <!-- Date created -->
|
||||
|
||||
### Next Steps
|
||||
1. Checkout the branch: `git fetch origin && git checkout dev/XX.YY.ZZ`
|
||||
2. Begin development for version XX.YY.ZZ
|
||||
3. Create PRs targeting this branch for feature development
|
||||
4. When ready, merge this branch to main for release
|
||||
|
||||
### Branch Strategy
|
||||
This branch follows the semantic versioning patch increment strategy. It represents a patch release.
|
||||
|
||||
---
|
||||
|
||||
## Launch Checklist - Prepare for Merge to Main
|
||||
|
||||
Complete this checklist before merging this dev branch to main. Reference: [Copilot Pre-Merge Checklist Policy](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/copilot-pre-merge-checklist.md)
|
||||
|
||||
### 1. Version Management ✅
|
||||
- [ ] All version numbers updated consistently (VERSION file, package.json, etc.)
|
||||
- [ ] Version updated in documentation headers
|
||||
- [ ] Version updated in CHANGELOG.md
|
||||
- [ ] No version number inconsistencies remain
|
||||
|
||||
### 2. Changelog Updates ✅
|
||||
- [ ] CHANGELOG.md updated with all branch changes
|
||||
- [ ] Changes grouped by type (Added, Changed, Fixed, Deprecated, Removed, Security)
|
||||
- [ ] Clear descriptions with implementation details
|
||||
- [ ] Files affected listed
|
||||
- [ ] Proper date format used (YYYY-MM-DD or ISO 8601 UTC)
|
||||
|
||||
### 3. Code Review Response ✅
|
||||
- [ ] All review comments addressed
|
||||
- [ ] Requested changes implemented
|
||||
- [ ] Explanations provided for declined suggestions
|
||||
- [ ] Re-review requested if significant changes made
|
||||
|
||||
### 4. Security Scanning ✅
|
||||
- [ ] CodeQL security analysis completed
|
||||
- [ ] Dependency vulnerabilities checked
|
||||
- [ ] No secrets/credentials in code
|
||||
- [ ] Critical and high severity issues fixed
|
||||
- [ ] Accepted risks documented (if any)
|
||||
|
||||
### 5. Code Quality ✅
|
||||
- [ ] All linters pass without errors
|
||||
- [ ] Code properly formatted
|
||||
- [ ] No compiler warnings
|
||||
- [ ] All tests passing
|
||||
- [ ] Code coverage meets threshold
|
||||
- [ ] Shell scripts validated with shellcheck
|
||||
|
||||
### 6. Documentation Updates ✅
|
||||
- [ ] README updated (if public API changed)
|
||||
- [ ] API documentation updated
|
||||
- [ ] User guides updated (if features changed)
|
||||
- [ ] Code comments accurate and complete
|
||||
- [ ] Examples working and updated
|
||||
- [ ] Links validated
|
||||
|
||||
### 7. Drift Detection ✅
|
||||
- [ ] Documentation matches implementation
|
||||
- [ ] File paths in docs are correct
|
||||
- [ ] Code examples validated and working
|
||||
- [ ] Workflow inputs match documentation
|
||||
- [ ] No outdated information remains
|
||||
|
||||
### 8. Standards Compliance ✅
|
||||
- [ ] File headers present and correct
|
||||
- [ ] Tabs used (not spaces) except in YAML/Makefiles
|
||||
- [ ] Timestamps use UTC
|
||||
- [ ] Revision histories in descending order
|
||||
- [ ] Metadata tables complete and accurate
|
||||
- [ ] Semantic versioning followed
|
||||
|
||||
### 9. Release Preparation ✅
|
||||
- [ ] Release notes drafted
|
||||
- [ ] Breaking changes documented
|
||||
- [ ] Migration guide prepared (if needed)
|
||||
- [ ] Deployment notes documented
|
||||
- [ ] Rollback plan prepared
|
||||
|
||||
### 10. Final Verification ✅
|
||||
- [ ] All PRs to this branch reviewed and merged
|
||||
- [ ] No pending issues blocking release
|
||||
- [ ] Stakeholders notified of upcoming merge
|
||||
- [ ] Final PR to main created and ready for review
|
||||
- [ ] All checklist items above completed
|
||||
|
||||
---
|
||||
|
||||
### 📝 Pull Requests
|
||||
|
||||
This section tracks all PRs associated with this branch:
|
||||
|
||||
<!-- PRs will be automatically linked by the enterprise-issue-manager workflow -->
|
||||
|
||||
---
|
||||
|
||||
*This is a manual tracking issue. For automatically created tracking issues, merge a PR to main to trigger the auto-create-dev-branch workflow.*
|
||||
@@ -1,107 +0,0 @@
|
||||
---
|
||||
name: Documentation Issue
|
||||
about: Report missing, unclear, or incorrect documentation
|
||||
title: '[DOCS] '
|
||||
labels: ['documentation']
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Documentation Issue
|
||||
|
||||
**Type of issue**:
|
||||
- [ ] Missing documentation
|
||||
- [ ] Unclear/confusing documentation
|
||||
- [ ] Incorrect/outdated documentation
|
||||
- [ ] Broken links
|
||||
- [ ] Typos/grammar
|
||||
- [ ] Missing examples
|
||||
- [ ] Other: __________
|
||||
|
||||
## Location
|
||||
|
||||
**Affected documentation**:
|
||||
- File:
|
||||
- Section:
|
||||
- URL (if applicable):
|
||||
|
||||
**Related scripts/features** (if applicable):
|
||||
|
||||
|
||||
## Current State
|
||||
|
||||
**What's currently documented** (or what's missing):
|
||||
|
||||
|
||||
## Problem
|
||||
|
||||
**What's the issue with the current documentation?**
|
||||
|
||||
|
||||
**How does this affect users?**
|
||||
|
||||
|
||||
## Proposed Improvement
|
||||
|
||||
**What should be added/changed/fixed:**
|
||||
|
||||
|
||||
**Suggested content** (draft text or outline):
|
||||
|
||||
```markdown
|
||||
# Your suggested documentation content here
|
||||
```
|
||||
|
||||
## Target Audience
|
||||
|
||||
**Who needs this documentation?**
|
||||
|
||||
- [ ] New users/contributors
|
||||
- [ ] Experienced developers
|
||||
- [ ] DevOps engineers
|
||||
- [ ] System administrators
|
||||
- [ ] All users
|
||||
|
||||
## Priority
|
||||
|
||||
**Impact on users**:
|
||||
- [ ] Critical (blocking adoption/usage)
|
||||
- [ ] High (frequently referenced, currently confusing)
|
||||
- [ ] Medium (would help some users)
|
||||
- [ ] Low (minor improvement)
|
||||
|
||||
## Related Documentation
|
||||
|
||||
**Related documentation sections**:
|
||||
-
|
||||
-
|
||||
|
||||
**External references** (if any):
|
||||
-
|
||||
-
|
||||
|
||||
## Examples Needed
|
||||
|
||||
**What examples would help?**
|
||||
|
||||
- [ ] Code examples
|
||||
- [ ] Command examples
|
||||
- [ ] Configuration examples
|
||||
- [ ] Workflow examples
|
||||
- [ ] Diagrams/flowcharts
|
||||
- [ ] Screenshots
|
||||
|
||||
## Additional Context
|
||||
|
||||
**Any other relevant information:**
|
||||
|
||||
|
||||
**Screenshots of current documentation** (if applicable):
|
||||
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have searched existing issues to avoid duplicates
|
||||
- [ ] I have identified the specific location of the issue
|
||||
- [ ] I have provided clear suggestions for improvement
|
||||
- [ ] I have considered the target audience
|
||||
- [ ] I am willing to contribute a documentation PR (if applicable)
|
||||
@@ -1,141 +0,0 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest a new feature, script, or enhancement for MokoStandards
|
||||
title: '[FEATURE] '
|
||||
labels: ['enhancement']
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Feature Description
|
||||
|
||||
**Brief summary of the proposed feature:**
|
||||
|
||||
|
||||
**Category**:
|
||||
- [ ] New script
|
||||
- [ ] Enhancement to existing script
|
||||
- [ ] New enterprise library
|
||||
- [ ] Schema improvement
|
||||
- [ ] Documentation improvement
|
||||
- [ ] Workflow enhancement
|
||||
- [ ] Other (specify): __________
|
||||
|
||||
## Problem Statement
|
||||
|
||||
**What problem does this feature solve?**
|
||||
|
||||
|
||||
**Who is affected by this problem?**
|
||||
- [ ] Individual developers
|
||||
- [ ] Development teams
|
||||
- [ ] DevOps teams
|
||||
- [ ] All organization members
|
||||
- [ ] External contributors
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
**Describe your proposed solution:**
|
||||
|
||||
|
||||
**How should it work?**
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
**Example usage** (if applicable):
|
||||
|
||||
```bash
|
||||
# Example command or code
|
||||
```
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
**What alternatives have you considered?**
|
||||
|
||||
|
||||
**Why is this solution better?**
|
||||
|
||||
|
||||
## Benefits
|
||||
|
||||
**What are the expected benefits?**
|
||||
|
||||
- [ ] Improved productivity
|
||||
- [ ] Better code quality
|
||||
- [ ] Enhanced security
|
||||
- [ ] Cost savings
|
||||
- [ ] Compliance/audit improvements
|
||||
- [ ] Developer experience
|
||||
- [ ] Other: __________
|
||||
|
||||
**Estimated impact**:
|
||||
|
||||
|
||||
## Implementation Details
|
||||
|
||||
**Do you have implementation ideas?**
|
||||
|
||||
|
||||
**Required resources**:
|
||||
- [ ] Development time (estimate: _____ hours/days)
|
||||
- [ ] Testing infrastructure
|
||||
- [ ] Documentation
|
||||
- [ ] Training materials
|
||||
- [ ] External dependencies (specify): __________
|
||||
|
||||
## Compatibility
|
||||
|
||||
**Platform compatibility**:
|
||||
- [ ] Generic projects
|
||||
- [ ] Joomla/MokoWaaS components
|
||||
- [ ] Dolibarr/MokoCRM modules
|
||||
- [ ] All platforms
|
||||
|
||||
**Backward compatibility**:
|
||||
- [ ] Fully backward compatible
|
||||
- [ ] Requires migration (describe): __________
|
||||
- [ ] Breaking change (justify): __________
|
||||
|
||||
## Priority
|
||||
|
||||
**How urgent is this feature?**
|
||||
|
||||
- [ ] Critical (needed urgently)
|
||||
- [ ] High (would significantly help)
|
||||
- [ ] Medium (nice to have)
|
||||
- [ ] Low (future consideration)
|
||||
|
||||
**Justification for priority**:
|
||||
|
||||
|
||||
## Related Issues
|
||||
|
||||
**Related issues or PRs**:
|
||||
- #
|
||||
- #
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**How will we know when this feature is complete?**
|
||||
|
||||
- [ ]
|
||||
- [ ]
|
||||
- [ ]
|
||||
|
||||
## Additional Context
|
||||
|
||||
**Any other relevant information:**
|
||||
|
||||
|
||||
**Screenshots/Mockups** (if applicable):
|
||||
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have searched existing issues to avoid duplicates
|
||||
- [ ] I have clearly described the problem and solution
|
||||
- [ ] I have considered alternatives
|
||||
- [ ] I have estimated the impact and benefits
|
||||
- [ ] I have checked compatibility requirements
|
||||
- [ ] I am willing to contribute to implementation (if applicable)
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question about MokoStandards usage, features, or best practices
|
||||
title: '[QUESTION] '
|
||||
labels: ['question']
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Question
|
||||
|
||||
**Your question:**
|
||||
|
||||
|
||||
## Context
|
||||
|
||||
**What are you trying to accomplish?**
|
||||
|
||||
|
||||
**What have you already tried?**
|
||||
|
||||
|
||||
**Category**:
|
||||
- [ ] Script usage
|
||||
- [ ] Enterprise library integration
|
||||
- [ ] Schema configuration
|
||||
- [ ] Workflow setup
|
||||
- [ ] Documentation interpretation
|
||||
- [ ] Best practices
|
||||
- [ ] Platform-specific (Generic/Joomla/Dolibarr)
|
||||
- [ ] Other: __________
|
||||
|
||||
## Environment (if relevant)
|
||||
|
||||
**Your setup**:
|
||||
- Operating System:
|
||||
- Repository type: [ ] Generic [ ] Joomla/WaaS [ ] Dolibarr/CRM
|
||||
- MokoStandards version:
|
||||
|
||||
## What You've Researched
|
||||
|
||||
**Documentation reviewed**:
|
||||
- [ ] README.md
|
||||
- [ ] Script documentation (/docs/scripts/)
|
||||
- [ ] Enterprise library docs
|
||||
- [ ] Schema documentation
|
||||
- [ ] Sublime Text setup guide
|
||||
- [ ] Other (specify): __________
|
||||
|
||||
**Similar issues/questions found**:
|
||||
- #
|
||||
- #
|
||||
|
||||
## Expected Outcome
|
||||
|
||||
**What result are you hoping for?**
|
||||
|
||||
|
||||
## Code/Configuration Samples
|
||||
|
||||
**Relevant code or configuration** (if applicable):
|
||||
|
||||
```bash
|
||||
# Your code here
|
||||
```
|
||||
|
||||
## Additional Context
|
||||
|
||||
**Any other relevant information:**
|
||||
|
||||
|
||||
**Screenshots** (if helpful):
|
||||
|
||||
|
||||
## Urgency
|
||||
|
||||
- [ ] Urgent (blocking work)
|
||||
- [ ] Normal (can work on other things meanwhile)
|
||||
- [ ] Low priority (just curious)
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have searched existing issues and discussions
|
||||
- [ ] I have reviewed relevant documentation
|
||||
- [ ] I have provided sufficient context
|
||||
- [ ] I have included code/configuration samples if relevant
|
||||
- [ ] This is a genuine question (not a bug report or feature request)
|
||||
@@ -1,110 +0,0 @@
|
||||
---
|
||||
name: License Request
|
||||
about: Request an organization license for Sublime Text
|
||||
title: '[LICENSE REQUEST] Sublime Text - [Your Name]'
|
||||
labels: ['license-request', 'admin']
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## License Request
|
||||
|
||||
### Tool Information
|
||||
**Tool Name**: Sublime Text
|
||||
|
||||
**License Type Requested**: Organization Pool
|
||||
|
||||
**Personal Purchase**:
|
||||
- [ ] I prefer to purchase my own license ($99 USD - recommended, immediate access)
|
||||
- [ ] I prefer an organization license (1-2 business days, organization use only)
|
||||
- [ ] I have already purchased my own license (registration only for support)
|
||||
|
||||
### Requestor Information
|
||||
**Name**:
|
||||
**GitHub Username**: @
|
||||
**Email**: @mokoconsulting.tech
|
||||
**Team/Department**:
|
||||
**Manager**: @
|
||||
|
||||
### Justification
|
||||
**Why do you need this license?**
|
||||
|
||||
|
||||
**Primary use case**:
|
||||
- [ ] Remote development (SFTP to servers)
|
||||
- [ ] Local development
|
||||
- [ ] Code review
|
||||
- [ ] Documentation editing
|
||||
- [ ] Other (specify):
|
||||
|
||||
**Which projects/repositories will you work on?**
|
||||
|
||||
|
||||
**Have you evaluated the free trial?**
|
||||
- [ ] Yes, I've used the trial and Sublime Text meets my needs
|
||||
- [ ] No, requesting license before trial
|
||||
|
||||
**Alternative tools considered**:
|
||||
- [ ] VS Code (free alternative)
|
||||
- [ ] Vim/Neovim (free, terminal-based)
|
||||
- [ ] Other: _______________
|
||||
|
||||
### Platform
|
||||
- [ ] Windows
|
||||
- [ ] macOS
|
||||
- [ ] Linux (distribution: ________)
|
||||
|
||||
### Urgency
|
||||
- [ ] Urgent (needed within 24 hours - please justify)
|
||||
- [ ] Normal (1-2 business days)
|
||||
- [ ] Low priority (when available)
|
||||
|
||||
**If urgent, please explain why:**
|
||||
|
||||
|
||||
### SFTP Plugin
|
||||
**Note**: Sublime SFTP plugin ($16 USD) is a **separate personal purchase** and is NOT provided by the organization.
|
||||
|
||||
- [ ] I understand SFTP plugin requires separate personal purchase
|
||||
- [ ] I have already purchased SFTP plugin
|
||||
- [ ] I will purchase SFTP plugin if needed for my work
|
||||
- [ ] I don't need SFTP plugin (local development only)
|
||||
|
||||
### Acknowledgments
|
||||
- [ ] I have read the License Management Policy (/docs/github-private/LICENSE_MANAGEMENT.md)
|
||||
- [ ] I understand organization licenses are for work use only
|
||||
- [ ] I understand organization licenses must be returned upon leaving
|
||||
- [ ] I understand personal purchases ($99) are an alternative with lifetime access
|
||||
- [ ] I understand SFTP plugin ($16) requires separate personal purchase
|
||||
- [ ] I agree to the terms of use
|
||||
|
||||
### Additional Information
|
||||
|
||||
**Expected daily usage hours**: _____ hours/day
|
||||
|
||||
**Duration of need**:
|
||||
- [ ] Permanent (ongoing role)
|
||||
- [ ] Temporary project (_____ months)
|
||||
- [ ] Trial/Evaluation (_____ weeks)
|
||||
|
||||
**Comments/Questions**:
|
||||
|
||||
|
||||
---
|
||||
|
||||
## For Admin Use Only
|
||||
|
||||
**Do not edit below this line**
|
||||
|
||||
- [ ] Manager approval received (@manager-username)
|
||||
- [ ] License available in pool (current: __/20)
|
||||
- [ ] License type confirmed (Organization / Personal registration)
|
||||
- [ ] License key sent via encrypted email
|
||||
- [ ] Activation confirmed by user
|
||||
- [ ] Added to license tracking sheet
|
||||
- [ ] User notified of SFTP plugin requirement
|
||||
|
||||
**License Key ID**: _____________
|
||||
**Date Issued**: _____________
|
||||
**Issued By**: @_____________
|
||||
|
||||
**Notes**:
|
||||
@@ -1,127 +0,0 @@
|
||||
---
|
||||
name: Security Vulnerability
|
||||
about: Report a security vulnerability (use private reporting if critical)
|
||||
title: '[SECURITY] '
|
||||
labels: ['security']
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## ⚠️ Security Vulnerability Report
|
||||
|
||||
**IMPORTANT**: For **critical vulnerabilities**, please use GitHub's [Private Vulnerability Reporting](https://github.com/mokoconsulting-tech/MokoStandards/security/advisories/new) or email security@mokoconsulting.tech directly. Do NOT create a public issue.
|
||||
|
||||
Use this template only for **low to medium severity** issues that don't pose immediate risk.
|
||||
|
||||
---
|
||||
|
||||
## Vulnerability Summary
|
||||
|
||||
**Brief description** (without sensitive details):
|
||||
|
||||
|
||||
**Affected component**:
|
||||
- [ ] Script
|
||||
- [ ] Enterprise library
|
||||
- [ ] Workflow
|
||||
- [ ] Schema
|
||||
- [ ] Documentation
|
||||
- [ ] Other: __________
|
||||
|
||||
## Severity Assessment
|
||||
|
||||
**Severity level** (your assessment):
|
||||
- [ ] Critical (immediate exploitation possible, high impact)
|
||||
- [ ] High (exploitation likely, significant impact)
|
||||
- [ ] Medium (exploitation possible with conditions, moderate impact)
|
||||
- [ ] Low (theoretical risk, minimal impact)
|
||||
|
||||
**CVSS Score** (if calculated): _____ / 10
|
||||
|
||||
## Affected Versions
|
||||
|
||||
**MokoStandards version(s)**:
|
||||
- Commit/tag:
|
||||
|
||||
**Affected platforms**:
|
||||
- [ ] All platforms
|
||||
- [ ] Windows
|
||||
- [ ] macOS
|
||||
- [ ] Linux
|
||||
- [ ] Generic repos
|
||||
- [ ] Joomla/WaaS repos
|
||||
- [ ] Dolibarr/CRM repos
|
||||
|
||||
## Impact Analysis
|
||||
|
||||
**What could an attacker do?**
|
||||
|
||||
|
||||
**What data/systems are at risk?**
|
||||
|
||||
|
||||
**Who is affected?**
|
||||
- [ ] All organization members
|
||||
- [ ] Repository administrators
|
||||
- [ ] CI/CD pipelines
|
||||
- [ ] Specific teams (specify): __________
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
**Proof of concept** (without exploit code):
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Potential Fix
|
||||
|
||||
**Do you have suggestions for remediation?**
|
||||
|
||||
|
||||
**Temporary workarounds** (if any):
|
||||
|
||||
|
||||
## References
|
||||
|
||||
**Related CVEs, advisories, or security issues**:
|
||||
-
|
||||
-
|
||||
|
||||
**Security best practices violated**:
|
||||
-
|
||||
|
||||
## Discovery Context
|
||||
|
||||
**How was this discovered?**
|
||||
- [ ] Security audit
|
||||
- [ ] Code review
|
||||
- [ ] Automated scanning (tool: ______)
|
||||
- [ ] Incident response
|
||||
- [ ] User report
|
||||
- [ ] Other: __________
|
||||
|
||||
## Additional Information
|
||||
|
||||
**Relevant logs, configurations, or context** (sanitized):
|
||||
|
||||
|
||||
## Responsible Disclosure
|
||||
|
||||
- [ ] I understand this is a public issue and should not contain sensitive details
|
||||
- [ ] I have verified this is NOT a critical vulnerability requiring private reporting
|
||||
- [ ] I have checked for existing security advisories
|
||||
- [ ] I am willing to work with maintainers on a fix (if applicable)
|
||||
- [ ] I understand security@mokoconsulting.tech is the contact for critical issues
|
||||
|
||||
## Checklist for Maintainers
|
||||
|
||||
**Do not edit below this line**
|
||||
|
||||
- [ ] Severity confirmed
|
||||
- [ ] Impact assessment completed
|
||||
- [ ] Fix developed
|
||||
- [ ] Fix tested
|
||||
- [ ] Security advisory created (if needed)
|
||||
- [ ] Affected users notified
|
||||
- [ ] CVE requested (if applicable)
|
||||
- [ ] Documentation updated
|
||||
@@ -1,116 +0,0 @@
|
||||
name: Sub-Task
|
||||
description: Create a sub-task or sub-issue to track a specific piece of work related to a parent issue
|
||||
title: "[Task] "
|
||||
labels: ["sub-task"]
|
||||
assignees: []
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Sub-Task Issue
|
||||
|
||||
This template is for creating sub-tasks that are part of a larger parent issue. Sub-tasks help break down complex work into manageable pieces.
|
||||
|
||||
- type: input
|
||||
id: parent_issue
|
||||
attributes:
|
||||
label: Parent Issue
|
||||
description: The issue number this sub-task belongs to (e.g., #193)
|
||||
placeholder: "#193"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: task_title
|
||||
attributes:
|
||||
label: Task Title
|
||||
description: A brief, descriptive title for this sub-task
|
||||
placeholder: "Investigate token permissions issue"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: task_description
|
||||
attributes:
|
||||
label: Task Description
|
||||
description: Detailed description of what needs to be done
|
||||
placeholder: "Describe the specific work to be completed..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: task_type
|
||||
attributes:
|
||||
label: Task Type
|
||||
description: What type of work is this?
|
||||
options:
|
||||
- Investigation
|
||||
- Bug Fix
|
||||
- Feature Implementation
|
||||
- Documentation
|
||||
- Testing
|
||||
- Refactoring
|
||||
- Configuration
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: Priority
|
||||
description: How urgent is this sub-task?
|
||||
options:
|
||||
- Low
|
||||
- Medium
|
||||
- High
|
||||
- Critical
|
||||
default: 1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: acceptance_criteria
|
||||
attributes:
|
||||
label: Acceptance Criteria
|
||||
description: What conditions must be met for this sub-task to be considered complete?
|
||||
placeholder: |
|
||||
- [ ] Criterion 1
|
||||
- [ ] Criterion 2
|
||||
- [ ] Criterion 3
|
||||
value: |
|
||||
- [ ]
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: dependencies
|
||||
attributes:
|
||||
label: Dependencies
|
||||
description: Does this sub-task depend on other issues or sub-tasks?
|
||||
placeholder: "List any blocking issues or required prerequisites..."
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: notes
|
||||
attributes:
|
||||
label: Additional Notes
|
||||
description: Any other relevant information
|
||||
placeholder: "Additional context, links, or references..."
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Pre-submission Checklist
|
||||
description: Please confirm before creating this sub-task
|
||||
options:
|
||||
- label: This sub-task is linked to a parent issue
|
||||
required: true
|
||||
- label: The task description is clear and actionable
|
||||
required: true
|
||||
- label: I have assigned appropriate labels and priority
|
||||
required: false
|
||||
@@ -1,107 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Dependabot
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/dependabot.yml
|
||||
# VERSION: 04.00.03
|
||||
# BRIEF: Dependabot configuration for automated dependency updates and security patches
|
||||
# NOTE: Monitors GitHub Actions for vulnerabilities and keeps ecosystem secure
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# Monitor GitHub Actions for security updates
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 1
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "security"
|
||||
- "automated"
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
include: "scope"
|
||||
reviewers:
|
||||
- "mokoconsulting-tech/maintainers"
|
||||
assignees:
|
||||
- "jmiller-moko"
|
||||
# Group all updates together
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
# Monitor Python dependencies for security updates
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 1
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "security"
|
||||
- "automated"
|
||||
- "python"
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
include: "scope"
|
||||
reviewers:
|
||||
- "mokoconsulting-tech/maintainers"
|
||||
assignees:
|
||||
- "jmiller-moko"
|
||||
# Group all updates together
|
||||
groups:
|
||||
python-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
# Monitor npm dependencies for security updates
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 1
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "security"
|
||||
- "automated"
|
||||
- "javascript"
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
include: "scope"
|
||||
reviewers:
|
||||
- "mokoconsulting-tech/maintainers"
|
||||
assignees:
|
||||
- "jmiller-moko"
|
||||
# Group all updates together
|
||||
groups:
|
||||
npm-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
# Monitor Composer dependencies for security updates
|
||||
- package-ecosystem: "composer"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 1
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "security"
|
||||
- "automated"
|
||||
- "php"
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
include: "scope"
|
||||
reviewers:
|
||||
- "mokoconsulting-tech/maintainers"
|
||||
assignees:
|
||||
- "jmiller-moko"
|
||||
# Group all updates together
|
||||
groups:
|
||||
composer-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
@@ -1,191 +0,0 @@
|
||||
## Description
|
||||
|
||||
<!-- Provide a clear and concise description of the changes in this PR -->
|
||||
|
||||
## Type of Change
|
||||
|
||||
<!-- Check all that apply -->
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Documentation update
|
||||
- [ ] Infrastructure/tooling change
|
||||
- [ ] Refactoring (no functional changes)
|
||||
- [ ] Performance improvement
|
||||
- [ ] Security fix
|
||||
|
||||
## Related Issues
|
||||
|
||||
<!-- Link to related issues, e.g., "Fixes #123" or "Relates to #456" -->
|
||||
|
||||
Fixes #
|
||||
Relates to #
|
||||
|
||||
## Pre-Merge Copilot Checklist
|
||||
|
||||
<!-- All items must be completed before merge. See docs/policy/copilot-pre-merge-checklist.md for details and sample prompts -->
|
||||
|
||||
### 1. Version Management ✅
|
||||
- [ ] All version numbers updated consistently (VERSION file, package.json, etc.)
|
||||
- [ ] Version updated in documentation headers
|
||||
- [ ] Version updated in CHANGELOG.md
|
||||
- [ ] No version number inconsistencies remain
|
||||
|
||||
**Copilot Prompt Used**:
|
||||
```
|
||||
Update all version numbers from X.Y.Z to X.Y.Z+1 across the repository
|
||||
```
|
||||
|
||||
### 2. Changelog Updates ✅
|
||||
- [ ] CHANGELOG.md updated with all PR changes
|
||||
- [ ] Changes grouped by type (Added, Changed, Fixed, Deprecated, Removed, Security)
|
||||
- [ ] Clear descriptions with implementation details
|
||||
- [ ] Files affected listed
|
||||
- [ ] Proper date format used (YYYY-MM-DD or ISO 8601 UTC)
|
||||
|
||||
**Copilot Prompt Used**:
|
||||
```
|
||||
Update CHANGELOG.md with all changes from this PR, grouped by type with implementation details
|
||||
```
|
||||
|
||||
### 3. Code Review Response ✅
|
||||
- [ ] All review comments addressed
|
||||
- [ ] Requested changes implemented
|
||||
- [ ] Explanations provided for declined suggestions
|
||||
- [ ] Re-review requested if significant changes made
|
||||
|
||||
### 4. Security Scanning ✅
|
||||
- [ ] CodeQL security analysis completed
|
||||
- [ ] Dependency vulnerabilities checked
|
||||
- [ ] No secrets/credentials in code
|
||||
- [ ] Critical and high severity issues fixed
|
||||
- [ ] Accepted risks documented (if any)
|
||||
|
||||
**Security Scan Results**: <!-- Link to scan results or summarize findings -->
|
||||
|
||||
### 5. Code Quality ✅
|
||||
- [ ] All linters pass without errors
|
||||
- [ ] Code properly formatted
|
||||
- [ ] No compiler warnings
|
||||
- [ ] All tests passing
|
||||
- [ ] Code coverage meets threshold
|
||||
- [ ] Shell scripts validated with shellcheck
|
||||
|
||||
**Test Results**: <!-- Summarize test execution results -->
|
||||
|
||||
### 6. Documentation Updates ✅
|
||||
- [ ] README updated (if public API changed)
|
||||
- [ ] API documentation updated
|
||||
- [ ] User guides updated (if features changed)
|
||||
- [ ] Code comments accurate and complete
|
||||
- [ ] Examples working and updated
|
||||
- [ ] Links validated
|
||||
|
||||
### 7. Drift Detection ✅
|
||||
- [ ] Documentation matches implementation
|
||||
- [ ] File paths in docs are correct
|
||||
- [ ] Code examples validated and working
|
||||
- [ ] Workflow inputs match documentation
|
||||
- [ ] No outdated information remains
|
||||
|
||||
### 8. Standards Compliance ✅
|
||||
- [ ] File headers present and correct
|
||||
- [ ] Tabs used (not spaces) except in YAML/Makefiles
|
||||
- [ ] Timestamps use UTC
|
||||
- [ ] Revision histories in descending order
|
||||
- [ ] Metadata tables complete and accurate
|
||||
- [ ] Semantic versioning followed
|
||||
|
||||
## Testing Performed
|
||||
|
||||
<!-- Describe the testing you've done -->
|
||||
|
||||
### Unit Tests
|
||||
- [ ] All existing tests pass
|
||||
- [ ] New tests added for new functionality
|
||||
- [ ] Edge cases covered
|
||||
|
||||
### Integration Tests
|
||||
- [ ] Integration tests pass
|
||||
- [ ] End-to-end scenarios tested
|
||||
|
||||
### Manual Testing
|
||||
<!-- Describe manual testing performed -->
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
<!-- If this introduces breaking changes, describe them here -->
|
||||
|
||||
- None
|
||||
|
||||
<!-- OR -->
|
||||
|
||||
### Breaking Change Details
|
||||
<!-- Describe what breaks and how to migrate -->
|
||||
|
||||
## Deployment Notes
|
||||
|
||||
<!-- Any special deployment considerations? -->
|
||||
|
||||
- None
|
||||
|
||||
<!-- OR -->
|
||||
|
||||
<!-- Describe special deployment steps, configuration changes, database migrations, etc. -->
|
||||
|
||||
## Screenshots/Videos
|
||||
|
||||
<!-- If applicable, add screenshots or videos demonstrating the changes -->
|
||||
|
||||
## Comprehensive Pre-Merge Prompt (Optional)
|
||||
|
||||
<!-- If you used the comprehensive pre-merge prompt, document it here -->
|
||||
|
||||
<details>
|
||||
<summary>Comprehensive Prompt Used</summary>
|
||||
|
||||
```
|
||||
Prepare this PR for merge by completing all pre-merge requirements:
|
||||
|
||||
1. UPDATE VERSION NUMBERS: Update all version numbers from X.Y.Z to X.Y.Z+1
|
||||
2. UPDATE CHANGELOG: Review all commits and update CHANGELOG.md
|
||||
3. ADDRESS CODE REVIEW: Review and address all code review comments
|
||||
4. RUN SECURITY SCANS: Execute CodeQL, dependency scanning, secret detection
|
||||
5. FIX QUALITY ISSUES: Run linters, formatters, tests
|
||||
6. UPDATE DOCUMENTATION: Update README, API docs, user guides, examples
|
||||
7. CHECK FOR DRIFT: Validate documentation matches implementation
|
||||
8. VERIFY STANDARDS COMPLIANCE: File headers, indentation, timestamps, metadata
|
||||
9. CREATE FINAL SUMMARY: Generate comprehensive PR description
|
||||
10. REQUEST FINAL REVIEW: Tag reviewers for final approval
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Additional Context
|
||||
|
||||
<!-- Add any other context about the PR here -->
|
||||
|
||||
## Checklist for Reviewers
|
||||
|
||||
<!-- For reviewers to verify -->
|
||||
|
||||
- [ ] Code changes align with described functionality
|
||||
- [ ] Pre-merge checklist completed
|
||||
- [ ] Version numbers consistent
|
||||
- [ ] CHANGELOG accurate and complete
|
||||
- [ ] Tests adequate and passing
|
||||
- [ ] Documentation updated
|
||||
- [ ] No security concerns
|
||||
- [ ] Follows coding standards
|
||||
- [ ] Breaking changes properly documented
|
||||
- [ ] Deployment notes clear
|
||||
|
||||
---
|
||||
|
||||
**Policy Reference**: [Copilot Pre-Merge Checklist Policy](../docs/policy/copilot-pre-merge-checklist.md)
|
||||
|
||||
<!--
|
||||
For detailed guidance on each checklist item and sample Copilot prompts,
|
||||
see docs/policy/copilot-pre-merge-checklist.md
|
||||
-->
|
||||
@@ -1,372 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoStandards.Workflows
|
||||
# INGROUP: MokoStandards.Automation
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/workflows/auto-create-dev-branch.yml
|
||||
# VERSION: 04.00.03
|
||||
# BRIEF: Automatically creates dev/<version> branch and tracking issue assigned to copilot and jmiller-moko
|
||||
|
||||
name: Auto-Create Dev Branch
|
||||
|
||||
env:
|
||||
ACTIONS_STEP_DEBUG: true
|
||||
ACTIONS_RUNNER_DEBUG: true
|
||||
|
||||
# MokoStandards Policy Compliance:
|
||||
# - File formatting: Enforces organizational coding standards
|
||||
# - Reference: docs/policy/file-formatting.md
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────┐
|
||||
# │ WORKFLOW FLOW DIAGRAM │
|
||||
# └─────────────────────────────────────────────────────────────────────────┘
|
||||
#
|
||||
# TRIGGER: PR Merged to Main
|
||||
# │
|
||||
# ▼
|
||||
# ┌──────────────────┐
|
||||
# │ Extract Current │
|
||||
# │ Version │──────┐ Read CHANGELOG.md, VERSION file
|
||||
# │ (XX.YY.ZZ) │ │ or fallback to 03.00.00
|
||||
# └──────────────────┘ │
|
||||
# │ │
|
||||
# ▼ │
|
||||
# ┌──────────────────┐ │
|
||||
# │ Calculate Next │◀─────┘
|
||||
# │ Version │
|
||||
# │ (XX.YY.ZZ+1) │
|
||||
# └──────────────────┘
|
||||
# │
|
||||
# ├─────────────────────────┐
|
||||
# ▼ ▼
|
||||
# ┌──────────────────┐ ┌──────────────────┐
|
||||
# │ Create Branch │ │ Create Issue │
|
||||
# │ dev/XX.YY.ZZ │ │ "Dev Branch: │
|
||||
# │ │ │ vXX.YY.ZZ" │
|
||||
# └──────────────────┘ └──────────────────┘
|
||||
# │ │
|
||||
# │ ▼
|
||||
# │ ┌──────────────────┐
|
||||
# │ │ Assign to: │
|
||||
# │ │ • copilot │
|
||||
# │ │ • jmiller-moko │
|
||||
# │ └──────────────────┘
|
||||
# │ │
|
||||
# └─────────────┬───────────┘
|
||||
# ▼
|
||||
# ┌──────────────┐
|
||||
# │ OUTPUTS: │
|
||||
# │ • New Branch │
|
||||
# │ • New Issue │
|
||||
# └──────────────┘
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
create-dev-branch:
|
||||
name: Create Development Branch with Version Bump
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
set -x
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Extract current version
|
||||
id: version
|
||||
run: |
|
||||
set -x
|
||||
# Try to extract version from CHANGELOG.md first
|
||||
if [ -f "CHANGELOG.md" ]; then
|
||||
# Look for version in format [XX.YY.ZZ] or XX.YY.ZZ
|
||||
VERSION=$(grep -oP '\[?\d+\.\d+\.\d+\]?' CHANGELOG.md | head -1 | tr -d '[]')
|
||||
fi
|
||||
|
||||
# Fallback: Try to find VERSION file or header
|
||||
if [ -z "$VERSION" ]; then
|
||||
if [ -f "VERSION" ]; then
|
||||
VERSION=$(cat VERSION)
|
||||
else
|
||||
# Look for VERSION: in any markdown file
|
||||
VERSION=$(grep -r "^VERSION: " . --include="*.md" | head -1 | grep -oP '\d+\.\d+\.\d+' || echo "03.00.00")
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Current version: $VERSION"
|
||||
echo "current=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Calculate next patch version
|
||||
id: next_version
|
||||
run: |
|
||||
set -x
|
||||
CURRENT="${{ steps.version.outputs.current }}"
|
||||
|
||||
# Parse version components
|
||||
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
|
||||
|
||||
# Increment patch version
|
||||
NEXT_PATCH=$((PATCH + 1))
|
||||
|
||||
# Format with leading zeros if needed
|
||||
if [ ${#MAJOR} -eq 2 ]; then
|
||||
# Zero-padded format (XX.YY.ZZ)
|
||||
NEXT_VERSION=$(printf "%02d.%02d.%02d" "$MAJOR" "$MINOR" "$NEXT_PATCH")
|
||||
else
|
||||
# Standard semver format (X.Y.Z)
|
||||
NEXT_VERSION="$MAJOR.$MINOR.$NEXT_PATCH"
|
||||
fi
|
||||
|
||||
echo "Next version: $NEXT_VERSION"
|
||||
echo "version=$NEXT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check if dev branch already exists
|
||||
id: check_branch
|
||||
run: |
|
||||
set -x
|
||||
BRANCH_NAME="dev/${{ steps.next_version.outputs.version }}"
|
||||
|
||||
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
|
||||
echo "exists=true" >> $GITHUB_OUTPUT
|
||||
echo "Branch $BRANCH_NAME already exists"
|
||||
else
|
||||
echo "exists=false" >> $GITHUB_OUTPUT
|
||||
echo "Branch $BRANCH_NAME does not exist"
|
||||
fi
|
||||
|
||||
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create new dev branch
|
||||
if: steps.check_branch.outputs.exists == 'false'
|
||||
run: |
|
||||
set -x
|
||||
BRANCH_NAME="${{ steps.check_branch.outputs.branch_name }}"
|
||||
|
||||
# Create branch from main
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
|
||||
# Push new branch
|
||||
git push origin "$BRANCH_NAME"
|
||||
|
||||
echo "✅ Created and pushed branch: $BRANCH_NAME"
|
||||
|
||||
- name: Create tracking issue
|
||||
if: steps.check_branch.outputs.exists == 'false'
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const nextVersion = '${{ steps.next_version.outputs.version }}';
|
||||
const branchName = '${{ steps.check_branch.outputs.branch_name }}';
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const prTitle = context.payload.pull_request.title;
|
||||
|
||||
const issueBody = `## Development Branch Created
|
||||
|
||||
A new development branch has been automatically created following the merge of PR #${prNumber}.
|
||||
|
||||
### Details
|
||||
- **Branch**: \`${branchName}\`
|
||||
- **Version**: ${nextVersion}
|
||||
- **Merged PR**: #${prNumber} - ${prTitle}
|
||||
- **Created**: ${new Date().toISOString()}
|
||||
|
||||
### Next Steps
|
||||
1. Checkout the new branch: \`git fetch origin && git checkout ${branchName}\`
|
||||
2. Begin development for version ${nextVersion}
|
||||
3. Create PRs targeting this branch for feature development
|
||||
4. When ready, merge this branch to main for release
|
||||
|
||||
### Branch Strategy
|
||||
This branch follows the semantic versioning patch increment strategy. It represents the next patch release after the current version.
|
||||
|
||||
---
|
||||
|
||||
## Launch Checklist - Prepare for Merge to Main
|
||||
|
||||
Complete this checklist before merging this dev branch to main. Reference: [Copilot Pre-Merge Checklist Policy](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/policy/copilot-pre-merge-checklist.md)
|
||||
|
||||
### 1. Version Management ✅
|
||||
- [ ] All version numbers updated consistently (VERSION file, package.json, etc.)
|
||||
- [ ] Version updated in documentation headers
|
||||
- [ ] Version updated in CHANGELOG.md
|
||||
- [ ] No version number inconsistencies remain
|
||||
|
||||
### 2. Changelog Updates ✅
|
||||
- [ ] CHANGELOG.md updated with all branch changes
|
||||
- [ ] Changes grouped by type (Added, Changed, Fixed, Deprecated, Removed, Security)
|
||||
- [ ] Clear descriptions with implementation details
|
||||
- [ ] Files affected listed
|
||||
- [ ] Proper date format used (YYYY-MM-DD or ISO 8601 UTC)
|
||||
|
||||
### 3. Code Review Response ✅
|
||||
- [ ] All review comments addressed
|
||||
- [ ] Requested changes implemented
|
||||
- [ ] Explanations provided for declined suggestions
|
||||
- [ ] Re-review requested if significant changes made
|
||||
|
||||
### 4. Security Scanning ✅
|
||||
- [ ] CodeQL security analysis completed
|
||||
- [ ] Dependency vulnerabilities checked
|
||||
- [ ] No secrets/credentials in code
|
||||
- [ ] Critical and high severity issues fixed
|
||||
- [ ] Accepted risks documented (if any)
|
||||
|
||||
### 5. Code Quality ✅
|
||||
- [ ] All linters pass without errors
|
||||
- [ ] Code properly formatted
|
||||
- [ ] No compiler warnings
|
||||
- [ ] All tests passing
|
||||
- [ ] Code coverage meets threshold
|
||||
- [ ] Shell scripts validated with shellcheck
|
||||
|
||||
### 6. Documentation Updates ✅
|
||||
- [ ] README updated (if public API changed)
|
||||
- [ ] API documentation updated
|
||||
- [ ] User guides updated (if features changed)
|
||||
- [ ] Code comments accurate and complete
|
||||
- [ ] Examples working and updated
|
||||
- [ ] Links validated
|
||||
|
||||
### 7. Drift Detection ✅
|
||||
- [ ] Documentation matches implementation
|
||||
- [ ] File paths in docs are correct
|
||||
- [ ] Code examples validated and working
|
||||
- [ ] Workflow inputs match documentation
|
||||
- [ ] No outdated information remains
|
||||
|
||||
### 8. Standards Compliance ✅
|
||||
- [ ] File headers present and correct
|
||||
- [ ] Tabs used (not spaces) except in YAML/Makefiles
|
||||
- [ ] Timestamps use UTC
|
||||
- [ ] Revision histories in descending order
|
||||
- [ ] Metadata tables complete and accurate
|
||||
- [ ] Semantic versioning followed
|
||||
|
||||
### 9. Release Preparation ✅
|
||||
- [ ] Release notes drafted
|
||||
- [ ] Breaking changes documented
|
||||
- [ ] Migration guide prepared (if needed)
|
||||
- [ ] Deployment notes documented
|
||||
- [ ] Rollback plan prepared
|
||||
|
||||
### 10. Final Verification ✅
|
||||
- [ ] All PRs to this branch reviewed and merged
|
||||
- [ ] No pending issues blocking release
|
||||
- [ ] Stakeholders notified of upcoming merge
|
||||
- [ ] Final PR to main created and ready for review
|
||||
- [ ] All checklist items above completed
|
||||
|
||||
---
|
||||
|
||||
*This issue was automatically created by the auto-create-dev-branch 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);
|
||||
|
||||
await github.rest.issues.create({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
title: `Development Branch: ${branchName}`,
|
||||
body: issueBody,
|
||||
labels: ['automation', 'version-management', 'dev-branch'],
|
||||
assignees: validAssignees
|
||||
});
|
||||
|
||||
- name: Comment on merged PR
|
||||
if: steps.check_branch.outputs.exists == 'false'
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const branchName = '${{ steps.check_branch.outputs.branch_name }}';
|
||||
const nextVersion = '${{ steps.next_version.outputs.version }}';
|
||||
|
||||
const comment = `## ✅ Development Branch Created
|
||||
|
||||
A new development branch has been automatically created:
|
||||
- **Branch**: \`${branchName}\`
|
||||
- **Version**: ${nextVersion}
|
||||
|
||||
You can checkout this branch with:
|
||||
\`\`\`bash
|
||||
git fetch origin
|
||||
git checkout ${branchName}
|
||||
\`\`\`
|
||||
|
||||
This branch is ready for the next development cycle.`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: comment
|
||||
});
|
||||
|
||||
- name: Branch already exists notification
|
||||
if: steps.check_branch.outputs.exists == 'true'
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const branchName = '${{ steps.check_branch.outputs.branch_name }}';
|
||||
|
||||
const comment = `## ℹ️ Development Branch Already Exists
|
||||
|
||||
The development branch \`${branchName}\` already exists.
|
||||
|
||||
No new branch was created. Continue development on the existing branch.`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: comment
|
||||
});
|
||||
|
||||
- name: Workflow summary
|
||||
run: |
|
||||
set -x
|
||||
echo "## Workflow Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Current Version**: ${{ steps.version.outputs.current }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Next Version**: ${{ steps.next_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Branch Name**: \`${{ steps.check_branch.outputs.branch_name }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "${{ steps.check_branch.outputs.exists }}" == "false" ]; then
|
||||
echo "- **Status**: ✅ Branch created successfully" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- **Status**: ℹ️ Branch already exists" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -1,123 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# This file is part of a Moko Consulting project.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/workflows/codeql-analysis.yml
|
||||
# VERSION: 04.00.03
|
||||
# BRIEF: CodeQL security scanning workflow for PHP codebase
|
||||
# NOTE: Repository is PHP-only (v04.00.03). Python was removed Feb 12, 2026.
|
||||
|
||||
name: "CodeQL Security Scanning"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev/**
|
||||
- rc/**
|
||||
- version/**
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev/**
|
||||
- rc/**
|
||||
schedule:
|
||||
# Run weekly on Monday at 6:00 AM UTC
|
||||
- cron: '0 6 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Configuration Security Scan
|
||||
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
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
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
|
||||
queries: security-extended,security-and-quality
|
||||
|
||||
# Skip autobuild - no code compilation needed for config scanning
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "/language:config"
|
||||
upload: true
|
||||
output: sarif-results
|
||||
wait-for-processing: true
|
||||
|
||||
- name: Upload SARIF results (optional)
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4.5.0
|
||||
with:
|
||||
name: codeql-results-config
|
||||
path: sarif-results
|
||||
retention-days: 30
|
||||
|
||||
- name: Check for Critical/High Findings
|
||||
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.03)." >> $GITHUB_STEP_SUMMARY
|
||||
echo "This scan analyzes workflow files, JSON configs, YAML, and shell scripts." >> $GITHUB_STEP_SUMMARY
|
||||
echo "For PHP-specific security: Use PHP SecurityValidator enterprise library." >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
URL="https://github.com/${{ github.repository }}/security/code-scanning"
|
||||
echo "Check the [Security tab]($URL) for detailed 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
|
||||
|
||||
summary:
|
||||
name: Security Scan Summary
|
||||
runs-on: ubuntu-latest
|
||||
needs: analyze
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Generate 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 "" >> $GITHUB_STEP_SUMMARY
|
||||
SECURITY_URL="https://github.com/${{ github.repository }}/security"
|
||||
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
|
||||
@@ -1,375 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoStandards.Validation
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/workflows/comprehensive-validation.yml
|
||||
# VERSION: 04.00.03
|
||||
# BRIEF: Comprehensive validation for all scripts, workflows, and templates
|
||||
# NOTE: PHP-only repository (v04.00.03). Python validation removed Feb 2026.
|
||||
|
||||
name: Comprehensive File Validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'scripts/**'
|
||||
- '.github/workflows/**'
|
||||
- 'templates/**'
|
||||
- 'docs/templates/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.github/pull_request_template.md'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'scripts/**'
|
||||
- '.github/workflows/**'
|
||||
- 'templates/**'
|
||||
schedule:
|
||||
# Run weekly on Sundays at 3 AM UTC
|
||||
- cron: '0 3 * * 0'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
file_type:
|
||||
description: 'Type of files to validate'
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- all
|
||||
- php
|
||||
- shell
|
||||
- powershell
|
||||
- workflow
|
||||
- template
|
||||
default: 'all'
|
||||
strict:
|
||||
description: 'Strict mode (fail on any issues)'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
validate-php:
|
||||
name: Validate PHP Scripts
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.inputs.file_type == 'all' || github.event.inputs.file_type == 'php' || github.event.inputs.file_type == ''
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0
|
||||
with:
|
||||
php-version: '8.1'
|
||||
extensions: mbstring, curl, json
|
||||
|
||||
- name: Validate PHP Scripts
|
||||
id: validate_php
|
||||
run: |
|
||||
echo "## 🐘 PHP Script Validation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Find all PHP files
|
||||
PHP_FILES=$(find scripts/ src/ public/ -name "*.php" -type f 2>/dev/null | wc -l)
|
||||
echo "Found $PHP_FILES PHP files" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Validate syntax
|
||||
EXIT_CODE=0
|
||||
while IFS= read -r file; do
|
||||
if ! php -l "$file" > /dev/null 2>&1; then
|
||||
echo "❌ Syntax error in $file" >> $GITHUB_STEP_SUMMARY
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done < <(find scripts/ src/ public/ -name "*.php" -type f 2>/dev/null)
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo "✅ All PHP files have valid syntax" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "❌ Some PHP files have syntax errors" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check Results
|
||||
if: steps.validate_php.outputs.exit_code != '0'
|
||||
run: |
|
||||
echo "❌ PHP validation failed"
|
||||
exit 1
|
||||
|
||||
validate-shell:
|
||||
name: Validate Shell Scripts
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.inputs.file_type == 'all' || github.event.inputs.file_type == 'shell' || github.event.inputs.file_type == ''
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install shellcheck
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y shellcheck
|
||||
|
||||
- name: Validate Shell Scripts
|
||||
id: validate_shell
|
||||
run: |
|
||||
echo "## 🐚 Shell Script Validation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Find all shell scripts
|
||||
SHELL_FILES=$(find scripts/ templates/ -name "*.sh" -type f 2>/dev/null | wc -l)
|
||||
echo "Found $SHELL_FILES shell scripts" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Validate with shellcheck
|
||||
EXIT_CODE=0
|
||||
while IFS= read -r file; do
|
||||
if ! shellcheck "$file" 2>&1 | tee -a /tmp/shell-validation.log; then
|
||||
echo "⚠️ Issues found in $file" >> $GITHUB_STEP_SUMMARY
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done < <(find scripts/ templates/ -name "*.sh" -type f 2>/dev/null)
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo "✅ All shell scripts passed shellcheck" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "⚠️ Some shell scripts have issues" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
cat /tmp/shell-validation.log >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Shell Validation Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v6.0.0
|
||||
with:
|
||||
name: shell-validation-report
|
||||
path: /tmp/shell-validation.log
|
||||
retention-days: 30
|
||||
|
||||
- name: Check Results
|
||||
if: steps.validate_shell.outputs.exit_code != '0'
|
||||
run: |
|
||||
echo "❌ Shell validation failed"
|
||||
exit 1
|
||||
|
||||
validate-workflows:
|
||||
name: Validate GitHub Workflows
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.inputs.file_type == 'all' || github.event.inputs.file_type == 'workflow' || github.event.inputs.file_type == ''
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Validate Workflows
|
||||
id: validate_workflows
|
||||
run: |
|
||||
echo "## ⚙️ Workflow Validation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Find all workflow files
|
||||
WORKFLOW_FILES=$(find .github/workflows/ -name "*.yml" -type f 2>/dev/null | wc -l)
|
||||
echo "Found $WORKFLOW_FILES workflow files" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Validate YAML syntax
|
||||
EXIT_CODE=0
|
||||
while IFS= read -r file; do
|
||||
if ! yamllint -d '{extends: default, rules: {line-length: disable}}' "$file" 2>&1 | tee -a /tmp/workflow-validation.log; then
|
||||
echo "⚠️ Issues found in $file" >> $GITHUB_STEP_SUMMARY
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done < <(find .github/workflows/ -name "*.yml" -type f 2>/dev/null)
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo "✅ All workflows have valid YAML syntax" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "⚠️ Some workflows have YAML issues" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
cat /tmp/workflow-validation.log >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Workflow Validation Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v6.0.0
|
||||
with:
|
||||
name: workflow-validation-report
|
||||
path: /tmp/workflow-validation.log
|
||||
retention-days: 30
|
||||
|
||||
- name: Check Results
|
||||
if: steps.validate_workflows.outputs.exit_code != '0'
|
||||
run: |
|
||||
echo "❌ Workflow validation failed"
|
||||
exit 1
|
||||
|
||||
validate-templates:
|
||||
name: Validate Templates
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.inputs.file_type == 'all' || github.event.inputs.file_type == 'template' || github.event.inputs.file_type == ''
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Validate Templates
|
||||
id: validate_templates
|
||||
run: |
|
||||
echo "## 📝 Template Validation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Find all template files
|
||||
TEMPLATE_FILES=$(find templates/ docs/templates/ .github/ISSUE_TEMPLATE/ -type f 2>/dev/null | wc -l)
|
||||
echo "Found $TEMPLATE_FILES template files" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Basic validation - check if files exist and are readable
|
||||
EXIT_CODE=0
|
||||
while IFS= read -r file; do
|
||||
if [ ! -r "$file" ]; then
|
||||
echo "❌ Cannot read $file" >> $GITHUB_STEP_SUMMARY
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done < <(find templates/ docs/templates/ .github/ISSUE_TEMPLATE/ -type f 2>/dev/null)
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo "✅ All template files are readable" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "❌ Some template files have issues" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check Results
|
||||
if: steps.validate_templates.outputs.exit_code != '0'
|
||||
run: |
|
||||
echo "❌ Template validation failed"
|
||||
exit 1
|
||||
|
||||
validate-yaml-tabs:
|
||||
name: Validate YAML Files (No Tabs)
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.inputs.file_type == 'all' || github.event.inputs.file_type == 'workflow' || github.event.inputs.file_type == 'template' || github.event.inputs.file_type == ''
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check YAML Files for Tabs
|
||||
id: validate_yaml_tabs
|
||||
run: |
|
||||
echo "## 📝 YAML Tab Validation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Checking for TAB characters in YAML files (forbidden by YAML spec)..." >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Find YAML files with tabs
|
||||
EXIT_CODE=0
|
||||
FILES_WITH_TABS=$(find . -name "*.yml" -o -name "*.yaml" | xargs grep -l $'\t' 2>/dev/null || true)
|
||||
|
||||
if [ -z "$FILES_WITH_TABS" ]; then
|
||||
echo "exit_code=0" >> $GITHUB_OUTPUT
|
||||
echo "✅ No tabs found in YAML files" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "exit_code=1" >> $GITHUB_OUTPUT
|
||||
echo "❌ Tabs found in YAML files" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**YAML Specification:** Tab characters are forbidden in YAML files" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Files with tabs:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "$FILES_WITH_TABS" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
- name: Check Results
|
||||
if: steps.validate_yaml_tabs.outputs.exit_code != '0'
|
||||
run: |
|
||||
echo "❌ YAML tab validation failed - tabs found in YAML files"
|
||||
echo "Fix: Use 'sed -i 's/\t/ /g' filename.yml' to convert tabs to spaces"
|
||||
exit 1
|
||||
|
||||
validation-summary:
|
||||
name: Validation Summary
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-php, validate-shell, validate-workflows, validate-templates, validate-yaml-tabs]
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Generate Summary
|
||||
run: |
|
||||
echo "# 🔍 Comprehensive Validation Results" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
PHP="${{ needs.validate-php.result }}"
|
||||
SHELL="${{ needs.validate-shell.result }}"
|
||||
WORKFLOWS="${{ needs.validate-workflows.result }}"
|
||||
TEMPLATES="${{ needs.validate-templates.result }}"
|
||||
YAML_TABS="${{ needs.validate-yaml-tabs.result }}"
|
||||
|
||||
if [ "$PHP" = "success" ] || [ "$PHP" = "skipped" ]; then
|
||||
echo "- ✅ **PHP Scripts**: PASSED" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- ❌ **PHP Scripts**: FAILED" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ "$SHELL" = "success" ] || [ "$SHELL" = "skipped" ]; then
|
||||
echo "- ✅ **Shell Scripts**: PASSED" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- ❌ **Shell Scripts**: FAILED" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ "$WORKFLOWS" = "success" ] || [ "$WORKFLOWS" = "skipped" ]; then
|
||||
echo "- ✅ **Workflows**: PASSED" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- ❌ **Workflows**: FAILED" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ "$TEMPLATES" = "success" ] || [ "$TEMPLATES" = "skipped" ]; then
|
||||
echo "- ✅ **Templates**: PASSED" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- ❌ **Templates**: FAILED" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ "$YAML_TABS" = "success" ] || [ "$YAML_TABS" = "skipped" ]; then
|
||||
echo "- ✅ **YAML Tab Check**: PASSED" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- ❌ **YAML Tab Check**: FAILED (tabs found)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Repository:** MokoStandards v04.00.03 (PHP-only)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Workflow:** Comprehensive File Validation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Fail if Critical Issues
|
||||
if: |
|
||||
needs.validate-php.result == 'failure' ||
|
||||
needs.validate-shell.result == 'failure' ||
|
||||
needs.validate-workflows.result == 'failure' ||
|
||||
needs.validate-templates.result == 'failure'
|
||||
run: |
|
||||
echo "❌ Critical validation issues detected - workflow failed"
|
||||
exit 1
|
||||
@@ -1,275 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# This file is part of a Moko Consulting project.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.WorkflowTemplate
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/workflows/dependency-review.yml
|
||||
# VERSION: 04.00.03
|
||||
# BRIEF: Dependency review workflow for vulnerability scanning in pull requests
|
||||
# NOTE: Scans dependencies for security vulnerabilities and license compliance
|
||||
|
||||
name: Dependency Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev/**
|
||||
- rc/**
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
name: Dependency Security Review
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Dependency Review
|
||||
uses: actions/dependency-review-action@v4
|
||||
with:
|
||||
# Fail on critical or high severity vulnerabilities
|
||||
fail-on-severity: moderate
|
||||
|
||||
# Allow specific licenses (customize for your project)
|
||||
# Common open-source licenses
|
||||
allow-licenses: GPL-3.0, GPL-3.0-or-later, MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, LGPL-3.0
|
||||
|
||||
# Deny specific licenses (customize as needed)
|
||||
# deny-licenses: AGPL-3.0, GPL-2.0
|
||||
|
||||
# Comment on PR with results
|
||||
comment-summary-in-pr: always
|
||||
|
||||
- name: Generate Dependency Report
|
||||
if: always()
|
||||
run: |
|
||||
echo "# Dependency Review Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "✅ Dependency review completed" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "This workflow checks:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Security vulnerabilities in new dependencies" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- License compatibility" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Dependency changes between base and head" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
npm-audit:
|
||||
name: npm Audit
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
if: ${{ hashFiles('package.json') != '' }}
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Run npm Audit
|
||||
if: ${{ hashFiles('package.json') != '' }}
|
||||
run: |
|
||||
echo "### npm Audit Results" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Run audit and capture results
|
||||
if npm audit --audit-level=moderate; then
|
||||
echo "✅ No moderate or higher severity vulnerabilities found" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "⚠️ Moderate or higher severity vulnerabilities detected - please review" >> $GITHUB_STEP_SUMMARY
|
||||
npm audit --audit-level=moderate || true
|
||||
fi
|
||||
|
||||
- name: Check for Outdated Packages
|
||||
if: ${{ hashFiles('package.json') != '' }}
|
||||
run: |
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Outdated Packages" >> $GITHUB_STEP_SUMMARY
|
||||
npm outdated || echo "All packages are up to date" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
composer-audit:
|
||||
name: Composer Audit
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup PHP
|
||||
if: ${{ hashFiles('composer.json') != '' }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.1'
|
||||
tools: composer:v2
|
||||
|
||||
- name: Install Dependencies
|
||||
if: ${{ hashFiles('composer.json') != '' }}
|
||||
run: composer install --no-interaction --prefer-dist
|
||||
|
||||
- name: Run Composer Audit
|
||||
if: ${{ hashFiles('composer.json') != '' }}
|
||||
run: |
|
||||
echo "### Composer Audit Results" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Run audit and capture results
|
||||
if composer audit; then
|
||||
echo "✅ No vulnerabilities found in Composer dependencies" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "⚠️ Vulnerabilities detected - please review" >> $GITHUB_STEP_SUMMARY
|
||||
composer audit || true
|
||||
fi
|
||||
|
||||
- name: Check for Outdated Packages
|
||||
if: ${{ hashFiles('composer.json') != '' }}
|
||||
run: |
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Outdated Composer Packages" >> $GITHUB_STEP_SUMMARY
|
||||
composer outdated --direct || echo "All packages are up to date" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
python-safety:
|
||||
name: Python Safety Check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
if: ${{ hashFiles('requirements.txt', 'pyproject.toml', 'Pipfile') != '' }}
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install Safety
|
||||
if: ${{ hashFiles('requirements.txt', 'pyproject.toml', 'Pipfile') != '' }}
|
||||
run: pip install safety
|
||||
|
||||
- name: Run Safety Check
|
||||
if: ${{ hashFiles('requirements.txt', 'pyproject.toml', 'Pipfile') != '' }}
|
||||
run: |
|
||||
echo "### Python Safety Check Results" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check requirements.txt if exists
|
||||
if [ -f "requirements.txt" ]; then
|
||||
if safety check -r requirements.txt; then
|
||||
echo "✅ No known vulnerabilities in Python dependencies" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "⚠️ Vulnerabilities detected in Python dependencies" >> $GITHUB_STEP_SUMMARY
|
||||
safety check -r requirements.txt || true
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ No requirements.txt found" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
license-check:
|
||||
name: License Compliance Check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check License File
|
||||
run: |
|
||||
echo "### License Compliance" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ -f "LICENSE" ] || [ -f "LICENSE.md" ] || [ -f "LICENSE.txt" ]; then
|
||||
echo "✅ LICENSE file present" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check for GPL-3.0 (MokoStandards default)
|
||||
if grep -qi "GNU GENERAL PUBLIC LICENSE" LICENSE* 2>/dev/null; then
|
||||
echo "✅ GPL-3.0 or compatible license detected" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "ℹ️ Non-GPL license detected - verify compatibility" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
else
|
||||
echo "❌ LICENSE file missing" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Please add a LICENSE file to the repository root" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check SPDX Headers (Optional)
|
||||
run: |
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### SPDX Header Compliance" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check for SPDX identifiers in source files
|
||||
MISSING_HEADERS=0
|
||||
|
||||
# Check PHP files
|
||||
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=$(find . -name "*.php" -type f ! -path "./vendor/*" -exec grep -l "SPDX-License-Identifier" {} \; | wc -l)
|
||||
echo "- PHP files: $WITH_SPDX/$TOTAL_PHP with SPDX headers" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Check JavaScript files
|
||||
if find . -name "*.js" -type f ! -path "./node_modules/*" ! -path "./vendor/*" | head -1 | grep -q .; then
|
||||
TOTAL_JS=$(find . -name "*.js" -type f ! -path "./node_modules/*" ! -path "./vendor/*" | wc -l)
|
||||
WITH_SPDX_JS=$(find . -name "*.js" -type f ! -path "./node_modules/*" ! -path "./vendor/*" -exec grep -l "SPDX-License-Identifier" {} \; | wc -l)
|
||||
echo "- JavaScript files: $WITH_SPDX_JS/$TOTAL_JS with SPDX headers" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "ℹ️ SPDX headers are recommended but not required for this check" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
summary:
|
||||
name: Review Summary
|
||||
runs-on: ubuntu-latest
|
||||
needs: [dependency-review, npm-audit, composer-audit, python-safety, license-check]
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Generate Final Summary
|
||||
run: |
|
||||
echo "# Dependency Review Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "All dependency security and license checks have been executed." >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Checks Performed:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- ✅ GitHub Dependency Review" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- ✅ Package Manager Audits (npm, composer, pip)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- ✅ License Compliance" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Review the job results above for any issues that need attention." >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# CUSTOMIZATION NOTES:
|
||||
#
|
||||
# 1. Adjust severity thresholds:
|
||||
# Change fail-on-severity to: low, moderate, high, critical
|
||||
#
|
||||
# 2. Modify allowed licenses:
|
||||
# Update allow-licenses list based on your project requirements
|
||||
#
|
||||
# 3. Add custom dependency checks:
|
||||
# - Snyk integration
|
||||
# - WhiteSource/Mend scanning
|
||||
# - Custom license scanners
|
||||
#
|
||||
# 4. Configure notification:
|
||||
# Add Slack/email notifications for critical findings
|
||||
#
|
||||
# 5. Integrate with existing tools:
|
||||
# Add steps for your organization's security tools
|
||||
@@ -1,310 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoStandards.SecurityScan
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/workflows/security-scan.yml
|
||||
# VERSION: 04.00.03
|
||||
# BRIEF: Daily security scanning and report generation
|
||||
# NOTE: Enhanced security scanning using PHP SecurityValidator
|
||||
|
||||
name: Security Scan
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run daily at 02:00 UTC
|
||||
- cron: '0 2 * * *'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'scripts/**'
|
||||
- '.github/workflows/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
scan_type:
|
||||
description: 'Type of security scan'
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- all
|
||||
- credentials
|
||||
- vulnerabilities
|
||||
- best-practices
|
||||
default: 'all'
|
||||
strict_mode:
|
||||
description: 'Fail on any security issues'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
security-scan:
|
||||
name: Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0
|
||||
with:
|
||||
php-version: '8.1'
|
||||
extensions: mbstring, curl, json
|
||||
tools: composer
|
||||
|
||||
- name: Install Composer Dependencies
|
||||
run: composer install --no-dev --optimize-autoloader
|
||||
|
||||
- name: Create Reports Directory
|
||||
run: |
|
||||
mkdir -p logs/security
|
||||
mkdir -p logs/reports
|
||||
|
||||
- name: Scan for Credentials
|
||||
id: credentials
|
||||
if: github.event.inputs.scan_type == 'all' || github.event.inputs.scan_type == 'credentials' || github.event.inputs.scan_type == ''
|
||||
run: |
|
||||
echo "## 🔐 Credential Scan" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
php << 'EOF'
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use MokoStandards\Enterprise\SecurityValidator;
|
||||
|
||||
try {
|
||||
$validator = new SecurityValidator();
|
||||
$allFindings = [];
|
||||
|
||||
// Scan PHP files
|
||||
$phpFiles = new RecursiveIteratorIterator(
|
||||
new RecursiveCallbackFilterIterator(
|
||||
new RecursiveDirectoryIterator('src', RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
function ($file, $key, $iterator) {
|
||||
return $file->isDir() || $file->getExtension() === 'php';
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
$phpFileCount = 0;
|
||||
foreach ($phpFiles as $file) {
|
||||
if ($file->isFile()) {
|
||||
$phpFileCount++;
|
||||
$findings = $validator->scanFile($file->getPathname(), true, false);
|
||||
if (!empty($findings)) {
|
||||
$allFindings = array_merge($allFindings, $findings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan workflow files
|
||||
$ymlFiles = glob('.github/workflows/*.yml');
|
||||
foreach ($ymlFiles as $filePath) {
|
||||
$findings = $validator->scanFile($filePath, true, false);
|
||||
if (!empty($findings)) {
|
||||
$allFindings = array_merge($allFindings, $findings);
|
||||
}
|
||||
}
|
||||
|
||||
$totalFiles = $phpFileCount + count($ymlFiles);
|
||||
echo "Scanned {$phpFileCount} PHP files and " . count($ymlFiles) . " workflow files\n";
|
||||
echo "Found " . count($allFindings) . " potential credential issues\n";
|
||||
|
||||
if (!empty($allFindings)) {
|
||||
echo "\n⚠️ Potential credential issues found:\n";
|
||||
foreach (array_slice($allFindings, 0, 10) as $finding) {
|
||||
echo " - {$finding['file']}: {$finding['issue']}\n";
|
||||
}
|
||||
} else {
|
||||
echo "✅ No credential issues found\n";
|
||||
}
|
||||
|
||||
// Save findings
|
||||
file_put_contents('logs/security/credentials-scan.json', json_encode($allFindings, JSON_PRETTY_PRINT));
|
||||
|
||||
$summary = [
|
||||
'files_scanned' => $totalFiles,
|
||||
'issues_found' => count($allFindings)
|
||||
];
|
||||
file_put_contents('/tmp/credential_summary.json', json_encode($summary));
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Credential scan failed: {$e->getMessage()}\n";
|
||||
exit(1);
|
||||
}
|
||||
EOF
|
||||
|
||||
if [ -f "/tmp/credential_summary.json" ]; then
|
||||
SUMMARY=$(cat /tmp/credential_summary.json)
|
||||
FILES=$(echo $SUMMARY | php -r 'echo json_decode(file_get_contents("php://stdin"), true)["files_scanned"];')
|
||||
ISSUES=$(echo $SUMMARY | php -r 'echo json_decode(file_get_contents("php://stdin"), true)["issues_found"];')
|
||||
|
||||
echo "files_scanned=$FILES" >> $GITHUB_OUTPUT
|
||||
echo "issues_found=$ISSUES" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "- Files scanned: **${FILES}**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Issues found: **${ISSUES}**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Vulnerability Scan
|
||||
id: vulnerabilities
|
||||
if: github.event.inputs.scan_type == 'all' || github.event.inputs.scan_type == 'vulnerabilities' || github.event.inputs.scan_type == ''
|
||||
run: |
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## 🛡️ Vulnerability Scan" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
php << 'EOF'
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use MokoStandards\Enterprise\SecurityValidator;
|
||||
|
||||
try {
|
||||
$validator = new SecurityValidator();
|
||||
$vulnerabilities = [];
|
||||
|
||||
// Scan for dangerous functions
|
||||
$phpFiles = new RecursiveIteratorIterator(
|
||||
new RecursiveCallbackFilterIterator(
|
||||
new RecursiveDirectoryIterator('src', RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
function ($file, $key, $iterator) {
|
||||
return $file->isDir() || $file->getExtension() === 'php';
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
$phpFileCount = 0;
|
||||
foreach ($phpFiles as $file) {
|
||||
if ($file->isFile()) {
|
||||
$phpFileCount++;
|
||||
$findings = $validator->scanFile($file->getPathname(), false, true);
|
||||
if (!empty($findings)) {
|
||||
$vulnerabilities = array_merge($vulnerabilities, $findings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Scanned {$phpFileCount} PHP files\n";
|
||||
echo "Found " . count($vulnerabilities) . " potential vulnerabilities\n";
|
||||
|
||||
if (!empty($vulnerabilities)) {
|
||||
echo "\n⚠️ Potential vulnerabilities found:\n";
|
||||
foreach (array_slice($vulnerabilities, 0, 10) as $vuln) {
|
||||
echo " - {$vuln['file']}: {$vuln['issue']}\n";
|
||||
}
|
||||
} else {
|
||||
echo "✅ No vulnerabilities found\n";
|
||||
}
|
||||
|
||||
// Save findings
|
||||
file_put_contents('logs/security/vulnerabilities-scan.json', json_encode($vulnerabilities, JSON_PRETTY_PRINT));
|
||||
|
||||
$summary = ['vulnerabilities_found' => count($vulnerabilities)];
|
||||
file_put_contents('/tmp/vuln_summary.json', json_encode($summary));
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Vulnerability scan failed: {$e->getMessage()}\n";
|
||||
exit(1);
|
||||
}
|
||||
EOF
|
||||
|
||||
if [ -f "/tmp/vuln_summary.json" ]; then
|
||||
SUMMARY=$(cat /tmp/vuln_summary.json)
|
||||
VULNS=$(echo $SUMMARY | php -r 'echo json_decode(file_get_contents("php://stdin"), true)["vulnerabilities_found"];')
|
||||
|
||||
echo "vulnerabilities_found=$VULNS" >> $GITHUB_OUTPUT
|
||||
echo "- Vulnerabilities found: **${VULNS}**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Generate Security Report
|
||||
run: |
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## 📋 Security Report" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
php << 'EOF'
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
try {
|
||||
$report = [
|
||||
'scan_date' => date('c'),
|
||||
'scan_type' => '${{ github.event.inputs.scan_type }}' ?: 'all',
|
||||
'repository' => 'MokoStandards',
|
||||
'results' => []
|
||||
];
|
||||
|
||||
// Load credential scan results
|
||||
$credFile = 'logs/security/credentials-scan.json';
|
||||
if (file_exists($credFile)) {
|
||||
$report['results']['credentials'] = json_decode(file_get_contents($credFile), true);
|
||||
}
|
||||
|
||||
// Load vulnerability scan results
|
||||
$vulnFile = 'logs/security/vulnerabilities-scan.json';
|
||||
if (file_exists($vulnFile)) {
|
||||
$report['results']['vulnerabilities'] = json_decode(file_get_contents($vulnFile), true);
|
||||
}
|
||||
|
||||
// Calculate summary
|
||||
$totalIssues = 0;
|
||||
foreach ($report['results'] as $issues) {
|
||||
if (is_array($issues)) {
|
||||
$totalIssues += count($issues);
|
||||
}
|
||||
}
|
||||
|
||||
$report['summary'] = [
|
||||
'total_issues' => $totalIssues,
|
||||
'credential_issues' => count($report['results']['credentials'] ?? []),
|
||||
'vulnerabilities' => count($report['results']['vulnerabilities'] ?? [])
|
||||
];
|
||||
|
||||
// Save report
|
||||
file_put_contents('logs/reports/security-report.json', json_encode($report, JSON_PRETTY_PRINT));
|
||||
|
||||
echo "✅ Security report generated\n";
|
||||
echo "Total issues: {$totalIssues}\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "⚠️ Report generation failed: {$e->getMessage()}\n";
|
||||
}
|
||||
EOF
|
||||
|
||||
if [ -f "logs/reports/security-report.json" ]; then
|
||||
echo "✅ Security report generated" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Upload Security Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v6.0.0
|
||||
with:
|
||||
name: security-report-${{ github.run_number }}
|
||||
path: |
|
||||
logs/security/
|
||||
logs/reports/security-report.json
|
||||
retention-days: 90
|
||||
|
||||
- name: Check Strict Mode
|
||||
if: github.event.inputs.strict_mode == 'true'
|
||||
run: |
|
||||
ISSUES=$(cat logs/reports/security-report.json | php -r 'echo json_decode(file_get_contents("php://stdin"), true)["summary"]["total_issues"];')
|
||||
if [ "$ISSUES" -gt "0" ]; then
|
||||
echo "❌ Security issues found in strict mode" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Notify on Failure
|
||||
if: failure()
|
||||
run: |
|
||||
echo "❌ Security scan failed or found critical issues" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Please review the security report" >> $GITHUB_STEP_SUMMARY
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,222 +0,0 @@
|
||||
# MokoJoomTOS Component - Implementation Summary
|
||||
|
||||
## Overview
|
||||
|
||||
A complete Joomla 4.x/5.x component for managing Terms of Service and Privacy Policy content with offline mode access capability has been successfully implemented in the `src/` directory.
|
||||
|
||||
## Component Structure
|
||||
|
||||
### 1. Core Files
|
||||
- **com_mokojoomtos.xml**: Component manifest with SQL installation scripts
|
||||
- **script.php**: Installation script that sets up the component and enables the offline access plugin
|
||||
|
||||
### 2. Administrator (Backend)
|
||||
|
||||
#### Controllers (`admin/src/Controller/`)
|
||||
- `DisplayController.php`: Default controller (shows terms view)
|
||||
- `TermsController.php`: Handles Terms of Service editing
|
||||
- `PrivacyController.php`: Handles Privacy Policy editing
|
||||
|
||||
#### Models (`admin/src/Model/`)
|
||||
- `TermsModel.php`: Manages Terms of Service data (CRUD operations)
|
||||
- `PrivacyModel.php`: Manages Privacy Policy data (CRUD operations)
|
||||
|
||||
#### Views (`admin/src/View/`)
|
||||
- `Terms/HtmlView.php`: Terms of Service view with toolbar
|
||||
- `Privacy/HtmlView.php`: Privacy Policy view with toolbar
|
||||
|
||||
#### Templates (`admin/tmpl/`)
|
||||
- `terms/default.php`: Overview page showing current terms status
|
||||
- `terms/edit.php`: Edit form for Terms of Service
|
||||
- `privacy/default.php`: Overview page showing current privacy policy status
|
||||
- `privacy/edit.php`: Edit form for Privacy Policy
|
||||
|
||||
#### Forms (`admin/forms/`)
|
||||
- `terms.xml`: Form definition for Terms of Service
|
||||
- `privacy.xml`: Form definition for Privacy Policy
|
||||
|
||||
#### Database (`admin/sql/`)
|
||||
- `install/mysql.sql`: Creates `#__mokojoomtos_content` table with default entries
|
||||
- `uninstall/mysql.sql`: Removes the table on uninstall
|
||||
|
||||
#### Service Provider (`admin/services/`)
|
||||
- `provider.php`: Dependency injection configuration
|
||||
|
||||
#### Extension Class (`admin/src/Extension/`)
|
||||
- `MokoJoomTOSComponent.php`: Main component class implementing RouterServiceInterface
|
||||
|
||||
### 3. Site (Frontend)
|
||||
|
||||
#### Controllers (`site/src/Controller/`)
|
||||
- `DisplayController.php`: Handles view display (with offline access flag)
|
||||
|
||||
#### Models (`site/src/Model/`)
|
||||
- `TermsModel.php`: Fetches published Terms of Service content
|
||||
- `PrivacyModel.php`: Fetches published Privacy Policy content
|
||||
|
||||
#### Views (`site/src/View/`)
|
||||
- `Terms/HtmlView.php`: Renders Terms of Service page
|
||||
- `Privacy/HtmlView.php`: Renders Privacy Policy page
|
||||
|
||||
#### Templates (`site/tmpl/`)
|
||||
- `terms/default.php`: Template for Terms of Service display
|
||||
- `terms/default.xml`: Menu item type definition for Terms
|
||||
- `privacy/default.php`: Template for Privacy Policy display
|
||||
- `privacy/default.xml`: Menu item type definition for Privacy
|
||||
|
||||
#### Router (`site/src/Service/`)
|
||||
- `Router.php`: SEF URL routing configuration
|
||||
|
||||
### 4. Media Assets
|
||||
|
||||
#### CSS (`media/css/`)
|
||||
- `mokojoomtos.css`: Stylesheet for frontend display with responsive design
|
||||
|
||||
### 5. Offline Access Plugin (`plugins/system/mokojoomtosaccess/`)
|
||||
- `mokojoomtosaccess.php`: System plugin that bypasses offline check for com_mokojoomtos
|
||||
- `mokojoomtosaccess.xml`: Plugin manifest
|
||||
- Language files for plugin strings
|
||||
|
||||
## Key Features
|
||||
|
||||
### 1. **Dual Content Management**
|
||||
- Separate editors for Terms of Service and Privacy Policy
|
||||
- Rich text editor support with filtering
|
||||
- Published/Unpublished status
|
||||
- Version tracking (created/modified dates and users)
|
||||
|
||||
### 2. **Offline Mode Access**
|
||||
The component includes a system plugin that automatically:
|
||||
- Detects when users access `option=com_mokojoomtos`
|
||||
- Temporarily sets the site as "online" for that request only
|
||||
- Allows legal documents to be accessible during site maintenance
|
||||
- Auto-enabled during component installation
|
||||
|
||||
### 3. **Modern Joomla Architecture**
|
||||
- Namespaced classes following PSR-4 standards
|
||||
- MVC architecture with proper separation of concerns
|
||||
- Service provider for dependency injection
|
||||
- RouterView implementation for SEF URLs
|
||||
- Joomla 4.x/5.x compatibility
|
||||
|
||||
### 4. **Menu Integration**
|
||||
- XML metadata for menu item types
|
||||
- Two menu item types available:
|
||||
- Terms of Service
|
||||
- Privacy Policy
|
||||
- Full integration with Joomla menu system
|
||||
|
||||
### 5. **Database Design**
|
||||
Single table design (`#__mokojoomtos_content`):
|
||||
- Stores both terms and privacy content
|
||||
- Differentiated by `content_type` field ('terms' or 'privacy')
|
||||
- Includes version tracking fields
|
||||
- Supports multiple languages (language field)
|
||||
- Access level support
|
||||
|
||||
## Installation Flow
|
||||
|
||||
1. **Pre-flight Check**: Validates minimum Joomla (4.0) and PHP (7.4) versions
|
||||
2. **Database Setup**: Creates table and inserts default content
|
||||
3. **Plugin Activation**: Automatically enables the offline access plugin
|
||||
4. **Post-install Message**: Displays setup instructions to administrator
|
||||
|
||||
## Usage
|
||||
|
||||
### Backend
|
||||
1. Navigate to Components → MokoJoomTOS
|
||||
2. Select either Terms or Privacy from submenu
|
||||
3. Click Edit to modify content
|
||||
4. Save/Apply changes
|
||||
|
||||
### Frontend
|
||||
1. Create menu items pointing to Terms or Privacy views
|
||||
2. Direct URLs also work: `?option=com_mokojoomtos&view=terms`
|
||||
3. Content remains accessible even when site is offline
|
||||
|
||||
## Database Schema
|
||||
|
||||
```sql
|
||||
CREATE TABLE `#__mokojoomtos_content` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`content_type` varchar(50) NOT NULL DEFAULT 'terms',
|
||||
`title` varchar(255) NOT NULL DEFAULT '',
|
||||
`content` mediumtext NOT NULL,
|
||||
`published` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`access` int(11) UNSIGNED NOT NULL DEFAULT 1,
|
||||
`language` char(7) NOT NULL DEFAULT '*',
|
||||
`created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||
`created_by` int(11) NOT NULL DEFAULT 0,
|
||||
`modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||
`modified_by` int(11) NOT NULL DEFAULT 0,
|
||||
`checked_out` int(11) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`checked_out_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||
`params` text NOT NULL,
|
||||
`version` int(11) UNSIGNED NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_access` (`access`),
|
||||
KEY `idx_checkout` (`checked_out`),
|
||||
KEY `idx_state` (`published`),
|
||||
KEY `idx_language` (`language`),
|
||||
KEY `idx_content_type` (`content_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Makefile
|
||||
Updated `COMPONENT_NAME` to `mokojoomtos` for proper building.
|
||||
|
||||
### .gitignore
|
||||
Added exception `!src/site/` to allow the Joomla site directory while still ignoring generated site files.
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Joomla**: 4.0 or later
|
||||
- **PHP**: 7.4 or later
|
||||
- **MySQL**: 5.7+ or MariaDB 10.2+
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Content Filtering**: Editor fields use `JComponentHelper::filterText` filter
|
||||
2. **Access Control**: Prepared for Joomla ACL (access field in database)
|
||||
3. **SQL Injection**: All queries use prepared statements via Joomla Database API
|
||||
4. **XSS Prevention**: Output is properly escaped in templates
|
||||
5. **Offline Bypass**: Only affects com_mokojoomtos component, doesn't expose other content
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements for future versions:
|
||||
1. Version history for tracking content changes over time
|
||||
2. Multi-language support per content type
|
||||
3. Email notifications when content is updated
|
||||
4. Approval workflow for content changes
|
||||
5. Export to PDF functionality
|
||||
6. Acceptance tracking (log when users accept terms)
|
||||
7. Custom fields for additional legal requirements
|
||||
8. Integration with user registration/profile
|
||||
|
||||
## File Count Summary
|
||||
|
||||
- **PHP Files**: 18
|
||||
- **XML Files**: 8
|
||||
- **SQL Files**: 2
|
||||
- **INI Language Files**: 6
|
||||
- **CSS Files**: 1
|
||||
- **Total**: 35 files
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v3.0 or later (GPL-3.0-or-later)
|
||||
|
||||
## Author
|
||||
|
||||
Moko Consulting
|
||||
- Email: hello@mokoconsulting.tech
|
||||
- Website: https://mokoconsulting.tech
|
||||
|
||||
---
|
||||
|
||||
**Implementation Date**: 2026-02-23
|
||||
**Component Version**: 03.08.04
|
||||
**Status**: Complete and ready for testing
|
||||
@@ -1,234 +0,0 @@
|
||||
# MokoJoomTOS Implementation Summary
|
||||
|
||||
## Overview
|
||||
|
||||
**Project**: MokoJoomTOS Offline Access Plugin
|
||||
**Version**: 03.08.04
|
||||
**Date**: 2026-02-28
|
||||
**Status**: ✅ Complete and Production-Ready
|
||||
|
||||
## What Was Built
|
||||
|
||||
A lightweight Joomla system plugin that allows specific menu items (e.g., Terms of Service) to remain accessible when a Joomla site is in offline/maintenance mode.
|
||||
|
||||
## Architecture Evolution
|
||||
|
||||
### Initial Requirement
|
||||
> "add option to create default menu with Menu of 'Legal' with both Terms of Service and Privacy Policy, convert code to plugin and component with plugin controlling the offline and pulling from the component"
|
||||
|
||||
### Evolution Path
|
||||
|
||||
1. **Complex Component + Plugin** ❌
|
||||
- Full component with database tables
|
||||
- Package structure with multiple extensions
|
||||
- Menu creation in installation script
|
||||
- **Abandoned**: Too complex for the need
|
||||
|
||||
2. **Menu Item ID Configuration** ❌
|
||||
- Plugin with menu item ID configuration
|
||||
- Component-free approach
|
||||
- **Abandoned**: Menu IDs are not user-friendly
|
||||
|
||||
3. **Final: Slug-Based Approach** ✅
|
||||
- Single plugin only
|
||||
- Configure menu slug (e.g., "terms-of-service")
|
||||
- Uses native Joomla articles and menus
|
||||
- **Selected**: Simple, elegant, maintainable
|
||||
|
||||
## Final Solution
|
||||
|
||||
### Plugin: `plg_system_mokojoomtos`
|
||||
|
||||
**Architecture**:
|
||||
- Modern Joomla 4.x/5.x structure with namespaced Extension class
|
||||
- Implements `SubscriberInterface` for event handling
|
||||
- Namespace: `Joomla\Plugin\System\MokoJoomTOS\Extension`
|
||||
- Custom field types for enhanced configuration
|
||||
- Backward compatible legacy loader maintained
|
||||
|
||||
**Core Functionality**:
|
||||
- Hooks into `onAfterInitialise` event
|
||||
- Checks if site is offline
|
||||
- Compares requested URL against configured slug
|
||||
- Temporarily disables offline mode for matching requests
|
||||
- Sets component-only view (`tmpl=component`) for clean display
|
||||
- All other pages remain offline
|
||||
|
||||
**Configuration**:
|
||||
- Menu slug dropdown: Select from available published menu items
|
||||
- Shows menu title and alias in format: "Title (alias)"
|
||||
- Grouped by menu type with visual separators
|
||||
|
||||
**Technical Specs**:
|
||||
- PHP: 150+ lines of code (Extension + Field classes)
|
||||
- Size: 9.1 KB (zipped)
|
||||
- Files: 9 total (including src/Extension/ and src/Field/)
|
||||
- Events: 1 (`onAfterInitialise`)
|
||||
- Database: Query to fetch menu items for dropdown
|
||||
- Dependencies: None
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
src/plugins/system/mokojoomtos/
|
||||
├── mokojoomtos.php # Legacy plugin loader
|
||||
├── mokojoomtos.xml # Manifest with configuration
|
||||
├── script.php # Installation script
|
||||
├── src/
|
||||
│ ├── Extension/
|
||||
│ │ └── MokoJoomTOS.php # Modern namespaced plugin class (115 lines)
|
||||
│ └── Field/
|
||||
│ └── MenuslugField.php # Custom dropdown field (95 lines)
|
||||
└── language/
|
||||
├── en-GB/
|
||||
│ ├── plg_system_mokojoomtos.ini
|
||||
│ └── plg_system_mokojoomtos.sys.ini
|
||||
└── en-US/
|
||||
├── plg_system_mokojoomtos.ini
|
||||
└── plg_system_mokojoomtos.sys.ini
|
||||
```
|
||||
|
||||
## User Workflow
|
||||
|
||||
### Administrator Setup (One-Time)
|
||||
|
||||
1. **Create Article**
|
||||
- Create normal Joomla article with Terms content
|
||||
|
||||
2. **Create Menu Item**
|
||||
- Link to the article
|
||||
- Set alias to "terms-of-service"
|
||||
|
||||
3. **Configure Plugin**
|
||||
- Enter slug: "terms-of-service"
|
||||
- Enable plugin
|
||||
|
||||
### End User Experience
|
||||
|
||||
**When Site is Online**:
|
||||
- All pages work normally
|
||||
- Plugin does nothing
|
||||
|
||||
**When Site is Offline**:
|
||||
- Visitor accesses `/terms-of-service` → Accessible! ✅
|
||||
- Visitor accesses any other page → Offline message ✅
|
||||
|
||||
## Benefits of Final Solution
|
||||
|
||||
✅ **Simplicity**: One plugin, one configuration field
|
||||
✅ **Native**: Uses Joomla's existing content system
|
||||
✅ **Lightweight**: < 100 lines of code, 6.4 KB
|
||||
✅ **Flexible**: Works with any menu slug
|
||||
✅ **Maintainable**: No custom database tables
|
||||
✅ **Extensible**: Easy to add more features if needed
|
||||
|
||||
## Build System
|
||||
|
||||
Build scripts are being migrated to the [scripts](./scripts/) directory.
|
||||
|
||||
### Current Manual Build Process
|
||||
|
||||
1. Copy plugin files from `src/plugins/system/mokojoomtos/`
|
||||
2. Create ZIP with structure: `mokojoomtos.php`, `mokojoomtos.xml`, `script.php`, `src/`, `language/`, `administrator/`
|
||||
3. Name as `plg_system_mokojoomtos-{version}.zip`
|
||||
4. Output location: `dist/` directory (create if needed)
|
||||
|
||||
### Expected Output
|
||||
|
||||
The build process should produce a distributable ZIP file:
|
||||
- **Filename**: `plg_system_mokojoomtos-03.08.04.zip`
|
||||
- **Location**: `dist/` directory
|
||||
- **Size**: ~9 KB (zipped)
|
||||
- **Contents**: All plugin files ready for Joomla installation
|
||||
|
||||
## Installation
|
||||
|
||||
### For Administrators
|
||||
1. Upload ZIP via Joomla Extensions installer
|
||||
2. Enable plugin
|
||||
3. Configure menu slug
|
||||
4. Done!
|
||||
|
||||
### For Developers
|
||||
1. Clone repository
|
||||
2. Manually package plugin (see Build System section) or use pre-built releases
|
||||
3. Install generated ZIP in test Joomla instance
|
||||
4. Test functionality
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [x] Plugin installs successfully
|
||||
- [x] Plugin configuration appears
|
||||
- [x] Slug field saves correctly
|
||||
- [x] Offline mode works for configured slug
|
||||
- [x] Other pages remain offline
|
||||
- [x] Works with SEF URLs
|
||||
- [x] Language strings display correctly
|
||||
- [x] No JavaScript errors
|
||||
- [x] No PHP errors or warnings
|
||||
|
||||
## Documentation
|
||||
|
||||
### Created Files
|
||||
- [x] README.md - Complete user guide
|
||||
- [x] IMPLEMENTATION_SUMMARY.md - This file
|
||||
- [x] .gitignore - Excludes build artifacts
|
||||
|
||||
### README Sections
|
||||
- Features overview
|
||||
- How it works
|
||||
- Installation instructions
|
||||
- Quick setup guide (4 steps)
|
||||
- Configuration details
|
||||
- Technical specifications
|
||||
- FAQ
|
||||
- Use cases
|
||||
- Development guide
|
||||
- License and contact
|
||||
|
||||
## Compatibility
|
||||
|
||||
- **Joomla**: 4.x, 5.x
|
||||
- **PHP**: 7.4, 8.0, 8.1, 8.2, 8.3
|
||||
- **MySQL**: 5.7+, 8.0+
|
||||
- **MariaDB**: 10.2+
|
||||
|
||||
## Future Enhancements (Optional)
|
||||
|
||||
Potential additions if needed:
|
||||
- [ ] Support multiple slugs (comma-separated)
|
||||
- [ ] Whitelist by menu ID as alternative
|
||||
- [ ] IP whitelist for offline access
|
||||
- [ ] Time-based offline scheduling
|
||||
- [ ] Activity logging
|
||||
|
||||
## Performance Impact
|
||||
|
||||
- **Memory**: Negligible (< 1 KB)
|
||||
- **Execution Time**: < 1ms per request
|
||||
- **Database Queries**: 0 additional queries
|
||||
- **HTTP Requests**: 0 additional requests
|
||||
|
||||
## Security Considerations
|
||||
|
||||
✅ URL slug validated with `cmd` filter
|
||||
✅ No SQL injection risk (no database queries)
|
||||
✅ No XSS risk (no user input displayed)
|
||||
✅ No file inclusion risks
|
||||
✅ Follows Joomla coding standards
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v3.0 or later (GPL-3.0-or-later)
|
||||
|
||||
## Author
|
||||
|
||||
**Moko Consulting**
|
||||
- Website: https://mokoconsulting.tech
|
||||
- Email: hello@mokoconsulting.tech
|
||||
|
||||
## Conclusion
|
||||
|
||||
The MokoJoomTOS plugin successfully solves the problem of keeping legal documents accessible during site maintenance with a minimal, elegant solution that leverages Joomla's native features rather than adding complexity.
|
||||
|
||||
**Status**: Ready for production use ✅
|
||||
-266
@@ -1,266 +0,0 @@
|
||||
# Testing Guide for MokoJoomTOS Component
|
||||
|
||||
## Prerequisites
|
||||
- Joomla 4.x or 5.x installed
|
||||
- PHP 7.4 or later
|
||||
- MySQL 5.7+ or MariaDB 10.2+
|
||||
- Administrator access to Joomla
|
||||
|
||||
## Installation Testing
|
||||
|
||||
### Step 1: Build the Component Package (Optional)
|
||||
If you want to build from source:
|
||||
```bash
|
||||
cd /path/to/MokoJoomTOS
|
||||
make build
|
||||
```
|
||||
This will create a package in the `dist/` directory.
|
||||
|
||||
### Step 2: Install via Joomla Administrator
|
||||
|
||||
1. Log in to Joomla Administrator
|
||||
2. Navigate to **System → Install → Extensions**
|
||||
3. Choose **Upload Package File** tab
|
||||
4. Upload the component package (or install from directory: `src/`)
|
||||
5. Wait for installation to complete
|
||||
6. Verify the success message appears
|
||||
7. Check that post-installation message shows setup instructions
|
||||
|
||||
### Step 3: Verify Database Installation
|
||||
|
||||
Check that the table was created:
|
||||
```sql
|
||||
SELECT * FROM `#__mokojoomtos_content`;
|
||||
```
|
||||
|
||||
Expected result: 2 rows (one for terms, one for privacy) with default content.
|
||||
|
||||
### Step 4: Verify Plugin Installation
|
||||
|
||||
1. Go to **System → Plugins**
|
||||
2. Search for "MokoJoomTOS"
|
||||
3. Verify "System - MokoJoomTOS Offline Access" plugin is installed and enabled
|
||||
|
||||
## Backend (Admin) Testing
|
||||
|
||||
### Test 1: Navigate to Component
|
||||
1. Go to **Components → MokoJoomTOS**
|
||||
2. Should show the Terms of Service overview page
|
||||
3. Click on **Privacy Policy** in the submenu
|
||||
4. Should show the Privacy Policy overview page
|
||||
|
||||
### Test 2: Edit Terms of Service
|
||||
1. Navigate to **Components → MokoJoomTOS → Terms of Service**
|
||||
2. Click the **Edit** button
|
||||
3. Modify the title (e.g., "Terms and Conditions of Service")
|
||||
4. Edit the content using the WYSIWYG editor
|
||||
5. Change the published status
|
||||
6. Click **Save**
|
||||
7. Verify success message appears
|
||||
8. Check that changes are reflected on the overview page
|
||||
|
||||
### Test 3: Edit Privacy Policy
|
||||
1. Navigate to **Components → MokoJoomTOS → Privacy Policy**
|
||||
2. Click the **Edit** button
|
||||
3. Modify the title and content
|
||||
4. Click **Apply** (to save without closing)
|
||||
5. Make additional changes
|
||||
6. Click **Save**
|
||||
7. Verify all changes were saved
|
||||
|
||||
### Test 4: Check Version Tracking
|
||||
1. Edit either Terms or Privacy
|
||||
2. Note the "Last Updated" date
|
||||
3. Make a change and save
|
||||
4. Verify the "Last Updated" date changes
|
||||
5. Verify your user ID is recorded as the modifier
|
||||
|
||||
## Frontend Testing
|
||||
|
||||
### Test 5: Create Menu Items
|
||||
1. Go to **Menus → Main Menu** (or any menu)
|
||||
2. Click **New**
|
||||
3. Click **Select** for Menu Item Type
|
||||
4. Find **MokoJoomTOS** in the list
|
||||
5. Select **Terms of Service**
|
||||
6. Set a title (e.g., "Terms of Service")
|
||||
7. Save the menu item
|
||||
8. Repeat for Privacy Policy
|
||||
|
||||
### Test 6: View Pages on Frontend
|
||||
1. Navigate to your site's frontend
|
||||
2. Click on the Terms of Service menu item
|
||||
3. Verify:
|
||||
- Page title is correct
|
||||
- Content displays properly
|
||||
- CSS styling is applied
|
||||
- Last updated date shows (if modified)
|
||||
4. Repeat for Privacy Policy page
|
||||
|
||||
### Test 7: Direct URL Access
|
||||
1. Navigate to: `https://yoursite.com/index.php?option=com_mokojoomtos&view=terms`
|
||||
2. Verify Terms page loads
|
||||
3. Navigate to: `https://yoursite.com/index.php?option=com_mokojoomtos&view=privacy`
|
||||
4. Verify Privacy page loads
|
||||
|
||||
### Test 8: Responsive Design
|
||||
1. Open a ToS or Privacy page on frontend
|
||||
2. Resize browser window to mobile size
|
||||
3. Verify content is readable and properly formatted
|
||||
4. Test on actual mobile device if possible
|
||||
|
||||
## Offline Mode Testing
|
||||
|
||||
### Test 9: Enable Offline Mode
|
||||
1. Go to **System → Global Configuration**
|
||||
2. In the **Site** tab, set **Site Offline** to **Yes**
|
||||
3. Add an offline message (optional)
|
||||
4. Save the configuration
|
||||
|
||||
### Test 10: Test Offline Access
|
||||
1. Log out of administrator
|
||||
2. Try to access the homepage - should show offline message
|
||||
3. Navigate to: `https://yoursite.com/index.php?option=com_mokojoomtos&view=terms`
|
||||
4. **Expected**: Terms page should load despite site being offline
|
||||
5. Navigate to: `https://yoursite.com/index.php?option=com_mokojoomtos&view=privacy`
|
||||
6. **Expected**: Privacy page should load despite site being offline
|
||||
7. Try to access any other page
|
||||
8. **Expected**: Should show offline message
|
||||
|
||||
### Test 11: Disable Plugin and Test
|
||||
1. Log back into administrator
|
||||
2. Go to **System → Plugins**
|
||||
3. Find "System - MokoJoomTOS Offline Access" and disable it
|
||||
4. Log out
|
||||
5. Try to access Terms/Privacy pages
|
||||
6. **Expected**: Should show offline message (plugin disabled)
|
||||
7. Re-enable the plugin
|
||||
|
||||
## Published Status Testing
|
||||
|
||||
### Test 12: Unpublish Content
|
||||
1. Log into administrator
|
||||
2. Edit Terms of Service
|
||||
3. Set **Published** to **Unpublished**
|
||||
4. Save
|
||||
5. View Terms page on frontend
|
||||
6. **Expected**: Should show "Content not found" error (404)
|
||||
7. Re-publish the content
|
||||
|
||||
## SEF URL Testing (if enabled)
|
||||
|
||||
### Test 13: Clean URLs
|
||||
If SEF URLs are enabled:
|
||||
1. Check that URLs are clean (e.g., `/terms-of-service`)
|
||||
2. Verify pages still load correctly
|
||||
3. Test breadcrumb navigation
|
||||
|
||||
## Security Testing
|
||||
|
||||
### Test 14: XSS Prevention
|
||||
1. Edit Terms or Privacy content
|
||||
2. Try to inject JavaScript: `<script>alert('XSS')</script>`
|
||||
3. Save and view on frontend
|
||||
4. **Expected**: Script should be filtered/escaped, no alert shown
|
||||
|
||||
### Test 15: SQL Injection Prevention
|
||||
This is built into Joomla's database API, but verify:
|
||||
1. All database queries use prepared statements
|
||||
2. No direct SQL concatenation exists in code
|
||||
|
||||
### Test 16: Access Control
|
||||
1. Create a test user with limited permissions
|
||||
2. Log in as that user
|
||||
3. Try to access the component backend
|
||||
4. **Expected**: Should be denied if user doesn't have proper permissions
|
||||
|
||||
## Performance Testing
|
||||
|
||||
### Test 17: Page Load Speed
|
||||
1. Use browser developer tools
|
||||
2. Load Terms/Privacy pages
|
||||
3. Check network tab for load times
|
||||
4. Verify CSS is cached after first load
|
||||
|
||||
## Uninstallation Testing
|
||||
|
||||
### Test 18: Uninstall Component
|
||||
1. Go to **System → Extensions → Manage**
|
||||
2. Find "MokoJoomTOS - Terms & Privacy"
|
||||
3. Click the checkbox
|
||||
4. Click **Uninstall**
|
||||
5. Verify success message
|
||||
6. Check database - table should be removed
|
||||
7. Check plugins - offline access plugin should be removed
|
||||
8. Try to access frontend pages
|
||||
9. **Expected**: 404 error (component removed)
|
||||
|
||||
## Expected Test Results Summary
|
||||
|
||||
| Test | Expected Result | Status |
|
||||
|------|----------------|--------|
|
||||
| Installation | Success, plugin enabled | ✓ |
|
||||
| Database Creation | Table exists with 2 default rows | ✓ |
|
||||
| Admin Navigation | Can access both Terms and Privacy | ✓ |
|
||||
| Content Edit | Can save changes, version tracked | ✓ |
|
||||
| Menu Items | Can create and view | ✓ |
|
||||
| Frontend Display | Content shows with styling | ✓ |
|
||||
| Direct URLs | Pages load correctly | ✓ |
|
||||
| Responsive Design | Mobile-friendly layout | ✓ |
|
||||
| Offline Access | Pages load when site offline | ✓ |
|
||||
| Plugin Disable | Offline access blocked when disabled | ✓ |
|
||||
| Unpublished Content | Shows 404 when unpublished | ✓ |
|
||||
| XSS Prevention | Scripts filtered/escaped | ✓ |
|
||||
| Uninstallation | Clean removal, database cleaned | ✓ |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Component doesn't appear in menu
|
||||
- Clear Joomla cache: System → Clear Cache
|
||||
- Check file permissions on src/ directory
|
||||
|
||||
### Issue: Offline access doesn't work
|
||||
- Verify plugin is enabled
|
||||
- Check plugin ordering (should run early)
|
||||
- Clear cache
|
||||
|
||||
### Issue: CSS not loading
|
||||
- Check media files are in correct location
|
||||
- Verify template doesn't conflict
|
||||
- Clear browser cache
|
||||
|
||||
### Issue: Database error on installation
|
||||
- Check database credentials
|
||||
- Verify database user has CREATE TABLE permission
|
||||
- Check MySQL/MariaDB version compatibility
|
||||
|
||||
### Issue: 404 error on frontend pages
|
||||
- Verify content is published
|
||||
- Check menu items are properly configured
|
||||
- Clear Joomla cache
|
||||
|
||||
## Manual Code Review Checklist
|
||||
|
||||
- [ ] All files have proper copyright headers
|
||||
- [ ] All PHP files have `defined('_JEXEC') or die;`
|
||||
- [ ] Database queries use prepared statements
|
||||
- [ ] Output is properly escaped
|
||||
- [ ] Language strings are used (no hardcoded text)
|
||||
- [ ] Error handling is in place
|
||||
- [ ] Code follows Joomla coding standards
|
||||
- [ ] Component works on Joomla 4.x and 5.x
|
||||
|
||||
## Automated Testing (Future)
|
||||
|
||||
Consider adding:
|
||||
- PHPUnit tests for models
|
||||
- Integration tests for database operations
|
||||
- Selenium tests for UI workflows
|
||||
- PHP CodeSniffer for coding standards
|
||||
- PHPStan for static analysis
|
||||
|
||||
## Conclusion
|
||||
|
||||
This comprehensive testing guide covers all major functionality of the MokoJoomTOS component. Follow each test in order to ensure proper installation, configuration, and operation of the component in a Joomla environment.
|
||||
|
||||
If all tests pass, the component is ready for production use.
|
||||
@@ -1,492 +0,0 @@
|
||||
# MokoStandards Scripts
|
||||
|
||||
This directory contains automation scripts for MokoStandards repository management and documentation maintenance.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
The scripts are organized into functional categories (with Python, Shell, and PowerShell scripts side-by-side):
|
||||
|
||||
- **[`automation/`](automation/)** - Repository automation and bulk operations
|
||||
- `bulk_update_repos.php` - Bulk update organization repositories (Python)
|
||||
- `Invoke-BulkUpdateGUI.ps1` - Bulk update GUI for Windows (PowerShell)
|
||||
- `Update-BulkRepositories.ps1` - Bulk update command-line (PowerShell)
|
||||
- `sync_file_to_project.py` - Sync files to target repositories
|
||||
- `auto_create_org_projects.py` - Auto-create GitHub Projects
|
||||
- `create_repo_project.py` - Create repository-specific projects
|
||||
- `file-distributor.py` / `file-distributor.ps1` - Enterprise file distribution
|
||||
- **[`maintenance/`](maintenance/)** - Repository maintenance tasks
|
||||
- `update_changelog.py` - Manage CHANGELOG.md updates
|
||||
- `release_version.py` - Release version management
|
||||
- `validate_file_headers.py` - Validate file headers
|
||||
- `update_gitignore_patterns.sh` - Update gitignore patterns
|
||||
- `setup-labels.sh` - Setup standard GitHub labels
|
||||
- **[`analysis/`](analysis/)** - Analysis and reporting
|
||||
- `analyze_pr_conflicts.py` - Analyze PR conflicts
|
||||
- `generate_canonical_config.py` - Generate canonical configs
|
||||
- **[`tests/`](tests/)** - Test scripts
|
||||
- `test_bulk_update_repos.php` - Test bulk update automation
|
||||
- `test_dry_run.py` - Test dry-run functionality
|
||||
- **[`docs/`](docs/)** - Documentation generation and maintenance scripts
|
||||
- `rebuild_indexes.py` - Generate documentation indexes
|
||||
- `check_doc_coverage.py` - Check documentation coverage
|
||||
- `generate_script_catalog.py` - Generate script catalog
|
||||
- `update_metadata.py` - Update script metadata
|
||||
- **Documentation Files:**
|
||||
- `ARCHITECTURE.md` - Scripts architecture documentation
|
||||
- `AUTO_CREATE_ORG_PROJECTS.md` - Auto-create projects guide
|
||||
- `DRY_RUN_PATTERN.md` - Dry-run implementation pattern
|
||||
- `NEW_SCRIPTS.md` - New scripts development guide
|
||||
- `QUICKSTART_ORG_PROJECTS.md` - Quick start guide
|
||||
- `README_update_gitignore_patterns.md` - Gitignore update guide
|
||||
- `LEGAL_DOC_GENERATOR_WEB_README.md` - Legal doc generator guide
|
||||
- `legal_doc_generator.html` - Legal doc generator web interface
|
||||
- **[`run/`](run/)** - Operational setup scripts
|
||||
- `setup_github_project_v2.py` - Setup GitHub Projects
|
||||
- **[`lib/`](lib/)** - Shared library code
|
||||
- `common.py` - Python utility functions
|
||||
- `common.sh` - Shell utility functions
|
||||
- `Common.psm1` - PowerShell utility module
|
||||
- `ConfigManager.psm1` - PowerShell configuration management
|
||||
- `GuiUtils.psm1` - PowerShell GUI utilities
|
||||
- `extension_utils.py` - Extension detection utilities
|
||||
- `joomla_manifest.py` - Joomla manifest parsing
|
||||
- **[`build/`](build/)** - Build and compilation scripts
|
||||
- `resolve_makefile.py` - Makefile resolution
|
||||
- **[`release/`](release/)** - Release automation
|
||||
- `dolibarr_release.py` - Dolibarr module releases
|
||||
- `detect_platform.py` - Platform detection
|
||||
- `package_extension.py` - Create distribution packages
|
||||
- `update_dates.sh` - Update copyright dates
|
||||
- **[`validate/`](validate/)** - Validation and linting
|
||||
- `manifest.py` - Validate extension manifests
|
||||
- `xml_wellformed.py` - Validate XML syntax
|
||||
- `workflows.py` - Validate GitHub Actions workflows
|
||||
- `tabs.py` - Check for tab characters
|
||||
- `no_secrets.py` - Scan for secrets
|
||||
- `paths.py` - Check for Windows paths
|
||||
- `php_syntax.py` - Validate PHP syntax
|
||||
- `check_repo_health.py` - Repository health checks (Python)
|
||||
- `Invoke-RepoHealthCheckGUI.ps1` - Repository health checks GUI (PowerShell)
|
||||
- `Invoke-PlatformDetection.ps1` - Platform detection (PowerShell)
|
||||
- `validate_repo_health.py` - Comprehensive validation
|
||||
- `validate_structure.py` - Validate repository structure
|
||||
- `validate_codeql_config.py` - Validate CodeQL config
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.7+
|
||||
- `requests` library (for API access with token)
|
||||
- GitHub Personal Access Token with permissions:
|
||||
- `project` (read and write)
|
||||
- `read:org` (organization read)
|
||||
- `repo` (repository access)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Automation Scripts
|
||||
```bash
|
||||
# Bulk update all organization repositories
|
||||
./scripts/automation/bulk_update_repos.php --dry-run
|
||||
./scripts/automation/bulk_update_repos.php --yes
|
||||
|
||||
# Sync specific file to repositories
|
||||
./scripts/automation/sync_file_to_project.py
|
||||
```
|
||||
|
||||
### Maintenance Scripts
|
||||
```bash
|
||||
# Update changelog
|
||||
python3 scripts/maintenance/update_changelog.py --category Added --entry "New feature"
|
||||
|
||||
# Create a release
|
||||
python3 scripts/maintenance/release_version.py --version 05.01.00
|
||||
|
||||
# Validate file headers
|
||||
python3 scripts/maintenance/validate_file_headers.py
|
||||
```
|
||||
|
||||
### Validation Scripts
|
||||
```bash
|
||||
# Validate repository health
|
||||
python3 scripts/validate/validate_repo_health.py
|
||||
|
||||
# Validate manifests
|
||||
python3 scripts/validate/manifest.py
|
||||
|
||||
# Validate CodeQL configuration
|
||||
python3 scripts/validate/validate_codeql_config.py
|
||||
```
|
||||
|
||||
## Scripts Overview
|
||||
|
||||
### Repository Automation Scripts
|
||||
|
||||
See the [automation/ directory](automation/) for detailed documentation.
|
||||
|
||||
#### auto_create_org_projects.py
|
||||
|
||||
Automatically create smart GitHub Projects for every repository in the organization. Intelligently detects project types (Joomla, Dolibarr, or Generic) and creates appropriate project structures with customized fields and views. Also generates and pushes roadmaps to repositories that don't have one.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Dry run (preview what would be created)
|
||||
python3 scripts/automation/auto_create_org_projects.py --dry-run
|
||||
|
||||
# Actually create projects and roadmaps
|
||||
export GH_PAT="your_token"
|
||||
python3 scripts/automation/auto_create_org_projects.py
|
||||
|
||||
# With verbose logging
|
||||
python3 scripts/automation/auto_create_org_projects.py --verbose
|
||||
|
||||
# For a different organization
|
||||
python3 scripts/automation/auto_create_org_projects.py --org your-org-name
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
- Fetches all repositories in the organization
|
||||
- Automatically detects project type (Joomla/Dolibarr/Generic)
|
||||
- Checks for existing roadmaps
|
||||
- Generates type-specific roadmaps if missing
|
||||
- Pushes roadmaps to `docs/ROADMAP.md` in each repo
|
||||
- Creates GitHub Projects with appropriate custom fields and views
|
||||
- Skips MokoStandards (Project #7 already exists)
|
||||
|
||||
See [AUTO_CREATE_ORG_PROJECTS.md](./AUTO_CREATE_ORG_PROJECTS.md) for detailed documentation.
|
||||
|
||||
#### bulk_update_repos.php
|
||||
|
||||
Bulk update script to push workflows, scripts, and configurations to multiple organization repositories.
|
||||
|
||||
**Important**: Only processes repositories whose names begin with "Moko".
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Dry run (preview changes)
|
||||
./scripts/automation/bulk_update_repos.php --dry-run
|
||||
|
||||
# Update all non-archived repos beginning with "Moko"
|
||||
./scripts/automation/bulk_update_repos.php
|
||||
|
||||
# Update specific repos
|
||||
./scripts/automation/bulk_update_repos.php --repos repo1 repo2
|
||||
|
||||
# Exclude specific repos
|
||||
./scripts/automation/bulk_update_repos.php --exclude legacy-repo archived-repo
|
||||
|
||||
# Automated execution (skip confirmation)
|
||||
./scripts/automation/bulk_update_repos.php --yes
|
||||
|
||||
# Only sync workflows (not scripts)
|
||||
./scripts/automation/bulk_update_repos.php --files-only
|
||||
|
||||
# Only sync scripts (not workflows)
|
||||
./scripts/automation/bulk_update_repos.php --scripts-only
|
||||
|
||||
# Set missing standards options (repository variables)
|
||||
./scripts/automation/bulk_update_repos.php --set-standards --yes
|
||||
```
|
||||
|
||||
**Automated Monthly Sync:**
|
||||
The repository includes `.github/workflows/bulk-repo-sync.yml` which automatically runs this script monthly on the 1st at 00:00 UTC. Can also be triggered manually via workflow_dispatch.
|
||||
|
||||
**What it does:**
|
||||
- Clones target repositories
|
||||
- Creates feature branches
|
||||
- Copies workflows, scripts, and configurations
|
||||
- Commits and pushes changes
|
||||
- Creates pull requests for review
|
||||
- Optionally sets missing standards options (repository variables)
|
||||
|
||||
**Repository Variables Set:**
|
||||
- `RS_FTP_PATH_SUFFIX` - Release system FTP path suffix (e.g., `/mokocrm`)
|
||||
- `DEV_FTP_PATH_SUFFIX` - Development system FTP path suffix (e.g., `/mokocrm`)
|
||||
|
||||
**Organization Variables Used:**
|
||||
- Release System: `RS_FTP_PATH`
|
||||
- Development System: `DEV_FTP_PATH`
|
||||
|
||||
**Organization Secrets Used:**
|
||||
- Release System: `RS_FTP_HOST`, `RS_FTP_USER`, `RS_FTP_PASSWORD`
|
||||
- Development System: `DEV_FTP_HOST`, `DEV_FTP_USER`, `DEV_FTP_PASSWORD`
|
||||
- Authentication: `FTP_KEY` (optional SSH key), `FTP_PROTOCOL`, `FTP_PORT`
|
||||
|
||||
**What gets synced:**
|
||||
- Dependabot configuration (monthly schedule for Python, JavaScript, PHP, and GitHub Actions)
|
||||
- GitHub workflow templates (CI, CodeQL with Python/JavaScript/PHP, build, release)
|
||||
- Reusable workflows
|
||||
- Maintenance scripts (validation, changelog, release)
|
||||
|
||||
See [Bulk Repository Updates Guide](../docs/guide/bulk-repository-updates.md) for detailed documentation.
|
||||
|
||||
### Documentation Scripts (`docs/`)
|
||||
|
||||
#### rebuild_indexes.py
|
||||
|
||||
Automatically generates `index.md` files for each folder in the documentation directory.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Generate indexes
|
||||
python3 scripts/docs/rebuild_indexes.py
|
||||
|
||||
# Check mode (CI/CD)
|
||||
python3 scripts/docs/rebuild_indexes.py --check
|
||||
|
||||
# Custom root directory
|
||||
python3 scripts/docs/rebuild_indexes.py --root path/to/docs
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
- Scans documentation folders recursively
|
||||
- Creates/updates index.md files with links to documents and subfolders
|
||||
- Maintains consistent structure across documentation
|
||||
- Supports check mode for CI/CD validation
|
||||
|
||||
### GitHub Project v2 Automation Scripts
|
||||
|
||||
This section covers scripts for managing the GitHub Project v2 "MokoStandards Documentation Control Register".
|
||||
|
||||
#### 1. `setup_github_project_v2.py` - Create New Project ⭐ ENHANCED
|
||||
|
||||
Creates a brand new GitHub Project v2 and populates it with documentation tasks.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
export GH_PAT="your_personal_access_token"
|
||||
python3 scripts/setup_github_project_v2.py
|
||||
|
||||
# With verbose logging
|
||||
python3 scripts/setup_github_project_v2.py --verbose
|
||||
|
||||
# Skip view documentation
|
||||
python3 scripts/setup_github_project_v2.py --skip-views
|
||||
```
|
||||
|
||||
**New Features:**
|
||||
- ✅ `--verbose` flag for detailed error logging and debug output
|
||||
- ✅ Enhanced error messages with stack traces
|
||||
- ✅ Verbose logging for GraphQL queries and responses
|
||||
- ✅ Structured error context and details
|
||||
- ✅ Automatic view documentation (Board, Table, Roadmap)
|
||||
- ✅ `--skip-views` flag to skip view documentation
|
||||
|
||||
#### 2. `sync_file_to_project.py` - Sync Documentation Files to Project
|
||||
|
||||
Syncs individual documentation files or folders to GitHub Project #7 with full path as clickable title.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
export GH_TOKEN="your_github_token" # or use gh auth login
|
||||
python3 scripts/automation/sync_file_to_project.py docs/policy/example.md
|
||||
|
||||
# Sync a folder
|
||||
python3 scripts/automation/sync_file_to_project.py docs/policy --folder
|
||||
|
||||
# Specify different project number
|
||||
python3 scripts/automation/sync_file_to_project.py docs/guide/example.md 8
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- ✅ Creates GitHub issue for each document
|
||||
- ✅ Title is full path as markdown link to document
|
||||
- ✅ Supports GH_TOKEN environment variable
|
||||
- ✅ Automatically adds to specified project
|
||||
- ✅ Sets project fields based on document type
|
||||
- ✅ Tracks document metadata and status
|
||||
|
||||
#### 3. `populate_project_from_scan.py` - Populate Existing Project
|
||||
|
||||
Scans docs/ and templates/ directories and populates an existing project with tasks.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
export GH_PAT="your_personal_access_token"
|
||||
python3 scripts/populate_project_from_scan.py --project-number 7
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Works with existing Project #7
|
||||
- Lists all subdirectories in templates/
|
||||
- Scans all .md files in docs/ and templates/
|
||||
- Creates tasks for each document
|
||||
- Infers metadata from file paths and names
|
||||
|
||||
#### 4. `setup_project_views.py` - Configure Views
|
||||
|
||||
Creates standardized views for your GitHub Project v2.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
export GH_PAT="your_personal_access_token"
|
||||
python3 scripts/setup_project_views.py --project-number 7
|
||||
|
||||
# With verbose logging
|
||||
python3 scripts/setup_project_views.py --verbose --project-number 7
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- ✅ Creates 3 standard views: Board, Table, Roadmap
|
||||
- ✅ Configures fields, grouping, and sorting
|
||||
- ✅ Checks for existing views before creating
|
||||
- ✅ Verbose error handling
|
||||
- ✅ View-specific documentation
|
||||
|
||||
### Validation Scripts (`validate/`)
|
||||
|
||||
Scripts for validating repository structure, code quality, and standards compliance.
|
||||
|
||||
#### manifest.py
|
||||
|
||||
Validates extension manifests for Joomla and Dolibarr projects.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python3 scripts/validate/manifest.py
|
||||
python3 scripts/validate/manifest.py --path src/
|
||||
```
|
||||
|
||||
#### xml_wellformed.py
|
||||
|
||||
Validates XML file syntax and structure.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python3 scripts/validate/xml_wellformed.py
|
||||
python3 scripts/validate/xml_wellformed.py file.xml
|
||||
```
|
||||
|
||||
#### workflows.py
|
||||
|
||||
Validates GitHub Actions workflow files.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python3 scripts/validate/workflows.py
|
||||
```
|
||||
|
||||
#### php_syntax.py
|
||||
|
||||
Validates PHP file syntax using PHP parser.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python3 scripts/validate/php_syntax.py
|
||||
python3 scripts/validate/php_syntax.py src/
|
||||
```
|
||||
|
||||
#### no_secrets.py
|
||||
|
||||
Scans for accidentally committed secrets and credentials.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python3 scripts/validate/no_secrets.py
|
||||
```
|
||||
|
||||
### Release Scripts (`release/`)
|
||||
|
||||
Scripts for creating releases and packages.
|
||||
|
||||
#### package_extension.py
|
||||
|
||||
Creates distributable ZIP packages for Joomla and Dolibarr extensions.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python3 scripts/release/package_extension.py dist/
|
||||
python3 scripts/release/package_extension.py --output-dir releases/
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Auto-detects extension type (Joomla/Dolibarr)
|
||||
- Creates proper directory structure
|
||||
- Excludes development files
|
||||
- Generates checksums
|
||||
|
||||
#### detect_platform.py
|
||||
|
||||
Auto-detects whether project is Joomla or Dolibarr extension.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python3 scripts/release/detect_platform.py
|
||||
```
|
||||
|
||||
#### update_dates.sh
|
||||
|
||||
Updates copyright dates in files.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
bash scripts/release/update_dates.sh
|
||||
```
|
||||
|
||||
## Library Functions (`lib/`)
|
||||
|
||||
### common.py
|
||||
|
||||
Python utility functions for common operations.
|
||||
|
||||
### common.sh
|
||||
|
||||
Shell utility functions for common operations.
|
||||
|
||||
### extension_utils.py
|
||||
|
||||
Unified extension detection and utilities for Joomla and Dolibarr projects.
|
||||
|
||||
**Features:**
|
||||
- Auto-detect extension type
|
||||
- Parse extension metadata
|
||||
- Get version information
|
||||
- Extract extension details
|
||||
|
||||
### joomla_manifest.py
|
||||
|
||||
Joomla manifest (XML) parsing utilities.
|
||||
|
||||
**Features:**
|
||||
- Parse Joomla manifest files
|
||||
- Extract extension metadata
|
||||
- Validate manifest structure
|
||||
|
||||
## Authentication
|
||||
|
||||
All scripts support two authentication methods:
|
||||
|
||||
**Option 1: GH_PAT environment variable (Recommended)**
|
||||
```bash
|
||||
export GH_PAT="your_personal_access_token"
|
||||
python3 scripts/<script_name>.py
|
||||
```
|
||||
|
||||
**Option 2: GitHub CLI**
|
||||
```bash
|
||||
gh auth login
|
||||
python3 scripts/<script_name>.py
|
||||
```
|
||||
|
||||
## Common Flags
|
||||
|
||||
Most scripts support:
|
||||
- `--verbose` - Enable detailed logging
|
||||
- `--dry-run` - Preview changes without executing
|
||||
- `--help` - Show help message
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication Issues
|
||||
- Verify your token has required permissions
|
||||
- Check token hasn't expired
|
||||
- Ensure `GH_PAT` is exported, not just set
|
||||
|
||||
### GraphQL Errors
|
||||
- Use `--verbose` flag to see full error details
|
||||
- Check API rate limits
|
||||
- Verify field names match project schema
|
||||
|
||||
### Missing Dependencies
|
||||
```bash
|
||||
pip install requests
|
||||
```
|
||||
@@ -1,16 +0,0 @@
|
||||
# Docs Index: /templates/repos/joomla/component/scripts
|
||||
|
||||
## Purpose
|
||||
|
||||
This index provides navigation to documentation within this folder.
|
||||
|
||||
## Metadata
|
||||
|
||||
- **Document Type:** index
|
||||
- **Auto-generated:** This file is automatically generated by rebuild_indexes.py
|
||||
|
||||
## Revision History
|
||||
|
||||
| Change | Notes | Author |
|
||||
| --- | --- | --- |
|
||||
| Automated update | Generated by documentation index automation | rebuild_indexes.py |
|
||||
@@ -1,373 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoStandards.Scripts.Validate
|
||||
* INGROUP: MokoStandards
|
||||
* REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
* PATH: /scripts/validate/auto_detect_platform.php
|
||||
* VERSION: 04.00.03
|
||||
* BRIEF: Automatic platform detection and validation - PHP implementation
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../../src/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoStandards\Enterprise\CLIApp;
|
||||
|
||||
/**
|
||||
* Automatic Platform Detection and Validation
|
||||
*
|
||||
* Detects whether a repository is a Joomla/WaaS component, Dolibarr/CRM module,
|
||||
* or generic repository, then validates against appropriate schema
|
||||
*/
|
||||
class AutoDetectPlatform extends CLIApp
|
||||
{
|
||||
private const DETECTION_THRESHOLD = 0.5; // 50% confidence required
|
||||
|
||||
private array $detectionResults = [
|
||||
'joomla' => ['score' => 0, 'indicators' => []],
|
||||
'dolibarr' => ['score' => 0, 'indicators' => []],
|
||||
'generic' => ['score' => 0, 'indicators' => []],
|
||||
];
|
||||
|
||||
private string $detectedPlatform = 'generic';
|
||||
private string $schemaFile = '';
|
||||
|
||||
protected function setupArguments(): array
|
||||
{
|
||||
return [
|
||||
'repo-path:' => 'Path to repository to analyze (default: current directory)',
|
||||
'schema-dir:' => 'Path to schema definitions directory (default: scripts/definitions)',
|
||||
'output-dir:' => 'Directory for output reports (default: logs/validation)',
|
||||
];
|
||||
}
|
||||
|
||||
protected function run(): int
|
||||
{
|
||||
$repoPath = $this->getOption('repo-path', '.');
|
||||
$schemaDir = $this->getOption('schema-dir', 'scripts/definitions');
|
||||
$outputDir = $this->getOption('output-dir', 'logs/validation');
|
||||
|
||||
// Make paths absolute
|
||||
$repoPath = $this->getAbsolutePath($repoPath);
|
||||
$schemaDir = $this->getAbsolutePath($schemaDir);
|
||||
$outputDir = $this->getAbsolutePath($outputDir);
|
||||
|
||||
if (!is_dir($repoPath)) {
|
||||
$this->log("Repository path not found: {$repoPath}", 'ERROR');
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (!is_dir($schemaDir)) {
|
||||
$this->log("Schema directory not found: {$schemaDir}", 'ERROR');
|
||||
return 3;
|
||||
}
|
||||
|
||||
$this->log("Analyzing repository: {$repoPath}", 'INFO');
|
||||
|
||||
// Run platform detection
|
||||
$this->detectJoomla($repoPath);
|
||||
$this->detectDolibarr($repoPath);
|
||||
|
||||
// Determine platform
|
||||
$this->determinePlatform();
|
||||
|
||||
// Map to schema file
|
||||
$this->schemaFile = $this->mapPlatformToSchema($schemaDir);
|
||||
|
||||
if (!file_exists($this->schemaFile)) {
|
||||
$this->log("Schema file not found: {$this->schemaFile}", 'ERROR');
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Output results
|
||||
if ($this->jsonOutput) {
|
||||
$this->outputJson();
|
||||
} else {
|
||||
$this->displayResults();
|
||||
}
|
||||
|
||||
// Generate reports
|
||||
$this->generateReports($outputDir, $repoPath);
|
||||
|
||||
$this->log("Platform detection completed: {$this->detectedPlatform}", 'INFO');
|
||||
$this->log("Schema file: {$this->schemaFile}", 'INFO');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function detectJoomla(string $repoPath): void
|
||||
{
|
||||
$score = 0;
|
||||
$indicators = [];
|
||||
|
||||
// Look for Joomla manifest files
|
||||
$manifests = $this->findFiles($repoPath, '*.xml', 3);
|
||||
foreach ($manifests as $manifest) {
|
||||
$content = @file_get_contents($manifest);
|
||||
if ($content && (
|
||||
strpos($content, '<extension') !== false ||
|
||||
strpos($content, '<install') !== false
|
||||
)) {
|
||||
$score += 0.3;
|
||||
$indicators[] = "Found Joomla manifest: " . basename($manifest);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Joomla directory structure
|
||||
$joomlaDirs = ['site', 'admin', 'administrator', 'language', 'media'];
|
||||
foreach ($joomlaDirs as $dir) {
|
||||
if (is_dir("{$repoPath}/{$dir}")) {
|
||||
$score += 0.1;
|
||||
$indicators[] = "Found Joomla directory: {$dir}/";
|
||||
}
|
||||
}
|
||||
|
||||
// Check for index.html files (Joomla security pattern)
|
||||
$indexCount = count($this->findFiles($repoPath, 'index.html', 2));
|
||||
if ($indexCount > 2) {
|
||||
$score += 0.2;
|
||||
$indicators[] = "Found {$indexCount} index.html files (Joomla pattern)";
|
||||
}
|
||||
|
||||
$this->detectionResults['joomla'] = [
|
||||
'score' => min(1.0, $score),
|
||||
'indicators' => $indicators,
|
||||
];
|
||||
}
|
||||
|
||||
private function detectDolibarr(string $repoPath): void
|
||||
{
|
||||
$score = 0;
|
||||
$indicators = [];
|
||||
|
||||
// Look for Dolibarr module descriptor
|
||||
$descriptors = $this->findFiles($repoPath, 'mod*.class.php', 3);
|
||||
foreach ($descriptors as $descriptor) {
|
||||
$content = @file_get_contents($descriptor);
|
||||
if ($content && strpos($content, 'DolibarrModules') !== false) {
|
||||
$score += 0.4;
|
||||
$indicators[] = "Found Dolibarr module descriptor: " . basename($descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Dolibarr-specific code patterns
|
||||
$phpFiles = $this->findFiles($repoPath, '*.php', 3);
|
||||
$dolibarrPatterns = ['dol_include_once', '$this->numero', 'DoliDB', 'Translate'];
|
||||
|
||||
foreach ($phpFiles as $file) {
|
||||
$content = @file_get_contents($file);
|
||||
if (!$content) continue;
|
||||
|
||||
foreach ($dolibarrPatterns as $pattern) {
|
||||
if (strpos($content, $pattern) !== false) {
|
||||
$score += 0.05;
|
||||
$indicators[] = "Found Dolibarr pattern '{$pattern}' in " . basename($file);
|
||||
break; // Only count once per file
|
||||
}
|
||||
}
|
||||
|
||||
if ($score >= 0.8) break; // Stop early if confident
|
||||
}
|
||||
|
||||
// Check for Dolibarr directory structure
|
||||
$dolibarrDirs = ['core/modules', 'sql', 'class', 'lib', 'langs'];
|
||||
foreach ($dolibarrDirs as $dir) {
|
||||
if (is_dir("{$repoPath}/{$dir}")) {
|
||||
$score += 0.1;
|
||||
$indicators[] = "Found Dolibarr directory: {$dir}/";
|
||||
}
|
||||
}
|
||||
|
||||
// Check for SQL files in sql/ directory
|
||||
if (is_dir("{$repoPath}/sql")) {
|
||||
$sqlFiles = $this->findFiles("{$repoPath}/sql", '*.sql', 1);
|
||||
if (count($sqlFiles) > 0) {
|
||||
$score += 0.1;
|
||||
$indicators[] = "Found " . count($sqlFiles) . " SQL files in sql/";
|
||||
}
|
||||
}
|
||||
|
||||
$this->detectionResults['dolibarr'] = [
|
||||
'score' => min(1.0, $score),
|
||||
'indicators' => $indicators,
|
||||
];
|
||||
}
|
||||
|
||||
private function determinePlatform(): void
|
||||
{
|
||||
$joomlaScore = $this->detectionResults['joomla']['score'];
|
||||
$dolibarrScore = $this->detectionResults['dolibarr']['score'];
|
||||
|
||||
// Require minimum threshold
|
||||
if ($joomlaScore >= self::DETECTION_THRESHOLD && $joomlaScore > $dolibarrScore) {
|
||||
$this->detectedPlatform = 'joomla';
|
||||
} elseif ($dolibarrScore >= self::DETECTION_THRESHOLD && $dolibarrScore > $joomlaScore) {
|
||||
$this->detectedPlatform = 'dolibarr';
|
||||
} else {
|
||||
$this->detectedPlatform = 'generic';
|
||||
}
|
||||
}
|
||||
|
||||
private function mapPlatformToSchema(string $schemaDir): string
|
||||
{
|
||||
$mapping = [
|
||||
'joomla' => 'waas-component.xml',
|
||||
'dolibarr' => 'crm-module.xml',
|
||||
'generic' => 'default-repository.xml',
|
||||
];
|
||||
|
||||
return $schemaDir . '/' . $mapping[$this->detectedPlatform];
|
||||
}
|
||||
|
||||
private function displayResults(): void
|
||||
{
|
||||
echo "\n=== Platform Detection Results ===\n\n";
|
||||
|
||||
echo "Platform: {$this->detectedPlatform}\n";
|
||||
echo "Schema: {$this->schemaFile}\n\n";
|
||||
|
||||
echo "Detection Scores:\n";
|
||||
foreach ($this->detectionResults as $platform => $data) {
|
||||
$percentage = round($data['score'] * 100, 1);
|
||||
$status = ($data['score'] >= self::DETECTION_THRESHOLD) ? '✅' : '❌';
|
||||
echo sprintf(" %s %s: %.1f%%\n", $status, ucfirst($platform), $percentage);
|
||||
}
|
||||
|
||||
echo "\nDetection Indicators:\n";
|
||||
$indicators = $this->detectionResults[$this->detectedPlatform]['indicators'];
|
||||
if (empty($indicators)) {
|
||||
echo " No specific indicators found (generic repository)\n";
|
||||
} else {
|
||||
foreach ($indicators as $indicator) {
|
||||
echo " • {$indicator}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
private function outputJson(): void
|
||||
{
|
||||
$output = [
|
||||
'platform' => $this->detectedPlatform,
|
||||
'schema' => $this->schemaFile,
|
||||
'detection_results' => $this->detectionResults,
|
||||
'threshold' => self::DETECTION_THRESHOLD,
|
||||
'timestamp' => date('c'),
|
||||
];
|
||||
|
||||
echo json_encode($output, JSON_PRETTY_PRINT) . PHP_EOL;
|
||||
}
|
||||
|
||||
private function generateReports(string $outputDir, string $repoPath): void
|
||||
{
|
||||
// Ensure output directory exists
|
||||
if (!is_dir($outputDir)) {
|
||||
@mkdir($outputDir, 0755, true);
|
||||
}
|
||||
|
||||
$timestamp = date('Ymd_His');
|
||||
|
||||
// Generate detection report
|
||||
$detectionReport = $outputDir . "/detection_report_{$timestamp}.md";
|
||||
$this->writeDetectionReport($detectionReport, $repoPath);
|
||||
|
||||
// Generate summary report
|
||||
$summaryReport = $outputDir . "/SUMMARY_{$timestamp}.md";
|
||||
$this->writeSummaryReport($summaryReport, $repoPath);
|
||||
|
||||
$this->log("Reports generated in: {$outputDir}", 'INFO');
|
||||
}
|
||||
|
||||
private function writeDetectionReport(string $file, string $repoPath): void
|
||||
{
|
||||
$content = "# Platform Detection Report\n\n";
|
||||
$content .= "**Generated**: " . date('Y-m-d H:i:s') . "\n";
|
||||
$content .= "**Repository**: {$repoPath}\n\n";
|
||||
|
||||
$content .= "## Detected Platform\n\n";
|
||||
$content .= "**Type**: " . strtoupper($this->detectedPlatform) . "\n";
|
||||
$content .= "**Confidence**: " . round($this->detectionResults[$this->detectedPlatform]['score'] * 100, 1) . "%\n";
|
||||
$content .= "**Schema**: {$this->schemaFile}\n\n";
|
||||
|
||||
$content .= "## Detection Indicators\n\n";
|
||||
foreach ($this->detectionResults[$this->detectedPlatform]['indicators'] as $indicator) {
|
||||
$content .= "- {$indicator}\n";
|
||||
}
|
||||
|
||||
$content .= "\n## All Platform Scores\n\n";
|
||||
foreach ($this->detectionResults as $platform => $data) {
|
||||
$percentage = round($data['score'] * 100, 1);
|
||||
$content .= "- **" . ucfirst($platform) . "**: {$percentage}%\n";
|
||||
}
|
||||
|
||||
@file_put_contents($file, $content);
|
||||
}
|
||||
|
||||
private function writeSummaryReport(string $file, string $repoPath): void
|
||||
{
|
||||
$content = "# Platform Detection Summary\n\n";
|
||||
$content .= "| Property | Value |\n";
|
||||
$content .= "|----------|-------|\n";
|
||||
$content .= "| Repository | {$repoPath} |\n";
|
||||
$content .= "| Platform | " . strtoupper($this->detectedPlatform) . " |\n";
|
||||
$content .= "| Confidence | " . round($this->detectionResults[$this->detectedPlatform]['score'] * 100, 1) . "% |\n";
|
||||
$content .= "| Schema | " . basename($this->schemaFile) . " |\n";
|
||||
$content .= "| Timestamp | " . date('Y-m-d H:i:s') . " |\n\n";
|
||||
|
||||
$content .= "## Next Steps\n\n";
|
||||
$content .= "1. Review detection indicators\n";
|
||||
$content .= "2. Validate repository against schema: {$this->schemaFile}\n";
|
||||
$content .= "3. Address any validation errors or warnings\n";
|
||||
|
||||
@file_put_contents($file, $content);
|
||||
}
|
||||
|
||||
private function findFiles(string $dir, string $pattern, int $maxDepth = 1): array
|
||||
{
|
||||
$files = [];
|
||||
$pattern = str_replace('*', '.*', $pattern);
|
||||
$pattern = str_replace('.', '\.', $pattern);
|
||||
|
||||
try {
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
$iterator->setMaxDepth($maxDepth);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile() && preg_match("/{$pattern}$/", $file->getFilename())) {
|
||||
$files[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Directory not accessible
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
private function getAbsolutePath(string $path): string
|
||||
{
|
||||
if (strlen($path) > 0 && $path[0] === '/') {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return getcwd() . '/' . $path;
|
||||
}
|
||||
}
|
||||
|
||||
// Run the application
|
||||
$app = new AutoDetectPlatform('auto_detect_platform', 'Automatically detect platform type and validate repository');
|
||||
exit($app->execute());
|
||||
@@ -1,317 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoStandards.Scripts.Validate
|
||||
* INGROUP: MokoStandards
|
||||
* REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
* PATH: /scripts/validate/check_repo_health.php
|
||||
* VERSION: 04.00.03
|
||||
* BRIEF: Repository health checker - PHP implementation
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use MokoStandards\Enterprise\{
|
||||
AuditLogger,
|
||||
CliFramework,
|
||||
MetricsCollector,
|
||||
UnifiedValidation
|
||||
};
|
||||
|
||||
/**
|
||||
* Repository Health Checker
|
||||
*
|
||||
* Performs comprehensive repository health checks
|
||||
*/
|
||||
class RepoHealthChecker extends CliFramework
|
||||
{
|
||||
private const DEFAULT_THRESHOLD = 70.0;
|
||||
|
||||
private AuditLogger $logger;
|
||||
private MetricsCollector $metrics;
|
||||
private UnifiedValidation $validator;
|
||||
|
||||
private array $results = [
|
||||
'categories' => [],
|
||||
'checks' => [],
|
||||
'score' => 0,
|
||||
'max_score' => 100,
|
||||
'percentage' => 0.0,
|
||||
'level' => 'unknown',
|
||||
];
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setDescription('Check repository health and compliance');
|
||||
$this->addArgument('--path', 'Repository path to check', '.');
|
||||
$this->addArgument('--threshold', 'Minimum health threshold (%)', '70');
|
||||
$this->addArgument('--json', 'Output results as JSON', false);
|
||||
}
|
||||
|
||||
protected function initialize(): void
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$this->logger = new AuditLogger('repo_health_checker');
|
||||
$this->metrics = new MetricsCollector();
|
||||
$this->validator = new UnifiedValidation();
|
||||
|
||||
$this->log('Repository health checker initialized');
|
||||
}
|
||||
|
||||
protected function run(): int
|
||||
{
|
||||
$path = $this->getArgument('--path');
|
||||
$threshold = (float)$this->getArgument('--threshold');
|
||||
$jsonOutput = $this->getArgument('--json');
|
||||
|
||||
$this->log("Checking repository health: {$path}");
|
||||
|
||||
// Run checks
|
||||
$this->runStructureChecks($path);
|
||||
$this->runDocumentationChecks($path);
|
||||
$this->runWorkflowChecks($path);
|
||||
$this->runSecurityChecks($path);
|
||||
|
||||
// Calculate scores
|
||||
$this->calculateScore();
|
||||
|
||||
// Output results
|
||||
if ($jsonOutput) {
|
||||
echo json_encode($this->results, JSON_PRETTY_PRINT) . PHP_EOL;
|
||||
} else {
|
||||
$this->displayResults();
|
||||
}
|
||||
|
||||
// Record metrics
|
||||
$this->metrics->setGauge('repo_health_score', $this->results['percentage']);
|
||||
$this->metrics->setGauge('repo_health_checks_passed',
|
||||
count(array_filter($this->results['checks'], fn($c) => $c['passed'])));
|
||||
|
||||
// Check threshold
|
||||
if ($this->results['percentage'] < $threshold) {
|
||||
$this->error(sprintf(
|
||||
"Health check failed: %.1f%% < %.1f%% threshold",
|
||||
$this->results['percentage'],
|
||||
$threshold
|
||||
));
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->log(sprintf(
|
||||
"Health check passed: %.1f%% >= %.1f%% threshold",
|
||||
$this->results['percentage'],
|
||||
$threshold
|
||||
));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function runStructureChecks(string $path): void
|
||||
{
|
||||
$category = 'structure';
|
||||
$this->results['categories'][$category] = [
|
||||
'name' => 'Repository Structure',
|
||||
'max_points' => 30,
|
||||
'earned_points' => 0,
|
||||
'checks_passed' => 0,
|
||||
'checks_failed' => 0,
|
||||
];
|
||||
|
||||
// Check README exists
|
||||
$this->addCheck($category, 'README.md exists',
|
||||
file_exists("{$path}/README.md"), 10);
|
||||
|
||||
// Check LICENSE exists
|
||||
$this->addCheck($category, 'LICENSE file exists',
|
||||
file_exists("{$path}/LICENSE"), 10);
|
||||
|
||||
// Check .gitignore exists
|
||||
$this->addCheck($category, '.gitignore exists',
|
||||
file_exists("{$path}/.gitignore"), 5);
|
||||
|
||||
// Check CHANGELOG exists
|
||||
$this->addCheck($category, 'CHANGELOG.md exists',
|
||||
file_exists("{$path}/CHANGELOG.md"), 5);
|
||||
}
|
||||
|
||||
private function runDocumentationChecks(string $path): void
|
||||
{
|
||||
$category = 'documentation';
|
||||
$this->results['categories'][$category] = [
|
||||
'name' => 'Documentation',
|
||||
'max_points' => 25,
|
||||
'earned_points' => 0,
|
||||
'checks_passed' => 0,
|
||||
'checks_failed' => 0,
|
||||
];
|
||||
|
||||
// Check docs directory exists
|
||||
$this->addCheck($category, 'docs/ directory exists',
|
||||
is_dir("{$path}/docs"), 10);
|
||||
|
||||
// Check README has content
|
||||
if (file_exists("{$path}/README.md")) {
|
||||
$content = file_get_contents("{$path}/README.md");
|
||||
$this->addCheck($category, 'README has substantial content',
|
||||
strlen($content) > 500, 10);
|
||||
}
|
||||
|
||||
// Check for code of conduct
|
||||
$this->addCheck($category, 'CODE_OF_CONDUCT.md exists',
|
||||
file_exists("{$path}/CODE_OF_CONDUCT.md"), 5);
|
||||
}
|
||||
|
||||
private function runWorkflowChecks(string $path): void
|
||||
{
|
||||
$category = 'workflows';
|
||||
$this->results['categories'][$category] = [
|
||||
'name' => 'GitHub Workflows',
|
||||
'max_points' => 20,
|
||||
'earned_points' => 0,
|
||||
'checks_passed' => 0,
|
||||
'checks_failed' => 0,
|
||||
];
|
||||
|
||||
$workflowDir = "{$path}/.github/workflows";
|
||||
|
||||
// Check workflows directory exists
|
||||
$this->addCheck($category, 'Workflows directory exists',
|
||||
is_dir($workflowDir), 10);
|
||||
|
||||
// Check for CI workflow
|
||||
if (is_dir($workflowDir)) {
|
||||
$hasCI = glob("{$workflowDir}/ci*.yml") || glob("{$workflowDir}/ci*.yaml");
|
||||
$this->addCheck($category, 'CI workflow exists',
|
||||
!empty($hasCI), 10);
|
||||
}
|
||||
}
|
||||
|
||||
private function runSecurityChecks(string $path): void
|
||||
{
|
||||
$category = 'security';
|
||||
$this->results['categories'][$category] = [
|
||||
'name' => 'Security',
|
||||
'max_points' => 25,
|
||||
'earned_points' => 0,
|
||||
'checks_passed' => 0,
|
||||
'checks_failed' => 0,
|
||||
];
|
||||
|
||||
// Check for SECURITY.md
|
||||
$this->addCheck($category, 'SECURITY.md exists',
|
||||
file_exists("{$path}/SECURITY.md") ||
|
||||
file_exists("{$path}/.github/SECURITY.md"), 10);
|
||||
|
||||
// Check for CodeQL workflow
|
||||
$workflowDir = "{$path}/.github/workflows";
|
||||
if (is_dir($workflowDir)) {
|
||||
$hasCodeQL = glob("{$workflowDir}/*codeql*.yml") ||
|
||||
glob("{$workflowDir}/*codeql*.yaml");
|
||||
$this->addCheck($category, 'CodeQL workflow exists',
|
||||
!empty($hasCodeQL), 10);
|
||||
}
|
||||
|
||||
// Check for dependabot
|
||||
$this->addCheck($category, 'Dependabot configured',
|
||||
file_exists("{$path}/.github/dependabot.yml") ||
|
||||
file_exists("{$path}/.github/dependabot.yaml"), 5);
|
||||
}
|
||||
|
||||
private function addCheck(string $category, string $name, bool $passed, int $points): void
|
||||
{
|
||||
$this->results['checks'][] = [
|
||||
'category' => $category,
|
||||
'name' => $name,
|
||||
'passed' => $passed,
|
||||
'points' => $points,
|
||||
];
|
||||
|
||||
if ($passed) {
|
||||
$this->results['categories'][$category]['earned_points'] += $points;
|
||||
$this->results['categories'][$category]['checks_passed']++;
|
||||
} else {
|
||||
$this->results['categories'][$category]['checks_failed']++;
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateScore(): void
|
||||
{
|
||||
$totalEarned = 0;
|
||||
$maxScore = 0;
|
||||
|
||||
foreach ($this->results['categories'] as $category) {
|
||||
$totalEarned += $category['earned_points'];
|
||||
$maxScore += $category['max_points'];
|
||||
}
|
||||
|
||||
$this->results['score'] = $totalEarned;
|
||||
$this->results['max_score'] = $maxScore;
|
||||
$this->results['percentage'] = $maxScore > 0 ? ($totalEarned / $maxScore * 100) : 0;
|
||||
|
||||
// Determine level
|
||||
$pct = $this->results['percentage'];
|
||||
if ($pct >= 90) {
|
||||
$this->results['level'] = 'excellent';
|
||||
} elseif ($pct >= 80) {
|
||||
$this->results['level'] = 'good';
|
||||
} elseif ($pct >= 70) {
|
||||
$this->results['level'] = 'fair';
|
||||
} elseif ($pct >= 60) {
|
||||
$this->results['level'] = 'poor';
|
||||
} else {
|
||||
$this->results['level'] = 'critical';
|
||||
}
|
||||
}
|
||||
|
||||
private function displayResults(): void
|
||||
{
|
||||
echo "\n=== Repository Health Check Results ===\n\n";
|
||||
|
||||
foreach ($this->results['categories'] as $catId => $category) {
|
||||
$pct = $category['max_points'] > 0
|
||||
? ($category['earned_points'] / $category['max_points'] * 100)
|
||||
: 0;
|
||||
|
||||
echo sprintf(
|
||||
"%s: %d/%d points (%.1f%%) - %d passed, %d failed\n",
|
||||
$category['name'],
|
||||
$category['earned_points'],
|
||||
$category['max_points'],
|
||||
$pct,
|
||||
$category['checks_passed'],
|
||||
$category['checks_failed']
|
||||
);
|
||||
}
|
||||
|
||||
echo sprintf(
|
||||
"\nOverall Score: %d/%d points (%.1f%%) - Level: %s\n",
|
||||
$this->results['score'],
|
||||
$this->results['max_score'],
|
||||
$this->results['percentage'],
|
||||
strtoupper($this->results['level'])
|
||||
);
|
||||
|
||||
// Show failed checks
|
||||
$failedChecks = array_filter($this->results['checks'], fn($c) => !$c['passed']);
|
||||
if (!empty($failedChecks)) {
|
||||
echo "\nFailed Checks:\n";
|
||||
foreach ($failedChecks as $check) {
|
||||
echo sprintf(" ❌ %s (%d points)\n", $check['name'], $check['points']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the application
|
||||
$app = new RepoHealthChecker();
|
||||
exit($app->execute($argv));
|
||||
@@ -1,339 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoStandards.Scripts
|
||||
* INGROUP: MokoStandards.Validation
|
||||
* REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
* PATH: /scripts/validate/check_version_consistency.php
|
||||
* VERSION: 04.00.03
|
||||
* BRIEF: Check for version number consistency across repository
|
||||
*
|
||||
* This script validates that version numbers are consistent across
|
||||
* all critical files in the repository to prevent version mismatches.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Configuration
|
||||
$repoRoot = dirname(__DIR__, 2);
|
||||
$expectedVersionFile = $repoRoot . '/composer.json';
|
||||
|
||||
// ANSI color codes
|
||||
const COLOR_RED = "\033[31m";
|
||||
const COLOR_GREEN = "\033[32m";
|
||||
const COLOR_YELLOW = "\033[33m";
|
||||
const COLOR_BLUE = "\033[34m";
|
||||
const COLOR_RESET = "\033[0m";
|
||||
const COLOR_BOLD = "\033[1m";
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*/
|
||||
function parseArguments(): array
|
||||
{
|
||||
global $argv;
|
||||
$options = [
|
||||
'verbose' => false,
|
||||
'help' => false,
|
||||
];
|
||||
|
||||
for ($i = 1; $i < count($argv); $i++) {
|
||||
switch ($argv[$i]) {
|
||||
case '--verbose':
|
||||
case '-v':
|
||||
$options['verbose'] = true;
|
||||
break;
|
||||
case '--help':
|
||||
case '-h':
|
||||
$options['help'] = true;
|
||||
break;
|
||||
default:
|
||||
echo COLOR_RED . "Unknown option: {$argv[$i]}" . COLOR_RESET . "\n";
|
||||
$options['help'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display help message
|
||||
*/
|
||||
function displayHelp(): void
|
||||
{
|
||||
echo COLOR_BOLD . "Version Consistency Checker" . COLOR_RESET . "\n\n";
|
||||
echo "Usage: php check_version_consistency.php [options]\n\n";
|
||||
echo "Options:\n";
|
||||
echo " -v, --verbose Enable verbose output\n";
|
||||
echo " -h, --help Display this help message\n\n";
|
||||
echo "Description:\n";
|
||||
echo " Checks for version number consistency across critical repository files.\n";
|
||||
echo " The expected version is read from composer.json.\n\n";
|
||||
echo "Exit Codes:\n";
|
||||
echo " 0 - All versions are consistent\n";
|
||||
echo " 1 - Version mismatches found\n";
|
||||
echo " 2 - Error reading files\n\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expected version from composer.json
|
||||
*/
|
||||
function getExpectedVersion(string $composerFile, bool $verbose): ?string
|
||||
{
|
||||
if (!file_exists($composerFile)) {
|
||||
echo COLOR_RED . "✗ composer.json not found at: $composerFile" . COLOR_RESET . "\n";
|
||||
return null;
|
||||
}
|
||||
|
||||
$content = file_get_contents($composerFile);
|
||||
$data = json_decode($content, true);
|
||||
|
||||
if (!isset($data['version'])) {
|
||||
echo COLOR_RED . "✗ Version not found in composer.json" . COLOR_RESET . "\n";
|
||||
return null;
|
||||
}
|
||||
|
||||
$version = $data['version'];
|
||||
if ($verbose) {
|
||||
echo COLOR_BLUE . "Expected version from composer.json: $version" . COLOR_RESET . "\n";
|
||||
}
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check version in a file with pattern
|
||||
*/
|
||||
function checkVersionInFile(string $file, string $pattern, string $expectedVersion, bool $verbose): array
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
return ['found' => false, 'error' => 'File not found'];
|
||||
}
|
||||
|
||||
$content = file_get_contents($file);
|
||||
$mismatches = [];
|
||||
|
||||
if (preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
foreach ($matches[1] as $match) {
|
||||
$foundVersion = $match[0];
|
||||
if ($foundVersion !== $expectedVersion) {
|
||||
$lineNum = substr_count(substr($content, 0, $match[1]), "\n") + 1;
|
||||
$mismatches[] = [
|
||||
'found' => $foundVersion,
|
||||
'expected' => $expectedVersion,
|
||||
'line' => $lineNum
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['found' => count($matches[0]) > 0, 'mismatches' => $mismatches];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check specific critical files
|
||||
*/
|
||||
function checkCriticalFiles(string $repoRoot, string $expectedVersion, bool $verbose): array
|
||||
{
|
||||
$issues = [];
|
||||
|
||||
// Check README.md
|
||||
$readmeFile = $repoRoot . '/README.md';
|
||||
if ($verbose) echo COLOR_BLUE . "Checking README.md..." . COLOR_RESET . "\n";
|
||||
|
||||
// Check VERSION header
|
||||
$result = checkVersionInFile($readmeFile, '/VERSION:\s*([\d.]+)/', $expectedVersion, $verbose);
|
||||
if (!empty($result['mismatches'])) {
|
||||
$issues[] = [
|
||||
'file' => 'README.md',
|
||||
'type' => 'VERSION header',
|
||||
'mismatches' => $result['mismatches']
|
||||
];
|
||||
}
|
||||
|
||||
// Check badge
|
||||
$result = checkVersionInFile($readmeFile, '/MokoStandards-([\d.]+)/', $expectedVersion, $verbose);
|
||||
if (!empty($result['mismatches'])) {
|
||||
$issues[] = [
|
||||
'file' => 'README.md',
|
||||
'type' => 'Version badge',
|
||||
'mismatches' => $result['mismatches']
|
||||
];
|
||||
}
|
||||
|
||||
// Check CHANGELOG.md
|
||||
$changelogFile = $repoRoot . '/CHANGELOG.md';
|
||||
if ($verbose) echo COLOR_BLUE . "Checking CHANGELOG.md..." . COLOR_RESET . "\n";
|
||||
|
||||
$result = checkVersionInFile($changelogFile, '/VERSION:\s*([\d.]+)/', $expectedVersion, $verbose);
|
||||
if (!empty($result['mismatches'])) {
|
||||
$issues[] = [
|
||||
'file' => 'CHANGELOG.md',
|
||||
'type' => 'VERSION header',
|
||||
'mismatches' => $result['mismatches']
|
||||
];
|
||||
}
|
||||
|
||||
// Check for current version in title
|
||||
$result = checkVersionInFile($changelogFile, '/CHANGELOG - MokoStandards \(VERSION:\s*([\d.]+)\)/', $expectedVersion, $verbose);
|
||||
if (!empty($result['mismatches'])) {
|
||||
$issues[] = [
|
||||
'file' => 'CHANGELOG.md',
|
||||
'type' => 'Title version',
|
||||
'mismatches' => $result['mismatches']
|
||||
];
|
||||
}
|
||||
|
||||
// Check CONTRIBUTING.md
|
||||
$contributingFile = $repoRoot . '/CONTRIBUTING.md';
|
||||
if ($verbose) echo COLOR_BLUE . "Checking CONTRIBUTING.md..." . COLOR_RESET . "\n";
|
||||
|
||||
$result = checkVersionInFile($contributingFile, '/VERSION:\s*([\d.]+)/', $expectedVersion, $verbose);
|
||||
if (!empty($result['mismatches'])) {
|
||||
$issues[] = [
|
||||
'file' => 'CONTRIBUTING.md',
|
||||
'type' => 'VERSION header',
|
||||
'mismatches' => $result['mismatches']
|
||||
];
|
||||
}
|
||||
|
||||
return $issues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check workflow files
|
||||
*/
|
||||
function checkWorkflowFiles(string $repoRoot, string $expectedVersion, bool $verbose): array
|
||||
{
|
||||
$issues = [];
|
||||
$workflowDir = $repoRoot . '/.github/workflows';
|
||||
|
||||
if (!is_dir($workflowDir)) {
|
||||
return $issues;
|
||||
}
|
||||
|
||||
$files = glob($workflowDir . '/*.yml');
|
||||
if ($verbose) echo COLOR_BLUE . "Checking " . count($files) . " workflow files..." . COLOR_RESET . "\n";
|
||||
|
||||
foreach ($files as $file) {
|
||||
$result = checkVersionInFile($file, '/#\s*VERSION:\s*([\d.]+)/', $expectedVersion, $verbose);
|
||||
if (!empty($result['mismatches'])) {
|
||||
$issues[] = [
|
||||
'file' => str_replace($repoRoot . '/', '', $file),
|
||||
'type' => 'Workflow VERSION comment',
|
||||
'mismatches' => $result['mismatches']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $issues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check PHP source files
|
||||
*/
|
||||
function checkPhpSourceFiles(string $repoRoot, string $expectedVersion, bool $verbose): array
|
||||
{
|
||||
$issues = [];
|
||||
$srcDir = $repoRoot . '/src';
|
||||
|
||||
if (!is_dir($srcDir)) {
|
||||
return $issues;
|
||||
}
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($srcDir, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
$phpFiles = [];
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile() && $file->getExtension() === 'php') {
|
||||
$phpFiles[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
if ($verbose) echo COLOR_BLUE . "Checking " . count($phpFiles) . " PHP source files..." . COLOR_RESET . "\n";
|
||||
|
||||
foreach ($phpFiles as $file) {
|
||||
$result = checkVersionInFile($file, '/VERSION:\s*([\d.]+)/', $expectedVersion, $verbose);
|
||||
if (!empty($result['mismatches'])) {
|
||||
$issues[] = [
|
||||
'file' => str_replace($repoRoot . '/', '', $file),
|
||||
'type' => 'PHP VERSION header',
|
||||
'mismatches' => $result['mismatches']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $issues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main execution
|
||||
*/
|
||||
function main(): int
|
||||
{
|
||||
global $repoRoot, $expectedVersionFile;
|
||||
|
||||
$options = parseArguments();
|
||||
|
||||
if ($options['help']) {
|
||||
displayHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
echo COLOR_BOLD . "=== MokoStandards Version Consistency Checker ===" . COLOR_RESET . "\n\n";
|
||||
|
||||
// Get expected version
|
||||
$expectedVersion = getExpectedVersion($expectedVersionFile, $options['verbose']);
|
||||
if ($expectedVersion === null) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
echo COLOR_GREEN . "✓ Expected version: $expectedVersion" . COLOR_RESET . "\n\n";
|
||||
|
||||
// Check critical files
|
||||
echo COLOR_BOLD . "Checking critical files..." . COLOR_RESET . "\n";
|
||||
$criticalIssues = checkCriticalFiles($repoRoot, $expectedVersion, $options['verbose']);
|
||||
|
||||
// Check workflow files
|
||||
echo COLOR_BOLD . "\nChecking workflow files..." . COLOR_RESET . "\n";
|
||||
$workflowIssues = checkWorkflowFiles($repoRoot, $expectedVersion, $options['verbose']);
|
||||
|
||||
// Check PHP source files
|
||||
echo COLOR_BOLD . "\nChecking PHP source files..." . COLOR_RESET . "\n";
|
||||
$phpIssues = checkPhpSourceFiles($repoRoot, $expectedVersion, $options['verbose']);
|
||||
|
||||
// Combine all issues
|
||||
$allIssues = array_merge($criticalIssues, $workflowIssues, $phpIssues);
|
||||
|
||||
// Report results
|
||||
echo "\n" . COLOR_BOLD . "=== Results ===" . COLOR_RESET . "\n\n";
|
||||
|
||||
if (empty($allIssues)) {
|
||||
echo COLOR_GREEN . "✓ All version numbers are consistent!" . COLOR_RESET . "\n";
|
||||
echo COLOR_GREEN . "✓ Expected version $expectedVersion found in all checked files." . COLOR_RESET . "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
echo COLOR_RED . "✗ Found " . count($allIssues) . " version mismatch(es):" . COLOR_RESET . "\n\n";
|
||||
|
||||
foreach ($allIssues as $issue) {
|
||||
echo COLOR_YELLOW . "File: " . $issue['file'] . COLOR_RESET . "\n";
|
||||
echo " Type: " . $issue['type'] . "\n";
|
||||
foreach ($issue['mismatches'] as $mismatch) {
|
||||
echo COLOR_RED . " Line " . $mismatch['line'] . ": Found " . $mismatch['found'] .
|
||||
" (expected " . $mismatch['expected'] . ")" . COLOR_RESET . "\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
echo COLOR_RED . "\n✗ Please update the version numbers in the files listed above." . COLOR_RESET . "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
exit(main());
|
||||
@@ -1,100 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# This file is part of a Moko Consulting project.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Shell wrapper template for Python scripts
|
||||
# This wrapper provides a convenient way to call Python scripts with proper error handling
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Script Configuration - UPDATE THESE FOR EACH WRAPPER
|
||||
SCRIPT_NAME="check_markdown_links"
|
||||
SCRIPT_PATH="scripts/validate/check_markdown_links.py"
|
||||
SCRIPT_CATEGORY="validation" # automation, validation, maintenance, etc.
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}✅ $1${NC}"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# Get repository root
|
||||
get_repo_root() {
|
||||
git rev-parse --show-toplevel 2>/dev/null || pwd
|
||||
}
|
||||
|
||||
# Check if Python is available
|
||||
check_python() {
|
||||
if command -v python3 &> /dev/null; then
|
||||
echo "python3"
|
||||
elif command -v python &> /dev/null; then
|
||||
echo "python"
|
||||
else
|
||||
log_error "Python is not installed or not in PATH"
|
||||
echo "Please install Python 3.7 or later"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
local repo_root
|
||||
repo_root=$(get_repo_root)
|
||||
|
||||
local python_cmd
|
||||
python_cmd=$(check_python)
|
||||
|
||||
local full_script_path="$repo_root/$SCRIPT_PATH"
|
||||
|
||||
# Check if script exists
|
||||
if [ ! -f "$full_script_path" ]; then
|
||||
log_error "Python script not found: $full_script_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup logging directory
|
||||
local log_dir="$repo_root/logs/$SCRIPT_CATEGORY"
|
||||
mkdir -p "$log_dir"
|
||||
|
||||
local timestamp
|
||||
timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||
local log_file="$log_dir/${SCRIPT_NAME}_${timestamp}.log"
|
||||
|
||||
# Execute Python script with all arguments
|
||||
log_info "Running $SCRIPT_NAME..."
|
||||
log_info "Log file: $log_file"
|
||||
|
||||
if "$python_cmd" "$full_script_path" "$@" 2>&1 | tee "$log_file"; then
|
||||
log_success "$SCRIPT_NAME completed successfully"
|
||||
exit 0
|
||||
else
|
||||
local exit_code=$?
|
||||
log_error "$SCRIPT_NAME failed with exit code: $exit_code"
|
||||
log_info "Check log file for details: $log_file"
|
||||
exit $exit_code
|
||||
fi
|
||||
}
|
||||
|
||||
# Run main with all arguments
|
||||
main "$@"
|
||||
@@ -1,100 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# This file is part of a Moko Consulting project.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Shell wrapper template for Python scripts
|
||||
# This wrapper provides a convenient way to call Python scripts with proper error handling
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Script Configuration - UPDATE THESE FOR EACH WRAPPER
|
||||
SCRIPT_NAME="rebuild_indexes"
|
||||
SCRIPT_PATH="scripts/docs/rebuild_indexes.py"
|
||||
SCRIPT_CATEGORY="docs" # automation, validation, maintenance, etc.
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}✅ $1${NC}"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# Get repository root
|
||||
get_repo_root() {
|
||||
git rev-parse --show-toplevel 2>/dev/null || pwd
|
||||
}
|
||||
|
||||
# Check if Python is available
|
||||
check_python() {
|
||||
if command -v python3 &> /dev/null; then
|
||||
echo "python3"
|
||||
elif command -v python &> /dev/null; then
|
||||
echo "python"
|
||||
else
|
||||
log_error "Python is not installed or not in PATH"
|
||||
echo "Please install Python 3.7 or later"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
local repo_root
|
||||
repo_root=$(get_repo_root)
|
||||
|
||||
local python_cmd
|
||||
python_cmd=$(check_python)
|
||||
|
||||
local full_script_path="$repo_root/$SCRIPT_PATH"
|
||||
|
||||
# Check if script exists
|
||||
if [ ! -f "$full_script_path" ]; then
|
||||
log_error "Python script not found: $full_script_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup logging directory
|
||||
local log_dir="$repo_root/logs/$SCRIPT_CATEGORY"
|
||||
mkdir -p "$log_dir"
|
||||
|
||||
local timestamp
|
||||
timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||
local log_file="$log_dir/${SCRIPT_NAME}_${timestamp}.log"
|
||||
|
||||
# Execute Python script with all arguments
|
||||
log_info "Running $SCRIPT_NAME..."
|
||||
log_info "Log file: $log_file"
|
||||
|
||||
if "$python_cmd" "$full_script_path" "$@" 2>&1 | tee "$log_file"; then
|
||||
log_success "$SCRIPT_NAME completed successfully"
|
||||
exit 0
|
||||
else
|
||||
local exit_code=$?
|
||||
log_error "$SCRIPT_NAME failed with exit code: $exit_code"
|
||||
log_info "Check log file for details: $log_file"
|
||||
exit $exit_code
|
||||
fi
|
||||
}
|
||||
|
||||
# Run main with all arguments
|
||||
main "$@"
|
||||
@@ -1,100 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# This file is part of a Moko Consulting project.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Shell wrapper template for Python scripts
|
||||
# This wrapper provides a convenient way to call Python scripts with proper error handling
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Script Configuration - UPDATE THESE FOR EACH WRAPPER
|
||||
SCRIPT_NAME="validate_file_headers"
|
||||
SCRIPT_PATH="scripts/maintenance/validate_file_headers.py"
|
||||
SCRIPT_CATEGORY="maintenance" # automation, validation, maintenance, etc.
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}✅ $1${NC}"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# Get repository root
|
||||
get_repo_root() {
|
||||
git rev-parse --show-toplevel 2>/dev/null || pwd
|
||||
}
|
||||
|
||||
# Check if Python is available
|
||||
check_python() {
|
||||
if command -v python3 &> /dev/null; then
|
||||
echo "python3"
|
||||
elif command -v python &> /dev/null; then
|
||||
echo "python"
|
||||
else
|
||||
log_error "Python is not installed or not in PATH"
|
||||
echo "Please install Python 3.7 or later"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
local repo_root
|
||||
repo_root=$(get_repo_root)
|
||||
|
||||
local python_cmd
|
||||
python_cmd=$(check_python)
|
||||
|
||||
local full_script_path="$repo_root/$SCRIPT_PATH"
|
||||
|
||||
# Check if script exists
|
||||
if [ ! -f "$full_script_path" ]; then
|
||||
log_error "Python script not found: $full_script_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup logging directory
|
||||
local log_dir="$repo_root/logs/$SCRIPT_CATEGORY"
|
||||
mkdir -p "$log_dir"
|
||||
|
||||
local timestamp
|
||||
timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||
local log_file="$log_dir/${SCRIPT_NAME}_${timestamp}.log"
|
||||
|
||||
# Execute Python script with all arguments
|
||||
log_info "Running $SCRIPT_NAME..."
|
||||
log_info "Log file: $log_file"
|
||||
|
||||
if "$python_cmd" "$full_script_path" "$@" 2>&1 | tee "$log_file"; then
|
||||
log_success "$SCRIPT_NAME completed successfully"
|
||||
exit 0
|
||||
else
|
||||
local exit_code=$?
|
||||
log_error "$SCRIPT_NAME failed with exit code: $exit_code"
|
||||
log_info "Check log file for details: $log_file"
|
||||
exit $exit_code
|
||||
fi
|
||||
}
|
||||
|
||||
# Run main with all arguments
|
||||
main "$@"
|
||||
@@ -3,7 +3,7 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - MokoJoomTOS"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when the site is in offline mode. Simply configure the menu slug (e.g., 'terms-of-service') and that page will remain accessible even when the site is offline."
|
||||
|
||||
; Configuration
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - MokoJoomTOS"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when site is offline"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - MokoJoomTOS"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when the site is in offline mode. Simply configure the menu slug (e.g., 'terms-of-service') and that page will remain accessible even when the site is offline."
|
||||
|
||||
; Configuration
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - MokoJoomTOS"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when site is offline"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - MokoJoomTOS"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when the site is in offline mode. Simply configure the menu slug (e.g., 'terms-of-service') and that page will remain accessible even when the site is offline."
|
||||
|
||||
; Configuration
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - MokoJoomTOS"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when the site is in offline mode. Simply configure the menu slug (e.g., 'terms-of-service') and that page will remain accessible even when the site is offline."
|
||||
|
||||
; Configuration
|
||||
|
||||
@@ -77,9 +77,10 @@ class MenuslugField extends ListField
|
||||
$lastMenuType = $item->menutype;
|
||||
}
|
||||
|
||||
$displayText = $item->title !== '' ? $item->title : ucwords(str_replace(['-', '_'], ' ', $item->alias));
|
||||
$options[] = (object) [
|
||||
'value' => $item->alias,
|
||||
'text' => $item->title . ' (' . $item->alias . ')'
|
||||
'text' => $displayText . ' (' . $item->alias . ')'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user