diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index 8d30e4096..d974544c7 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -30,6 +30,7 @@ permissions: jobs: actionlint: runs-on: ubuntu-latest + timeout-minutes: 15 name: actionlint steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/almalinux-8-build.yml b/.github/workflows/almalinux-8-build.yml index e39e53dfc..3138a2e85 100644 --- a/.github/workflows/almalinux-8-build.yml +++ b/.github/workflows/almalinux-8-build.yml @@ -24,9 +24,13 @@ on: schedule: - cron: '42 8 * * *' +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest + timeout-minutes: 45 container: image: almalinux:8 name: Test rsync on AlmaLinux 8 diff --git a/.github/workflows/android-static-build.yml b/.github/workflows/android-static-build.yml index 4630f55e1..f0e4235b3 100644 --- a/.github/workflows/android-static-build.yml +++ b/.github/workflows/android-static-build.yml @@ -28,6 +28,9 @@ on: - cron: '42 8 * * 1' workflow_dispatch: +permissions: + contents: read + env: # Minimum supported API level. 24 (Android 7.0) runs on every modern # phone while keeping broad reach; bump if you need newer Bionic APIs. @@ -36,6 +39,7 @@ env: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 45 name: ${{ matrix.abi }} strategy: fail-fast: false diff --git a/.github/workflows/asan-build.yml b/.github/workflows/asan-build.yml index 3c2fc5086..b36b7902b 100644 --- a/.github/workflows/asan-build.yml +++ b/.github/workflows/asan-build.yml @@ -23,9 +23,13 @@ on: - cron: '42 9 * * 1' workflow_dispatch: +permissions: + contents: read + jobs: asan: runs-on: ubuntu-latest + timeout-minutes: 45 name: rsync ASan+UBSan (clang) env: # rsync intentionally leaks small allocations at process exit, so leak diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 14652d1e5..fb22ba266 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -4,24 +4,20 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true +# Coverage duplicates the Ubuntu suite to measure it rather than protect a +# distinct PR failure mode. Keep that cost scheduled and available on demand. on: - push: - branches: [ master ] - paths-ignore: - - '.github/workflows/*.yml' - - '!.github/workflows/coverage.yml' - pull_request: - types: [opened, synchronize, reopened] - paths-ignore: - - '.github/workflows/*.yml' - - '!.github/workflows/coverage.yml' schedule: - cron: '42 9 * * 1' workflow_dispatch: +permissions: + contents: read + jobs: coverage: runs-on: ubuntu-latest + timeout-minutes: 45 name: gcov coverage steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/cygwin-build.yml b/.github/workflows/cygwin-build.yml index 329c6631f..7b660b269 100644 --- a/.github/workflows/cygwin-build.yml +++ b/.github/workflows/cygwin-build.yml @@ -18,9 +18,13 @@ on: schedule: - cron: '42 8 * * *' +permissions: + contents: read + jobs: test: runs-on: windows-2022 + timeout-minutes: 60 name: Test rsync on Cygwin steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/fleettest.yml b/.github/workflows/fleettest.yml index f9d68cdb2..095c7adc4 100644 --- a/.github/workflows/fleettest.yml +++ b/.github/workflows/fleettest.yml @@ -31,9 +31,13 @@ on: schedule: - cron: '17 7 * * 1' +permissions: + contents: read + jobs: fleettest: runs-on: ubuntu-latest + timeout-minutes: 45 name: fleettest against localhost steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/freebsd-build.yml b/.github/workflows/freebsd-build.yml index 5e6396abe..60e1d5752 100644 --- a/.github/workflows/freebsd-build.yml +++ b/.github/workflows/freebsd-build.yml @@ -18,9 +18,13 @@ on: schedule: - cron: '42 8 * * 1' +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest + timeout-minutes: 45 name: Test rsync on FreeBSD steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/macos-build.yml b/.github/workflows/macos-build.yml index f4c20d6a0..bc9a32fc1 100644 --- a/.github/workflows/macos-build.yml +++ b/.github/workflows/macos-build.yml @@ -18,9 +18,13 @@ on: schedule: - cron: '42 8 * * *' +permissions: + contents: read + jobs: test: runs-on: macos-latest + timeout-minutes: 45 name: Test rsync on macOS steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/netbsd-build.yml b/.github/workflows/netbsd-build.yml index bfae7180a..104e6b8c7 100644 --- a/.github/workflows/netbsd-build.yml +++ b/.github/workflows/netbsd-build.yml @@ -18,9 +18,13 @@ on: schedule: - cron: '42 8 * * 1' +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest + timeout-minutes: 45 name: Test rsync on NetBSD steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/openbsd-build.yml b/.github/workflows/openbsd-build.yml index 11d717201..edc987293 100644 --- a/.github/workflows/openbsd-build.yml +++ b/.github/workflows/openbsd-build.yml @@ -18,9 +18,13 @@ on: schedule: - cron: '42 8 * * 1' +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest + timeout-minutes: 45 name: Test rsync on OpenBSD steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/scan-build.yml b/.github/workflows/scan-build.yml index 18cb4ef5b..237d877c0 100644 --- a/.github/workflows/scan-build.yml +++ b/.github/workflows/scan-build.yml @@ -17,6 +17,9 @@ on: - '!.github/workflows/scan-build.yml' workflow_dispatch: +permissions: + contents: read + jobs: # GATING run: pinned clang-18 on a pinned runner so the checker set -- and # thus the expected zero -- is deterministic. The tree is kept clean for @@ -25,6 +28,7 @@ jobs: # and the runner (ubuntu-24.04, whose apt repos carry those packages). gate-clang18: runs-on: ubuntu-24.04 + timeout-minutes: 45 name: scan-build gate (clang-18, pinned) steps: - uses: actions/checkout@v4 @@ -66,6 +70,7 @@ jobs: # broken run from affecting the workflow's required status. informational-latest: runs-on: ubuntu-latest + timeout-minutes: 45 name: scan-build (latest clang, informational) continue-on-error: true steps: diff --git a/.github/workflows/solaris-build.yml b/.github/workflows/solaris-build.yml index 9528f315c..57322dabe 100644 --- a/.github/workflows/solaris-build.yml +++ b/.github/workflows/solaris-build.yml @@ -18,9 +18,13 @@ on: schedule: - cron: '42 8 * * 1' +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest + timeout-minutes: 45 name: Test rsync on Solaris steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/ubuntu-22.04-build.yml b/.github/workflows/ubuntu-22.04-build.yml deleted file mode 100644 index 2fcd7ce9c..000000000 --- a/.github/workflows/ubuntu-22.04-build.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Test rsync on Ubuntu 22.04 - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} - cancel-in-progress: true - -# Older-LTS coverage to help with backporting security fixes. ubuntu-22.04 -# is currently the oldest GitHub Actions runner image (20.04 was retired -# in April 2025). - -on: - push: - branches: [ master ] - paths-ignore: - - '.github/workflows/*.yml' - - '!.github/workflows/ubuntu-22.04-build.yml' - pull_request: - types: [opened, synchronize, reopened] - paths-ignore: - - '.github/workflows/*.yml' - - '!.github/workflows/ubuntu-22.04-build.yml' - schedule: - - cron: '42 8 * * *' - -jobs: - test: - runs-on: ubuntu-22.04 - name: Test rsync on Ubuntu 22.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: prep - run: | - sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl - echo "/usr/local/bin" >>"$GITHUB_PATH" - - name: configure - run: ./configure --with-rrsync - - name: make - run: make - - name: install - run: sudo make install - - name: info - run: rsync --version - - name: check - run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt make check - - name: check30 - run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt make check30 - - name: check29 - run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt,@testsuite/skiplist/proto29.txt make check29 - - name: check (TCP daemon transport) - # Second run with daemon tests over a real loopback rsyncd; the default - # 'make check' above uses the secure stdio-pipe transport. - run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8 - - name: ssl file list - run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true - - name: save artifact - uses: actions/upload-artifact@v4 - with: - retention-days: 45 - name: ubuntu-22.04-bin - path: | - rsync - rsync-ssl - rsync.1 - rsync-ssl.1 - rsyncd.conf.5 - rrsync.1 - rrsync diff --git a/.github/workflows/ubuntu-build.yml b/.github/workflows/ubuntu-build.yml index 7be3ec967..4c0dc748b 100644 --- a/.github/workflows/ubuntu-build.yml +++ b/.github/workflows/ubuntu-build.yml @@ -18,10 +18,26 @@ on: schedule: - cron: '42 8 * * *' +permissions: + contents: read + jobs: test: - runs-on: ubuntu-latest - name: Test rsync on Ubuntu + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-latest + name: Test rsync on Ubuntu latest + artifact: ubuntu-bin + nonroot: true + - runner: ubuntu-22.04 + name: Test rsync on Ubuntu 22.04 + artifact: ubuntu-22.04-bin + nonroot: false + runs-on: ${{ matrix.runner }} + timeout-minutes: 45 + name: ${{ matrix.name }} steps: - uses: actions/checkout@v4 with: @@ -51,6 +67,7 @@ jobs: # is env-dependent here (chroot-acl), so leave RSYNC_EXPECT_SKIPPED unset. run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8 - name: check (non-root, targeted) + if: matrix.nonroot # Every run above is root (sudo), so privilege-sensitive tests never hit # their non-root path. Run those here as the unprivileged 'runner' user # (NO sudo). Explicit test names make runtests.py full_run False, so @@ -72,7 +89,7 @@ jobs: uses: actions/upload-artifact@v4 with: retention-days: 45 - name: ubuntu-bin + name: ${{ matrix.artifact }} path: | rsync rsync-ssl diff --git a/.github/workflows/ubuntu-version-mix.yml b/.github/workflows/ubuntu-version-mix.yml index 9c176968f..0a47da4ad 100644 --- a/.github/workflows/ubuntu-version-mix.yml +++ b/.github/workflows/ubuntu-version-mix.yml @@ -39,9 +39,13 @@ on: schedule: - cron: '52 8 * * 1' +permissions: + contents: read + jobs: version-mix: runs-on: ubuntu-latest + timeout-minutes: 45 name: rsync version-mix steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index d31a4417b..ce0ecafcf 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -4,25 +4,20 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true +# ASan+UBSan remains the per-PR memory-safety gate. Run the four slower +# Valgrind combinations daily and on demand instead of occupying PR runners. on: - push: - branches: [ master ] - paths-ignore: - - '.github/workflows/*.yml' - - '!.github/workflows/valgrind.yml' - pull_request: - types: [opened, synchronize, reopened] - paths-ignore: - - '.github/workflows/*.yml' - - '!.github/workflows/valgrind.yml' schedule: - cron: '17 4 * * *' workflow_dispatch: +permissions: + contents: read + jobs: memcheck: runs-on: ubuntu-latest - timeout-minutes: 120 + timeout-minutes: 60 strategy: fail-fast: false matrix: diff --git a/testsuite/fleettest.json.example b/testsuite/fleettest.json.example index 32c0da09a..a54cc6eb7 100644 --- a/testsuite/fleettest.json.example +++ b/testsuite/fleettest.json.example @@ -91,10 +91,10 @@ ] }, { - "_comment": "Ubuntu 18.04 older-LTS backport coverage on a root@ box; no 18.04 runner image exists so it mirrors the 22.04 workflow.", + "_comment": "Ubuntu 18.04 older-LTS backport coverage on a root@ box; no 18.04 runner image exists so it mirrors the 22.04 matrix lane.", "name": "ubuntu-1804", "ssh_host": "root@ubuntu-1804", - "workflow": "ubuntu-22.04-build.yml", + "workflow": "ubuntu-build.yml", "_python_comment": "18.04's default python3 is 3.6, but runtests.py uses subprocess capture_output (3.7+); run the suite under the 3.10 that's also installed.", "python": "python3.10", "_configure_flags_comment": "18.04's libzstd 1.3.3 lacks ZSTD_minCLevel (configure aborts), so disable zstd; also disable lz4 so the default -z compressor is zlib -- with lz4 as the default, compress-options' --compress-level=9 check fails since lz4 has no levels. xxhash is fine.", @@ -110,10 +110,10 @@ ] }, { - "_comment": "Ubuntu 20.04 older-LTS backport coverage on a root@ box; no 20.04 runner image exists so it mirrors the 22.04 workflow.", + "_comment": "Ubuntu 20.04 older-LTS backport coverage on a root@ box; no 20.04 runner image exists so it mirrors the 22.04 matrix lane.", "name": "ubuntu-2004", "ssh_host": "root@ubuntu-2004", - "workflow": "ubuntu-22.04-build.yml", + "workflow": "ubuntu-build.yml", "configure_flags": [ "--with-rrsync" ], @@ -126,7 +126,7 @@ "_comment": "Builds unprivileged (like a CI runner) and runs the suite via sudo; the nonroot pass reruns the privilege-sensitive tests as the ssh user. protocols: [30, 29] runs the check30/check29 passes on this NO-xattrat kernel: the CI's proto-29 step runs here, but the only other proto-29 fleet box (ubuntu-2604) is xattrat, so without this the (no-xattrat x proto-29) cell is uncovered.", "name": "ubuntu-2204", "ssh_host": "runner@ubuntu-2204", - "workflow": "ubuntu-22.04-build.yml", + "workflow": "ubuntu-build.yml", "privilege": "sudo", "nonroot": true, "protocols": [ diff --git a/testsuite/fleettest.py b/testsuite/fleettest.py index 154514c89..9ccc7db3c 100755 --- a/testsuite/fleettest.py +++ b/testsuite/fleettest.py @@ -6,10 +6,10 @@ --use-tcp) in parallel, and prints one report of only the UNEXPECTED results -- a fast local pre-flight for the GitHub CI matrix. -Each target maps 1:1 to a .github/workflows/*.yml job: the per-target configure -flags mirror that workflow, and the pipe-run RSYNC_EXPECT_SKIPPED list is PARSED -from the workflow (not hardcoded). The --use-tcp run never sets an expected-skip -list (matching the workflows), so only test FAILs matter there. +Each target maps to a .github/workflows/*.yml job or matrix lane: the per-target +configure flags mirror that lane, and the pipe-run RSYNC_EXPECT_SKIPPED list is +PARSED from the workflow (not hardcoded). The --use-tcp run never sets an +expected-skip list (matching the workflows), so only test FAILs matter there. The tcp pass runs only the tests that can reach the daemon transport, because it follows a full pipe pass over the very same build: --use-tcp is observable only @@ -167,7 +167,7 @@ class Target: name: str ssh_host: str | None # null in JSON => run locally - workflow: str # filename under .github/workflows + workflow: str # workflow containing the matching job or matrix lane configure_flags: list[str] make: str = "make" # e.g. "gmake" on the BSDs/Solaris env_prefix: str = "" # exported before configure AND make (e.g. PATH)