blob: 8950df6b65e706c630a191f7913bf182ac53d3c0 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
//! Current Git observations use the original Session's workspace authority.
use super::*;
use maka_capabilities::call::Scope;
use std::{io, path::Path, sync::atomic::AtomicBool};
impl Owner {
pub async fn git_status(
self: &Arc<Self>,
input: SessionRead,
scope: &Scope,
) -> Result<Option<GitStatus>, String> {
self.resource_work(scope, move |owner, scope| {
Box::pin(async move {
owner
.read_git(
&input.session_id,
&scope,
|workspace, private_root, cancel| {
maka_fs_tools::git::status(
Path::new(&workspace.path),
&workspace.directory_identity,
private_root,
workspace.worktree.as_deref(),
cancel,
)
},
)
.await
})
})
.await
}
pub async fn git_diff(
self: &Arc<Self>,
input: GitDiffRead,
scope: &Scope,
) -> Result<Option<GitDiff>, String> {
input
.path
.validate()
.map_err(|_| "agent_git_input_invalid")?;
self.resource_work(scope, move |owner, scope| {
Box::pin(async move {
owner
.read_git(
&input.session_id,
&scope,
move |workspace, private_root, cancel| {
maka_fs_tools::git::diff(
Path::new(&workspace.path),
&workspace.directory_identity,
private_root,
workspace.worktree.as_deref(),
input.area,
&input.path,
cancel,
)
},
)
.await
})
})
.await
}
async fn read_git<T: std::marker::Send + 'static>(
&self,
session_id: &str,
scope: &Scope,
read: impl FnOnce(&Workspace, &Path, Arc<AtomicBool>) -> io::Result<T>
+ std::marker::Send
+ 'static,
) -> Result<T, String> {
let original = self.session(session_id).await?.configuration;
workspace::validate(
&self.workspace_roots,
&original.workspace,
&self.managed_worktrees,
)
.await?;
let current = self.session(session_id).await?.configuration;
if current.workspace != original.workspace
|| current.boundary_revision != original.boundary_revision
{
return Err("agent_git_changed".into());
}
if scope.cancellation.is_cancelled() || self.stop.is_cancelled() {
return Err("agent_git_cancelled".into());
}
let private_root = self
.store
.owned_root()
.map_err(|_| "agent_root_unavailable")?
.canonical_path()
.to_owned();
let selected = original.workspace.clone();
let session = session_id.to_owned();
let result = worktrees::blocking(scope, &self.stop, move |cancel| {
read(&selected, &private_root, cancel).map_err(|error| {
let cause = git_failure(&error);
if cause != "agent_git_cancelled" {
tracing::warn!(stage = "git_read", cause, session_id = %session,
io_kind = ?error.kind(), os_code = error.raw_os_error(), "Workspace Git query failed");
}
cause.to_owned()
})
}).await;
if scope.cancellation.is_cancelled() || self.stop.is_cancelled() {
return Err("agent_git_cancelled".into());
}
let current = self.session(session_id).await?.configuration;
if current.workspace != original.workspace
|| current.boundary_revision != original.boundary_revision
{
return Err("agent_git_changed".into());
}
workspace::validate(
&self.workspace_roots,
&original.workspace,
&self.managed_worktrees,
)
.await?;
result
}
}
// The backend sanitizes errors; keep only its closed vocabulary at this boundary.
fn git_failure(error: &io::Error) -> &'static str {
match error.to_string().as_str() {
"git_invalid_path" => "agent_git_invalid_path",
"git_unsupported" => "agent_git_unsupported",
"git_private" => "agent_git_private",
"git_cancelled" => "agent_git_cancelled",
"git_changed" => "agent_git_changed",
"git_unavailable" => "agent_git_unavailable",
_ => "agent_git_unavailable",
}
}