Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions crates/pet/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
// Licensed under the MIT License.

fn main() {
println!("cargo:rerun-if-env-changed=PET_BUILD_ID");
println!("cargo:rerun-if-env-changed=BUILD_BUILDID");

if let Some(build_id) = std::env::var("PET_BUILD_ID")
.ok()
.or_else(|| std::env::var("BUILD_BUILDID").ok())
.filter(|value| !value.is_empty())
{
println!("cargo:rustc-env=PET_BUILD_ID={build_id}");
}

#[cfg(target_os = "windows")]
{
if std::env::var("CARGO_BIN_NAME").is_err() {
Expand Down
35 changes: 35 additions & 0 deletions crates/pet/src/jsonrpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ pub fn start_jsonrpc_server() {
};

let mut handlers = HandlersKeyedByMethodName::new(Arc::new(context));
handlers.add_request_handler("info", handle_info);
handlers.add_request_handler("configure", handle_configure);
handlers.add_request_handler("refresh", handle_refresh);
handlers.add_request_handler("resolve", handle_resolve);
Expand All @@ -516,6 +517,29 @@ pub fn start_jsonrpc_server() {
start_server(&handlers)
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InfoResponse {
pub pet_version: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub build_id: Option<String>,
}

impl InfoResponse {
fn current() -> Self {
Self {
pet_version: env!("CARGO_PKG_VERSION").to_string(),
build_id: option_env!("PET_BUILD_ID")
.filter(|value| !value.is_empty())
.map(ToString::to_string),
}
}
}

pub fn handle_info(_context: Arc<Context>, id: u32, _params: Value) {
send_reply(id, Some(InfoResponse::current()));
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ConfigureOptions {
Expand Down Expand Up @@ -1512,6 +1536,17 @@ mod tests {
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot generated:
-1552

The build_id assertion .is_none_or(|build_id| !build_id.is_empty()) is tautological — InfoResponse::current() already filters empty strings via .filter(|value| !value.is_empty()), so build_id can only ever be None or Some(non_empty). This assertion cannot fail regardless of implementation behavior. Consider adding a brief comment explaining why the assertion is intentionally weak (env var absent in local builds), or simply drop the build_id check and only assert pet_version.

[verified]


#[test]
fn test_info_response_uses_package_version_and_optional_build_id() {
let info = InfoResponse::current();

assert_eq!(info.pet_version, env!("CARGO_PKG_VERSION"));
assert!(info
.build_id
.as_deref()
.is_none_or(|build_id| !build_id.is_empty()));
}

#[test]
fn test_parse_refresh_options_rejects_non_empty_array() {
assert!(parse_refresh_options(json!([{"searchKind": "Conda"}])).is_err());
Expand Down
11 changes: 11 additions & 0 deletions crates/pet/tests/jsonrpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ pub struct RefreshResult {
pub duration: u128,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PetInfoResponse {
pub pet_version: String,
pub build_id: Option<String>,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EnvironmentNotification {
Expand Down Expand Up @@ -156,6 +163,10 @@ impl PetJsonRpcClient {
)
}

pub fn info(&self) -> Result<PetInfoResponse, String> {
self.send_request("info", json!({}), DEFAULT_REQUEST_TIMEOUT)
}

#[allow(dead_code)]
pub fn resolve(&self, executable: &str) -> Result<Value, String> {
self.send_request_value(
Expand Down
13 changes: 13 additions & 0 deletions crates/pet/tests/jsonrpc_server_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ fn assert_single_environment(
assert_eq!(environment.error, None);
}

#[test]
fn info_reports_pet_version_and_optional_build_id() {
let client = PetJsonRpcClient::spawn().expect("failed to spawn PET server");

let info = client.info().expect("info request failed");

assert_eq!(info.pet_version, env!("CARGO_PKG_VERSION"));
assert!(info
.build_id
.as_deref()
.is_none_or(|build_id| !build_id.is_empty()));
}

#[test]
fn configure_and_workspace_refresh_report_fake_venv() {
let client = PetJsonRpcClient::spawn().expect("failed to spawn PET server");
Expand Down
29 changes: 29 additions & 0 deletions docs/JSONRPC.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,35 @@ For samples using JSONRPC, please have a look at the [sample.js](./sample.js) fi

Any requests/notifications not documented here are not supported.

# Info Request

Returns metadata about the running PET binary. This request does not require a prior
`configure` request. Clients can cache this response and attach it to PET-related
telemetry such as `refresh` and `resolve` timings.

_Request_:

- method: `info`
- params: `{}`

_Response_:

- result: `InfoResponse` defined as below.

```typescript
interface InfoResponse {
/**
* PET package version baked into the binary at build time.
* Pre-release builds may include a suffix such as `0.1.0-dev.12345`.
*/
petVersion: string;
/**
* Build identifier baked into the binary when built by CI.
*/
buildId?: string;
}
```

# Configuration Request

This should always be the first request sent to the tool.
Expand Down
Loading