diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..810afc1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,158 @@ +name: Release + +# Pushing a tag such as v0.4.0 builds the `rust-agent-runtime` CLI on native runners, +# packages it and publishes a GitHub release. Running the workflow by hand +# (workflow_dispatch) builds and uploads the archives as workflow artifacts +# but never publishes. See docs/releasing.md. + +on: + push: + tags: [ "v*" ] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + prepare: + name: Check version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Read workspace version and compare with the tag + id: version + run: | + version="$(cargo metadata --no-deps --format-version 1 --locked \ + | jq -r '.packages[] | select(.name == "rust-agent-runtime") | .version')" + if [ -z "$version" ] || [ "$version" = "null" ]; then + echo "::error::could not read the rust-agent-runtime version from Cargo.toml" + exit 1 + fi + if [ "$GITHUB_REF_TYPE" = "tag" ] && [ "$GITHUB_REF_NAME" != "v$version" ]; then + echo "::error::tag $GITHUB_REF_NAME does not match the workspace version $version (expected v$version). Fix the version in Cargo.toml or push the right tag." + exit 1 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + + build: + name: Build ${{ matrix.target }} + needs: prepare + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + include: + # Linux builds run on the oldest hosted images so the binaries link + # against an older glibc (2.35) and run on most current distributions. + - { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 } + - { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04-arm } + - { target: x86_64-apple-darwin, os: macos-15-intel } + - { target: aarch64-apple-darwin, os: macos-latest } + - { target: x86_64-pc-windows-msvc, os: windows-latest } + env: + VERSION: ${{ needs.prepare.outputs.version }} + TARGET: ${{ matrix.target }} + steps: + - uses: actions/checkout@v4 + + - name: Set up Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + targets: ${{ matrix.target }} + + - name: Build + run: cargo build --release --locked -p rust-agent-runtime --target ${{ matrix.target }} + + - name: Package (unix) + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + name="rust-agent-runtime-${VERSION}-${TARGET}" + mkdir -p "dist/${name}" + cp "target/${TARGET}/release/rust-agent-runtime" LICENSE "dist/${name}/" + cp packaging/README.txt "dist/${name}/README.txt" + tar -C dist -czf "dist/${name}.tar.gz" "${name}" + cd dist + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "${name}.tar.gz" > "${name}.tar.gz.sha256" + else + shasum -a 256 "${name}.tar.gz" > "${name}.tar.gz.sha256" + fi + cat "${name}.tar.gz.sha256" + + - name: Package (windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $name = "rust-agent-runtime-$env:VERSION-$env:TARGET" + New-Item -ItemType Directory -Force -Path "dist/$name" | Out-Null + Copy-Item "target/$env:TARGET/release/rust-agent-runtime.exe" "dist/$name/" + Copy-Item LICENSE "dist/$name/" + Copy-Item packaging/README.txt "dist/$name/README.txt" + Compress-Archive -Path "dist/$name" -DestinationPath "dist/$name.zip" + $hash = (Get-FileHash "dist/$name.zip" -Algorithm SHA256).Hash.ToLower() + "$hash $name.zip" | Set-Content -Path "dist/$name.zip.sha256" -Encoding ascii + Get-Content "dist/$name.zip.sha256" + + - name: Smoke test the binary + shell: bash + run: | + set -euo pipefail + exe="target/${TARGET}/release/rust-agent-runtime" + if [ "$RUNNER_OS" = "Windows" ]; then exe="${exe}.exe"; fi + out="$("$exe" --version)" + echo "$out" + case "$out" in + *"${VERSION}"*) ;; + *) echo "::error::binary reports '$out', expected version ${VERSION}"; exit 1 ;; + esac + + - name: Upload archives + uses: actions/upload-artifact@v4 + with: + name: rust-agent-runtime-${{ matrix.target }} + path: | + dist/*.tar.gz + dist/*.zip + dist/*.sha256 + if-no-files-found: error + + release: + name: Publish release + # Only for pushed tags; a manual run stops after the build job. + if: github.ref_type == 'tag' + needs: [prepare, build] + runs-on: ubuntu-latest + permissions: + contents: write + env: + VERSION: ${{ needs.prepare.outputs.version }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/download-artifact@v4 + with: + path: dist + pattern: rust-agent-runtime-* + merge-multiple: true + + - name: List files + run: ls -l dist + + - name: Create the GitHub release + run: | + set -euo pipefail + flags="" + case "$VERSION" in *-*) flags="--prerelease" ;; esac + gh release create "$GITHUB_REF_NAME" \ + --repo "$GITHUB_REPOSITORY" \ + --verify-tag \ + --title "rust-agent-runtime $VERSION" \ + --generate-notes \ + $flags \ + dist/*.tar.gz dist/*.zip dist/*.sha256 diff --git a/CHANGELOG.md b/CHANGELOG.md index 99d2c0e..8089b7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,11 @@ All notable changes to this project are documented here. ## [Unreleased] +## [0.1.2] - 2026-10-05 + ### Added +- Prebuilt binaries for Linux (x86_64, aarch64), macOS (Intel, Apple Silicon) and Windows on each release, with SHA-256 files, plus `install.sh` and `install.ps1` installers. - `provider::mock::MockProvider`, a deterministic scripted provider (including scripted failures) used by the agent and worker tests. - Tests for provider outage handling: a provider 503 after a verified patch fails the task while diff --git a/Cargo.lock b/Cargo.lock index 5d04919..07ac70d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,7 @@ dependencies = [ [[package]] name = "rust-agent-runtime" -version = "0.1.1" +version = "0.1.2" dependencies = [ "libc", "serde", diff --git a/Cargo.toml b/Cargo.toml index b190a8e..ae9c181 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rust-agent-runtime" -version = "0.1.1" +version = "0.1.2" edition = "2021" rust-version = "1.85" license = "MIT" diff --git a/README.md b/README.md index 21de5ce..4a8b33f 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,22 @@ Rust Agent Runtime executes automated coding tasks using language models while e ## Quick start +### Try the released CLI (macOS or Linux) + +The prebuilt CLI does not require Rust. The installer downloads a release, checks its SHA-256 checksum, and installs to `~/.local/bin`: + +```bash +curl -fsSL https://raw.githubusercontent.com/rustfuture/rust-agent-runtime/main/install.sh | sh +export PATH="$HOME/.local/bin:$PATH" +rust-agent-runtime --version +# Watch the runtime finish a task with a scripted model; no API key needed +rust-agent-runtime demo +``` + +Prebuilt Linux binaries require glibc; unsupported platforms can build from source. On Windows, use the [PowerShell installer](install.ps1). + +### Build from source + You need Git and Rust 1.85 or newer, including Cargo ([rustup](https://rustup.rs/)). The first build downloads Cargo dependencies. Run the commands below from the cloned repository. ### Build and test diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..cbf4bc0 --- /dev/null +++ b/install.ps1 @@ -0,0 +1,129 @@ +# Installer for the `rust-agent-runtime` CLI (https://github.com/rustfuture/rust-agent-runtime). +# +# irm https://raw.githubusercontent.com/rustfuture/rust-agent-runtime/main/install.ps1 | iex +# +# Downloads the prebuilt Windows release archive, checks its SHA-256 checksum and +# copies rust-agent-runtime.exe into an install directory. It needs no administrator rights. +# It does NOT change your PATH: if the install directory is not on it, the script +# prints the command to add it. +# +# Environment variables (all optional): +# RAR_VERSION Version to install, e.g. 0.3.0 or v0.3.0 (default: latest release) +# RAR_INSTALL_DIR Where to put rust-agent-runtime.exe (default: $env:LOCALAPPDATA\rust-agent-runtime\bin) +# RAR_DOWNLOAD_BASE Base URL, local directory or file:// URL holding the archive +# and its .sha256 file, instead of the GitHub release. Needs +# RAR_VERSION. Meant for mirrors and testing. +# RAR_REPO GitHub repository (default: rustfuture/rust-agent-runtime) +# +# Everything runs inside a function and reports problems with `throw`, never `exit`, +# so piping this script to `iex` cannot close your terminal. + +function Install-Tool { + Set-StrictMode -Version 2 + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' # the progress bar makes Windows PowerShell 5.1 downloads very slow + + $repo = if ($env:RAR_REPO) { $env:RAR_REPO } else { 'rustfuture/rust-agent-runtime' } + $fromSource = "cargo install --git https://github.com/$repo rust-agent-runtime" + + # Windows PowerShell 5.1 does not enable TLS 1.2 by default. + try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { } + + # --- platform ------------------------------------------------------------- + if ($PSVersionTable.PSVersion.Major -ge 6 -and -not $IsWindows) { + throw "This installer is for Windows. On macOS or Linux use: curl -fsSL https://raw.githubusercontent.com/$repo/main/install.sh | sh" + } + $osArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString() + switch ($osArch) { + 'X64' { } + 'Arm64' { Write-Host 'Windows on ARM: installing the x64 build, which runs under emulation.' } + default { throw "Unsupported CPU architecture: $osArch. No prebuilt binary is available; if you have Rust (1.85 or newer), build from source: $fromSource" } + } + $target = 'x86_64-pc-windows-msvc' + + # --- version -------------------------------------------------------------- + if ($env:RAR_VERSION) { + $version = $env:RAR_VERSION.TrimStart('v') + } elseif ($env:RAR_DOWNLOAD_BASE) { + throw 'RAR_DOWNLOAD_BASE is set, so also set RAR_VERSION (for example $env:RAR_VERSION = ''0.3.0'').' + } else { + Write-Host 'Looking up the latest release...' + try { + $release = Invoke-RestMethod -UseBasicParsing -Uri "https://api.github.com/repos/$repo/releases/latest" -Headers @{ 'User-Agent' = 'rust-agent-runtime-installer' } + } catch { + throw 'Could not look up the latest release (no release published yet, no network, or the GitHub API rate limit). Set $env:RAR_VERSION, e.g. $env:RAR_VERSION = ''0.3.0''' + } + $version = ([string]$release.tag_name).TrimStart('v') + } + if ($version -notmatch '^[0-9A-Za-z.+-]+$') { throw "Invalid version: $version" } + + $installDir = if ($env:RAR_INSTALL_DIR) { $env:RAR_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'rust-agent-runtime\bin' } + $name = "rust-agent-runtime-$version-$target" + $archive = "$name.zip" + $base = if ($env:RAR_DOWNLOAD_BASE) { $env:RAR_DOWNLOAD_BASE } else { "https://github.com/$repo/releases/download/v$version" } + $base = $base.TrimEnd('/', '\') + + $tmp = Join-Path ([IO.Path]::GetTempPath()) ("rust-agent-runtime-install-" + [Guid]::NewGuid().ToString('N')) + New-Item -ItemType Directory -Path $tmp | Out-Null + try { + Write-Host "Installing rust-agent-runtime $version for $target" + + # --- download --------------------------------------------------------- + foreach ($file in @($archive, "$archive.sha256")) { + $dest = Join-Path $tmp $file + try { + if ($base -match '^https?://') { + Invoke-WebRequest -UseBasicParsing -Uri "$base/$file" -OutFile $dest + } else { + $localBase = if ($base -match '^file://') { ([Uri]$base).LocalPath } else { $base } + Copy-Item -LiteralPath (Join-Path $localBase $file) -Destination $dest + } + } catch { + throw "Could not download $base/$file (is $version a published version for $target?)" + } + } + + # --- verify ----------------------------------------------------------- + $sumLine = (Get-Content -LiteralPath (Join-Path $tmp "$archive.sha256") -TotalCount 1) + $expected = ($sumLine -split '\s+')[0].ToLower() + if ($expected -notmatch '^[0-9a-f]{64}$') { throw "The checksum file $archive.sha256 is not valid." } + $actual = (Get-FileHash -LiteralPath (Join-Path $tmp $archive) -Algorithm SHA256).Hash.ToLower() + if ($expected -ne $actual) { + Write-Error "Checksum mismatch for $archive`n expected: $expected`n actual: $actual" -ErrorAction Continue + throw 'Refusing to install a file that does not match its checksum.' + } + Write-Host 'Checksum OK' + + # --- unpack and install ---------------------------------------------- + Expand-Archive -LiteralPath (Join-Path $tmp $archive) -DestinationPath $tmp -Force + $exe = Join-Path $tmp "$name\rust-agent-runtime.exe" + if (-not (Test-Path -LiteralPath $exe)) { throw "The archive does not contain $name\rust-agent-runtime.exe" } + + New-Item -ItemType Directory -Force -Path $installDir | Out-Null + $installed = Join-Path $installDir 'rust-agent-runtime.exe' + try { + Copy-Item -LiteralPath $exe -Destination $installed -Force + } catch { + throw "Could not write $installed. If rust-agent-runtime is running (for example as an agent hook), close it and try again; or set `$env:RAR_INSTALL_DIR to another directory." + } + + Write-Host "Installed: $installed" + & $installed --version + if ($LASTEXITCODE -ne 0) { throw "The installed binary did not run. Build from source instead: $fromSource" } + + # --- PATH hint (never edited automatically) -------------------------- + $dirs = @($env:PATH -split ';' | Where-Object { $_ } | ForEach-Object { $_.TrimEnd('\') }) + if ($dirs -notcontains $installDir.TrimEnd('\')) { + Write-Host '' + Write-Host "$installDir is not on your PATH. To add it for your user account, run:" + Write-Host " [Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path','User') + ';$installDir', 'User')" + Write-Host 'then open a new terminal. (This installer does not change your PATH.)' + } + Write-Host '' + Write-Host "Next: run 'rust-agent-runtime --help' to get started." + } finally { + Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue + } +} + +Install-Tool diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..c7f34dc --- /dev/null +++ b/install.sh @@ -0,0 +1,186 @@ +#!/bin/sh +# Installer for the `rust-agent-runtime` CLI (https://github.com/rustfuture/rust-agent-runtime). +# +# curl -fsSL https://raw.githubusercontent.com/rustfuture/rust-agent-runtime/main/install.sh | sh +# +# Downloads the prebuilt release archive for this machine, checks its SHA-256 +# checksum and copies `rust-agent-runtime` into an install directory. It never uses sudo +# and never edits shell startup files. +# +# Environment variables (all optional): +# RAR_VERSION Version to install, e.g. 0.3.0 or v0.3.0 (default: latest release) +# RAR_INSTALL_DIR Where to put the binary (default: $HOME/.local/bin) +# RAR_DOWNLOAD_BASE Base URL (or file:// URL) holding the archive and its +# .sha256 file, instead of the GitHub release. Needs +# RAR_VERSION. Meant for mirrors and testing. +# RAR_REPO GitHub repository (default: rustfuture/rust-agent-runtime) + +set -eu + +REPO="${RAR_REPO:-rustfuture/rust-agent-runtime}" +FROM_SOURCE="cargo install --git https://github.com/${REPO} rust-agent-runtime" + +say() { printf '%s\n' "$*"; } +err() { printf 'rust-agent-runtime-install: error: %s\n' "$*" >&2; } +die() { err "$*"; exit 1; } + +have() { command -v "$1" >/dev/null 2>&1; } + +unsupported() { + err "$1" + err "No prebuilt binary is available for this platform. If you have Rust (1.85 or newer), build from source:" + err " ${FROM_SOURCE}" + exit 1 +} + +detect_target() { + os="$(uname -s)" + arch="$(uname -m)" + + case "$arch" in + x86_64 | amd64) arch="x86_64" ;; + aarch64 | arm64) arch="aarch64" ;; + *) unsupported "unsupported CPU architecture: ${arch}" ;; + esac + + case "$os" in + Linux) + # The Linux binaries are linked against glibc; musl (e.g. Alpine) cannot run them. + if have ldd && ldd --version 2>&1 | grep -qi musl; then + unsupported "this Linux uses musl libc, but the prebuilt binaries need glibc" + fi + TARGET="${arch}-unknown-linux-gnu" + ;; + Darwin) + # A shell running under Rosetta reports x86_64 on an Apple Silicon Mac. + if [ "$arch" = "x86_64" ] && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || true)" = "1" ]; then + arch="aarch64" + fi + TARGET="${arch}-apple-darwin" + ;; + MINGW* | MSYS* | CYGWIN*) + die "this looks like Windows. Use the PowerShell installer instead: irm https://raw.githubusercontent.com/${REPO}/main/install.ps1 | iex" + ;; + *) + unsupported "unsupported operating system: ${os}" + ;; + esac +} + +# fetch URL DEST +fetch() { + if have curl; then + curl -fsSL --retry 3 -o "$2" "$1" || return 1 + elif have wget; then + wget -q -O "$2" "$1" || return 1 + else + die "need curl or wget to download files" + fi +} + +# fetch_stdout URL +fetch_stdout() { + if have curl; then + curl -fsSL --retry 3 "$1" + elif have wget; then + wget -q -O - "$1" + else + die "need curl or wget to download files" + fi +} + +resolve_version() { + if [ -n "${RAR_VERSION:-}" ]; then + VERSION="${RAR_VERSION#v}" + elif [ -n "${RAR_DOWNLOAD_BASE:-}" ]; then + die "RAR_DOWNLOAD_BASE is set, so also set RAR_VERSION (for example RAR_VERSION=0.3.0)" + else + say "Looking up the latest release..." + json="$(fetch_stdout "https://api.github.com/repos/${REPO}/releases/latest")" || + die "could not look up the latest release (no release published yet, no network, or the GitHub API rate limit). Set RAR_VERSION, e.g. RAR_VERSION=0.3.0" + tag="$(printf '%s\n' "$json" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)" + [ -n "$tag" ] || die "could not read the latest release tag. Set RAR_VERSION, e.g. RAR_VERSION=0.3.0" + VERSION="${tag#v}" + fi + case "$VERSION" in + '' | *[!0-9A-Za-z.+-]*) die "invalid version: ${VERSION}" ;; + esac +} + +sha256_of() { + if have sha256sum; then + sha256sum "$1" | awk '{print $1}' + elif have shasum; then + shasum -a 256 "$1" | awk '{print $1}' + else + die "need sha256sum or shasum to verify the download" + fi +} + +main() { + have tar || die "need tar to unpack the download" + have curl || have wget || die "need curl or wget to download files" + + detect_target + resolve_version + + INSTALL_DIR="${RAR_INSTALL_DIR:-${HOME:?HOME is not set}/.local/bin}" + NAME="rust-agent-runtime-${VERSION}-${TARGET}" + ARCHIVE="${NAME}.tar.gz" + BASE="${RAR_DOWNLOAD_BASE:-https://github.com/${REPO}/releases/download/v${VERSION}}" + BASE="${BASE%/}" + + TMP="$(mktemp -d 2>/dev/null || mktemp -d -t rust-agent-runtime-install)" + trap 'rm -rf "$TMP"' EXIT INT TERM HUP + + say "Installing rust-agent-runtime ${VERSION} for ${TARGET}" + fetch "${BASE}/${ARCHIVE}" "${TMP}/${ARCHIVE}" || + die "could not download ${BASE}/${ARCHIVE} (is ${VERSION} a published version for ${TARGET}?)" + fetch "${BASE}/${ARCHIVE}.sha256" "${TMP}/${ARCHIVE}.sha256" || + die "could not download ${BASE}/${ARCHIVE}.sha256" + + expected="$(awk 'NR == 1 {print tolower($1)}' "${TMP}/${ARCHIVE}.sha256")" + actual="$(sha256_of "${TMP}/${ARCHIVE}")" + case "$expected" in + '' | *[!0-9a-f]*) die "the checksum file ${ARCHIVE}.sha256 is not valid" ;; + esac + if [ "${#expected}" -ne 64 ]; then + die "the checksum file ${ARCHIVE}.sha256 is not valid" + fi + if [ "$expected" != "$actual" ]; then + err "checksum mismatch for ${ARCHIVE}" + err " expected: ${expected}" + err " actual: ${actual}" + die "refusing to install a file that does not match its checksum" + fi + say "Checksum OK" + + tar -xzf "${TMP}/${ARCHIVE}" -C "$TMP" || die "could not unpack ${ARCHIVE}" + [ -f "${TMP}/${NAME}/rust-agent-runtime" ] || die "the archive does not contain ${NAME}/rust-agent-runtime" + + mkdir -p "$INSTALL_DIR" || die "could not create ${INSTALL_DIR}; set RAR_INSTALL_DIR to a writable directory" + # Copy under a temporary name and rename, so a running rust-agent-runtime is replaced safely. + cp "${TMP}/${NAME}/rust-agent-runtime" "${INSTALL_DIR}/.rust-agent-runtime.new.$$" || + die "could not write to ${INSTALL_DIR}; set RAR_INSTALL_DIR to a writable directory" + chmod 755 "${INSTALL_DIR}/.rust-agent-runtime.new.$$" + mv -f "${INSTALL_DIR}/.rust-agent-runtime.new.$$" "${INSTALL_DIR}/rust-agent-runtime" + + say "Installed: ${INSTALL_DIR}/rust-agent-runtime" + "${INSTALL_DIR}/rust-agent-runtime" --version || + die "the installed binary did not run; it may not suit this system. Build from source instead: ${FROM_SOURCE}" + + case ":${PATH}:" in + *":${INSTALL_DIR}:"*) ;; + *) + say "" + say "${INSTALL_DIR} is not on your PATH. Add it, for example:" + say " export PATH=\"${INSTALL_DIR}:\$PATH\"" + say "(put that line in your shell startup file to keep it; this installer does not edit it)" + ;; + esac + + say "" + say "Next: run 'rust-agent-runtime --help' to get started." +} + +main "$@" diff --git a/packaging/README.txt b/packaging/README.txt new file mode 100644 index 0000000..edee26b --- /dev/null +++ b/packaging/README.txt @@ -0,0 +1,27 @@ +rust-agent-runtime (rust-agent-runtime) +======================================= + +Runs automated coding tasks with execution timeouts, command restrictions and verification tests. This archive holds the `rust-agent-runtime` command-line tool, built from the tagged release. + +Install +------- +Put the `rust-agent-runtime` binary (`rust-agent-runtime.exe` on Windows) in a directory on your PATH, +or use the installer, which does that for you and checks the checksum: + + macOS / Linux: curl -fsSL https://raw.githubusercontent.com/rustfuture/rust-agent-runtime/main/install.sh | sh + Windows: irm https://raw.githubusercontent.com/rustfuture/rust-agent-runtime/main/install.ps1 | iex + +Then: + + rust-agent-runtime --version + rust-agent-runtime --help + +Verify this download +-------------------- +Each archive has a matching .sha256 file on the release page: + + sha256sum -c rust-agent-runtime--.tar.gz.sha256 (Linux) + shasum -a 256 -c rust-agent-runtime--.tar.gz.sha256 (macOS) + +Docs and source: https://github.com/rustfuture/rust-agent-runtime +License: see LICENSE diff --git a/src/main.rs b/src/main.rs index f9ca46a..e106e2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,9 @@ fn main() -> ExitCode { fn run() -> io::Result<()> { let args: Vec = env::args().skip(1).collect(); match args.as_slice() { + [flag] if flag == "--version" || flag == "-V" => { + println!("rust-agent-runtime {}", env!("CARGO_PKG_VERSION")); + } [command] if command == "demo" => demo::run()?, [command, data_dir, id] if command == "enqueue" => { let mut runtime = Runtime::open(Path::new(data_dir))?; @@ -66,7 +69,7 @@ fn run() -> io::Result<()> { _ => { return Err(io::Error::new( io::ErrorKind::InvalidInput, - "usage: rust-agent-runtime ", + "usage: rust-agent-runtime <--version | demo | enqueue DATA_DIR ID | cancel DATA_DIR ID | run DATA_DIR ID WORKSPACE | status DATA_DIR | watch DATA_DIR INTERVAL_MS>", )); } } diff --git a/tests/cli_version.rs b/tests/cli_version.rs new file mode 100644 index 0000000..c82e082 --- /dev/null +++ b/tests/cli_version.rs @@ -0,0 +1,14 @@ +use std::process::Command; + +#[test] +fn version_flag_prints_the_package_version() { + let output = Command::new(env!("CARGO_BIN_EXE_rust-agent-runtime")) + .arg("--version") + .output() + .expect("run CLI"); + assert!(output.status.success()); + assert_eq!( + String::from_utf8_lossy(&output.stdout).trim(), + format!("rust-agent-runtime {}", env!("CARGO_PKG_VERSION")) + ); +}