├── .dockerignore ├── .eslintrc.json ├── .gitattributes ├── .github ├── renovate.json └── workflows │ └── CI.yml ├── .gitignore ├── .gitmodules ├── .npmignore ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .terserrc.mjs ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE.dependencies.txt ├── LICENSE.txt ├── README.md ├── README_DOCKER.md ├── action.yml ├── babel.config.mts ├── biome.jsonc ├── cspell.config.yaml ├── dev ├── docker │ ├── ci │ │ ├── alpine-gcc.dockerfile │ │ ├── alpine-llvm.dockerfile │ │ ├── alpine-mingw.dockerfile │ │ ├── alpine.dockerfile │ │ ├── arch-gcc.dockerfile │ │ ├── arch-llvm.dockerfile │ │ ├── arch-mingw.dockerfile │ │ ├── arch.dockerfile │ │ ├── docker-ci.mjs │ │ ├── fedora-gcc.dockerfile │ │ ├── fedora-llvm.dockerfile │ │ ├── fedora-mingw.dockerfile │ │ ├── fedora.dockerfile │ │ ├── tests │ │ │ ├── base.yml │ │ │ ├── gcc.yml │ │ │ ├── index.mts │ │ │ ├── llvm.yml │ │ │ └── mingw.yml │ │ ├── ubuntu-gcc.dockerfile │ │ ├── ubuntu-llvm.dockerfile │ │ ├── ubuntu-mingw.dockerfile │ │ └── ubuntu.dockerfile │ ├── examples │ │ ├── alpine-gcc.dockerfile │ │ ├── alpine-llvm.dockerfile │ │ ├── alpine-mingw.dockerfile │ │ ├── arch-gcc.dockerfile │ │ ├── arch-llvm.dockerfile │ │ ├── arch-mingw.dockerfile │ │ ├── fedora-gcc.dockerfile │ │ ├── fedora-llvm.dockerfile │ │ ├── fedora-mingw.dockerfile │ │ ├── ubuntu-gcc.dockerfile │ │ ├── ubuntu-llvm.dockerfile │ │ └── ubuntu-mingw.dockerfile │ └── setup-cpp │ │ ├── setup-cpp-arch-llvm.dockerfile │ │ ├── setup-cpp-arch-mingw.dockerfile │ │ ├── setup-cpp-fedora-llvm.dockerfile │ │ ├── setup-cpp-fedora-mingw.dockerfile │ │ ├── setup-cpp-ubuntu-20.0.4-llvm.dockerfile │ │ ├── setup-cpp-ubuntu-llvm.dockerfile │ │ └── setup-cpp-ubuntu-mingw.dockerfile ├── readme │ └── template.md └── scripts │ ├── pack-exe.mjs │ └── package.json ├── dist ├── legacy │ ├── assets │ │ ├── actions_python-CuNJhsEe.js │ │ ├── actions_python-CuNJhsEe.js.map │ │ ├── index-BLmG_vBb.js │ │ ├── index-BLmG_vBb.js.map │ │ ├── proxy-agent-8TMZ-BeU.js │ │ └── proxy-agent-8TMZ-BeU.js.map │ ├── gcc_matcher.json │ ├── github_brechtsanders_winlibs_mingw.json │ ├── github_facebook_infer.json │ ├── github_llvm_llvm-project.json │ ├── llvm_matcher.json │ ├── llvm_org_releases.json │ ├── llvm_repo_remove.bash │ ├── msvc_matcher.json │ ├── python_matcher.json │ ├── setup-cpp.js │ ├── setup-cpp.js.map │ └── versions.json └── modern │ ├── assets │ ├── actions_python-4hsZpmjR.mjs │ ├── actions_python-4hsZpmjR.mjs.map │ ├── index-DbYobIHH.mjs │ ├── index-DbYobIHH.mjs.map │ ├── proxy-agent-BoAwJO67.mjs │ └── proxy-agent-BoAwJO67.mjs.map │ ├── gcc_matcher.json │ ├── github_brechtsanders_winlibs_mingw.json │ ├── github_facebook_infer.json │ ├── github_llvm_llvm-project.json │ ├── llvm_matcher.json │ ├── llvm_org_releases.json │ ├── llvm_repo_remove.bash │ ├── msvc_matcher.json │ ├── python_matcher.json │ ├── setup-cpp.mjs │ ├── setup-cpp.mjs.map │ └── versions.json ├── dprint.json ├── jest.config.mjs ├── lefthook.yml ├── package-version.json ├── package.json ├── packages ├── ci-log │ ├── .eslintrc.json │ ├── README.md │ ├── __tests__ │ │ ├── index.test.ts │ │ └── tsconfig.json │ ├── jest.config.mjs │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── envosman │ ├── .eslintrc.json │ ├── README.md │ ├── __tests__ │ │ ├── index.test.ts │ │ └── tsconfig.json │ ├── jest.config.mjs │ ├── package.json │ ├── src │ │ ├── add-env.ts │ │ ├── add-path.ts │ │ ├── index.ts │ │ ├── rc-file.ts │ │ └── utils.ts │ └── tsconfig.json ├── exec-powershell │ ├── .eslintrc.json │ ├── README.md │ ├── __tests__ │ │ ├── index.test.ts │ │ └── tsconfig.json │ ├── jest.config.mjs │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── setup-alpine │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── index.test.ts │ │ └── tsconfig.json │ ├── jest.config.mjs │ ├── package.json │ ├── src │ │ ├── apk-repository.ts │ │ ├── has-apk.ts │ │ ├── index.ts │ │ ├── init-apt.ts │ │ ├── install-package.ts │ │ ├── is-alpine.ts │ │ ├── qualify-install.ts │ │ └── update.ts │ └── tsconfig.json ├── setup-apt │ ├── .eslintrc.json │ ├── README.md │ ├── __tests__ │ │ ├── apt-fast.test.ts │ │ ├── nala.test.ts │ │ ├── testBin.ts │ │ └── tsconfig.json │ ├── jest.config.mjs │ ├── package.json │ ├── src │ │ ├── alternatives.ts │ │ ├── apt-env.ts │ │ ├── apt-fast.ts │ │ ├── apt-key.ts │ │ ├── apt-repository.ts │ │ ├── apt-timeout.ts │ │ ├── get-apt.ts │ │ ├── index.ts │ │ ├── init-apt.ts │ │ ├── install.ts │ │ ├── is-installed.ts │ │ ├── nala.ts │ │ ├── qualify-install.ts │ │ └── update.ts │ └── tsconfig.json ├── setup-brew │ ├── .eslintrc.json │ ├── README.md │ ├── __tests__ │ │ ├── brew.test.ts │ │ └── tsconfig.json │ ├── jest.config.mjs │ ├── package.json │ ├── src │ │ ├── InstallationInfo.ts │ │ ├── index.ts │ │ ├── install-pack-options.ts │ │ ├── install-pack.ts │ │ ├── install.ts │ │ └── pack-install-dir.ts │ └── tsconfig.json └── untildify-user │ ├── .eslintrc.json │ ├── README.md │ ├── __tests__ │ ├── index.test.ts │ └── tsconfig.json │ ├── jest.config.mjs │ ├── package.json │ ├── src │ └── index.ts │ └── tsconfig.json ├── patches └── @actions__http-client@2.2.3.patch ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── prettier.config.mjs ├── src ├── __tests__ │ ├── lib.test.ts │ └── main.test.ts ├── asset-list.ts ├── bazel │ ├── __tests__ │ │ └── bazel.test.ts │ └── bazel.ts ├── ccache │ ├── __tests__ │ │ └── ccache.test.ts │ └── ccache.ts ├── chocolatey │ ├── __tests__ │ │ └── chocolatey.test.ts │ └── chocolatey.ts ├── cmake │ ├── __tests__ │ │ └── cmake.test.ts │ └── cmake.ts ├── cmakelang │ ├── __tests__ │ │ └── cmakelang.test.ts │ └── cmakelang.ts ├── compilers.ts ├── conan │ ├── __tests__ │ │ └── conan.test.ts │ └── conan.ts ├── cppcheck │ ├── __tests__ │ │ └── cppcheck.test.ts │ └── cppcheck.ts ├── cpplint │ ├── __tests__ │ │ └── cpplint.test.ts │ └── cpplint.ts ├── doxygen │ ├── __tests__ │ │ └── doxygen.test.ts │ └── doxygen.ts ├── flawfinder │ ├── __tests__ │ │ └── flawfinder.test.ts │ └── flawfinder.ts ├── gcc │ ├── __tests__ │ │ ├── gcc.test.ts │ │ └── main.cpp │ ├── assets-list.ts │ ├── gcc.ts │ ├── gccMatcher.ts │ ├── gcc_matcher.json │ ├── github_brechtsanders_winlibs_mingw.json │ └── mingw.ts ├── gcovr │ ├── __tests__ │ │ └── gcovr.test.ts │ └── gcovr.ts ├── git │ ├── __tests__ │ │ └── git.test.ts │ └── git.ts ├── graphviz │ ├── __tests__ │ │ └── graphviz.test.ts │ └── graphviz.ts ├── infer │ ├── __tests__ │ │ └── infer.test.ts │ ├── assets-list.ts │ ├── github_facebook_infer.json │ └── infer.ts ├── installTool.ts ├── kcov │ ├── __tests__ │ │ └── kcov.test.ts │ ├── gcc13.patch │ └── kcov.ts ├── lib.ts ├── lizard │ ├── __tests__ │ │ └── lizard.test.ts │ └── lizard.ts ├── llvm │ ├── __tests__ │ │ ├── llvm.test.ts │ │ └── main.cpp │ ├── apple-clang.ts │ ├── assets-list.ts │ ├── github_llvm_llvm-project.json │ ├── llvm.ts │ ├── llvm_apk_installer.ts │ ├── llvm_apt_installer.ts │ ├── llvm_bin.ts │ ├── llvm_brew_installer.ts │ ├── llvm_matcher.json │ ├── llvm_org_releases.json │ ├── llvm_repo_remove.bash │ ├── llvm_url.ts │ └── utils.ts ├── macos-sdk │ ├── __tests__ │ │ └── macos-sdk.test.ts │ └── macos-sdk.ts ├── make │ ├── __tests__ │ │ └── make.test.ts │ └── make.ts ├── meson │ ├── __tests__ │ │ └── meson.test.ts │ └── meson.ts ├── msvc │ ├── __tests__ │ │ └── msvc.test.ts │ ├── msvc.ts │ └── msvc_matcher.json ├── ninja │ ├── __tests__ │ │ └── ninja.test.ts │ └── ninja.ts ├── opencppcoverage │ ├── __tests__ │ │ └── opencppcoverage.test.ts │ └── opencppcoverage.ts ├── options.ts ├── powershell │ ├── __tests__ │ │ └── powershell.test.ts │ └── powershell.ts ├── python │ ├── __tests__ │ │ └── python.test.ts │ ├── actions_python.ts │ ├── python.ts │ └── python_matcher.json ├── sccache │ ├── __tests__ │ │ └── sccache.test.ts │ └── sccache.ts ├── setup-cpp-installer.ts ├── setup-cpp.ts ├── sevenzip │ ├── __tests__ │ │ └── sevenzip.test.ts │ └── sevenzip.ts ├── task │ ├── __tests__ │ │ └── task.test.ts │ └── task.ts ├── tool.ts ├── types │ ├── numerous.d.ts │ └── time-delta.d.ts ├── utils │ ├── asset │ │ ├── fetch-github-assets.ts │ │ ├── fetch-html-assets.ts │ │ └── load-assets.ts │ ├── compat │ │ ├── crypto │ │ │ ├── index.ts │ │ │ ├── randomuuid.mjs │ │ │ └── type.d.ts │ │ ├── fs │ │ │ ├── promises.ts │ │ │ └── types.d.ts │ │ ├── node.d.ts │ │ └── stream │ │ │ └── promises.ts │ ├── env │ │ ├── arch.ts │ │ ├── hasDnf.ts │ │ ├── isArch.ts │ │ ├── macos_version.ts │ │ └── ubuntu_version.ts │ ├── setup │ │ ├── extract.ts │ │ ├── setupBin.ts │ │ ├── setupChocoPack.ts │ │ ├── setupDmg.ts │ │ ├── setupDnfPack.ts │ │ ├── setupPacmanPack.ts │ │ ├── setupPipPack.ts │ │ └── version.ts │ ├── std │ │ └── index.ts │ └── tests │ │ └── test-helpers.ts ├── vcpkg │ ├── __tests__ │ │ └── vcpkg.test.ts │ └── vcpkg.ts ├── vcvarsall │ └── vcvarsall.ts └── versions │ ├── versions.json │ └── versions.ts ├── swc.legacy.json ├── swc.modern.json ├── tsconfig.json ├── tsconfig.types.json ├── turbo.json └── vite.config.mts /.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | # OS metadata 3 | **/.DS_Store 4 | **/Thumbs.db 5 | 6 | # Node 7 | **/node_modules 8 | **/package-lock.json 9 | **/temp-* 10 | 11 | # TypeScript 12 | **/*.tsbuildinfo 13 | 14 | # Build directories 15 | **/packages/*/dist/ 16 | **/.parcel-cache 17 | **/exe/ 18 | **/*.log 19 | **/*.exe 20 | **/.cache/ 21 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-atomic", 3 | "ignorePatterns": ["dist/", "node_modules/", "dev/cpp_vcpkg_project"], 4 | "rules": { 5 | "no-unused-vars": "off", 6 | "no-empty-function": "off", 7 | "@typescript-eslint/no-unused-vars": [ 8 | "warn", 9 | { 10 | "argsIgnorePattern": "^_", 11 | "varsIgnorePattern": "^_", 12 | "caughtErrorsIgnorePattern": "^_", 13 | "destructuredArrayIgnorePattern": "^_" 14 | } 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # line endings 2 | * text=auto eol=lf 3 | *.{cmd,[cC][mM][dD]} text eol=crlf 4 | *.{bat,[bB][aA][tT]} text eol=crlf 5 | *.{vcxproj,vcxproj.filters} text eol=crlf 6 | 7 | # autogenerated files 8 | dist/ -diff 9 | package-lock.json -diff 10 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base"], 4 | "schedule": ["every weekend"], 5 | "labels": ["dependencies"], 6 | "separateMajorMinor": true, 7 | "packageRules": [ 8 | { 9 | "matchDepTypes": ["devDependencies"], 10 | "matchUpdateTypes": ["major", "minor", "patch", "pin", "digest", "lockFileMaintenance", "rollback", "bump"], 11 | "groupName": "devDependencies", 12 | "semanticCommitType": "chore", 13 | "automerge": true 14 | }, 15 | { 16 | "matchDepTypes": ["dependencies"], 17 | "matchUpdateTypes": ["major", "minor", "patch", "pin", "digest", "lockFileMaintenance", "rollback", "bump"], 18 | "groupName": "dependencies", 19 | "semanticCommitType": "fix" 20 | }, 21 | { 22 | "matchDatasources": ["npm"], 23 | "matchPackageNames": ["eslint", "numerous", "execa", "which", ",@types/eslint"], 24 | "enabled": false 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS metadata 2 | .DS_Store 3 | Thumbs.db 4 | 5 | # Node 6 | node_modules 7 | package-lock.json 8 | temp-* 9 | 10 | # TypeScript 11 | *.tsbuildinfo 12 | 13 | # Build directories 14 | packages/*/dist/ 15 | .parcel-cache 16 | exe/ 17 | *.log 18 | *.exe 19 | .cache/ 20 | 21 | coverage 22 | .turbo 23 | 24 | /src/llvm/assets/ 25 | 26 | /build 27 | dist/library 28 | *.tgz 29 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dev/cpp_vcpkg_project"] 2 | path = dev/cpp_vcpkg_project 3 | url = https://github.com/aminya/cpp_vcpkg_project 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | **/node_modules 4 | **/temp-* 5 | **/tsconfig.tsbuildinfo 6 | .parcel-cache 7 | **/exe/ 8 | **/log 9 | **/exe 10 | **/cache 11 | **/.cache 12 | **/coverage 13 | **/.turbo 14 | src/llvm/assets/ 15 | **/build 16 | **/__tests__ 17 | **/dev/cpp_vcpkg_project 18 | **/packages/*/dist/ 19 | **/*.tgz 20 | **/.eslintrc.json 21 | **/jest.config.mjs 22 | .vscode/ 23 | .github/ 24 | biome.jsonc 25 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | lockfile=true 3 | shamefully-hoist=true 4 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.16.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package.json 3 | pnpm-lock.yaml 4 | package-lock.json 5 | CHANGELOG.md 6 | dist 7 | stats.html 8 | src/python/setup-python/ 9 | src/msvc/msvc-dev-cmd/ 10 | dev/cpp_vcpkg_project 11 | -------------------------------------------------------------------------------- /.terserrc.mjs: -------------------------------------------------------------------------------- 1 | import { buildTerserOptions } from "terser-config-atomic/dist/builder.js" 2 | const config = buildTerserOptions(process.env.NODE_ENV, undefined, true) 3 | 4 | if ( 5 | typeof config.compress === "object" 6 | && "unsafe_math" in config.compress 7 | ) { 8 | config.compress.unsafe_math = false 9 | } 10 | 11 | export default config 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "orta.vscode-jest", 4 | "esbenp.prettier-vscode", 5 | "dbaeumer.vscode-eslint", 6 | "biomejs.biome", 7 | "p42ai.refactor" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Jest Current File", 8 | "runtimeExecutable": "sh", 9 | "program": "node_modules/.bin/jest", 10 | "args": ["${relativeFile}"], 11 | "console": "integratedTerminal", 12 | "internalConsoleOptions": "openOnFirstSessionStart" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[dockerfile]": { 3 | "editor.defaultFormatter": "dprint.dprint" 4 | }, 5 | "[javascript]": { 6 | "editor.defaultFormatter": "dprint.dprint" 7 | }, 8 | "[javascriptreact]": { 9 | "editor.defaultFormatter": "dprint.dprint" 10 | }, 11 | "[json]": { 12 | "editor.defaultFormatter": "dprint.dprint" 13 | }, 14 | "[jsonc]": { 15 | "editor.defaultFormatter": "dprint.dprint" 16 | }, 17 | "[markdown]": { 18 | "editor.defaultFormatter": "dprint.dprint" 19 | }, 20 | "[typescript]": { 21 | "editor.defaultFormatter": "dprint.dprint" 22 | }, 23 | "[typescriptreact]": { 24 | "editor.defaultFormatter": "dprint.dprint" 25 | }, 26 | "[yaml]": { 27 | "editor.defaultFormatter": "dprint.dprint" 28 | }, 29 | "[github-actions-workflow]": { 30 | "editor.defaultFormatter": "dprint.dprint" 31 | }, 32 | "cSpell.advanced.feature.useReferenceProviderWithRename": true, 33 | "cSpell.checkOnlyEnabledFileTypes": false, 34 | "cSpell.numSuggestions": 3, 35 | "cSpell.showAutocompleteSuggestions": true, 36 | "eslint.enable": true, 37 | "eslint.options": { 38 | "cache": true, 39 | "cacheLocation": "./node_modules/.cache/eslint/", 40 | "errorOnUnmatchedPattern": false, 41 | "extensions": [ 42 | "ts", 43 | "tsx", 44 | "js", 45 | "jsx", 46 | "cjs", 47 | "mjs", 48 | "json", 49 | "yaml", 50 | "astro", 51 | "mdx", 52 | "html" 53 | ] 54 | }, 55 | "eslint.probe": [ 56 | "typescript", 57 | "typescriptreact", 58 | "javascript", 59 | "javascriptreact", 60 | "json", 61 | "jsonc", 62 | "yaml", 63 | "markdown", 64 | "html", 65 | "astro" 66 | ], 67 | "explorer.copyRelativePathSeparator": "/", 68 | "files.eol": "\n", 69 | "files.insertFinalNewline": true, 70 | "files.trimFinalNewlines": true, 71 | "hadolint.hadolintPath": "./target/bin/hadolint", 72 | "prettier.enable": false, 73 | "javascript.preferences.importModuleSpecifierEnding": "js", 74 | "typescript.preferences.importModuleSpecifierEnding": "js", 75 | "eslint.useESLintClass": true, 76 | "yaml.schemas": { 77 | "https://json.schemastore.org/container-structure-test.json": "/dev/docker/ci/tests/*.yml" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21...3.28) 2 | 3 | project( 4 | setup_cpp_tests 5 | VERSION 1.6.2 6 | DESCRIPTION "Tests for setup-cpp" 7 | HOMEPAGE_URL "https://github.com/aminya/setup-cpp" 8 | LANGUAGES CXX C) 9 | 10 | add_subdirectory("./dev/cpp_vcpkg_project") 11 | 12 | add_executable(test_gcc ./src/gcc/__tests__/main.cpp) 13 | target_compile_features(test_gcc PRIVATE cxx_std_17) 14 | 15 | add_executable(test_llvm ./src/llvm/__tests__/main.cpp) 16 | target_compile_features(test_llvm PRIVATE cxx_std_17) 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | You will need [`pnpm`](https://pnpm.io/installation) to build and test `setup-cpp`: 4 | 5 | ```shell 6 | pnpm install 7 | ``` 8 | 9 | To avoid permenant changes to your system, you can use the test docker images under `./dev/docker/__tests__`. 10 | 11 | Before running the tests locally, backup your environment variables because faulty code might corrupt the environment. 12 | 13 | 14 | 15 | Install [container-structure-test](https://github.com/GoogleContainerTools/container-structure-test) for docker testing. 16 | -------------------------------------------------------------------------------- /LICENSE.dependencies.txt: -------------------------------------------------------------------------------- 1 | setup-cpp reused some code from the following projects: 2 | 3 | - [install-llvm-action](https://github.com/KyleMayes/install-llvm-action/blob/master/LICENSE.txt): Apache-2.0 4 | - [install-cmake](https://github.com/Symbitic/install-cmake/blob/master/LICENSE.md): MIT 5 | - [get-cmake](https://github.com/lukka/get-cmake/blob/main/LICENSE.txt): MIT 6 | - [gha-setup-ninja](https://github.com/seanmiddleditch/gha-setup-ninja): MIT 7 | - [msvc-problem-matcher](https://github.com/ammaraskar/msvc-problem-matcher): Apache-2.0 8 | - [gcc-problem-matcher](https://github.com/ammaraskar/gcc-problem-matcher): Apache-2.0 9 | 10 | This package also uses the dependencies listed in package.json. You can get the list of their licenses using the following command: 11 | ``` 12 | npm install -g license-checker 13 | license-checker --summary --excludePackages "setup-python@v4.0.0" 14 | ``` 15 | 16 | ``` 17 | ├─ MIT: 21 18 | ├─ Apache-2.0: 8 19 | └─ ISC: 2 20 | ``` 21 | 22 | Note: setup-python is MIT licensed. 23 | -------------------------------------------------------------------------------- /babel.config.mts: -------------------------------------------------------------------------------- 1 | import type { TransformOptions } from "@babel/core" 2 | // @ts-ignore 3 | import RemoveNodePrefix from "@upleveled/babel-plugin-remove-node-prefix" 4 | 5 | const babelConfig: TransformOptions = { 6 | plugins: [ 7 | RemoveNodePrefix, 8 | ], 9 | sourceMaps: true, 10 | sourceType: "module", 11 | } 12 | export default babelConfig 13 | -------------------------------------------------------------------------------- /biome.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.1/schema.json", 3 | "files": { 4 | "ignore": [ 5 | "**/node_modules/**", 6 | "**/.pnpm-store/**", 7 | "**/dist/**", 8 | "dev/cpp_vcpkg_project/**/*", 9 | "**/.venv/", 10 | "**/.*cache/", 11 | "coverage/", 12 | "**/coverage/", 13 | "**/github_brechtsanders_winlibs_mingw.json" 14 | ], 15 | "ignoreUnknown": true 16 | }, 17 | "organizeImports": { 18 | "enabled": true 19 | }, 20 | "linter": { 21 | "enabled": true, 22 | "rules": { 23 | "recommended": true, 24 | "style": { 25 | "noInferrableTypes": "off", 26 | "noUselessElse": "off", 27 | "noNonNullAssertion": "off", 28 | "useNodejsImportProtocol": "off" 29 | }, 30 | "complexity": { 31 | "useLiteralKeys": "off" 32 | }, 33 | "suspicious": { 34 | "noConfusingVoidType": "off" 35 | }, 36 | "correctness": { 37 | "useImportExtensions": { 38 | "level": "error", 39 | "options": { 40 | "suggestedExtensions": { 41 | "ts": { 42 | "component": "js", 43 | "module": "js" 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | }, 51 | "formatter": { 52 | "enabled": false, 53 | "indentWidth": 4, 54 | "indentStyle": "space" 55 | }, 56 | "json": { 57 | "formatter": { 58 | "enabled": false, 59 | "trailingCommas": "none" 60 | }, 61 | "parser": { 62 | "allowComments": true, 63 | "allowTrailingCommas": true 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /dev/docker/ci/alpine-gcc.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-alpine:latest AS setup-cpp-alpine-gcc 2 | 3 | # install gcc 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler gcc && \ 6 | # cleanup 7 | rm -rf /var/cache/apk/* 8 | 9 | SHELL ["/entrypoint.sh", "/bin/sh", "-c"] 10 | ENTRYPOINT ["/entrypoint.sh", "/bin/sh"] 11 | -------------------------------------------------------------------------------- /dev/docker/ci/alpine-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-alpine:latest AS setup-cpp-alpine-llvm 2 | 3 | # install llvm 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler llvm && \ 6 | # cleanup 7 | rm -rf /var/cache/apk/* 8 | 9 | SHELL ["/entrypoint.sh", "/bin/sh", "-c"] 10 | ENTRYPOINT ["/entrypoint.sh", "/bin/sh"] 11 | -------------------------------------------------------------------------------- /dev/docker/ci/alpine-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-alpine:latest AS setup-cpp-alpine-mingw 2 | 3 | # install mingw/powershell 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler mingw \ 6 | --powershell true && \ 7 | # cleanup 8 | rm -rf /var/cache/apk/* 9 | 10 | SHELL ["/entrypoint.sh", "/bin/sh", "-c"] 11 | ENTRYPOINT ["/entrypoint.sh", "/bin/sh"] 12 | -------------------------------------------------------------------------------- /dev/docker/ci/alpine.dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_VERSION=22-alpine3.21 2 | 3 | #### Base Image with Node.js 4 | FROM --platform=$BUILDPLATFORM node:${BASE_VERSION} AS alpine-nodejs 5 | 6 | #### Base Image with Tools 7 | FROM alpine-nodejs AS setup-cpp-alpine 8 | 9 | COPY "./dist/modern" "/usr/lib/setup-cpp/" 10 | 11 | # install the cpp tools 12 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 13 | --cmake true \ 14 | --ninja true \ 15 | --task true \ 16 | --python true \ 17 | --make true \ 18 | --cppcheck true \ 19 | --gcovr true \ 20 | --doxygen true \ 21 | --vcpkg true \ 22 | --ccache true \ 23 | --conan true \ 24 | --cmakelang true \ 25 | --meson true && \ 26 | # cleanup 27 | rm -rf /var/cache/apk/* 28 | 29 | # Custom entrypoint due to bash -l limitations on Alpine 30 | RUN printf '#!/bin/bash\nsource $HOME/.cpprc\nexec "$@"\n' > /entrypoint.sh && \ 31 | chmod +x /entrypoint.sh 32 | 33 | SHELL ["/entrypoint.sh", "/bin/sh", "-c"] 34 | ENTRYPOINT ["/entrypoint.sh", "/bin/sh"] 35 | -------------------------------------------------------------------------------- /dev/docker/ci/arch-gcc.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-arch:latest AS setup-cpp-arch-gcc 2 | 3 | # install llvm 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler gcc && \ 6 | # arch cleanup 7 | pacman -Scc --noconfirm && \ 8 | rm -rf /var/cache/pacman/pkg/* && \ 9 | rm -rf /tmp/* 10 | 11 | SHELL ["/bin/bash", "-l", "-c"] 12 | ENTRYPOINT ["/bin/bash", "-l"] 13 | -------------------------------------------------------------------------------- /dev/docker/ci/arch-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-arch:latest AS setup-cpp-arch-llvm 2 | 3 | # install llvm 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler llvm && \ 6 | # arch cleanup 7 | pacman -Scc --noconfirm && \ 8 | rm -rf /var/cache/pacman/pkg/* && \ 9 | rm -rf /tmp/* 10 | 11 | SHELL ["/bin/bash", "-l", "-c"] 12 | ENTRYPOINT ["/bin/bash", "-l"] 13 | -------------------------------------------------------------------------------- /dev/docker/ci/arch-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-arch:latest AS setup-cpp-arch-mingw 2 | 3 | # install mingw/powershell 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler mingw \ 6 | --powershell true && \ 7 | # arch cleanup 8 | pacman -Scc --noconfirm && \ 9 | rm -rf /var/cache/pacman/pkg/* && \ 10 | rm -rf /tmp/* 11 | 12 | SHELL ["/bin/bash", "-l", "-c"] 13 | ENTRYPOINT ["/bin/bash", "-l"] 14 | -------------------------------------------------------------------------------- /dev/docker/ci/arch.dockerfile: -------------------------------------------------------------------------------- 1 | ## base image 2 | FROM --platform=$BUILDPLATFORM archlinux:base AS arch-nodejs 3 | 4 | # Setup bash environment 5 | RUN echo '[[ -f ~/.bashrc ]] && . ~/.bashrc' > /root/.bash_profile 6 | 7 | RUN pacman -Syuu --noconfirm && \ 8 | pacman-db-upgrade && \ 9 | # install nodejs 10 | pacman -S --noconfirm --needed nodejs npm && \ 11 | # cleanup 12 | pacman -Scc --noconfirm && \ 13 | rm -rf /var/cache/pacman/pkg/* && \ 14 | rm -rf /tmp/* 15 | 16 | FROM arch-nodejs AS setup-cpp-arch 17 | 18 | COPY "./dist/modern" "/usr/lib/setup-cpp/" 19 | 20 | # install the cpp tools 21 | RUN pacman -Syuu --noconfirm && \ 22 | pacman-db-upgrade && \ 23 | node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 24 | --cmake true \ 25 | --ninja true \ 26 | --task true \ 27 | --vcpkg true \ 28 | --python true \ 29 | --make true \ 30 | --cppcheck true \ 31 | --gcovr true \ 32 | --doxygen true \ 33 | --ccache true \ 34 | --conan true \ 35 | --cmakelang true \ 36 | --meson true && \ 37 | # arch cleanup 38 | pacman -Scc --noconfirm && \ 39 | rm -rf /var/cache/pacman/pkg/* && \ 40 | rm -rf /tmp/* 41 | 42 | SHELL ["/bin/bash", "-l", "-c"] 43 | ENTRYPOINT ["/bin/bash", "-l"] 44 | -------------------------------------------------------------------------------- /dev/docker/ci/docker-ci.mjs: -------------------------------------------------------------------------------- 1 | import { readFile, writeFile } from "fs/promises" 2 | 3 | async function main() { 4 | const names = ["ubuntu-llvm", "arch-llvm", "fedora-llvm", "ubuntu-mingw", "arch-mingw", "fedora-mingw"] 5 | await Promise.all( 6 | names.map(async (name) => { 7 | const dockerFileContent = await readFile(`./dev/docker/setup-cpp/setup-cpp-${name}.dockerfile`, "utf-8") 8 | const modifiedDockerFile = dockerFileContent 9 | // load the externally built setup-cpp 10 | .replace(/FROM (.*)/g, `FROM $1\n\nCOPY "./dist/modern" "/usr/lib/setup-cpp/"`) 11 | .replace("setup-cpp ", "node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs ") 12 | // remove the npm install line 13 | .replace(/# install setup-cpp\n\s*npm install -g setup-cpp.*\n/, "") 14 | 15 | // write the new file in dev/docker/ci 16 | await writeFile(`./dev/docker/ci/${name}.dockerfile`, modifiedDockerFile) 17 | }), 18 | ) 19 | } 20 | 21 | await main() 22 | -------------------------------------------------------------------------------- /dev/docker/ci/fedora-gcc.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-fedora:latest AS setup-cpp-fedora-gcc 2 | 3 | # install gcc 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler gcc && \ 6 | # cleanup 7 | dnf clean all && \ 8 | rm -rf /tmp/* 9 | 10 | SHELL ["/bin/bash", "-l", "-c"] 11 | ENTRYPOINT ["/bin/bash", "-l"] 12 | -------------------------------------------------------------------------------- /dev/docker/ci/fedora-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-fedora:latest AS setup-cpp-fedora-llvm 2 | 3 | # install llvm 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler llvm && \ 6 | # cleanup 7 | dnf clean all && \ 8 | rm -rf /tmp/* 9 | 10 | SHELL ["/bin/bash", "-l", "-c"] 11 | ENTRYPOINT ["/bin/bash", "-l"] 12 | -------------------------------------------------------------------------------- /dev/docker/ci/fedora-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-fedora:latest AS setup-cpp-fedora-mingw 2 | 3 | # install mingw 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler mingw \ 6 | --powershell true && \ 7 | # cleanup 8 | dnf clean all && \ 9 | rm -rf /tmp/* 10 | 11 | SHELL ["/bin/bash", "-l", "-c"] 12 | ENTRYPOINT ["/bin/bash", "-l"] 13 | -------------------------------------------------------------------------------- /dev/docker/ci/fedora.dockerfile: -------------------------------------------------------------------------------- 1 | ## base image 2 | FROM --platform=$BUILDPLATFORM fedora:40 AS fedora-nodejs 3 | 4 | # install nodejs 5 | RUN dnf -y install nodejs npm && \ 6 | # cleanup 7 | dnf clean all && \ 8 | rm -rf /tmp/* 9 | 10 | FROM fedora-nodejs AS setup-cpp-fedora 11 | 12 | COPY "./dist/modern" "/usr/lib/setup-cpp/" 13 | 14 | # install the cpp tools 15 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 16 | --cmake true \ 17 | --ninja true \ 18 | --task true \ 19 | --vcpkg true \ 20 | --python true \ 21 | --make true \ 22 | --cppcheck true \ 23 | --gcovr true \ 24 | --doxygen true \ 25 | --ccache true \ 26 | --conan true \ 27 | --cmakelang true \ 28 | --meson true && \ 29 | # cleanup 30 | dnf clean all && \ 31 | rm -rf /tmp/* 32 | 33 | SHELL ["/bin/bash", "-l", "-c"] 34 | ENTRYPOINT ["/bin/bash", "-l"] 35 | -------------------------------------------------------------------------------- /dev/docker/ci/tests/base.yml: -------------------------------------------------------------------------------- 1 | schemaVersion: 2.0.0 2 | 3 | commandTests: 4 | - name: cmake 5 | command: bash 6 | args: ["-l", "-c", "cmake --version"] 7 | expectedOutput: [".*3.*"] 8 | - name: ninja 9 | command: bash 10 | args: ["-l", "-c", "ninja --version"] 11 | expectedOutput: [".*1.*"] 12 | - name: task 13 | command: bash 14 | args: ["-l", "-c", "task --version"] 15 | expectedOutput: [".*Task version:\\s*v3.*"] 16 | - name: python 17 | command: bash 18 | args: ["-l", "-c", "python --version"] 19 | expectedOutput: [".*Python.*"] 20 | - name: python3 21 | command: bash 22 | args: ["-l", "-c", "python3 --version"] 23 | expectedOutput: [".*Python.*"] 24 | - name: make 25 | command: bash 26 | args: ["-l", "-c", "make --version"] 27 | expectedOutput: [".*GNU Make.*"] 28 | - name: cppcheck 29 | command: bash 30 | args: ["-l", "-c", "cppcheck --version"] 31 | expectedOutput: [".*"] 32 | - name: gcovr 33 | command: bash 34 | args: ["-l", "-c", "gcovr --version"] 35 | expectedOutput: [".*gcovr.*"] 36 | - name: doxygen 37 | command: bash 38 | args: ["-l", "-c", "doxygen --version"] 39 | expectedOutput: [".*1.*"] 40 | - name: ccache 41 | command: bash 42 | args: ["-l", "-c", "ccache --version"] 43 | expectedOutput: [".*"] 44 | - name: conan 45 | command: bash 46 | args: ["-l", "-c", "conan --version"] 47 | expectedOutput: [".*"] 48 | - name: meson 49 | command: bash 50 | args: ["-l", "-c", "meson --version"] 51 | expectedOutput: [".*"] 52 | - name: cmake-format 53 | command: bash 54 | args: ["-l", "-c", "cmake-format --version"] 55 | expectedOutput: [".*"] 56 | - name: cmake-lint 57 | command: bash 58 | args: ["-l", "-c", "cmake-lint --version"] 59 | expectedOutput: [".*"] 60 | 61 | fileExistenceTests: 62 | - name: "vcpkg" 63 | path: "/root/vcpkg" 64 | shouldExist: true 65 | - name: "ninja" 66 | path: "/root/ninja" 67 | shouldExist: true 68 | - name: "cmake" 69 | path: "/root/cmake" 70 | shouldExist: true 71 | - name: "task" 72 | path: "/root/task" 73 | shouldExist: true 74 | -------------------------------------------------------------------------------- /dev/docker/ci/tests/gcc.yml: -------------------------------------------------------------------------------- 1 | schemaVersion: 2.0.0 2 | 3 | commandTests: 4 | - name: gcc 5 | command: bash 6 | args: ["-l", "-c", "gcc --version"] 7 | expectedOutput: [".*gcc.*"] 8 | - name: g++ 9 | command: bash 10 | args: ["-l", "-c", "g++ --version"] 11 | expectedOutput: [".*g\\+\\+.*"] 12 | -------------------------------------------------------------------------------- /dev/docker/ci/tests/index.mts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { fileURLToPath } from "url" 3 | import { execa } from "execa" 4 | 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 6 | const rootDir = path.resolve(__dirname, "..", "..", "..", "..") 7 | const testsDir = path.resolve(rootDir, "./dev/docker/ci/tests") 8 | 9 | async function main() { 10 | const variants = ["base", "gcc", "llvm", "mingw"] 11 | 12 | const distros = ["ubuntu", "fedora", "arch"] 13 | 14 | let failed = false 15 | for (const distro of distros) { 16 | for (const variant of variants) { 17 | // eslint-disable-next-line no-await-in-loop 18 | const result = await testDocker(variant, distro) 19 | if (result !== 0) { 20 | failed = true 21 | } 22 | } 23 | } 24 | 25 | if (failed) { 26 | process.exit(1) 27 | } 28 | } 29 | await main() 30 | 31 | /** 32 | * Test the docker image 33 | * @param variant - The variant to test 34 | * @param distro - The distro to test 35 | * @returns The exit code of the test 36 | */ 37 | async function testDocker(variant: string, distro: string): Promise { 38 | try { 39 | const image = variant === "base" 40 | ? `aminya/setup-cpp-${distro}:latest` 41 | : `aminya/setup-cpp-${distro}-${variant}:latest` 42 | const testConfig = path.join(testsDir, `${variant}.yml`) 43 | 44 | console.log(`Testing ${image} with ${testConfig} `) 45 | 46 | // Test the specific config 47 | await runContainerStructureTest(image, testConfig) 48 | 49 | // Test the base config 50 | if (variant !== "base") { 51 | const baseResult = await testDocker("base", distro) 52 | if (baseResult !== 0) { 53 | return baseResult 54 | } 55 | } 56 | 57 | return 0 58 | } catch (error) { 59 | if (error instanceof Error) { 60 | console.error(error.message) 61 | } else { 62 | console.error(error) 63 | } 64 | return 1 65 | } 66 | } 67 | 68 | async function runContainerStructureTest(image: string, testConfig: string) { 69 | await execa("container-structure-test", [ 70 | "test", 71 | "--pull", 72 | "--image", 73 | image, 74 | "--config", 75 | testConfig, 76 | "--platform", 77 | "linux/amd64", 78 | ], { 79 | stdio: "inherit", 80 | }) 81 | } 82 | -------------------------------------------------------------------------------- /dev/docker/ci/tests/llvm.yml: -------------------------------------------------------------------------------- 1 | schemaVersion: 2.0.0 2 | 3 | commandTests: 4 | - name: clang 5 | command: bash 6 | args: ["-l", "-c", "clang --version"] 7 | expectedOutput: [".*clang version.*"] 8 | - name: clang++ 9 | command: bash 10 | args: ["-l", "-c", "clang++ --version"] 11 | expectedOutput: [".*clang version.*"] 12 | -------------------------------------------------------------------------------- /dev/docker/ci/tests/mingw.yml: -------------------------------------------------------------------------------- 1 | schemaVersion: 2.0.0 2 | 3 | commandTests: 4 | - name: mingw c compiler 5 | command: bash 6 | args: ["-l", "-c", "x86_64-w64-mingw32-gcc --version"] 7 | expectedOutput: [".*x86_64-w64-mingw32-gcc.*"] 8 | - name: mingw c++ compiler 9 | command: bash 10 | args: ["-l", "-c", "x86_64-w64-mingw32-g++ --version"] 11 | expectedOutput: [".*x86_64-w64-mingw32-g\\+\\+.*"] 12 | - name: powershell 13 | command: bash 14 | args: ["-l", "-c", "pwsh --version"] 15 | expectedOutput: [".*PowerShell.*"] 16 | 17 | fileExistenceTests: 18 | - name: "cross root" 19 | path: "/usr/x86_64-w64-mingw32" 20 | shouldExist: true 21 | -------------------------------------------------------------------------------- /dev/docker/ci/ubuntu-gcc.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-ubuntu:latest AS setup-cpp-ubuntu-gcc 2 | 3 | # install gcc 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler gcc && \ 6 | # cleanup 7 | apt-get clean autoclean && \ 8 | apt-get autoremove -y && \ 9 | rm -rf /var/lib/apt/lists/* && \ 10 | rm -rf /tmp/* 11 | 12 | SHELL ["/bin/bash", "-l", "-c"] 13 | ENTRYPOINT ["/bin/bash", "-l"] 14 | -------------------------------------------------------------------------------- /dev/docker/ci/ubuntu-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-ubuntu:latest AS setup-cpp-ubuntu-llvm 2 | 3 | # install llvm 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler llvm && \ 6 | # cleanup 7 | apt-get clean autoclean && \ 8 | apt-get autoremove -y && \ 9 | rm -rf /var/lib/apt/lists/* && \ 10 | rm -rf /tmp/* 11 | 12 | SHELL ["/bin/bash", "-l", "-c"] 13 | ENTRYPOINT ["/bin/bash", "-l"] 14 | -------------------------------------------------------------------------------- /dev/docker/ci/ubuntu-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | FROM aminya/setup-cpp-ubuntu:latest AS setup-cpp-ubuntu-mingw 2 | 3 | # install mingw/powershell 4 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 5 | --compiler mingw \ 6 | --powershell true && \ 7 | # cleanup 8 | apt-get clean autoclean && \ 9 | apt-get autoremove -y && \ 10 | rm -rf /var/lib/apt/lists/* && \ 11 | rm -rf /tmp/* 12 | 13 | SHELL ["/bin/bash", "-l", "-c"] 14 | ENTRYPOINT ["/bin/bash", "-l"] 15 | -------------------------------------------------------------------------------- /dev/docker/ci/ubuntu.dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_VERSION=22.04 2 | 3 | #### Base Image with Node.js 4 | FROM --platform=$BUILDPLATFORM ubuntu:${BASE_VERSION} AS ubuntu-nodejs 5 | 6 | # install latest nodejs 7 | RUN apt-get update -qq && \ 8 | apt-get install -y --no-install-recommends curl gnupg ca-certificates && \ 9 | mkdir -p /etc/apt/keyrings && \ 10 | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ 11 | echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ 12 | apt-get update -qq && \ 13 | apt-get install -y --no-install-recommends nodejs && \ 14 | # cleanup 15 | apt-get clean && \ 16 | rm -rf /var/lib/apt/lists/* && \ 17 | rm -rf /tmp/* 18 | 19 | #### Base Image with Tools 20 | FROM ubuntu-nodejs AS setup-cpp-ubuntu 21 | 22 | COPY "./dist/modern" "/usr/lib/setup-cpp/" 23 | 24 | # install the cpp tools 25 | RUN node --enable-source-maps /usr/lib/setup-cpp/setup-cpp.mjs \ 26 | --apt-fast true \ 27 | --cmake true \ 28 | --ninja true \ 29 | --task true \ 30 | --python true \ 31 | --make true \ 32 | --cppcheck true \ 33 | --gcovr true \ 34 | --doxygen true \ 35 | --vcpkg true \ 36 | --ccache true \ 37 | --conan true \ 38 | --cmakelang true \ 39 | --meson true && \ 40 | # cleanup 41 | apt-get clean autoclean && \ 42 | apt-get autoremove -y && \ 43 | rm -rf /var/lib/apt/lists/* && \ 44 | rm -rf /tmp/* 45 | 46 | SHELL ["/bin/bash", "-l", "-c"] 47 | ENTRYPOINT ["/bin/bash", "-l"] 48 | -------------------------------------------------------------------------------- /dev/docker/examples/alpine-gcc.dockerfile: -------------------------------------------------------------------------------- 1 | #### Building (example) 2 | FROM aminya/setup-cpp-alpine-gcc AS builder 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN task build 7 | 8 | #### Running environment 9 | # use a fresh image as the runner 10 | FROM alpine:3.21 AS runner 11 | 12 | # copy the built binaries and their runtime dependencies 13 | COPY --from=builder /home/app/build/my_exe/Release/ /home/app/ 14 | WORKDIR /home/app/ 15 | ENTRYPOINT ["./my_exe"] 16 | -------------------------------------------------------------------------------- /dev/docker/examples/alpine-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | #### Building (example) 2 | FROM aminya/setup-cpp-alpine-llvm AS builder 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN task build 7 | 8 | #### Running environment 9 | # use a fresh image as the runner 10 | FROM alpine:3.21 AS runner 11 | 12 | # copy the built binaries and their runtime dependencies 13 | COPY --from=builder /home/app/build/my_exe/Release/ /home/app/ 14 | WORKDIR /home/app/ 15 | ENTRYPOINT ["./my_exe"] 16 | -------------------------------------------------------------------------------- /dev/docker/examples/alpine-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | #### Cross Building (example) 2 | FROM aminya/setup-cpp-alpine-mingw AS builder-mingw 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN bash -c 'source ~/.cpprc \ 7 | && task build_cross_mingw' 8 | -------------------------------------------------------------------------------- /dev/docker/examples/arch-gcc.dockerfile: -------------------------------------------------------------------------------- 1 | #### Building (example) 2 | FROM aminya/setup-cpp-arch-gcc AS builder 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN task build 7 | 8 | #### Running environment 9 | # use a fresh image as the runner 10 | FROM archlinux:base AS runner 11 | 12 | # copy the built binaries and their runtime dependencies 13 | COPY --from=builder /home/app/build/my_exe/Release/ /home/app/ 14 | WORKDIR /home/app/ 15 | ENTRYPOINT ["./my_exe"] 16 | -------------------------------------------------------------------------------- /dev/docker/examples/arch-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | #### Building (example) 2 | FROM aminya/setup-cpp-arch-llvm AS builder 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN task build 7 | 8 | #### Running environment 9 | # use a fresh image as the runner 10 | FROM archlinux:base AS runner 11 | 12 | # copy the built binaries and their runtime dependencies 13 | COPY --from=builder /home/app/build/my_exe/Release/ /home/app/ 14 | WORKDIR /home/app/ 15 | ENTRYPOINT ["./my_exe"] 16 | -------------------------------------------------------------------------------- /dev/docker/examples/arch-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | #### Cross Building (example) 2 | FROM aminya/setup-cpp-arch-mingw AS builder-mingw 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN bash -c 'source ~/.cpprc \ 7 | && task build_cross_mingw' 8 | -------------------------------------------------------------------------------- /dev/docker/examples/fedora-gcc.dockerfile: -------------------------------------------------------------------------------- 1 | #### Building (example) 2 | FROM aminya/setup-cpp-fedora-gcc AS builder 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN task build 7 | 8 | #### Running environment 9 | # use a fresh image as the runner 10 | FROM fedora:40 AS runner 11 | 12 | # copy the built binaries and their runtime dependencies 13 | COPY --from=builder /home/app/build/my_exe/Release/ /home/app/ 14 | WORKDIR /home/app/ 15 | ENTRYPOINT ["./my_exe"] 16 | -------------------------------------------------------------------------------- /dev/docker/examples/fedora-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | #### Building (example) 2 | FROM aminya/setup-cpp-fedora-llvm AS builder 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN task build 7 | 8 | #### Running environment 9 | # use a fresh image as the runner 10 | FROM fedora:40 AS runner 11 | 12 | # copy the built binaries and their runtime dependencies 13 | COPY --from=builder /home/app/build/my_exe/Release/ /home/app/ 14 | WORKDIR /home/app/ 15 | ENTRYPOINT ["./my_exe"] 16 | -------------------------------------------------------------------------------- /dev/docker/examples/fedora-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | #### Cross Building (example) 2 | FROM aminya/setup-cpp-fedora-mingw AS builder-mingw 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN bash -c 'source ~/.cpprc \ 7 | && task build_cross_mingw' 8 | -------------------------------------------------------------------------------- /dev/docker/examples/ubuntu-gcc.dockerfile: -------------------------------------------------------------------------------- 1 | #### Building (example) 2 | FROM aminya/setup-cpp-ubuntu-gcc AS builder 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN task build 7 | 8 | #### Running environment 9 | # use a fresh image as the runner 10 | FROM ubuntu:22.04 AS runner 11 | 12 | # copy the built binaries and their runtime dependencies 13 | COPY --from=builder /home/app/build/my_exe/Release/ /home/app/ 14 | WORKDIR /home/app/ 15 | ENTRYPOINT ["./my_exe"] 16 | -------------------------------------------------------------------------------- /dev/docker/examples/ubuntu-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | #### Building (example) 2 | FROM aminya/setup-cpp-ubuntu-llvm AS builder 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN task build 7 | 8 | #### Running environment 9 | # use a fresh image as the runner 10 | FROM ubuntu:22.04 AS runner 11 | 12 | # copy the built binaries and their runtime dependencies 13 | COPY --from=builder /home/app/build/my_exe/Release/ /home/app/ 14 | WORKDIR /home/app/ 15 | ENTRYPOINT ["./my_exe"] 16 | -------------------------------------------------------------------------------- /dev/docker/examples/ubuntu-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | #### Cross Building (example) 2 | FROM aminya/setup-cpp-ubuntu-mingw AS builder-mingw 3 | 4 | COPY ./dev/cpp_vcpkg_project /home/app 5 | WORKDIR /home/app 6 | RUN bash -c 'source ~/.cpprc \ 7 | && task build_cross_mingw' 8 | -------------------------------------------------------------------------------- /dev/docker/setup-cpp/setup-cpp-arch-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | ## base image 2 | FROM archlinux:base AS setup-cpp-arch 3 | 4 | RUN pacman -Syuu --noconfirm && \ 5 | pacman-db-upgrade && \ 6 | # install nodejs 7 | pacman -S --noconfirm --needed nodejs npm && \ 8 | # install setup-cpp 9 | npm install -g setup-cpp@v1.1.1 && \ 10 | # install the compiler and tools 11 | NODE_OPTIONS="--enable-source-maps" \ 12 | setup-cpp \ 13 | --compiler llvm \ 14 | --cmake true \ 15 | --ninja true \ 16 | --task true \ 17 | --vcpkg true \ 18 | --python true \ 19 | --make true \ 20 | --cppcheck true \ 21 | --gcovr true \ 22 | --doxygen true \ 23 | --ccache true && \ 24 | # arch cleanup 25 | pacman -Scc --noconfirm && \ 26 | rm -rf /var/cache/pacman/pkg/* && \ 27 | rm -rf /tmp/* 28 | 29 | SHELL ["/bin/bash", "-l", "-c"] 30 | ENTRYPOINT ["/bin/bash", "-l"] 31 | -------------------------------------------------------------------------------- /dev/docker/setup-cpp/setup-cpp-arch-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | ## base image 2 | FROM archlinux:base AS setup-cpp-arch-mingw 3 | 4 | RUN pacman -Syuu --noconfirm && \ 5 | pacman-db-upgrade && \ 6 | # install nodejs 7 | pacman -S --noconfirm --needed nodejs npm && \ 8 | # install setup-cpp 9 | npm install -g setup-cpp@v1.1.1 && \ 10 | # install the compiler and tools 11 | NODE_OPTIONS="--enable-source-maps" \ 12 | setup-cpp \ 13 | --compiler mingw \ 14 | --cmake true \ 15 | --ninja true \ 16 | --task true \ 17 | --vcpkg true \ 18 | --python true \ 19 | --make true \ 20 | --cppcheck true \ 21 | --gcovr true \ 22 | --doxygen true \ 23 | --ccache true && \ 24 | # arch cleanup 25 | pacman -Scc --noconfirm && \ 26 | rm -rf /var/cache/pacman/pkg/* && \ 27 | rm -rf /tmp/* 28 | 29 | SHELL ["/bin/bash", "-l", "-c"] 30 | ENTRYPOINT ["/bin/bash", "-l"] 31 | -------------------------------------------------------------------------------- /dev/docker/setup-cpp/setup-cpp-fedora-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | ## base image 2 | FROM fedora:40 AS setup-cpp-fedora 3 | 4 | # install nodejs 5 | RUN dnf -y install nodejs npm && \ 6 | # install setup-cpp 7 | npm install -g setup-cpp@v1.1.1 && \ 8 | # install the compiler and tools 9 | NODE_OPTIONS="--enable-source-maps" \ 10 | setup-cpp \ 11 | --compiler llvm \ 12 | --cmake true \ 13 | --ninja true \ 14 | --task true \ 15 | --vcpkg true \ 16 | --python true \ 17 | --make true \ 18 | --cppcheck true \ 19 | --gcovr true \ 20 | --doxygen true \ 21 | --ccache true && \ 22 | # cleanup 23 | dnf clean all && \ 24 | rm -rf /tmp/* 25 | 26 | SHELL ["/bin/bash", "-l", "-c"] 27 | ENTRYPOINT ["/bin/bash", "-l"] 28 | -------------------------------------------------------------------------------- /dev/docker/setup-cpp/setup-cpp-fedora-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | ## base image 2 | FROM fedora:40 AS setup-cpp-fedora-mingw 3 | 4 | # install nodejs 5 | RUN dnf -y install nodejs npm && \ 6 | # install setup-cpp 7 | npm install -g setup-cpp@v1.1.1 && \ 8 | # install the compiler and tools 9 | NODE_OPTIONS="--enable-source-maps" \ 10 | setup-cpp \ 11 | --compiler mingw \ 12 | --cmake true \ 13 | --ninja true \ 14 | --task true \ 15 | --vcpkg true \ 16 | --python true \ 17 | --make true \ 18 | --cppcheck true \ 19 | --gcovr true \ 20 | --doxygen true \ 21 | --ccache true \ 22 | --powershell true && \ 23 | # cleanup 24 | dnf clean all && \ 25 | rm -rf /tmp/* 26 | 27 | SHELL ["/bin/bash", "-l", "-c"] 28 | ENTRYPOINT ["/bin/bash", "-l"] 29 | -------------------------------------------------------------------------------- /dev/docker/setup-cpp/setup-cpp-ubuntu-20.0.4-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | #### Base Image 2 | FROM ubuntu:20.04 AS setup-cpp-ubuntu-mingw 3 | 4 | RUN apt-get update -qq && \ 5 | # install latest nodejs on ubuntu 20.04 6 | apt-get update -qq && \ 7 | apt-get install -y --no-install-recommends curl gnupg ca-certificates && \ 8 | mkdir -p /etc/apt/keyrings && \ 9 | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ 10 | echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ 11 | apt-get update -qq && \ 12 | apt-get install -y --no-install-recommends nodejs && \ 13 | # install setup-cpp 14 | npm install -g setup-cpp@v1.1.1 && \ 15 | # install the compiler and tools 16 | NODE_OPTIONS="--enable-source-maps" \ 17 | setup-cpp \ 18 | --nala true \ 19 | --compiler mingw \ 20 | --cmake true \ 21 | --ninja true \ 22 | --task true \ 23 | --vcpkg true \ 24 | --python true \ 25 | --make true \ 26 | --cppcheck true \ 27 | --gcovr true \ 28 | --doxygen true \ 29 | --ccache true \ 30 | --powershell true && \ 31 | # cleanup 32 | nala autoremove -y && \ 33 | nala autopurge -y && \ 34 | apt-get clean && \ 35 | nala clean --lists && \ 36 | rm -rf /var/lib/apt/lists/* && \ 37 | rm -rf /tmp/* 38 | 39 | SHELL ["/bin/bash", "-l", "-c"] 40 | ENTRYPOINT ["/bin/bash", "-l"] 41 | -------------------------------------------------------------------------------- /dev/docker/setup-cpp/setup-cpp-ubuntu-llvm.dockerfile: -------------------------------------------------------------------------------- 1 | #### Base Image 2 | FROM ubuntu:22.04 AS setup-cpp-ubuntu 3 | 4 | # install latest nodejs 5 | RUN apt-get update -qq && \ 6 | apt-get install -y --no-install-recommends curl gnupg ca-certificates && \ 7 | mkdir -p /etc/apt/keyrings && \ 8 | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ 9 | echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ 10 | apt-get update -qq && \ 11 | apt-get install -y --no-install-recommends nodejs && \ 12 | # install setup-cpp 13 | npm install -g setup-cpp@v1.1.1 && \ 14 | # install the compiler and tools 15 | NODE_OPTIONS="--enable-source-maps" \ 16 | setup-cpp \ 17 | --nala true \ 18 | --compiler llvm \ 19 | --cmake true \ 20 | --ninja true \ 21 | --task true \ 22 | --vcpkg true \ 23 | --python true \ 24 | --make true \ 25 | --cppcheck true \ 26 | --gcovr true \ 27 | --doxygen true \ 28 | --ccache true && \ 29 | # cleanup 30 | nala autoremove -y && \ 31 | nala autopurge -y && \ 32 | apt-get clean && \ 33 | nala clean --lists && \ 34 | rm -rf /var/lib/apt/lists/* && \ 35 | rm -rf /tmp/* 36 | 37 | SHELL ["/bin/bash", "-l", "-c"] 38 | ENTRYPOINT ["/bin/bash", "-l"] 39 | -------------------------------------------------------------------------------- /dev/docker/setup-cpp/setup-cpp-ubuntu-mingw.dockerfile: -------------------------------------------------------------------------------- 1 | #### Base Image 2 | FROM ubuntu:22.04 AS setup-cpp-ubuntu-mingw 3 | 4 | # install latest nodejs 5 | RUN apt-get update -qq && \ 6 | apt-get install -y --no-install-recommends curl gnupg ca-certificates && \ 7 | mkdir -p /etc/apt/keyrings && \ 8 | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ 9 | echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ 10 | apt-get update -qq && \ 11 | apt-get install -y --no-install-recommends nodejs && \ 12 | # install setup-cpp 13 | npm install -g setup-cpp@v1.1.1 && \ 14 | # install the compiler and tools 15 | NODE_OPTIONS="--enable-source-maps" \ 16 | setup-cpp \ 17 | --nala true \ 18 | --compiler mingw \ 19 | --cmake true \ 20 | --ninja true \ 21 | --task true \ 22 | --vcpkg true \ 23 | --python true \ 24 | --make true \ 25 | --cppcheck true \ 26 | --gcovr true \ 27 | --doxygen true \ 28 | --ccache true \ 29 | --powershell true && \ 30 | # cleanup 31 | nala autoremove -y && \ 32 | nala autopurge -y && \ 33 | apt-get clean && \ 34 | nala clean --lists && \ 35 | rm -rf /var/lib/apt/lists/* && \ 36 | rm -rf /tmp/* 37 | 38 | SHELL ["/bin/bash", "-l", "-c"] 39 | ENTRYPOINT ["/bin/bash", "-l"] 40 | -------------------------------------------------------------------------------- /dev/readme/template.md: -------------------------------------------------------------------------------- 1 |

