2026-05-23 22:37:37 -05:00
#!/usr/bin/env php
<? php
2026-05-26 14:27:48 -05:00
2026-05-23 22:37:37 -05:00
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: moko-platform.CLI
* INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_validate.php
2026-05-31 11:39:10 -05:00
* BRIEF: Pre-release validation -- version consistency, required files, manifest checks
2026-05-23 22:37:37 -05:00
*/
declare ( strict_types = 1 );
2026-05-31 11:39:10 -05:00
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php' ;
2026-05-26 14:27:48 -05:00
2026-05-31 11:39:10 -05:00
use MokoEnterprise\CliFramework ;
2026-05-23 22:37:37 -05:00
2026-05-31 11:39:10 -05:00
class ReleaseValidateCli extends CliFramework
2026-05-26 14:27:48 -05:00
{
2026-05-31 11:39:10 -05:00
private int $pass = 0 ;
private int $fail = 0 ;
private int $warn = 0 ;
private array $results = [];
protected function configure () : void
{
$this -> setDescription ( 'Pre-release validation -- version consistency, required files, manifest checks' );
$this -> addArgument ( '--path' , 'Repository root' , '.' );
$this -> addArgument ( '--version' , 'Expected version string' , null );
$this -> addArgument ( '--platform' , 'joomla|dolibarr|generic' , null );
$this -> addArgument ( '--output-summary' , 'Write markdown to $GITHUB_STEP_SUMMARY' , false );
$this -> addArgument ( '--github-output' , 'Export counts to $GITHUB_OUTPUT' , false );
}
protected function run () : int
{
$path = $this -> getArgument ( '--path' ); $version = $this -> getArgument ( '--version' );
$platform = $this -> getArgument ( '--platform' );
$outputSummary = ( bool ) $this -> getArgument ( '--output-summary' );
$githubOutput = ( bool ) $this -> getArgument ( '--github-output' );
if ( $version === null ) { $this -> log ( 'ERROR' , "Usage: release_validate.php --path . --version XX.YY.ZZ [--platform joomla]" ); return 1 ; }
$root = realpath ( $path ) ?: $path ;
if ( $platform === null ) {
$manifestXml = " { $root } /.mokogitea/manifest.xml" ;
if ( file_exists ( $manifestXml )) { $mContent = file_get_contents ( $manifestXml ); if ( preg_match ( '/<platform>([^<]+)<\/platform>/' , $mContent , $pm )) { $platform = trim ( $pm [ 1 ]); } }
if ( in_array ( $platform , [ 'waas-component' ], true )) { $platform = 'joomla' ; }
if ( in_array ( $platform , [ 'crm-module' ], true )) { $platform = 'dolibarr' ; }
if ( $platform === null ) { $platform = 'generic' ; }
}
$hasSource = is_dir ( " { $root } /src" ) || is_dir ( " { $root } /htdocs" );
$this -> addVResult ( 'Source directory' , $hasSource ? 'PASS' : 'WARN' , $hasSource ? 'src/ or htdocs/ found' : 'No src/ or htdocs/ directory' );
if ( ! file_exists ( " { $root } /README.md" )) { $this -> addVResult ( 'README.md' , 'FAIL' , 'Not found' ); }
else { $readme = file_get_contents ( " { $root } /README.md" ); $this -> addVResult ( 'README.md version' , ( preg_match ( '/VERSION:\s*' . preg_quote ( $version , '/' ) . '/' , $readme ) || strpos ( $readme , $version ) !== false ) ? 'PASS' : 'FAIL' , ( preg_match ( '/VERSION:\s*' . preg_quote ( $version , '/' ) . '/' , $readme ) || strpos ( $readme , $version ) !== false ) ? "` { $version } ` found" : "` { $version } ` not found" ); }
if ( ! file_exists ( " { $root } /CHANGELOG.md" )) { $this -> addVResult ( 'CHANGELOG.md' , 'WARN' , 'Not found' ); }
else { $cl = file_get_contents ( " { $root } /CHANGELOG.md" ); $this -> addVResult ( 'CHANGELOG.md version' , preg_match ( '/^##\s.*' . preg_quote ( $version , '/' ) . '/m' , $cl ) ? 'PASS' : 'WARN' , preg_match ( '/^##\s.*' . preg_quote ( $version , '/' ) . '/m' , $cl ) ? "Section found" : "No section header" ); }
$licenseFound = false ; foreach ([ 'LICENSE' , 'LICENSE.md' , 'LICENSE.txt' , 'COPYING' ] as $lf ) { if ( file_exists ( " { $root } / { $lf } " )) { $licenseFound = true ; break ; } }
$this -> addVResult ( 'LICENSE' , $licenseFound ? 'PASS' : 'FAIL' , $licenseFound ? 'Found' : 'Not found' );
if ( $platform === 'joomla' ) {
$manifest = null ; foreach ([ " { $root } /src" , $root ] as $dir ) { if ( ! is_dir ( $dir )) continue ; foreach ( glob ( " { $dir } /*.xml" ) as $xmlFile ) { $content = file_get_contents ( $xmlFile ); if ( strpos ( $content , '<extension' ) !== false ) { $manifest = $xmlFile ; break 2 ; } } }
if ( $manifest === null ) { $this -> addVResult ( 'XML manifest' , 'FAIL' , 'No Joomla manifest found' ); }
else { if ( preg_match ( '/<version>([^<]+)<\/version>/' , file_get_contents ( $manifest ), $m )) { $mVer = trim ( $m [ 1 ]); $this -> addVResult ( 'Manifest version' , $mVer === $version ? 'PASS' : 'FAIL' , $mVer === $version ? "` { $mVer } ` matches" : "` { $mVer } ` != ` { $version } `" ); } else { $this -> addVResult ( 'Manifest version' , 'FAIL' , 'No <version> tag' ); } }
if ( ! file_exists ( " { $root } /updates.xml" )) { $this -> addVResult ( 'updates.xml' , 'WARN' , 'Not found' ); }
else { $ux = file_get_contents ( " { $root } /updates.xml" ); $this -> addVResult ( 'updates.xml version' , preg_match ( '/<version>' . preg_quote ( $version , '/' ) . '<\/version>/' , $ux ) ? 'PASS' : 'FAIL' , preg_match ( '/<version>' . preg_quote ( $version , '/' ) . '<\/version>/' , $ux ) ? "` { $version } ` found" : "` { $version } ` not found" ); }
} elseif ( $platform === 'dolibarr' ) {
$modFile = null ; foreach ([ 'src' , 'htdocs' ] as $sd ) { $matches = glob ( " { $root } / { $sd } /mod*.class.php" ); if ( ! empty ( $matches )) { $modFile = $matches [ 0 ]; break ; } }
if ( $modFile === null ) { $this -> addVResult ( 'Dolibarr mod file' , 'FAIL' , 'No mod*.class.php found' ); }
else { $mc = file_get_contents ( $modFile ); $this -> addVResult ( 'Dolibarr version' , preg_match ( "/ \\\$ this->version\s*=\s*'" . preg_quote ( $version , '/' ) . "'/" , $mc ) ? 'PASS' : 'FAIL' , preg_match ( "/ \\\$ this->version\s*=\s*'" . preg_quote ( $version , '/' ) . "'/" , $mc ) ? "` { $version } ` matches" : "` { $version } ` not found" ); }
}
if ( file_exists ( " { $root } /composer.json" )) { $composer = json_decode ( file_get_contents ( " { $root } /composer.json" ), true ); if ( isset ( $composer [ 'version' ])) { $this -> addVResult ( 'composer.json version' , $composer [ 'version' ] === $version ? 'PASS' : 'WARN' , $composer [ 'version' ] === $version ? "` { $version } ` matches" : "` { $composer [ 'version' ] } ` != ` { $version } `" ); } }
$table = "| Check | Result | Details | \n |-------|--------|--------| \n " ;
foreach ( $this -> results as $r ) { $table .= "| { $r [ 'check' ] } | { $r [ 'status' ] } | { $r [ 'details' ] } | \n " ; }
$table .= " \n **Validation: { $this -> pass } passed, { $this -> fail } failed, { $this -> warn } warnings** \n " ;
echo $table ;
if ( $outputSummary ) { $summaryFile = getenv ( 'GITHUB_STEP_SUMMARY' ); if ( $summaryFile ) { file_put_contents ( $summaryFile , "## Pre-Release Sanity Checks ( { $platform } ) \n\n { $table } \n " , FILE_APPEND ); } }
if ( $githubOutput ) { $ghOutput = getenv ( 'GITHUB_OUTPUT' ); if ( $ghOutput ) { file_put_contents ( $ghOutput , "validation_pass= { $this -> pass } \n validation_fail= { $this -> fail } \n validation_warn= { $this -> warn } \n validation_platform= { $platform } \n " , FILE_APPEND ); } }
return $this -> fail > 0 ? 1 : 0 ;
}
private function addVResult ( string $check , string $status , string $details ) : void
{
$this -> results [] = [ 'check' => $check , 'status' => $status , 'details' => $details ];
if ( $status === 'PASS' ) { $this -> pass ++ ; } elseif ( $status === 'FAIL' ) { $this -> fail ++ ; } elseif ( $status === 'WARN' ) { $this -> warn ++ ; }
}
2026-05-23 22:37:37 -05:00
}
2026-05-31 11:39:10 -05:00
$app = new ReleaseValidateCli ();
exit ( $app -> execute ());