Skip to content

Commit

Permalink
feat(php): convert composer installer (#2848)
Browse files Browse the repository at this point in the history
* feat(php): convert composer installer

* test: disable latest test

* feat(php): add composer version resolver

* docs: update custom registries
  • Loading branch information
viceice authored Jun 27, 2024
1 parent 556e1b1 commit b336e4d
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 41 deletions.
11 changes: 8 additions & 3 deletions docs/custom-registries.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,17 +424,22 @@ https://github.com/containerbase/php-prebuild/releases/8.3.2/php-8.3.2-jammy-x86

Composer releases are downloaded from:

- `https://github.com/containerbase/maven-prebuild/releases`
- `https://getcomposer.org/download`
- `https://api.github.com/repos/composer/composer/releases/latest`
- `https://getcomposer.org/versions`

The second url is only used when `latest` is passed as version.
Then we try to find the latest version from GitHub.
The first url is preferred and the second is used as fallback for older versions.
The last url is only used when `latest` or nothing is passed as version.
Then we try to find the latest version from getcomposer.org.

Samples:

```txt
https://github.com/containerbase/composer-prebuild/releases/2.7.7/composer-2.7.7.tar.xz.sha512
https://github.com/containerbase/composer-prebuild/releases/2.7.7/composer-2.7.7.tar.xz
https://getcomposer.org/download/2.6.6/composer.phar.sha256sum
https://getcomposer.org/download/2.6.6/composer.phar
https://getcomposer.org/versions
```

## `powershell`
Expand Down
8 changes: 7 additions & 1 deletion src/cli/install-tool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ import {
YarnVersionResolver,
} from '../tools/node/resolver';
import { InstallNpmBaseService } from '../tools/node/utils';
import {
ComposerInstallService,
ComposerVersionResolver,
} from '../tools/php/composer';
import { InstallCocoapodsService } from '../tools/ruby/gem';
import { InstallRubyBaseService } from '../tools/ruby/utils';
import { logger } from '../utils';
Expand All @@ -55,15 +59,16 @@ function prepareInstallContainer(): Container {
container.bind(InstallLegacyToolService).toSelf();

// tool services
container.bind(INSTALL_TOOL_TOKEN).to(ComposerInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallBazeliskService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallBunService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallGleamService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallCocoapodsService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallDartService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallDockerService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallDotnetService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallFlutterService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallFluxService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallGleamService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallGradleService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallJavaService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallJavaJreService);
Expand All @@ -87,6 +92,7 @@ function prepareResolveContainer(): Container {
container.bind(ToolVersionResolverService).toSelf();

// tool version resolver
container.bind(TOOL_VERSION_RESOLVER).to(ComposerVersionResolver);
container.bind(TOOL_VERSION_RESOLVER).to(GradleVersionResolver);
container.bind(TOOL_VERSION_RESOLVER).to(JavaVersionResolver);
container.bind(TOOL_VERSION_RESOLVER).to(JavaJreVersionResolver);
Expand Down
1 change: 1 addition & 0 deletions src/cli/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const NoPrepareTools = [
'bun',
'bundler',
'cocoapods',
'composer',
'corepack',
'flux',
'gleam',
Expand Down
135 changes: 135 additions & 0 deletions src/cli/tools/php/composer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
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 { sort } from 'semver';
import { z } from 'zod';
import { InstallToolBaseService } from '../../install-tool/install-tool-base.service';
import { ToolVersionResolver } from '../../install-tool/tool-version-resolver';
import {
CompressionService,
EnvService,
HttpService,
PathService,
} from '../../services';
import type { HttpChecksumType } from '../../services/http.service';
import { logger } from '../../utils';

@injectable()
export class ComposerInstallService extends InstallToolBaseService {
readonly name = 'composer';

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<void> {
const name = this.name;
let filename = `${name}-${version}.tar.xz`;
let url = `https://github.com/containerbase/${name}-prebuild/releases/download/${version}/${filename}`;
let checksumFileUrl = `${url}.sha512`;
const isOnGithub = await this.http.exists(checksumFileUrl);
let file: string;

if (isOnGithub) {
logger.info(`using github`);
const checksumFile = await this.http.download({ url: checksumFileUrl });
const expectedChecksum = (
await fs.readFile(checksumFile, 'utf-8')
).trim();
file = await this.http.download({
url,
checksumType: 'sha512',
expectedChecksum,
});
} else {
logger.info(`using getcomposer.org`);
// fallback to getcomposer.org
filename = `composer.phar`;
url = `https://getcomposer.org/download/${version}/composer.phar`;
checksumFileUrl = `${url}.sha256sum`;
let expectedChecksum: string | undefined;
let checksumType: HttpChecksumType | undefined;
if (await this.http.exists(checksumFileUrl)) {
logger.debug(`using sha256sum checksum for ${filename}`);
expectedChecksum = await this.readChecksum(`${url}.sha256sum`);
checksumType = 'sha256';
} else {
throw new Error(`checksum file not found for ${filename}`);
}

if (!checksumType || !expectedChecksum) {
throw new Error(`checksum not found for ${filename}`);
}

file = await this.http.download({
url,
checksumType,
expectedChecksum,
});
}

let path = await this.pathSvc.findToolPath(this.name);
if (!path) {
path = await this.pathSvc.createToolPath(this.name);
}

if (isOnGithub) {
await this.compress.extract({ file, cwd: path });
} else {
// from getcomposer.org
path = await this.pathSvc.createVersionedToolPath(this.name, version);
path = join(path, 'bin');
await fs.mkdir(path);
path = join(path, filename);
await fs.cp(file, path);
await fs.chmod(path, 0o755);
}
}

override async link(version: string): Promise<void> {
const src = join(this.pathSvc.versionedToolPath(this.name, version), 'bin');
await this.shellwrapper({ srcDir: src });
}

override async test(_version: string): Promise<void> {
await execa('composer', ['--version'], {
stdio: ['inherit', 'inherit', 1],
});
}

private async readChecksum(url: string): Promise<string | undefined> {
const checksumFile = await this.http.download({ url });
return (await fs.readFile(checksumFile, 'utf-8')).split(' ')[0]?.trim();
}
}

@injectable()
export class ComposerVersionResolver extends ToolVersionResolver {
readonly tool = 'composer';

async resolve(version: string | undefined): Promise<string | undefined> {
if (!isNonEmptyStringAndNotWhitespace(version) || version === 'latest') {
const meta = ComposerVersionsSchema.parse(
await this.http.getJson('https://getcomposer.org/versions'),
);
// we know that the latest version is the first entry, so search for first lts
return meta;
}
return version;
}
}

const ComposerVersionsSchema = z
.object({
stable: z.array(z.object({ version: z.string() })),
})
.transform(({ stable }) => {
return sort(stable.map((v) => v.version)).pop();
});
36 changes: 0 additions & 36 deletions src/usr/local/containerbase/tools/v2/composer.sh

This file was deleted.

7 changes: 6 additions & 1 deletion test/php/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ RUN install-tool php 8.3.7
# renovate: datasource=github-releases packageName=composer/composer
RUN install-tool composer 2.7.7

# test with latest version
RUN install-tool composer latest

USER 1000
Expand Down Expand Up @@ -152,7 +153,8 @@ USER 1000
# renovate: datasource=github-releases packageName=containerbase/php-prebuild
RUN install-tool php 8.3.7

RUN install-tool composer latest
# test without version
RUN install-tool composer

# renovate: datasource=github-releases packageName=composer/composer
RUN install-tool composer 2.7.7
Expand All @@ -176,6 +178,9 @@ RUN set -ex; \
RUN php --version
RUN composer --version

# test from getcomposer.org
RUN install-tool composer 2.0.0

#--------------------------------------
# final
#--------------------------------------
Expand Down

0 comments on commit b336e4d

Please sign in to comment.