<%= projectName %>

2 |

3 | <% if (isProjectOnNpm) { -%> 4 | 5 | Version 6 | 7 | <% } -%> 8 | <% if (projectVersion && !isProjectOnNpm) { -%> 9 | Version 10 | <% } -%> 11 | <% if (projectPrerequisites) { -%> 12 | <% projectPrerequisites.map(({ name, value }) => { -%> 13 | 14 | <% }) -%> 15 | <% } -%> 16 | <% if (projectDocumentationUrl) { -%> 17 | 18 | Documentation 19 | 20 | <% } -%> 21 | <% if (isGithubRepos) { -%> 22 | 23 | Maintenance 24 | 25 | <% } -%> 26 | <% if (licenseName) { -%> 27 | 28 | License: <%= licenseName %> 29 | 30 | <% } -%> 31 | <% if (authorTwitterUsername) { -%> 32 | 33 | Twitter: <%= authorTwitterUsername %> 34 | 35 | <% } -%> 36 |

37 | <% if (projectDescription) { -%> 38 | 39 | > <%= projectDescription %> 40 | > <% } -%> 41 | 42 | ## Install 43 | 44 | ```sh 45 | npm install --save <%= projectName %> 46 | ``` 47 | 48 | ## Usage 49 | 50 | 51 | 52 | 53 | 54 | ## 🤝 Contributing 55 | 56 | You can sponsor my work here: 57 | 58 | https://github.com/sponsors/aminya 59 | 60 | Pull requests, issues and feature requests are welcome. 61 | See the [Contributing guide](https://github.com/aminya/setup-cpp/blob/master/CONTRIBUTING.md). 62 | -------------------------------------------------------------------------------- /dev/scripts/pack-exe.mjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import { execaNode } from "execa" 3 | 4 | function getPlatformName() { 5 | switch (process.platform) { 6 | case "win32": { 7 | return "windows" 8 | } 9 | case "darwin": { 10 | return "macos" 11 | } 12 | default: { 13 | return process.platform 14 | } 15 | } 16 | } 17 | 18 | async function main() { 19 | const exe = process.platform === "win32" ? ".exe" : "" 20 | 21 | await execaNode("./node_modules/caxa/build/index.mjs", [ 22 | "--input", 23 | "./dist/modern", 24 | "--output", 25 | `./exe/setup-cpp-${process.arch}-${getPlatformName()}${exe}`, 26 | "--", 27 | `{{caxa}}/node_modules/.bin/node${exe}`, 28 | "{{caxa}}/setup-cpp.mjs", 29 | ]) 30 | } 31 | 32 | main().catch((err) => { 33 | throw err 34 | }) 35 | -------------------------------------------------------------------------------- /dev/scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /dist/legacy/gcc_matcher.json: -------------------------------------------------------------------------------- 1 | {"problemMatcher":[{"owner":"gcc","pattern":[{"regexp":"^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$","file":1,"line":2,"column":3,"severity":4,"message":5}]}]} -------------------------------------------------------------------------------- /dist/legacy/github_facebook_infer.json: -------------------------------------------------------------------------------- 1 | {"v1.2.0":["infer-osx-x86_64-v1.2.0.tar.xz","infer-osx-arm64-v1.2.0.tar.xz","infer-linux-x86_64-v1.2.0.tar.xz"],"v1.1.0":["infer-linux64-v1.1.0.tar.xz"],"v1.0.0":["infer-osx-v1.0.0.tar.xz","infer-linux64-v1.0.0.tar.xz"],"v0.17.0":["infer-osx-v0.17.0.tar.xz","infer-linux64-v0.17.0.tar.xz"],"v0.16.0":["infer-osx-v0.16.0.tar.xz","infer-linux64-v0.16.0.tar.xz"],"v0.15.0":["infer-osx-v0.15.0.tar.xz","infer-linux64-v0.15.0.tar.xz"],"v0.14.0":["infer-osx-v0.14.0.tar.xz","infer-linux64-v0.14.0.tar.xz"],"v0.13.1":["infer-osx-v0.13.1.tar.xz","infer-linux64-v0.13.1.tar.xz"],"v0.13.0":["infer-osx-v0.13.0.tar.xz","infer-linux64-v0.13.0.tar.xz"],"v0.12.1":["infer-osx-v0.12.1.tar.xz","infer-linux64-v0.12.1.tar.xz"],"v0.12.0":["infer-osx-v0.12.0.tar.xz","infer-linux64-v0.12.0.tar.xz"],"v0.11.0":["infer-osx-v0.11.0.tar.xz","infer-linux64-v0.11.0.tar.xz"],"v0.10.0":["infer-osx-v0.10.0.tar.xz","infer-linux64-v0.10.0.tar.xz"],"v0.9.5":["infer-osx-v0.9.5.tar.xz","infer-linux64-v0.9.5.tar.xz"],"v0.9.4.1":["infer-osx-v0.9.4.1.tar.xz","infer-linux64-v0.9.4.1.tar.xz"],"v0.9.4":["infer-osx-v0.9.4.tar.xz","infer-linux64-v0.9.4.tar.xz"],"v0.9.3":["infer-osx-v0.9.3.tar.xz","infer-linux64-v0.9.3.tar.xz"],"v0.9.2":["infer-osx-v0.9.2.tar.xz","infer-linux64-v0.9.2.tar.xz"],"v0.9.1":["infer-osx-v0.9.1.tar.xz","infer-linux64-v0.9.1.tar.xz"],"v0.9.0":["infer-osx-v0.9.0.tar.xz","infer-linux64-v0.9.0.tar.xz"],"v0.8.1":["infer-osx-v0.8.1.tar.xz","infer-linux64-v0.8.1.tar.xz"],"v0.8.0":["infer-osx-v0.8.0.tar.xz","infer-linux64-v0.8.0.tar.xz"],"v0.7.0":["infer-osx-v0.7.0.tar.xz","infer-linux64-v0.7.0.tar.xz"],"v0.6.0":["infer-osx-v0.6.0.tar.xz","infer-linux64-v0.6.0.tar.xz"],"v0.5.0":["infer-osx-v0.5.0.tar.xz","infer-linux64-v0.5.0.tar.xz"],"v0.4.0":["infer-osx-v0.4.0.tar.xz","infer-linux64-v0.4.0.tar.xz"],"v0.3.0":["infer-osx-v0.3.0.tar.xz","infer-linux64-v0.3.0.tar.xz"]} -------------------------------------------------------------------------------- /dist/legacy/llvm_matcher.json: -------------------------------------------------------------------------------- 1 | {"problemMatcher":[{"owner":"llvm","pattern":[{"regexp":"^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$","file":1,"line":2,"column":3,"severity":4,"message":5}]}]} -------------------------------------------------------------------------------- /dist/legacy/msvc_matcher.json: -------------------------------------------------------------------------------- 1 | {"problemMatcher":[{"owner":"msvc","pattern":[{"regexp":"^(?:\\s+\\d+>)?(\\S.*)\\((\\d+),?(\\d+)?(?:,\\d+,\\d+)?\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$","file":1,"line":2,"column":3,"severity":4,"code":5,"message":6}]}]} -------------------------------------------------------------------------------- /dist/legacy/python_matcher.json: -------------------------------------------------------------------------------- 1 | {"problemMatcher":[{"owner":"python","pattern":[{"regexp":"^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$","file":1,"line":2},{"regexp":"^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$","message":2}]}]} -------------------------------------------------------------------------------- /dist/legacy/versions.json: -------------------------------------------------------------------------------- 1 | {"ninja":"1.12.1","cmake":"3.31.7","task":"3.43.3","powershell":"7.5.1","pip":">=22.2.0","python":">=3.7.9","meson":{"linux":{"ubuntu":{"20":"1.8.1","18":"0.61.4","else":"0.61.4"},"else":"1.8.1"},"else":"1.8.1"},"kcov":{"linux":{"ubuntu":{"22":"42-binary","20":"40-binary","14":"40","else":"42"},"else":"42"},"else":"42"},"doxygen":{"linux":{"archlinux":"1.13.2-2","ubuntu":{"22":"1.14.0","18":"1.10.0","else":"1.10.0"},"else":"1.14.0"},"else":"1.14.0"},"gcc":{"win32":"15.1.0posix-12.0.0-ucrt-r1","else":""},"mingw":{"win32":"15.1.0posix-12.0.0-ucrt-r1","else":""},"gcovr":{"linux":{"ubuntu":{"20":"","18":"5.0","else":"5.0"},"else":""},"else":""},"nala":{"linux":{"ubuntu":{"22":"","21":"legacy","else":"legacy"},"else":""},"else":""},"llvm":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clang":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clang++":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clang-tidy":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clangtidy":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clang-format":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clangformat":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"}} -------------------------------------------------------------------------------- /dist/modern/gcc_matcher.json: -------------------------------------------------------------------------------- 1 | {"problemMatcher":[{"owner":"gcc","pattern":[{"regexp":"^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$","file":1,"line":2,"column":3,"severity":4,"message":5}]}]} -------------------------------------------------------------------------------- /dist/modern/github_facebook_infer.json: -------------------------------------------------------------------------------- 1 | {"v1.2.0":["infer-osx-x86_64-v1.2.0.tar.xz","infer-osx-arm64-v1.2.0.tar.xz","infer-linux-x86_64-v1.2.0.tar.xz"],"v1.1.0":["infer-linux64-v1.1.0.tar.xz"],"v1.0.0":["infer-osx-v1.0.0.tar.xz","infer-linux64-v1.0.0.tar.xz"],"v0.17.0":["infer-osx-v0.17.0.tar.xz","infer-linux64-v0.17.0.tar.xz"],"v0.16.0":["infer-osx-v0.16.0.tar.xz","infer-linux64-v0.16.0.tar.xz"],"v0.15.0":["infer-osx-v0.15.0.tar.xz","infer-linux64-v0.15.0.tar.xz"],"v0.14.0":["infer-osx-v0.14.0.tar.xz","infer-linux64-v0.14.0.tar.xz"],"v0.13.1":["infer-osx-v0.13.1.tar.xz","infer-linux64-v0.13.1.tar.xz"],"v0.13.0":["infer-osx-v0.13.0.tar.xz","infer-linux64-v0.13.0.tar.xz"],"v0.12.1":["infer-osx-v0.12.1.tar.xz","infer-linux64-v0.12.1.tar.xz"],"v0.12.0":["infer-osx-v0.12.0.tar.xz","infer-linux64-v0.12.0.tar.xz"],"v0.11.0":["infer-osx-v0.11.0.tar.xz","infer-linux64-v0.11.0.tar.xz"],"v0.10.0":["infer-osx-v0.10.0.tar.xz","infer-linux64-v0.10.0.tar.xz"],"v0.9.5":["infer-osx-v0.9.5.tar.xz","infer-linux64-v0.9.5.tar.xz"],"v0.9.4.1":["infer-osx-v0.9.4.1.tar.xz","infer-linux64-v0.9.4.1.tar.xz"],"v0.9.4":["infer-osx-v0.9.4.tar.xz","infer-linux64-v0.9.4.tar.xz"],"v0.9.3":["infer-osx-v0.9.3.tar.xz","infer-linux64-v0.9.3.tar.xz"],"v0.9.2":["infer-osx-v0.9.2.tar.xz","infer-linux64-v0.9.2.tar.xz"],"v0.9.1":["infer-osx-v0.9.1.tar.xz","infer-linux64-v0.9.1.tar.xz"],"v0.9.0":["infer-osx-v0.9.0.tar.xz","infer-linux64-v0.9.0.tar.xz"],"v0.8.1":["infer-osx-v0.8.1.tar.xz","infer-linux64-v0.8.1.tar.xz"],"v0.8.0":["infer-osx-v0.8.0.tar.xz","infer-linux64-v0.8.0.tar.xz"],"v0.7.0":["infer-osx-v0.7.0.tar.xz","infer-linux64-v0.7.0.tar.xz"],"v0.6.0":["infer-osx-v0.6.0.tar.xz","infer-linux64-v0.6.0.tar.xz"],"v0.5.0":["infer-osx-v0.5.0.tar.xz","infer-linux64-v0.5.0.tar.xz"],"v0.4.0":["infer-osx-v0.4.0.tar.xz","infer-linux64-v0.4.0.tar.xz"],"v0.3.0":["infer-osx-v0.3.0.tar.xz","infer-linux64-v0.3.0.tar.xz"]} -------------------------------------------------------------------------------- /dist/modern/llvm_matcher.json: -------------------------------------------------------------------------------- 1 | {"problemMatcher":[{"owner":"llvm","pattern":[{"regexp":"^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$","file":1,"line":2,"column":3,"severity":4,"message":5}]}]} -------------------------------------------------------------------------------- /dist/modern/msvc_matcher.json: -------------------------------------------------------------------------------- 1 | {"problemMatcher":[{"owner":"msvc","pattern":[{"regexp":"^(?:\\s+\\d+>)?(\\S.*)\\((\\d+),?(\\d+)?(?:,\\d+,\\d+)?\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$","file":1,"line":2,"column":3,"severity":4,"code":5,"message":6}]}]} -------------------------------------------------------------------------------- /dist/modern/python_matcher.json: -------------------------------------------------------------------------------- 1 | {"problemMatcher":[{"owner":"python","pattern":[{"regexp":"^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$","file":1,"line":2},{"regexp":"^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$","message":2}]}]} -------------------------------------------------------------------------------- /dist/modern/versions.json: -------------------------------------------------------------------------------- 1 | {"ninja":"1.12.1","cmake":"3.31.7","task":"3.43.3","powershell":"7.5.1","pip":">=22.2.0","python":">=3.7.9","meson":{"linux":{"ubuntu":{"20":"1.8.1","18":"0.61.4","else":"0.61.4"},"else":"1.8.1"},"else":"1.8.1"},"kcov":{"linux":{"ubuntu":{"22":"42-binary","20":"40-binary","14":"40","else":"42"},"else":"42"},"else":"42"},"doxygen":{"linux":{"archlinux":"1.13.2-2","ubuntu":{"22":"1.14.0","18":"1.10.0","else":"1.10.0"},"else":"1.14.0"},"else":"1.14.0"},"gcc":{"win32":"15.1.0posix-12.0.0-ucrt-r1","else":""},"mingw":{"win32":"15.1.0posix-12.0.0-ucrt-r1","else":""},"gcovr":{"linux":{"ubuntu":{"20":"","18":"5.0","else":"5.0"},"else":""},"else":""},"nala":{"linux":{"ubuntu":{"22":"","21":"legacy","else":"legacy"},"else":""},"else":""},"llvm":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clang":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clang++":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clang-tidy":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clangtidy":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clang-format":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"},"clangformat":{"darwin":{"else":{"else":{"x64":"15.0.7","else":"20.1.4"}}},"linux":{"alpine":{"else":{"else":"19.1.7"}},"else":"20.1.6"},"else":"20.1.6"}} -------------------------------------------------------------------------------- /dprint.json: -------------------------------------------------------------------------------- 1 | { 2 | "lineWidth": 120, 3 | "indentWidth": 2, 4 | "newLineKind": "lf", 5 | "useTabs": false, 6 | "incremental": true, 7 | "typescript": { 8 | "semiColons": "asi", 9 | "quoteProps": "asNeeded", 10 | "useBraces": "always", 11 | "module.sortImportDeclarations": "maintain", 12 | "importDeclaration.sortNamedImports": "maintain" 13 | }, 14 | "json": { 15 | "trailingCommas": "never" 16 | }, 17 | "markdown": {}, 18 | "dockerfile": {}, 19 | "exec": { 20 | "commands": [ 21 | { 22 | "command": "node ./node_modules/prettier/bin/prettier.cjs --stdin-filepath {{file_path}} --tab-width {{indent_width}} --print-width {{line_width}}", 23 | "exts": ["yaml", "yml"] 24 | } 25 | ], 26 | "associations": [ 27 | "**/*{.yaml,.yml}" 28 | ] 29 | }, 30 | "includes": [ 31 | "**/*{.ts,.tsx,.mts,.cts,.js,.jsx,.cjs,.mjs,.json,.jsonc,.md,.yaml,.yml,.dockerfile}" 32 | ], 33 | "excludes": [ 34 | "**/node_modules", 35 | "*.lock*", 36 | "*-lock*", 37 | "./.cache/", 38 | "**/build", 39 | "**/dist", 40 | "dev/cpp_vcpkg_project" 41 | ], 42 | "plugins": [ 43 | "https://plugins.dprint.dev/typescript-0.89.3.wasm", 44 | "https://plugins.dprint.dev/json-0.19.2.wasm", 45 | "https://plugins.dprint.dev/markdown-0.16.4.wasm", 46 | "https://plugins.dprint.dev/dockerfile-0.3.0.wasm", 47 | "https://plugins.dprint.dev/exec-0.4.4.json@c207bf9b9a4ee1f0ecb75c594f774924baf62e8e53a2ce9d873816a408cecbf7" 48 | ], 49 | "$schema": "https://dprint.dev/schemas/v0.json" 50 | } 51 | -------------------------------------------------------------------------------- /jest.config.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('jest').Config} 3 | */ 4 | const jestConfig = { 5 | testMatch: ["**/*.test.ts"], 6 | testEnvironment: "node", 7 | extensionsToTreatAsEsm: [".ts", ".tsx", ".jsx"], 8 | transformIgnorePatterns: [], // transform node_modules 9 | transform: { 10 | "^.+\\.(t|j)sx?$": "@swc/jest", 11 | }, 12 | // resolve js files from ts files 13 | moduleNameMapper: { 14 | "(.+)\\.js": "$1", 15 | }, 16 | // coverage 17 | collectCoverageFrom: ["src/**/*.{ts,tsx,js,jsx}"], 18 | coveragePathIgnorePatterns: ["assets", ".css.d.ts"], 19 | verbose: true, 20 | } 21 | 22 | export default jestConfig 23 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: false 3 | commands: 4 | test.lint: 5 | run: pnpm run test.lint 6 | build: 7 | run: pnpm run build -- --no-color && git add ./dist 8 | -------------------------------------------------------------------------------- /package-version.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "setup-cpp", 3 | "version": "1.6.2" 4 | } 5 | -------------------------------------------------------------------------------- /packages/ci-log/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/ci-log/README.md: -------------------------------------------------------------------------------- 1 |

