fix(bug): resolve nixpacks composer support failed to build Laravel13 of using php8.4
This commit is contained in:
1 parent
a18ed92cea
commit
b48b87814a
1 file changed
+71
-7
@@ -1886,6 +1886,18 @@ function ensureLaravelRuntimeEnv(env: string | undefined): string {
|
||||
);
|
||||
}
|
||||
|
||||
function resolveComposerPlatformPhpVersion(requirement?: string | null) {
|
||||
const normalized = requirement?.trim();
|
||||
if (!normalized) return null;
|
||||
|
||||
const phpEightMatch = normalized.match(/(^|[^0-9])8\.(\d+)([^0-9]|$)/);
|
||||
if (phpEightMatch?.[2]) {
|
||||
return `8.${phpEightMatch[2]}.0`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function ensureImageExists(
|
||||
server: Server,
|
||||
imageTag: string,
|
||||
@@ -1928,11 +1940,13 @@ async function buildImageWithNixpacks(
|
||||
`START_CMD=${escapeShellArg(startCommand || "")}`,
|
||||
'if [ "$BUILD_PATH" = "." ]; then TARGET_PATH="$DEPLOY_PATH"; else TARGET_PATH="$DEPLOY_PATH/$BUILD_PATH"; fi',
|
||||
'if [ ! -d "$TARGET_PATH" ]; then echo "Build path not found: $BUILD_PATH"; exit 1; fi',
|
||||
'echo "[doktainer] Preparing Nixpacks build for $TARGET_PATH"',
|
||||
'NIXPACKS_BIN="$(command -v nixpacks || true)"',
|
||||
'if [ -z "$NIXPACKS_BIN" ]; then',
|
||||
' NIXPACKS_BIN_DIR="/opt/doktainer/bin"',
|
||||
' mkdir -p "$NIXPACKS_BIN_DIR"',
|
||||
' if [ ! -x "$NIXPACKS_BIN_DIR/nixpacks" ]; then',
|
||||
' echo "[doktainer] Nixpacks is not installed; installing to $NIXPACKS_BIN_DIR"',
|
||||
" if command -v curl >/dev/null 2>&1; then",
|
||||
' curl -fsSL https://raw.githubusercontent.com/railwayapp/nixpacks/main/install.sh | bash -s -- -b "$NIXPACKS_BIN_DIR"',
|
||||
" elif command -v wget >/dev/null 2>&1; then",
|
||||
@@ -1944,6 +1958,20 @@ async function buildImageWithNixpacks(
|
||||
" fi",
|
||||
' NIXPACKS_BIN="$NIXPACKS_BIN_DIR/nixpacks"',
|
||||
"fi",
|
||||
'echo "[doktainer] Using Nixpacks binary: $NIXPACKS_BIN"',
|
||||
'"$NIXPACKS_BIN" --version || true',
|
||||
'if [ -f "$TARGET_PATH/composer.json" ]; then',
|
||||
' PHP_REQUIRE="$(tr -d \'\\n\' < "$TARGET_PATH/composer.json" | sed -n \'s/.*"require"[[:space:]]*:[[:space:]]*{[^}]*"php"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p\' | head -n1)"',
|
||||
' if [ -n "$PHP_REQUIRE" ]; then echo "[doktainer] composer.json requires php: $PHP_REQUIRE"; fi',
|
||||
"fi",
|
||||
...(buildEnvs.length > 0
|
||||
? [
|
||||
"echo \"[doktainer] Nixpacks build env overrides:\"",
|
||||
...buildEnvs.map(
|
||||
(value) => `printf " - %s\\n" ${escapeShellArg(value)}`,
|
||||
),
|
||||
]
|
||||
: []),
|
||||
'NIXPACKS_CMD=("$NIXPACKS_BIN" build "$TARGET_PATH" --name "$IMAGE_TAG")',
|
||||
...buildEnvs.map(
|
||||
(value) => `NIXPACKS_CMD+=(--env ${escapeShellArg(value)})`,
|
||||
@@ -2036,6 +2064,7 @@ async function inspectSourceProject(
|
||||
'HAS_DOT_ENV_DIST=0; [ -f "$TARGET_PATH/.env.dist" ] && HAS_DOT_ENV_DIST=1',
|
||||
'HAS_ENV_TEMPLATE=0; [ -f "$TARGET_PATH/env" ] && HAS_ENV_TEMPLATE=1',
|
||||
'HAS_ARTISAN=0; [ -f "$TARGET_PATH/artisan" ] && HAS_ARTISAN=1',
|
||||
'HAS_LARAVEL_FRAMEWORK=0; if [ -f "$TARGET_PATH/composer.json" ] && grep -q "\"laravel/framework\"" "$TARGET_PATH/composer.json"; then HAS_LARAVEL_FRAMEWORK=1; fi',
|
||||
'HAS_SPARK=0; [ -f "$TARGET_PATH/spark" ] && HAS_SPARK=1',
|
||||
'HAS_PACKAGE_JSON=0; [ -f "$TARGET_PATH/package.json" ] && HAS_PACKAGE_JSON=1',
|
||||
'HAS_PROCFILE=0; [ -f "$TARGET_PATH/Procfile" ] && HAS_PROCFILE=1',
|
||||
@@ -2046,7 +2075,8 @@ async function inspectSourceProject(
|
||||
'HAS_NVMRC=0; [ -f "$TARGET_PATH/.nvmrc" ] && HAS_NVMRC=1',
|
||||
'PACKAGE_JSON_HAS_NODE_ENGINE=0; if [ -f "$TARGET_PATH/package.json" ] && grep -q "\"node\"" "$TARGET_PATH/package.json"; then PACKAGE_JSON_HAS_NODE_ENGINE=1; fi',
|
||||
'HAS_PHP_FILE=0; find "$TARGET_PATH" -maxdepth 2 -type f -name "*.php" -print -quit | grep -q . && HAS_PHP_FILE=1 || true',
|
||||
'printf "HAS_COMPOSER_JSON=%s\nHAS_COMPOSER_LOCK=%s\nHAS_DOT_ENV=%s\nHAS_DOT_ENV_EXAMPLE=%s\nHAS_DOT_ENV_DIST=%s\nHAS_ENV_TEMPLATE=%s\nHAS_ARTISAN=%s\nHAS_SPARK=%s\nHAS_PACKAGE_JSON=%s\nHAS_PROCFILE=%s\nHAS_PUBLIC_INDEX=%s\nHAS_WRITABLE_DIR=%s\nHAS_STORAGE_DIR=%s\nHAS_BOOTSTRAP_CACHE_DIR=%s\nHAS_NVMRC=%s\nPACKAGE_JSON_HAS_NODE_ENGINE=%s\nHAS_PHP_FILE=%s\n" "$HAS_COMPOSER_JSON" "$HAS_COMPOSER_LOCK" "$HAS_DOT_ENV" "$HAS_DOT_ENV_EXAMPLE" "$HAS_DOT_ENV_DIST" "$HAS_ENV_TEMPLATE" "$HAS_ARTISAN" "$HAS_SPARK" "$HAS_PACKAGE_JSON" "$HAS_PROCFILE" "$HAS_PUBLIC_INDEX" "$HAS_WRITABLE_DIR" "$HAS_STORAGE_DIR" "$HAS_BOOTSTRAP_CACHE_DIR" "$HAS_NVMRC" "$PACKAGE_JSON_HAS_NODE_ENGINE" "$HAS_PHP_FILE"',
|
||||
'PHP_REQUIRE=""; if [ -f "$TARGET_PATH/composer.json" ]; then PHP_REQUIRE="$(tr -d \'\\n\' < "$TARGET_PATH/composer.json" | sed -n \'s/.*"require"[[:space:]]*:[[:space:]]*{[^}]*"php"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p\' | head -n1)"; fi',
|
||||
'printf "HAS_COMPOSER_JSON=%s\nHAS_COMPOSER_LOCK=%s\nHAS_DOT_ENV=%s\nHAS_DOT_ENV_EXAMPLE=%s\nHAS_DOT_ENV_DIST=%s\nHAS_ENV_TEMPLATE=%s\nHAS_ARTISAN=%s\nHAS_LARAVEL_FRAMEWORK=%s\nHAS_SPARK=%s\nHAS_PACKAGE_JSON=%s\nHAS_PROCFILE=%s\nHAS_PUBLIC_INDEX=%s\nHAS_WRITABLE_DIR=%s\nHAS_STORAGE_DIR=%s\nHAS_BOOTSTRAP_CACHE_DIR=%s\nHAS_NVMRC=%s\nPACKAGE_JSON_HAS_NODE_ENGINE=%s\nHAS_PHP_FILE=%s\nPHP_REQUIRE=%s\n" "$HAS_COMPOSER_JSON" "$HAS_COMPOSER_LOCK" "$HAS_DOT_ENV" "$HAS_DOT_ENV_EXAMPLE" "$HAS_DOT_ENV_DIST" "$HAS_ENV_TEMPLATE" "$HAS_ARTISAN" "$HAS_LARAVEL_FRAMEWORK" "$HAS_SPARK" "$HAS_PACKAGE_JSON" "$HAS_PROCFILE" "$HAS_PUBLIC_INDEX" "$HAS_WRITABLE_DIR" "$HAS_STORAGE_DIR" "$HAS_BOOTSTRAP_CACHE_DIR" "$HAS_NVMRC" "$PACKAGE_JSON_HAS_NODE_ENGINE" "$HAS_PHP_FILE" "$PHP_REQUIRE"',
|
||||
].join("\n");
|
||||
|
||||
const stdout = await execStrict(
|
||||
@@ -2054,23 +2084,29 @@ async function inspectSourceProject(
|
||||
privilegedCommand(server, `bash -lc ${escapeShellArg(script)}`),
|
||||
);
|
||||
|
||||
const values = Object.fromEntries(
|
||||
const rawValues = Object.fromEntries(
|
||||
stdout
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => {
|
||||
const separatorIndex = line.indexOf("=");
|
||||
const key = line.slice(0, separatorIndex);
|
||||
const value = line.slice(separatorIndex + 1);
|
||||
return [
|
||||
line.slice(0, separatorIndex),
|
||||
line.slice(separatorIndex + 1) === "1",
|
||||
key,
|
||||
key === "PHP_REQUIRE" ? value : value === "1",
|
||||
];
|
||||
}),
|
||||
) as Record<string, string | boolean>;
|
||||
|
||||
const values = Object.fromEntries(
|
||||
Object.entries(rawValues).map(([key, value]) => [key, value === true]),
|
||||
) as Record<string, boolean>;
|
||||
|
||||
const likelyPhp =
|
||||
values.HAS_COMPOSER_JSON || values.HAS_ARTISAN || values.HAS_PHP_FILE;
|
||||
const likelyLaravel = values.HAS_ARTISAN;
|
||||
const likelyLaravel = values.HAS_ARTISAN && values.HAS_LARAVEL_FRAMEWORK;
|
||||
const likelyCodeIgniter = values.HAS_SPARK || values.HAS_ENV_TEMPLATE;
|
||||
const hasNodeVersionHints =
|
||||
values.HAS_NVMRC || values.PACKAGE_JSON_HAS_NODE_ENGINE;
|
||||
@@ -2092,6 +2128,10 @@ async function inspectSourceProject(
|
||||
hasStorageDir: values.HAS_STORAGE_DIR,
|
||||
hasBootstrapCacheDir: values.HAS_BOOTSTRAP_CACHE_DIR,
|
||||
hasNodeVersionHints,
|
||||
phpRequirement:
|
||||
typeof rawValues.PHP_REQUIRE === "string" && rawValues.PHP_REQUIRE.trim()
|
||||
? rawValues.PHP_REQUIRE.trim()
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2195,20 +2235,26 @@ async function generateComposerLockIfMissing(
|
||||
opts: {
|
||||
deploymentPath: string;
|
||||
buildPath?: string;
|
||||
platformPhpVersion?: string | null;
|
||||
},
|
||||
) {
|
||||
const buildPath = normalizeBuildSubdirectory(opts.buildPath);
|
||||
const platformPhpVersion = opts.platformPhpVersion?.trim();
|
||||
const script = [
|
||||
"set -euo pipefail",
|
||||
`DEPLOY_PATH=${escapeShellArg(opts.deploymentPath)}`,
|
||||
`BUILD_PATH=${escapeShellArg(buildPath)}`,
|
||||
`PLATFORM_PHP=${escapeShellArg(platformPhpVersion || "")}`,
|
||||
'if [ "$BUILD_PATH" = "." ]; then TARGET_PATH="$DEPLOY_PATH"; else TARGET_PATH="$DEPLOY_PATH/$BUILD_PATH"; fi',
|
||||
'if [ -f "$TARGET_PATH/composer.lock" ] || [ ! -f "$TARGET_PATH/composer.json" ]; then exit 0; fi',
|
||||
'echo "[doktainer] composer.lock is missing; generating one before build"',
|
||||
'cd "$TARGET_PATH"',
|
||||
'if [ -n "$PLATFORM_PHP" ]; then echo "[doktainer] Composer platform.php: $PLATFORM_PHP"; fi',
|
||||
"if command -v composer >/dev/null 2>&1; then",
|
||||
" composer update --no-interaction --no-install --no-scripts --prefer-dist",
|
||||
' if [ -n "$PLATFORM_PHP" ]; then composer config platform.php "$PLATFORM_PHP"; fi',
|
||||
" composer update --no-interaction --no-install --no-scripts --prefer-dist --ignore-platform-req=ext-*",
|
||||
"elif command -v docker >/dev/null 2>&1; then",
|
||||
' docker run --rm -v "$TARGET_PATH":/app -w /app composer:2 composer update --no-interaction --no-install --no-scripts --prefer-dist',
|
||||
' docker run --rm -e PLATFORM_PHP="$PLATFORM_PHP" -v "$TARGET_PATH":/app -w /app composer:2 sh -lc \'if [ -n "$PLATFORM_PHP" ]; then composer config platform.php "$PLATFORM_PHP"; fi; composer update --no-interaction --no-install --no-scripts --prefer-dist --ignore-platform-req=ext-*\'',
|
||||
"else",
|
||||
' echo "composer.lock is missing and neither composer nor docker is available to generate it"',
|
||||
" exit 1",
|
||||
@@ -2455,6 +2501,20 @@ export async function deployContainerFromGitSource(
|
||||
buildPath,
|
||||
sourceProject,
|
||||
});
|
||||
if (
|
||||
sourceProject.likelyLaravel &&
|
||||
sourceProject.hasComposerJson &&
|
||||
!sourceProject.hasComposerLock
|
||||
) {
|
||||
await generateComposerLockIfMissing(server, {
|
||||
deploymentPath,
|
||||
buildPath,
|
||||
platformPhpVersion: resolveComposerPlatformPhpVersion(
|
||||
sourceProject.phpRequirement,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
const nixpacksBuildEnvs: string[] = [];
|
||||
|
||||
if (
|
||||
@@ -2476,6 +2536,10 @@ export async function deployContainerFromGitSource(
|
||||
nixpacksBuildEnvs.push("NIXPACKS_NODE_VERSION=22");
|
||||
}
|
||||
|
||||
if (sourceProject.likelyLaravel) {
|
||||
nixpacksBuildEnvs.push("COMPOSER_NO_DEV=1");
|
||||
}
|
||||
|
||||
const nixpacksStartCommand = resolveNixpacksStartCommand({
|
||||
sourceProject,
|
||||
startCommand: opts.startCommand,
|
||||
|
||||
Reference in new issue
Block a user