diff --git a/cliff.toml b/cliff.toml deleted file mode 100644 index f31e1bd89c..0000000000 --- a/cliff.toml +++ /dev/null @@ -1,46 +0,0 @@ -# https://git-cliff.org/docs/configuration - -[changelog] -header = """ -# Changelog - -You can install any of these versions: `npm install -g @openai/codex@` -""" - -body = """ -{% if version -%} -## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} -{%- else %} -## [unreleased] -{% endif %} - -{%- for group, commits in commits | group_by(attribute="group") %} -### {{ group | striptags | trim }} - -{% for commit in commits %}- {% if commit.scope %}*({{ commit.scope }})* {% endif %}{% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }} -{% endfor %} - -{%- endfor -%} -""" - -footer = """ - -""" - -trim = true -postprocessors = [] - -[git] -conventional_commits = true - -commit_parsers = [ - { message = "^feat", group = "🚀 Features" }, - { message = "^fix", group = "🪲 Bug Fixes" }, - { message = "^bump", group = "🛳️ Release" }, - # Fallback – skip anything that didn't match the above rules. - { message = ".*", group = "💼 Other" }, -] - -filter_unconventional = false -sort_commits = "oldest" -topo_order = false \ No newline at end of file diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 3561550db5..6ac51d403f 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -3077,6 +3077,7 @@ dependencies = [ "anyhow", "codex-login", "codex-protocol", + "mime_guess", "pretty_assertions", "sentry", "tracing", diff --git a/codex-rs/feedback/Cargo.toml b/codex-rs/feedback/Cargo.toml index 032f0398be..dd89ddb76b 100644 --- a/codex-rs/feedback/Cargo.toml +++ b/codex-rs/feedback/Cargo.toml @@ -11,6 +11,7 @@ workspace = true anyhow = { workspace = true } codex-login = { workspace = true } codex-protocol = { workspace = true } +mime_guess = { workspace = true } sentry = { version = "0.46" } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/codex-rs/feedback/src/lib.rs b/codex-rs/feedback/src/lib.rs index 7c27d2b3b0..cd97349c48 100644 --- a/codex-rs/feedback/src/lib.rs +++ b/codex-rs/feedback/src/lib.rs @@ -4,6 +4,7 @@ use std::collections::btree_map::Entry; use std::fs; use std::io::Write; use std::io::{self}; +use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; @@ -593,10 +594,22 @@ impl FeedbackSnapshot { .map(|s| s.to_string_lossy().to_string()) .unwrap_or_else(|| "extra-log.log".to_string()) }); + let content_type = match Path::new(&filename) + .extension() + .and_then(|extension| extension.to_str()) + { + Some(extension) if extension.eq_ignore_ascii_case("jsonl") => { + "text/plain".to_string() + } + _ => mime_guess::from_path(&filename) + .first_or_octet_stream() + .essence_str() + .to_string(), + }; attachments.push(Attachment { buffer: data, filename, - content_type: Some("text/plain".to_string()), + content_type: Some(content_type), ty: None, }); } @@ -774,6 +787,10 @@ mod tests { b"Connectivity diagnostics\n\n- Proxy environment variables are set and may affect connectivity.\n - HTTPS_PROXY = https://example.com:443".to_vec() ); assert_eq!(attachments_with_diagnostics[3].buffer, b"rollout".to_vec()); + assert_eq!( + attachments_with_diagnostics[3].content_type.as_deref(), + Some("text/plain") + ); assert_eq!( OsStr::new(attachments_with_diagnostics[3].filename.as_str()), OsStr::new(extra_filename.as_str()) @@ -794,6 +811,62 @@ mod tests { fs::remove_file(extra_path).expect("extra attachment should be removed"); } + #[test] + fn path_backed_attachments_use_binary_content_types() { + let suffix = ThreadId::new(); + let gzip_filename = format!("codex-desktop-app-logs-{suffix}.tar.gz"); + let unknown_filename = format!("codex-feedback-extra-{suffix}.binunknown"); + let gzip_path = std::env::temp_dir().join(&gzip_filename); + let unknown_path = std::env::temp_dir().join(&unknown_filename); + let gzip_bytes = b"\x1f\x8b\x08\x00\xff"; + let unknown_bytes = b"\x00\x9f\x92\x96"; + fs::write(&gzip_path, gzip_bytes).expect("gzip attachment should be written"); + fs::write(&unknown_path, unknown_bytes).expect("unknown attachment should be written"); + + let attachments = CodexFeedback::new() + .snapshot(/*session_id*/ None) + .feedback_attachments( + /*include_logs*/ false, + &[], + &[ + FeedbackAttachmentPath { + path: gzip_path.clone(), + attachment_filename_override: None, + }, + FeedbackAttachmentPath { + path: unknown_path.clone(), + attachment_filename_override: None, + }, + ], + /*logs_override*/ None, + ); + + fs::remove_file(gzip_path).expect("gzip attachment should be removed"); + fs::remove_file(unknown_path).expect("unknown attachment should be removed"); + assert_eq!( + attachments + .iter() + .map(|attachment| ( + attachment.filename.as_str(), + attachment.content_type.as_deref(), + attachment.buffer.as_slice(), + )) + .collect::>(), + vec![ + ( + gzip_filename.as_str(), + Some("application/gzip"), + gzip_bytes.as_slice(), + ), + ( + unknown_filename.as_str(), + Some("application/octet-stream"), + unknown_bytes.as_slice(), + ), + ] + ); + } + #[test] fn upload_tags_include_client_tags_and_preserve_reserved_fields() { let mut tags = BTreeMap::new();