ci-log

2 |

3 | 4 | Version 5 | 6 | 7 | License: Apache--2.0 8 | 9 |

10 | 11 | > Colorful logging and print for any environment including GitHub Actions 12 | 13 | ## Install 14 | 15 | ```sh 16 | npm install --save ci-log 17 | ``` 18 | 19 | ## Usage 20 | 21 | 22 | 23 | ### `error` (function) 24 | 25 | **Parameters:** 26 | 27 | - err (`string | Error`) 28 | 29 | **returns:** void 30 | 31 | ### `success` (function) 32 | 33 | **Parameters:** 34 | 35 | - msg (`string`) 36 | 37 | **returns:** void 38 | 39 | ### `warning` (function) 40 | 41 | **Parameters:** 42 | 43 | - msg (`string`) 44 | 45 | **returns:** void 46 | 47 | ### `notice` (function) 48 | 49 | **Parameters:** 50 | 51 | - msg (`string`) 52 | 53 | **returns:** void 54 | 55 | ### `info` (function) 56 | 57 | **Parameters:** 58 | 59 | - msg (`string`) 60 | 61 | **returns:** void 62 | 63 | 64 | 65 | ## 🤝 Contributing 66 | 67 | You can sponsor my work here: 68 | 69 | https://github.com/sponsors/aminya 70 | 71 | Pull requests, issues and feature requests are welcome. 72 | See the [Contributing guide](https://github.com/aminya/setup-cpp/blob/master/CONTRIBUTING.md). 73 | -------------------------------------------------------------------------------- /packages/ci-log/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { error, info, notice, success, warning } from "../src/index.js" 2 | 3 | describe("ci-log", () => { 4 | it("should be a function", () => { 5 | expect(error).toBeInstanceOf(Function) 6 | expect(success).toBeInstanceOf(Function) 7 | expect(warning).toBeInstanceOf(Function) 8 | expect(notice).toBeInstanceOf(Function) 9 | expect(info).toBeInstanceOf(Function) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/ci-log/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/ci-log/jest.config.mjs: -------------------------------------------------------------------------------- 1 | import jestConfig from "../../jest.config.mjs" 2 | export default jestConfig 3 | -------------------------------------------------------------------------------- /packages/ci-log/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ci-log", 3 | "version": "1.0.2", 4 | "description": "Colorful logging and print for any environment including GitHub Actions", 5 | "repository": "https://github.com/aminya/setup-cpp", 6 | "homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/ci-log", 7 | "license": "Apache-2.0", 8 | "author": "Amin Yahyaabadi", 9 | "main": "./dist/index.js", 10 | "source": "./src/index.ts", 11 | "type": "module", 12 | "scripts": { 13 | "build": "tsc --pretty", 14 | "dev": "tsc --watch --pretty", 15 | "lint.tsc.test": "tsc --noEmit --pretty -p ./__tests__/tsconfig.json", 16 | "lint.tsc": "tsc --noEmit --pretty", 17 | "lint.eslint": "eslint '**/*.{ts,tsx,js,jsx,cjs,mjs,json,yaml}' --no-error-on-unmatched-pattern --cache --cache-location ./.cache/eslint/ --fix", 18 | "prepublishOnly": "pnpm run build", 19 | "test": "jest --coverage" 20 | }, 21 | "dependencies": { 22 | "@actions/core": "^1.10.1", 23 | "ci-info": "^4.0.0" 24 | }, 25 | "keywords": [ 26 | "log", 27 | "print", 28 | "GitHub", 29 | "actions", 30 | "Gitlab", 31 | "CI", 32 | "color", 33 | "console", 34 | "info", 35 | "notifications" 36 | ], 37 | "devDependencies": {} 38 | } 39 | -------------------------------------------------------------------------------- /packages/ci-log/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as core from "@actions/core" 2 | import ciInfo from "ci-info" 3 | const { GITHUB_ACTIONS } = ciInfo 4 | 5 | export function error(err: string | Error) { 6 | return GITHUB_ACTIONS ? core.error(err) : console.log(`\x1b[31m${err}\x1b[0m`) 7 | } 8 | 9 | export function success(msg: string) { 10 | return console.log(`\x1b[32m${msg}\x1b[0m`) 11 | } 12 | 13 | export function warning(msg: string) { 14 | return GITHUB_ACTIONS ? core.warning(msg) : console.log(`\x1b[33m${msg}\x1b[0m`) 15 | } 16 | 17 | export function notice(msg: string) { 18 | return GITHUB_ACTIONS ? core.notice(msg) : console.log(`\x1b[94m${msg}\x1b[0m`) 19 | } 20 | 21 | export function info(msg: string) { 22 | return GITHUB_ACTIONS ? core.info(msg) : console.log(msg) 23 | } 24 | -------------------------------------------------------------------------------- /packages/ci-log/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "noEmit": false, 6 | "allowImportingTsExtensions": false 7 | }, 8 | "include": ["./src"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/envosman/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/envosman/README.md: -------------------------------------------------------------------------------- 1 |

envosman

2 |

3 | 4 | Version 5 | 6 | 7 | 8 | License: Apache--2.0 9 | 10 |

11 | 12 | > Manage environment variables, PATH, and rc files 13 | 14 | ## Install 15 | 16 | ```sh 17 | npm install --save envosman 18 | ``` 19 | 20 | ## Usage 21 | 22 | 23 | 24 | ### `defaultRcPath` (variable) 25 | 26 | ### `RcOptions` (type) 27 | 28 | Options for adding an rc file 29 | 30 | ### `sourceRCInRc` (variable) 31 | 32 | handles adding conditions to source rc file from .bashrc and .profile 33 | 34 | ### `finalizeRC` (function) 35 | 36 | **Parameters:** 37 | 38 | - rcOptions (`RcOptions`) 39 | 40 | **returns:** Promise 41 | 42 | ### `escapeString` (function) 43 | 44 | Escape a string for use in a shell command 45 | 46 | **Parameters:** 47 | 48 | - valGiven (`string`) - The string to escape 49 | - shouldEscapeSpace (`boolean`) - Whether to escape spaces in the string 50 | 51 | **returns:** any 52 | 53 | ### `AddEnvOptions` (type) 54 | 55 | The options for adding an environment variable 56 | 57 | ### `addEnv` (function) 58 | 59 | Add an environment variable. 60 | 61 | This function is cross-platforms and works in all the local or CI systems. 62 | 63 | **Parameters:** 64 | 65 | - name (`string`) 66 | - valGiven (`string`) 67 | - givenOptions (`Partial`) 68 | 69 | **returns:** Promise 70 | 71 | ### `AddPathOptions` (type) 72 | 73 | The options for adding a PATH variable 74 | 75 | ### `addPath` (function) 76 | 77 | Add a path to the PATH environment variable. 78 | 79 | This function is cross-platforms and works in all the local or CI systems. 80 | 81 | **Parameters:** 82 | 83 | - path (`string`) 84 | - givenOptions (`Partial`) 85 | 86 | **returns:** Promise 87 | 88 | 89 | 90 | ## 🤝 Contributing 91 | 92 | You can sponsor my work here: 93 | 94 | https://github.com/sponsors/aminya 95 | 96 | Pull requests, issues and feature requests are welcome. 97 | See the [Contributing guide](https://github.com/aminya/setup-cpp/blob/master/CONTRIBUTING.md). 98 | -------------------------------------------------------------------------------- /packages/envosman/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { addEnv, addPath, finalizeRC, sourceRC } from "../src/index.js" 2 | 3 | describe("envosman", () => { 4 | it("should be a function", () => { 5 | expect(addEnv).toBeInstanceOf(Function) 6 | expect(addPath).toBeInstanceOf(Function) 7 | expect(finalizeRC).toBeInstanceOf(Function) 8 | expect(sourceRC).toBeInstanceOf(Function) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/envosman/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/envosman/jest.config.mjs: -------------------------------------------------------------------------------- 1 | import jestConfig from "../../jest.config.mjs" 2 | export default jestConfig 3 | -------------------------------------------------------------------------------- /packages/envosman/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "envosman", 3 | "version": "1.0.5", 4 | "description": "Manage environment variables, PATH, and rc files", 5 | "repository": "https://github.com/aminya/setup-cpp", 6 | "homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/envosman", 7 | "license": "Apache-2.0", 8 | "author": "Amin Yahyaabadi", 9 | "main": "./dist/index.js", 10 | "source": "./src/index.ts", 11 | "type": "module", 12 | "scripts": { 13 | "build": "tsc --pretty", 14 | "dev": "tsc --watch --pretty", 15 | "lint.tsc.test": "tsc --noEmit --pretty -p ./__tests__/tsconfig.json", 16 | "lint.tsc": "tsc --noEmit --pretty", 17 | "lint.eslint": "eslint '**/*.{ts,tsx,js,jsx,cjs,mjs,json,yaml}' --no-error-on-unmatched-pattern --cache --cache-location ./.cache/eslint/ --fix", 18 | "prepublishOnly": "pnpm run build", 19 | "test": "jest --coverage" 20 | }, 21 | "dependencies": { 22 | "@actions/core": "^1.10.1", 23 | "@types/node": "22.15.29", 24 | "admina": "^1.0.1", 25 | "ci-info": "^4.0.0", 26 | "escape-path-with-spaces": "^1.0.2", 27 | "escape-quotes": "^1.0.2", 28 | "memoizee": "^0.4.17", 29 | "path-exists": "^5.0.0", 30 | "ci-log": "workspace:*", 31 | "exec-powershell": "workspace:*", 32 | "untildify-user": "workspace:*" 33 | }, 34 | "engines": { 35 | "node": ">=12" 36 | }, 37 | "keywords": [ 38 | "env", 39 | "path", 40 | "dotenv", 41 | "rc", 42 | "addEnv", 43 | "addPath", 44 | "setEnv", 45 | "linux", 46 | "windows", 47 | "unix", 48 | "macos" 49 | ], 50 | "devDependencies": { 51 | "@types/escape-quotes": "~1.0.0", 52 | "@types/memoizee": "0.4.12" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/envosman/src/index.ts: -------------------------------------------------------------------------------- 1 | export { addEnv, AddEnvOptions } from "./add-env.js" 2 | export { addPath, AddPathOptions } from "./add-path.js" 3 | export { finalizeRC, sourceRCInRc as sourceRC } from "./rc-file.js" 4 | -------------------------------------------------------------------------------- /packages/envosman/src/utils.ts: -------------------------------------------------------------------------------- 1 | import escapeSpace from "escape-path-with-spaces" 2 | import escapeQuote from "escape-quotes" 3 | 4 | /** 5 | * Escape a string for use in a shell command 6 | * @param valGiven The string to escape 7 | * @param shouldEscapeSpace Whether to escape spaces in the string 8 | * 9 | * @private 10 | */ 11 | export function escapeString(valGiven: string, shouldEscapeSpace: boolean = false) { 12 | const spaceEscaped = shouldEscapeSpace ? escapeSpace(valGiven) : valGiven 13 | return escapeQuote(spaceEscaped, "\"", "\\") 14 | } 15 | -------------------------------------------------------------------------------- /packages/envosman/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "noEmit": false, 6 | "allowImportingTsExtensions": false 7 | }, 8 | "include": ["./src"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/exec-powershell/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/exec-powershell/README.md: -------------------------------------------------------------------------------- 1 |

