name: EasyTier Test on: workflow_dispatch: push: branches: [ "develop", "main", "releases/**" ] pull_request: branches: [ "develop", "main" ] types: [ opened, synchronize, reopened, ready_for_review ] concurrency: # A new PR or branch push must not cancel a full build. group: ${{ github.workflow }}-${{ (github.event_name == 'workflow_dispatch' || startsWith(github.ref_name, 'releases/')) && 'full' || 'check' }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: CARGO_TERM_COLOR: always # RUSTC_WRAPPER: "sccache" # SCCACHE_GHA_ENABLED: "true" defaults: run: # necessary for windows shell: bash jobs: ci-scripts: name: Test CI scripts runs-on: ubuntu-latest if: github.event_name != 'pull_request' || !github.event.pull_request.draft steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v5 with: node-version: 22 - run: node --test .github/scripts/*.test.js pre_job: runs-on: ubuntu-latest if: github.event_name != 'pull_request' || !github.event.pull_request.draft env: RELEVANT_PATHS: >- ["Cargo.toml", "Cargo.lock", "rust-toolchain.toml", ".cargo/**", "pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "easytier/**", "easytier-core/**", "easytier-proto/**", "easytier-rpc-build/**", "easytier-web/**", "easytier-gui/src-tauri/**", "tauri-plugin-vpnservice/**", "easytier-contrib/**", ".github/workflows/test.yml", ".github/actions/**"] outputs: full_build: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.ref_name, 'releases/') }} should_skip: >- ${{ ( steps.skip_check.outputs.should_skip == 'true' || ( github.event_name == 'pull_request' && steps.path_filter.outputs.relevant != 'true' ) ) && github.event_name != 'workflow_dispatch' && !startsWith(github.ref_name, 'releases/') }} steps: - id: skip_check if: github.event_name != 'workflow_dispatch' && !startsWith(github.ref_name, 'releases/') uses: fkirc/skip-duplicate-actions@v5 with: concurrent_skipping: 'never' skip_after_successful_duplicate: 'true' paths: ${{ env.RELEVANT_PATHS }} - id: path_filter if: >- github.event_name == 'pull_request' && steps.skip_check.outputs.should_skip != 'true' uses: dorny/paths-filter@v4 with: filters: | relevant: ${{ env.RELEVANT_PATHS }} check: name: Run linters & check runs-on: ubuntu-latest needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' steps: - uses: actions/checkout@v5 - name: Prepare build environment uses: ./.github/actions/prepare-build with: gui: true pnpm: ${{ needs.pre_job.outputs.full_build == 'true' }} token: ${{ secrets.GITHUB_TOKEN }} - uses: actions-rust-lang/setup-rust-toolchain@v1 with: components: rustfmt,clippy target: wasm32-wasip1 cache: false rustflags: '' - uses: Swatinem/rust-cache@v2 with: shared-key: test-check save-if: ${{ github.event_name != 'pull_request' }} - uses: taiki-e/install-action@cargo-hack if: needs.pre_job.outputs.full_build == 'true' - name: Check formatting if: ${{ !cancelled() }} run: cargo fmt --all -- --check - name: Check Clippy if: ${{ !cancelled() }} run: cargo clippy --all-targets --features full --all -- -D warnings - name: Check features if: ${{ !cancelled() && needs.pre_job.outputs.full_build == 'true' }} run: cargo hack check --package easytier --each-feature --exclude-features macos-ne --verbose - name: Check WASI if: ${{ !cancelled() }} run: >- cargo check --package easytier-core --lib --target wasm32-wasip1 --features management-rpc,proxy-smoltcp-stack,ring-crypto,wasi-crypto-offload - name: Check Cargo.lock is up to date if: ${{ !cancelled() }} run: | if ! cargo metadata --format-version 1 --locked > /dev/null; then echo "::error::Cargo.lock is out of date. Run cargo generate-lockfile or cargo build locally, then commit Cargo.lock." exit 1 fi pre-test: name: Build test runs-on: ubuntu-latest needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' steps: - uses: actions/checkout@v5 - name: Prepare build environment uses: ./.github/actions/prepare-build with: gui: false pnpm: false token: ${{ secrets.GITHUB_TOKEN }} - uses: Swatinem/rust-cache@v2 - uses: taiki-e/install-action@nextest - name: Archive test run: >- cargo nextest archive --archive-file tests.tar.zst --package easytier --package easytier-core --features full - uses: actions/upload-artifact@v5 with: name: tests path: tests.tar.zst retention-days: 1 test_matrix: name: Test (${{ matrix.name }}) runs-on: ubuntu-latest needs: [ pre_job, pre-test ] if: needs.pre_job.outputs.should_skip != 'true' strategy: fail-fast: false matrix: include: - name: "easytier" opts: "-E 'not test(tests::three_node)' --test-threads 1 --no-fail-fast" - name: "three_node" opts: "-E 'test(tests::three_node) and not test(subnet_proxy_three_node_test)' --test-threads 1 --no-fail-fast" - name: "three_node::subnet_proxy_three_node_test" opts: "-E 'test(subnet_proxy_three_node_test)' --test-threads 1 --no-fail-fast" steps: - uses: actions/checkout@v5 - name: Setup tools for test run: sudo apt install bridge-utils - name: Setup upnpd for test run: | sudo apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y miniupnpd miniupnpd-iptables iptables - name: Setup system for test run: | sudo modprobe br_netfilter sudo modprobe tun if [ ! -e /dev/net/tun ]; then sudo mkdir -p /dev/net sudo mknod /dev/net/tun c 10 200 fi sudo sysctl net.bridge.bridge-nf-call-iptables=0 sudo sysctl net.bridge.bridge-nf-call-ip6tables=0 sudo sysctl net.ipv6.conf.lo.disable_ipv6=0 sudo ip addr add 2001:db8::2/64 dev lo - uses: taiki-e/install-action@nextest - name: Download tests uses: actions/download-artifact@v4 with: name: tests - name: Run tests run: | sudo prlimit --pid $$ --nofile=1048576:1048576 sudo -E env "PATH=$PATH" EASYTIER_LINUX_BPF_INTEGRATION=required \ cargo nextest run --archive-file tests.tar.zst ${{ matrix.opts }} test: runs-on: ubuntu-latest needs: [ pre_job, ci-scripts, check, pre-test, test_matrix ] if: ${{ always() }} steps: - name: Mark result as failed if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') run: exit 1