diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31e74563..fa93e0e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,22 +121,22 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Test all shells listed in https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell - name: Test bash - run: just --version && shfmt --version && protoc --version + run: just --version && shfmt --version && protoc --version && rustc --version && cargo --version && rustup --version shell: bash - name: Test sh - run: just --version && shfmt --version && protoc --version + run: just --version && shfmt --version && protoc --version && rustc --version && cargo --version && rustup --version shell: sh if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') - name: Test pwsh - run: just --version; shfmt --version; protoc --version + run: just --version; shfmt --version; protoc --version; rustc --version; cargo --version; rustup --version shell: pwsh if: matrix.os != 'ubuntu-slim' - name: Test powershell - run: just --version; shfmt --version; protoc --version + run: just --version; shfmt --version; protoc --version; rustc --version; cargo --version; rustup --version shell: powershell if: startsWith(matrix.os, 'windows') - name: Test cmd - run: just --version & shfmt --version & protoc --version + run: just --version & shfmt --version & protoc --version & rustc --version & cargo --version & rustup --version shell: cmd # zizmor: ignore[misfeature] used for compatibility testing if: startsWith(matrix.os, 'windows') # We use the version output to check the version of cargo-binstall, but they @@ -300,6 +300,10 @@ jobs: env: CONTAINER: ${{ matrix.container }} if: startsWith(matrix.container, 'centos') + # gcc for rustc + - run: apk --no-cache add gcc + shell: sh + if: startsWith(matrix.container, 'alpine') - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 # cross attempts to install rust-src when Cargo.toml is available even if `cross --version` - run: rm -- Cargo.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 04e674ae..0c9ad291 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [Unreleased] +- Support `rust`. ([#1779](https://github.com/taiki-e/install-action/pull/1779)) + + This installs rust using rustup. + + If rustup is not yet installed, this action downloads [rustup-init for the current platform](https://rust-lang.github.io/rustup/installation/other.html#manual-installation) using HTTPS with tlsv1.2+, verifies SHA256 checksum, and then installs rustup using it. + ## [2.76.0] - 2026-05-04 - Support `mdbook-d2`. ([#1737](https://github.com/taiki-e/install-action/pull/1737), thanks @nhu) diff --git a/README.md b/README.md index a8dcb0cc..41d81e12 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,11 @@ When installing with `tool: ` or `tool: @ ### Security on other installation methods -See the linked documentation for information on security when installed using [snap](https://snapcraft.io/docs) or [cargo-binstall](https://github.com/cargo-bins/cargo-binstall#faq). +See the linked documentation for information on security when installed using [rustup](https://rust-lang.github.io/rustup/security.html), [snap](https://snapcraft.io/docs), or [cargo-binstall](https://github.com/cargo-bins/cargo-binstall#faq). + +If the installation method is rustup and rustup is not yet installed, this action downloads [rustup-init for the current platform](https://rust-lang.github.io/rustup/installation/other.html#manual-installation) using HTTPS with tlsv1.2+, verifies SHA256 checksum, and then installs rustup using it. + +If the installation method is cargo-binstall and cargo-binstall is not yet installed or outdated, this action installs cargo-binstall [from GitHub Releases](#security-on-installation-from-github-releases). See the [Supported tools section](#supported-tools) for how to ensure that fallback is not used. diff --git a/TOOLS.md b/TOOLS.md index 3642b744..03f8e420 100644 --- a/TOOLS.md +++ b/TOOLS.md @@ -17,9 +17,9 @@ See the [Supported tools section in README.md](README.md#supported-tools) for ho | [**auto-doc**](https://github.com/tj-actions/auto-doc) | `$HOME/.install-action/bin` | [GitHub Releases](https://github.com/tj-actions/auto-doc/releases) | Linux, macOS, Windows | [Apache-2.0](https://github.com/tj-actions/auto-doc/blob/main/LICENSE) | | [**biome**](https://biomejs.dev) | `$HOME/.install-action/bin` | [GitHub Releases](https://github.com/biomejs/biome/releases) | Linux, macOS, Windows | [Apache-2.0](https://github.com/biomejs/biome/blob/main/LICENSE-APACHE) OR [MIT](https://github.com/biomejs/biome/blob/main/LICENSE-MIT) | | [**cargo-apple-runner**](https://github.com/madsmtm/cargo-apple-runner) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/madsmtm/cargo-apple-runner/releases) | macOS | [Zlib](https://github.com/madsmtm/cargo-apple-runner/blob/main/LICENSE-ZLIB.txt) OR [Apache-2.0](https://github.com/madsmtm/cargo-apple-runner/blob/main/LICENSE-APACHE.txt) OR [MIT](https://github.com/madsmtm/cargo-apple-runner/blob/main/LICENSE-MIT.txt) | -| [**cargo-audit**](https://github.com/rustsec/rustsec/tree/HEAD/cargo-audit) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/rustsec/rustsec/releases) | Linux, macOS, Windows | [Apache-2.0](https://github.com/rustsec/rustsec/blob/HEAD/cargo-audit/LICENSE-APACHE) OR [MIT](https://github.com/rustsec/rustsec/blob/HEAD/cargo-audit/LICENSE-MIT) | -| [**cargo-auditable**](https://github.com/rust-secure-code/cargo-auditable) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/rust-secure-code/cargo-auditable/releases) | Linux, macOS, Windows | [Apache-2.0](https://github.com/rust-secure-code/cargo-auditable/blob/HEAD/LICENSE-APACHE) OR [MIT](https://github.com/rust-secure-code/cargo-auditable/blob/HEAD/LICENSE-MIT) | -| [**cargo-binstall**](https://github.com/cargo-bins/cargo-binstall) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/cargo-bins/cargo-binstall/releases) | Linux, macOS, Windows | [GPL-3.0](https://github.com/cargo-bins/cargo-binstall/blob/HEAD/crates/bin/LICENSE) | +| [**cargo-audit**](https://github.com/rustsec/rustsec/tree/HEAD/cargo-audit) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/rustsec/rustsec/releases) | Linux, macOS, Windows | [Apache-2.0](https://github.com/rustsec/rustsec/blob/main/cargo-audit/LICENSE-APACHE) OR [MIT](https://github.com/rustsec/rustsec/blob/main/cargo-audit/LICENSE-MIT) | +| [**cargo-auditable**](https://github.com/rust-secure-code/cargo-auditable) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/rust-secure-code/cargo-auditable/releases) | Linux, macOS, Windows | [Apache-2.0](https://github.com/rust-secure-code/cargo-auditable/blob/master/LICENSE-APACHE) OR [MIT](https://github.com/rust-secure-code/cargo-auditable/blob/master/LICENSE-MIT) | +| [**cargo-binstall**](https://github.com/cargo-bins/cargo-binstall) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/cargo-bins/cargo-binstall/releases) | Linux, macOS, Windows | [GPL-3.0](https://github.com/cargo-bins/cargo-binstall/blob/main/crates/bin/LICENSE) | | [**cargo-careful**](https://github.com/RalfJung/cargo-careful) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/RalfJung/cargo-careful/releases) | Linux, macOS, Windows | [MIT](https://github.com/RalfJung/cargo-careful/blob/master/LICENSE-MIT) OR [Apache-2.0](https://github.com/RalfJung/cargo-careful/blob/master/LICENSE-APACHE) | | [**cargo-cyclonedx**](https://github.com/CycloneDX/cyclonedx-rust-cargo) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/CycloneDX/cyclonedx-rust-cargo/releases) | Linux, macOS, Windows | [Apache-2.0](https://github.com/CycloneDX/cyclonedx-rust-cargo/blob/main/LICENSE) | | [**cargo-deadlinks**](https://github.com/deadlinks/cargo-deadlinks) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/deadlinks/cargo-deadlinks/releases) | Linux, macOS, Windows | [MIT](https://github.com/deadlinks/cargo-deadlinks/blob/master/LICENSE-MIT) OR [Apache-2.0](https://github.com/deadlinks/cargo-deadlinks/blob/master/LICENSE-APACHE) | @@ -80,6 +80,7 @@ See the [Supported tools section in README.md](README.md#supported-tools) for ho | [**protoc**](https://github.com/protocolbuffers/protobuf) | `$HOME/.install-action/bin` | [GitHub Releases](https://github.com/protocolbuffers/protobuf/releases) | Linux, macOS, Windows | [BSD-3-Clause](https://github.com/protocolbuffers/protobuf/blob/main/LICENSE) | | [**rclone**](https://github.com/rclone/rclone) | `$HOME/.install-action/bin` | [GitHub Releases](https://github.com/rclone/rclone/releases) | Linux, macOS, Windows | [MIT](https://github.com/rclone/rclone/blob/master/COPYING) | | [**release-plz**](https://release-plz.dev/) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/release-plz/release-plz/releases) | Linux, macOS, Windows | [MIT](https://github.com/release-plz/release-plz/blob/main/LICENSE-MIT) OR [Apache-2.0](https://github.com/release-plz/release-plz/blob/main/LICENSE-APACHE) | +| [**rust**](https://rust-lang.org) | `$CARGO_HOME/bin` | rustup | Linux, macOS, Windows | [Apache-2.0 OR MIT](https://github.com/rust-lang/rust/blob/main/COPYRIGHT) | | [**sccache**](https://github.com/mozilla/sccache) | `$CARGO_HOME/bin` | [GitHub Releases](https://github.com/mozilla/sccache/releases) | Linux, macOS, Windows | [Apache-2.0](https://github.com/mozilla/sccache/blob/main/LICENSE) | | [**shellcheck**](https://www.shellcheck.net) | `$HOME/.install-action/bin` | [GitHub Releases](https://github.com/koalaman/shellcheck/releases) | Linux, macOS, Windows | [GPL-3.0](https://github.com/koalaman/shellcheck/blob/master/LICENSE) | | [**shfmt**](https://github.com/mvdan/sh) | `$HOME/.install-action/bin` | [GitHub Releases](https://github.com/mvdan/sh/releases) | Linux, macOS, Windows | [BSD-3-Clause](https://github.com/mvdan/sh/blob/master/LICENSE) | diff --git a/main.sh b/main.sh index dc3f4848..d36573fa 100755 --- a/main.sh +++ b/main.sh @@ -9,6 +9,14 @@ rx() { "$@" ) } +g() { + IFS=' ' + local cmd="$*" + IFS=$'\n\t' + printf '::group::%s\n' "${cmd#retry }" + "$@" + printf '::endgroup::\n' +} retry() { for i in {1..10}; do if "$@"; then @@ -730,17 +738,113 @@ for tool in "${tools[@]}"; do if [[ "${tool}" == *"@"* ]]; then version="${tool#*@}" tool="${tool%@*}" - if [[ ! "${version}" =~ ^([1-9][0-9]*(\.[0-9]+(\.[0-9]+)?)?|0\.[1-9][0-9]*(\.[0-9]+)?|^0\.0\.[0-9]+)(-[0-9A-Za-z\.-]+)?$|^latest$ ]]; then - if [[ ! "${version}" =~ ^([1-9][0-9]*(\.[0-9]+(\.[0-9]+)?)?|0\.[1-9][0-9]*(\.[0-9]+)?|^0\.0\.[0-9]+)(-[0-9A-Za-z\.-]+)?(\+[0-9A-Za-z\.-]+)?$|^latest$ ]]; then - bail "install-action does not support semver operators: '${version}'" + if [[ "${tool}" != 'rust' ]]; then + if [[ ! "${version}" =~ ^([1-9][0-9]*(\.[0-9]+(\.[0-9]+)?)?|0\.[1-9][0-9]*(\.[0-9]+)?|^0\.0\.[0-9]+)(-[0-9A-Za-z\.-]+)?$|^latest$ ]]; then + if [[ ! "${version}" =~ ^([1-9][0-9]*(\.[0-9]+(\.[0-9]+)?)?|0\.[1-9][0-9]*(\.[0-9]+)?|^0\.0\.[0-9]+)(-[0-9A-Za-z\.-]+)?(\+[0-9A-Za-z\.-]+)?$|^latest$ ]]; then + bail "install-action does not support semver operators: '${version}'" + fi + bail "install-action v2 does not support semver build-metadata: '${version}'; if you need these supports again, please submit an issue at " fi - bail "install-action v2 does not support semver build-metadata: '${version}'; if you need these supports again, please submit an issue at " fi else version=latest fi installed_bin=() case "${tool}" in + rust) + if [[ "${version}" == 'latest' ]]; then + version=stable + fi + info "installing ${tool}@${version}" + export RUSTUP_MAX_RETRIES="${RUSTUP_MAX_RETRIES:-10}" + rustup_args=(--profile minimal) + if type -P rustup >/dev/null; then + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + g retry rustup toolchain add "${version}" --no-self-update "${rustup_args[@]}" + g rustup default "${version}" + else + # https://github.com/rust-lang/rustup/tags + # Run tools/rustup-hash.sh to get sha256 hash. + rustup_version=1.29.0 + # https://rust-lang.github.io/rustup/installation/other.html#manual-installation + rust_target='' + checksum='' + case "${host_os}" in + linux) + rust_target="${host_arch}-unknown-${host_os}-${host_env}" + case "${host_arch}" in + x86_64) + case "${host_env}" in + gnu) checksum=4acc9acc76d5079515b46346a485974457b5a79893cfb01112423c89aeb5aa10 ;; + musl) checksum=9cd3fda5fd293890e36ab271af6a786ee22084b5f6c2b83fd8323cec6f0992c1 ;; + esac + ;; + aarch64) + case "${host_env}" in + gnu) checksum=9732d6c5e2a098d3521fca8145d826ae0aaa067ef2385ead08e6feac88fa5792 ;; + musl) checksum=88761caacddb92cd79b0b1f939f3990ba1997d701a38b3e8dd6746a562f2a759 ;; + esac + ;; + powerpc64le) + case "${host_env}" in + gnu) checksum=4bfff85bd3967d988e14567aa9cc6ab0ea386f0ffeff0f9f14d23f0103bf1f97 ;; + musl) checksum=e15d033af90b7a55d170aac2d82cc28ddd96dbfcdda7c6d4eb8cb064a99c4646 ;; + esac + ;; + riscv64) + rust_target="${host_arch}gc-unknown-${host_os}-${host_env}" + # riscv64gc-unknown-linux-musl is tier 2 without host tools + case "${host_env}" in + gnu) checksum=7e43f2b2e6307d61da17a4dff61e6bceef408b8189822df64e1094590d2a70f9 ;; + esac + ;; + s390x) + # s390x-unknown-linux-musl is tier 3 + case "${host_env}" in + gnu) checksum=66c2c132428b6b77803facb02cbdf33b89d20c00bd20da142be8cb651f2e7cd8 ;; + esac + ;; + esac + ;; + macos) + rust_target="${host_arch}-apple-darwin" + case "${host_arch}" in + x86_64) checksum=33cf85df9142bc6d29cbc62fa5ca1d4c29622cddb55213a4c1a43c457fb9b2d7 ;; + aarch64) checksum=aeb4105778ca1bd3c6b0e75768f581c656633cd51368fa61289b6a71696ac7e1 ;; + esac + ;; + windows) + rust_target="${host_arch}-pc-windows-msvc" + case "${host_arch}" in + x86_64) checksum=86478e53f769379d7f0ebfa7c9aa97cb76ca92233f79aa2cc0dbee2efaac73c7 ;; + aarch64) checksum=3af309e6c3062aa11df0e932954f69d13b734d8a431e593812f3ecd9ff9e6ef6 ;; + esac + ;; + esac + if [[ -z "${rust_target}" ]] || [[ -z "${checksum}" ]]; then + bail "unsupported host platform ${host_arch}_${host_os} for ${tool}" + fi + url="https://static.rust-lang.org/rustup/archive/${rustup_version}/${rust_target}/rustup-init${exe}" + mkdir -p -- "${tmp_dir}" + ( + cd -- "${tmp_dir}" + download_and_checksum "${url}" "${checksum}" + mv -- tmp rustup-init + case "${host_os}" in + linux | macos) chmod +x ./rustup-init ;; + esac + g retry ./rustup-init -y --default-toolchain "${version}" --no-modify-path "${rustup_args[@]}" + ) + rm -rf -- "${tmp_dir}" + cargo_bin_dir="${CARGO_HOME:-"${home}/.cargo"}/bin" + export PATH="${PATH}:${cargo_bin_dir}" + cargo_bin_dir=$(canonicalize_windows_path "${cargo_bin_dir}") + info "adding '${cargo_bin_dir}' to PATH" + printf '%s\n' "${cargo_bin_dir}" >>"${GITHUB_PATH}" + cargo_path=$(type -P cargo || true) + fi + installed_bin=("rustc${exe}" "cargo${exe}") + ;; protoc) info "installing ${tool}@${version}" read_manifest "protoc" "${version}" @@ -1002,7 +1106,6 @@ if [[ ${#unsupported_tools[@]} -gt 0 ]]; then fi if [[ -z "${cargo_path}" ]]; then _bin_dir=$(canonicalize_windows_path "${home}/.cargo/bin") - # TODO: avoid this when already added info "adding '${_bin_dir}' to PATH" printf '%s\n' "${_bin_dir}" >>"${GITHUB_PATH}" fi diff --git a/tools/ci/tool-list.sh b/tools/ci/tool-list.sh index 3070ac68..95797d6a 100755 --- a/tools/ci/tool-list.sh +++ b/tools/ci/tool-list.sh @@ -48,6 +48,7 @@ glibc_pre_2_27_incompat=( glibc_pre_2_17_incompat=( "${glibc_pre_2_27_incompat[@]}" deepsource # https://github.com/DeepSourceCorp/cli/issues/245 + rust ) musl_incompat=( "${glibc_pre_2_17_incompat[@]}" @@ -194,6 +195,19 @@ if [[ "${version}" != "latest" ]]; then fi # Not manifest-based +case "${runner}" in + # requires glibc 2.17 / musl 1.2 + centos:6 | alpine:3.2) ;; + *) + case $((RANDOM % 5)) in + 0) tools+=(rust) ;; + 1) tools+=(rust@stable) ;; + 2) tools+=(rust@nightly) ;; + 3) tools+=(rust@1.93) ;; + 4) tools+=(rust@1.93.0) ;; + esac + ;; +esac case "${host_os}" in linux*) # Installing snap to container is difficult... diff --git a/tools/codegen/src/tools-markdown.rs b/tools/codegen/src/tools-markdown.rs index ce643bb9..dd4b423d 100644 --- a/tools/codegen/src/tools-markdown.rs +++ b/tools/codegen/src/tools-markdown.rs @@ -52,18 +52,32 @@ fn main() { let mut paths: Vec<_> = fs::read_dir(&manifest_dir).unwrap().map(|r| r.unwrap()).collect(); paths.sort_by_key(fs_err::DirEntry::path); - let mut tools = vec![MarkdownEntry { - name: "valgrind".to_owned(), - alias: None, - website: "https://valgrind.org/".to_owned(), - installed_to: InstalledTo::Snap, - installed_from: InstalledFrom::Snap, - platforms: Platforms { linux: true, ..Default::default() }, - repository: "https://sourceware.org/git/valgrind.git".to_owned(), - license_markdown: - "[GPL-2.0](https://sourceware.org/git/?p=valgrind.git;a=blob;f=COPYING;hb=HEAD)" - .to_owned(), - }]; + let mut tools = vec![ + MarkdownEntry { + name: "rust".to_owned(), + alias: None, + website: "https://rust-lang.org".to_owned(), + installed_to: InstalledTo::Cargo, + installed_from: InstalledFrom::Rustup, + platforms: Platforms { linux: true, macos: true, windows: true }, + repository: "https://github.com/rust-lang/rust".to_owned(), + license_markdown: + "[Apache-2.0 OR MIT](https://github.com/rust-lang/rust/blob/main/COPYRIGHT)" + .to_owned(), + }, + MarkdownEntry { + name: "valgrind".to_owned(), + alias: None, + website: "https://valgrind.org/".to_owned(), + installed_to: InstalledTo::Snap, + installed_from: InstalledFrom::Snap, + platforms: Platforms { linux: true, ..Default::default() }, + repository: "https://sourceware.org/git/valgrind.git".to_owned(), + license_markdown: + "[GPL-2.0](https://sourceware.org/git/?p=valgrind.git;a=blob;f=COPYING;hb=HEAD)" + .to_owned(), + }, + ]; for path in paths { let file_name = path.file_name(); @@ -153,6 +167,7 @@ struct MarkdownEntry { #[derive(Debug, Eq, PartialEq)] enum InstalledFrom { GitHubRelease, + Rustup, Snap, } @@ -215,6 +230,9 @@ impl fmt::Display for MarkdownEntry { let markdown = format!("| [GitHub Releases]({}/releases) ", self.repository); f.write_str(&markdown)?; } + InstalledFrom::Rustup => { + f.write_str("| rustup ")?; + } InstalledFrom::Snap => { let markdown = format!("| [snap](https://snapcraft.io/install/{}/ubuntu) ", self.name); diff --git a/tools/rustup-hash.sh b/tools/rustup-hash.sh new file mode 100755 index 00000000..78a4c91a --- /dev/null +++ b/tools/rustup-hash.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 OR MIT +set -CeEuo pipefail +IFS=$'\n\t' +trap -- 's=$?; printf >&2 "%s\n" "${0##*/}:${LINENO}: \`${BASH_COMMAND}\` exit with ${s}"; exit ${s}' ERR +cd -- "$(dirname -- "$0")"/.. + +# Get sha256 hash of rustup-init binaries + +# NB: Synch with main.sh. +rustup_version=1.29.0 +targets=( + x86_64-unknown-linux-gnu + x86_64-unknown-linux-musl + aarch64-unknown-linux-gnu + aarch64-unknown-linux-musl + powerpc64le-unknown-linux-gnu + powerpc64le-unknown-linux-musl + riscv64gc-unknown-linux-gnu + # riscv64gc-unknown-linux-musl # tier 2 without host tools + s390x-unknown-linux-gnu + # s390x-unknown-linux-musl # tier 3 + x86_64-apple-darwin + aarch64-apple-darwin + x86_64-pc-windows-msvc + aarch64-pc-windows-msvc +) + +for rust_target in "${targets[@]}"; do + exe='' + case "${rust_target}" in + *-windows*) exe=.exe ;; + esac + url="https://static.rust-lang.org/rustup/archive/${rustup_version}/${rust_target}/rustup-init${exe}.sha256" + printf '%s: ' "${rust_target}" + curl --proto '=https' --tlsv1.2 -fsSL --retry 10 "${url}" | cut -d' ' -f1 +done