exec-powershell

2 |

3 | 4 | Version 5 | 6 | 7 | License: Apache--2.0 8 | 9 |

10 | 11 | > Run a powershell command. 12 | 13 | ## Install 14 | 15 | ```sh 16 | npm install --save exec-powershell 17 | ``` 18 | 19 | ## Usage 20 | 21 | 22 | 23 | ### `execPowershell` (function) 24 | 25 | Asynchronously execute a powershell command. 26 | 27 | **Parameters:** 28 | 29 | - command (`string`) - The powershell command to execute 30 | - startupFlags (`string[]`) - The optional startup flags to be passed to powershell. Defaults to `["-NoProfile", "-NoLogo", "-NonInteractive"]`. This means that the Powershell profile is not sourced first. 31 | - execOptions (`Options`) - The options passed to `execa`. Defaults to `{ stdio: "inherit" }` 32 | 33 | **returns:** ExecaChildProcess 34 | 35 | ### `execPowershellSync` (function) 36 | 37 | Execute a powershell command. 38 | 39 | **Parameters:** 40 | 41 | - command (`string`) - The powershell command to execute 42 | - startupFlags (`string[]`) - The optional startup flags to be passed to powershell. Defaults to `["-NoProfile", "-NoLogo", "-NonInteractive"]`. This means that the Powershell profile is not sourced first. 43 | - execOptions (`SyncOptions`) - The options passed to `execa`. Defaults to `{ stdio: "inherit" }` 44 | 45 | **returns:** ExecaReturnBase 46 | 47 | ### `getPowerShell` (function) 48 | 49 | Get the path to the powershell executable. 50 | 51 | **returns:** string 52 | 53 | 54 | 55 | ## 🤝 Contributing 56 | 57 | You can sponsor my work here: 58 | 59 | https://github.com/sponsors/aminya 60 | 61 | Pull requests, issues and feature requests are welcome. 62 | See the [Contributing guide](https://github.com/aminya/setup-cpp/blob/master/CONTRIBUTING.md). 63 | -------------------------------------------------------------------------------- /packages/exec-powershell/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { execPowershell, execPowershellSync, getPowerShell } from "../src/index.js" 2 | 3 | describe("exec-powershell", () => { 4 | it("should be a function", () => { 5 | expect(execPowershell).toBeInstanceOf(Function) 6 | expect(execPowershellSync).toBeInstanceOf(Function) 7 | expect(getPowerShell).toBeInstanceOf(Function) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/exec-powershell/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/exec-powershell/jest.config.mjs: -------------------------------------------------------------------------------- 1 | import jestConfig from "../../jest.config.mjs" 2 | export default jestConfig 3 | -------------------------------------------------------------------------------- /packages/exec-powershell/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exec-powershell", 3 | "version": "1.0.1", 4 | "description": "Run a powershell command.", 5 | "repository": "https://github.com/aminya/setup-cpp", 6 | "homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/exec-powershell", 7 | "license": "Apache-2.0", 8 | "author": "Amin Yahyaabadi", 9 | "main": "./dist/index.js", 10 | "source": "./src/index.ts", 11 | "type": "module", 12 | "scripts": { 13 | "build": "tsc --pretty", 14 | "dev": "tsc --watch --pretty", 15 | "lint.tsc.test": "tsc --noEmit --pretty -p ./__tests__/tsconfig.json", 16 | "lint.tsc": "tsc --noEmit --pretty", 17 | "lint.eslint": "eslint '**/*.{ts,tsx,js,jsx,cjs,mjs,json,yaml}' --no-error-on-unmatched-pattern --cache --cache-location ./.cache/eslint/ --fix", 18 | "prepublishOnly": "pnpm run build", 19 | "test": "jest --coverage" 20 | }, 21 | "dependencies": { 22 | "execa": "7.2.0", 23 | "which": "^4.0.0", 24 | "@types/node": "^22.0.0" 25 | }, 26 | "devDependencies": { 27 | "@types/which": "^3.0.0" 28 | }, 29 | "keywords": [ 30 | "powershell", 31 | "pwsh", 32 | "exec", 33 | "execa", 34 | "spawn", 35 | "system", 36 | "github-actions", 37 | "github", 38 | "actions", 39 | "gitlab", 40 | "ci" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /packages/exec-powershell/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "noEmit": false, 6 | "allowImportingTsExtensions": false 7 | }, 8 | "include": ["./src"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/setup-alpine/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/setup-alpine/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { hasApk, installApkPack } from "../src/index.js" 2 | 3 | describe("setup-alpine", () => { 4 | it("should be a function", () => { 5 | expect(installApkPack).toBeInstanceOf(Function) 6 | expect(hasApk).toBeInstanceOf(Function) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /packages/setup-alpine/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/setup-alpine/jest.config.mjs: -------------------------------------------------------------------------------- 1 | import jestConfig from "../../jest.config.mjs" 2 | export default jestConfig 3 | -------------------------------------------------------------------------------- /packages/setup-alpine/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "setup-alpine", 3 | "version": "1.1.0", 4 | "description": "Setup apk packages and repositories in Alpine Linux distributions", 5 | "repository": "https://github.com/aminya/setup-cpp", 6 | "homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/setup-alpine", 7 | "license": "Apache-2.0", 8 | "author": "Amin Yahyaabadi", 9 | "main": "./dist/index.js", 10 | "source": "./src/index.ts", 11 | "type": "module", 12 | "files": [ 13 | "dist", 14 | "src", 15 | "tsconfig.json" 16 | ], 17 | "scripts": { 18 | "build": "tsc --pretty", 19 | "dev": "tsc --watch --pretty", 20 | "lint.tsc.test": "tsc --noEmit --pretty -p ./__tests__/tsconfig.json", 21 | "lint.tsc": "tsc --noEmit --pretty", 22 | "lint.eslint": "eslint '**/*.{ts,tsx,js,jsx,cjs,mjs,json,yaml}' --no-error-on-unmatched-pattern --cache --cache-location ./.cache/eslint/ --fix", 23 | "prepublishOnly": "pnpm run build", 24 | "test": "jest --coverage" 25 | }, 26 | "dependencies": { 27 | "@types/node": "22.15.29", 28 | "admina": "^1.0.1", 29 | "path-exists": "^5.0.0", 30 | "ci-log": "workspace:*", 31 | "envosman": "workspace:*", 32 | "which": "4.0.0", 33 | "execa": "7.2.0", 34 | "memoizee": "^0.4.17" 35 | }, 36 | "engines": { 37 | "node": ">=12" 38 | }, 39 | "keywords": [ 40 | "setup", 41 | "apk", 42 | "apk-add", 43 | "repository", 44 | "alpine", 45 | "install", 46 | "setup-apk", 47 | "repositories", 48 | "linux", 49 | "alpine-linux", 50 | "package" 51 | ], 52 | "devDependencies": { 53 | "@types/memoizee": "0.4.12", 54 | "@types/which": "~3.0.4" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/setup-alpine/src/apk-repository.ts: -------------------------------------------------------------------------------- 1 | import { info } from "ci-log" 2 | import { appendFile, readFile } from "fs/promises" 3 | import { pathExists } from "path-exists" 4 | import { hasApk } from "./has-apk.js" 5 | import { updateApkMemoized } from "./update.js" 6 | 7 | /** 8 | * Add an APK repository 9 | * @param repoUrl The URL of the repository to add 10 | * @returns Whether the repository was added successfully 11 | */ 12 | 13 | export async function addApkRepository(repoUrl: string): Promise { 14 | if (!(await hasApk())) { 15 | throw new Error("apk is not available on this system") 16 | } 17 | 18 | try { 19 | // Check if repositories file exists 20 | const reposFile = "/etc/apk/repositories" 21 | if (!(await pathExists(reposFile))) { 22 | throw new Error(`APK repositories file not found at ${reposFile}`) 23 | } 24 | 25 | // Add repository to the file 26 | info(`Adding repository: ${repoUrl}`) 27 | await appendFile(reposFile, `${repoUrl}\n`) 28 | 29 | // Update package index after adding repository 30 | await updateApkMemoized.clear() 31 | await updateApkMemoized() 32 | 33 | info(`Successfully added repository: ${repoUrl}`) 34 | return true 35 | } catch (error) { 36 | throw new Error(`Failed to add repository ${repoUrl}: ${error}`) 37 | } 38 | } 39 | 40 | /** 41 | * Enable the community repository 42 | * @returns Whether the repository was added successfully 43 | */ 44 | export async function enableCommunityRepository() { 45 | const alpineVersion = (await getAlpineVersion()).split(".").slice(0, 2).join(".") 46 | 47 | return addApkRepository(`https://dl-cdn.alpinelinux.org/alpine/v${alpineVersion}/community/`) 48 | } 49 | 50 | /** 51 | * Get the Alpine version 52 | * @returns The Alpine version 53 | */ 54 | export async function getAlpineVersion() { 55 | const releaseFile = "/etc/alpine-release" 56 | if (!(await pathExists(releaseFile))) { 57 | throw new Error(`Alpine release file not found at ${releaseFile}`) 58 | } 59 | return readFile(releaseFile, "utf8") 60 | } 61 | -------------------------------------------------------------------------------- /packages/setup-alpine/src/has-apk.ts: -------------------------------------------------------------------------------- 1 | import memoizee from "memoizee" 2 | import which from "which" 3 | import { isAlpine } from "./is-alpine.js" 4 | 5 | async function hasApk_() { 6 | if (!isAlpine()) { 7 | return false 8 | } 9 | try { 10 | await which("apk") 11 | return true 12 | } catch { 13 | return false 14 | } 15 | } 16 | /** 17 | * Check if apk is available on the system 18 | */ 19 | export const hasApk = memoizee(hasApk_, { promise: true }) 20 | -------------------------------------------------------------------------------- /packages/setup-alpine/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./apk-repository.js" 2 | export * from "./has-apk.js" 3 | export * from "./init-apt.js" 4 | export * from "./install-package.js" 5 | export * from "./is-alpine.js" 6 | export * from "./qualify-install.js" 7 | export * from "./update.js" 8 | -------------------------------------------------------------------------------- /packages/setup-alpine/src/init-apt.ts: -------------------------------------------------------------------------------- 1 | import { defaultExecOptions, execRootSync } from "admina" 2 | import memoize from "memoizee" 3 | import { filterAndQualifyApkPackages } from "./qualify-install.js" 4 | 5 | /** Install bash (usually missing from docker containers) */ 6 | export async function initApk() { 7 | const toInstall = await filterAndQualifyApkPackages([ 8 | { name: "bash" }, 9 | ]) 10 | 11 | if (toInstall.length !== 0) { 12 | execRootSync("apk", ["add", ...toInstall], { 13 | ...defaultExecOptions, 14 | }) 15 | } 16 | } 17 | 18 | /** Install bash (usually missing from docker containers) (memoized) */ 19 | export const initApkMemoized = memoize(initApk, { promise: true }) 20 | -------------------------------------------------------------------------------- /packages/setup-alpine/src/install-package.ts: -------------------------------------------------------------------------------- 1 | import { execRoot } from "admina" 2 | import { info, warning } from "ci-log" 3 | import { hasApk } from "./has-apk.js" 4 | import { initApkMemoized } from "./init-apt.js" 5 | import { type ApkPackage, filterAndQualifyApkPackages, formatPackageWithVersion } from "./qualify-install.js" 6 | import { updateApkMemoized } from "./update.js" 7 | 8 | /** 9 | * The information about an installation result 10 | */ 11 | export type InstallationInfo = { 12 | /** The install dir of the package (Defaults to `undefined`) */ 13 | installDir?: string 14 | /** The bin dir of the package (Defaults to `/usr/bin`) */ 15 | binDir: string 16 | /** The bin path of the package (Defaults to `undefined`) */ 17 | bin?: string 18 | } 19 | 20 | /** 21 | * Install a package using Alpine's apk package manager 22 | * @param packages The packages to install 23 | * @param update Whether to update the package index before installing 24 | * @returns The installation information 25 | */ 26 | export async function installApkPack(packages: ApkPackage[], update = false): Promise { 27 | // Check if apk is available 28 | if (!(await hasApk())) { 29 | throw new Error("apk is not available on this system") 30 | } 31 | 32 | try { 33 | // Update package index if requested 34 | 35 | // init the apk 36 | await initApkMemoized() 37 | 38 | if (update) { 39 | // Force update the repos 40 | await updateApkMemoized.clear() 41 | } 42 | // Update the repos if needed 43 | await updateApkMemoized() 44 | 45 | const packagesToInstall = await filterAndQualifyApkPackages(packages) 46 | 47 | if (packagesToInstall.length === 0) { 48 | info("All packages are already installed") 49 | return { binDir: "/usr/bin" } 50 | } 51 | 52 | // Install the packages 53 | info(`Installing ${packagesToInstall.join(" ")}`) 54 | await execRoot("apk", ["add", ...packagesToInstall]) 55 | 56 | info(`Successfully installed ${packagesToInstall.join(" ")}`) 57 | return { binDir: "/usr/bin" } 58 | } catch (error) { 59 | warning(`Failed to install ${packages.map((pkg) => formatPackageWithVersion(pkg)).join(" ")}: ${error}`) 60 | throw error 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/setup-alpine/src/is-alpine.ts: -------------------------------------------------------------------------------- 1 | import { pathExistsSync } from "path-exists" 2 | 3 | /** 4 | * Check if the current platform is Alpine 5 | */ 6 | export function isAlpine() { 7 | if (process.platform !== "linux") { 8 | return false 9 | } 10 | try { 11 | return pathExistsSync("/etc/alpine-release") 12 | } catch { 13 | return false 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/setup-alpine/src/update.ts: -------------------------------------------------------------------------------- 1 | import { execa } from "execa" 2 | import memoizee from "memoizee" 3 | 4 | async function updateApk() { 5 | await execa("apk", ["update"], { stdio: "inherit" }) 6 | } 7 | export const updateApkMemoized = memoizee(updateApk, { promise: true }) 8 | -------------------------------------------------------------------------------- /packages/setup-alpine/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "noEmit": false, 6 | "allowImportingTsExtensions": false 7 | }, 8 | "include": ["./src"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/setup-apt/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/setup-apt/__tests__/apt-fast.test.ts: -------------------------------------------------------------------------------- 1 | import { hasAptGet, setupAptFast } from "../src/index.js" 2 | import { testBin } from "./testBin.js" 3 | 4 | jest.setTimeout(300000) 5 | describe("setup-apt-fast", () => { 6 | if (!hasAptGet()) { 7 | test.skip("should setup apt-fast", () => {}) 8 | return 9 | } 10 | it("should setup apt-fast", async () => { 11 | const installInfo = await setupAptFast() 12 | await testBin("apt-fast", null, installInfo?.binDir) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /packages/setup-apt/__tests__/nala.test.ts: -------------------------------------------------------------------------------- 1 | import { execRootSync } from "admina" 2 | import { hasAptGet } from "../src/get-apt.js" 3 | import { setupNala } from "../src/nala.js" 4 | import { testBin } from "./testBin.js" 5 | 6 | jest.setTimeout(300000) 7 | describe("setup-nala", () => { 8 | if (!hasAptGet()) { 9 | test.skip("should setup nala", () => {}) 10 | return 11 | } 12 | it("should setup nala", async () => { 13 | const installInfo = await setupNala() 14 | await testBin("nala", ["--version"], installInfo?.binDir) 15 | }) 16 | 17 | afterAll(() => { 18 | // remove nala to run the rest of the tests with apt-get 19 | execRootSync("apt-get", ["remove", "-y", "nala"]) 20 | 21 | try { 22 | execRootSync("apt-get", ["remove", "-y", "nala-legacy"]) 23 | } catch (err) { 24 | // ignore 25 | console.error(err) 26 | } 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /packages/setup-apt/__tests__/testBin.ts: -------------------------------------------------------------------------------- 1 | import spawn from "cross-spawn" 2 | import { pathExists } from "path-exists" 3 | import { addExeExt, join } from "patha" 4 | import which from "which" 5 | 6 | export async function testBin( 7 | name: string, 8 | args: string[] | null = ["--version"], 9 | binDir: string | undefined = undefined, 10 | ) { 11 | let bin = name 12 | if (typeof binDir === "string") { 13 | console.log(`Testing the existence of ${binDir}`) 14 | expect(binDir).toBeDefined() 15 | expect(binDir).not.toHaveLength(0) 16 | expect(await pathExists(binDir)).toBeTruthy() 17 | bin = join(binDir, addExeExt(name)) 18 | } 19 | 20 | if (args !== null) { 21 | console.log(`Running ${bin} ${args.join(" ")}`) 22 | const { status } = spawn.sync(bin, args, { stdio: "inherit" }) 23 | expect(status).toBe(0) 24 | } 25 | 26 | expect((await which(name, { nothrow: true }))?.includes(bin)) 27 | } 28 | -------------------------------------------------------------------------------- /packages/setup-apt/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/setup-apt/jest.config.mjs: -------------------------------------------------------------------------------- 1 | import jestConfig from "../../jest.config.mjs" 2 | export default jestConfig 3 | -------------------------------------------------------------------------------- /packages/setup-apt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "setup-apt", 3 | "version": "3.1.1", 4 | "description": "Setup apt packages and repositories in Debian/Ubuntu-based distributions", 5 | "repository": "https://github.com/aminya/setup-cpp", 6 | "homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/setup-apt", 7 | "license": "Apache-2.0", 8 | "author": "Amin Yahyaabadi", 9 | "main": "./dist/index.js", 10 | "source": "./src/index.ts", 11 | "type": "module", 12 | "files": [ 13 | "dist", 14 | "src", 15 | "tsconfig.json" 16 | ], 17 | "scripts": { 18 | "build": "tsc --pretty", 19 | "dev": "tsc --watch --pretty", 20 | "lint.tsc.test": "tsc --noEmit --pretty -p ./__tests__/tsconfig.json", 21 | "lint.tsc": "tsc --noEmit --pretty", 22 | "lint.eslint": "eslint '**/*.{ts,tsx,js,jsx,cjs,mjs,json,yaml}' --no-error-on-unmatched-pattern --cache --cache-location ./.cache/eslint/ --fix", 23 | "prepublishOnly": "pnpm run build", 24 | "test": "jest --coverage --runInBand" 25 | }, 26 | "dependencies": { 27 | "@types/node": "22.15.29", 28 | "admina": "^1.0.1", 29 | "ci-info": "^4.0.0", 30 | "ci-log": "workspace:*", 31 | "envosman": "workspace:*", 32 | "escape-string-regexp": "^5.0.0", 33 | "execa": "7.2.0", 34 | "memoizee": "^0.4.17", 35 | "node-downloader-helper": "2.1.9", 36 | "path-exists": "^5.0.0", 37 | "which": "4.0.0" 38 | }, 39 | "engines": { 40 | "node": ">=12" 41 | }, 42 | "keywords": [ 43 | "setup", 44 | "apt", 45 | "apt-get", 46 | "repository", 47 | "add-apt-repository", 48 | "apt-cache", 49 | "aptitude", 50 | "install", 51 | "setup-apt", 52 | "repositories", 53 | "linux", 54 | "ubuntu", 55 | "debian", 56 | "package", 57 | "apt-key", 58 | "apt-fast", 59 | "nala" 60 | ], 61 | "devDependencies": { 62 | "@types/memoizee": "0.4.12", 63 | "@types/which": "~3.0.4", 64 | "cross-spawn": "^7.0.6", 65 | "patha": "^0.4.1" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/setup-apt/src/alternatives.ts: -------------------------------------------------------------------------------- 1 | import { promises } from "fs" 2 | import { execRoot } from "admina" 3 | import ciInfo from "ci-info" 4 | const { GITHUB_ACTIONS } = ciInfo 5 | import { sourceRC } from "envosman" 6 | import type { RcOptions } from "envosman/dist/rc-file.js" 7 | const { appendFile } = promises 8 | 9 | /** 10 | * Update the alternatives for a package 11 | * @param name The name of the package 12 | * @param path The path to the binary 13 | * @param priority The priority of the alternative (Defaults to `40`) 14 | */ 15 | export async function updateAptAlternatives(name: string, path: string, priority: number = 40) { 16 | await execRoot("update-alternatives", ["--install", `/usr/bin/${name}`, name, path, priority.toString()]) 17 | } 18 | 19 | /** 20 | * Add the update-alternatives command to the rc file 21 | * @param name The name of the package 22 | * @param path The path to the binary 23 | * @param rcOptions The options for the rc file to add the update-alternatives command to 24 | * @param priority The priority of the alternative (Defaults to `40`) 25 | */ 26 | export async function addUpdateAlternativesToRc( 27 | name: string, 28 | path: string, 29 | rcOptions: RcOptions, 30 | priority: number = 40, 31 | ) { 32 | if (GITHUB_ACTIONS) { 33 | await updateAptAlternatives(name, path, priority) 34 | } else { 35 | await sourceRC(rcOptions) 36 | await appendFile( 37 | rcOptions.rcPath, 38 | `\nif [ $UID -eq 0 ]; then update-alternatives --install /usr/bin/${name} ${name} ${path} ${priority}; fi\n`, 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/setup-apt/src/apt-env.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the environment variables to use for the apt command 3 | * @param apt The apt command to use 4 | * @private Used internally 5 | */ 6 | 7 | export function getAptEnv(apt: string) { 8 | const env: NodeJS.ProcessEnv = { ...process.env, DEBIAN_FRONTEND: "noninteractive" } 9 | 10 | if (apt === "nala") { 11 | // if LANG/LC_ALL is not set, enable utf8 otherwise nala fails because of ASCII encoding 12 | if (env.LANG === undefined) { 13 | env.LANG = "C.UTF-8" 14 | } 15 | if (env.LC_ALL === undefined) { 16 | env.LC_ALL = "C.UTF-8" 17 | } 18 | } 19 | 20 | return env 21 | } 22 | -------------------------------------------------------------------------------- /packages/setup-apt/src/apt-repository.ts: -------------------------------------------------------------------------------- 1 | import { defaultExecOptions, execRootSync } from "admina" 2 | import memoize from "memoizee" 3 | import { getAptEnv } from "./apt-env.js" 4 | import { aptTimeout } from "./apt-timeout.js" 5 | import { getApt } from "./get-apt.js" 6 | import { initAptMemoized } from "./init-apt.js" 7 | import { isAptPackInstalled } from "./is-installed.js" 8 | import { updateAptReposMemoized } from "./update.js" 9 | 10 | function hasNoUpdateFlag_(apt: string) { 11 | const { stdout } = execRootSync("add-apt-repository", ["--help"], { 12 | ...defaultExecOptions, 13 | env: getAptEnv(apt), 14 | stdio: "pipe", 15 | }) 16 | return stdout.includes("--no-update") 17 | } 18 | const hasNoUpdateFlag = memoize(hasNoUpdateFlag_) 19 | 20 | export async function addAptRepository(repo: string, apt = getApt()) { 21 | await initAptMemoized(apt) 22 | await installAddAptRepo() 23 | execRootSync( 24 | "add-apt-repository", 25 | ["-y", hasNoUpdateFlag(apt) ? "--no-update" : undefined, repo].filter(a => a !== undefined), 26 | { ...defaultExecOptions, env: getAptEnv(apt) }, 27 | ) 28 | 29 | // Update the repos 30 | updateAptReposMemoized.clear() // ensure update is called 31 | updateAptReposMemoized(apt) 32 | } 33 | 34 | export async function installAddAptRepo() { 35 | if (await isAptPackInstalled("software-properties-common")) { 36 | return 37 | } 38 | const apt = "apt-get" 39 | execRootSync( 40 | apt, 41 | ["install", "-y", "--fix-broken", "-o", aptTimeout, "software-properties-common"], 42 | { ...defaultExecOptions, env: getAptEnv(apt) }, 43 | ) 44 | } 45 | 46 | export async function removeAptRepository(repo: string, apt = getApt()) { 47 | await initAptMemoized(apt) 48 | await installAddAptRepo() 49 | execRootSync( 50 | "add-apt-repository", 51 | ["-y", "--remove", hasNoUpdateFlag(apt) ? undefined : "--no-update", repo].filter(a => a !== undefined), 52 | { ...defaultExecOptions, env: getAptEnv(apt) }, 53 | ) 54 | 55 | // Update the repos 56 | updateAptReposMemoized.clear() // ensure update is called 57 | updateAptReposMemoized(apt) 58 | } 59 | -------------------------------------------------------------------------------- /packages/setup-apt/src/apt-timeout.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The timeout to use for apt commands 3 | * Wait up to 300 seconds if the apt-get lock is held 4 | * @private Used internally 5 | */ 6 | 7 | export const aptTimeout = "Dpkg::Lock::Timeout=300" 8 | -------------------------------------------------------------------------------- /packages/setup-apt/src/get-apt.ts: -------------------------------------------------------------------------------- 1 | import which from "which" 2 | 3 | /** 4 | * Check if nala is installed 5 | */ 6 | export function hasNala() { 7 | return which.sync("nala", { nothrow: true }) !== null 8 | } 9 | 10 | /** 11 | * Check if apt-fast is installed 12 | */ 13 | export function hasAptFast() { 14 | return which.sync("apt-fast", { nothrow: true }) !== null 15 | } 16 | 17 | /** 18 | * Check if apt is installed 19 | */ 20 | export function hasApt() { 21 | return which.sync("apt", { nothrow: true }) !== null 22 | } 23 | 24 | /** 25 | * Check if apt-get is installed 26 | */ 27 | export function hasAptGet() { 28 | return which.sync("apt-get", { nothrow: true }) !== null 29 | } 30 | 31 | /** 32 | * Get the apt command to use 33 | * If nala is installed, use that, otherwise use apt-get 34 | */ 35 | export function getApt() { 36 | if (hasNala()) { 37 | return "nala" 38 | } else if (hasAptFast()) { 39 | return "apt-fast" 40 | // } else if (hasApt()) { 41 | // return "apt" 42 | } else if (hasAptGet()) { 43 | return "apt-get" 44 | } else { 45 | throw new Error("No apt command found") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/setup-apt/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./alternatives.js" 2 | export * from "./apt-fast.js" 3 | export * from "./apt-key.js" 4 | export * from "./apt-repository.js" 5 | export * from "./apt-timeout.js" 6 | export * from "./get-apt.js" 7 | export * from "./init-apt.js" 8 | export * from "./install.js" 9 | export * from "./is-installed.js" 10 | export * from "./nala.js" 11 | export * from "./qualify-install.js" 12 | export * from "./update.js" 13 | -------------------------------------------------------------------------------- /packages/setup-apt/src/init-apt.ts: -------------------------------------------------------------------------------- 1 | import { defaultExecOptions, execRootSync } from "admina" 2 | import memoize from "memoizee" 3 | import { getAptEnv } from "./apt-env.js" 4 | import { aptTimeout } from "./apt-timeout.js" 5 | import { filterAndQualifyAptPackages } from "./qualify-install.js" 6 | import { updateAptReposMemoized } from "./update.js" 7 | 8 | /** Install gnupg and certificates (usually missing from docker containers) */ 9 | export async function initApt(apt: string) { 10 | // Update the repos 11 | updateAptReposMemoized(apt) 12 | 13 | const toInstall = await filterAndQualifyAptPackages([ 14 | { name: "ca-certificates" }, 15 | { name: "gnupg" }, 16 | { name: "apt-utils" }, 17 | ], apt) 18 | 19 | if (toInstall.length !== 0) { 20 | execRootSync(apt, ["install", "-y", "--fix-broken", "-o", aptTimeout, ...toInstall], { 21 | ...defaultExecOptions, 22 | env: getAptEnv(apt), 23 | }) 24 | } 25 | } 26 | 27 | /** Install gnupg and certificates (usually missing from docker containers) (memoized) */ 28 | export const initAptMemoized = memoize(initApt, { promise: true }) 29 | -------------------------------------------------------------------------------- /packages/setup-apt/src/is-installed.ts: -------------------------------------------------------------------------------- 1 | import { execa } from "execa" 2 | import { getAptEnv } from "./apt-env.js" 3 | 4 | /** 5 | * Check if a package is installed 6 | * @param pack The package to check 7 | * @returns `true` if the package is installed, `false` otherwise 8 | */ 9 | export async function isAptPackInstalled(pack: string) { 10 | try { 11 | // check if a package is installed 12 | const { stdout } = await execa("dpkg", ["-s", pack], { env: getAptEnv("apt-get"), stdio: "pipe" }) 13 | if (typeof stdout !== "string") { 14 | return false 15 | } 16 | const lines = stdout.split("\n") 17 | // check if the output contains a line that starts with "Status: install ok installed" 18 | return lines.some((line) => line.startsWith("Status: install ok installed")) 19 | } catch { 20 | return false 21 | } 22 | } 23 | 24 | /** 25 | * Check if a package matching a regexp is installed 26 | * @param regexp The regexp to check 27 | * @returns `true` if a package is installed, `false` otherwise 28 | */ 29 | export async function isAptPackRegexInstalled(regexp: string) { 30 | try { 31 | // check if a package matching the regexp is installed 32 | const { stdout } = await execa("dpkg", ["-l", regexp], { env: getAptEnv("apt-get"), stdio: "pipe" }) 33 | if (typeof stdout !== "string") { 34 | return false 35 | } 36 | const lines = stdout.split("\n") 37 | // check if the output contains any lines that start with "ii" 38 | return lines.some((line) => line.startsWith("ii")) 39 | } catch { 40 | return false 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/setup-apt/src/update.ts: -------------------------------------------------------------------------------- 1 | import { defaultExecOptions, execRootSync } from "admina" 2 | import memoize from "memoizee" 3 | import { getAptEnv } from "./apt-env.js" 4 | import { aptTimeout } from "./apt-timeout.js" 5 | import { getApt } from "./get-apt.js" 6 | 7 | export let updatedRepos = false // eslint-disable-line import/no-mutable-exports 8 | 9 | /** 10 | * Update the apt repositories 11 | * @param apt The apt command to use (optional) 12 | */ 13 | export function updateAptRepos(apt: string = getApt()) { 14 | execRootSync( 15 | apt, 16 | ["update", "-o", aptTimeout], 17 | { ...defaultExecOptions, env: getAptEnv(apt) }, 18 | ) 19 | 20 | updatedRepos = true 21 | } 22 | 23 | /** 24 | * Update the apt repositories (memoized) 25 | * @param apt The apt command to use (optional) 26 | */ 27 | export const updateAptReposMemoized = memoize(updateAptRepos) 28 | -------------------------------------------------------------------------------- /packages/setup-apt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "noEmit": false, 6 | "allowImportingTsExtensions": false 7 | }, 8 | "include": ["./src"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/setup-brew/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/setup-brew/README.md: -------------------------------------------------------------------------------- 1 |

