diff --git a/src/cli/install-tool/index.ts b/src/cli/install-tool/index.ts index c2d7367b9..4c3c37b72 100644 --- a/src/cli/install-tool/index.ts +++ b/src/cli/install-tool/index.ts @@ -40,6 +40,7 @@ import { YarnVersionResolver, } from '../tools/node/resolver'; import { NpmBaseInstallService } from '../tools/node/utils'; +import { PhpInstallService, PhpVersionResolver } from '../tools/php'; import { ComposerInstallService, ComposerVersionResolver, @@ -99,6 +100,7 @@ function prepareInstallContainer(): Container { container.bind(INSTALL_TOOL_TOKEN).to(KustomizeInstallService); container.bind(INSTALL_TOOL_TOKEN).to(MavenInstallService); container.bind(INSTALL_TOOL_TOKEN).to(NodeInstallService); + container.bind(INSTALL_TOOL_TOKEN).to(PhpInstallService); container.bind(INSTALL_TOOL_TOKEN).to(RenovateInstallService); container.bind(INSTALL_TOOL_TOKEN).to(SkopeoInstallService); container.bind(INSTALL_TOOL_TOKEN).to(SopsInstallService); @@ -127,6 +129,7 @@ function prepareResolveContainer(): Container { container.bind(TOOL_VERSION_RESOLVER).to(JavaJdkVersionResolver); container.bind(TOOL_VERSION_RESOLVER).to(MavenVersionResolver); container.bind(TOOL_VERSION_RESOLVER).to(NodeVersionResolver); + container.bind(TOOL_VERSION_RESOLVER).to(PhpVersionResolver); container.bind(TOOL_VERSION_RESOLVER).to(YarnVersionResolver); logger.trace('preparing container done'); diff --git a/src/cli/prepare-tool/index.ts b/src/cli/prepare-tool/index.ts index c865bf9f3..3d9bdcdbc 100644 --- a/src/cli/prepare-tool/index.ts +++ b/src/cli/prepare-tool/index.ts @@ -10,6 +10,7 @@ import { JavaPrepareService, } from '../tools/java'; import { NodePrepareService } from '../tools/node'; +import { PhpPrepareService } from '../tools/php'; import { ConanPrepareService } from '../tools/python/conan'; import { logger } from '../utils'; import { PrepareLegacyToolsService } from './prepare-legacy-tools.service'; @@ -34,6 +35,7 @@ function prepareContainer(): Container { container.bind(PREPARE_TOOL_TOKEN).to(JavaJrePrepareService); container.bind(PREPARE_TOOL_TOKEN).to(JavaJdkPrepareService); container.bind(PREPARE_TOOL_TOKEN).to(NodePrepareService); + container.bind(PREPARE_TOOL_TOKEN).to(PhpPrepareService); logger.trace('preparing container done'); return container; diff --git a/src/cli/tools/php/index.ts b/src/cli/tools/php/index.ts new file mode 100644 index 000000000..cd69794e2 --- /dev/null +++ b/src/cli/tools/php/index.ts @@ -0,0 +1,155 @@ +import fs from 'node:fs/promises'; +import { join } from 'node:path'; +import { isNonEmptyStringAndNotWhitespace } from '@sindresorhus/is'; +import { execa } from 'execa'; +import { inject, injectable } from 'inversify'; +import { BaseInstallService } from '../../install-tool/base-install.service'; +import { ToolVersionResolver } from '../../install-tool/tool-version-resolver'; +import { BasePrepareService } from '../../prepare-tool/base-prepare.service'; +import { + AptService, + CompressionService, + EnvService, + HttpService, + PathService, +} from '../../services'; +import { getDistro, logger } from '../../utils'; + +@injectable() +export class PhpPrepareService extends BasePrepareService { + override readonly name = 'php'; + + constructor( + @inject(PathService) pathSvc: PathService, + @inject(EnvService) envSvc: EnvService, + @inject(AptService) private readonly aptSvc: AptService, + ) { + super(pathSvc, envSvc); + } + + override async execute(): Promise { + const distro = await getDistro(); + + switch (distro.versionCode) { + case 'focal': + await this.aptSvc.install( + 'libjpeg-turbo8', + 'libmcrypt4', + 'libonig5', + 'libpng16-16', + 'libtidy5deb1', + 'libxslt1.1', + 'libzip5', + ); + break; + + case 'jammy': + case 'noble': + await this.aptSvc.install( + 'libjpeg-turbo8', + 'libmcrypt4', + 'libonig5', + 'libpng16-16', + 'libtidy5deb1', + 'libxslt1.1', + 'libzip4', + ); + + break; + + default: + throw new Error(`Unsupported distro version: ${distro.versionCode}`); + } + } +} + +@injectable() +export class PhpInstallService extends BaseInstallService { + readonly name = 'php'; + + private get ghArch(): string { + switch (this.envSvc.arch) { + case 'arm64': + return 'aarch64'; + case 'amd64': + return 'x86_64'; + } + } + + constructor( + @inject(EnvService) envSvc: EnvService, + @inject(PathService) pathSvc: PathService, + @inject(HttpService) private http: HttpService, + @inject(CompressionService) private compress: CompressionService, + ) { + super(pathSvc, envSvc); + } + + override async install(version: string): Promise { + const name = this.name; + const distro = await getDistro(); + let code = distro.versionCode; + + if (code === 'noble') { + logger.debug(`Using jammy prebuild for ${name} on ${code}`); + code = 'jammy'; + } + const filename = `${version}/${name}-${version}-${code}-${this.ghArch}.tar.xz`; + const checksumFileUrl = `https://github.com/containerbase/${name}-prebuild/releases/download/${filename}.sha512`; + const hasChecksum = await this.http.exists(checksumFileUrl); + let file: string; + + if (hasChecksum) { + // no distro specific prebuilds + const expectedChecksum = await this.getChecksum(checksumFileUrl); + file = await this.http.download({ + url: `https://github.com/containerbase/${name}-prebuild/releases/download/${filename}`, + checksumType: 'sha512', + expectedChecksum, + }); + } else { + file = await this.http.download({ + url: `https://github.com/containerbase/${name}-prebuild/releases/download/${filename}`, + }); + } + + const path = await this.pathSvc.ensureToolPath(this.name); + + await this.compress.extract({ file, cwd: path }); + } + + override async link(version: string): Promise { + await this.postInstall(version); + } + + override async postInstall(version: string): Promise { + const src = join(this.pathSvc.versionedToolPath(this.name, version), 'bin'); + await this.shellwrapper({ srcDir: src }); + } + + override async test(_version: string): Promise { + await execa('php', ['--version'], { + stdio: ['inherit', 'inherit', 1], + }); + } + + private async getChecksum(checksumFileUrl: string): Promise { + const checksumFile = await this.http.download({ url: checksumFileUrl }); + const expectedChecksum = (await fs.readFile(checksumFile, 'utf-8')).trim(); + return expectedChecksum; + } +} + +@injectable() +export class PhpVersionResolver extends ToolVersionResolver { + readonly tool = 'php'; + + async resolve(version: string | undefined): Promise { + if (!isNonEmptyStringAndNotWhitespace(version) || version === 'latest') { + return await this.http.get( + `https://github.com/containerbase/${this.tool}-prebuild/releases/latest/download/version`, + ); + } + return version; + } +} diff --git a/src/usr/local/containerbase/tools/v2/php.sh b/src/usr/local/containerbase/tools/v2/php.sh deleted file mode 100644 index bd3c0b2c7..000000000 --- a/src/usr/local/containerbase/tools/v2/php.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash - -function prepare_tool() { - local version_codename - local tool_path - - version_codename="$(get_distro)" - case "$version_codename" in - "focal") apt_install \ - libjpeg-turbo8 \ - libmcrypt4 \ - libonig5 \ - libpng16-16 \ - libtidy5deb1 \ - libxslt1.1 \ - libzip5 \ - ;; - "jammy"|"noble") apt_install \ - libjpeg-turbo8 \ - libmcrypt4 \ - libonig5 \ - libpng16-16 \ - libtidy5deb1 \ - libxslt1.1 \ - libzip4 \ - ;; - *) - echo "Tool '${TOOL_NAME}' not supported on: ${version_codename}! Please use ubuntu 'focal' or 'jammy'." >&2 - exit 1 - ;; - esac - tool_path=$(create_tool_path) -} - -function install_tool () { - local tool_path - local file - local base_url - local arch=${ARCHITECTURE} - local name=${TOOL_NAME} - local version=${TOOL_VERSION} - local version_codename - local checksum_file - local expected_checksum - local checksum_exists - - tool_path=$(find_tool_path) - - if [[ ! -d "${tool_path}" ]]; then - if [[ $(is_root) -ne 0 ]]; then - echo "${name} not prepared" - exit 1 - fi - prepare_tool - tool_path=$(find_tool_path) - fi - - base_url="https://github.com/containerbase/${name}-prebuild/releases/download" - version_codename=$(get_distro) - - if [[ "${version_codename}" == "noble" ]]; then - version_codename="jammy" - fi - - # not all releases have checksums - checksum_exists=$(file_exists "${base_url}/${version}/${name}-${version}-${version_codename}-${arch}.tar.xz.sha512") - if [[ "${checksum_exists}" == "200" ]]; then - checksum_file=$(get_from_url "${base_url}/${version}/${name}-${version}-${version_codename}-${arch}.tar.xz.sha512") - # get checksum from file - expected_checksum=$(cat "${checksum_file}") - fi - - # download file - file=$(get_from_url \ - "${base_url}/${version}/${name}-${version}-${version_codename}-${arch}.tar.xz" \ - "${name}-${version}-${version_codename}-${arch}.tar.xz" \ - "${expected_checksum}" \ - sha512sum - ) - - if [[ -z "$file" ]]; then - echo "Download failed" >&2 - exit 1; - fi - - tar -C "${tool_path}" -xf "${file}" -} - -function link_tool () { - local versioned_tool_path - versioned_tool_path=$(find_versioned_tool_path) - - shell_wrapper php "${versioned_tool_path}/bin" - [[ -n $SKIP_VERSION ]] || php --version -} diff --git a/test/php/Dockerfile b/test/php/Dockerfile index b0603f165..688d10328 100644 --- a/test/php/Dockerfile +++ b/test/php/Dockerfile @@ -73,9 +73,8 @@ RUN prepare-tool php USER 1000 - -# renovate: datasource=github-releases packageName=containerbase/php-prebuild -RUN install-tool php 8.3.9 +# test without version +RUN install-tool php # test without version RUN install-tool composer