From 2b98e6b803a1242432ba0a510c1a7e24b0a29e5f Mon Sep 17 00:00:00 2001 From: Victor Sollerhed Date: Tue, 7 Jul 2026 11:08:22 +0200 Subject: [PATCH] feat: add apm (Agent Package Manager) install support Add an install service for APM (Agent Package Manager) which downloads the self-contained native binary bundle from the `microsoft/apm` GitHub releases, verifies the published `.sha256` checksum, extracts the PyInstaller `onedir` layout (the `apm` binary plus its sibling `_internal` directory), and wraps the binary onto PATH. This is a prerequisite for adding APM (`apm.yml`) support to Renovate. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C --- src/cli/install-tool/index.ts | 2 ++ src/cli/tools/apm.ts | 59 +++++++++++++++++++++++++++++++++++ src/cli/tools/index.ts | 1 + test/latest/Dockerfile | 5 ++- 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/cli/tools/apm.ts diff --git a/src/cli/install-tool/index.ts b/src/cli/install-tool/index.ts index d056aa131b..6d98c8e0b6 100644 --- a/src/cli/install-tool/index.ts +++ b/src/cli/install-tool/index.ts @@ -8,6 +8,7 @@ import { createContainer, } from '../services/index.ts'; import { ApkoInstallService } from '../tools/apko.ts'; +import { ApmInstallService } from '../tools/apm.ts'; import { BazeliskInstallService } from '../tools/bazelisk.ts'; import { BufInstallService } from '../tools/buf.ts'; import { BunInstallService } from '../tools/bun.ts'; @@ -134,6 +135,7 @@ async function prepareInstallContainer(): Promise { // modern tool services container.bind(INSTALL_TOOL_TOKEN).to(AndroidSdkCmdlineToolsInstallService); container.bind(INSTALL_TOOL_TOKEN).to(ApkoInstallService); + container.bind(INSTALL_TOOL_TOKEN).to(ApmInstallService); container.bind(INSTALL_TOOL_TOKEN).to(ComposerInstallService); container.bind(INSTALL_TOOL_TOKEN).to(BazeliskInstallService); container.bind(INSTALL_TOOL_TOKEN).to(BuildxInstallService); diff --git a/src/cli/tools/apm.ts b/src/cli/tools/apm.ts new file mode 100644 index 0000000000..96c8896777 --- /dev/null +++ b/src/cli/tools/apm.ts @@ -0,0 +1,59 @@ +import fs from 'node:fs/promises'; +import { injectFromHierarchy, injectable } from 'inversify'; +import { BaseInstallService } from '../install-tool/base-install.service.ts'; + +@injectable() +@injectFromHierarchy() +export class ApmInstallService extends BaseInstallService { + readonly name = 'apm'; + + private get ghArch(): string { + switch (this.envSvc.arch) { + case 'arm64': + return 'arm64'; + case 'amd64': + return 'x86_64'; + } + } + + override async install(version: string): Promise { + /** + * APM (Agent Package Manager) ships self-contained PyInstaller `onedir` + * bundles (an `apm` binary next to an `_internal` directory) as + * `apm--.tar.gz` release assets, each accompanied by a + * `.sha256` sidecar. + * @see {@link https://github.com/microsoft/apm/releases} + */ + const baseUrl = `https://github.com/microsoft/apm/releases/download/v${version}/`; + const filename = `apm-linux-${this.ghArch}.tar.gz`; + const url = `${baseUrl}${filename}`; + + const checksumFile = await this.http.download({ url: `${url}.sha256` }); + const expectedChecksum = (await fs.readFile(checksumFile, 'utf-8')) + .split('\n') + .find((l) => l.includes(filename)) + ?.split(/\s+/)[0]; + + const file = await this.http.download({ + url, + checksumType: 'sha256', + expectedChecksum, + }); + + await this.pathSvc.ensureToolPath(this.name); + + const path = await this.pathSvc.createVersionedToolPath(this.name, version); + // strip the top-level `apm-linux-` directory so the `apm` binary and + // its sibling `_internal` bundle end up directly in the versioned path. + await this.compress.extract({ file, cwd: path, strip: 1 }); + } + + override async link(version: string): Promise { + const src = this.pathSvc.versionedToolPath(this.name, version); + await this.shellwrapper({ srcDir: src }); + } + + override async test(_version: string): Promise { + await this._spawn(this.name, ['--version']); + } +} diff --git a/src/cli/tools/index.ts b/src/cli/tools/index.ts index 67fc281d88..c0c8552f32 100644 --- a/src/cli/tools/index.ts +++ b/src/cli/tools/index.ts @@ -3,6 +3,7 @@ import type { InstallToolType } from '../utils'; export const NoPrepareTools = [ 'android-sdk-cmdline-tools', 'apko', + 'apm', 'bazelisk', 'bower', 'buf', diff --git a/test/latest/Dockerfile b/test/latest/Dockerfile index 01443a6f50..278c12d208 100644 --- a/test/latest/Dockerfile +++ b/test/latest/Dockerfile @@ -212,7 +212,7 @@ RUN prepare-tool all RUN set -ex; [ -d /usr/local/erlang ] && echo "works" || exit 1; #-------------------------------------- -# test: bazelisk, buf, bun, deno, devbox, helmfile, kustomize, skopeo, tofu, vendir +# test: apm, bazelisk, buf, bun, deno, devbox, helmfile, kustomize, skopeo, tofu, vendir #-------------------------------------- FROM base AS teste @@ -231,6 +231,9 @@ RUN install-tool deno 2.9.1 # renovate: datasource=github-releases packageName=chainguard-dev/apko RUN install-tool apko 1.2.22 +# renovate: datasource=github-releases packageName=microsoft/apm +RUN install-tool apm 0.24.0 + # renovate: datasource=github-releases packageName=jetify-com/devbox RUN install-tool devbox 0.17.5