Workaround Fuse-T behavior on macOS #109
Workflow file for this run
This file contains hidden or 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: Build and Test | |
| on: [push, pull_request] | |
| jobs: | |
| ubuntu: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - run: sudo apt-get install ccache libfuse3-dev build-essential flex bison gperf libncurses5-dev libncursesw5-dev gawk libmpfr-dev libgpm-dev zlib1g-dev yasm graphviz | |
| - run: pip3 install sh | |
| - run: ./bootstrap.sh | |
| - run: cd test && ./test.sh --keep-going | |
| macos: | |
| runs-on: macos-latest | |
| env: | |
| PKG_CONFIG_PATH: /usr/local/lib/pkgconfig | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install FUSE-T and dependencies | |
| run: | | |
| # FUSE-T: kext-free FUSE for macOS using NFS v4 local server. | |
| # API-compatible with macFUSE, no kernel extension needed. | |
| # https://github.com/macos-fuse-t/fuse-t | |
| brew tap macos-fuse-t/homebrew-cask | |
| brew install fuse-t | |
| brew install pkg-config pcre2 ccache cmake | |
| pip3 install --break-system-packages sh | |
| - name: Create macFUSE-compatible header symlinks | |
| run: | | |
| # FUSE-T puts headers in a framework; tup expects /usr/local/include/fuse/ | |
| sudo mkdir -p /usr/local/include/fuse | |
| sudo ln -sf /Library/Frameworks/fuse_t.framework/Headers/* /usr/local/include/fuse/ | |
| - name: Build patched libfuse for FUSE-T | |
| run: | | |
| # FUSE-T's libfuse has an unmount teardown bug that causes non-zero | |
| # exit after every successful FUSE operation. We build from a patched | |
| # fork until the fix is merged upstream. | |
| # Fix: https://github.com/macos-fuse-t/libfuse/pull/11 | |
| git clone https://github.com/petemoore/libfuse.git /tmp/libfuse-src \ | |
| --branch fix/recv-eof-and-attrcache --depth 1 | |
| cd /tmp/libfuse-src && mkdir build && cd build | |
| cmake .. && make -j$(sysctl -n hw.ncpu) | |
| # Install patched library over FUSE-T's version | |
| install_name_tool -id /usr/local/lib/libfuse-t.dylib lib/libfuse-t.dylib | |
| sudo cp lib/libfuse-t.dylib /usr/local/lib/libfuse-t.dylib | |
| sudo cp lib/libfuse-t.a /usr/local/lib/libfuse-t.a | |
| # Create fuse.pc (FUSE-T doesn't ship one) | |
| sudo mkdir -p /usr/local/lib/pkgconfig | |
| sudo tee /usr/local/lib/pkgconfig/fuse.pc > /dev/null << 'EOF' | |
| prefix=/usr/local | |
| exec_prefix=${prefix} | |
| libdir=${exec_prefix}/lib | |
| includedir=${prefix}/include | |
| Name: fuse | |
| Description: FUSE-T libfuse | |
| Version: 2.9.9 | |
| Libs: -L${libdir} -lfuse-t -pthread | |
| Cflags: -I${includedir}/fuse -D_FILE_OFFSET_BITS=64 | |
| EOF | |
| - name: Build (bootstrap) | |
| run: ./bootstrap.sh | |
| - name: Run tests | |
| run: | | |
| cd test | |
| # Skip tests that consistently fail on macOS. | |
| # | |
| # macOS platform issues (fail with both macFUSE and FUSE-T): | |
| # t3083 — gcc --coverage gcno naming differs on macOS | |
| # t6082 — utimens() on non-job directory (macOS restriction) | |
| # | |
| # FUSE-T deterministic failures (fail every run, not flaky): | |
| # t2094, t2128, t2135 — run-script deps not tracked via NFS | |
| # t5074 — process management timeout under NFS latency | |
| # t6017 — input dependency missed by NFS client | |
| # t8079 — run-script in variant not tracked via NFS | |
| # t9006 — input dependency missed by NFS client | |
| skip="t2094-run4.sh t2128-run-preload.sh t2135-preload6.sh t3083-extra-outputs-bang3.sh t5074-tup-dies.sh t6017-broken-update8.sh t6082-broken-update61.sh t8079-run-variant.sh t9006-gitignore-without-glob.sh" | |
| tests="" | |
| for t in t[0-9]*.sh; do | |
| if echo " $skip " | grep -q " $t "; then | |
| echo "SKIP (macOS platform issue): $t" | |
| else | |
| tests="$tests $t" | |
| fi | |
| done | |
| # FUSE-T uses an NFS v4 backend instead of a kernel module. | |
| # Under CI load, the macOS NFS client occasionally drops or | |
| # delays FUSE callbacks, causing tup to miss file accesses. | |
| # This affects ~1-4 random tests per run out of ~980 (all | |
| # pass locally and pass with macFUSE). Retry failed tests | |
| # up to 3 times to distinguish NFS flakes from genuine | |
| # regressions. See: https://github.com/macos-fuse-t/fuse-t/issues/91 | |
| for attempt in 1 2 3 4; do | |
| if [ $attempt -gt 1 ]; then | |
| echo "" | |
| echo "=== Attempt $attempt/4: retrying failed tests ===" | |
| echo "" | |
| tests="$failed_tests" | |
| # Clean up leftover test directories from previous attempt | |
| for t in $tests; do | |
| rm -rf "tuptesttmp-${t%.sh}" 2>/dev/null || true | |
| done | |
| fi | |
| ./test.sh --keep-going $tests 2>&1 | tee /tmp/test-attempt-${attempt}.log | |
| rc=${PIPESTATUS[0]} | |
| if [ $rc -eq 0 ]; then | |
| break | |
| fi | |
| # Extract failed test script names (format: " *** t1234-name.sh failed") | |
| failed_tests=$(grep -oE 't[0-9]+-[a-zA-Z0-9_-]+\.sh failed' /tmp/test-attempt-${attempt}.log | sed 's/ failed$//' | sort -u | tr '\n' ' ') | |
| if [ -z "$failed_tests" ]; then | |
| break | |
| fi | |
| echo "Failed tests: $failed_tests" | |
| done | |
| exit $rc |