setup-brew

2 |

3 | 4 | Version 5 | 6 | 7 | 8 | License: Apache--2.0 9 | 10 |

11 | 12 | > Setup brew and brew packages 13 | 14 | ## Install 15 | 16 | ```sh 17 | npm install --save setup-brew 18 | ``` 19 | 20 | ## Usage 21 | 22 | 23 | 24 | ### `InstallationInfo` (type) 25 | 26 | The information about an installation result 27 | 28 | ### `SetupBrewOptions` (type) 29 | 30 | ### `setupBrew` (function) 31 | 32 | **Parameters:** 33 | 34 | - options (`SetupBrewOptions`) 35 | 36 | **returns:** Promise 37 | 38 | ### `getBrewBinDir` (function) 39 | 40 | Get the path to the bin directory of brew 41 | 42 | **returns:** string 43 | 44 | ### `getBrewDir` (function) 45 | 46 | Get the path where brew is installed 47 | 48 | **returns:** "/opt/homebrew" | "/usr/local" | "/home/linuxbrew/.linuxbrew" 49 | 50 | ### `BrewPackOptions` (type) 51 | 52 | ### `installBrewPack` (function) 53 | 54 | A function that installs a package using brew 55 | 56 | **Parameters:** 57 | 58 | - name (`string`) - The name of the package 59 | - version (`string`) - The version of the package (optional) 60 | - options - The options for installing the package 61 | - givenOptions (`BrewPackOptions`) 62 | 63 | **returns:** Promise 64 | 65 | 66 | 67 | ## 🤝 Contributing 68 | 69 | You can sponsor my work here: 70 | 71 | https://github.com/sponsors/aminya 72 | 73 | Pull requests, issues and feature requests are welcome. 74 | See the [Contributing guide](https://github.com/aminya/setup-cpp/blob/master/CONTRIBUTING.md). 75 | -------------------------------------------------------------------------------- /packages/setup-brew/__tests__/brew.test.ts: -------------------------------------------------------------------------------- 1 | import { join } from "path" 2 | import spawn from "cross-spawn" 3 | import { pathExists } from "path-exists" 4 | import { addExeExt } from "patha" 5 | import which from "which" 6 | import { setupBrew } from "../src/index.js" 7 | 8 | jest.setTimeout(300000) 9 | describe("setup-brew", () => { 10 | if ( 11 | process.platform === "win32" 12 | || process.platform === "linux" && process.arch !== "x64" 13 | ) { 14 | it.skip("should setup brew", () => {}) 15 | return 16 | } 17 | it("should setup brew", async () => { 18 | const installInfo = await setupBrew() 19 | await testBin("brew", ["--version"], installInfo?.binDir) 20 | }) 21 | }) 22 | 23 | async function testBin( 24 | name: string, 25 | args: string[] | null = ["--version"], 26 | binDir: string | undefined = undefined, 27 | ) { 28 | try { 29 | let bin = name 30 | if (typeof binDir === "string") { 31 | console.log(`Testing the existence of ${binDir}`) 32 | expect(binDir).toBeDefined() 33 | expect(binDir).not.toHaveLength(0) 34 | expect(await pathExists(binDir)).toBeTruthy() 35 | bin = join(binDir, addExeExt(name)) 36 | } 37 | 38 | if (args !== null) { 39 | console.log(`Running ${bin} ${args.join(" ")}`) 40 | const { status } = spawn.sync(bin, args, { stdio: "inherit" }) 41 | expect(status).toBe(0) 42 | } 43 | 44 | expect((await which(name, { nothrow: true }))?.includes(bin)) 45 | } catch (err) { 46 | throw new Error(`Failed to test bin ${name}: ${err}`) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/setup-brew/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/setup-brew/jest.config.mjs: -------------------------------------------------------------------------------- 1 | import jestConfig from "../../jest.config.mjs" 2 | export default jestConfig 3 | -------------------------------------------------------------------------------- /packages/setup-brew/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "setup-brew", 3 | "version": "1.1.1", 4 | "description": "Setup brew and brew packages", 5 | "repository": "https://github.com/aminya/setup-cpp", 6 | "homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/setup-brew", 7 | "license": "Apache-2.0", 8 | "author": "Amin Yahyaabadi", 9 | "main": "./dist/index.js", 10 | "source": "./src/index.ts", 11 | "type": "module", 12 | "files": [ 13 | "dist", 14 | "src", 15 | "tsconfig.json" 16 | ], 17 | "scripts": { 18 | "build": "tsc --pretty", 19 | "dev": "tsc --watch --pretty", 20 | "lint.tsc.test": "tsc --noEmit --pretty -p ./__tests__/tsconfig.json", 21 | "lint.tsc": "tsc --noEmit --pretty", 22 | "lint.eslint": "eslint '**/*.{ts,tsx,js,jsx,cjs,mjs,json,yaml}' --no-error-on-unmatched-pattern --cache --cache-location ./.cache/eslint/ --fix", 23 | "prepublishOnly": "pnpm run build", 24 | "test": "jest --coverage" 25 | }, 26 | "dependencies": { 27 | "@types/node": "22.15.29", 28 | "ci-log": "workspace:*", 29 | "envosman": "workspace:*", 30 | "which": "4.0.0", 31 | "execa": "7.2.0", 32 | "setup-apt": "workspace:*", 33 | "node-downloader-helper": "2.1.9" 34 | }, 35 | "devDependencies": { 36 | "@types/cross-spawn": "~6.0.6", 37 | "@types/which": "~3.0.4", 38 | "cross-spawn": "7.0.6", 39 | "path-exists": "5.0.0", 40 | "patha": "0.4.1" 41 | }, 42 | "engines": { 43 | "node": ">=12" 44 | }, 45 | "keywords": [ 46 | "setup", 47 | "brew", 48 | "install", 49 | "brew-install", 50 | "cask", 51 | "formula", 52 | "package", 53 | "setup-brew", 54 | "repositories", 55 | "macos", 56 | "homebrew", 57 | "linuxbrew", 58 | "osx", 59 | "linux", 60 | "ubuntu" 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /packages/setup-brew/src/InstallationInfo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The information about an installation result 3 | */ 4 | 5 | export type InstallationInfo = { 6 | /** The install dir of the package (Defaults to `undefined`) */ 7 | installDir?: string 8 | /** The bin dir of the package (Defaults to `/usr/bin`) */ 9 | binDir: string 10 | /** The bin path of the package (Defaults to `undefined`) */ 11 | bin?: string 12 | } 13 | -------------------------------------------------------------------------------- /packages/setup-brew/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./install-pack.js" 2 | export * from "./install.js" 3 | -------------------------------------------------------------------------------- /packages/setup-brew/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "noEmit": false, 6 | "allowImportingTsExtensions": false 7 | }, 8 | "include": ["./src"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/untildify-user/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/untildify-user/README.md: -------------------------------------------------------------------------------- 1 |

untildify-user

2 |

3 | 4 | Version 5 | 6 | 7 | License: Apache--2.0 8 | 9 |

