Make check-mode.sh
usable on macOS
#472
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: ci | |
on: | |
push: | |
branches: | |
- main | |
- 'run-ci/**' | |
- '**/run-ci/**' | |
tags-ignore: | |
- '*' | |
pull_request: | |
branches: | |
- main | |
workflow_dispatch: | |
permissions: | |
contents: read | |
env: | |
CARGO_TERM_COLOR: always | |
CLICOLOR: '1' | |
jobs: | |
pure-rust-build: | |
runs-on: ubuntu-latest | |
container: debian:stable-slim | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Prerequisites | |
run: | | |
prerequisites=( | |
ca-certificates | |
curl | |
gcc # rustc calls gcc to invoke the linker. | |
libc-dev # rustc, in the toolchain we are using, dynamically links to the system libc. | |
) | |
apt-get update | |
apt-get install --no-install-recommends -y -- "${prerequisites[@]}" | |
shell: bash | |
- name: Verify that we are in an environment with limited dev tools | |
run: | | |
set -x | |
for package in cmake g++ libssl-dev make pkgconf pkg-config; do | |
if dpkg-query --status -- "$package"; then | |
exit 1 | |
fi | |
done | |
for cmd in cmake g++ make pkgconf pkg-config; do | |
if command -v -- "$cmd"; then | |
exit 1 | |
fi | |
done | |
- name: Install Rust via Rustup | |
run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal | |
- name: Add Rust tools to path | |
run: echo "PATH=$HOME/.cargo/bin:$PATH" >> "$GITHUB_ENV" | |
- name: Generate dependency tree | |
run: cargo tree --locked --no-default-features --features max-pure > tree.txt | |
- name: Scan for dependencies that build C or C++ code | |
run: | | |
pattern='.*\b(-sys|cc|cmake|pkg-config|vcpkg)\b.*' | |
! GREP_COLORS='ms=30;48;5;214' grep --color=always -Ex -C 1000000 -e "$pattern" tree.txt | |
continue-on-error: true | |
- name: Check for unrecognized *-sys dependencies | |
run: | | |
! grep -qP '(?<!\blinux-raw)-sys\b' tree.txt | |
- name: Wrap cc1 (and cc1plus if present) to record calls | |
run: | | |
cat >/usr/local/bin/wrapper1 <<'EOF' | |
#!/bin/sh -e | |
printf '%s\n' "$0 $*" | | |
flock /run/lock/wrapper1.fbd136bd-9b1b-448d-84a9-e18be53ae63c.lock \ | |
tee -a -- /var/log/wrapper1.log ~/display >/dev/null # We'll link ~/display later. | |
exec "$0.orig" "$@" | |
EOF | |
cat >/usr/local/bin/wrap1 <<'EOF' | |
#!/bin/sh -e | |
dir="$(dirname -- "$1")" | |
base="$(basename -- "$1")" | |
cd -- "$dir" | |
mv -- "$base" "$base.orig" | |
ln -s -- /usr/local/bin/wrapper1 "$base" | |
EOF | |
chmod +x /usr/local/bin/wrap1 /usr/local/bin/wrapper1 | |
mkdir /run/lock/wrapper1.fbd136bd-9b1b-448d-84a9-e18be53ae63c.lock | |
find /usr/lib/gcc \( -name cc1 -o -name cc1plus \) \ | |
-print -exec /usr/local/bin/wrap1 {} \; | |
- name: Build max-pure with limited dev tools and log cc1 | |
run: | | |
ln -s -- "/proc/$$/fd/1" ~/display # Bypass `cc1` redirection. | |
cargo install --debug --locked --no-default-features --features max-pure --path . | |
- name: Show logged C and C++ compilations (should be none) | |
run: | | |
! cat /var/log/wrapper1.log | |
continue-on-error: true | |
test: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: dtolnay/rust-toolchain@stable | |
- uses: Swatinem/rust-cache@v2 | |
- name: Setup dependencies | |
run: sudo apt-get install -y --no-install-recommends liblzma-dev | |
- uses: extractions/setup-just@v2 | |
- uses: taiki-e/install-action@v2 | |
with: | |
tool: nextest | |
- name: test | |
env: | |
GIX_TEST_IGNORE_ARCHIVES: '1' | |
run: just ci-test | |
test-journey: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: dtolnay/rust-toolchain@stable | |
- uses: Swatinem/rust-cache@v2 | |
- uses: extractions/setup-just@v2 | |
- name: Run journey tests | |
run: just ci-journey-tests | |
test-fast: | |
strategy: | |
matrix: | |
os: | |
- windows-latest | |
- macos-latest | |
- ubuntu-latest | |
runs-on: ${{ matrix.os }} | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: dtolnay/rust-toolchain@stable | |
- uses: Swatinem/rust-cache@v2 | |
- name: Setup dependencies (macos) | |
if: startsWith(matrix.os, 'macos') | |
run: brew install openssl gnu-sed | |
- name: cargo check default features | |
if: startsWith(matrix.os, 'windows') | |
run: cargo check --workspace --bins --examples | |
- uses: taiki-e/install-action@v2 | |
with: | |
tool: nextest | |
- name: Test (nextest) | |
env: | |
GIX_TEST_CREATE_ARCHIVES_EVEN_ON_CI: '1' | |
run: cargo nextest run --workspace --no-fail-fast | |
- name: Doctest | |
run: cargo test --workspace --doc --no-fail-fast | |
- name: Check that tracked archives are up to date | |
run: git diff --exit-code # If this fails, the fix is usually to commit a regenerated archive. | |
test-fixtures-windows: | |
runs-on: windows-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: dtolnay/rust-toolchain@stable | |
- uses: Swatinem/rust-cache@v2 | |
- uses: taiki-e/install-action@v2 | |
with: | |
tool: nextest | |
- name: Test (nextest) | |
id: nextest | |
env: | |
GIX_TEST_IGNORE_ARCHIVES: '1' | |
run: cargo nextest --profile=with-xml run --workspace --no-fail-fast | |
continue-on-error: true | |
- name: Check for errors | |
run: | | |
[xml]$junit_xml = Get-Content -Path 'target/nextest/with-xml/junit.xml' | |
if ($junit_xml.testsuites.errors -ne 0) { exit 1 } | |
- name: Collect actual failures | |
run: | | |
[xml]$junit_xml = Get-Content -Path 'target/nextest/with-xml/junit.xml' | |
$actual_failures = $junit_xml.SelectNodes("//testcase[failure]") | | |
ForEach-Object { "$($_.classname) $($_.name)" } | | |
Sort-Object | |
Write-Output $actual_failures | |
Set-Content -Path 'actual-failures.txt' -Value $actual_failures | |
- name: Compare expected and actual failures | |
run: | | |
# Fail on any differences, even unexpectedly passing tests, so they can be investigated. | |
# (If the job is made blocking for PRs, it may make sense to make this less stringent.) | |
git --no-pager diff --no-index --exit-code --unified=1000000 --color=always -- ` | |
etc/test-fixtures-windows-expected-failures-see-issue-1358.txt actual-failures.txt | |
test-32bit: | |
runs-on: ubuntu-latest | |
container: i386/debian:stable-slim | |
steps: | |
- name: Prerequisites | |
run: | | |
prerequisites=( | |
build-essential | |
ca-certificates | |
cmake | |
curl | |
git | |
jq | |
libssl-dev | |
libstdc++6:amd64 # To support external 64-bit Node.js for actions. | |
pkgconf | |
) | |
dpkg --add-architecture amd64 | |
apt-get update | |
apt-get install --no-install-recommends -y -- "${prerequisites[@]}" | |
shell: bash | |
- uses: actions/checkout@v4 | |
- uses: dtolnay/rust-toolchain@stable | |
with: | |
toolchain: stable-i686-unknown-linux-gnu # Otherwise it may misdetect based on the amd64 kernel. | |
- uses: Swatinem/rust-cache@v2 | |
- uses: taiki-e/install-action@v2 | |
with: | |
tool: nextest | |
- name: Make `system` scope nonempty for "GitInstallation" tests | |
run: git config --system gitoxide.imaginary.arbitraryVariable arbitraryValue | |
- name: Test (nextest) | |
env: | |
GIX_TEST_IGNORE_ARCHIVES: '1' | |
run: cargo nextest run --workspace --no-fail-fast | |
test-32bit-cross: | |
runs-on: ubuntu-latest | |
strategy: | |
matrix: | |
target: [ armv7-linux-androideabi ] | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: dtolnay/rust-toolchain@stable | |
- uses: Swatinem/rust-cache@v2 | |
- name: Install Rust | |
uses: dtolnay/rust-toolchain@master | |
with: | |
toolchain: stable | |
targets: ${{ matrix.target }} | |
- name: Install cross | |
uses: taiki-e/install-action@v2 | |
with: | |
tool: cross | |
- name: check | |
run: cross check -p gix --target ${{ matrix.target }} | |
- name: Test (unit) | |
run: | | |
# Run some high-level unit tests that exercise various pure Rust code to ease building test binaries. | |
# We would prefer `-p gix`. But with `cross`, fixture scripts try to run amd64 `git` as an armv7 binary. | |
cross test -p gix-hashtable --target ${{ matrix.target }} | |
lint: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: dtolnay/rust-toolchain@master | |
with: | |
toolchain: stable | |
components: clippy,rustfmt | |
- uses: extractions/setup-just@v2 | |
- name: Run cargo clippy | |
run: just clippy -D warnings -A unknown-lints --no-deps | |
- name: Run cargo doc | |
run: just doc | |
- name: Run cargo fmt | |
run: cargo fmt --all -- --check | |
- name: Install cargo diet | |
env: | |
CARGO_DIET_TAG: v1.2.7 | |
run: | | |
curl -LSfs "https://raw.githubusercontent.com/the-lean-crate/cargo-diet/refs/tags/$CARGO_DIET_TAG/ci/install.sh" | | |
sh -s -- --git the-lean-crate/cargo-diet --target x86_64-unknown-linux-musl --tag "$CARGO_DIET_TAG" | |
- name: Run cargo diet | |
run: just check-size | |
# Let's not fail CI for this, it will fail locally often enough, and a crate a little bigger | |
# than allows is no problem either if it comes to that. | |
continue-on-error: true | |
# This job is not required for PR auto-merge, so that sudden announcement of a | |
# new advisory does not keep otherwise OK pull requests from being integrated. | |
cargo-deny-advisories: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: EmbarkStudios/cargo-deny-action@v2 | |
with: | |
command: check advisories | |
cargo-deny: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: EmbarkStudios/cargo-deny-action@v2 | |
with: | |
command: check bans licenses sources | |
wasm: | |
name: WebAssembly | |
runs-on: ubuntu-latest | |
strategy: | |
matrix: | |
target: [ wasm32-unknown-unknown, wasm32-wasi ] | |
env: | |
TARGET: ${{ matrix.target }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Install Rust | |
run: | | |
rustup update stable | |
rustup default stable | |
rustup target add "$TARGET" | |
- uses: Swatinem/rust-cache@v2 | |
- name: 'WASI only: crates without feature toggle' | |
if: endsWith(matrix.target, '-wasi') | |
run: | | |
set +x | |
for name in gix-sec; do | |
(cd -- "$name" && cargo build --target "$TARGET") | |
done | |
- name: crates without feature toggles | |
run: | | |
set +x | |
for name in gix-actor gix-attributes gix-bitmap gix-chunk gix-command gix-commitgraph gix-config-value gix-date gix-glob gix-hash gix-hashtable gix-mailmap gix-object gix-packetline gix-path gix-pathspec gix-prompt gix-quote gix-refspec gix-revision gix-traverse gix-url gix-validate; do | |
(cd -- "$name" && cargo build --target "$TARGET") | |
done | |
- name: features of gix-features | |
run: | | |
set +x | |
for feature in progress fs-walkdir-parallel parallel io-pipe crc32 zlib zlib-rust-backend fast-sha1 rustsha1 cache-efficiency-debug; do | |
(cd gix-features && cargo build --features "$feature" --target "$TARGET") | |
done | |
- name: crates with 'wasm' feature | |
run: | | |
set +x | |
for name in gix-pack; do | |
(cd -- "$name" && cargo build --features wasm --target "$TARGET") | |
done | |
- name: gix-pack with all features (including wasm) | |
run: cd gix-pack && cargo build --all-features --target "$TARGET" | |
check-mode: | |
# FIXME: Only run this on ubuntu-latest (don't use a matrix). | |
strategy: | |
fail-fast: false | |
matrix: | |
os: [ ubuntu-latest, macos-latest, windows-latest ] | |
runs-on: ${{ matrix.os }} | |
defaults: | |
run: | |
shell: bash | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Find scripts with mode/shebang mismatch | |
run: etc/check-mode.sh | |
check-packetline: | |
strategy: | |
fail-fast: false | |
matrix: | |
os: | |
- ubuntu-latest | |
# We consider this script read-only and its effect is the same everywhere. | |
# However, when changes are made to `etc/copy-packetline.sh`, re-enable the other platforms for testing. | |
# - macos-latest | |
# - windows-latest | |
runs-on: ${{ matrix.os }} | |
defaults: | |
run: | |
shell: bash # Use bash even on Windows, if we ever reenable windows-latest for testing. | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Check that working tree is initially clean | |
run: | | |
set -x | |
git status | |
git diff --exit-code | |
- name: Regenerate gix-packetline-blocking/src | |
run: etc/copy-packetline.sh | |
- name: Check that gix-packetline-blocking/src was already up to date | |
run: | | |
set -x | |
git status | |
git diff --exit-code | |
# Check that only jobs intended not to block PR auto-merge are omitted as | |
# dependencies of the `tests-pass` job below, so that whenever a job is | |
# added, a decision is made about whether it must pass for PRs to merge. | |
check-blocking: | |
runs-on: ubuntu-latest | |
env: | |
# List all jobs that are intended NOT to block PR auto-merge here. | |
EXPECTED_NONBLOCKING_JOBS: |- | |
test-fixtures-windows | |
cargo-deny-advisories | |
wasm | |
tests-pass | |
defaults: | |
run: | |
shell: bash # Without specifying this, we don't get `-o pipefail`. | |
steps: | |
- name: Find this workflow | |
run: | | |
relative_workflow_with_ref="${GITHUB_WORKFLOW_REF#"$GITHUB_REPOSITORY/"}" | |
echo "WORKFLOW_PATH=${relative_workflow_with_ref%@*}" >> "$GITHUB_ENV" | |
- uses: actions/checkout@v4 | |
with: | |
sparse-checkout: ${{ env.WORKFLOW_PATH }} | |
- name: Get all jobs | |
run: yq '.jobs | keys.[]' -- "$WORKFLOW_PATH" | sort | tee all-jobs.txt | |
- name: Get blocking jobs | |
run: yq '.jobs.tests-pass.needs.[]' -- "$WORKFLOW_PATH" | sort | tee blocking-jobs.txt | |
- name: Get jobs we intend do not block | |
run: sort <<<"$EXPECTED_NONBLOCKING_JOBS" | tee expected-nonblocking-jobs.txt | |
- name: Each job must block PRs or be declared not to | |
run: | | |
sort -m blocking-jobs.txt expected-nonblocking-jobs.txt | | |
diff --color=always -U1000 - all-jobs.txt | |
# Dummy job to have a stable name for the "all tests pass" requirement | |
tests-pass: | |
name: Tests pass | |
needs: | |
- pure-rust-build | |
- test | |
- test-journey | |
- test-fast | |
- test-32bit | |
- test-32bit-cross | |
- lint | |
- cargo-deny | |
- check-packetline | |
- check-blocking | |
if: always() # always run even if dependencies fail | |
runs-on: ubuntu-latest | |
steps: | |
- name: Fail if ANY dependency has failed or cancelled | |
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') | |
run: exit 1 | |
- name: OK | |
run: exit 0 |