Merge pull request #379 from aminya/alpine

feat: support Alpine + add setup-alpine package + detect externally managed python
This commit is contained in:
Amin Ya 2025-03-23 12:25:49 -07:00 committed by GitHub
commit be9dd533df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 737 additions and 69 deletions

View File

@ -244,6 +244,7 @@ jobs:
- { distro: "ubuntu", image: "setup-cpp-ubuntu", BASE_VERSION: "20.04", tag: "20.04-1.3.0" }
- { distro: "fedora", image: "setup-cpp-fedora", tag: "40-1.3.0" }
- { distro: "arch", image: "setup-cpp-arch", tag: "base-1.3.0" }
- { distro: "alpine", image: "setup-cpp-alpine", BASE_VERSION: "22-alpine3.21", tag: "3.21-1.3.0" }
include:
- os: ubuntu-24.04-arm
platform: linux/arm64
@ -254,6 +255,9 @@ jobs:
- os: ubuntu-24.04-arm
platform: linux/arm64
container: { distro: "ubuntu", image: "setup-cpp-ubuntu", BASE_VERSION: "20.04", tag: "20.04-1.3.0" }
- os: ubuntu-24.04-arm
platform: linux/arm64
container: { distro: "alpine", image: "setup-cpp-alpine", BASE_VERSION: "22-alpine3.21", tag: "3.21-1.3.0" }
steps:
- uses: actions/checkout@v4
with:
@ -300,7 +304,7 @@ jobs:
docker tag aminya/${{ matrix.container.image }}:${{ matrix.container.tag }}-${{ steps.platform.outputs.suffix }} aminya/${{ matrix.container.image }}:latest
- name: Push latest to Docker Hub
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'ubuntu' }}
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'ubuntu' && matrix.container.distro != 'alpine' }}
run: docker push aminya/${{ matrix.container.image }}:latest
- name: Docker Readme for setup-cpp-${{matrix.container.distro }}
@ -331,7 +335,7 @@ jobs:
docker tag aminya/${{ matrix.container.image }}-llvm:${{ matrix.container.tag }}-${{ steps.platform.outputs.suffix }} aminya/${{ matrix.container.image }}-llvm:latest
- name: Push latest to Docker Hub
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'ubuntu' }}
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'ubuntu' && matrix.container.distro != 'alpine' }}
run: docker push aminya/${{ matrix.container.image }}-llvm:latest
- name: Docker Readme for setup-cpp-${{matrix.container.distro }}-llvm
@ -372,7 +376,7 @@ jobs:
docker tag aminya/${{ matrix.container.image }}-gcc:${{ matrix.container.tag }}-${{ steps.platform.outputs.suffix }} aminya/${{ matrix.container.image }}-gcc:latest
- name: Push latest to Docker Hub
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'ubuntu' }}
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'ubuntu' && matrix.container.distro != 'alpine' }}
run: docker push aminya/${{ matrix.container.image }}-gcc:latest
- name: Docker Readme for setup-cpp-${{matrix.container.distro }}-gcc
@ -396,7 +400,7 @@ jobs:
- name: Build setup-cpp-${{matrix.container.distro }}-mingw
id: build_mingw
if: ${{ matrix.container.distro != 'fedora' }}
if: ${{ matrix.container.distro != 'fedora' && !(matrix.container.distro == 'alpine' && matrix.platform == 'linux/arm64') }}
uses: docker/build-push-action@v6
with:
context: .
@ -410,16 +414,16 @@ jobs:
cache-to: type=inline
- name: Tag latest locally
if: ${{ matrix.container.distro != 'fedora' }}
if: ${{ matrix.container.distro != 'fedora' && !(matrix.container.distro == 'alpine' && matrix.platform == 'linux/arm64') }}
run: |
docker tag aminya/${{ matrix.container.image }}-mingw:${{ matrix.container.tag }}-${{ steps.platform.outputs.suffix }} aminya/${{ matrix.container.image }}-mingw:latest
- name: Push latest to Docker Hub
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'ubuntu' && matrix.container.distro != 'fedora' }}
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'ubuntu' && matrix.container.distro != 'fedora' && matrix.container.distro != 'alpine' }}
run: docker push aminya/${{ matrix.container.image }}-mingw:latest
- name: Docker Readme for setup-cpp-${{matrix.container.distro }}-mingw
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'fedora' }}
if: ${{ github.event_name != 'pull_request' && matrix.container.distro != 'fedora' && !(matrix.container.distro == 'alpine' && matrix.platform == 'linux/arm64') }}
uses: peter-evans/dockerhub-description@v4
with:
username: aminya
@ -428,7 +432,7 @@ jobs:
readme-filepath: ./README_DOCKER.md
- name: Test Mingw
if: ${{ !contains(github.event.head_commit.message, '[skip test]') && matrix.container.distro != 'fedora' }}
if: ${{ !contains(github.event.head_commit.message, '[skip test]') && matrix.container.distro != 'fedora' && matrix.container.distro != 'alpine' }}
uses: docker/build-push-action@v6
with:
context: .
@ -459,6 +463,11 @@ jobs:
- { distro: "ubuntu", image: "setup-cpp-ubuntu", tag: "24.04-1.3.0", suffix: "-llvm", latest: true }
- { distro: "ubuntu", image: "setup-cpp-ubuntu", tag: "24.04-1.3.0", suffix: "-gcc", latest: true }
- { distro: "ubuntu", image: "setup-cpp-ubuntu", tag: "24.04-1.3.0", suffix: "-mingw", latest: true }
- { distro: "alpine", image: "setup-cpp-alpine", tag: "3.21-1.3.0", suffix: "", latest: true }
- { distro: "alpine", image: "setup-cpp-alpine", tag: "3.21-1.3.0", suffix: "-llvm", latest: true }
- { distro: "alpine", image: "setup-cpp-alpine", tag: "3.21-1.3.0", suffix: "-gcc", latest: true }
- { distro: "alpine", image: "setup-cpp-alpine", tag: "3.21-1.3.0", suffix: "-mingw", latest: true }
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

