prepare for cli updates with custom php binary
All checks were successful
CI/CD Pipeline / test (push) Successful in 2s
CI/CD Pipeline / deploy (push) Successful in 18s
CI/CD Pipeline / promote_stable (push) Successful in 2s

This commit is contained in:
2026-02-11 18:31:06 +01:00
parent af03c23c9f
commit 54d4cd7f99
5 changed files with 91 additions and 9 deletions

View File

@@ -2,6 +2,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Setting;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
@@ -16,9 +17,12 @@ class SystemStatusController extends Controller
} }
$phpDefaultPath = $this->resolveBinary('php'); $phpDefaultPath = $this->resolveBinary('php');
$phpSelectedPath = PHP_BINARY ?: $phpDefaultPath; $phpConfiguredPath = trim((string) Setting::where('key', 'system.php_binary')->value('value'));
$phpSelectedPath = $phpConfiguredPath ?: (PHP_BINARY ?: $phpDefaultPath);
$phpSelectedOk = (bool) $phpSelectedPath; $phpSelectedOk = (bool) $phpSelectedPath;
$phpSelectedVersion = PHP_VERSION; $phpSelectedVersion = $phpSelectedPath
? ($this->resolvePhpVersion($phpSelectedPath) ?? PHP_VERSION)
: PHP_VERSION;
$minVersions = $this->resolveMinVersions(); $minVersions = $this->resolveMinVersions();
$composerPath = $this->resolveBinary('composer'); $composerPath = $this->resolveBinary('composer');
$nodePath = $this->resolveBinary('node'); $nodePath = $this->resolveBinary('node');
@@ -40,6 +44,7 @@ class SystemStatusController extends Controller
return response()->json([ return response()->json([
'php' => PHP_VERSION, 'php' => PHP_VERSION,
'php_default' => $phpDefaultPath, 'php_default' => $phpDefaultPath,
'php_configured' => $phpConfiguredPath ?: null,
'php_selected_path' => $phpSelectedPath, 'php_selected_path' => $phpSelectedPath,
'php_selected_ok' => $phpSelectedOk, 'php_selected_ok' => $phpSelectedOk,
'php_selected_version' => $phpSelectedVersion, 'php_selected_version' => $phpSelectedVersion,

View File

@@ -2,6 +2,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Setting;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
@@ -169,7 +170,10 @@ class SystemUpdateController extends Controller
], 500); ], 500);
} }
$phpBinary = trim((string) Setting::where('key', 'system.php_binary')->value('value'));
if ($phpBinary === '') {
$phpBinary = env('SYSTEM_UPDATE_PHP_BINARY') ?: (PHP_BINARY ?: 'php'); $phpBinary = env('SYSTEM_UPDATE_PHP_BINARY') ?: (PHP_BINARY ?: 'php');
}
$append("Running migrations (using {$phpBinary})..."); $append("Running migrations (using {$phpBinary})...");
$migrate = new Process([$phpBinary, 'artisan', 'migrate', '--force'], base_path()); $migrate = new Process([$phpBinary, 'artisan', 'migrate', '--force'], base_path());
$migrate->setTimeout(600); $migrate->setTimeout(600);

View File

@@ -98,5 +98,5 @@
"minimum-stability": "stable", "minimum-stability": "stable",
"prefer-stable": true, "prefer-stable": true,
"version": "26.0.2", "version": "26.0.2",
"build": "49" "build": "52"
} }

View File

