name: CI # Runners: uvacompute (https://uvacompute.com) # To enable, set the UVA_RUNNER repo variable to the correct runner label. # runs-on: ${{ vars.UVA_RUNNER || 'ubuntu-latest' }} on: pull_request: branches: [main] push: branches: [main] workflow_dispatch: permissions: contents: write jobs: changes: name: Changes runs-on: ubuntu-latest outputs: rust: ${{ steps.check.outputs.rust }} version: ${{ steps.version.outputs.version }} tag: ${{ steps.version.outputs.tag }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: dorny/paths-filter@v3 id: filter with: filters: | rust: - 'src/**' - 'tests/**' - 'Cargo.toml' - 'Cargo.lock' - 'npm/**' - 'flake.nix' - 'flake.lock' - 'docker/**' - '.github/workflows/**' - 'Makefile' - name: Set outputs id: check run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then echo "rust=true" >> "$GITHUB_OUTPUT" else echo "rust=${{ steps.filter.outputs.rust }}" >> "$GITHUB_OUTPUT" fi - name: Calculate next version id: version if: github.event_name != 'pull_request' && steps.check.outputs.rust == 'true' run: | BASE=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE" LATEST=$(git tag -l "v${MAJOR}.${MINOR}.*" | sort -V | tail -1) if [ -z "$LATEST" ]; then NEW="$BASE" else LATEST_VER="${LATEST#v}" IFS='.' read -r _ _ LATEST_PATCH <<< "$LATEST_VER" NEW_PATCH=$((LATEST_PATCH + 1)) NEW="${MAJOR}.${MINOR}.${NEW_PATCH}" fi # Ensure the computed version does not already have a tag while git rev-parse "v${NEW}" >/dev/null 2>&1; do IFS='.' read -r MAJOR MINOR PATCH <<< "$NEW" NEW="${MAJOR}.${MINOR}.$((PATCH + 1))" done echo "version=${NEW}" >> "$GITHUB_OUTPUT" echo "tag=v${NEW}" >> "$GITHUB_OUTPUT" validate: name: Validate needs: changes if: needs.changes.outputs.rust == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: clippy - uses: Swatinem/rust-cache@v2 - uses: pnpm/action-setup@v4 with: version: 10 run_install: false - uses: actions/setup-node@v4 with: node-version: 22 cache: pnpm cache-dependency-path: site/pnpm-lock.yaml - name: Install site dependencies run: pnpm --dir site install --frozen-lockfile - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev - name: Format check run: make fmt-check - name: Clippy run: make lint - name: Unit tests run: make test-unit - name: Site format check run: make site-format-check integration: name: Integration (Xvfb) needs: changes if: needs.changes.outputs.rust == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev xvfb - name: Xvfb integration tests run: make test-integration distribution: name: Distribution Validate needs: changes if: needs.changes.outputs.rust == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - uses: actions/setup-node@v4 with: node-version: 22 - uses: cachix/install-nix-action@v30 with: extra_nix_config: | experimental-features = nix-command flakes - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev - name: Distribution validation run: make dist-validate update-manifests: name: Update Manifests needs: [changes, validate, integration, distribution] if: github.event_name != 'pull_request' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-node@v4 with: node-version: 22 - name: Update version in Cargo.toml run: | CURRENT=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') NEW="${{ needs.changes.outputs.version }}" if [ "$CURRENT" != "$NEW" ]; then sed -i "0,/^version = \"${CURRENT}\"/s//version = \"${NEW}\"/" Cargo.toml node -e 'const fs=require("node:fs"); const path="npm/deskctl/package.json"; const pkg=JSON.parse(fs.readFileSync(path,"utf8")); pkg.version=process.argv[1]; fs.writeFileSync(path, JSON.stringify(pkg, null, 2)+"\n");' "$NEW" cargo generate-lockfile fi - name: Commit, tag, and push run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" if ! git diff --quiet; then git add Cargo.toml Cargo.lock npm/deskctl/package.json git commit -m "release: ${{ needs.changes.outputs.tag }} [skip ci]" fi if ! git rev-parse "${{ needs.changes.outputs.tag }}" >/dev/null 2>&1; then git tag "${{ needs.changes.outputs.tag }}" fi git push origin main --tags build: name: Build Release Asset needs: [changes, update-manifests] if: github.event_name != 'pull_request' && needs.changes.outputs.rust == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ needs.changes.outputs.tag }} fetch-depth: 0 - uses: dtolnay/rust-toolchain@stable with: components: clippy - uses: Swatinem/rust-cache@v2 - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev - name: Clippy run: cargo clippy -- -D warnings - name: Build run: cargo build --release --locked - uses: actions/upload-artifact@v4 with: name: deskctl-linux-x86_64 path: target/release/deskctl retention-days: 7 release: name: Release needs: [changes, build, update-manifests] if: github.event_name != 'pull_request' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: deskctl-linux-x86_64 path: artifacts/ - name: Create release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | chmod +x artifacts/deskctl mv artifacts/deskctl artifacts/deskctl-linux-x86_64 cd artifacts && sha256sum deskctl-linux-x86_64 > checksums.txt && cd .. if gh release view "${{ needs.changes.outputs.tag }}" >/dev/null 2>&1; then gh release upload "${{ needs.changes.outputs.tag }}" \ artifacts/deskctl-linux-x86_64 \ artifacts/checksums.txt \ --clobber else gh release create "${{ needs.changes.outputs.tag }}" \ --title "${{ needs.changes.outputs.tag }}" \ --generate-notes \ artifacts/deskctl-linux-x86_64 \ artifacts/checksums.txt fi