View File

@ -0,0 +1,10 @@
FROM aminya/setup-cpp-alpine:latest AS setup-cpp-alpine-gcc
# install gcc
RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \
--compiler gcc && \
# cleanup
rm -rf /var/cache/apk/*
SHELL ["/entrypoint.sh", "/bin/sh", "-c"]
ENTRYPOINT ["/entrypoint.sh", "/bin/sh"]

View File

@ -0,0 +1,10 @@
FROM aminya/setup-cpp-alpine:latest AS setup-cpp-alpine-llvm
# install llvm
RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \
--compiler llvm && \
# cleanup
rm -rf /var/cache/apk/*
SHELL ["/entrypoint.sh", "/bin/sh", "-c"]
ENTRYPOINT ["/entrypoint.sh", "/bin/sh"]

View File

@ -0,0 +1,11 @@
FROM aminya/setup-cpp-alpine:latest AS setup-cpp-alpine-mingw
# install mingw/powershell
RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \
--compiler mingw \
--powershell true && \
# cleanup
rm -rf /var/cache/apk/*
SHELL ["/entrypoint.sh", "/bin/sh", "-c"]
ENTRYPOINT ["/entrypoint.sh", "/bin/sh"]

View File

@ -0,0 +1,34 @@
ARG BASE_VERSION=22-alpine3.21
#### Base Image with Node.js
FROM --platform=$BUILDPLATFORM node:${BASE_VERSION} AS alpine-nodejs
#### Base Image with Tools
FROM alpine-nodejs AS setup-cpp-alpine
COPY "./dist/modern" "/usr/lib/setup-cpp/"
# install the cpp tools
RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \
--cmake true \
--ninja true \
--task true \
--python true \
--make true \
--cppcheck true \
--gcovr true \
--doxygen true \
--vcpkg true \
--ccache true \
--conan true \
--cmakelang true \
--meson true && \
# cleanup
rm -rf /var/cache/apk/*
# Custom entrypoint due to bash -l limitations on Alpine
RUN printf '#!/bin/bash\nsource $HOME/.cpprc\nexec "$@"\n' > /entrypoint.sh && \
chmod +x /entrypoint.sh
SHELL ["/entrypoint.sh", "/bin/sh", "-c"]
ENTRYPOINT ["/entrypoint.sh", "/bin/sh"]

View File

@ -0,0 +1,15 @@
#### Building (example)
FROM aminya/setup-cpp-alpine-gcc AS builder
COPY ./dev/cpp_vcpkg_project /home/app
WORKDIR /home/app
RUN task build
#### Running environment
# use a fresh image as the runner
FROM alpine:3.21 AS runner
# copy the built binaries and their runtime dependencies
COPY --from=builder /home/app/build/my_exe/Release/ /home/app/
WORKDIR /home/app/
ENTRYPOINT ["./my_exe"]

View File

@ -0,0 +1,15 @@
#### Building (example)
FROM aminya/setup-cpp-alpine-llvm AS builder
COPY ./dev/cpp_vcpkg_project /home/app
WORKDIR /home/app
RUN task build
#### Running environment
# use a fresh image as the runner
FROM alpine:3.21 AS runner
# copy the built binaries and their runtime dependencies
COPY --from=builder /home/app/build/my_exe/Release/ /home/app/
WORKDIR /home/app/
ENTRYPOINT ["./my_exe"]

View File

@ -0,0 +1,7 @@
#### Cross Building (example)
FROM aminya/setup-cpp-alpine-mingw AS builder-mingw
COPY ./dev/cpp_vcpkg_project /home/app
WORKDIR /home/app
RUN bash -c 'source ~/.cpprc \
&& task build_cross_mingw'

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -135,6 +135,7 @@
"safe-stable-stringify": "^2.5.0",
"semver": "7.7.1",
"setup-apt": "workspace:*",
"setup-alpine": "workspace:*",
"setup-brew": "workspace:*",
"setup-python": "github:aminya/setup-python#9700887",
"shx": "0.4.0",
@ -184,6 +185,7 @@
"retry-as-promised",
"semver",
"setup-apt",
"setup-alpine",
"setup-brew",
"setup-python",
"simple-update-notifier",

View File

@ -1,6 +1,6 @@
{
"name": "envosman",
"version": "1.0.3",
"version": "1.0.4",
"description": "Manage environment variables, PATH, and rc files",
"repository": "https://github.com/aminya/setup-cpp",
"homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/envosman",

View File

@ -89,6 +89,8 @@ async function addEnvSystem(name: string, valGiven: string | undefined, options:
await appendFile(options.rcPath, `\nexport ${name}="${val}"\n`)
info(`${name}="${val}" was added to "${options.rcPath}`)
}
// eslint-disable-next-line require-atomic-updates
process.env[name] = val
return
}
default: {

View File

@ -0,0 +1,3 @@
{
"extends": "../../.eslintrc.json"
}

View File

@ -0,0 +1,55 @@
{
"name": "setup-alpine",
"version": "1.0.0",
"description": "Setup apk packages and repositories in Alpine Linux distributions",
"repository": "https://github.com/aminya/setup-cpp",
"homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/setup-alpine",
"license": "Apache-2.0",
"author": "Amin Yahyaabadi",
"main": "./dist/index.js",
"source": "./src/index.ts",
"type": "module",
"files": [
"dist",
"src",
"tsconfig.json"
],
"scripts": {
"build": "tsc --pretty",
"dev": "tsc --watch --pretty",
"lint.tsc": "tsc --noEmit --pretty",
"lint.eslint": "eslint '**/*.{ts,tsx,js,jsx,cjs,mjs,json,yaml}' --no-error-on-unmatched-pattern --cache --cache-location ./.cache/eslint/ --fix",
"prepublishOnly": "pnpm run build"
},
"dependencies": {
"@types/node": "22.13.11",
"admina": "^1.0.1",
"ci-info": "^4.0.0",
"path-exists": "^5.0.0",
"ci-log": "workspace:*",
"envosman": "workspace:*",
"which": "4.0.0",
"execa": "7.2.0",
"memoizee": "^0.4.17"
},
"engines": {
"node": ">=12"
},
"keywords": [
"setup",
"apk",
"apk-add",
"repository",
"alpine",
"install",
"setup-apk",
"repositories",
"linux",
"alpine-linux",
"package"
],
"devDependencies": {
"@types/memoizee": "0.4.11",
"@types/which": "~3.0.4"
}
}

