mirror of
https://github.com/openai/codex.git
synced 2026-04-24 06:35:50 +00:00
fix: System skills marker includes nested folders recursively (#10350)
Updated system skills bundled with Codex were not correctly replacing the user's skills in their .system folder. - Fix `.codex-system-skills.marker` not updating by hashing embedded system skills recursively (nested dirs + file contents), so updates trigger a reinstall. - Added a build Cargo hook to rerun if there are changes in `src/skills/assets/samples/*`, ensuring embedded skill updates rebuild correctly under caching. - Add a small unit test to ensure nested entries are included in the fingerprint.
This commit is contained in:
@@ -3,6 +3,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
name = "codex-core"
|
||||
version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
27
codex-rs/core/build.rs
Normal file
27
codex-rs/core/build.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let samples_dir = Path::new("src/skills/assets/samples");
|
||||
if !samples_dir.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed={}", samples_dir.display());
|
||||
visit_dir(samples_dir);
|
||||
}
|
||||
|
||||
fn visit_dir(dir: &Path) {
|
||||
let entries = match fs::read_dir(dir) {
|
||||
Ok(entries) => entries,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
if path.is_dir() {
|
||||
visit_dir(&path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,21 +86,8 @@ fn read_marker(path: &AbsolutePathBuf) -> Result<String, SystemSkillsError> {
|
||||
}
|
||||
|
||||
fn embedded_system_skills_fingerprint() -> String {
|
||||
let mut items: Vec<(String, Option<u64>)> = SYSTEM_SKILLS_DIR
|
||||
.entries()
|
||||
.iter()
|
||||
.map(|entry| match entry {
|
||||
include_dir::DirEntry::Dir(dir) => (dir.path().to_string_lossy().to_string(), None),
|
||||
include_dir::DirEntry::File(file) => {
|
||||
let mut file_hasher = DefaultHasher::new();
|
||||
file.contents().hash(&mut file_hasher);
|
||||
(
|
||||
file.path().to_string_lossy().to_string(),
|
||||
Some(file_hasher.finish()),
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut items = Vec::new();
|
||||
collect_fingerprint_items(&SYSTEM_SKILLS_DIR, &mut items);
|
||||
items.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
@@ -112,6 +99,25 @@ fn embedded_system_skills_fingerprint() -> String {
|
||||
format!("{:x}", hasher.finish())
|
||||
}
|
||||
|
||||
fn collect_fingerprint_items(dir: &Dir<'_>, items: &mut Vec<(String, Option<u64>)>) {
|
||||
for entry in dir.entries() {
|
||||
match entry {
|
||||
include_dir::DirEntry::Dir(subdir) => {
|
||||
items.push((subdir.path().to_string_lossy().to_string(), None));
|
||||
collect_fingerprint_items(subdir, items);
|
||||
}
|
||||
include_dir::DirEntry::File(file) => {
|
||||
let mut file_hasher = DefaultHasher::new();
|
||||
file.contents().hash(&mut file_hasher);
|
||||
items.push((
|
||||
file.path().to_string_lossy().to_string(),
|
||||
Some(file_hasher.finish()),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the embedded `include_dir::Dir` to disk under `dest`.
|
||||
///
|
||||
/// Preserves the embedded directory structure.
|
||||
@@ -163,3 +169,28 @@ impl SystemSkillsError {
|
||||
Self::Io { action, source }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SYSTEM_SKILLS_DIR;
|
||||
use super::collect_fingerprint_items;
|
||||
|
||||
#[test]
|
||||
fn fingerprint_traverses_nested_entries() {
|
||||
let mut items = Vec::new();
|
||||
collect_fingerprint_items(&SYSTEM_SKILLS_DIR, &mut items);
|
||||
let mut paths: Vec<String> = items.into_iter().map(|(path, _)| path).collect();
|
||||
paths.sort_unstable();
|
||||
|
||||
assert!(
|
||||
paths
|
||||
.binary_search_by(|probe| probe.as_str().cmp("skill-creator/SKILL.md"))
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
paths
|
||||
.binary_search_by(|probe| probe.as_str().cmp("skill-creator/scripts/init_skill.py"))
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user