From 691ae92d7193d27202d0bb54e9bd134f355c84a3 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Mon, 10 Aug 2026 10:03:21 +0100 Subject: [PATCH 1/3] feat: add support for GitHub CLI `gh` As per #7154, we can add support for the GitHub CLI, so it can be used for other purposes. We're only supporting the v2 of the GitHub CLI, and don't currently expose any means for installing extensions. Closes #7154. Co-authored-by: Claude Opus 5 --- .github/renovate.json | 2 ++ docs/custom-registries.md | 14 ++++++++ src/cli/install-tool/index.ts | 2 ++ src/cli/tools/gh.ts | 63 +++++++++++++++++++++++++++++++++++ src/cli/tools/index.ts | 1 + test/latest/Dockerfile | 7 +++- test/latest/Dockerfile.arm64 | 9 +++++ 7 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/cli/tools/gh.ts diff --git a/.github/renovate.json b/.github/renovate.json index d0f3d9201a..512eb455ad 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -75,6 +75,7 @@ "docker", "dotnet", "flutter", + "gh", "git", "git-lfs", "gleam", @@ -129,6 +130,7 @@ "docker", "dotnet", "flutter", + "gh", "git", "git-lfs", "gleam", diff --git a/docs/custom-registries.md b/docs/custom-registries.md index 0aea5b0997..d915a42ad4 100644 --- a/docs/custom-registries.md +++ b/docs/custom-registries.md @@ -278,6 +278,20 @@ https://github.com/fluxcd/flux2/releases/download/v0.19.0/flux_0.19.0_linux_amd6 https://github.com/fluxcd/flux2/releases/download/v2.1.0/flux_2.1.0_linux_arm64.tar.gz ``` +## `gh` + +GitHub CLI releases are downloaded from: + +- `https://github.com/cli/cli/releases` + +Samples: + +```txt +https://github.com/cli/cli/releases/download/v2.97.0/gh_2.97.0_linux_amd64.tar.gz +https://github.com/cli/cli/releases/download/v2.97.0/gh_2.97.0_linux_arm64.tar.gz +https://github.com/cli/cli/releases/download/v2.97.0/gh_2.97.0_checksums.txt +``` + ## `git` Git is downloaded from: diff --git a/src/cli/install-tool/index.ts b/src/cli/install-tool/index.ts index 6d98c8e0b6..80ea5d39b7 100644 --- a/src/cli/install-tool/index.ts +++ b/src/cli/install-tool/index.ts @@ -30,6 +30,7 @@ import { ElixirInstallService } from '../tools/erlang/elixir.ts'; import { ErlangInstallService } from '../tools/erlang/index.ts'; import { FlutterInstallService } from '../tools/flutter.ts'; import { FluxInstallService } from '../tools/flux.ts'; +import { GhInstallService } from '../tools/gh.ts'; import { GitLfsInstallService } from '../tools/git/lfs.ts'; import { GleamInstallService } from '../tools/gleam.ts'; import { GolangInstallService } from '../tools/golang.ts'; @@ -154,6 +155,7 @@ async function prepareInstallContainer(): Promise { container.bind(INSTALL_TOOL_TOKEN).to(ErlangInstallService); container.bind(INSTALL_TOOL_TOKEN).to(FlutterInstallService); container.bind(INSTALL_TOOL_TOKEN).to(FluxInstallService); + container.bind(INSTALL_TOOL_TOKEN).to(GhInstallService); container.bind(INSTALL_TOOL_TOKEN).to(GitLfsInstallService); container.bind(INSTALL_TOOL_TOKEN).to(GhcInstallService); container.bind(INSTALL_TOOL_TOKEN).to(GleamInstallService); diff --git a/src/cli/tools/gh.ts b/src/cli/tools/gh.ts new file mode 100644 index 0000000000..bb5b94670e --- /dev/null +++ b/src/cli/tools/gh.ts @@ -0,0 +1,63 @@ +import fs from 'node:fs/promises'; +import { join } from 'node:path'; +import { injectFromHierarchy, injectable } from 'inversify'; +import { BaseInstallService } from '../install-tool/base-install.service.ts'; +import { semverSatisfies } from '../utils/index.ts'; + +@injectable() +@injectFromHierarchy() +export class GhInstallService extends BaseInstallService { + readonly name = 'gh'; + + override async install(version: string): Promise { + /** + * The GitHub CLI ships self-contained Go binaries as `gh__linux_.tar.gz` release assets, alongside a single `gh__checksums.txt` covering every asset of that release. + * @see {@link https://github.com/cli/cli/releases} + */ + const baseUrl = `https://github.com/cli/cli/releases/download/v${version}/`; + const dirname = `gh_${version}_linux_${this.envSvc.arch}`; + const filename = `${dirname}.tar.gz`; + + const checksumFile = await this.http.download({ + url: `${baseUrl}gh_${version}_checksums.txt`, + }); + const expectedChecksum = (await fs.readFile(checksumFile, 'utf-8')) + .split('\n') + .find((l) => l.endsWith(filename)) + ?.split(/\s+/)[0]; + + const file = await this.http.download({ + url: `${baseUrl}${filename}`, + checksumType: 'sha256', + expectedChecksum, + }); + + await this.pathSvc.ensureToolPath(this.name); + + const path = await this.pathSvc.createVersionedToolPath(this.name, version); + // the archive also ships man pages and completions under `share/`, which + // aren't needed at runtime, so only extract `bin/gh`. + await this.compress.extract({ + file, + cwd: path, + strip: 1, + files: [`${dirname}/bin/gh`], + }); + } + + override async link(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 this._spawn(this.name, ['--version']); + } + + override async validate(version: string): Promise { + // only the `v2` line ships the release assets we consume + return ( + (await super.validate(version)) && semverSatisfies(version, '^2.0.0') + ); + } +} diff --git a/src/cli/tools/index.ts b/src/cli/tools/index.ts index c0c8552f32..7e0b817fc6 100644 --- a/src/cli/tools/index.ts +++ b/src/cli/tools/index.ts @@ -19,6 +19,7 @@ export const NoPrepareTools = [ 'devbox', 'docker-compose', 'flux', + 'gh', 'git-lfs', 'gleam', 'gradle', diff --git a/test/latest/Dockerfile b/test/latest/Dockerfile index adb1e9fb7a..ed52827755 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: apm, bazelisk, buf, bun, deno, devbox, helmfile, kustomize, skopeo, tofu, vendir +# test: apm, bazelisk, buf, bun, deno, devbox, gh, helmfile, kustomize, skopeo, tofu, vendir #-------------------------------------- FROM base AS teste @@ -237,6 +237,9 @@ RUN install-tool apm 0.28.0 # renovate: datasource=github-releases packageName=jetify-com/devbox RUN install-tool devbox 0.17.5 +# renovate: datasource=github-releases packageName=cli/cli +RUN install-tool gh v2.97.0 + # renovate: datasource=github-releases packageName=gleam-lang/gleam RUN install-tool gleam 1.18.1 @@ -291,6 +294,8 @@ USER 12021 RUN bazel --version +RUN gh --version + RUN vendir --version | grep "${VENDIR_VERSION#v}" RUN helmfile version | grep "${HELMFILE_VERSION#v}" diff --git a/test/latest/Dockerfile.arm64 b/test/latest/Dockerfile.arm64 index 8ef0f37d43..ed559cda87 100644 --- a/test/latest/Dockerfile.arm64 +++ b/test/latest/Dockerfile.arm64 @@ -80,6 +80,14 @@ FROM base AS test-devbox # renovate: datasource=github-releases packageName=jetify-com/devbox RUN install-tool devbox 0.17.5 +#-------------------------------------- +# Image: gh +#-------------------------------------- +FROM base AS test-gh + +# renovate: datasource=github-releases packageName=cli/cli +RUN install-tool gh v2.97.0 + #-------------------------------------- # Image: gleam #-------------------------------------- @@ -217,6 +225,7 @@ COPY --from=test-apko /.dummy /.dummy COPY --from=test-apm /.dummy /.dummy COPY --from=test-devbox /.dummy /.dummy COPY --from=test-docker /.dummy /.dummy +COPY --from=test-gh /.dummy /.dummy COPY --from=test-git /.dummy /.dummy COPY --from=test-git-lfs /.dummy /.dummy COPY --from=test-gleam /.dummy /.dummy From e8d775199d6607541f0be8c719c3f7bc04281508 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Mon, 10 Aug 2026 10:42:15 +0100 Subject: [PATCH 2/3] fixup! feat: add support for GitHub CLI `gh` Co-authored-by: Claude Sonnet 5 --- .github/renovate.json | 3 ++- test/latest/Dockerfile | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/renovate.json b/.github/renovate.json index 512eb455ad..b36327aed4 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -2,7 +2,8 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "github>containerbase/.github", - "github>containerbase/.github//merge-queue.json" + "github>containerbase/.github//merge-queue.json", + "customManagers:dockerfileVersions" ], "ignorePaths": ["**/node_modules/**", "test/*/test/**"], "enabledManagers": [ diff --git a/test/latest/Dockerfile b/test/latest/Dockerfile index ed52827755..b0d750fd94 100644 --- a/test/latest/Dockerfile +++ b/test/latest/Dockerfile @@ -237,6 +237,8 @@ RUN install-tool apm 0.28.0 # renovate: datasource=github-releases packageName=jetify-com/devbox RUN install-tool devbox 0.17.5 +# renovate: datasource=github-releases depName=gh-dash packageName=dlvhdr/gh-dash +ENV GH_DASH_EXTENSION_VERSION=v4.25.2 # renovate: datasource=github-releases packageName=cli/cli RUN install-tool gh v2.97.0 @@ -295,6 +297,8 @@ USER 12021 RUN bazel --version RUN gh --version +RUN gh extension install dlvhdr/gh-dash --pin $GH_DASH_EXTENSION_VERSION +RUN gh dash --version RUN vendir --version | grep "${VENDIR_VERSION#v}" From 342042146ed287f00c692638d497899b2279fff5 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Mon, 10 Aug 2026 11:05:58 +0100 Subject: [PATCH 3/3] fixup! fixup! feat: add support for GitHub CLI `gh` --- .github/renovate.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index b36327aed4..512eb455ad 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -2,8 +2,7 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "github>containerbase/.github", - "github>containerbase/.github//merge-queue.json", - "customManagers:dockerfileVersions" + "github>containerbase/.github//merge-queue.json" ], "ignorePaths": ["**/node_modules/**", "test/*/test/**"], "enabledManagers": [