View File

@ -0,0 +1,60 @@
import { info } from "ci-log"
import { appendFile, readFile } from "fs/promises"
import { pathExists } from "path-exists"
import { hasApk } from "./has-apk.js"
import { updateApkMemoized } from "./update.js"
/**
* Add an APK repository
* @param repoUrl The URL of the repository to add
* @returns Whether the repository was added successfully
*/
export async function addApkRepository(repoUrl: string): Promise<boolean> {
if (!(await hasApk())) {
throw new Error("apk is not available on this system")
}
try {
// Check if repositories file exists
const reposFile = "/etc/apk/repositories"
if (!(await pathExists(reposFile))) {
throw new Error(`APK repositories file not found at ${reposFile}`)
}
// Add repository to the file
info(`Adding repository: ${repoUrl}`)
await appendFile(reposFile, `${repoUrl}\n`)
// Update package index after adding repository
await updateApkMemoized.clear()
await updateApkMemoized()
info(`Successfully added repository: ${repoUrl}`)
return true
} catch (error) {
throw new Error(`Failed to add repository ${repoUrl}: ${error}`)
}
}
/**
* Enable the community repository
* @returns Whether the repository was added successfully
*/
export async function enableCommunityRepository() {
const alpineVersion = (await getAlpineVersion()).split(".").slice(0, 2).join(".")
return addApkRepository(`https://dl-cdn.alpinelinux.org/alpine/v${alpineVersion}/community/`)
}
/**
* Get the Alpine version
* @returns The Alpine version
*/
export async function getAlpineVersion() {
const releaseFile = "/etc/alpine-release"
if (!(await pathExists(releaseFile))) {
throw new Error(`Alpine release file not found at ${releaseFile}`)
}
return readFile(releaseFile, "utf8")
}

View File

@ -0,0 +1,15 @@
import memoizee from "memoizee"
import which from "which"
async function hasApk_() {
try {
await which("apk")
return true
} catch (error) {
return false
}
}
/**
* Check if apk is available on the system
*/
export const hasApk = memoizee(hasApk_, { promise: true })

View File

@ -0,0 +1,6 @@
export * from "./apk-repository.js"
export * from "./has-apk.js"
export * from "./init-apt.js"
export * from "./install-package.js"
export * from "./qualify-install.js"
export * from "./update.js"

View File

@ -0,0 +1,19 @@
import { defaultExecOptions, execRootSync } from "admina"
import memoize from "memoizee"
import { filterAndQualifyApkPackages } from "./qualify-install.js"
/** Install bash (usually missing from docker containers) */
export async function initApk() {
const toInstall = await filterAndQualifyApkPackages([
{ name: "bash" },
])
if (toInstall.length !== 0) {
execRootSync("apk", ["add", ...toInstall], {
...defaultExecOptions,
})
}
}
/** Install bash (usually missing from docker containers) (memoized) */
export const initApkMemoized = memoize(initApk, { promise: true })

View File

@ -0,0 +1,62 @@
import { execRoot } from "admina"
import { info, warning } from "ci-log"
import { hasApk } from "./has-apk.js"
import { initApkMemoized } from "./init-apt.js"
import { type ApkPackage, filterAndQualifyApkPackages, formatPackageWithVersion } from "./qualify-install.js"
import { updateApkMemoized } from "./update.js"
/**
* The information about an installation result
*/
export type InstallationInfo = {
/** The install dir of the package (Defaults to `undefined`) */
installDir?: string
/** The bin dir of the package (Defaults to `/usr/bin`) */
binDir: string
/** The bin path of the package (Defaults to `undefined`) */
bin?: string
}
/**
* Install a package using Alpine's apk package manager
* @param packages The packages to install
* @param update Whether to update the package index before installing
* @returns The installation information
*/
export async function installApkPack(packages: ApkPackage[], update = false): Promise<InstallationInfo> {
// Check if apk is available
if (!(await hasApk())) {
throw new Error("apk is not available on this system")
}
try {
// Update package index if requested
// init the apk
await initApkMemoized()
if (update) {
// Force update the repos
await updateApkMemoized.clear()
}
// Update the repos if needed
await updateApkMemoized()
const packagesToInstall = await filterAndQualifyApkPackages(packages)
if (packagesToInstall.length === 0) {
info("All packages are already installed")
return { binDir: "/usr/bin" }
}
// Install the packages
info(`Installing ${packagesToInstall.join(" ")}`)
await execRoot("apk", ["add", ...packagesToInstall])
info(`Successfully installed ${packagesToInstall.join(" ")}`)
return { binDir: "/usr/bin" }
} catch (error) {
warning(`Failed to install ${packages.map((pkg) => formatPackageWithVersion(pkg)).join(" ")}: ${error}`)
throw error
}
}

View File

