Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 34 additions & 28 deletions .git-hooks/pre-commit.d/run_platform_tests.php
Original file line number Diff line number Diff line change
@@ -1,46 +1,52 @@
<?php

include_once __DIR__.'/../../public_html/includes/app_header.inc.php';

ini_set('display_errors', 1);

function check_if_similar($var1, $var2) {

if (is_array($var1)) {
define('VMOD_DISABLED', true);

foreach ($var1 as $key => $value) {
if (is_array($value)) {
if (!array_key_exists($key, $var2) || !is_array($var2[$key]) || !check_if_similar($value, $var2[$key])) {
throw new Exception("1Arrays are not similar for key: " . $key);
}
}
}

} else {
if ($var1 !== $var2) {
throw new Exception('vars are not similar');
}
}
include_once __DIR__.'/../../public_html/includes/app_header.inc.php';

return true;
}
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

$directory = functions::file_resolve_path(__DIR__.'/../../tests/');

$files = functions::file_search($directory . '/*.php');

echo 'Found '. count($files) . ' test files' . PHP_EOL;
echo implode(PHP_EOL, array_map(function($file) {
return ' - '. basename($file);
}, $files)) . PHP_EOL;

$failed = 0;

foreach ($files as $file) {

echo 'Running tests from '. basename($file) .'...';

$result = include $file;
try {

$result = require $file;

if ($result === true) {
echo ' [OK]' . PHP_EOL;
} else {
echo ' [FAIL]' . PHP_EOL;
$failed++;
}

} catch (Error $e) {
echo ' [ERROR] ' . $e->getMessage() .' in '. $e->getFile() .' on line '. $e->getLine() . PHP_EOL;
$failed++;

if ($result === true) {
echo ' [OK]' . PHP_EOL;
} else {
echo ' [Failed]' . PHP_EOL;
exit(1);
} catch (Exception $e) {
echo ' [EXCEPTION] ' . $e->getMessage() . PHP_EOL;
$failed++;
}
}

if ($failed > 0) {
echo PHP_EOL . $failed . ' test(s) failed' . PHP_EOL;
exit(1);
}

echo PHP_EOL . 'All tests passed' . PHP_EOL;
86 changes: 86 additions & 0 deletions .github/ci/setup_database.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

// CI helper: Create tables from structure.json and import seed data

$link = new mysqli(
getenv('DB_SERVER') ?: '127.0.0.1',
getenv('DB_USERNAME') ?: 'litecore',
getenv('DB_PASSWORD') ?: 'litecore',
getenv('DB_DATABASE') ?: 'litecore'
);

$link->set_charset('utf8mb4');

$prefix = getenv('DB_TABLE_PREFIX') ?: 'lc_';

// Create tables from structure.json
$structure = json_decode(file_get_contents(__DIR__ . '/../../install/structure.json'), true);

if (empty($structure['tables'])) {
echo 'ERROR: No tables found in structure.json' . PHP_EOL;
exit(1);
}

foreach ($structure['tables'] as $key => $table) {

$name = $prefix . $key;
$cols = [];

foreach ($table['columns'] as $col => $def) {
$type = $def['type'];
if (stripos($type, 'ENUM') === 0 || stripos($type, 'SET') === 0) {
// ENUM/SET already includes values in type string
} elseif (!empty($def['length'])) {
$type .= '(' . $def['length'] . ')';
}
if (!empty($def['unsigned'])) $type .= ' UNSIGNED';

$null = (!isset($def['null']) || $def['null']) ? '' : ' NOT NULL';

$default = '';
if (array_key_exists('default', $def)) {
$d = $def['default'];
if (is_null($d)) {
$default = ' DEFAULT NULL';
} elseif (is_numeric($d)) {
$default = ' DEFAULT ' . $d;
} elseif (preg_match('/^(CURRENT_TIMESTAMP|current_timestamp\(\)|NOW\(\))$/i', $d)) {
$default = ' DEFAULT ' . $d;
} elseif (preg_match("/^'.*'$/", $d)) {
$default = ' DEFAULT ' . $d; // Already quoted (e.g. ENUM defaults)
} else {
$default = " DEFAULT '" . $link->real_escape_string($d) . "'";
}
}

$auto = !empty($def['auto_increment']) ? ' AUTO_INCREMENT' : '';
$cols[] = "`$col` $type$null$default$auto";
}

if (!empty($table['primary_key'])) {
$pk = is_array($table['primary_key']) ? $table['primary_key'] : [$table['primary_key']];
$cols[] = 'PRIMARY KEY (`' . implode('`,`', $pk) . '`)';
}

$sql = "CREATE TABLE IF NOT EXISTS `$name` (" . implode(', ', $cols) . ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci';

if (!$link->query($sql)) {
echo "ERROR creating $name: " . $link->error . PHP_EOL;
echo "SQL: $sql" . PHP_EOL;
exit(1);
}
}

echo count($structure['tables']) . ' tables created' . PHP_EOL;

// Import seed data
$data = file_get_contents(__DIR__ . '/../../install/data.sql');
$data = str_replace('`lc_', '`' . $prefix, $data);
$link->multi_query($data);
while ($link->next_result()) {}
echo 'Seed data imported' . PHP_EOL;

// Create admin user
$hash = password_hash('admin123456', PASSWORD_DEFAULT);
$link->query("INSERT INTO {$prefix}administrators (status, username, password_hash) VALUES (1, 'admin', '$hash')");
echo 'Admin user created' . PHP_EOL;
123 changes: 123 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: CI

on:
pull_request:
branches: [master]
push:
branches: [master]
workflow_dispatch:

jobs:

lint:
name: PHP Lint
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
tools: none
coverage: none

- name: PHP Lint
run: |
errors=0
while IFS= read -r file; do
if ! php -l "$file" > /dev/null 2>&1; then
php -l "$file"
errors=$((errors + 1))
fi
done < <(find public_html -name '*.php' -type f)
if [ $errors -gt 0 ]; then
echo "::error::$errors file(s) with PHP syntax errors"
exit 1
fi
echo "All PHP files passed syntax check"

build:
name: Gulp Build
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'

- name: Install dependencies
run: npm install --no-audit --no-fund

- name: Build framework assets
continue-on-error: true
run: |
npx gulp js-framework
npx gulp less-framework

platform-tests:
name: Platform Tests
runs-on: ubuntu-latest
needs: [lint]

services:
mariadb:
image: mariadb:11
env:
MARIADB_ROOT_PASSWORD: litecore_root
MARIADB_DATABASE: litecore
MARIADB_USER: litecore
MARIADB_PASSWORD: litecore
ports:
- 3306:3306
options: >-
--health-cmd="healthcheck.sh --connect --innodb_initialized"
--health-interval=10s
--health-timeout=5s
--health-retries=5

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mysqli, gd, intl, mbstring, zip, xml, curl, apcu
ini-values: date.timezone=Europe/London, apc.enable_cli=1
tools: none
coverage: none

- name: Create storage directories
run: |
mkdir -p public_html/storage/{cache,data,logs}
mkdir -p public_html/storage/vmods/.cache

- name: Generate config file
run: |
sed \
-e "s|define('DB_USERNAME', '');|define('DB_USERNAME', 'litecore');|" \
-e "s|define('DB_PASSWORD', '');|define('DB_PASSWORD', 'litecore');|" \
-e "s|define('DB_DATABASE', '');|define('DB_DATABASE', 'litecore');|" \
install/public_html/storage/config.inc.php \
> public_html/storage/config.inc.php
echo "Config file generated"

- name: Import database schema and seed data
run: php .github/ci/setup_database.php

- name: Verify installation
run: |
test -f public_html/storage/config.inc.php && echo "config exists"
mysql -h 127.0.0.1 -u litecore -plitecore litecore \
-e "SELECT COUNT(*) as tables FROM information_schema.tables WHERE table_schema='litecore' AND table_name LIKE 'lc_%';"

- name: Run platform tests
run: php .git-hooks/pre-commit.d/run_platform_tests.php
Loading
Loading