10 | 11 | > Untildify a path for the current user even if it is root 12 | 13 | ## Install 14 | 15 | ```sh 16 | npm install --save untildify-user 17 | ``` 18 | 19 | ## Usage 20 | 21 | 22 | 23 | ### `userHomeDir` (function) 24 | 25 | **returns:** string 26 | 27 | ### `untildifyUser` (function) 28 | 29 | Replaces a tilde with the user's home directory 30 | 31 | **Parameters:** 32 | 33 | - path (`string`) - The path to untildify 34 | 35 | **returns:** string 36 | 37 | ```tsx 38 | UntildifyUser("~/foo") // /home/user/foo 39 | ``` 40 | 41 | 42 | 43 | ## 🤝 Contributing 44 | 45 | You can sponsor my work here: 46 | 47 | https://github.com/sponsors/aminya 48 | 49 | Pull requests, issues and feature requests are welcome. 50 | See the [Contributing guide](https://github.com/aminya/setup-cpp/blob/master/CONTRIBUTING.md). 51 | -------------------------------------------------------------------------------- /packages/untildify-user/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { untildifyUser } from "../src/index.js" 2 | 3 | describe("untildify-user", () => { 4 | it("should be a function", () => { 5 | expect(untildifyUser).toBeInstanceOf(Function) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/untildify-user/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/untildify-user/jest.config.mjs: -------------------------------------------------------------------------------- 1 | import jestConfig from "../../jest.config.mjs" 2 | export default jestConfig 3 | -------------------------------------------------------------------------------- /packages/untildify-user/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "untildify-user", 3 | "version": "1.0.1", 4 | "description": "Untildify a path for the current user even if it is root", 5 | "repository": "https://github.com/aminya/setup-cpp", 6 | "homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/untildify-user", 7 | "license": "Apache-2.0", 8 | "author": "Amin Yahyaabadi", 9 | "main": "./dist/index.js", 10 | "source": "./src/index.ts", 11 | "type": "module", 12 | "scripts": { 13 | "build": "tsc --pretty", 14 | "dev": "tsc --watch --pretty", 15 | "lint.tsc.test": "tsc --noEmit --pretty -p ./__tests__/tsconfig.json", 16 | "lint.tsc": "tsc --noEmit --pretty", 17 | "lint.eslint": "eslint '**/*.{ts,tsx,js,jsx,cjs,mjs,json,yaml}' --no-error-on-unmatched-pattern --cache --cache-location ./.cache/eslint/ --fix", 18 | "prepublishOnly": "pnpm run build", 19 | "test": "jest --coverage" 20 | }, 21 | "dependencies": { 22 | "admina": "1.0.1" 23 | }, 24 | "keywords": [ 25 | "tilde", 26 | "expansion", 27 | "expand", 28 | "untildify", 29 | "path", 30 | "home", 31 | "directory", 32 | "user", 33 | "shell", 34 | "bash" 35 | ], 36 | "devDependencies": {} 37 | } 38 | -------------------------------------------------------------------------------- /packages/untildify-user/src/index.ts: -------------------------------------------------------------------------------- 1 | import { homedir } from "os" 2 | import { join } from "path" 3 | import { isSudo } from "admina" 4 | 5 | export function userHomeDir() { 6 | if (isSudo() && typeof process.env.SUDO_USER === "string" && process.env.SUDO_USER !== "") { 7 | // use the user profile even if root 8 | if (process.platform === "darwin") { 9 | return join("/Users/", process.env.SUDO_USER) 10 | } else { 11 | return join("/home/", process.env.SUDO_USER) 12 | } 13 | } else { 14 | const maybeHomeDir = homedir() 15 | if (maybeHomeDir === "") { 16 | return undefined 17 | } 18 | return maybeHomeDir 19 | } 20 | } 21 | 22 | const tildeRegex = /^~(?=$|\/|\\)/ 23 | 24 | /** 25 | * Replaces a tilde with the user's home directory 26 | * 27 | * @example UntildifyUser("~/foo") // /home/user/foo 28 | * 29 | * @param path The path to untildify 30 | * @returns The untildified path 31 | */ 32 | export function untildifyUser(path: string) { 33 | const maybeHomeDir = userHomeDir() 34 | if (maybeHomeDir === undefined) { 35 | return path 36 | } 37 | 38 | return path.replace(tildeRegex, maybeHomeDir) 39 | } 40 | -------------------------------------------------------------------------------- /packages/untildify-user/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "noEmit": false, 6 | "allowImportingTsExtensions": false 7 | }, 8 | "include": ["./src"] 9 | } 10 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* 3 | 4 | onlyBuiltDependencies: 5 | - "@biomejs/biome" 6 | - "@swc/core" 7 | - core-js 8 | - dprint 9 | - es5-ext 10 | - esbuild 11 | - iso-constants 12 | - lefthook 13 | -------------------------------------------------------------------------------- /prettier.config.mjs: -------------------------------------------------------------------------------- 1 | import config from "prettier-config-atomic" 2 | export default config 3 | -------------------------------------------------------------------------------- /src/__tests__/lib.test.ts: -------------------------------------------------------------------------------- 1 | import packageJson from "../../package-version.json" 2 | import { setupCpp } from "../lib.js" 3 | 4 | describe("setupCpp", () => { 5 | it("should install nothing if no tools are installed", async () => { 6 | const opts = { 7 | "setup-cpp": false, 8 | } 9 | const result = await setupCpp(opts) 10 | expect(result).toStrictEqual({ 11 | successMessages: [], 12 | errorMessages: [], 13 | }) 14 | }) 15 | 16 | it("should install setup-cpp if no tools are installed", async () => { 17 | const result = await setupCpp() 18 | 19 | const version = packageJson.version 20 | 21 | expect(result).toStrictEqual({ 22 | successMessages: [`setup-cpp@${version} already installed`], 23 | errorMessages: [], 24 | }) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /src/asset-list.ts: -------------------------------------------------------------------------------- 1 | import { basename, dirname } from "path" 2 | import { execa } from "execa" 3 | import glob from "fast-glob" 4 | 5 | async function main() { 6 | // create a github token from gh 7 | const token = await execa("gh", ["auth", "token"]) 8 | process.env.GITHUB_TOKEN = token.stdout 9 | 10 | const files = await glob("src/*/assets-list.ts", { 11 | onlyFiles: true, 12 | absolute: true, 13 | ignore: ["node_modules"], 14 | }) 15 | 16 | await Promise.all(files.map(async (file) => { 17 | console.log(`Updating ${basename(dirname(file))}`) 18 | 19 | const jsFile = file.replace(".ts", ".js") 20 | await import(jsFile) 21 | })) 22 | } 23 | 24 | main().catch((err) => { 25 | console.error(err) 26 | process.exit(1) 27 | }) 28 | -------------------------------------------------------------------------------- /src/bazel/__tests__/bazel.test.ts: -------------------------------------------------------------------------------- 1 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { setupBazel } from "../bazel.js" 4 | 5 | jest.setTimeout(300000) 6 | describe("setup-bazel", () => { 7 | if (process.platform === "linux" && process.arch === "arm64") { 8 | it("should skip bazel tests on Linux arm64", () => { 9 | expect(true).toBe(true) 10 | }) 11 | return 12 | } 13 | it("should setup bazel", async () => { 14 | const installInfo = await setupBazel("", "", process.arch) 15 | 16 | await testBin("bazel", ["--version"], (installInfo as InstallationInfo | undefined)?.binDir) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /src/bazel/bazel.ts: -------------------------------------------------------------------------------- 1 | import { execRoot } from "admina" 2 | import { hasApk, installApkPack } from "setup-alpine" 3 | import { addAptKeyViaURL, hasAptGet, installAptPack } from "setup-apt" 4 | import { installBrewPack } from "setup-brew" 5 | import { getDebArch } from "../utils/env/arch.js" 6 | import { hasDnf } from "../utils/env/hasDnf.js" 7 | import { isArch } from "../utils/env/isArch.js" 8 | import { setupChocoPack } from "../utils/setup/setupChocoPack.js" 9 | import { setupDnfPack } from "../utils/setup/setupDnfPack.js" 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 12 | export async function setupBazel(version: string, _setupDir: string, _arch: string) { 13 | switch (process.platform) { 14 | case "win32": { 15 | // install bazelisk because it contains both 16 | return setupChocoPack("bazelisk", version) 17 | } 18 | case "darwin": { 19 | // install bazelisk because it contains both 20 | return installBrewPack("bazelisk", version) 21 | } 22 | case "linux": { 23 | if (isArch()) { 24 | throw new Error("installing bazel on Arch linux is not supported yet") 25 | } else if (hasDnf()) { 26 | // https://bazel.build/install/redhat 27 | await setupDnfPack([{ name: "dnf-plugins-core" }]) 28 | await execRoot("dnf", ["copr", "enable", "vbatts/bazel"]) 29 | return setupDnfPack([{ name: "bazel4" }]) 30 | } else if (hasAptGet()) { 31 | // https://bazel.build/install/ubuntu 32 | const keyFileName = await addAptKeyViaURL({ 33 | fileName: "bazel-archive-keyring.gpg", 34 | keyUrl: "https://bazel.build/bazel-release.pub.gpg", 35 | }) 36 | await execRoot("bash", [ 37 | "-c", 38 | `echo "deb [arch=${ 39 | getDebArch(process.arch) 40 | } signed-by=${keyFileName}] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list`, 41 | ]) 42 | return installAptPack([{ name: "bazel", version }], true) 43 | } else if (await hasApk()) { 44 | return installApkPack([{ name: "bazel", version }], true) 45 | } 46 | throw new Error("Unsupported linux distribution") 47 | } 48 | default: { 49 | throw new Error("Unsupported platform") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/ccache/__tests__/ccache.test.ts: -------------------------------------------------------------------------------- 1 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { setupCcache } from "../ccache.js" 4 | 5 | jest.setTimeout(300000) 6 | describe("setup-ccache", () => { 7 | it("should setup ccache", async () => { 8 | const installInfo = await setupCcache("", "", process.arch) 9 | 10 | await testBin("ccache", ["--version"], (installInfo as InstallationInfo | undefined)?.binDir) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/ccache/ccache.ts: -------------------------------------------------------------------------------- 1 | import { hasApk, installApkPack } from "setup-alpine" 2 | import { hasAptGet, installAptPack } from "setup-apt" 3 | import { installBrewPack } from "setup-brew" 4 | import { hasDnf } from "../utils/env/hasDnf.js" 5 | import { isArch } from "../utils/env/isArch.js" 6 | import { setupChocoPack } from "../utils/setup/setupChocoPack.js" 7 | import { setupDnfPack } from "../utils/setup/setupDnfPack.js" 8 | import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js" 9 | 10 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 11 | export async function setupCcache(version: string, _setupDir: string, _arch: string) { 12 | switch (process.platform) { 13 | case "win32": { 14 | return setupChocoPack("ccache", version) 15 | } 16 | case "darwin": { 17 | return installBrewPack("ccache", version) 18 | } 19 | case "linux": { 20 | if (isArch()) { 21 | return setupPacmanPack("ccache", version) 22 | } else if (hasDnf()) { 23 | return setupDnfPack([{ name: "ccache", version }]) 24 | } else if (hasAptGet()) { 25 | return installAptPack([{ name: "ccache", version }]) 26 | } else if (await hasApk()) { 27 | return installApkPack([{ name: "ccache", version }]) 28 | } 29 | throw new Error("Unsupported linux distribution") 30 | } 31 | default: { 32 | throw new Error("Unsupported platform") 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/chocolatey/__tests__/chocolatey.test.ts: -------------------------------------------------------------------------------- 1 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { setupChocolatey } from "../chocolatey.js" 4 | 5 | jest.setTimeout(300000) 6 | describe("setup-chocolatey", () => { 7 | if (process.platform !== "win32") { 8 | it.skip("should setup chocolatey", () => {}) 9 | return 10 | } 11 | it("should setup chocolatey", async () => { 12 | const { binDir } = (await setupChocolatey("", "", process.arch)) as InstallationInfo 13 | await testBin("choco", ["--version"], binDir) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /src/chocolatey/chocolatey.ts: -------------------------------------------------------------------------------- 1 | import { dirname } from "path" 2 | import { addPath } from "envosman" 3 | import { execaSync } from "execa" 4 | import { pathExists } from "path-exists" 5 | import which from "which" 6 | import { rcOptions } from "../options.js" 7 | import type { InstallationInfo } from "../utils/setup/setupBin.js" 8 | 9 | /* eslint-disable require-atomic-updates */ 10 | let binDir: string | undefined 11 | 12 | export async function setupChocolatey( 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | _version: string, 15 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 16 | _setupDir: string, 17 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 18 | _arch: string, 19 | ): Promise { 20 | if (process.platform !== "win32") { 21 | return undefined 22 | } 23 | 24 | if (typeof binDir === "string") { 25 | return { binDir } 26 | } 27 | 28 | const maybeBinDir = which.sync("choco", { nothrow: true }) 29 | if (maybeBinDir !== null) { 30 | binDir = dirname(maybeBinDir) 31 | return { binDir } 32 | } 33 | 34 | let powershell = "powershell.exe" 35 | const maybePowerShell = which.sync(`${process.env.SystemRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`, { 36 | nothrow: true, 37 | }) 38 | if (maybePowerShell !== null) { 39 | powershell = maybePowerShell 40 | } 41 | 42 | // https://docs.chocolatey.org/en-us/choco/setup#install-with-cmd.exe 43 | execaSync( 44 | powershell, 45 | [ 46 | "-NoProfile", 47 | "-InputFormat", 48 | "None", 49 | "-ExecutionPolicy", 50 | "Bypass", 51 | "-Command", 52 | "[System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))", 53 | ], 54 | { stdio: "inherit" }, 55 | ) 56 | 57 | const chocoPath = `${process.env.ALLUSERSPROFILE}\\chocolatey\\bin` 58 | await addPath(chocoPath, rcOptions) 59 | 60 | const maybeChoco = which.sync("choco", { nothrow: true }) 61 | if (maybeChoco !== null) { 62 | binDir = dirname(maybeChoco) 63 | } else { 64 | binDir = `${process.env.ChocolateyInstall ?? "C:/ProgramData/chocolatey"}/bin` 65 | } 66 | 67 | if (await pathExists(binDir)) { 68 | return { binDir } 69 | } 70 | return undefined 71 | } 72 | -------------------------------------------------------------------------------- /src/cmake/__tests__/cmake.test.ts: -------------------------------------------------------------------------------- 1 | import ciInfo from "ci-info" 2 | const { GITHUB_ACTIONS } = ciInfo 3 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 4 | import { getVersion } from "../../versions/versions.js" 5 | import { setupCmake } from "../cmake.js" 6 | 7 | jest.setTimeout(300000) 8 | 9 | describe("setup-cmake", () => { 10 | let directory: string 11 | beforeAll(async () => { 12 | directory = await setupTmpDir("cmake") 13 | process.env.CACHE_TOOLS = "true" 14 | }) 15 | 16 | it("should setup CMake", async () => { 17 | const { binDir } = await setupCmake(getVersion("cmake", "true"), directory, process.arch) 18 | await testBin("cmake", ["--version"], binDir) 19 | }) 20 | 21 | it("should find CMake in the cache", async () => { 22 | const { binDir } = await setupCmake(getVersion("cmake", "true"), directory, process.arch) 23 | await testBin("cmake", ["--version"], binDir) 24 | if (GITHUB_ACTIONS) { 25 | expect(binDir).toMatch(process.env.RUNNER_TOOL_CACHE ?? "hostedtoolcache") 26 | } 27 | }) 28 | 29 | afterAll(async () => { 30 | await cleanupTmpDir("cmake") 31 | }, 100000) 32 | }) 33 | -------------------------------------------------------------------------------- /src/cmakelang/__tests__/cmakelang.test.ts: -------------------------------------------------------------------------------- 1 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { getVersion } from "../../versions/versions.js" 4 | import { setupCmakelang } from "../cmakelang.js" 5 | 6 | jest.setTimeout(300000) 7 | describe("setup-cmakelang", () => { 8 | it("should setup cmakelang", async () => { 9 | const installInfo = await setupCmakelang(getVersion("cmakelang", "true", await ubuntuVersion()), "", process.arch) 10 | await testBin("cmake-lint", ["--version"], installInfo.binDir) 11 | await testBin("cmake-format", ["--version"], installInfo.binDir) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /src/cmakelang/cmakelang.ts: -------------------------------------------------------------------------------- 1 | import { setupPipPack } from "../utils/setup/setupPipPack.js" 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 4 | export function setupCmakelang(version: string | undefined, _setupDir: string, _arch: string) { 5 | return setupPipPack("cmakelang[YAML]", version) 6 | } 7 | -------------------------------------------------------------------------------- /src/conan/__tests__/conan.test.ts: -------------------------------------------------------------------------------- 1 | import { testBin } from "../../utils/tests/test-helpers.js" 2 | import { getVersion } from "../../versions/versions.js" 3 | import { setupConan } from "../conan.js" 4 | 5 | jest.setTimeout(300000) 6 | describe("setup-conan", () => { 7 | it("should setup conan", async () => { 8 | const installInfo = await setupConan(getVersion("conan", "true"), "", process.arch) 9 | 10 | await testBin("conan", ["--version"], installInfo.binDir) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/conan/conan.ts: -------------------------------------------------------------------------------- 1 | import { setupPipPack } from "../utils/setup/setupPipPack.js" 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 4 | export function setupConan(version: string | undefined, _setupDir: string, _arch: string) { 5 | return setupPipPack("conan", version) 6 | } 7 | -------------------------------------------------------------------------------- /src/cppcheck/__tests__/cppcheck.test.ts: -------------------------------------------------------------------------------- 1 | import { testBin } from "../../utils/tests/test-helpers.js" 2 | import { getVersion } from "../../versions/versions.js" 3 | import { setupCppcheck } from "../cppcheck.js" 4 | 5 | jest.setTimeout(300000) 6 | describe("setup-cppcheck", () => { 7 | it("should setup cppcheck", async () => { 8 | // TODO: choco fails abnormally on windows 9 | if (process.platform !== "win32") { 10 | const installInfo = await setupCppcheck(getVersion("cppcheck", undefined), "", process.arch) 11 | 12 | await testBin("cppcheck", ["--version"], installInfo.binDir) 13 | } 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /src/cppcheck/cppcheck.ts: -------------------------------------------------------------------------------- 1 | import { addPath } from "envosman" 2 | import { hasApk, installApkPack } from "setup-alpine" 3 | import { hasAptGet, installAptPack } from "setup-apt" 4 | import { installBrewPack } from "setup-brew" 5 | import { rcOptions } from "../options.js" 6 | import { hasDnf } from "../utils/env/hasDnf.js" 7 | import { isArch } from "../utils/env/isArch.js" 8 | import { setupChocoPack } from "../utils/setup/setupChocoPack.js" 9 | import { setupDnfPack } from "../utils/setup/setupDnfPack.js" 10 | import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js" 11 | 12 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 13 | export async function setupCppcheck(version: string | undefined, _setupDir: string, _arch: string) { 14 | switch (process.platform) { 15 | case "win32": { 16 | await setupChocoPack("cppcheck", version) 17 | const binDir = await activateWinCppcheck() 18 | return { binDir } 19 | } 20 | case "darwin": { 21 | return installBrewPack("cppcheck", version) 22 | } 23 | case "linux": { 24 | if (isArch()) { 25 | return setupPacmanPack("cppcheck", version) 26 | } else if (hasDnf()) { 27 | return setupDnfPack([{ name: "ccache", version }]) 28 | } else if (hasAptGet()) { 29 | return installAptPack([{ name: "cppcheck", version }]) 30 | } else if (await hasApk()) { 31 | return installApkPack([{ name: "cppcheck", version }]) 32 | } 33 | throw new Error("Unsupported linux distribution") 34 | } 35 | default: { 36 | throw new Error("Unsupported platform") 37 | } 38 | } 39 | } 40 | 41 | async function activateWinCppcheck() { 42 | const binDir = "C:/Program Files/Cppcheck" 43 | await addPath(binDir, rcOptions) 44 | return binDir 45 | } 46 | -------------------------------------------------------------------------------- /src/cpplint/__tests__/cpplint.test.ts: -------------------------------------------------------------------------------- 1 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { getVersion } from "../../versions/versions.js" 4 | import { setupCpplint } from "../cpplint.js" 5 | 6 | jest.setTimeout(300000) 7 | describe("setup-cpplint", () => { 8 | it("should setup cpplint", async () => { 9 | const installInfo = await setupCpplint(getVersion("cpplint", "true", await ubuntuVersion()), "", process.arch) 10 | await testBin("cpplint", ["--version"], installInfo.binDir) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/cpplint/cpplint.ts: -------------------------------------------------------------------------------- 1 | import { setupPipPack } from "../utils/setup/setupPipPack.js" 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 4 | export function setupCpplint(version: string | undefined, _setupDir: string, _arch: string) { 5 | return setupPipPack("cpplint", version, { 6 | pythonVersion: ">=3.8.0", 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /src/doxygen/__tests__/doxygen.test.ts: -------------------------------------------------------------------------------- 1 | import which from "which" 2 | import { macosVersion } from "../../utils/env/macos_version.js" 3 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 4 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 5 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 6 | import { getVersion } from "../../versions/versions.js" 7 | import { setupDoxygen } from "../doxygen.js" 8 | 9 | jest.setTimeout(300000) 10 | describe("setup-doxygen", () => { 11 | if (process.platform === "darwin" && macosVersion()[0] <= 11) { 12 | test.skip("Skipping doxygen test on macOS 11 or earlier", () => {}) 13 | return 14 | } 15 | 16 | let directory: string 17 | beforeAll(async () => { 18 | directory = await setupTmpDir("doxygen") 19 | }) 20 | 21 | it("should setup doxygen and dot", async () => { 22 | const installInfo = await setupDoxygen( 23 | getVersion("doxygen", undefined, await ubuntuVersion()), 24 | directory, 25 | process.arch, 26 | ) 27 | 28 | await testBin("doxygen", ["--version"], (installInfo as InstallationInfo | undefined)?.binDir) 29 | 30 | expect(which.sync("dot")).toBeDefined() 31 | }) 32 | 33 | afterAll(async () => { 34 | await cleanupTmpDir("doxygen") 35 | }, 100000) 36 | }) 37 | -------------------------------------------------------------------------------- /src/flawfinder/__tests__/flawfinder.test.ts: -------------------------------------------------------------------------------- 1 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { getVersion } from "../../versions/versions.js" 4 | import { setupFlawfinder } from "../flawfinder.js" 5 | 6 | jest.setTimeout(300000) 7 | describe("setup-flawfinder", () => { 8 | it("should setup flawfinder", async () => { 9 | const installInfo = await setupFlawfinder(getVersion("flawfinder", "true", await ubuntuVersion()), "", process.arch) 10 | await testBin("flawfinder", ["--version"], installInfo.binDir) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/flawfinder/flawfinder.ts: -------------------------------------------------------------------------------- 1 | import { setupPipPack } from "../utils/setup/setupPipPack.js" 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 4 | export function setupFlawfinder(version: string | undefined, _setupDir: string, _arch: string) { 5 | return setupPipPack("flawfinder", version) 6 | } 7 | -------------------------------------------------------------------------------- /src/gcc/__tests__/gcc.test.ts: -------------------------------------------------------------------------------- 1 | import path, { join } from "path" 2 | import { fileURLToPath } from "url" 3 | import { execaSync } from "execa" 4 | import { chmod } from "fs/promises" 5 | import { addExeExt } from "patha" 6 | import { hasAptGet } from "setup-apt" 7 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 8 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 9 | import { getVersion } from "../../versions/versions.js" 10 | import { setupGcc } from "../gcc.js" 11 | 12 | const dirname = typeof __dirname === "string" ? __dirname : path.dirname(fileURLToPath(import.meta.url)) 13 | 14 | jest.setTimeout(3000000) 15 | describe("setup-gcc", () => { 16 | let directory: string 17 | beforeAll(async () => { 18 | directory = await setupTmpDir("gcc") 19 | }) 20 | 21 | it("should setup gcc", async () => { 22 | const ubuntuVersionOutput = await ubuntuVersion() 23 | const version = getVersion("gcc", undefined, ubuntuVersionOutput) 24 | const installInfo = await setupGcc(version, directory, process.arch) 25 | 26 | let gpp = "g++" 27 | if (hasAptGet()) { 28 | const ubuntuMajorVersion = ubuntuVersionOutput?.[0] 29 | // https://packages.ubuntu.com/search?keywords=gcc 30 | switch (ubuntuMajorVersion) { 31 | case 26: 32 | case 25: 33 | gpp = "g++-14" 34 | break 35 | case 24: 36 | case 23: 37 | gpp = "g++-13" 38 | break 39 | case 22: 40 | case 21: 41 | gpp = "g++-11" 42 | break 43 | case 20: 44 | gpp = "g++-9" 45 | break 46 | default: { 47 | // ignore 48 | } 49 | } 50 | } else if (process.platform === "darwin") { 51 | // https://formulae.brew.sh/formula/gcc 52 | // As of 3, Jun, 2025 53 | gpp = "g++-15" 54 | } 55 | 56 | await testBin(gpp, ["--version"], installInfo?.binDir) 57 | 58 | expect(process.env.CC?.includes("gcc")).toBeTruthy() 59 | expect(process.env.CXX?.includes("g++")).toBeTruthy() 60 | 61 | // test compilation 62 | const file = join(dirname, "main.cpp") 63 | const main_exe = join(dirname, addExeExt("main")) 64 | execaSync("g++", [file, "-o", main_exe], { cwd: dirname }) 65 | if (process.platform !== "win32") { 66 | await chmod(main_exe, "755") 67 | } 68 | execaSync(main_exe, { cwd: dirname, stdio: "inherit" }) 69 | }) 70 | 71 | afterAll(async () => { 72 | await cleanupTmpDir("gcc") 73 | }, 100000) 74 | }) 75 | -------------------------------------------------------------------------------- /src/gcc/__tests__/main.cpp: -------------------------------------------------------------------------------- 1 | // test std libraries 2 | #include 3 | #include 4 | 5 | // test c libraries 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int main() { 14 | const auto val1 = 10.0; 15 | std::cout << "Testing " << val1 << '\n'; 16 | 17 | const auto val2 = std::to_string(val1); 18 | std::cout << "Testing " << val2 << '\n'; 19 | 20 | return static_cast(std::sin(val1) + std::log(static_cast(val2.size())) - 1); 21 | } 22 | -------------------------------------------------------------------------------- /src/gcc/assets-list.ts: -------------------------------------------------------------------------------- 1 | import { saveGitHubAssetList } from "../utils/asset/fetch-github-assets.ts" 2 | 3 | /** 4 | * Generate the list of all releases of a GitHub repository and save it to a json file 5 | */ 6 | async function main() { 7 | // https://github.com/brechtsanders/winlibs_mingw/releases 8 | await saveGitHubAssetList( 9 | "brechtsanders", 10 | "winlibs_mingw", 11 | "./src/gcc/github_brechtsanders_winlibs_mingw.json", 12 | (asset) => asset.endsWith(".7z"), 13 | ) 14 | } 15 | 16 | main().catch((err) => { 17 | console.error(err) 18 | process.exit(1) 19 | }) 20 | -------------------------------------------------------------------------------- /src/gcc/gccMatcher.ts: -------------------------------------------------------------------------------- 1 | import { join } from "path" 2 | import { info, warning } from "ci-log" 3 | import { pathExists } from "path-exists" 4 | import { dirname } from "./gcc.ts" 5 | 6 | export async function addGccLoggingMatcher() { 7 | const matcherPath = join(dirname, "gcc_matcher.json") 8 | if (!(await pathExists(matcherPath))) { 9 | return warning("the gcc_matcher.json file does not exist in the same folder as setup-cpp.js") 10 | } 11 | info(`::add-matcher::${matcherPath}`) 12 | } 13 | -------------------------------------------------------------------------------- /src/gcc/gcc_matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "gcc", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "severity": 4, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/gcovr/__tests__/gcovr.test.ts: -------------------------------------------------------------------------------- 1 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { getVersion } from "../../versions/versions.js" 4 | import { setupGcovr } from "../gcovr.js" 5 | 6 | jest.setTimeout(300000) 7 | describe("setup-gcovr", () => { 8 | it("should setup gcovr", async () => { 9 | const installInfo = await setupGcovr(getVersion("gcovr", "true", await ubuntuVersion()), "", process.arch) 10 | await testBin("gcovr", ["--version"], installInfo.binDir) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/gcovr/gcovr.ts: -------------------------------------------------------------------------------- 1 | import { addEnv } from "envosman" 2 | import semverMajor from "semver/functions/major" 3 | import semverValid from "semver/functions/valid" 4 | import { rcOptions } from "../options.js" 5 | import { setupPipPack } from "../utils/setup/setupPipPack.js" 6 | 7 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 8 | export function setupGcovr(version: string | undefined, _setupDir: string, _arch: string) { 9 | return setupPipPack("gcovr", version) 10 | } 11 | 12 | export function activateGcovLLVM() { 13 | return addEnv("GCOV", "llvm-cov gcov", rcOptions) 14 | } 15 | 16 | export function activateGcovGCC(gccVersion: string) { 17 | const gccSemver = semverValid(gccVersion) 18 | const gccMajor = gccSemver !== null ? semverMajor(gccSemver) : gccVersion 19 | const gcov = gccMajor !== "" ? `gcov-${gccMajor}` : "gcov" 20 | 21 | return addEnv("GCOV", gcov, rcOptions) 22 | } 23 | -------------------------------------------------------------------------------- /src/git/__tests__/git.test.ts: -------------------------------------------------------------------------------- 1 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { setupGit } from "../git.js" 4 | 5 | jest.setTimeout(300000) 6 | describe("setup-git", () => { 7 | it("should setup git", async () => { 8 | const installInfo = await setupGit("", "", process.arch) 9 | 10 | await testBin("git", ["--version"], (installInfo as InstallationInfo | undefined)?.binDir) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/git/git.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from "fs" 2 | import { join } from "path" 3 | import { info, warning } from "ci-log" 4 | import { addPath } from "envosman" 5 | import { hasApk, installApkPack } from "setup-alpine" 6 | import { hasAptGet, installAptPack } from "setup-apt" 7 | import { installBrewPack } from "setup-brew" 8 | import which from "which" 9 | import { rcOptions } from "../options.js" 10 | import { hasDnf } from "../utils/env/hasDnf.js" 11 | import { isArch } from "../utils/env/isArch.js" 12 | import { setupChocoPack } from "../utils/setup/setupChocoPack.js" 13 | import { setupDnfPack } from "../utils/setup/setupDnfPack.js" 14 | import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js" 15 | 16 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 17 | export async function setupGit(version: string, _setupDir: string, _arch: string) { 18 | const git = await which("git", { nothrow: true }) 19 | if (git !== null) { 20 | info(`Git already installed at ${git}`) 21 | return 22 | } 23 | 24 | switch (process.platform) { 25 | case "win32": { 26 | const result = await setupChocoPack("git", version) 27 | const gitDir = findWindowsGit() 28 | if (gitDir !== null) { 29 | await addPath(gitDir, rcOptions) 30 | } 31 | return result 32 | } 33 | case "darwin": { 34 | return installBrewPack("git", version) 35 | } 36 | case "linux": { 37 | if (isArch()) { 38 | return setupPacmanPack("git", version) 39 | } else if (hasDnf()) { 40 | return setupDnfPack([{ name: "git", version }]) 41 | } else if (hasAptGet()) { 42 | return installAptPack([{ name: "git", version }]) 43 | } else if (await hasApk()) { 44 | return installApkPack([{ name: "git", version }]) 45 | } 46 | throw new Error("Unsupported linux distribution") 47 | } 48 | default: { 49 | throw new Error("Unsupported platform") 50 | } 51 | } 52 | } 53 | 54 | function findWindowsGit() { 55 | const candidates = [ 56 | "C:/Program Files/Git/bin/", 57 | "C:/Program Files (x86)/Git/bin/", 58 | ] 59 | for (const candidate of candidates) { 60 | if (existsSync(join(candidate, "git.exe"))) { 61 | return candidate 62 | } 63 | } 64 | 65 | warning("Git not found in the default locations. Add git to PATH manually.") 66 | return null 67 | } 68 | -------------------------------------------------------------------------------- /src/graphviz/__tests__/graphviz.test.ts: -------------------------------------------------------------------------------- 1 | import { macosVersion } from "../../utils/env/macos_version.js" 2 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 3 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 4 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 5 | import { getVersion } from "../../versions/versions.js" 6 | import { setupGraphviz } from "../graphviz.js" 7 | 8 | jest.setTimeout(300000) 9 | describe("setup-graphviz", () => { 10 | if (process.platform === "darwin" && macosVersion()[0] <= 11) { 11 | test.skip("Skipping graphviz test on macOS 11 or earlier", () => {}) 12 | return 13 | } 14 | 15 | let directory: string 16 | beforeAll(async () => { 17 | directory = await setupTmpDir("graphviz") 18 | }) 19 | 20 | it("should setup graphviz", async () => { 21 | const installInfo = await setupGraphviz( 22 | getVersion("graphviz", undefined, await ubuntuVersion()), 23 | directory, 24 | process.arch, 25 | ) 26 | 27 | await testBin("dot", ["-V"], (installInfo as InstallationInfo | undefined)?.binDir) 28 | }) 29 | 30 | afterAll(async () => { 31 | await cleanupTmpDir("graphviz") 32 | }, 100000) 33 | }) 34 | -------------------------------------------------------------------------------- /src/graphviz/graphviz.ts: -------------------------------------------------------------------------------- 1 | import { addPath } from "envosman" 2 | import { hasApk, installApkPack } from "setup-alpine" 3 | import { hasAptGet, installAptPack } from "setup-apt" 4 | import { installBrewPack } from "setup-brew" 5 | import { rcOptions } from "../options.js" 6 | import { hasDnf } from "../utils/env/hasDnf.js" 7 | import { isArch } from "../utils/env/isArch.js" 8 | import type { InstallationInfo } from "../utils/setup/setupBin.js" 9 | import { setupChocoPack } from "../utils/setup/setupChocoPack.js" 10 | import { setupDnfPack } from "../utils/setup/setupDnfPack.js" 11 | import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js" 12 | 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | export async function setupGraphviz(version: string, _setupDir: string, _arch: string) { 15 | switch (process.platform) { 16 | case "win32": { 17 | await setupChocoPack("graphviz", version) 18 | return activateGraphviz() 19 | } 20 | case "darwin": { 21 | return installBrewPack("graphviz", version) 22 | } 23 | case "linux": { 24 | if (isArch()) { 25 | return setupPacmanPack("graphviz", version) 26 | } else if (hasDnf()) { 27 | return setupDnfPack([{ name: "graphviz", version }]) 28 | } else if (hasAptGet()) { 29 | return installAptPack([{ name: "graphviz", version }]) 30 | } else if (await hasApk()) { 31 | return installApkPack([{ name: "graphviz", version }]) 32 | } 33 | throw new Error("Unsupported linux distribution") 34 | } 35 | default: { 36 | throw new Error("Unsupported platform") 37 | } 38 | } 39 | } 40 | 41 | async function activateGraphviz(): Promise { 42 | switch (process.platform) { 43 | case "win32": { 44 | const binDir = "C:/Program Files/Graphviz/bin" 45 | await addPath(binDir, rcOptions) 46 | return { binDir } 47 | } 48 | default: { 49 | throw new Error("Unsupported platform") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/infer/__tests__/infer.test.ts: -------------------------------------------------------------------------------- 1 | import { info } from "ci-log" 2 | import { hasAptGet } from "setup-apt" 3 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 4 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 5 | import { getVersion } from "../../versions/versions.js" 6 | import { setupInfer } from "../infer.js" 7 | 8 | jest.setTimeout(300000) 9 | 10 | describe("setup-infer", () => { 11 | if (process.platform === "win32" || (process.platform === "linux" && process.arch === "arm64")) { 12 | it("should skip infer tests on Windows and Linux arm64", () => { 13 | expect(true).toBe(true) 14 | }) 15 | return 16 | } 17 | 18 | let directory: string 19 | beforeAll(async () => { 20 | directory = await setupTmpDir("infer") 21 | process.env.CACHE_TOOLS = "true" 22 | }) 23 | 24 | it("should setup infer", async () => { 25 | /* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ 26 | if (hasAptGet() && (await ubuntuVersion())?.[0]! <= 20) { 27 | info("Skipping infer test on ubuntu 20 and below") 28 | return 29 | } 30 | 31 | const { binDir } = await setupInfer(getVersion("infer", "true"), directory, process.arch) 32 | await testBin("infer", ["--version"], binDir) 33 | }) 34 | 35 | afterAll(async () => { 36 | await cleanupTmpDir("infer") 37 | }, 100000) 38 | }) 39 | -------------------------------------------------------------------------------- /src/infer/assets-list.ts: -------------------------------------------------------------------------------- 1 | import { saveGitHubAssetList } from "../utils/asset/fetch-github-assets.ts" 2 | 3 | /** 4 | * Generate the list of all releases of a GitHub repository and save it to a json file 5 | */ 6 | async function main() { 7 | // https://github.com/facebook/infer/releases 8 | await saveGitHubAssetList( 9 | "facebook", 10 | "infer", 11 | "./src/infer/github_facebook_infer.json", 12 | ) 13 | } 14 | 15 | main().catch((err) => { 16 | console.error(err) 17 | process.exit(1) 18 | }) 19 | -------------------------------------------------------------------------------- /src/installTool.ts: -------------------------------------------------------------------------------- 1 | import { join } from "path" 2 | import { endGroup, startGroup } from "@actions/core" 3 | import { error } from "ci-log" 4 | import { setupBrew } from "setup-brew" 5 | import { getSuccessMessage, rcOptions } from "./options.js" 6 | import { type ToolName, llvmTools, setups } from "./tool.js" 7 | import type { InstallationInfo } from "./utils/setup/setupBin.js" 8 | import { setupVCVarsall } from "./vcvarsall/vcvarsall.js" 9 | import { getVersion } from "./versions/versions.js" 10 | 11 | export const DEFAULT_TIMEOUT = 60 * 60 * 1000 // 60 minutes 12 | 13 | export async function installTool( 14 | tool: ToolName, 15 | version: string, 16 | osVersion: number[] | null, 17 | arch: string, 18 | setupCppDir: string, 19 | successMessages: string[], 20 | errorMessages: string[], 21 | _timeout: number = DEFAULT_TIMEOUT, // TODO: pass to execa 22 | ) { 23 | startGroup(`Installing ${tool} ${version}`) 24 | try { 25 | await installToolImpl(tool, version, osVersion, arch, setupCppDir, successMessages) 26 | } catch (e) { 27 | // push error message to the logger 28 | error(e as string | Error) 29 | if (e instanceof Error && e.stack !== undefined) { 30 | error(e.stack) 31 | } 32 | errorMessages.push(`${tool} failed to install`) 33 | } 34 | endGroup() 35 | } 36 | 37 | async function installToolImpl( 38 | tool: ToolName, 39 | version: string, 40 | osVersion: number[] | null, 41 | arch: string, 42 | setupCppDir: string, 43 | successMessages: string[], 44 | ) { 45 | const hasLLVM = llvmTools.includes(tool) 46 | 47 | let installationInfo: InstallationInfo | undefined | void 48 | if (tool === "vcvarsall") { 49 | // eslint-disable-next-line no-await-in-loop 50 | await setupVCVarsall(getVersion(tool, version, osVersion), undefined, arch, undefined, undefined, false, false) 51 | } else if (tool === "brew") { 52 | // eslint-disable no-await-in-loop 53 | installationInfo = await setupBrew({ rcOptions }) 54 | } else { 55 | // the tool installation directory (for the functions that ue it) 56 | const setupDir = join(setupCppDir, hasLLVM ? "llvm" : tool) 57 | 58 | const setupVersion = getVersion(tool, version, osVersion) 59 | 60 | // get the setup function 61 | const setupFunction = setups[tool] 62 | 63 | // eslint-disable no-await-in-loop 64 | installationInfo = await setupFunction(setupVersion, setupDir, arch) 65 | } 66 | // preparing a report string 67 | successMessages.push(getSuccessMessage(tool, installationInfo)) 68 | } 69 | -------------------------------------------------------------------------------- /src/kcov/__tests__/kcov.test.ts: -------------------------------------------------------------------------------- 1 | import { info } from "@actions/core" 2 | import which from "which" 3 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 4 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 5 | import { setupKcov } from "../kcov.js" 6 | 7 | jest.setTimeout(300000) 8 | describe("setup-Kcov", () => { 9 | if (process.platform !== "linux") { 10 | it.todo("should setup kcov on non-Windows") 11 | return 12 | } 13 | 14 | it("should build and setup kcov-41", async () => { 15 | const directory = await setupTmpDir("kcov-v41") 16 | const { binDir } = (await setupKcov("41", directory, "")) as InstallationInfo 17 | // the prebuild binary only works on ubuntu 20.04 18 | try { 19 | await testBin("kcov", ["--version"], binDir) 20 | } catch (err) { 21 | info((err as Error).message) 22 | } 23 | await cleanupTmpDir("kcov-v41") 24 | }) 25 | 26 | it("should setup Kcov v40 via downloading the binaries", async () => { 27 | const directory = await setupTmpDir("kcov-v40") 28 | const { binDir } = (await setupKcov("40-binary", directory, "")) as InstallationInfo 29 | // the prebuild binary only works on ubuntu 20.04 30 | try { 31 | await testBin("kcov", ["--version"], binDir) 32 | } catch (err) { 33 | info((err as Error).message) 34 | } 35 | await cleanupTmpDir("kcov-v40") 36 | }) 37 | 38 | it("should build and setup Kcov v40", async () => { 39 | const directory = await setupTmpDir("kcov-v40") 40 | const { binDir } = (await setupKcov("40", directory, "")) as InstallationInfo 41 | await testBin("kcov", ["--version"], binDir) 42 | await cleanupTmpDir("kcov-v40") 43 | }) 44 | 45 | it("should build and setup Kcov v38", async () => { 46 | try { 47 | const directory2 = await setupTmpDir("kcov-v38") 48 | 49 | await setupKcov("38", directory2, "") 50 | 51 | expect(which.sync("kcov", { nothrow: true })).toBeTruthy() 52 | 53 | await testBin("kcov", ["--version"], "usr/local/bin") // because of cmake --install 54 | 55 | await cleanupTmpDir("kcov-v38") 56 | } catch (err) { 57 | // TODO 58 | console.warn(err) 59 | } 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /src/kcov/gcc13.patch: -------------------------------------------------------------------------------- 1 | From b63754b53b3a7cf43e13ec56bd0be76cb6175437 Mon Sep 17 00:00:00 2001 2 | From: Sergei Trofimovich 3 | Date: Thu, 15 Sep 2022 19:55:21 +0100 4 | Subject: [PATCH] Fix build on gcc-13: add missing include 5 | 6 | [ 15%] Building CXX object src/CMakeFiles/kcov.dir/writers/cobertura-writer.cc.o 7 | In file included from kcov/src/writers/cobertura-writer.cc:6: 8 | kcov/src/include/reporter.hh:24:90: error: 'uint64_t' has not been declared 9 | 24 | LineExecutionCount(unsigned int hits, unsigned int possibleHits, uint64_t order) : 10 | | ^~~~~~~~ 11 | --- 12 | src/include/collector.hh | 2 ++ 13 | src/include/reporter.hh | 1 + 14 | src/include/source-file-cache.hh | 2 ++ 15 | 3 files changed, 5 insertions(+) 16 | 17 | diff --git a/src/include/collector.hh b/src/include/collector.hh 18 | index 79e5d5f2..1369a416 100644 19 | --- a/src/include/collector.hh 20 | +++ b/src/include/collector.hh 21 | @@ -2,6 +2,8 @@ 22 | 23 | #include 24 | 25 | +#include 26 | + 27 | namespace kcov 28 | { 29 | class IFileParser; 30 | diff --git a/src/include/reporter.hh b/src/include/reporter.hh 31 | index bc058e69..98d8e56b 100644 32 | --- a/src/include/reporter.hh 33 | +++ b/src/include/reporter.hh 34 | @@ -3,6 +3,7 @@ 35 | #include 36 | 37 | #include 38 | +#include 39 | 40 | namespace kcov 41 | { 42 | diff --git a/src/include/source-file-cache.hh b/src/include/source-file-cache.hh 43 | index c0cb00ee..cfc73b81 100644 44 | --- a/src/include/source-file-cache.hh 45 | +++ b/src/include/source-file-cache.hh 46 | @@ -3,6 +3,8 @@ 47 | #include 48 | #include 49 | 50 | +#include 51 | + 52 | namespace kcov 53 | { 54 | /** 55 | -------------------------------------------------------------------------------- /src/lizard/__tests__/lizard.test.ts: -------------------------------------------------------------------------------- 1 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { getVersion } from "../../versions/versions.js" 4 | import { setupLizard } from "../lizard.js" 5 | 6 | jest.setTimeout(300000) 7 | describe("setup-lizard", () => { 8 | it("should setup lizard", async () => { 9 | const installInfo = await setupLizard(getVersion("lizard", "true", await ubuntuVersion()), "", process.arch) 10 | await testBin("lizard", ["--version"], installInfo.binDir) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/lizard/lizard.ts: -------------------------------------------------------------------------------- 1 | import { setupPipPack } from "../utils/setup/setupPipPack.js" 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 4 | export function setupLizard(version: string | undefined, _setupDir: string, _arch: string) { 5 | return setupPipPack("lizard", version) 6 | } 7 | -------------------------------------------------------------------------------- /src/llvm/__tests__/main.cpp: -------------------------------------------------------------------------------- 1 | // test std libraries 2 | #include 3 | #include 4 | 5 | // test c libraries 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int main() { 14 | const auto val1 = 10.0; 15 | std::cout << "Testing " << val1 << '\n'; 16 | 17 | const auto val2 = std::to_string(val1); 18 | std::cout << "Testing " << val2 << '\n'; 19 | 20 | return static_cast(std::sin(val1) + std::log(static_cast(val2.size())) - 1); 21 | } 22 | -------------------------------------------------------------------------------- /src/llvm/apple-clang.ts: -------------------------------------------------------------------------------- 1 | import { error } from "console" 2 | import { notice } from "ci-log" 3 | import { addEnv } from "envosman" 4 | import which from "which" 5 | import { rcOptions } from "../options.js" 6 | 7 | export async function setupAppleClang() { 8 | if (process.platform !== "darwin") { 9 | return 10 | } 11 | 12 | if (await which("clang", { nothrow: true }) !== null && await which("clang++", { nothrow: true }) !== null) { 13 | notice("Assuming clang is an Apple Clang compiler") 14 | await Promise.all([addEnv("CC", "clang", rcOptions), addEnv("CXX", "clang++", rcOptions)]) 15 | } 16 | 17 | // TODO install Apple Clang automatically 18 | error("Apple Clang automatic installation is not supported yet") 19 | } 20 | -------------------------------------------------------------------------------- /src/llvm/assets-list.ts: -------------------------------------------------------------------------------- 1 | import { saveGitHubAssetList } from "../utils/asset/fetch-github-assets.ts" 2 | import { saveHTMLAssets } from "../utils/asset/fetch-html-assets.ts" 3 | 4 | /** 5 | * Generate the list of all releases of a GitHub repository and save it to a json file 6 | */ 7 | async function main() { 8 | // https://github.com/llvm/llvm-project/releases 9 | await saveGitHubAssetList( 10 | "llvm", 11 | "llvm-project", 12 | "./src/llvm/github_llvm_llvm-project.json", 13 | isAssetArchive, 14 | ) 15 | 16 | // go through https://releases.llvm.org/x.y.z and get all the assets 17 | await saveHTMLAssets({ 18 | htmlDownloadDir: "./src/llvm/assets/", 19 | path: "./src/llvm/llvm_org_releases.json", 20 | *getAssetVersionAndURL() { 21 | for (let major = 1; major <= 9; major++) { 22 | for (let minor = 0; minor <= 9; minor++) { 23 | for (let patch = 0; patch <= 9; patch++) { 24 | const version = (major >= 3 && minor >= 4 && patch >= 1) 25 | ? `${major}.${minor}` 26 | : `${major}.${minor}.${patch}` 27 | yield [version, `https://releases.llvm.org/${version}`] as [string, string] 28 | } 29 | } 30 | } 31 | }, 32 | filterAssets: isAssetArchive, 33 | }) 34 | } 35 | 36 | function isAssetArchive(asset: string): boolean { 37 | // only download the LLVM and clang+llvm archives 38 | return (asset.startsWith("LLVM-") 39 | || asset.startsWith("clang+llvm-") 40 | || asset.startsWith("clang%2Bllvm-")) // cspell: disable-line 41 | // only download the archives 42 | && (asset.endsWith("tar.xz") 43 | || asset.endsWith("zip") 44 | || asset.endsWith("exe") 45 | || asset.endsWith("tar.gz")) 46 | } 47 | 48 | main().catch((err) => { 49 | console.error(err) 50 | process.exit(1) 51 | }) 52 | -------------------------------------------------------------------------------- /src/llvm/llvm_apk_installer.ts: -------------------------------------------------------------------------------- 1 | import { info } from "ci-log" 2 | import { type InstallationInfo, hasApk, installApkPack } from "setup-alpine" 3 | import { majorLLVMVersion } from "./utils.ts" 4 | 5 | /** 6 | * Try to setup LLVM via the apk package manager 7 | * 8 | * @param {string} version - The version of LLVM to install 9 | * 10 | * @returns {InstallationInfo} The installation info if the installation was successful 11 | * @returns {undefined} If the installation fails, it will try to remove the repository and will return undefined 12 | */ 13 | export async function trySetupLLVMApk( 14 | version: string, 15 | ): Promise { 16 | if (!await hasApk()) { 17 | return undefined 18 | } 19 | 20 | try { 21 | return await setupLLVMApk(version) 22 | } catch (err) { 23 | info(`Failed to install llvm via system package manager ${err}.`) 24 | } 25 | return undefined 26 | } 27 | 28 | export function setupLLVMApk(version: string): Promise { 29 | const majorVersion = majorLLVMVersion(version) 30 | return installApkPack([{ name: `llvm${majorVersion}` }]) 31 | } 32 | -------------------------------------------------------------------------------- /src/llvm/llvm_brew_installer.ts: -------------------------------------------------------------------------------- 1 | import { info } from "ci-log" 2 | import { addPath } from "envosman" 3 | import { installBrewPack } from "setup-brew" 4 | import { rcOptions } from "../options.ts" 5 | import { majorLLVMVersion } from "./utils.ts" 6 | 7 | export async function trySetupLLVMBrew(version: string, _setupDir: string, _arch: string) { 8 | if (process.platform !== "darwin") { 9 | return Promise.resolve(undefined) 10 | } 11 | 12 | try { 13 | return await setupLLVMBrew(version, _setupDir, _arch) 14 | } catch (err) { 15 | info(`Failed to install llvm via brew: ${err}`) 16 | return undefined 17 | } 18 | } 19 | 20 | export async function setupLLVMBrew(version: string, _setupDir: string, _arch: string) { 21 | const majorVersion = majorLLVMVersion(version) 22 | 23 | // install llvm via brew if a bottle is available for it 24 | const installInfo = await installBrewPack("llvm", `${majorVersion}`, { "force-bottle": true }) 25 | 26 | // add the bin directory to the PATH 27 | await addPath(installInfo.binDir, rcOptions) 28 | 29 | return installInfo 30 | } 31 | -------------------------------------------------------------------------------- /src/llvm/llvm_matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "llvm", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "severity": 4, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/llvm/utils.ts: -------------------------------------------------------------------------------- 1 | import memoize from "memoizee" 2 | import { semverCoerceIfInvalid } from "../utils/setup/version.ts" 3 | function majorLLVMVersion_(version: string) { 4 | const coeredVersion = semverCoerceIfInvalid(version) 5 | return Number.parseInt(coeredVersion.split(".")[0], 10) 6 | } 7 | 8 | export const majorLLVMVersion = memoize(majorLLVMVersion_) 9 | -------------------------------------------------------------------------------- /src/macos-sdk/__tests__/macos-sdk.test.ts: -------------------------------------------------------------------------------- 1 | import { setupMacOSSDK } from "../macos-sdk.js" 2 | 3 | jest.setTimeout(300000) 4 | describe("setup-macos-sdk", () => { 5 | if (process.platform !== "darwin") { 6 | it.skip("should setup macos-sdk", () => {}) 7 | return 8 | } 9 | it("should setup macos-sdk", async () => { 10 | process.env.SDKROOT = undefined 11 | await setupMacOSSDK() 12 | expect(process.env.SDKROOT).toBeTruthy() 13 | expect(typeof process.env.SDKROOT).toBe("string") 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /src/macos-sdk/macos-sdk.ts: -------------------------------------------------------------------------------- 1 | import { getExecOutput } from "@actions/exec" 2 | import { error } from "ci-log" 3 | import { addEnv } from "envosman" 4 | import { rcOptions } from "../options.js" 5 | 6 | export async function setupMacOSSDK() { 7 | if (process.platform === "darwin") { 8 | try { 9 | const xcrun = await getExecOutput("xcrun --sdk macosx --show-sdk-path") 10 | const sdkroot = xcrun.stdout || xcrun.stderr 11 | if (sdkroot) { 12 | await addEnv("SDKROOT", sdkroot.trim(), rcOptions) 13 | } else { 14 | error("SDKROOT not set") 15 | } 16 | } catch (e) { 17 | error(e as Error | string) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/make/__tests__/make.test.ts: -------------------------------------------------------------------------------- 1 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { setupMake } from "../make.js" 4 | 5 | jest.setTimeout(300000) 6 | describe("setup-make", () => { 7 | it("should setup make", async () => { 8 | const installInfo = await setupMake("", "", process.arch) 9 | 10 | await testBin("make", ["--version"], (installInfo as InstallationInfo | undefined)?.binDir) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/make/make.ts: -------------------------------------------------------------------------------- 1 | import { join } from "path" 2 | import { addPath } from "envosman" 3 | import { hasApk, installApkPack } from "setup-alpine" 4 | import { hasAptGet, installAptPack } from "setup-apt" 5 | import { getBrewDir, installBrewPack } from "setup-brew" 6 | import { rcOptions } from "../options.js" 7 | import { hasDnf } from "../utils/env/hasDnf.js" 8 | import { isArch } from "../utils/env/isArch.js" 9 | import { setupChocoPack } from "../utils/setup/setupChocoPack.js" 10 | import { setupDnfPack } from "../utils/setup/setupDnfPack.js" 11 | import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js" 12 | 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | export async function setupMake(version: string, _setupDir: string, _arch: string) { 15 | switch (process.platform) { 16 | case "win32": { 17 | return setupChocoPack("make", version) 18 | } 19 | case "darwin": { 20 | await installBrewPack("make", version) 21 | 22 | const gnuBinDir = join(getBrewDir(), "opt/make/libexec/gnubin") 23 | await addPath(gnuBinDir, rcOptions) 24 | return { binDir: gnuBinDir } 25 | } 26 | case "linux": { 27 | if (isArch()) { 28 | return setupPacmanPack("make", version) 29 | } else if (hasDnf()) { 30 | return setupDnfPack([{ name: "make", version }]) 31 | } else if (hasAptGet()) { 32 | return installAptPack([{ name: "make", version }]) 33 | } else if (await hasApk()) { 34 | return installApkPack([{ name: "make", version }]) 35 | } 36 | throw new Error("Unsupported linux distribution") 37 | } 38 | default: { 39 | throw new Error("Unsupported platform") 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/meson/__tests__/meson.test.ts: -------------------------------------------------------------------------------- 1 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { getVersion } from "../../versions/versions.js" 4 | import { setupMeson } from "../meson.js" 5 | 6 | jest.setTimeout(300000) 7 | describe("setup-meson", () => { 8 | it("should setup meson", async () => { 9 | const installInfo = await setupMeson(getVersion("meson", "true", await ubuntuVersion()), "", process.arch) 10 | 11 | await testBin("meson", ["--version"], installInfo.binDir) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /src/meson/meson.ts: -------------------------------------------------------------------------------- 1 | import { setupPipPack } from "../utils/setup/setupPipPack.js" 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 4 | export function setupMeson(version: string | undefined, _setupDir: string, _arch: string) { 5 | return setupPipPack("meson", version) 6 | } 7 | -------------------------------------------------------------------------------- /src/msvc/__tests__/msvc.test.ts: -------------------------------------------------------------------------------- 1 | import { warning } from "ci-log" 2 | import which from "which" 3 | import { runnerWindowsVersion } from "../../utils/tests/test-helpers.js" 4 | import { setupMSVC } from "../msvc.js" 5 | 6 | jest.setTimeout(300000) 7 | describe("setup-msvc", () => { 8 | if (process.platform !== "win32") { 9 | it.skip("should setup msvc", () => {}) 10 | return 11 | } 12 | it("should setup the pre-installed msvc", async () => { 13 | try { 14 | await setupMSVC("", "", process.arch) 15 | console.log(which.sync("cl")) 16 | } catch (err) { 17 | if (err instanceof Error) { 18 | warning(err.toString()) 19 | } 20 | } 21 | }) 22 | 23 | for (const version of [2022, 2019, 2017, 2015]) { 24 | if (runnerWindowsVersion() !== undefined && runnerWindowsVersion()! > version) { 25 | it.skip(`should setup msvc ${version}`, () => {}) 26 | return 27 | } 28 | it(`should setup msvc ${version}`, async () => { 29 | try { 30 | await setupMSVC(`${version}`, "", process.arch) 31 | console.log(which.sync("cl")) 32 | } catch (err) { 33 | if (err instanceof Error) { 34 | warning(err.toString()) 35 | } 36 | } 37 | }) 38 | } 39 | }) 40 | -------------------------------------------------------------------------------- /src/msvc/msvc_matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "msvc", 5 | "pattern": [ 6 | { 7 | "regexp": "^(?:\\s+\\d+>)?(\\S.*)\\((\\d+),?(\\d+)?(?:,\\d+,\\d+)?\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "severity": 4, 12 | "code": 5, 13 | "message": 6 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/ninja/__tests__/ninja.test.ts: -------------------------------------------------------------------------------- 1 | import ciInfo from "ci-info" 2 | const { GITHUB_ACTIONS } = ciInfo 3 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 4 | import { getVersion } from "../../versions/versions.js" 5 | import { setupNinja } from "../ninja.js" 6 | 7 | jest.setTimeout(300000) 8 | async function testNinja(directory: string) { 9 | const { binDir } = await setupNinja(getVersion("ninja", "true"), directory, process.arch) 10 | await testBin("ninja", ["--version"], binDir) 11 | return binDir 12 | } 13 | 14 | describe("setup-ninja", () => { 15 | let directory: string 16 | beforeEach(async () => { 17 | directory = await setupTmpDir("ninja") 18 | process.env.CACHE_TOOLS = "true" 19 | }) 20 | 21 | it("should setup Ninja", async () => { 22 | await testNinja(directory) 23 | }) 24 | 25 | it("should find Ninja in the cache", async () => { 26 | const binDir = await testNinja(directory) 27 | if (GITHUB_ACTIONS) { 28 | expect(binDir).toMatch(process.env.RUNNER_TOOL_CACHE ?? "hostedtoolcache") 29 | } 30 | }) 31 | 32 | afterEach(async () => { 33 | await cleanupTmpDir("ninja") 34 | }, 100000) 35 | }) 36 | -------------------------------------------------------------------------------- /src/ninja/ninja.ts: -------------------------------------------------------------------------------- 1 | import { addPath } from "envosman" 2 | import { addExeExt } from "patha" 3 | import { enableCommunityRepository, hasApk, installApkPack } from "setup-alpine" 4 | import { rcOptions } from "../options.js" 5 | import { arm64, x86, x86_64 } from "../utils/env/arch.js" 6 | import { type InstallationInfo, type PackageInfo, setupBin } from "../utils/setup/setupBin.js" 7 | 8 | /** Get the platform name Ninja uses in their download links */ 9 | function getNinjaPlatformArch(platform: NodeJS.Platform, arch: string) { 10 | switch (platform) { 11 | case "win32": 12 | return x86_64.includes(arch) 13 | || x86.includes(arch) 14 | ? "win" 15 | : arm64.includes(arch) 16 | ? "winarm64" 17 | : "win" 18 | case "darwin": 19 | return "mac" 20 | case "linux": 21 | return x86_64.includes(arch) 22 | || x86.includes(arch) 23 | ? "linux" 24 | : arm64.includes(arch) 25 | ? "linux-aarch64" 26 | : "linux" 27 | default: 28 | throw new Error(`Unsupported platform '${platform}'`) 29 | } 30 | } 31 | 32 | /** Get the platform data for ninja */ 33 | function getNinjaPackageInfo(version: string, platform: NodeJS.Platform, arch: string): PackageInfo { 34 | const ninjaPlatform = getNinjaPlatformArch(platform, arch) 35 | return { 36 | binRelativeDir: "", 37 | binFileName: addExeExt("ninja"), 38 | extractedFolderName: "", 39 | url: `https://github.com/ninja-build/ninja/releases/download/v${version}/ninja-${ninjaPlatform}.zip`, 40 | } 41 | } 42 | 43 | export async function setupNinja(version: string, setupDir: string, arch: string): Promise { 44 | if (await hasApk()) { 45 | await enableCommunityRepository() 46 | await installApkPack([ 47 | { 48 | name: "ninja-build", 49 | // version, 50 | }, 51 | { 52 | name: "ninja-is-really-ninja", 53 | }, 54 | ]) 55 | await addPath("/usr/lib/ninja-build/bin", rcOptions) 56 | return { 57 | binDir: "/usr/lib/ninja-build/bin", 58 | installDir: "/usr/lib/ninja-build/", 59 | bin: "/usr/lib/ninja-build/bin/ninja", 60 | } 61 | } 62 | 63 | return setupBin("ninja", version, getNinjaPackageInfo, setupDir, arch) 64 | } 65 | -------------------------------------------------------------------------------- /src/opencppcoverage/__tests__/opencppcoverage.test.ts: -------------------------------------------------------------------------------- 1 | import { testBin } from "../../utils/tests/test-helpers.js" 2 | import { setupOpencppcoverage } from "../opencppcoverage.js" 3 | 4 | jest.setTimeout(300000) 5 | describe("setup-OpenCppCoverage", () => { 6 | if (process.platform !== "win32") { 7 | it.skip("should setup OpenCppCoverage", () => {}) 8 | return 9 | } 10 | it("should setup OpenCppCoverage", async () => { 11 | const installationInfo = await setupOpencppcoverage("", "", process.arch) 12 | 13 | await testBin("OpenCppCoverage", null, installationInfo?.binDir) // OpenCppCoverage exits with non-zero even with --help 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /src/opencppcoverage/opencppcoverage.ts: -------------------------------------------------------------------------------- 1 | import { addPath } from "envosman" 2 | import { rcOptions } from "../options.js" 3 | import { setupChocoPack } from "../utils/setup/setupChocoPack.js" 4 | 5 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 6 | export async function setupOpencppcoverage(version: string | undefined, _setupDir: string, _arch: string) { 7 | if (process.platform !== "win32") { 8 | return 9 | } 10 | await setupChocoPack("opencppcoverage", version) 11 | const binDir = await activateOpencppcoverage() 12 | return { binDir } 13 | } 14 | 15 | async function activateOpencppcoverage() { 16 | const binDir = "C:/Program Files/OpenCppCoverage" 17 | await addPath(binDir, rcOptions) 18 | return binDir 19 | } 20 | -------------------------------------------------------------------------------- /src/options.ts: -------------------------------------------------------------------------------- 1 | import { getInput } from "@actions/core" 2 | import type { AddPathOptions } from "envosman" 3 | import { untildifyUser } from "untildify-user" 4 | import type { Inputs } from "./tool.ts" 5 | import type { InstallationInfo } from "./utils/setup/setupBin.ts" 6 | 7 | /** 8 | * The options for the setup-cpp function 9 | */ 10 | export type Opts = Partial> & { 11 | "setup-cpp"?: boolean 12 | timeout?: string 13 | "node-package-manager"?: string 14 | } 15 | 16 | /** Get an object from github actions */ 17 | export function maybeGetInput(key: string) { 18 | const value = getInput(key.toLowerCase()) 19 | if (value !== "false" && value !== "") { 20 | return value 21 | } 22 | return undefined // skip installation 23 | } 24 | 25 | export function getSuccessMessage(tool: string, installationInfo: InstallationInfo | undefined | void) { 26 | let msg = `✅ ${tool} was installed successfully:` 27 | if (installationInfo === undefined) { 28 | return msg 29 | } 30 | if ("installDir" in installationInfo) { 31 | msg += `\n- The installation directory is ${installationInfo.installDir}` 32 | } 33 | if (installationInfo.binDir !== "") { 34 | msg += `\n- The binary directory is ${installationInfo.binDir}` 35 | } 36 | return msg 37 | } 38 | 39 | export const rcOptions: AddPathOptions = { 40 | rcPath: untildifyUser("~/.cpprc"), 41 | guard: "cpp", 42 | } 43 | -------------------------------------------------------------------------------- /src/powershell/__tests__/powershell.test.ts: -------------------------------------------------------------------------------- 1 | import ciInfo from "ci-info" 2 | const { GITHUB_ACTIONS } = ciInfo 3 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 4 | import { getVersion } from "../../versions/versions.js" 5 | import { setupPowershell } from "../powershell.js" 6 | 7 | jest.setTimeout(300000) 8 | describe("setup-powershell", () => { 9 | let directory: string 10 | beforeEach(async () => { 11 | directory = await setupTmpDir("powershell") 12 | process.env.CACHE_TOOLS = "true" 13 | }) 14 | 15 | it("should setup powershell", async () => { 16 | if (process.platform === "win32" && GITHUB_ACTIONS) { 17 | // results in errors 18 | return 19 | } 20 | 21 | const installInfo = await setupPowershell(getVersion("powershell", undefined), directory, process.arch) 22 | 23 | await testBin("pwsh", ["--version"], installInfo.binDir) 24 | }) 25 | 26 | afterEach(async () => { 27 | await cleanupTmpDir("ninja") 28 | }, 100000) 29 | }) 30 | -------------------------------------------------------------------------------- /src/python/__tests__/python.test.ts: -------------------------------------------------------------------------------- 1 | import ciInfo from "ci-info" 2 | const { GITHUB_ACTIONS } = ciInfo 3 | import { info } from "ci-log" 4 | import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" 5 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 6 | import { getVersion } from "../../versions/versions.js" 7 | import { setupPython } from "../python.js" 8 | 9 | jest.setTimeout(300000) 10 | describe("setup-python", () => { 11 | let directory: string 12 | beforeAll(async () => { 13 | directory = await setupTmpDir("python") 14 | }) 15 | 16 | it("should setup python in GitHub Actions", async () => { 17 | if (GITHUB_ACTIONS) { 18 | info("Installing python in GitHub Actions") 19 | const { setupActionsPython } = await import("../actions_python.js") 20 | await setupActionsPython(getVersion("python", "true", await ubuntuVersion()), directory, process.arch) 21 | 22 | await testBin("python", ["--version"]) 23 | } 24 | }) 25 | 26 | it("should setup python via system", async () => { 27 | process.env.CI = "false" 28 | process.env.GITHUB_ACTIONS = "false" 29 | 30 | const installInfo = await setupPython(getVersion("python", "true", await ubuntuVersion()), directory, process.arch) 31 | 32 | const python = process.platform === "darwin" ? "python3" : "python" 33 | await testBin(python, ["--version"], installInfo.binDir) 34 | }) 35 | 36 | afterAll(async () => { 37 | await cleanupTmpDir("python") 38 | }, 100000) 39 | }) 40 | -------------------------------------------------------------------------------- /src/python/python_matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "python", 5 | "pattern": [ 6 | { 7 | "regexp": "^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$", 8 | "file": 1, 9 | "line": 2 10 | }, 11 | { 12 | "regexp": "^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$", 13 | "message": 2 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/sccache/__tests__/sccache.test.ts: -------------------------------------------------------------------------------- 1 | import { hasAptGet } from "setup-apt" 2 | import { getUbuntuVersion } from "ubuntu-version" 3 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 4 | import { testBin } from "../../utils/tests/test-helpers.js" 5 | import { setupSccache } from "../sccache.js" 6 | 7 | jest.setTimeout(300000) 8 | describe("setup-sccache", () => { 9 | it("should setup sccache", async () => { 10 | const ubuntuVersion = hasAptGet() ? await getUbuntuVersion() : undefined 11 | const ubuntuVersionMajor = ubuntuVersion?.[0] ?? 0 12 | if (process.platform === "linux" && process.arch === "arm64" && ubuntuVersionMajor < 24) { 13 | return 14 | } 15 | 16 | const installInfo = await setupSccache("", "", process.arch) 17 | 18 | await testBin("sccache", ["--version"], (installInfo as InstallationInfo | undefined)?.binDir) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /src/sccache/sccache.ts: -------------------------------------------------------------------------------- 1 | import { hasApk, installApkPack } from "setup-alpine" 2 | import { hasAptGet, installAptPack } from "setup-apt" 3 | import { installBrewPack } from "setup-brew" 4 | import { getUbuntuVersion } from "ubuntu-version" 5 | import { setupChocoPack } from "../utils/setup/setupChocoPack.js" 6 | 7 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 8 | export async function setupSccache(version: string, _setupDir: string, _arch: string) { 9 | switch (process.platform) { 10 | case "win32": { 11 | return setupChocoPack("sccache", version) 12 | } 13 | case "linux": { 14 | if (hasAptGet()) { 15 | const ubuntuVersion = await getUbuntuVersion() 16 | if (ubuntuVersion[0] >= 24) { 17 | return installAptPack([{ name: "sccache", version }]) 18 | } 19 | } else if (await hasApk()) { 20 | return installApkPack([{ name: "sccache", version }]) 21 | } 22 | 23 | return installBrewPack("sccache", version) 24 | } 25 | case "darwin": { 26 | return installBrewPack("sccache", version) 27 | } 28 | default: { 29 | throw new Error("Unsupported platform") 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/setup-cpp-installer.ts: -------------------------------------------------------------------------------- 1 | import { info } from "ci-log" 2 | import { execa } from "execa" 3 | import which from "which" 4 | /** 5 | * Install the setup-cpp CLI globally 6 | * @param version - The version of setup-cpp to install 7 | * @param packageManager - The package manager to use 8 | */ 9 | export async function installSetupCpp(version: string, packageManager: string = "npm") { 10 | try { 11 | // check if `setup-cpp` is available in the shell, if so, skip the installation to avoid overwriting the existing version 12 | const setupCppPath = await which("setup-cpp", { nothrow: true }) 13 | if (setupCppPath !== null) { 14 | return `setup-cpp@${version} already installed` 15 | } 16 | 17 | // Install setup-cpp globally 18 | info(`Installing setup-cpp@${version} via ${packageManager}...`) 19 | await execa(packageManager, ["install", "-g", `setup-cpp@${version}`], { 20 | stdio: "inherit", 21 | // 1 minutes timeout 22 | timeout: 1000 * 60 * 1, 23 | }) 24 | return `setup-cpp@${version} installed successfully` 25 | } catch (err) { 26 | return new Error(`Failed to install the setup-cpp@${version} CLI: ${err}. Ignoring...`) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/sevenzip/__tests__/sevenzip.test.ts: -------------------------------------------------------------------------------- 1 | import type { InstallationInfo } from "../../utils/setup/setupBin.js" 2 | import { testBin } from "../../utils/tests/test-helpers.js" 3 | import { setupSevenZip } from "../sevenzip.js" 4 | 5 | jest.setTimeout(300000) 6 | describe("setup-7z", () => { 7 | it("should setup 7z", async () => { 8 | const installInfo = await setupSevenZip("", "", process.arch) 9 | 10 | await testBin("7z", ["--help"], (installInfo as InstallationInfo | undefined)?.binDir) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/sevenzip/sevenzip.ts: -------------------------------------------------------------------------------- 1 | import { hasApk, installApkPack } from "setup-alpine" 2 | import { hasAptGet, installAptPack } from "setup-apt" 3 | import { installBrewPack } from "setup-brew" 4 | import { hasDnf } from "../utils/env/hasDnf.js" 5 | import { isArch } from "../utils/env/isArch.js" 6 | import { setupChocoPack } from "../utils/setup/setupChocoPack.js" 7 | import { setupDnfPack } from "../utils/setup/setupDnfPack.js" 8 | import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js" 9 | 10 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 11 | export async function setupSevenZip(version: string, _setupDir: string, _arch: string) { 12 | switch (process.platform) { 13 | case "win32": { 14 | return setupChocoPack("7zip", version) 15 | } 16 | case "darwin": { 17 | return installBrewPack("p7zip", version) 18 | } 19 | case "linux": { 20 | if (isArch()) { 21 | return setupPacmanPack("p7zip", version) 22 | } else if (hasDnf()) { 23 | return setupDnfPack([ 24 | { name: "p7zip", version }, 25 | { name: "p7zip-plugins", version }, 26 | ]) 27 | } else if (hasAptGet()) { 28 | return installAptPack([{ name: "p7zip-full", version }]) 29 | } else if (await hasApk()) { 30 | return installApkPack([{ name: "p7zip", version }]) 31 | } 32 | throw new Error("Unsupported linux distribution") 33 | } 34 | default: { 35 | throw new Error("Unsupported platform") 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/task/__tests__/task.test.ts: -------------------------------------------------------------------------------- 1 | import ciInfo from "ci-info" 2 | const { GITHUB_ACTIONS } = ciInfo 3 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 4 | import { getVersion } from "../../versions/versions.js" 5 | import { setupTask } from "../task.js" 6 | 7 | jest.setTimeout(300000) 8 | describe("setup-task", () => { 9 | let directory: string 10 | beforeAll(async () => { 11 | directory = await setupTmpDir("task") 12 | process.env.CACHE_TOOLS = "true" 13 | }) 14 | 15 | it("should setup task", async () => { 16 | const { binDir } = await setupTask(getVersion("task", "true"), directory, process.arch) 17 | 18 | await testBin("task", ["--version"], binDir) 19 | }) 20 | 21 | it("should find task in the cache", async () => { 22 | const { binDir } = await setupTask(getVersion("task", "true"), directory, process.arch) 23 | if (GITHUB_ACTIONS) { 24 | expect(binDir).toMatch(process.env.RUNNER_TOOL_CACHE ?? "hostedtoolcache") 25 | } 26 | }) 27 | 28 | afterEach(async () => { 29 | await cleanupTmpDir("task") 30 | }, 100000) 31 | }) 32 | -------------------------------------------------------------------------------- /src/task/task.ts: -------------------------------------------------------------------------------- 1 | import { addExeExt } from "patha" 2 | import { type InstallationInfo, type PackageInfo, setupBin } from "../utils/setup/setupBin.js" 3 | 4 | /** Get the platform name task uses in their download links */ 5 | function getTaskPlatform(platform: NodeJS.Platform) { 6 | switch (platform) { 7 | case "win32": 8 | return "windows" 9 | default: 10 | return platform 11 | } 12 | } 13 | 14 | /** Get the arch name task uses in their download links */ 15 | function getTaskArch(arch: string) { 16 | switch (arch) { 17 | case "x64": 18 | return "amd64" 19 | case "ia32": 20 | case "x86": 21 | case "i386": 22 | case "x32": 23 | return "386" 24 | default: 25 | return arch 26 | } 27 | } 28 | 29 | /** Get the platform data for task */ 30 | function getTaskPackageInfo(version: string, platform: NodeJS.Platform, arch: string): PackageInfo { 31 | const taskPlatform = getTaskPlatform(platform) 32 | const taskArch = getTaskArch(arch) 33 | const extension = platform === "win32" ? "zip" : "tar.gz" 34 | return { 35 | binRelativeDir: "", 36 | binFileName: addExeExt("task"), 37 | extractedFolderName: "", 38 | url: `https://github.com/go-task/task/releases/download/v${version}/task_${taskPlatform}_${taskArch}.${extension}`, 39 | } 40 | } 41 | 42 | export function setupTask(version: string, setupDir: string, arch: string): Promise { 43 | return setupBin("task", version, getTaskPackageInfo, setupDir, arch) 44 | } 45 | -------------------------------------------------------------------------------- /src/types/numerous.d.ts: -------------------------------------------------------------------------------- 1 | declare module "numerous" { 2 | export type Locale = unknown 3 | 4 | /** Adds pluralization data for the specified locale. Should be called in browser. */ 5 | export function addLocale(localeData: Locale | Locale[]) 6 | } 7 | 8 | declare module "numerous/locales/en.js" { 9 | import { Locale } from "numerous" 10 | 11 | declare const En = Locale 12 | export = En 13 | } 14 | -------------------------------------------------------------------------------- /src/types/time-delta.d.ts: -------------------------------------------------------------------------------- 1 | declare module "time-delta/locales/en.js" { 2 | import { Locale } from "time-delta" 3 | 4 | declare const En = Locale 5 | export = En 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/compat/crypto/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line node/no-deprecated-api 2 | export * from "crypto" 3 | import * as crypto from "crypto" 4 | export default crypto 5 | 6 | export { randomUUID } from "./randomuuid.mjs" 7 | -------------------------------------------------------------------------------- /src/utils/compat/crypto/randomuuid.mjs: -------------------------------------------------------------------------------- 1 | // from https://www.npmjs.com/package/randomuuid-polyfill 2 | 3 | import { performance } from "perf_hooks" 4 | 5 | // Adapted from https://stackoverflow.com/a/8809472/2993077 6 | if (!global.crypto?.randomUUID) { 7 | if (!global.crypto) { 8 | global.crypto = {} 9 | } 10 | 11 | global.crypto.randomUUID = () => { 12 | let date = new Date().getTime() 13 | let performanceNow = performance.now() * 1000 14 | 15 | // cspell:disable-next-line 16 | return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, char => { 17 | let random = Math.random() * 16 18 | if (date > 0) { 19 | random = (date + random) % 16 | 0 20 | date = Math.floor(date / 16) 21 | } else { 22 | random = (performanceNow + random) % 16 | 0 23 | performanceNow = Math.floor(performanceNow / 16) 24 | } 25 | return (char === "x" ? random : (random & 0x3 | 0x8)).toString(16) 26 | }) 27 | } 28 | } 29 | 30 | const randomUUID = global.crypto.randomUUID.bind(global.crypto) 31 | export { randomUUID } 32 | -------------------------------------------------------------------------------- /src/utils/compat/crypto/type.d.ts: -------------------------------------------------------------------------------- 1 | declare module "randomuuid-polyfill" { 2 | export function randomUUID(): string 3 | } 4 | -------------------------------------------------------------------------------- /src/utils/compat/fs/promises.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs" 2 | export default fs.promises 3 | 4 | export const { 5 | access, 6 | appendFile, 7 | chmod, 8 | chown, 9 | copyFile, 10 | lchmod, 11 | lchown, 12 | link, 13 | lstat, 14 | mkdir, 15 | mkdtemp, 16 | open, 17 | readdir, 18 | readFile, 19 | readlink, 20 | realpath, 21 | rename, 22 | rmdir, 23 | stat, 24 | symlink, 25 | truncate, 26 | unlink, 27 | utimes, 28 | writeFile, 29 | } = fs.promises 30 | 31 | import { promisify } from "util" 32 | export const rm = "rm" in fs.promises 33 | ? ( 34 | fs.promises as typeof fs.promises & { 35 | rm: (path: string, options?: fs.RmDirOptions) => Promise 36 | } 37 | ).rm 38 | : promisify(fs.unlink) 39 | -------------------------------------------------------------------------------- /src/utils/compat/fs/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module "fs/promises" { 2 | import * as fs from "fs" 3 | export default fs.promises 4 | 5 | export const { 6 | access, 7 | appendFile, 8 | chmod, 9 | chown, 10 | copyFile, 11 | lchmod, 12 | lchown, 13 | link, 14 | lstat, 15 | mkdir, 16 | mkdtemp, 17 | open, 18 | readdir, 19 | readFile, 20 | readlink, 21 | realpath, 22 | rename, 23 | rmdir, 24 | stat, 25 | symlink, 26 | truncate, 27 | unlink, 28 | utimes, 29 | writeFile, 30 | } = fs.promises 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/compat/node.d.ts: -------------------------------------------------------------------------------- 1 | // the installed @types/node package is version 12 so that backwards compatibility is maintained 2 | // node: prefix is removed by Babel, so define the types of those packages here so that TypeScript can find them 3 | 4 | declare module "node:fs" { 5 | import fs from "fs" 6 | export = fs 7 | } 8 | 9 | declare module "node:path" { 10 | import path from "path" 11 | export = path 12 | } 13 | 14 | declare module "node:child_process" { 15 | import child_process from "child_process" 16 | export = child_process 17 | } 18 | 19 | declare module "node:os" { 20 | import os from "os" 21 | export = os 22 | } 23 | 24 | declare module "node:util" { 25 | import util from "util" 26 | export = util 27 | } 28 | 29 | declare module "node:stream" { 30 | import stream from "stream" 31 | export = stream 32 | } 33 | 34 | declare module "node:zlib" { 35 | import zlib from "zlib" 36 | export = zlib 37 | } 38 | 39 | declare module "node:crypto" { 40 | import crypto from "crypto" 41 | export = crypto 42 | } 43 | declare module "node:http" { 44 | import http from "http" 45 | export = http 46 | } 47 | 48 | declare module "node:https" { 49 | import https from "https" 50 | export = https 51 | } 52 | 53 | declare module "node:events" { 54 | import events from "events" 55 | export = events 56 | } 57 | 58 | declare module "node:assert" { 59 | import assert from "assert" 60 | export = assert 61 | } 62 | 63 | declare module "node:constants" { 64 | import constants from "constants" 65 | export = constants 66 | } 67 | 68 | declare module "node:querystring" { 69 | import querystring from "querystring" 70 | export = querystring 71 | } 72 | 73 | declare module "node:url" { 74 | import url from "url" 75 | export = url 76 | } 77 | 78 | declare module "node:fs/promises" { 79 | import fsPromises from "fs/promises" 80 | export = fsPromises 81 | } 82 | 83 | declare module "node:path/posix" { 84 | import pathPosix from "path/posix" 85 | export = pathPosix 86 | } 87 | 88 | declare module "node:path/win32" { 89 | import pathWin32 from "path/win32" 90 | export = pathWin32 91 | } 92 | -------------------------------------------------------------------------------- /src/utils/compat/stream/promises.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-types */ 2 | /* eslint-disable @typescript-eslint/no-explicit-any */ 3 | 4 | import * as stream from "stream" 5 | import { promisify } from "util" 6 | 7 | // biome-ignore lint: lint/complexity/noBannedTypes 8 | export const pipeline = "promises" in stream && "pipeline" in (stream as any).promises 9 | // biome-ignore lint: lint/complexity/noBannedTypes 10 | ? ((stream.promises as any).pipeline as Function) 11 | : promisify(stream.pipeline) 12 | 13 | // biome-ignore lint: lint/complexity/noBannedTypes 14 | export const finished = "promises" in stream && "finished" in (stream as any).promises 15 | // biome-ignore lint: lint/complexity/noBannedTypes 16 | ? ((stream.promises as any).finished as Function) 17 | : promisify(stream.finished) 18 | -------------------------------------------------------------------------------- /src/utils/env/arch.ts: -------------------------------------------------------------------------------- 1 | export const x86_64 = ["x64", "amd64", "x86_64", "win64", "64", "amd64_x86", "amd64_arm64"] 2 | export const x86 = ["x86", "i386", "ia32", "win32", "32", "x32"] 3 | export const arm64 = ["aarch64", "arm64", "woa64", "arm"] 4 | export const armv7 = ["armv7", "armv7a"] 5 | export const powerpc64le = ["powerpc64le", "ppc64le"] 6 | export const sparc64 = ["sparc64"] 7 | export const sparcv9 = ["sparcv9"] 8 | 9 | export function getDebArch(arch: string) { 10 | if (arm64.includes(arch)) { 11 | return "arm64" 12 | } else if (x86_64.includes(arch)) { 13 | return "amd64" 14 | } else { 15 | return arch 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/env/hasDnf.ts: -------------------------------------------------------------------------------- 1 | import which from "which" 2 | 3 | let hasDnfCache: undefined | boolean = undefined 4 | 5 | export function hasDnf(): boolean { 6 | if (process.platform !== "linux") { 7 | return false 8 | } 9 | if (hasDnfCache === undefined) { 10 | hasDnfCache = which.sync("dnf", { nothrow: true }) !== null 11 | } 12 | 13 | return hasDnfCache 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/env/isArch.ts: -------------------------------------------------------------------------------- 1 | import which from "which" 2 | 3 | let isArchCache: undefined | boolean = undefined 4 | 5 | export function isArch(): boolean { 6 | if (process.platform !== "linux") { 7 | return false 8 | } 9 | if (isArchCache === undefined) { 10 | // detect arch by checking if pacman exists 11 | isArchCache = which.sync("pacman", { nothrow: true }) !== null 12 | } 13 | 14 | return isArchCache 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/env/macos_version.ts: -------------------------------------------------------------------------------- 1 | import macosRelease from "macos-release" 2 | import memoize from "memoizee" 3 | 4 | /** 5 | * Get macOS version 6 | * 7 | * @returns {number[]} - The macOS version as an array of numbers 8 | */ 9 | function macosVersion_() { 10 | if (process.platform !== "darwin") { 11 | return [] 12 | } 13 | 14 | const { version } = macosRelease() 15 | return version.split(".").map((v) => Number.parseInt(v, 10)) 16 | } 17 | export const macosVersion = memoize(macosVersion_) 18 | -------------------------------------------------------------------------------- /src/utils/env/ubuntu_version.ts: -------------------------------------------------------------------------------- 1 | import os from "os" 2 | import { warning } from "ci-log" 3 | import memoize from "memoizee" 4 | import { hasAptGet, installAptPack } from "setup-apt" 5 | import { getUbuntuVersion } from "ubuntu-version" 6 | import which from "which" 7 | 8 | async function ubuntuVersion_(): Promise { 9 | try { 10 | if (hasAptGet()) { 11 | try { 12 | if (which.sync("lsb_release", { nothrow: true }) === null) { 13 | await installAptPack([{ name: "lsb-release" }]) 14 | } 15 | } catch { 16 | return detectUsingOsVersion() 17 | } 18 | 19 | const versionSplitted = await getUbuntuVersion() 20 | 21 | if (versionSplitted.length === 0) { 22 | return detectUsingOsVersion() 23 | } 24 | 25 | return versionSplitted 26 | } else { 27 | return null 28 | } 29 | } catch (err) { 30 | warning((err as Error).toString()) 31 | return null 32 | } 33 | } 34 | 35 | /** Detect Ubuntu version */ 36 | export const ubuntuVersion = memoize(ubuntuVersion_, { promise: true }) 37 | 38 | /** Detect Ubuntu version using os.version() for Ubuntu based distros */ 39 | function detectUsingOsVersion() { 40 | if (!("version" in os && typeof os.version === "function")) { 41 | return null 42 | } 43 | 44 | // #40~22.04.3-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 30 17:30:19 UTC 2 45 | const osVersion: string = os.version() 46 | // parse the version 47 | const versionMatch = osVersion.match(/(\d+)\.(\d+)\.(\d+)/) 48 | if (versionMatch === null) { 49 | return null 50 | } 51 | 52 | const majorVersion = Number.parseInt(versionMatch[1], 10) 53 | const minorVersion = Number.parseInt(versionMatch[2], 10) 54 | const patchVersion = Number.parseInt(versionMatch[3], 10) 55 | 56 | return [majorVersion, minorVersion, patchVersion] 57 | } 58 | -------------------------------------------------------------------------------- /src/utils/setup/setupChocoPack.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable require-atomic-updates */ 2 | import { info } from "ci-log" 3 | import { addPath } from "envosman" 4 | import { execaSync } from "execa" 5 | import which from "which" 6 | import { setupChocolatey } from "../../chocolatey/chocolatey.js" 7 | import { rcOptions } from "../../options.js" 8 | import type { InstallationInfo } from "./setupBin.js" 9 | 10 | let hasChoco = false 11 | 12 | /** A function that installs a package using choco */ 13 | export async function setupChocoPack(name: string, version?: string, args: string[] = []): Promise { 14 | info(`Installing ${name} ${version ?? ""} via chocolatey`) 15 | 16 | if (!hasChoco || which.sync("choco", { nothrow: true }) === null) { 17 | await setupChocolatey("", "", process.arch) 18 | hasChoco = true 19 | } 20 | 21 | // https://github.com/jberezanski/ChocolateyPackages/issues/97#issuecomment-986825694 22 | const PATH = process.env.PATH 23 | const env = { ...process.env } 24 | env.TMP = undefined 25 | env.TEMP = undefined 26 | env.Path = undefined 27 | env.PATH = PATH 28 | 29 | if (version !== undefined && version !== "") { 30 | execaSync("choco", ["install", "-y", name, `--version=${version}`, ...args], { 31 | env, 32 | extendEnv: false, 33 | stdio: "inherit", 34 | }) 35 | } else { 36 | try { 37 | execaSync("choco", ["install", "-y", name, ...args], { env, extendEnv: false, stdio: "inherit" }) 38 | } catch (err) { 39 | // if the package requires a reboot, downgrade the error to a notice 40 | if ((err as Error).message.includes("exit code 3010")) { 41 | info(`${name} might require a reboot for the completion of the installation.`) 42 | } else { 43 | throw err 44 | } 45 | } 46 | } 47 | 48 | const binDir = `${process.env.ChocolateyInstall ?? "C:/ProgramData/chocolatey"}/bin` 49 | await addPath(binDir, rcOptions) 50 | 51 | return { binDir } 52 | } 53 | -------------------------------------------------------------------------------- /src/utils/setup/setupDmg.ts: -------------------------------------------------------------------------------- 1 | import { join } from "path" 2 | 3 | export async function setupDmg(path: string, destDir: string) { 4 | const { ArchiveHdi } = await import("@shockpkg/archive-files") 5 | 6 | const dmg = new ArchiveHdi(path) 7 | await dmg.read(async (entry) => { 8 | await entry.extract(join(destDir, entry.path)) 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/setup/setupDnfPack.ts: -------------------------------------------------------------------------------- 1 | import { execRootSync } from "admina" 2 | import { info, warning } from "ci-log" 3 | import { execa } from "execa" 4 | import type { InstallationInfo } from "./setupBin.js" 5 | 6 | type DnfPackage = { 7 | name: string 8 | version?: string 9 | } 10 | 11 | /** A function that installs a package using dnf */ 12 | export async function setupDnfPack(packages: DnfPackage[]): Promise { 13 | for (const { name, version } of packages) { 14 | info(`Installing ${name} ${version ?? ""} via dnf`) 15 | } 16 | 17 | const dnfArgs = await Promise.all(packages.map((pack) => getDnfArg(pack.name, pack.version))) 18 | execRootSync("dnf", ["-y", "install", ...dnfArgs]) 19 | 20 | return { binDir: "/usr/bin/" } 21 | } 22 | 23 | async function getDnfArg(name: string, version: string | undefined) { 24 | if (version === undefined || version === "") { 25 | return name 26 | } 27 | 28 | // check if name-version is available 29 | const { stdout: nameDashVersionSearch } = await execa("dnf", ["search", "-q", `${name}-${version}`]) 30 | if (nameDashVersionSearch.trim() !== "") { 31 | return `${name}-${version}` 32 | } 33 | 34 | // try with ${name}${version} 35 | const { stdout: nameVersionSearch } = await execa("dnf", ["search", "-q", `${name}${version}`]) 36 | if (nameVersionSearch.trim() !== "") { 37 | return `${name}${version}` 38 | } 39 | 40 | warning(`Failed to install ${name} ${version} via dnf, trying without version`) 41 | return name 42 | } 43 | -------------------------------------------------------------------------------- /src/utils/std/index.ts: -------------------------------------------------------------------------------- 1 | export function unique(dirs: string[]) { 2 | return [...new Set(dirs)] 3 | } 4 | 5 | export function quoteIfHasSpace(str: string, quoteChar = "\"") { 6 | return str.includes(" ") ? `${quoteChar}${str}${quoteChar}` : str 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/tests/test-helpers.ts: -------------------------------------------------------------------------------- 1 | import { tmpdir } from "os" 2 | import * as path from "path" 3 | import { join } from "path" 4 | import * as io from "@actions/io" 5 | import spawn from "cross-spawn" 6 | import { addExeExt } from "patha" 7 | 8 | import { pathExists } from "path-exists" 9 | 10 | export async function setupTmpDir(testName: string, useSpaces: boolean = false) { 11 | const tempName = useSpaces ? "setup cpp temp" : "setup-cpp-temp" 12 | 13 | const tempDirectory = path.join(tmpdir(), tempName, testName) 14 | try { 15 | await io.rmRF(tempDirectory) 16 | await io.mkdirP(tempDirectory) 17 | } catch { 18 | console.log("Failed to remove test directories") 19 | } 20 | // eslint-disable-next-line require-atomic-updates 21 | process.env.SETUP_CPP_DIR = tempDirectory 22 | return tempDirectory 23 | } 24 | 25 | export async function cleanupTmpDir(testName: string) { 26 | if (process.env.SETUP_CPP_DIR !== undefined) { 27 | const tempDirectory = path.join(process.env.SETUP_CPP_DIR, testName) 28 | 29 | try { 30 | await io.rmRF(tempDirectory) 31 | } catch { 32 | console.log("Failed to remove test directories") 33 | } 34 | } 35 | } 36 | 37 | export async function testBin( 38 | name: string, 39 | args: string[] | null = ["--version"], 40 | binDir: string | undefined = undefined, 41 | ) { 42 | try { 43 | let bin = name 44 | if (typeof binDir === "string") { 45 | console.log(`Testing the existence of ${binDir}`) 46 | expect(binDir).toBeDefined() 47 | expect(binDir).not.toHaveLength(0) 48 | expect(await pathExists(binDir)).toBeTruthy() 49 | bin = join(binDir, addExeExt(name)) 50 | } 51 | 52 | if (args !== null) { 53 | console.log(`Running ${bin} ${args.join(" ")}`) 54 | const { status } = spawn.sync(bin, args, { stdio: "inherit" }) 55 | expect(status).toBe(0) 56 | } 57 | 58 | expect((await io.which(name, true)).includes(bin)) 59 | } catch (err) { 60 | throw new Error(`Failed to test bin ${name}: ${err}`) 61 | } 62 | } 63 | 64 | export function runnerWindowsVersion() { 65 | if (process.platform !== "win32") { 66 | return undefined 67 | } 68 | const maybeVersionString = process.env.RUNNER_OS_NAME?.split("-")[1] 69 | if (maybeVersionString === undefined) { 70 | return undefined 71 | } 72 | 73 | return Number.parseInt(maybeVersionString, 10) 74 | } 75 | -------------------------------------------------------------------------------- /src/vcpkg/__tests__/vcpkg.test.ts: -------------------------------------------------------------------------------- 1 | import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" 2 | import { setupVcpkg } from "../vcpkg.js" 3 | 4 | jest.setTimeout(300000) 5 | 6 | describe("setup-vcpkg", () => { 7 | let directory: string 8 | beforeEach(async () => { 9 | // TODO setup-vcpkg bootstrap fails on Linux arm64 with spaces in the path 10 | const noSpaces = process.platform === "linux" && process.arch === "arm64" 11 | directory = await setupTmpDir("vcpkg", !noSpaces) 12 | }) 13 | 14 | it("should setup vcpkg", async () => { 15 | console.log(!("true" in ["", "true"])) 16 | const { binDir } = await setupVcpkg("", directory, "") 17 | await testBin("vcpkg", ["--version"], binDir) 18 | return binDir 19 | }) 20 | 21 | it("should setup vcpkg with specific version", async () => { 22 | const { binDir } = await setupVcpkg("e590c2b30c08caf1dd8d612ec602a003f9784b7d", directory, "") 23 | await testBin("vcpkg", ["--version"], binDir) 24 | return binDir 25 | }) 26 | 27 | afterEach(async () => { 28 | await cleanupTmpDir(directory) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /src/vcvarsall/vcvarsall.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 2 | // @ts-ignore 3 | import { info } from "ci-log" 4 | import { addEnv } from "envosman" 5 | import { setupMSVCDevCmd } from "msvc-dev-cmd/lib.js" 6 | import { pathExists } from "path-exists" 7 | import { rcOptions } from "../options.js" 8 | 9 | function getArch(arch: string): string { 10 | switch (arch) { 11 | case "x32": 12 | case "32": 13 | case "ia32": { 14 | return "x86" 15 | } 16 | case "64": { 17 | return "x64" 18 | } 19 | default: { 20 | return arch 21 | } 22 | } 23 | } 24 | 25 | export async function setupVCVarsall( 26 | vsversion: string, 27 | VCTargetsPath: string | undefined, 28 | arch: string, 29 | toolset: string | undefined, 30 | sdk?: string, 31 | uwp?: boolean, 32 | spectre?: boolean, 33 | ) { 34 | if (VCTargetsPath !== undefined && (await pathExists(VCTargetsPath))) { 35 | info(`Adding ${VCTargetsPath} to PATH`) 36 | await addEnv("VCTargetsPath", VCTargetsPath, rcOptions) 37 | } 38 | 39 | await setupMSVCDevCmd(getArch(arch), sdk, toolset, uwp, spectre, vsversion) 40 | } 41 | -------------------------------------------------------------------------------- /swc.legacy.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "sourceMaps": true, 4 | "jsc": { 5 | "target": "es2019", 6 | "transform": { 7 | "optimizer": { 8 | "simplify": true, 9 | "jsonify": { 10 | "minCost": 1000 11 | } 12 | } 13 | }, 14 | "parser": { 15 | "syntax": "typescript", 16 | "tsx": false, 17 | "dynamicImport": true 18 | } 19 | }, 20 | "minify": true, 21 | "module": { 22 | "type": "commonjs" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /swc.modern.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "sourceMaps": true, 4 | "jsc": { 5 | "target": "esnext", 6 | "transform": { 7 | "optimizer": { 8 | "simplify": true, 9 | "jsonify": { 10 | "minCost": 1000 11 | } 12 | } 13 | }, 14 | "parser": { 15 | "syntax": "typescript", 16 | "tsx": false, 17 | "dynamicImport": true 18 | }, 19 | "baseUrl": ".", 20 | "paths": { 21 | "./lib.js": ["./lib.mjs"] 22 | } 23 | }, 24 | "minify": true, 25 | "module": { 26 | "type": "nodenext", 27 | "outFileExtension": "mjs" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "strictNullChecks": true, 5 | "noUnusedLocals": false, 6 | "noUnusedParameters": true, 7 | "noImplicitReturns": true, 8 | "noImplicitAny": true, 9 | "noImplicitThis": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "declaration": true, 13 | "emitDecoratorMetadata": true, 14 | "experimentalDecorators": true, 15 | "incremental": true, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "preserveSymlinks": true, 19 | "removeComments": false, 20 | "skipLibCheck": true, 21 | "allowImportingTsExtensions": true, 22 | "noEmit": true, 23 | "lib": [ 24 | // target Node.js 12 https://node.green/#ES2019 25 | "ES2019", 26 | // https://node.green/#ES2020-features-String-prototype-matchAll 27 | "ES2020.String" 28 | ], 29 | "target": "ESNext", 30 | "allowJs": true, 31 | "esModuleInterop": true, 32 | "resolveJsonModule": true, 33 | "module": "ESNext", 34 | "moduleResolution": "node", 35 | "importHelpers": false, 36 | "outDir": "./dist" 37 | }, 38 | "compileOnSave": false, 39 | "include": [ 40 | "./src", 41 | "./dev/", 42 | "./*.ts", 43 | "jest.config.mjs", 44 | "vite.config.mts" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /tsconfig.types.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": true, 5 | "noEmit": false, 6 | "declarationDir": "./dist/library", 7 | "rootDir": "./src", 8 | "incremental": false 9 | }, 10 | "include": [ 11 | "./src/**/*.ts" 12 | ], 13 | "exclude": [ 14 | "./src/**/*.test.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "globalEnv": ["OS", "RUNNER_OS", "NODE_ENV"], 4 | "cacheDir": ".cache/turbo", 5 | "tasks": { 6 | "build": { 7 | "dependsOn": ["^build"] 8 | }, 9 | "dev": { 10 | "persistent": true, 11 | "cache": false 12 | }, 13 | "lint.tsc": { 14 | "dependsOn": ["build", "^lint.tsc", "lint.tsc.test"] 15 | }, 16 | "lint.tsc.test": { 17 | "dependsOn": ["^lint.tsc.test"] 18 | }, 19 | "lint.eslint": { 20 | "dependsOn": ["^lint.eslint"] 21 | }, 22 | "lint": { 23 | "dependsOn": ["lint.tsc", "lint.eslint"] 24 | }, 25 | "test": { 26 | "dependsOn": ["build", "^test"] 27 | } 28 | }, 29 | "ui": "stream" 30 | } 31 | -------------------------------------------------------------------------------- /vite.config.mts: -------------------------------------------------------------------------------- 1 | import module from "module" 2 | import { type AliasOptions, type TerserOptions, defineConfig } from "vite" 3 | import babel from "vite-plugin-babel" 4 | import terserRc from "./.terserrc.mjs" 5 | import babelConfig from "./babel.config.mts" 6 | 7 | const viteConfig = defineConfig((configEnv) => { 8 | const isLegacy = configEnv.mode.includes("legacy") 9 | 10 | const plugins = [] 11 | if (isLegacy) { 12 | plugins.push( 13 | babel({ 14 | babelConfig, 15 | }), 16 | ) 17 | } 18 | 19 | let aliasOpts: AliasOptions = {} 20 | if (isLegacy) { 21 | aliasOpts = { 22 | ...aliasOpts, 23 | "fs/promises": "./src/utils/compat/fs/promises.ts", 24 | "stream/promises": "./src/utils/compat/stream/promises.ts", 25 | "stream/web": "web-streams-polyfill/dist/ponyfill.mjs", 26 | "util/types": "util.types/index.js", 27 | "timers/promises": "timers-browserify", 28 | diagnostics_channel: "diagnostics_channel/index.js", 29 | crypto: "./src/utils/compat/crypto/index.ts", 30 | } 31 | } 32 | 33 | const isLibrary = configEnv.mode.includes("library") 34 | return { 35 | build: { 36 | ssr: isLibrary 37 | ? "./src/lib.ts" 38 | : "./src/setup-cpp.ts", 39 | outDir: isLibrary ? "./dist/library" : isLegacy ? "./dist/legacy" : "./dist/modern", 40 | target: isLegacy ? "node12" : "node20", 41 | minify: process.env.NODE_ENV === "production" ? "terser" : false, 42 | terserOptions: terserRc as TerserOptions, 43 | sourcemap: true, 44 | rollupOptions: { 45 | output: { 46 | format: isLegacy 47 | ? "cjs" 48 | : "es", 49 | }, 50 | }, 51 | emptyOutDir: true, 52 | }, 53 | resolve: { 54 | alias: aliasOpts, 55 | }, 56 | ssr: { 57 | target: "node", 58 | noExternal: true, 59 | external: [...module.builtinModules], 60 | }, 61 | plugins, 62 | } 63 | }) 64 | 65 | export default viteConfig 66 | --------------------------------------------------------------------------------