@ -0,0 +1,83 @@
import { execRoot } from "admina"
/**
* The information about an apt package
*/
export type ApkPackage = {
/** The name of the package */
name: string
/** The version of the package (optional) */
version?: string
/** The repository to add before installing the package (optional) */
repository?: string
/**
* If the given version is not available, fall back to the latest version
* @default false
*/
fallBackToLatest?: boolean
}
/**
* Check if a package is already installed
* @param pkg The package to check
* @returns Whether the package is installed
*/
export async function checkPackageInstalled(pkg: ApkPackage): Promise<boolean> {
try {
// First check if the package is installed at all
const { exitCode } = await execRoot("apk", ["info", "-e", pkg.name], {
reject: false,
stdio: ["ignore", "pipe", "ignore"],
})
if (exitCode !== 0) {
return false
}
// If no specific version is required, we're done
if (pkg.version === undefined || pkg.version === "") {
return true
}
// Check the installed version
const { stdout: versionOutput } = await execRoot("apk", ["info", "-v", pkg.name], {
stdio: ["ignore", "pipe", "ignore"],
})
// Parse the version from output (format is typically "package-name-version")
const installedVersion = versionOutput.trim().split("-").slice(-1)[0]
return installedVersion === pkg.version
} catch (error) {
return false
}
}
/**
* Filter out packages that are already installed and qualify those that need installation
* @param packages List of packages to check
* @returns List of packages that need to be installed
*/
export async function filterAndQualifyApkPackages(packages: ApkPackage[]): Promise<string[]> {
const results = await Promise.all(
packages.map(async (pack) => {
const isInstalled = await checkPackageInstalled(pack)
return isInstalled ? undefined : pack
}),
)
return results.filter((pack): pack is ApkPackage => pack !== undefined)
.map(formatPackageWithVersion)
}
/**
* Format a package with its version if specified
* @param pkg The package object
* @returns Formatted package string (name=version or just name)
*/
export function formatPackageWithVersion(pkg: ApkPackage): string {
if (pkg.version !== undefined && pkg.version !== "") {
return `${pkg.name}=${pkg.version}`
}
return pkg.name
}

View File

@ -0,0 +1,7 @@
import { execa } from "execa"
import memoizee from "memoizee"
async function updateApk() {
await execa("apk", ["update"], { stdio: "inherit" })
}
export const updateApkMemoized = memoizee(updateApk, { promise: true })

View File

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"noEmit": false,
"allowImportingTsExtensions": false
},
"include": ["./src"]
}

View File

@ -232,6 +232,9 @@ importers:
semver:
specifier: 7.7.1
version: 7.7.1
setup-alpine:
specifier: workspace:*
version: link:packages/setup-alpine
setup-apt:
specifier: workspace:*
version: link:packages/setup-apt
@ -364,6 +367,43 @@ importers:
specifier: ^3.0.0
version: 3.0.4
packages/setup-alpine:
dependencies:
'@types/node':
specifier: 22.13.11
version: 22.13.11
admina:
specifier: ^1.0.1
version: 1.0.1
ci-info:
specifier: ^4.0.0
version: 4.2.0
ci-log:
specifier: workspace:*
version: link:../ci-log
envosman:
specifier: workspace:*
version: link:../envosman
execa:
specifier: 7.2.0
version: 7.2.0
memoizee:
specifier: ^0.4.17
version: 0.4.17
path-exists:
specifier: ^5.0.0
version: 5.0.0
which:
specifier: 4.0.0
version: 4.0.0
devDependencies:
'@types/memoizee':
specifier: 0.4.11
version: 0.4.11
'@types/which':
specifier: ~3.0.4
version: 3.0.4
packages/setup-apt:
dependencies:
'@types/node':

View File

