This commit is contained in:
hashbro
2026-08-07 06:05:33 +08:00
parent 82f8a8fffe
commit db3a789c2e
3 changed files with 63 additions and 7 deletions
+45 -6
View File
@@ -12,6 +12,9 @@ class ChannelProjectService
/**
* Run tools/new_project.py to materialize public/web/{channelId}/.
* Domains come from config (系统设置 / env); script rebuilds sync only when domains changed.
*
* Panel hosts often set open_basedir to server/ only — do not probe lab-root
* paths with is_file(); the Python child process is not bound by open_basedir.
*/
public function generate(string $channelId): void
{
@@ -26,14 +29,12 @@ class ChannelProjectService
}
$labRoot = dirname(base_path()); // coruna-lab/
$script = $labRoot.'/tools/new_project.py';
if (! is_file($script)) {
throw new RuntimeException('new_project.py not found');
}
$script = $this->resolveNewProjectScript($labRoot);
$python = $this->resolvePythonBin();
$joined = implode(',', $domains);
$cmd = [
'python3',
$python,
$script,
'--channel-id',
$channelId,
@@ -43,7 +44,8 @@ class ChannelProjectService
$joined,
];
$process = new Process($cmd, $labRoot);
// cwd must stay inside open_basedir (server/). Script resolves LAB_ROOT from __file__.
$process = new Process($cmd, base_path());
$process->setTimeout(600);
$process->run();
@@ -71,4 +73,41 @@ class ChannelProjectService
File::deleteDirectory($dir);
}
}
private function resolveNewProjectScript(string $labRoot): string
{
$configured = trim((string) config('coruna.new_project_py', ''));
if ($configured !== '') {
return $configured;
}
return $labRoot.'/tools/new_project.py';
}
/**
* Prefer an absolute python path so the child process gets a non-empty sys.executable.
*/
private function resolvePythonBin(): string
{
$configured = trim((string) config('coruna.python_bin', 'python3'));
if ($configured === '') {
$configured = 'python3';
}
if (str_contains($configured, '/')) {
return $configured;
}
$which = trim((string) shell_exec('command -v '.escapeshellarg($configured).' 2>/dev/null'));
if ($which !== '' && ! str_contains($which, "\n")) {
return $which;
}
foreach (['/usr/bin/python3', '/usr/local/bin/python3'] as $fallback) {
if (@is_file($fallback)) {
return $fallback;
}
}
return $configured;
}
}
+3
View File
@@ -15,6 +15,9 @@ return [
// Absolute path, project-local bin/7z, or bare "7z" on PATH. Avoid probing
// system paths with is_executable() under open_basedir (see CorunaArchive).
'seven_zip' => env('CORUNA_7Z_BIN', ''),
// Channel pack builder (outside server/ — do not is_file() under tight open_basedir).
'new_project_py' => env('CORUNA_NEW_PROJECT_PY', ''),
'python_bin' => env('CORUNA_PYTHON_BIN', 'python3'),
'telegram' => [
'bot_token' => env('TELEGRAM_BOT_TOKEN'),
'owner_chat_id' => env('TELEGRAM_OWNER_CHAT_ID'),
+15 -1
View File
@@ -87,6 +87,20 @@ def pick_channel(explicit: str | None, web_root: Path) -> str:
return channel
def resolve_python() -> str:
"""PHP/panel often launches `python3` such that sys.executable is ''."""
exe = (sys.executable or "").strip()
if exe:
return exe
for name in ("python3", "python"):
found = shutil.which(name)
if found:
return found
raise SystemExit(
"cannot locate python interpreter (sys.executable is empty; install python3 on PATH)"
)
def run(cmd: list[str]) -> None:
print("+", " ".join(cmd), flush=True)
subprocess.run(cmd, cwd=str(LAB_ROOT), check=True)
@@ -230,7 +244,7 @@ def main() -> int:
for item in fixed_rep:
domain_args += ["--reporting-domains", item]
py = sys.executable
py = resolve_python()
if same_domains:
dep = args.deployment_seed or prev_seeds.get("deployment_seed")