@@ -21,14 +21,41 @@ git pull --ff-only
echo "Installing PHP dependencies..." echo "Installing PHP dependencies..."
composer install --no-dev --optimize-autoloader composer install --no-dev --optimize-autoloader
resolve_php_bin() {
if [[ -n "${PHP_BIN:-}" ]]; then
echo "$PHP_BIN"
return
fi
if command -v keyhelp-php84 >/dev/null 2>&1; then
echo "keyhelp-php84"
return
fi
if command -v php >/dev/null 2>&1; then
echo "php"
return
fi
echo "php"
}
PHP_BIN="$(resolve_php_bin)"
if [[ -x "artisan" ]]; then
CONFIGURED_PHP="$($PHP_BIN artisan tinker --execute="echo \\App\\Models\\Setting::where('key','system.php_binary')->value('value') ?? '';" 2>/dev/null || true)"
if [[ -n "$CONFIGURED_PHP" ]]; then
if command -v "$CONFIGURED_PHP" >/dev/null 2>&1; then
PHP_BIN="$CONFIGURED_PHP"
elif [[ -x "$CONFIGURED_PHP" ]]; then
PHP_BIN="$CONFIGURED_PHP"
fi
fi
fi
echo "Installing JS dependencies..." echo "Installing JS dependencies..."
npm install npm install
echo "Building assets..." echo "Building assets..."
npm run build npm run build
PHP_BIN="${PHP_BIN:-php}"
echo "Running migrations..." echo "Running migrations..."
$PHP_BIN artisan migrate --force $PHP_BIN artisan migrate --force

View File

@@ -201,6 +201,11 @@ function Acp({ isAdmin }) {
favicon_128: '', favicon_128: '',
favicon_256: '', favicon_256: '',
}) })
const [systemCliSettings, setSystemCliSettings] = useState({
php_binary: '',
})
const [systemCliSaving, setSystemCliSaving] = useState(false)
const [systemCliError, setSystemCliError] = useState('')
const settingsDetailMap = { const settingsDetailMap = {
forum_name: 'forumName', forum_name: 'forumName',
default_theme: 'defaultTheme', default_theme: 'defaultTheme',
@@ -292,6 +297,9 @@ function Acp({ isAdmin }) {
favicon_256: settingsMap.get('favicon_256') || '', favicon_256: settingsMap.get('favicon_256') || '',
} }
setGeneralSettings(next) setGeneralSettings(next)
setSystemCliSettings({
php_binary: settingsMap.get('system.php_binary') || '',
})
setAttachmentSettings({ setAttachmentSettings({
display_images_inline: settingsMap.get('attachments.display_images_inline') || 'true', display_images_inline: settingsMap.get('attachments.display_images_inline') || 'true',
create_thumbnails: settingsMap.get('attachments.create_thumbnails') || 'true', create_thumbnails: settingsMap.get('attachments.create_thumbnails') || 'true',
@@ -373,6 +381,23 @@ function Acp({ isAdmin }) {
} }
} }
const handleSystemCliSave = async (event) => {
event.preventDefault()
setSystemCliSaving(true)
setSystemCliError('')
try {
const value = typeof systemCliSettings.php_binary === 'string'
? systemCliSettings.php_binary.trim()
: String(systemCliSettings.php_binary ?? '')
await saveSetting('system.php_binary', value)
setSystemCliSettings((prev) => ({ ...prev, php_binary: value }))
} catch (err) {
setSystemCliError(err.message)
} finally {
setSystemCliSaving(false)
}
}
const handleLogoUpload = async (file, settingKey) => { const handleLogoUpload = async (file, settingKey) => {
if (!file) return if (!file) return
setGeneralUploading(true) setGeneralUploading(true)
@@ -3588,9 +3613,30 @@ function Acp({ isAdmin }) {
<h5 className="mb-0">CLI</h5> <h5 className="mb-0">CLI</h5>
</div> </div>
<div className="bb-acp-panel-body"> <div className="bb-acp-panel-body">
<p className="bb-muted mb-0"> {systemCliError && <p className="text-danger">{systemCliError}</p>}
Placeholder: CLI upgrade commands and automation helpers will live here. <Form onSubmit={handleSystemCliSave}>
</p> <Form.Group className="mb-3">
<Form.Label>PHP interpreter</Form.Label>
<Form.Control
type="text"
placeholder={systemStatus?.php_default || '/usr/bin/php'}
value={systemCliSettings.php_binary}
onChange={(event) =>
setSystemCliSettings((prev) => ({
...prev,
php_binary: event.target.value,
}))
}
/>
<Form.Text className="bb-muted">
Used for CLI-based updates and maintenance tasks. Leave empty to use
the system default.
</Form.Text>
</Form.Group>
<Button type="submit" variant="dark" disabled={systemCliSaving}>
{t('acp.save')}
</Button>
</Form>
</div> </div>
</div> </div>
)} )}