Merge pull request 'feat: deploy-sftp.php --env demo/live + multi-instance (#184)' (#287) from feature/184-deploy-env-demo-live into main
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m23s

This commit was merged in pull request #287.
This commit is contained in:
2026-06-21 02:22:39 +00:00
39 changed files with 245 additions and 41 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation
# VERSION: 09.32.00
# VERSION: 09.32.01
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
+1 -1
View File
@@ -6,7 +6,7 @@ DEFGROUP: MokoPlatform.Root
INGROUP: MokoPlatform
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
PATH: /README.md
VERSION: 09.32.00
VERSION: 09.32.01
BRIEF: Project overview and documentation
-->
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/branch_rename.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Rename a git branch via Gitea API (create new, update PR, delete old)
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/bulk_workflow_push.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Push a workflow file to all governed repos via the Gitea Contents API
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/bulk_workflow_trigger.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Trigger a workflow across multiple repos at once
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/client_dashboard.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Generate unified client dashboard HTML
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/client_inventory.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Discover and list all client-waas repos with their server configuration status
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/client_provision.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Provision a new client environment end-to-end
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/grafana_dashboard.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Manage Grafana dashboards via API
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/joomla_build.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Build a Joomla extension ZIP from manifest — all types supported
* NOTE: Called by pre-release and auto-release workflows.
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/joomla_metadata_validate.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Validate MokoGitea repo metadata against Joomla extension manifest XML
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/manifest_detect.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Auto-detect manifest fields from source files and optionally push to API
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/manifest_integrity.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Cross-check manifest API fields against repo contents across the org
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/manifest_licensing.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Ensure licensing tags (updateservers, dlid) in Joomla extension manifests
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_read.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Read repo metadata from Gitea manifest API, auto-detect the rest
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/platform_detect.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Auto-detect repository platform type and optionally update manifest
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_cascade.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Cascade release zip to all lower stability channels
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/release_publish.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Publish a release and create copies for all lesser stability streams.
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/scaffold_client.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/updates_xml_sync.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Sync updates.xml to target branches via Gitea API
* NOTE: Called by pre-release and auto-release workflows after updates.xml
* is modified on the current branch. Pushes the file to other branches
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/version_auto_bump.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/version_check.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Validate version consistency across README, manifests, and sub-packages
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/wiki_sync.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Sync select wiki pages from mokoplatform to all template repos
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/workflow_sync.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Sync workflows from Generic → platform templates → live repos based on manifest.platform
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /deploy/backup-before-deploy.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Snapshot Joomla directories before deployment for rollback capability
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /deploy/deploy-dolibarr.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Deploy Dolibarr module files to a remote server via SFTP/rsync
*/
+111 -4
View File
@@ -66,8 +66,16 @@ class DeploySftp extends CliFramework
*/
protected function run(): int
{
$repoPath = $this->resolveRepoPath();
$srcDir = $this->resolveSrcDir($repoPath);
$repoPath = $this->resolveRepoPath();
$srcDir = $this->resolveSrcDir($repoPath);
$env = strtolower($this->getArgument('--env', '') ?: '');
// Multi-target: LIVE_TARGETS env var overrides config file for live deploys
$liveTargets = getenv('LIVE_TARGETS') ?: '';
if ($liveTargets !== '' && ($env === 'live' || $env === '')) {
return $this->deployMultiTarget($repoPath, $srcDir, $liveTargets);
}
$configPath = $this->resolveConfigPath($repoPath);
$this->log("Repository : {$repoPath}");
@@ -130,6 +138,103 @@ class DeploySftp extends CliFramework
return $exitCode;
}
// ─── Multi-target deploy ────────────────────────────────────────────────
/**
* Deploy to multiple live targets from LIVE_TARGETS JSON.
*
* LIVE_TARGETS format (JSON array of objects):
* [
* {"host": "web1.example.com", "user": "deploy", "remote_path": "/var/www/module/", "ssh_key_file": "~/.ssh/id_rsa"},
* {"host": "web2.example.com", "user": "deploy", "remote_path": "/var/www/module/", "ssh_key_file": "~/.ssh/id_rsa"}
* ]
*
* @return int POSIX exit code (0 if all targets succeed)
*/
private function deployMultiTarget(string $repoPath, string $srcDir, string $liveTargetsJson): int
{
$targets = json_decode($liveTargetsJson, true);
if (!is_array($targets) || empty($targets)) {
$this->log('ERROR', 'LIVE_TARGETS is not a valid JSON array');
return self::EXIT_USAGE;
}
$this->section("Multi-target live deploy ({$this->count($targets)} targets)");
$succeeded = 0;
$failed = 0;
foreach ($targets as $i => $target) {
$host = $target['host'] ?? 'unknown';
$this->section("Target " . ($i + 1) . ": {$host}");
// Merge target config into $this->config for this iteration
$this->config = $target;
if (!$this->validateConfig()) {
$this->log('ERROR', "Skipping target {$host} — invalid config");
$failed++;
continue;
}
$remotePath = rtrim((string) $this->config['remote_path'], '/');
$ignores = array_merge(
$this->buildIgnorePatterns(),
$this->loadFtpIgnorePatterns($srcDir),
$this->loadFtpIgnorePatterns($repoPath)
);
$user = (string) $this->config['user'];
$port = (int) ($this->config['port'] ?? 22);
if ($this->dryRun) {
$this->log("[DRY RUN] Would deploy to {$user}@{$host}:{$port}{$remotePath}");
$succeeded++;
continue;
}
$sftp = $this->connect($host, $port, $user, $repoPath);
if ($sftp === null) {
$this->log('ERROR', "Failed to connect to {$host}");
$failed++;
continue;
}
// Reset counters per target
$this->uploaded = 0;
$this->skipped = 0;
$this->unchanged = 0;
$this->deleted = 0;
$dirCheck = @$sftp->nlist(dirname($remotePath));
$baseName = basename($remotePath);
$dirExists = is_array($dirCheck) && in_array($baseName, $dirCheck, true);
if (!$dirExists) {
$sftp->mkdir($remotePath, -1, true);
}
$exitCode = $this->uploadDirectory($sftp, $srcDir, $remotePath, $srcDir, $ignores);
$this->log(" {$host}: Uploaded={$this->uploaded} Unchanged={$this->unchanged} Deleted={$this->deleted} Skipped={$this->skipped}");
if ($exitCode === 0) {
$succeeded++;
} else {
$failed++;
}
}
$this->section('Multi-target summary');
$this->log("Succeeded: {$succeeded}, Failed: {$failed}");
return $failed > 0 ? self::EXIT_FAILURE : self::EXIT_SUCCESS;
}
private function count(array $arr): int
{
return \count($arr);
}
// ─── Private helpers ──────────────────────────────────────────────────────
/**
@@ -171,8 +276,10 @@ class DeploySftp extends CliFramework
/** Map of --env values to their sftp-config filename. */
private const ENV_CONFIG_MAP = [
'dev' => 'sftp-config.dev.json',
'rs' => 'sftp-config.rs.json',
'dev' => 'sftp-config.dev.json',
'rs' => 'sftp-config.rs.json',
'demo' => 'sftp-config.demo.json',
'live' => 'sftp-config.live.json',
];
/**
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /deploy/health-check.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Post-deploy health check — verify a Joomla site is responding correctly
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /deploy/rollback-joomla.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Rollback a Joomla deployment by restoring from a pre-deploy snapshot
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /deploy/sync-joomla.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Sync Joomla site directories between two servers via rsync over SSH
*/
+1 -1
View File
@@ -14,7 +14,7 @@
DEFGROUP: dolibarr-api-mcp.Documentation
INGROUP: dolibarr-api-mcp
REPO: https://git.mokoconsulting.tech/MokoConsulting/dolibarr-api-mcp
VERSION: 09.32.00
VERSION: 09.32.01
PATH: ./CONTRIBUTING.md
BRIEF: Contribution guidelines for the project
-->
+1 -1
View File
@@ -10,7 +10,7 @@ DEFGROUP: dolibarr-api-mcp.Documentation
INGROUP: dolibarr-api-mcp
REPO: https://git.mokoconsulting.tech/MokoConsulting/dolibarr-api-mcp
PATH: /SECURITY.md
VERSION: 09.32.00
VERSION: 09.32.01
BRIEF: Security vulnerability reporting and handling policy
-->
+1 -1
View File
@@ -14,7 +14,7 @@
DEFGROUP:
INGROUP: Project.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCli-Template-Generic
VERSION: 09.32.00
VERSION: 09.32.01
PATH: ./CONTRIBUTING.md
BRIEF: Contribution guidelines for the project
-->
+1 -1
View File
@@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME]
INGROUP: [PROJECT_NAME].Documentation
REPO: [REPOSITORY_URL]
PATH: /SECURITY.md
VERSION: 09.32.00
VERSION: 09.32.01
BRIEF: Security vulnerability reporting and handling policy
-->
@@ -0,0 +1,48 @@
{
"_template": "Copy this file to scripts/sftp-config/sftp-config.demo.json — it is gitignored",
"_env": "demo",
"type": "sftp",
"save_before_upload": false,
"upload_on_save": false,
"sync_down_on_open": false,
"sync_skip_deletes": false,
"sync_same_age": true,
"confirm_downloads": false,
"confirm_sync": true,
"confirm_overwrite_newer": true,
"host": "YOUR_DEMO_HOST",
"user": "YOUR_DEMO_USERNAME",
"ssh_key_file": "jmiller_private.ppk",
"port": "22",
"remote_path": "/home/YOUR_USER/YOUR_DEMO_DOMAIN/htdocs/custom/YOUR_MODULE/",
"ignore_regexes": [
"\\.sublime-(project|workspace|settings)",
"\\.libsass.json/",
"sftp-config(-alt\\d?)?\\.json",
"sftp-settings\\.json",
"/venv/",
"\\.svn/",
"\\.hg/",
"\\.bzr",
"_darcs",
"CVS",
"\\.DS_Store",
"Thumbs\\.db",
"robots\\.txt",
"desktop\\.ini",
"configuration\\.php",
"\\.ffs*",
"\\.git*",
"\\.editorconfig",
"conf\\.php",
"\\.ps1",
"\\.tx"
],
"connect_timeout": 30
}
@@ -0,0 +1,49 @@
{
"_template": "Copy this file to scripts/sftp-config/sftp-config.live.json — it is gitignored",
"_env": "live",
"_note": "For multi-instance live deploy, use the LIVE_TARGETS env var instead (JSON array of target objects)",
"type": "sftp",
"save_before_upload": false,
"upload_on_save": false,
"sync_down_on_open": false,
"sync_skip_deletes": false,
"sync_same_age": true,
"confirm_downloads": false,
"confirm_sync": true,
"confirm_overwrite_newer": true,
"host": "YOUR_LIVE_HOST",
"user": "YOUR_LIVE_USERNAME",
"ssh_key_file": "~/.ssh/id_rsa",
"port": "22",
"remote_path": "/home/YOUR_USER/YOUR_LIVE_DOMAIN/htdocs/custom/YOUR_MODULE/",
"ignore_regexes": [
"\\.sublime-(project|workspace|settings)",
"\\.libsass.json/",
"sftp-config(-alt\\d?)?\\.json",
"sftp-settings\\.json",
"/venv/",
"\\.svn/",
"\\.hg/",
"\\.bzr",
"_darcs",
"CVS",
"\\.DS_Store",
"Thumbs\\.db",
"robots\\.txt",
"desktop\\.ini",
"configuration\\.php",
"\\.ffs*",
"\\.git*",
"\\.editorconfig",
"conf\\.php",
"\\.ps1",
"\\.tx"
],
"connect_timeout": 30
}
+1 -1
View File
@@ -63,7 +63,7 @@ class VersionBumpTest extends TestCase
{
file_put_contents(
"{$this->tmpDir}/README.md",
"<!-- VERSION: 09.32.00 -->\nSome content\n"
"<!-- VERSION: 09.32.01 -->\nSome content\n"
);
$this->execute();
+2 -2
View File
@@ -34,7 +34,7 @@ class VersionReadTest extends TestCase
{
file_put_contents(
"{$this->tmpDir}/README.md",
"# Test\n<!-- VERSION: 09.32.00 -->\n"
"# Test\n<!-- VERSION: 09.32.01 -->\n"
);
$this->assertSame('02.03.04', trim($this->runScript()));
@@ -68,7 +68,7 @@ class VersionReadTest extends TestCase
{
file_put_contents(
"{$this->tmpDir}/README.md",
"<!-- VERSION: 09.32.00 -->\n"
"<!-- VERSION: 09.32.01 -->\n"
);
mkdir("{$this->tmpDir}/src", 0755, true);
file_put_contents(
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /validate/check_file_integrity.php
* VERSION: 09.32.00
* VERSION: 09.32.01
* BRIEF: Compare deployed files on a remote server against the local repository to detect drift
*/