Files
MokoCLI/definitions/sync/MokoDoliMulti.def.tf
T
Jonathan Miller 38a975ee57
Branch Policy Check / Verify merge target (pull_request) Has been cancelled
chore: remove VERSION from all file header comments
Remove VERSION: XX.YY.ZZ lines from 213 file headers across PHP,
TypeScript, TF definitions, workflows, CSS, markdown, and XML files.
Version is tracked in composer.json and CHANGELOG.md only.

Authored-by: Moko Consulting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 16:51:00 -05:00

1335 lines
55 KiB
Terraform
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Repository Sync Tracking Definition: mokoconsulting-tech/MokoDoliMulti
*
* Auto-generated by MokoStandards bulk sync on 2026-04-02T15:21:37+00:00
* Platform : crm-module
* Description: Domainbased multitenant orchestration for Dolibarr using one codebase and pertenant configuration, documents, and databases mapped by virtual host.
*
* DO NOT EDIT MANUALLY — this file is regenerated on every successful sync.
* To change what gets synced, edit api/definitions/default/crm-module.tf
* and re-run the bulk-repo-sync workflow.
*/
locals {
sync_record = {
metadata = {
repo = "mokoconsulting-tech/MokoDoliMulti"
default_branch = "main"
detected_platform = "crm-module"
description = "Domainbased multitenant orchestration for Dolibarr using one codebase and pertenant configuration, documents, and databases mapped by virtual host."
sync_timestamp = "2026-04-02T15:21:37+00:00"
source_repo = "mokoconsulting-tech/MokoStandards"
base_definition = "api/definitions/default/crm-module.tf"
}
sync_stats = {
total_files = 45
created_files = 5
updated_files = 35
skipped_files = 5
}
synced_files = [
{ path = "CONTRIBUTING.md" action = "updated" },
{ path = "LICENSE" action = "updated" },
{ path = "composer.json" action = "updated" },
{ path = "phpstan.neon" action = "updated" },
{ path = "Makefile" action = "updated" },
{ path = "src/.ftpignore" action = "updated" },
{ path = ".mokostandards" action = "created" },
{ path = "docs/update-server.md" action = "created" },
{ path = ".github/copilot.yml" action = "updated" },
{ path = ".github/copilot-instructions.md" action = "updated" },
{ path = ".github/CLAUDE.md" action = "updated" },
{ path = ".github/workflows/ci-dolibarr.yml" action = "updated" },
{ path = ".github/workflows/codeql-analysis.yml" action = "updated" },
{ path = ".github/workflows/standards-compliance.yml" action = "updated" },
{ path = ".github/workflows/enterprise-firewall-setup.yml" action = "updated" },
{ path = ".github/workflows/deploy-dev.yml" action = "updated" },
{ path = ".github/workflows/deploy-demo.yml" action = "updated" },
{ path = ".github/workflows/deploy-rs.yml" action = "updated" },
{ path = ".github/workflows/publish-to-mokodolimods.yml" action = "created" },
{ path = ".github/workflows/sync-version-on-merge.yml" action = "updated" },
{ path = ".github/workflows/auto-release.yml" action = "updated" },
{ path = ".github/workflows/repository-cleanup.yml" action = "updated" },
{ path = ".github/workflows/auto-dev-issue.yml" action = "updated" },
{ path = ".github/workflows/repo_health.yml" action = "created" },
{ path = ".github/ISSUE_TEMPLATE/config.yml" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/adr.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/bug_report.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/documentation.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/enterprise_support.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/feature_request.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/firewall-request.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/question.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/request-license.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/rfc.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/security.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/dolibarr_issue.md" action = "updated" },
{ path = ".github/ISSUE_TEMPLATE/dolibarr_module_id_request.md" action = "updated" },
{ path = "src/img/object_favicon_256.png" action = "updated" },
{ path = ".github/CODEOWNERS" action = "updated" },
{ path = "update.txt" action = "created" },
{ path = ".github/.mokostandards" action = "migrated from root" },
]
skipped_files = [
{ path = "README.md" reason = "README — never overwritten" },
{ path = "src/ChangeLog.md" reason = "CHANGELOG — never overwritten" },
{ path = "GOVERNANCE.md" reason = "Preserved (always_overwrite=false)" },
{ path = "src/README.md" reason = "README — never overwritten" },
{ path = ".github/workflows/custom/README.md" reason = "README — never overwritten" },
]
}
}
# ---- Base platform definition (reference copy) ----
/**
* MokoCRM Module Structure Definition
* Standard repository structure for MokoCRM (Dolibarr) modules
*
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Schema Version: 1.0
*/
locals {
repository_structure = {
metadata = {
name = "MokoCRM Module"
description = "Standard repository structure for MokoCRM (Dolibarr) modules"
repository_type = "crm-module"
platform = "dolibarr"
last_updated = "2026-01-07T00:00:00Z"
maintainer = "Moko Consulting"
version = "05.00.00"
schema_version = "1.0"
}
root_files = [
{
name = "README.md"
extension = "md"
description = "Developer-focused documentation for contributors and maintainers"
required = true
always_overwrite = false
protected = true
audience = "developer"
stub_content = <<-EOT
# {MODULE_NAME}
## For Developers
This README is for developers contributing to this module.
### Development Setup
1. Clone this repository
2. Install dependencies: `make install-dev`
3. Run tests: `make test`
### Building
```bash
make build
```
### Testing
```bash
make test
make lint
```
### Contributing
See CONTRIBUTING.md for contribution guidelines.
## For End Users
End user documentation is available in `src/README.md` after installation.
## License
See LICENSE file for details.
EOT
},
{
name = "CONTRIBUTING.md"
extension = "md"
description = "Contribution guidelines"
required = true
always_overwrite = true
template = "templates/docs/required/template-CONTRIBUTING.md"
audience = "contributor"
},
{
name = "ROADMAP.md"
extension = "md"
description = "Project roadmap with version goals and milestones"
required = false
audience = "general"
},
{
name = "LICENSE"
extension = ""
description = "License file (GPL-3.0-or-later) - Default for Dolibarr/CRM modules"
required = true
audience = "general"
template = "templates/licenses/GPL-3.0"
license_type = "GPL-3.0-or-later"
},
{
name = "ChangeLog.md"
extension = "md"
description = "Version history and changes"
required = true
always_overwrite = false
audience = "general"
destination_path = "src"
stub_content = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n### Added\n- Initial release\n"
},
{
name = "composer.json"
extension = "json"
description = "Composer manifest — requires mokoconsulting-tech/enterprise for CLI scripts and tooling"
required = true
always_overwrite = false
audience = "developer"
template = "templates/configs/composer.dolibarr.json"
},
{
name = "phpstan.neon"
extension = "neon"
description = "PHPStan static analysis config with Dolibarr class stubs"
required = true
always_overwrite = true
audience = "developer"
template = "templates/configs/phpstan.dolibarr.neon"
},
{
name = "Makefile"
description = "Build automation using MokoStandards templates"
required = true
always_overwrite = true
audience = "developer"
source_path = "templates/makefiles"
source_filename = "Makefile.dolibarr.template"
source_type = "template"
destination_path = "."
destination_filename = "Makefile"
create_path = false
template = "templates/makefiles/Makefile.dolibarr.template"
},
{
name = ".editorconfig"
extension = "editorconfig"
description = "Editor configuration for consistent coding style"
required = true
audience = "developer"
},
{
name = ".gitignore"
extension = "gitignore"
description = "Git ignore patterns - preserved during sync operations"
required = true
always_overwrite = false
audience = "developer"
},
{
name = "src/.ftpignore"
description = "SFTP upload exclusion rules — gitignore syntax"
required = true
always_overwrite = false
audience = "developer"
template = "templates/configs/ftp_ignore"
},
{
name = ".gitattributes"
extension = "gitattributes"
description = "Git attributes configuration"
required = true
audience = "developer"
},
{
name = ".mokostandards"
extension = "yml"
description = "MokoStandards governance attachment — links this repo back to the standards source"
required = true
always_overwrite = true
audience = "developer"
template = "templates/configs/mokostandards.yml.template"
},
{
name = "GOVERNANCE.md"
extension = "md"
description = "Project governance rules, roles, and decision process — auto-maintained by MokoStandards"
required = true
always_overwrite = false
protected = true
audience = "all"
template = "templates/docs/required/GOVERNANCE.md"
}
]
directories = [
{
name = "src"
path = "src"
description = "Module source code for deployment"
required = true
purpose = "Contains the actual module code that gets deployed to Dolibarr"
files = [
{
name = "README.md"
extension = "md"
description = "End-user documentation deployed with the module"
required = true
always_overwrite = false
protected = true
audience = "end-user"
stub_content = <<-EOT
# {MODULE_NAME}
## For End Users
This module provides {MODULE_DESCRIPTION}.
### Installation
1. Navigate to Home → Setup → Modules/Applications
2. Find "{MODULE_NAME}" in the list
3. Click "Activate"
### Configuration
After activation, configure the module:
1. Go to Home → Setup → Modules/Applications
2. Click on the module settings icon
3. Configure as needed
### Usage
{USAGE_INSTRUCTIONS}
### Support
For support, contact: {SUPPORT_EMAIL}
## Version
Current version: {VERSION}
See CHANGELOG.md for version history.
EOT
},
{
name = "core/modules/mod{ModuleName}.class.php"
extension = "php"
description = "Main module descriptor file"
required = true
audience = "developer"
}
]
subdirectories = [
{
name = "core"
path = "src/core"
description = "Core module files"
required = true
},
{
name = "langs"
path = "src/langs"
description = "Language translation files"
required = true
},
{
name = "sql"
path = "src/sql"
description = "Database schema files"
requirement_status = "suggested"
},
{
name = "css"
path = "src/css"
description = "Stylesheets"
requirement_status = "suggested"
},
{
name = "js"
path = "src/js"
description = "JavaScript files"
requirement_status = "suggested"
},
{
name = "class"
path = "src/class"
description = "PHP class files"
requirement_status = "suggested"
},
{
name = "lib"
path = "src/lib"
description = "Library files"
requirement_status = "suggested"
}
]
},
{
name = "docs"
path = "docs"
description = "Developer and technical documentation"
required = true
purpose = "Contains technical documentation, API docs, architecture diagrams"
files = [
{
name = "index.md"
extension = "md"
description = "Documentation index"
required = true
},
{
name = "update-server.md"
extension = "md"
description = "Developer guide for Dolibarr module update checks using update.txt"
required = true
always_overwrite = true
template = "templates/docs/required/template-update-server-dolibarr.md"
}
]
},
{
name = "scripts"
path = "scripts"
description = "Repo-specific build and deployment scripts (not synced)"
required = true
purpose = "Contains scripts written specifically for this repository — CI helpers, deploy hooks, etc. MokoStandards does not sync files into this directory."
files = [
{
name = "index.md"
extension = "md"
description = "Scripts documentation"
requirement_status = "required"
},
{
name = "MokoStandards.override.xml"
extension = "xml"
description = "MokoStandards sync override configuration"
requirement_status = "optional"
always_overwrite = false
}
]
},
{
name = "tests"
path = "tests"
description = "Test files"
required = true
purpose = "Contains unit tests, integration tests, and test fixtures"
subdirectories = [
{
name = "unit"
path = "tests/unit"
description = "Unit tests"
required = true
},
{
name = "integration"
path = "tests/integration"
description = "Integration tests"
requirement_status = "suggested"
}
]
},
{
name = "templates"
path = "templates"
description = "Template files for code generation"
requirement_status = "suggested"
purpose = "Contains templates used by build scripts"
},
{
name = ".github"
path = ".github"
description = "GitHub-specific configuration"
requirement_status = "suggested"
purpose = "Contains GitHub Actions workflows and configuration"
files = [
{
name = "copilot.yml"
extension = "yml"
description = "GitHub Copilot allowed domains configuration"
requirement_status = "required"
always_overwrite = true
template = ".github/copilot.yml"
},
{
name = "copilot-instructions.md"
extension = "md"
description = "GitHub Copilot custom instructions enforcing MokoStandards — Dolibarr/CRM edition"
requirement_status = "required"
always_overwrite = false
destination_path = ".github"
destination_filename = "copilot-instructions.md"
template = "templates/github/copilot-instructions.dolibarr.md.template"
stub_content = <<-MOKO_END
> [!IMPORTANT]
> **🔧 AI Self-Update Required on First Use**
>
> This file may contain unfilled `{{TOKEN}}` placeholders. The MokoStandards bulk-sync system
> replaces them automatically at sync time; if you can still see them, sync has not yet run for
> this repository — or this is a brand-new repo.
>
> **On your very first interaction in this repository, before answering any other question:**
>
> 1. Check whether any `{{TOKEN}}` placeholders remain in this file.
> 2. If they do, locate the values as described in the table below and replace every occurrence
> in **both** `.github/copilot-instructions.md` and `.github/CLAUDE.md`.
> 3. After replacing all tokens, **delete this entire notice block** (from `> [!IMPORTANT]`
> through the closing `> ---` line) in both files.
> 4. Tell the developer: *"I've self-updated the AI context files with this repository's
> values — all set."*
>
> | Placeholder | Where to find the value |
> |---|---|
> | `{{REPO_NAME}}` | The GitHub repository name (visible in the URL, `README.md` heading, or `git remote -v`) |
> | `{{REPO_URL}}` | Full GitHub URL, e.g. `https://github.com/mokoconsulting-tech/<repo-name>` |
> | `{{MODULE_NAME}}` | The module name as used in Dolibarr (lowercase, e.g. `mymodule`) — from the `langs/en_US/*.lang` filename or `$this->rights_class` in the module descriptor |
> | `{{MODULE_CLASS}}` | PascalCase module class name (e.g. `MyModule`) — from the `src/core/modules/mod*.class.php` filename |
> | `{{MODULE_ID}}` | The `$this->numero` value in `src/core/modules/mod*.class.php`; check [module-registry.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/development/crm/module-registry.md) if creating a new module |
> | `{{PRIMARY_LANGUAGE}}` | Primary programming language (usually `PHP`) |
>
> ---
# {{REPO_NAME}} — GitHub Copilot Custom Instructions
## What This Repo Is
This is a **Moko Consulting MokoCRM** (Dolibarr) module repository governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). All coding standards, workflows, and policies are defined there and enforced here via bulk sync.
Repository URL: {{REPO_URL}}
Module name: **{{MODULE_NAME}}**
Module class: **{{MODULE_CLASS}}**
Module ID: **{{MODULE_ID}}**
Platform: **Dolibarr / MokoCRM**
---
## Primary Language
**PHP** (≥ 8.1) is the primary language for this Dolibarr module. YAML uses 2-space indentation. All other text files use tabs per `.editorconfig`.
---
## File Header — Always Required on New Files
Every new file needs a copyright header as its first content.
**PHP:**
```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: {{REPO_NAME}}.Module
* INGROUP: {{REPO_NAME}}
* REPO: {{REPO_URL}}
* PATH: /src/path/to/file.php
* VERSION: XX.YY.ZZ
* BRIEF: One-line description of purpose
*/
```
**Markdown:**
```markdown
<!--
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: {{REPO_NAME}}.Documentation
INGROUP: {{REPO_NAME}}
REPO: {{REPO_URL}}
PATH: /docs/file.md
VERSION: XX.YY.ZZ
BRIEF: One-line description
-->
```
**YAML / Shell:** Use `#` comments with the same fields. JSON files are exempt.
---
## Version Management
**`README.md` is the single source of truth for the repository version.**
- **Bump the patch version on every PR** — increment `XX.YY.ZZ` (e.g. `01.02.03` → `01.02.04`) in `README.md` before opening the PR; the `sync-version-on-merge` workflow propagates it automatically to all badges and `FILE INFORMATION` headers on merge to `main`.
- The `VERSION: XX.YY.ZZ` field in `README.md` governs all other version references.
- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`).
- Never hardcode a specific version in document body text — use the badge or FILE INFORMATION header only.
### Dolibarr Module Version Alignment
The version in `README.md` **must always match** the `$this->version` property in the main module descriptor class (`src/core/modules/mod{{MODULE_CLASS}}.class.php`).
- On `dev/**` branches: `$this->version = 'development'` (set automatically by deploy-dev workflow)
- On merge to main: `$this->version` is set to the real version by the auto-release workflow
- **Never manually set `$this->version`** — the workflows handle it
### Module Update Server (update.txt)
Every Dolibarr module must wire up `$this->url_last_version` so the admin panel can check for updates.
**In `src/core/modules/mod{{MODULE_CLASS}}.class.php` constructor**, add:
```php
$this->version = 'development';
$this->url_last_version = 'https://raw.githubusercontent.com/mokoconsulting-tech/{{REPO_NAME}}/main/update.txt';
```
**How it works:**
1. The `auto-release.yml` workflow writes `update.txt` to the repo root on every GitHub Release
2. `update.txt` contains the latest version from `README.md`
3. Dolibarr fetches `$this->url_last_version` and compares against the installed version
**Add this method** to the module descriptor to parse the JSON response:
```php
public function getLatestVersion(): string
{
if (empty($this->url_last_version)) {
return '';
}
$content = @file_get_contents($this->url_last_version);
if ($content === false) {
return '';
}
$data = json_decode($content, true);
return $data['version'] ?? '';
}
```
**update.txt format** (auto-generated, do not edit manually):
```json
{
"version": "01.02.03",
"tag": "v01.02.03",
"repo": "mokoconsulting-tech/{{REPO_NAME}}",
"release_url": "https://github.com/mokoconsulting-tech/{{REPO_NAME}}/releases/tag/v01.02.03",
"updated": "2026-03-27T00:00:00Z"
}
```
Full guide: [docs/guide/crm/dolibarr-update-check.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/guide/crm/dolibarr-update-check.md)
---
## Dolibarr Module Structure
```
{{REPO_NAME}}/
├── src/ # Module source code (deployed to Dolibarr)
│ ├── README.md # End-user documentation
│ ├── core/
│ │ └── modules/
│ │ └── mod{{MODULE_CLASS}}.class.php # Main module descriptor
│ ├── langs/
│ │ └── en_US/
│ │ └── {{MODULE_NAME}}.lang
│ ├── sql/ # Database schema
│ │ ├── llx_{{MODULE_NAME}}.sql
│ │ └── llx_{{MODULE_NAME}}.key.sql
│ ├── class/ # PHP class files
│ └── lib/ # Library files
├── docs/ # Technical documentation
├── scripts/ # Build and maintenance scripts
├── tests/ # Test suite
├── .github/
│ ├── workflows/
│ ├── copilot-instructions.md # This file
│ └── CLAUDE.md
├── README.md # Version source of truth
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE # GPL-3.0-or-later
└── Makefile # Build automation
```
---
## Module Descriptor Class Pattern
The main module descriptor (`src/core/modules/mod{{MODULE_CLASS}}.class.php`) must follow this pattern:
```php
<?php
/* … file header … */
/**
* Description of module {{MODULE_NAME}}
*/
class mod{{MODULE_CLASS}} extends DolibarrModules
{
/**
* Constructor
*
* @param DoliDB $db Database handler
*/
public function __construct($db)
{
global $langs, $conf;
$this->db = $db;
$this->numero = {{MODULE_ID}}; // Unique module ID — do not change
$this->rights_class = '{{MODULE_NAME}}';
$this->family = 'crm';
$this->module_position = '50';
$this->name = preg_replace('/^mod/i', '', get_class($this));
$this->description = 'Description of {{MODULE_NAME}} module';
$this->version = 'XX.YY.ZZ'; // Must match README.md version
$this->const_name = 'MAIN_MODULE_' . strtoupper($this->name);
$this->picto = 'object_favicon_256.png@mokocrm';
$this->editor_name = 'Moko Consulting';
$this->editor_url = 'https://mokoconsulting.tech'; // Must be an external online web site
$this->editor_squarred_logo = 'object_favicon_256.png@mokocrm';
}
}
```
**Key rules for the module descriptor:**
- `$this->numero` is a globally unique ID registered in [module-registry.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/development/crm/module-registry.md) — **never change it**.
- `$this->version` must exactly match the version in `README.md`.
- Register new modules in the module registry before using any ID.
---
## GitHub Actions — Token Usage
Every workflow must use **`secrets.GH_TOKEN`** (the org-level Personal Access Token).
```yaml
# ✅ Correct
- uses: actions/checkout@v4
with:
token: ${{ secrets.GH_TOKEN }}
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
```
```yaml
# ❌ Wrong — never use these in workflows
token: ${{ github.token }}
token: ${{ secrets.GITHUB_TOKEN }}
```
PHP scripts read the token with: `getenv('GH_TOKEN') ?: getenv('GITHUB_TOKEN')` — `GH_TOKEN` is always preferred; `GITHUB_TOKEN` is a local-dev fallback only.
---
## MokoStandards Reference
This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). Authoritative policies:
| Document | Purpose |
|----------|---------|
| [file-header-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/file-header-standards.md) | Copyright-header rules for every file type |
| [coding-style-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md) | Naming and formatting conventions |
| [branching-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/branching-strategy.md) | Branch naming, hierarchy, and release workflow |
| [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) | Squash-merge policy and PR title/body conventions |
| [changelog-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/changelog-standards.md) | How and when to update CHANGELOG.md |
| [module-registry.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/development/crm/module-registry.md) | Dolibarr module ID registry — check before reserving a new ID |
| [crm-development-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/crm/development-standards.md) | MokoCRM Dolibarr module development standards |
---
## Naming Conventions
| Context | Convention | Example |
|---------|-----------|---------|
| PHP class | `PascalCase` | `MyService` |
| PHP method / function | `camelCase` | `getUserData()` |
| PHP variable | `$snake_case` | `$module_name` |
| PHP constant | `UPPER_SNAKE_CASE` | `MAX_RETRIES` |
| PHP class file | `PascalCase.php` | `ApiClient.php` |
| PHP script file | `snake_case.php` | `check_health.php` |
| YAML workflow | `kebab-case.yml` | `ci-dolibarr.yml` |
| Markdown doc | `kebab-case.md` | `installation-guide.md` |
---
## Commit Messages
Format: `<type>(<scope>): <subject>` — imperative, lower-case subject, no trailing period.
Valid types: `feat` · `fix` · `docs` · `chore` · `ci` · `refactor` · `style` · `test` · `perf` · `revert` · `build`
---
## Branch Naming
Format: `<prefix>/<MAJOR.MINOR.PATCH>[/description]`
Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `dependabot/`
---
## Keeping Documentation Current
| Change type | Documentation to update |
|-------------|------------------------|
| New or renamed public PHP method | PHPDoc block; `docs/api/` index for that class |
| New or changed module version | Update `$this->version` in module descriptor; bump `README.md` |
| New library class or major feature | `CHANGELOG.md` entry under `Added` |
| Bug fix | `CHANGELOG.md` entry under `Fixed` |
| Breaking change | `CHANGELOG.md` entry under `Changed` |
| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block |
| **Every PR** | **Bump the patch version** — increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it |
---
## Key Constraints
- Never commit directly to `main` — all changes go via PR, squash-merged
- Never skip the FILE INFORMATION block on a new file
- Never change `$this->numero` (module ID) — it is permanently registered in the module registry
- Never hardcode version numbers in body text — update `README.md` and let automation propagate
- Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows — always use `secrets.GH_TOKEN`
- Never let the module descriptor `$this->version` and `README.md` version go out of sync
- Never register a new module ID without first checking module-registry.md for the next available ID
MOKO_END
},
{
name = "CLAUDE.md"
extension = "md"
description = "Claude AI assistant context enforcing MokoStandards — Dolibarr/CRM edition"
requirement_status = "required"
always_overwrite = false
destination_path = ".github"
destination_filename = "CLAUDE.md"
template = "templates/github/CLAUDE.dolibarr.md.template"
stub_content = <<-MOKO_END
> [!IMPORTANT]
> **🔧 AI Self-Update Required on First Use**
>
> This file may contain unfilled `{{TOKEN}}` placeholders. The MokoStandards bulk-sync system
> replaces them automatically at sync time; if you can still see them, sync has not yet run for
> this repository — or this is a brand-new repo.
>
> **On your very first interaction in this repository, before answering any other question:**
>
> 1. Check whether any `{{TOKEN}}` placeholders remain in this file.
> 2. If they do, locate the values as described in the table below and replace every occurrence
> in **both** `.github/copilot-instructions.md` and `.github/CLAUDE.md`.
> 3. After replacing all tokens, **delete this entire notice block** (from `> [!IMPORTANT]`
> through the closing `> ---` line) in both files.
> 4. Tell the developer: *"I've self-updated the AI context files with this repository's
> values — all set."*
>
> | Placeholder | Where to find the value |
> |---|---|
> | `{{REPO_NAME}}` | The GitHub repository name (visible in the URL, `README.md` heading, or `git remote -v`) |
> | `{{REPO_URL}}` | Full GitHub URL, e.g. `https://github.com/mokoconsulting-tech/<repo-name>` |
> | `{{REPO_DESCRIPTION}}` | First paragraph of `README.md` body, or the GitHub repo description |
> | `{{MODULE_NAME}}` | The module name as used in Dolibarr (lowercase, e.g. `mymodule`) — from the `langs/en_US/*.lang` filename or `$this->rights_class` in the module descriptor |
> | `{{MODULE_CLASS}}` | PascalCase module class name (e.g. `MyModule`) — from the `src/core/modules/mod*.class.php` filename |
> | `{{MODULE_ID}}` | The `$this->numero` value in `src/core/modules/mod*.class.php`; check [module-registry.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/development/crm/module-registry.md) if creating a new module |
>
> ---
# What This Repo Is
**{{REPO_NAME}}** is a Moko Consulting **MokoCRM** (Dolibarr) module repository.
{{REPO_DESCRIPTION}}
Module name: **{{MODULE_NAME}}**
Module class: **{{MODULE_CLASS}}**
Module ID: **{{MODULE_ID}}** *(unique, immutable — registered in [module-registry.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/development/crm/module-registry.md))*
Repository URL: {{REPO_URL}}
This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) — the single source of truth for coding standards, file-header policies, GitHub Actions workflows, and Terraform configuration templates across all Moko Consulting repositories.
---
# Repo Structure
```
{{REPO_NAME}}/
├── src/ # Module source (deployed to Dolibarr)
│ ├── README.md # End-user documentation
│ ├── core/
│ │ └── modules/
│ │ └── mod{{MODULE_CLASS}}.class.php # Main module descriptor
│ ├── langs/
│ │ └── en_US/{{MODULE_NAME}}.lang
│ ├── sql/ # Database schema
│ ├── class/ # PHP class files
│ └── lib/ # Library files
├── docs/ # Technical documentation
├── scripts/ # Build and maintenance scripts
├── tests/ # Test suite
│ ├── unit/
│ └── integration/
├── .github/
│ ├── workflows/ # CI/CD workflows (synced from MokoStandards)
│ ├── copilot-instructions.md
│ └── CLAUDE.md # This file
├── README.md # Version source of truth
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE # GPL-3.0-or-later
└── Makefile # Build automation
```
---
# Primary Language
**PHP** (≥ 8.1) is the primary language for this Dolibarr module. YAML uses 2-space indentation. All other text files use tabs per `.editorconfig`.
---
# Version Management
**`README.md` is the single source of truth for the repository version.**
- **Bump the patch version on every PR** — increment `XX.YY.ZZ` (e.g. `01.02.03` → `01.02.04`) in `README.md` before opening the PR; the `sync-version-on-merge` workflow propagates it to all `FILE INFORMATION` headers automatically on merge.
- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`).
- Never hardcode a version number in body text — use the badge or FILE INFORMATION header only.
### Dolibarr Version Alignment
Two artefacts must always carry the same version:
| Artefact | Location |
|----------|----------|
| `README.md` | `FILE INFORMATION VERSION` field + badge |
| Module descriptor | `$this->version` in `src/core/modules/mod{{MODULE_CLASS}}.class.php` |
---
# Module Descriptor Class
The file `src/core/modules/mod{{MODULE_CLASS}}.class.php` is the Dolibarr module descriptor. The key properties:
```php
public $numero = {{MODULE_ID}}; // IMMUTABLE — never change; registered globally
public $version = 'XX.YY.ZZ'; // Must match README.md version exactly
```
**`$numero` is permanent.** It was registered in [module-registry.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/development/crm/module-registry.md) when this module was created. Changing it would break all Dolibarr installations that have this module activated.
Before creating a new module, always check the registry for the next available ID.
---
# File Header Requirements
Every new file **must** have a copyright header as its first content. JSON files, binary files, generated files, and third-party files are exempt.
**PHP:**
```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: {{REPO_NAME}}.Module
* INGROUP: {{REPO_NAME}}
* REPO: {{REPO_URL}}
* PATH: /src/class/MyClass.php
* VERSION: XX.YY.ZZ
* BRIEF: One-line description of file purpose
*/
```
**Markdown / YAML / Shell:** Use the appropriate comment syntax with the same fields.
---
# Coding Standards
## Naming Conventions
| Context | Convention | Example |
|---------|-----------|---------|
| PHP class | `PascalCase` | `MyService` |
| PHP method / function | `camelCase` | `getUserData()` |
| PHP variable | `$snake_case` | `$module_name` |
| PHP constant | `UPPER_SNAKE_CASE` | `MAX_RETRIES` |
| PHP class file | `PascalCase.php` | `ApiClient.php` |
| PHP script file | `snake_case.php` | `check_health.php` |
| YAML workflow | `kebab-case.yml` | `ci-dolibarr.yml` |
| Markdown doc | `kebab-case.md` | `installation-guide.md` |
## Commit Messages
Format: `<type>(<scope>): <subject>` — imperative, lower-case subject, no trailing period.
Valid types: `feat` · `fix` · `docs` · `chore` · `ci` · `refactor` · `style` · `test` · `perf` · `revert` · `build`
## Branch Naming
Format: `<prefix>/<MAJOR.MINOR.PATCH>[/description]`
Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `dependabot/`
---
# GitHub Actions — Token Usage
Every workflow must use **`secrets.GH_TOKEN`** (the org-level Personal Access Token).
```yaml
# ✅ Correct
- uses: actions/checkout@v4
with:
token: ${{ secrets.GH_TOKEN }}
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
```
```yaml
# ❌ Wrong — never use these
token: ${{ github.token }}
token: ${{ secrets.GITHUB_TOKEN }}
```
PHP scripts read the token with: `getenv('GH_TOKEN') ?: getenv('GITHUB_TOKEN')` — `GH_TOKEN` is always preferred; `GITHUB_TOKEN` is a local-dev fallback only.
---
# Keeping Documentation Current
| Change type | Documentation to update |
|-------------|------------------------|
| New or renamed PHP class/method | PHPDoc block; `docs/api/` entry |
| New or changed module version | Update `$this->version` in module descriptor; bump `README.md` |
| New library class or major feature | `CHANGELOG.md` entry under `Added` |
| Bug fix | `CHANGELOG.md` entry under `Fixed` |
| Breaking change | `CHANGELOG.md` entry under `Changed` |
| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block |
| **Every PR** | **Bump the patch version** — increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it |
---
# What NOT to Do
- **Never commit directly to `main`** — all changes go through a PR.
- **Never hardcode version numbers** in body text — update `README.md` and let automation propagate.
- **Never change `$this->numero`** — the module ID is permanent and globally registered.
- **Never skip the FILE INFORMATION block** on a new source file.
- **Never use bare `catch (\Throwable $e) {}`** — always log or re-throw.
- **Never mix tabs and spaces** within a file — follow `.editorconfig`.
- **Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows** — always use `secrets.GH_TOKEN`.
- **Never register a new module ID** without first consulting module-registry.md.
- **Never let `$this->version` and `README.md` version diverge.**
---
# PR Checklist
Before opening a PR, verify:
- [ ] Patch version bumped in `README.md` (e.g. `01.02.03` → `01.02.04`)
- [ ] `$this->version` in module descriptor updated to match
- [ ] FILE INFORMATION headers updated in modified files
- [ ] CHANGELOG.md updated
---
# Key Policy Documents (MokoStandards)
| Document | Purpose |
|----------|---------|
| [file-header-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/file-header-standards.md) | Copyright-header rules for every file type |
| [coding-style-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md) | Naming and formatting conventions |
| [branching-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/branching-strategy.md) | Branch naming, hierarchy, and release workflow |
| [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) | Squash-merge policy and PR conventions |
| [changelog-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/changelog-standards.md) | How and when to update CHANGELOG.md |
| [module-registry.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/development/crm/module-registry.md) | Dolibarr module ID registry — check before reserving a new ID |
| [crm/development-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/crm/development-standards.md) | MokoCRM Dolibarr module development standards |
| [dolibarr-development-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/guide/crm/dolibarr-development-guide.md) | MokoCRM full development guide |
MOKO_END
}
]
subdirectories = [
{
name = "workflows"
path = ".github/workflows"
description = "GitHub Actions workflows"
requirement_status = "required"
files = [
{
name = "ci-dolibarr.yml"
extension = "yml"
description = "Dolibarr-specific CI workflow"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/dolibarr/ci-dolibarr.yml.template"
},
{
name = "codeql-analysis.yml"
extension = "yml"
description = "CodeQL security analysis workflow"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/generic/codeql-analysis.yml.template"
},
{
name = "standards-compliance.yml"
extension = "yml"
description = "MokoStandards compliance validation"
requirement_status = "required"
always_overwrite = true
template = ".github/workflows/standards-compliance.yml"
},
{
name = "enterprise-firewall-setup.yml"
extension = "yml"
description = "Enterprise firewall configuration for trusted domain access"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/shared/enterprise-firewall-setup.yml.template"
},
{
name = "deploy-dev.yml"
extension = "yml"
description = "SFTP deployment of src/ to the development server"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/shared/deploy-dev.yml.template"
},
{
name = "deploy-demo.yml"
extension = "yml"
description = "SFTP deployment of src/ to the demo server on merge to main"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/shared/deploy-demo.yml.template"
},
{
name = "deploy-rs.yml"
extension = "yml"
description = "SFTP deployment of src/ to the release staging server on merge to main"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/shared/deploy-rs.yml.template"
},
{
name = "publish-to-mokodolimods.yml"
extension = "yml"
description = "On release, copies src/ to htdocs/custom/$DEV_FTP_SUFFIX in mokodolimods and opens a PR"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/dolibarr/publish-to-mokodolimods.yml.template"
},
{
name = "sync-version-on-merge.yml"
extension = "yml"
description = "Auto-bump patch version on merge and propagate to all file headers"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/shared/sync-version-on-merge.yml.template"
},
{
name = "auto-release.yml"
extension = "yml"
description = "Auto-create GitHub Release on push to main with version from README.md"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/shared/auto-release.yml.template"
},
{
name = "repository-cleanup.yml"
extension = "yml"
description = "Scheduled cleanup: delete retired workflows, stale branches, old workflow runs"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/shared/repository-cleanup.yml.template"
},
{
name = "auto-dev-issue.yml"
extension = "yml"
description = "Auto-create tracking issue when a dev/** branch is pushed"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/shared/auto-dev-issue.yml.template"
},
{
name = "repo_health.yml"
extension = "yml"
description = "Dolibarr module health checks — descriptor, numero, url_last_version, repo artifacts"
requirement_status = "required"
always_overwrite = true
template = "templates/workflows/dolibarr/repo_health.yml.template"
}
]
},
{
name = "ISSUE_TEMPLATE"
path = ".github/ISSUE_TEMPLATE"
description = "GitHub issue templates synced from MokoStandards"
requirement_status = "required"
files = [
{
name = "config.yml"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/config.yml"
},
{
name = "adr.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/adr.md"
},
{
name = "bug_report.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/bug_report.md"
},
{
name = "documentation.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/documentation.md"
},
{
name = "enterprise_support.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/enterprise_support.md"
},
{
name = "feature_request.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/feature_request.md"
},
{
name = "firewall-request.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/firewall-request.md"
},
{
name = "question.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/question.md"
},
{
name = "request-license.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/request-license.md"
},
{
name = "rfc.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/rfc.md"
},
{
name = "security.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/security.md"
},
{
name = "dolibarr_issue.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/dolibarr_issue.md"
},
{
name = "dolibarr_module_id_request.md"
always_overwrite = true
template = "templates/github/ISSUE_TEMPLATE/dolibarr_module_id_request.md"
}
]
}
]
},
{
name = "img"
path = "src/img"
description = "Module image assets including Dolibarr picto"
requirement_status = "required"
purpose = "Contains the module picto displayed in the Dolibarr UI"
files = [
{
name = "object_favicon_256.png"
extension = "png"
description = "Moko Consulting picto shown in Dolibarr module list (256 px); object_ prefix follows Dolibarr icon naming convention"
requirement_status = "required"
always_overwrite = true
template = "templates/images/primary/favicon_256.png"
}
]
}
]
repository_requirements = {
secrets = [
{
name = "GH_TOKEN"
description = "Org-level GitHub PAT for automation"
required = true
scope = "org"
},
{
name = "DEV_FTP_KEY"
description = "SSH private key for SFTP dev deployment (preferred); if DEV_FTP_PASSWORD is also set it is used as the key passphrase, with password-only as fallback"
required = false
scope = "org"
},
{
name = "DEV_FTP_PASSWORD"
description = "SFTP password for dev deployment; used as SSH key passphrase when DEV_FTP_KEY is also set, and as standalone fallback if key auth fails"
required = false
scope = "org"
note = "At least one of DEV_FTP_KEY or DEV_FTP_PASSWORD must be configured"
}
]
variables = [
{
name = "DEV_FTP_HOST"
description = "Dev server hostname; may include port suffix (e.g. dev.example.com or dev.example.com:2222)"
required = true
scope = "org"
},
{
name = "DEV_FTP_PATH"
description = "Base remote path for SFTP deployment (e.g. /var/www/html)"
required = true
scope = "org"
},
{
name = "DEV_FTP_USERNAME"
description = "SFTP username for dev server authentication"
required = true
scope = "org"
},
{
name = "DEV_FTP_PORT"
description = "Explicit SFTP port override; if omitted the port is parsed from DEV_FTP_HOST or defaults to 22"
required = false
scope = "org"
},
{
name = "DEV_FTP_SUFFIX"
description = "Per-repo path suffix appended to DEV_FTP_PATH (e.g. /my-module)"
required = false
scope = "repo"
}
]
}
}
}