mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Add curated plugin catalog revision markers
This commit is contained in:
@@ -27,6 +27,7 @@ const OPENAI_PLUGINS_GIT_URL: &str = "https://github.com/openai/plugins.git";
|
||||
const CURATED_PLUGINS_FETCH_REF: &str = "refs/codex/curated-sync";
|
||||
const CURATED_PLUGINS_RELATIVE_DIR: &str = ".tmp/plugins";
|
||||
const CURATED_PLUGINS_SHA_FILE: &str = ".tmp/plugins.sha";
|
||||
const CURATED_PLUGINS_CATALOG_REVISION_FILE: &str = ".codex-plugin-catalog-revision";
|
||||
const CURATED_PLUGINS_SYNC_LOCK_FILE: &str = ".tmp/plugins.sync.lock";
|
||||
const CURATED_PLUGINS_BACKUP_ARCHIVE_FALLBACK_VERSION: &str = "export-backup";
|
||||
const CURATED_PLUGINS_GIT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
@@ -67,6 +68,10 @@ fn curated_plugins_sha_path(codex_home: &Path) -> PathBuf {
|
||||
codex_home.join(CURATED_PLUGINS_SHA_FILE)
|
||||
}
|
||||
|
||||
pub(crate) fn curated_plugins_catalog_revision_path(repo_path: &Path) -> PathBuf {
|
||||
repo_path.join(CURATED_PLUGINS_CATALOG_REVISION_FILE)
|
||||
}
|
||||
|
||||
pub fn sync_openai_plugins_repo(codex_home: &Path) -> Result<String, String> {
|
||||
sync_openai_plugins_repo_with_transport_overrides(
|
||||
codex_home,
|
||||
@@ -164,6 +169,7 @@ fn sync_openai_plugins_repo_via_git(codex_home: &Path, git_binary: &str) -> Resu
|
||||
let local_sha = read_local_git_or_sha_file(&repo_path, &sha_path, git_binary);
|
||||
|
||||
if local_sha.as_deref() == Some(remote_sha.as_str()) && repo_path.join(".git").is_dir() {
|
||||
ensure_curated_plugins_catalog_revision(&repo_path, &remote_sha)?;
|
||||
return Ok(remote_sha);
|
||||
}
|
||||
|
||||
@@ -196,6 +202,7 @@ fn sync_openai_plugins_repo_via_git(codex_home: &Path, git_binary: &str) -> Resu
|
||||
}
|
||||
|
||||
ensure_marketplace_manifest_exists(staged_repo_dir.path())?;
|
||||
write_curated_plugins_catalog_revision(staged_repo_dir.path(), &remote_sha)?;
|
||||
activate_curated_repo(&repo_path, staged_repo_dir)?;
|
||||
write_curated_plugins_sha(&sha_path, &remote_sha)?;
|
||||
Ok(remote_sha)
|
||||
@@ -299,6 +306,7 @@ fn sync_openai_plugins_repo_via_http(
|
||||
let local_sha = read_sha_file(&sha_path);
|
||||
|
||||
if local_sha.as_deref() == Some(remote_sha.as_str()) && repo_path.is_dir() {
|
||||
ensure_curated_plugins_catalog_revision(&repo_path, &remote_sha)?;
|
||||
return Ok(remote_sha);
|
||||
}
|
||||
|
||||
@@ -306,6 +314,7 @@ fn sync_openai_plugins_repo_via_http(
|
||||
let zipball_bytes = runtime.block_on(fetch_curated_repo_zipball(api_base_url, &remote_sha))?;
|
||||
extract_zipball_to_dir(&zipball_bytes, staged_repo_dir.path())?;
|
||||
ensure_marketplace_manifest_exists(staged_repo_dir.path())?;
|
||||
write_curated_plugins_catalog_revision(staged_repo_dir.path(), &remote_sha)?;
|
||||
activate_curated_repo(&repo_path, staged_repo_dir)?;
|
||||
write_curated_plugins_sha(&sha_path, &remote_sha)?;
|
||||
Ok(remote_sha)
|
||||
@@ -329,6 +338,7 @@ fn sync_openai_plugins_repo_via_backup_archive(
|
||||
ensure_marketplace_manifest_exists(staged_repo_dir.path())?;
|
||||
let export_version = read_extracted_backup_archive_git_sha(staged_repo_dir.path())?
|
||||
.unwrap_or_else(|| CURATED_PLUGINS_BACKUP_ARCHIVE_FALLBACK_VERSION.to_string());
|
||||
write_curated_plugins_catalog_revision(staged_repo_dir.path(), &export_version)?;
|
||||
activate_curated_repo(&repo_path, staged_repo_dir)?;
|
||||
write_curated_plugins_sha(&sha_path, &export_version)?;
|
||||
Ok(export_version)
|
||||
@@ -564,6 +574,24 @@ fn write_curated_plugins_sha(sha_path: &Path, remote_sha: &str) -> Result<(), St
|
||||
})
|
||||
}
|
||||
|
||||
fn ensure_curated_plugins_catalog_revision(repo_path: &Path, revision: &str) -> Result<(), String> {
|
||||
let revision_path = curated_plugins_catalog_revision_path(repo_path);
|
||||
if read_sha_file(&revision_path).as_deref() == Some(revision) {
|
||||
return Ok(());
|
||||
}
|
||||
write_curated_plugins_catalog_revision(repo_path, revision)
|
||||
}
|
||||
|
||||
fn write_curated_plugins_catalog_revision(repo_path: &Path, revision: &str) -> Result<(), String> {
|
||||
let revision_path = curated_plugins_catalog_revision_path(repo_path);
|
||||
std::fs::write(&revision_path, format!("{revision}\n")).map_err(|err| {
|
||||
format!(
|
||||
"failed to write curated plugins catalog revision file {}: {err}",
|
||||
revision_path.display()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn read_local_git_or_sha_file(
|
||||
repo_path: &Path,
|
||||
sha_path: &Path,
|
||||
|
||||
@@ -21,6 +21,14 @@ fn write_file(path: &Path, contents: &str) {
|
||||
std::fs::write(path, contents).unwrap();
|
||||
}
|
||||
|
||||
fn assert_curated_catalog_revision(repo_path: &Path, expected_revision: &str) {
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(curated_plugins_catalog_revision_path(repo_path))
|
||||
.expect("read catalog revision"),
|
||||
format!("{expected_revision}\n")
|
||||
);
|
||||
}
|
||||
|
||||
fn write_curated_plugin(root: &Path, plugin_name: &str) {
|
||||
let plugin_root = root.join("plugins").join(plugin_name);
|
||||
write_file(
|
||||
@@ -343,6 +351,7 @@ exit 1
|
||||
let repo_path = curated_plugins_repo_path(tmp.path());
|
||||
assert!(repo_path.join(".git").is_dir());
|
||||
assert_curated_gmail_repo(&repo_path);
|
||||
assert_curated_catalog_revision(&repo_path, sha);
|
||||
assert_eq!(read_curated_plugins_sha(tmp.path()).as_deref(), Some(sha));
|
||||
let invocations = std::fs::read_to_string(invocation_log).expect("read invocation log");
|
||||
assert_eq!(
|
||||
@@ -451,7 +460,9 @@ fn sync_openai_plugins_repo_via_git_succeeds_with_local_rewritten_remote() {
|
||||
.expect("git sync should succeed");
|
||||
|
||||
assert_eq!(synced_sha, sha);
|
||||
assert_curated_gmail_repo(&curated_plugins_repo_path(tmp.path()));
|
||||
let curated_repo_path = curated_plugins_repo_path(tmp.path());
|
||||
assert_curated_gmail_repo(&curated_repo_path);
|
||||
assert_curated_catalog_revision(&curated_repo_path, &sha);
|
||||
assert_eq!(
|
||||
read_curated_plugins_sha(tmp.path()).as_deref(),
|
||||
Some(sha.as_str())
|
||||
@@ -505,6 +516,7 @@ fn sync_openai_plugins_repo_via_git_succeeds_with_local_rewritten_remote() {
|
||||
.expect("incremental git sync should succeed");
|
||||
|
||||
assert_eq!(synced_sha, updated_sha);
|
||||
assert_curated_catalog_revision(&curated_plugins_repo_path(tmp.path()), &updated_sha);
|
||||
assert!(
|
||||
curated_plugins_repo_path(tmp.path())
|
||||
.join("plugins/linear/.codex-plugin/plugin.json")
|
||||
@@ -556,11 +568,16 @@ fn sync_openai_plugins_repo_via_git_succeeds_with_local_rewritten_remote() {
|
||||
assert!(!has_plugins_clone_dirs(tmp.path()));
|
||||
|
||||
let unchanged_sync_invocation_count = invocation_log_contents.lines().count();
|
||||
std::fs::remove_file(curated_plugins_catalog_revision_path(
|
||||
&curated_plugins_repo_path(tmp.path()),
|
||||
))
|
||||
.expect("remove catalog revision before unchanged sync");
|
||||
let synced_sha =
|
||||
sync_openai_plugins_repo_via_git(tmp.path(), git_wrapper.to_str().expect("utf8 path"))
|
||||
.expect("unchanged git sync should succeed");
|
||||
|
||||
assert_eq!(synced_sha, updated_sha);
|
||||
assert_curated_catalog_revision(&curated_plugins_repo_path(tmp.path()), &updated_sha);
|
||||
let invocation_log = std::fs::read_to_string(&invocation_log).expect("read sync invocations");
|
||||
let unchanged_sync_invocations = invocation_log
|
||||
.lines()
|
||||
@@ -599,6 +616,7 @@ async fn sync_openai_plugins_repo_falls_back_to_http_when_git_is_unavailable() {
|
||||
let repo_path = curated_plugins_repo_path(tmp.path());
|
||||
assert_eq!(synced_sha, sha);
|
||||
assert_curated_gmail_repo(&repo_path);
|
||||
assert_curated_catalog_revision(&repo_path, sha);
|
||||
assert_eq!(read_curated_plugins_sha(tmp.path()).as_deref(), Some(sha));
|
||||
}
|
||||
|
||||
@@ -796,6 +814,7 @@ async fn sync_openai_plugins_repo_skips_archive_download_when_sha_matches() {
|
||||
|
||||
assert_eq!(read_curated_plugins_sha(tmp.path()).as_deref(), Some(sha));
|
||||
assert!(repo_path.join(".agents/plugins/marketplace.json").is_file());
|
||||
assert_curated_catalog_revision(&repo_path, sha);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -824,6 +843,7 @@ async fn sync_openai_plugins_repo_falls_back_to_export_archive_when_no_snapshot_
|
||||
let repo_path = curated_plugins_repo_path(tmp.path());
|
||||
assert_eq!(synced_sha, export_sha);
|
||||
assert_curated_gmail_repo(&repo_path);
|
||||
assert_curated_catalog_revision(&repo_path, export_sha);
|
||||
assert_eq!(
|
||||
read_curated_plugins_sha(tmp.path()).as_deref(),
|
||||
Some(export_sha)
|
||||
|
||||
Reference in New Issue
Block a user