name: ci on: push: pull_request: workflow_dispatch: schedule: - cron: "17 7 * * 1" jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Format check run: cargo fmt -- --check - name: Shell syntax check run: sh -n install.sh scripts/*.sh - name: Clippy run: cargo clippy --all-targets -- -D warnings - name: Test run: cargo test --all-targets - name: TUI harness run: sh scripts/tui-harness.sh - name: Build release run: cargo build --release - name: Docker SSH benchmark gate run: sh scripts/ci-docker-ssh-bench.sh fuzz-smoke: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install nightly toolchain id: nightly continue-on-error: true uses: dtolnay/rust-toolchain@nightly - name: Install cargo-fuzz id: install if: steps.nightly.outcome == 'success' continue-on-error: true run: cargo install cargo-fuzz --locked - name: Run fuzz targets briefly if: steps.nightly.outcome == 'success' && steps.install.outcome == 'success' run: | set -e if [ "${{ github.event_name }}" = "schedule" ] || [ "${{ github.event_name }}" = "workflow_dispatch" ]; then DOSH_FUZZ_SECONDS="${DOSH_FUZZ_SECONDS:-300}" sh scripts/fuzz-run.sh else sh scripts/fuzz-run.sh 20 fi - name: Note when fuzzing was skipped if: steps.nightly.outcome != 'success' || steps.install.outcome != 'success' run: echo "cargo-fuzz / nightly toolchain unavailable; skipped fuzz smoke run." windows-client: runs-on: windows-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Build Windows client run: cargo build --release --bin dosh-client --bin dosh-bench - name: Package Windows client shell: bash run: sh scripts/package-release.sh package-release: if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/') strategy: matrix: include: - os: ubuntu-latest name: linux-x86_64 - os: macos-14 name: macos-aarch64 - os: macos-13 name: macos-x86_64 - os: windows-latest name: windows-x86_64 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Package release shell: bash run: sh scripts/package-release.sh - uses: actions/upload-artifact@v4 with: name: dosh-${{ matrix.name }} path: | target/dosh-release/dosh-* !target/dosh-release/stage/** publish-gitea-release: if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/') needs: package-release runs-on: ubuntu-latest env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} GITEA_URL: ${{ secrets.GITEA_URL || 'https://git.palav.dev' }} GITEA_REPO: ${{ secrets.GITEA_REPO || 'Palav/dosh' }} steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: pattern: dosh-* path: target/dosh-release merge-multiple: true - name: Verify release artifacts run: sh scripts/verify-release-artifacts.sh - name: Skip Gitea upload without token if: env.GITEA_TOKEN == '' run: echo "GITEA_TOKEN secret is not configured; release artifacts were verified but not uploaded." - name: Upload Gitea release assets if: env.GITEA_TOKEN != '' run: | set -eu case "$GITHUB_REF" in refs/tags/*) tag="${GITHUB_REF_NAME}" ;; *) version="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | sed -n '1p')" tag="v$version" ;; esac scripts/upload-gitea-release.sh "$tag" remote-bench: runs-on: ubuntu-latest env: DOSH_BENCH_HOST: ${{ secrets.DOSH_BENCH_HOST }} DOSH_BENCH_USER: ${{ secrets.DOSH_BENCH_USER }} DOSH_BENCH_SSH_KEY: ${{ secrets.DOSH_BENCH_SSH_KEY }} DOSH_BENCH_SSH_PORT: ${{ secrets.DOSH_BENCH_SSH_PORT }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Check remote benchmark configuration id: config run: | if [ -n "$DOSH_BENCH_HOST" ] && [ -n "$DOSH_BENCH_USER" ] && [ -n "$DOSH_BENCH_SSH_KEY" ]; then echo "configured=true" >> "$GITHUB_OUTPUT" else echo "configured=false" >> "$GITHUB_OUTPUT" echo "Skipping remote benchmark; configure DOSH_BENCH_HOST, DOSH_BENCH_USER, and DOSH_BENCH_SSH_KEY secrets to enable it." fi - name: Build release if: steps.config.outputs.configured == 'true' run: cargo build --release - name: Configure SSH key if: steps.config.outputs.configured == 'true' run: | port="${DOSH_BENCH_SSH_PORT:-22}" mkdir -p ~/.ssh printf '%s\n' "$DOSH_BENCH_SSH_KEY" > ~/.ssh/dosh_bench chmod 600 ~/.ssh/dosh_bench ssh-keyscan -p "$port" "$DOSH_BENCH_HOST" >> ~/.ssh/known_hosts - name: Copy binaries if: steps.config.outputs.configured == 'true' run: | port="${DOSH_BENCH_SSH_PORT:-22}" scp -P "$port" -i ~/.ssh/dosh_bench \ target/release/dosh-server target/release/dosh-auth \ "$DOSH_BENCH_USER@$DOSH_BENCH_HOST:/tmp/" - name: Start remote server if: steps.config.outputs.configured == 'true' run: | port="${DOSH_BENCH_SSH_PORT:-22}" ssh -p "$port" -i ~/.ssh/dosh_bench "$DOSH_BENCH_USER@$DOSH_BENCH_HOST" \ 'mkdir -p ~/.local/bin && install -m 0755 /tmp/dosh-server /tmp/dosh-auth ~/.local/bin/ && pkill -f "dosh-server serve" || true; nohup ~/.local/bin/dosh-server serve >/tmp/dosh-server-ci.log 2>&1 &' sleep 1 - name: Run remote benchmark if: steps.config.outputs.configured == 'true' run: | port="${DOSH_BENCH_SSH_PORT:-22}" target/release/dosh-bench \ --server "$DOSH_BENCH_USER@$DOSH_BENCH_HOST" \ --ssh-port "$port" \ --ssh-key ~/.ssh/dosh_bench \ --ssh-auth-command "~/.local/bin/dosh-auth" \ --iterations 3