mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
fix(ci): restore green baseline (#162)
* fix(ci): restore green baseline * test(cli): normalize purge preview paths * test(cli): avoid temp path spelling in purge preview
This commit is contained in:
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- Upgraded `crossbeam-epoch` to the RUSTSEC-2026-0204 fixed release.
|
||||
- Fixed the Docker wrapper on macOS with rootless Docker so host-config
|
||||
commands (`install-mcp`, `install-hooks`, `install-instructions`, and
|
||||
related setup/removal commands) keep the `-u 0:0` mapping needed to write
|
||||
bind-mounted agent configuration as the invoking host user.
|
||||
- Stabilized the Windows uninstall purge-preview regression test against
|
||||
verbatim temp-path spelling.
|
||||
|
||||
## [1.11.1] - 2026-07-09
|
||||
|
||||
### Fixed
|
||||
|
||||
Generated
+2
-2
@@ -696,9 +696,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
version = "0.9.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
+12
-4
@@ -319,8 +319,11 @@ esac
|
||||
# write to a host bind mount: other commands only touch the /data named
|
||||
# volume, which isn't host-visible and doesn't have this problem, so leave
|
||||
# their UID mapping alone.
|
||||
if [ "${WRITES_HOST_FILES}" -eq 1 ] \
|
||||
&& "${DOCKER}" info --format '{{.SecurityOptions}}' 2>/dev/null | grep -q 'name=rootless'; then
|
||||
ROOTLESS_DOCKER=0
|
||||
if "${DOCKER}" info --format '{{.SecurityOptions}}' 2>/dev/null | grep -q 'name=rootless'; then
|
||||
ROOTLESS_DOCKER=1
|
||||
fi
|
||||
if [ "${WRITES_HOST_FILES}" -eq 1 ] && [ "${ROOTLESS_DOCKER}" -eq 1 ]; then
|
||||
USER_ARGS=(-u 0:0)
|
||||
fi
|
||||
|
||||
@@ -350,8 +353,13 @@ case "$(uname -s 2>/dev/null || true)" in
|
||||
# create log files or write to the data dir, crashing with
|
||||
# "Permission denied" in the rolling file appender.
|
||||
# Omitting -u lets the container run as its default (uid 1000)
|
||||
# which matches the volume owner.
|
||||
USER_ARGS=()
|
||||
# which matches the volume owner. Keep the earlier rootless-Docker
|
||||
# exception for commands that write host config: under rootless Docker,
|
||||
# UID 0 maps back to the invoking host user and is the only mapping that
|
||||
# can write bind-mounted agent config reliably.
|
||||
if ! { [ "${ROOTLESS_DOCKER}" -eq 1 ] && [ "${WRITES_HOST_FILES}" -eq 1 ]; }; then
|
||||
USER_ARGS=()
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -211,9 +211,19 @@ fn macos_wrapper_routes_urls_by_real_subcommand() {
|
||||
// logging argv to a file for `run` (read back by the test).
|
||||
#[cfg(unix)]
|
||||
fn run_wrapper_with_fake_docker(args: &[&str], docker_info_stdout: &str) -> String {
|
||||
run_wrapper_with_fake_docker_and_uname(args, docker_info_stdout, None)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn run_wrapper_with_fake_docker_and_uname(
|
||||
args: &[&str],
|
||||
docker_info_stdout: &str,
|
||||
uname_stdout: Option<&str>,
|
||||
) -> String {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let docker_args = tmp.path().join("docker-args.txt");
|
||||
let docker = tmp.path().join("docker");
|
||||
let uname = tmp.path().join("uname");
|
||||
std::fs::write(
|
||||
&docker,
|
||||
format!(
|
||||
@@ -226,22 +236,44 @@ fn run_wrapper_with_fake_docker(args: &[&str], docker_info_stdout: &str) -> Stri
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
if let Some(uname_stdout) = uname_stdout {
|
||||
std::fs::write(
|
||||
&uname,
|
||||
format!("#!/usr/bin/env bash\nprintf '{}\\n'\n", uname_stdout),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
std::fs::set_permissions(&docker, std::fs::Permissions::from_mode(0o755)).unwrap();
|
||||
if uname_stdout.is_some() {
|
||||
std::fs::set_permissions(&uname, std::fs::Permissions::from_mode(0o755)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let path = if uname_stdout.is_some() {
|
||||
Some(format!(
|
||||
"{}:{}",
|
||||
shell_path(tmp.path()),
|
||||
std::env::var("PATH").unwrap_or_default()
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut command = shell_script_command(&repo_root().join("bin/ai-memory"));
|
||||
let output = command
|
||||
command
|
||||
.args(args)
|
||||
.env("AI_MEMORY_DOCKER", shell_path(&docker))
|
||||
.env("AI_MEMORY_NO_VERSION_CHECK", "1")
|
||||
.env("AI_MEMORY_DATA_VOLUME", "test-ai-memory-data")
|
||||
.env("HOME", shell_path(tmp.path()))
|
||||
.env_remove("AI_MEMORY_SERVER_URL")
|
||||
.output()
|
||||
.unwrap();
|
||||
.env_remove("AI_MEMORY_SERVER_URL");
|
||||
if let Some(path) = path {
|
||||
command.env("PATH", path);
|
||||
}
|
||||
let output = command.output().unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"wrapper failed: stdout={} stderr={}",
|
||||
@@ -251,6 +283,15 @@ fn run_wrapper_with_fake_docker(args: &[&str], docker_info_stdout: &str) -> Stri
|
||||
std::fs::read_to_string(docker_args).unwrap()
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn run_wrapper_with_fake_rootless_docker_on_fake_macos(args: &[&str]) -> String {
|
||||
run_wrapper_with_fake_docker_and_uname(
|
||||
args,
|
||||
"[name=apparmor name=seccomp,profile=default name=rootless]",
|
||||
Some("Darwin"),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn rootless_docker_uses_root_uid_only_for_host_config_commands() {
|
||||
@@ -284,6 +325,36 @@ fn rootless_docker_uses_root_uid_only_for_host_config_commands() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn fake_macos_rootless_docker_keeps_root_uid_for_host_config_commands() {
|
||||
let args = run_wrapper_with_fake_rootless_docker_on_fake_macos(&["install-mcp"]);
|
||||
assert!(
|
||||
args.contains("-u\n0:0"),
|
||||
"macOS rootless Docker still needs uid 0 for host config writes; got {args}"
|
||||
);
|
||||
|
||||
let args = run_wrapper_with_fake_rootless_docker_on_fake_macos(&["status"]);
|
||||
assert!(
|
||||
!args.contains("-u\n0:0"),
|
||||
"macOS thin-client commands should keep Docker Desktop's default uid; got {args}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn fake_macos_rootful_docker_keeps_default_uid_for_host_config_commands() {
|
||||
let args = run_wrapper_with_fake_docker_and_uname(
|
||||
&["install-mcp"],
|
||||
"[name=seccomp,profile=default]",
|
||||
Some("Darwin"),
|
||||
);
|
||||
assert!(
|
||||
!args.contains("-u\n0:0") && !args.contains("-u\n"),
|
||||
"macOS rootful Docker should keep Docker Desktop's default uid; got {args}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn rootful_docker_keeps_host_uid_for_host_config_commands() {
|
||||
|
||||
@@ -39,7 +39,11 @@ fn command_with_home(home: &Path) -> Command {
|
||||
}
|
||||
|
||||
fn normalize_path_text(value: impl AsRef<str>) -> String {
|
||||
value.as_ref().replace('\\', "/")
|
||||
value
|
||||
.as_ref()
|
||||
.replace('\\', "/")
|
||||
.replace("//?/UNC/", "//")
|
||||
.replace("//?/", "")
|
||||
}
|
||||
|
||||
fn run_uninstall(project: &Path, home: &Path, args: &[&str]) -> std::process::Output {
|
||||
@@ -719,10 +723,14 @@ fn uninstall_dry_run_previews_purge() {
|
||||
|
||||
let stdout = String::from_utf8(out.stdout).unwrap();
|
||||
assert!(stdout.contains("would purge"), "stdout was: {stdout}");
|
||||
let normalized_stdout = normalize_path_text(&stdout);
|
||||
for sub in ["wiki", "db", "raw"] {
|
||||
let p = data.path().join(sub);
|
||||
let expected_suffix = format!("/{sub}");
|
||||
assert!(
|
||||
stdout.contains(&p.display().to_string()),
|
||||
normalized_stdout
|
||||
.lines()
|
||||
.any(|line| line.starts_with("would purge ") && line.ends_with(&expected_suffix)),
|
||||
"missing {sub} in: {stdout}"
|
||||
);
|
||||
// Dry-run must not delete.
|
||||
|
||||
Reference in New Issue
Block a user