diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..203d843 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,67 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + # Format, lint and test on Linux. + check: + name: Check (fmt, clippy, test) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Cache cargo + uses: Swatinem/rust-cache@v2 + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Clippy + run: cargo clippy --all-targets -- -D warnings + + - name: Tests + run: cargo test --workspace + + # Compile on every target we ship so per-OS code paths (e.g. the platform + # keyring backends in hagent-core) are exercised on each platform. + build: + name: Build (${{ matrix.target }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + - target: aarch64-apple-darwin + os: macos-latest + - target: x86_64-pc-windows-msvc + os: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache cargo + uses: Swatinem/rust-cache@v2 + with: + shared-key: ${{ matrix.target }} + + - name: Build + run: cargo build --workspace --target ${{ matrix.target }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1242a2d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,144 @@ +name: Release + +on: + push: + tags: ["v*"] + workflow_dispatch: + inputs: + dry_run: + description: "Dry run (build artifacts but don't create a release or publish)" + type: boolean + default: true + +permissions: + contents: write + +env: + CARGO_TERM_COLOR: always + +jobs: + # Build a release binary for each target and package it with a sha256 checksum. + build: + name: Build (${{ matrix.target }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + - target: aarch64-unknown-linux-gnu + os: ubuntu-24.04-arm + - target: aarch64-apple-darwin + os: macos-latest + - target: x86_64-apple-darwin + os: macos-13 + - target: x86_64-pc-windows-msvc + os: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Install musl toolchain + if: matrix.target == 'x86_64-unknown-linux-musl' + run: sudo apt-get update && sudo apt-get install -y musl-tools + + - name: Cache cargo + uses: Swatinem/rust-cache@v2 + with: + shared-key: release-${{ matrix.target }} + + - name: Build + run: cargo build -p hagent --release --target ${{ matrix.target }} + + - name: Package (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + TARGET="${{ matrix.target }}" + mkdir -p staging artifacts + cp "target/$TARGET/release/hagent" staging/ + cp README.md LICENSE-APACHE LICENSE-MIT staging/ + ARCHIVE="hagent-${TARGET}.tar.gz" + tar czf "artifacts/$ARCHIVE" -C staging . + (cd artifacts && shasum -a 256 "$ARCHIVE") >> "artifacts/checksums.txt" + echo "Built $ARCHIVE" + + - name: Package (Windows) + if: runner.os == 'Windows' + shell: bash + run: | + TARGET="${{ matrix.target }}" + mkdir -p staging artifacts + cp "target/$TARGET/release/hagent.exe" staging/ + cp README.md LICENSE-APACHE LICENSE-MIT staging/ + ARCHIVE="hagent-${TARGET}.zip" + (cd staging && 7z a -tzip "../artifacts/$ARCHIVE" .) + (cd artifacts && sha256sum "$ARCHIVE") >> "artifacts/checksums.txt" + echo "Built $ARCHIVE" + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-${{ matrix.target }} + path: artifacts/ + retention-days: 3 + + # Create the GitHub Release with all binaries (tag push, not dry-run). + release: + name: Create Release + needs: build + if: github.ref_type == 'tag' && !(github.event_name == 'workflow_dispatch' && inputs.dry_run) + runs-on: ubuntu-latest + steps: + - name: Download all build artifacts + uses: actions/download-artifact@v4 + with: + pattern: build-* + path: all-artifacts + + - name: Collect release assets + run: | + mkdir -p release + for dir in all-artifacts/build-*/; do + mv "$dir"/*.tar.gz release/ 2>/dev/null || true + mv "$dir"/*.zip release/ 2>/dev/null || true + cat "$dir/checksums.txt" >> release/hagent-checksums-sha256.txt 2>/dev/null || true + done + echo "Release assets:" + ls -lh release/ + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + files: release/* + + # Publish hagent-core then hagent to crates.io (tag push, not dry-run). + # `--workspace` publishes members in dependency order and waits for the index. + publish-crate: + name: Publish to crates.io + needs: build + if: github.ref_type == 'tag' && !(github.event_name == 'workflow_dispatch' && inputs.dry_run) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo + uses: Swatinem/rust-cache@v2 + with: + shared-key: publish + + - name: Publish to crates.io + run: cargo publish --workspace + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/hagent/Cargo.toml b/hagent/Cargo.toml index 579a734..df2fb6a 100644 --- a/hagent/Cargo.toml +++ b/hagent/Cargo.toml @@ -11,7 +11,9 @@ name = "hagent" path = "src/main.rs" [dependencies] -hagent-core = { path = "../hagent-core" } +# `version` (alongside `path`) is required for `cargo publish`: the path is used +# for local/workspace builds, the version is what resolves on crates.io. +hagent-core = { path = "../hagent-core", version = "0.1.0" } tokio.workspace = true serde.workspace = true serde_json.workspace = true