@ -1,4 +1,5 @@
import { execRoot } from "admina"
import { hasApk, installApkPack } from "setup-alpine"
import { addAptKeyViaURL, installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { getDebArch } from "../utils/env/arch.js"
@ -40,6 +41,8 @@ export async function setupBazel(version: string, _setupDir: string, _arch: stri
} signed-by=${keyFileName}] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list`,
])
return installAptPack([{ name: "bazel", version }], true)
} else if (await hasApk()) {
return installApkPack([{ name: "bazel", version }], true)
}
throw new Error("Unsupported linux distribution")
}

View File

@ -1,3 +1,4 @@
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { hasDnf } from "../utils/env/hasDnf.js"
@ -8,7 +9,7 @@ import { setupDnfPack } from "../utils/setup/setupDnfPack.js"
import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js"
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function setupCcache(version: string, _setupDir: string, _arch: string) {
export async function setupCcache(version: string, _setupDir: string, _arch: string) {
switch (process.platform) {
case "win32": {
return setupChocoPack("ccache", version)
@ -23,6 +24,8 @@ export function setupCcache(version: string, _setupDir: string, _arch: string) {
return setupDnfPack([{ name: "ccache", version }])
} else if (isUbuntu()) {
return installAptPack([{ name: "ccache", version }])
} else if (await hasApk()) {
return installApkPack([{ name: "ccache", version }])
}
throw new Error("Unsupported linux distribution")
}

View File

@ -2,6 +2,7 @@ import { info } from "ci-log"
import { addExeExt } from "patha"
import semverCoerce from "semver/functions/coerce"
import semverLte from "semver/functions/lte"
import { hasApk, installApkPack } from "setup-alpine"
import { arm64, x86, x86_64 } from "../utils/env/arch.js"
import { type InstallationInfo, type PackageInfo, setupBin } from "../utils/setup/setupBin.js"
@ -66,6 +67,15 @@ function getCmakePackageInfo(version: string, platform: NodeJS.Platform, arch: s
}
/** Setup cmake */
export function setupCmake(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
export async function setupCmake(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
if (await hasApk()) {
return installApkPack([
{
name: "cmake",
// version,
},
])
}
return setupBin("cmake", version, getCmakePackageInfo, setupDir, arch)
}

View File

@ -1,4 +1,5 @@
import { addPath } from "envosman"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { rcOptions } from "../cli-options.js"
@ -27,6 +28,8 @@ export async function setupCppcheck(version: string | undefined, _setupDir: stri
return setupDnfPack([{ name: "ccache", version }])
} else if (isUbuntu()) {
return installAptPack([{ name: "cppcheck", version }])
} else if (await hasApk()) {
return installApkPack([{ name: "cppcheck", version }])
}
throw new Error("Unsupported linux distribution")
}

View File

@ -1,26 +1,26 @@
import { join } from "path"
import { info, notice } from "ci-log"
import { addPath } from "envosman"
import { pathExists } from "path-exists"
import { addExeExt } from "patha"
import retry from "retry-as-promised"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { setupGraphviz } from "../graphviz/graphviz.js"
import { type PackageInfo, setupBin } from "../utils/setup/setupBin.js"
import { setupChocoPack } from "../utils/setup/setupChocoPack.js"
import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js"
import { getVersion } from "../versions/versions.js"
import { pathExists } from "path-exists"
import retry from "retry-as-promised"
import { rcOptions } from "../cli-options.js"
import { setupGraphviz } from "../graphviz/graphviz.js"
import { arm64 } from "../utils/env/arch.js"
import { hasDnf } from "../utils/env/hasDnf.js"
import { isArch } from "../utils/env/isArch.js"
import { isUbuntu } from "../utils/env/isUbuntu.js"
import { macosVersion } from "../utils/env/macos_version.js"
import { ubuntuVersion } from "../utils/env/ubuntu_version.js"
import { type PackageInfo, setupBin } from "../utils/setup/setupBin.js"
import { setupChocoPack } from "../utils/setup/setupChocoPack.js"
import { setupDmg } from "../utils/setup/setupDmg.js"
import { setupDnfPack } from "../utils/setup/setupDnfPack.js"
import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js"
import { getVersion } from "../versions/versions.js"
/** Get the platform data for cmake */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -111,6 +111,13 @@ async function setupLinuxDoxygen(version: string, setupDir: string, arch: string
return setupDnfPack([{ name: "doxygen", version }])
} else if (isUbuntu()) {
return await installAptPack([{ name: "doxygen", version, fallBackToLatest: arm64.includes(arch) }])
} else if (await hasApk()) {
return installApkPack([
{
name: "doxygen",
// version,
},
])
} else {
throw new Error("Unsupported linux distributions")
}

View File

@ -10,6 +10,7 @@ import { readdir } from "fs/promises"
import { pathExists } from "path-exists"
import { addExeExt } from "patha"
import semverMajor from "semver/functions/major"
import { hasApk, installApkPack } from "setup-alpine"
import { addUpdateAlternativesToRc, installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { rcOptions } from "../cli-options.js"
@ -46,6 +47,8 @@ export async function setupGcc(version: string, setupDir: string, arch: string,
{ name: "gcc-c++", version },
{ name: "libstdc++-devel" },
])
} else if (await hasApk()) {
installationInfo = await installApkPack([{ name: "gcc", version }, { name: "g++", version }])
} else if (isUbuntu()) {
if (version === "") {
// the default version

View File

@ -8,6 +8,7 @@ import { pathExists } from "path-exists"
import { addExeExt } from "patha"
import semverCoerce from "semver/functions/coerce.js"
import semverSatisfies from "semver/functions/satisfies.js"
import { enableCommunityRepository, hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { rcOptions } from "../cli-options.js"
import { loadAssetList, matchAsset } from "../utils/asset/load-assets.js"
@ -45,6 +46,12 @@ export async function setupMingw(version: string, setupDir: string, arch: string
installationInfo = await setupDnfPack([{ name: "mingw64-gcc", version }])
} else if (isUbuntu()) {
installationInfo = await installAptPack([{ name: "mingw-w64", version }])
} else if (await hasApk()) {
await enableCommunityRepository()
installationInfo = await installApkPack([
{ name: "mingw-w64-gcc", version },
{ name: "mingw-w64-crt", version },
])
} else {
throw new Error(`Unsupported Linux distro for ${arch}`)
}

View File

@ -1,6 +1,7 @@
import { existsSync } from "fs"
import { info, warning } from "ci-log"
import { addPath } from "envosman"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import which from "which"
@ -39,6 +40,8 @@ export async function setupGit(version: string, _setupDir: string, _arch: string
return setupDnfPack([{ name: "git", version }])
} else if (isUbuntu()) {
return installAptPack([{ name: "git", version }])
} else if (await hasApk()) {
return installApkPack([{ name: "git", version }])
}
throw new Error("Unsupported linux distribution")
}

View File

@ -1,4 +1,5 @@
import { addPath } from "envosman"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { rcOptions } from "../cli-options.js"
@ -27,6 +28,8 @@ export async function setupGraphviz(version: string, _setupDir: string, _arch: s
return setupDnfPack([{ name: "graphviz", version }])
} else if (isUbuntu()) {
return installAptPack([{ name: "graphviz", version }])
} else if (await hasApk()) {
return installApkPack([{ name: "graphviz", version }])
}
throw new Error("Unsupported linux distribution")
}

View File

@ -16,6 +16,7 @@ import { ubuntuVersion } from "../utils/env/ubuntu_version.js"
import type { InstallationInfo } from "../utils/setup/setupBin.js"
import { quoteIfHasSpace } from "../utils/std/index.js"
import { getVersion } from "../versions/versions.js"
import { trySetupLLVMApk } from "./llvm_apk_installer.js"
import { LLVMPackages, trySetupLLVMApt } from "./llvm_apt_installer.js"
import { setupLLVMBin } from "./llvm_bin.js"
import { trySetupLLVMBrew } from "./llvm_brew_installer.js"
@ -50,6 +51,11 @@ async function setupLLVMOnly(
return aptInstallInfo
}
const apkInstallInfo = await trySetupLLVMApk(version)
if (apkInstallInfo !== undefined) {
return apkInstallInfo
}
const brewInstallInfo = await trySetupLLVMBrew(version, setupDir, arch)
if (brewInstallInfo !== undefined) {
return brewInstallInfo

View File

@ -0,0 +1,31 @@
import { info } from "ci-log"
import { type InstallationInfo, hasApk, installApkPack } from "setup-alpine"
import { majorLLVMVersion } from "./utils.ts"
/**
* Try to setup LLVM via the apk package manager
*
* @param {string} version - The version of LLVM to install
*
* @returns {InstallationInfo} The installation info if the installation was successful
* @returns {undefined} If the installation fails, it will try to remove the repository and will return undefined
*/
export async function trySetupLLVMApk(
version: string,
): Promise<InstallationInfo | undefined> {
if (!await hasApk()) {
return undefined
}
try {
return await setupLLVMApk(version)
} catch (err) {
info(`Failed to install llvm via system package manager ${err}.`)
}
return undefined
}
export function setupLLVMApk(version: string): Promise<InstallationInfo> {
const majorVersion = majorLLVMVersion(version)
return installApkPack([{ name: `llvm${majorVersion}` }])
}

View File

@ -1,5 +1,6 @@
import { join } from "path"
import { addPath } from "envosman"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { getBrewDir, installBrewPack } from "setup-brew"
import { rcOptions } from "../cli-options.js"
@ -30,6 +31,8 @@ export async function setupMake(version: string, _setupDir: string, _arch: strin
return setupDnfPack([{ name: "make", version }])
} else if (isUbuntu()) {
return installAptPack([{ name: "make", version }])
} else if (await hasApk()) {
return installApkPack([{ name: "make", version }])
}
throw new Error("Unsupported linux distribution")
}

View File

@ -1,4 +1,7 @@
import { addPath } from "envosman"
import { addExeExt } from "patha"
import { enableCommunityRepository, hasApk, installApkPack } from "setup-alpine"
import { rcOptions } from "../cli-options.js"
import { arm64, x86, x86_64 } from "../utils/env/arch.js"
import { type InstallationInfo, type PackageInfo, setupBin } from "../utils/setup/setupBin.js"
@ -37,6 +40,25 @@ function getNinjaPackageInfo(version: string, platform: NodeJS.Platform, arch: s
}
}
export function setupNinja(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
export async function setupNinja(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
if (await hasApk()) {
await enableCommunityRepository()
await installApkPack([
{
name: "ninja-build",
// version,
},
{
name: "ninja-is-really-ninja",
},
])
await addPath("/usr/lib/ninja-build/bin", rcOptions)
return {
binDir: "/usr/lib/ninja-build/bin",
installDir: "/usr/lib/ninja-build/",
bin: "/usr/lib/ninja-build/bin/ninja",
}
}
return setupBin("ninja", version, getNinjaPackageInfo, setupDir, arch)
}

View File

@ -2,6 +2,7 @@ import { execRootSync } from "admina"
import { error } from "ci-log"
import { addPath } from "envosman"
import { addExeExt } from "patha"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { rcOptions } from "../cli-options.js"
@ -62,6 +63,10 @@ function getPowershellUrl(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export async function setupPowershell(version: string, setupDir: string, arch: string) {
try {
if (await hasApk()) {
return setupPowershellSystem(version, setupDir, arch)
}
return await setupBin("pwsh", version, getPowerShellPackageInfo, setupDir, arch)
} catch (err) {
error(`Failed to setup pwsh via download: ${err}. Trying package managers...`)
@ -113,6 +118,8 @@ export async function setupPowershellSystem(version: string | undefined, _setupD
// ])
return installAptPack([{ name: "powershell", version }], true)
} else if (await hasApk()) {
return installApkPack([{ name: "powershell", version: undefined }])
}
throw new Error("Unsupported linux distribution")
}

View File

@ -1,7 +1,6 @@
import assert from "assert"
import { homedir } from "os"
import { dirname, join, parse as pathParse } from "path"
import { getExecOutput } from "@actions/exec"
import ciInfo from "ci-info"
const { GITHUB_ACTIONS } = ciInfo
import { endGroup, startGroup } from "@actions/core"
@ -9,9 +8,9 @@ import { info, notice, warning } from "ci-log"
import { addPath } from "envosman"
import { execa } from "execa"
import { readdir } from "fs/promises"
import memoize from "memoizee"
import { pathExists } from "path-exists"
import { addExeExt } from "patha"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack, isAptPackInstalled } from "setup-apt"
import { installBrewPack } from "setup-brew"
import which from "which"
@ -26,11 +25,11 @@ import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js"
import {
hasPipxBinary,
hasPipxModule,
isExternallyManaged,
setupPipPackSystem,
setupPipPackWithPython,
} from "../utils/setup/setupPipPack.js"
import { isBinUptoDate } from "../utils/setup/version.js"
import { unique } from "../utils/std/index.js"
import { getVersionDefault, isMinVersion } from "../versions/versions.js"
export async function setupPython(
@ -75,7 +74,7 @@ async function setupPipx(foundPython: string) {
if (!(await hasPipxModule(foundPython))) {
// install pipx for the system-wide python
try {
await setupPipPackSystem("pipx", isArch())
await setupPipPackSystem("pipx")
} catch (err) {
notice(`pipx was not installed completely for the system-wide python: ${err}`)
}
@ -234,6 +233,8 @@ async function setupPythonSystem(setupDir: string, version: string) {
installInfo = await setupDnfPack([{ name: "python3", version }])
} else if (isUbuntu()) {
installInfo = await installAptPack([{ name: "python3", version }, { name: "python-is-python3" }])
} else if (await hasApk()) {
installInfo = await installApkPack([{ name: "python3", version }])
} else {
throw new Error("Unsupported linux distributions")
}
@ -353,6 +354,11 @@ async function setupPip(foundPython: string) {
}
async function ensurePipUpgrade(foundPython: string) {
if (await isExternallyManaged(foundPython)) {
// let system tools handle pip
return false
}
try {
await execa(foundPython, ["-m", "ensurepip", "-U", "--upgrade"], { stdio: "inherit" })
return true
@ -370,29 +376,3 @@ async function ensurePipUpgrade(foundPython: string) {
// all methods failed
return false
}
async function addPythonBaseExecPrefix_(python: string) {
const dirs: string[] = []
// detection based on the platform
if (process.platform === "linux") {
dirs.push("/home/runner/.local/bin/")
} else if (process.platform === "darwin") {
dirs.push("/usr/local/bin/")
}
// detection using python.sys
const base_exec_prefix = (await getExecOutput(`${python} -c "import sys;print(sys.base_exec_prefix);"`)).stdout.trim()
// any of these are possible depending on the operating system!
dirs.push(join(base_exec_prefix, "Scripts"), join(base_exec_prefix, "Scripts", "bin"), join(base_exec_prefix, "bin"))
// remove duplicates
return unique(dirs)
}
/**
* Add the base exec prefix to the PATH. This is required for Conan, Meson, etc. to work properly.
*
* The answer is cached for subsequent calls
*/
export const addPythonBaseExecPrefix = memoize(addPythonBaseExecPrefix_, { promise: true })

View File

@ -1,3 +1,4 @@
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { getUbuntuVersion } from "ubuntu-version"
@ -16,6 +17,8 @@ export async function setupSccache(version: string, _setupDir: string, _arch: st
if (ubuntuVersion[0] >= 24) {
return installAptPack([{ name: "sccache", version }])
}
} else if (await hasApk()) {
return installApkPack([{ name: "sccache", version }])
}
return installBrewPack("sccache", version)

View File

@ -1,3 +1,4 @@
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { hasDnf } from "../utils/env/hasDnf.js"
@ -8,7 +9,7 @@ import { setupDnfPack } from "../utils/setup/setupDnfPack.js"
import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js"
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function setupSevenZip(version: string, _setupDir: string, _arch: string) {
export async function setupSevenZip(version: string, _setupDir: string, _arch: string) {
switch (process.platform) {
case "win32": {
return setupChocoPack("7zip", version)
@ -26,6 +27,8 @@ export function setupSevenZip(version: string, _setupDir: string, _arch: string)
])
} else if (isUbuntu()) {
return installAptPack([{ name: "p7zip-full", version }])
} else if (await hasApk()) {
return installApkPack([{ name: "p7zip", version }])
}
throw new Error("Unsupported linux distribution")
}

View File

@ -4,6 +4,7 @@ import { info, warning } from "ci-log"
import { execa } from "execa"
import { mkdirp, move } from "fs-extra"
import { rm } from "fs/promises"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import which from "which"
import { setupSevenZip } from "../../sevenzip/sevenzip.js"
@ -172,6 +173,8 @@ async function installTarDependencies(archiveType: ArchiveType) {
await setupDnfPack([{ name: "gzip" }, { name: "tar" }])
} else if (isUbuntu()) {
await installAptPack([{ name: "gzip" }, { name: "tar" }])
} else if (await hasApk()) {
await installApkPack([{ name: "gzip" }, { name: "tar" }])
}
}
break
@ -185,6 +188,8 @@ async function installTarDependencies(archiveType: ArchiveType) {
await setupDnfPack([{ name: "xz" }, { name: "tar" }])
} else if (isUbuntu()) {
await installAptPack([{ name: "xz-utils" }, { name: "tar" }])
} else if (await hasApk()) {
await installApkPack([{ name: "xz" }, { name: "tar" }])
}
}
break

View File

@ -1,25 +1,30 @@
import { dirname, join } from "path"
import { info } from "@actions/core"
import { getExecOutput } from "@actions/exec"
import { warning } from "ci-log"
import { addPath } from "envosman"
import { execa, execaSync } from "execa"
import memoize from "memoizee"
import { mkdirp } from "mkdirp"
import { pathExists } from "path-exists"
import { addExeExt } from "patha"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { untildifyUser } from "untildify-user"
import which from "which"
import { rcOptions } from "../../cli-options.js"
import { addPythonBaseExecPrefix, setupPython } from "../../python/python.js"
import { setupPython } from "../../python/python.js"
import { getVersion } from "../../versions/versions.js"
import { hasDnf } from "../env/hasDnf.js"
import { isArch } from "../env/isArch.js"
import { isUbuntu } from "../env/isUbuntu.js"
import { ubuntuVersion } from "../env/ubuntu_version.js"
import { unique } from "../std/index.js"
import type { InstallationInfo } from "./setupBin.js"
import { setupDnfPack } from "./setupDnfPack.js"
import { setupPacmanPack } from "./setupPacmanPack.js"
import { getBinVersion } from "./version.js"
export type SetupPipPackOptions = {
/** Whether to use pipx instead of pip */
@ -49,40 +54,42 @@ export async function setupPipPackWithPython(
version?: string,
options: SetupPipPackOptions = {},
): Promise<InstallationInfo> {
const { usePipx = true, user = true, upgrade = false, isLibrary = false } = options
const { usePipx: givenUsePipx = true, user = true, upgrade = false, isLibrary = false } = options
const isPipx = usePipx && !isLibrary && (await hasPipxModule(givenPython))
const usePipx = givenUsePipx && !isLibrary && (await hasPipxModule(givenPython))
const pip = isPipx ? "pipx" : "pip"
// if the package is externally managed, let the system tools handle it
const externallyManaged = !usePipx && (await isExternallyManaged(givenPython))
const pip = usePipx ? "pipx" : "pip"
// remove `[]` extensions
const nameOnly = getPackageName(name)
// if upgrade is not requested, check if the package is already installed, and return if it is
if (!upgrade) {
const installed = isPipx
const installed = usePipx
? await pipxPackageInstalled(givenPython, nameOnly)
: await pipPackageIsInstalled(givenPython, nameOnly)
if (installed) {
const binDir = isPipx
const binDir = usePipx
? await finishPipxPackageInstall()
: await finishPipPackageInstall(givenPython, nameOnly)
return { binDir }
}
}
const hasPackage = await pipHasPackage(givenPython, nameOnly)
if (hasPackage) {
if (!externallyManaged && await pipHasPackage(givenPython, nameOnly)) {
try {
info(`Installing ${name} ${version ?? ""} via ${pip}`)
const nameAndVersion = version !== undefined && version !== "" ? `${name}==${version}` : name
const upgradeFlag = upgrade ? (isPipx ? ["upgrade"] : ["install", "--upgrade"]) : ["install"]
const userFlag = !isPipx && user ? ["--user"] : []
const upgradeFlag = upgrade ? (usePipx ? ["upgrade"] : ["install", "--upgrade"]) : ["install"]
const userFlag = !usePipx && user ? ["--user"] : []
const env = process.env
if (isPipx && user) {
if (usePipx && user) {
// install to user home
env.PIPX_HOME = await getPipxHome()
env.PIPX_BIN_DIR = await getPipxBinDir()
@ -103,7 +110,7 @@ export async function setupPipPackWithPython(
throw new Error(`Failed to install ${name} as it was not found via ${pip} or the system package manager`)
}
const binDir = isPipx
const binDir = usePipx
? await finishPipxPackageInstall()
: await finishPipPackageInstall(givenPython, nameOnly)
return { binDir }
@ -275,15 +282,20 @@ async function findBinDir(dirs: string[], name: string) {
return dirs[dirs.length - 1]
}
export function setupPipPackSystem(name: string, addPythonPrefix = true) {
export async function setupPipPackSystem(name: string, givenAddPythonPrefix?: boolean) {
if (process.platform === "linux") {
info(`Installing ${name} via the system package manager`)
const addPythonPrefix = name === "pipx" ? isArch() : (givenAddPythonPrefix ?? true)
if (isArch()) {
return setupPacmanPack(addPythonPrefix ? `python-${name}` : name)
} else if (hasDnf()) {
return setupDnfPack([{ name: addPythonPrefix ? `python3-${name}` : name }])
} else if (isUbuntu()) {
return installAptPack([{ name: addPythonPrefix ? `python3-${name}` : name }])
} else if (await hasApk()) {
return installApkPack([{ name: addPythonPrefix ? `py3-${name}` : name }])
}
} else if (process.platform === "darwin") {
// brew doesn't have venv
@ -295,3 +307,70 @@ export function setupPipPackSystem(name: string, addPythonPrefix = true) {
}
return null
}
async function addPythonBaseExecPrefix_(python: string) {
const dirs: string[] = []
// detection based on the platform
if (process.platform === "linux") {
dirs.push("/home/runner/.local/bin/")
} else if (process.platform === "darwin") {
dirs.push("/usr/local/bin/")
}
// detection using python.sys
const base_exec_prefix = await getPythonBaseExecPrefix(python)
// any of these are possible depending on the operating system!
dirs.push(join(base_exec_prefix, "Scripts"), join(base_exec_prefix, "Scripts", "bin"), join(base_exec_prefix, "bin"))
// remove duplicates
return unique(dirs)
}
/**
* Add the base exec prefix to the PATH. This is required for Conan, Meson, etc. to work properly.
*
* The answer is cached for subsequent calls
*/
export const addPythonBaseExecPrefix = memoize(addPythonBaseExecPrefix_, { promise: true })
async function getPythonBaseExecPrefix_(python: string) {
return (await getExecOutput(`${python} -c "import sys;print(sys.base_exec_prefix);"`)).stdout.trim()
}
/**
* Get the base exec prefix of a Python installation
* This is the directory where the Python interpreter is installed
* and where the standard library is located
*/
export const getPythonBaseExecPrefix = memoize(getPythonBaseExecPrefix_, { promise: true })
async function isExternallyManaged_(python: string) {
try {
const base_exec_prefix = await getPythonBaseExecPrefix(python)
const pythonVersion = await getBinVersion(python)
if (pythonVersion === undefined) {
warning(`Failed to get the version of ${python}`)
return false
}
const externallyManagedPath = join(
base_exec_prefix,
"lib",
`python${pythonVersion.major}.${pythonVersion.minor}`,
"EXTERNALLY-MANAGED",
)
return pathExists(externallyManagedPath)
} catch (err) {
warning(`Failed to check if ${python} is externally managed: ${err}`)
return false
}
}
/**
* Check if the given Python installation is externally managed
* This is required for Conan, Meson, etc. to work properly
*
* The answer is cached for subsequent calls
*/
export const isExternallyManaged = memoize(isExternallyManaged_, { promise: true })

View File

@ -5,6 +5,7 @@ import { addEnv, addPath } from "envosman"
import { execa } from "execa"
import { pathExists } from "path-exists"
import { addShExt, addShRelativePrefix } from "patha"
import { hasApk, installApkPack } from "setup-alpine"
import { installAptPack } from "setup-apt"
import which from "which"
import { rcOptions } from "../cli-options.js"
@ -54,6 +55,18 @@ export async function setupVcpkg(version: string, setupDir: string, arch: string
{ name: "tar" },
{ name: "pkg-config" },
])
} else if (await hasApk()) {
const deps = [
{ name: "curl" },
{ name: "zip" },
{ name: "unzip" },
{ name: "tar" },
{ name: "pkgconf" },
]
if (arm64.includes(arch)) {
deps.push({ name: "build-base" })
}
await installApkPack(deps)
}
}
@ -74,7 +87,7 @@ export async function setupVcpkg(version: string, setupDir: string, arch: string
}
// Add VCPKG_FORCE_SYSTEM_BINARIES=1 for Linux arm64
if (process.platform === "linux" && arch in arm64) {
if (process.platform === "linux" && arm64.includes(arch)) {
await addEnv("VCPKG_FORCE_SYSTEM_BINARIES", "1")
}