Prefer CODE_VERSION with fallback to CARGO_PKG_VERSION across codex-rs\n\n- Replace direct env!("CARGO_PKG_VERSION") with option_env!("CODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"))\n- Update clap version field, TUI upgrade banners, user agent, MCP client init, and OpenAI header\n- Verified via ./build-fast.sh

This commit is contained in:
James Peter
2025-08-25 08:39:26 +10:00
parent 729b512162
commit af3a8bcfac
12 changed files with 179 additions and 100 deletions
+32 -85
View File
@@ -40,17 +40,13 @@ jobs:
shell: bash
run: |
CURRENT_VERSION=$(node -p "require('./package.json').version")
echo "Current version in package.json: $CURRENT_VERSION"
PUBLISHED_VERSION=$(npm view @just-every/code version 2>/dev/null || echo "0.0.0")
echo "Latest published version: $PUBLISHED_VERSION"
if [ "$CURRENT_VERSION" = "$PUBLISHED_VERSION" ] || [ "$CURRENT_VERSION" \< "$PUBLISHED_VERSION" ]; then
IFS='.' read -ra V <<< "$PUBLISHED_VERSION"
NEW_VERSION="${V[0]}.${V[1]}.$((${V[2]} + 1))"
else
NEW_VERSION=$CURRENT_VERSION
fi
echo "version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
build-binaries:
@@ -89,7 +85,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
# Install Rust (needed before using cargo)
- name: Install Rust (minimal)
shell: bash
run: |
@@ -97,59 +92,24 @@ jobs:
rustup toolchain install 1.89.0 --profile minimal --target ${{ matrix.target }}
rustup default 1.89.0
# 🔧 Update workspace package version in Cargo.toml (portable)
# Prefer [workspace.package], fallback to [package] if present
- name: Set crate version in Cargo.toml (portable)
shell: bash
working-directory: codex-rs
run: |
set -euo pipefail
VERSION="${{ needs.determine-version.outputs.version }}"
echo "Embedding version ${VERSION} into codex-rs/Cargo.toml"
awk -v ver="$VERSION" '
BEGIN{in_ws_pkg=0; in_pkg=0; done=0}
/^\[workspace\.package\]/{in_ws_pkg=1; in_pkg=0}
/^\[package\]/{in_pkg=1; in_ws_pkg=0}
/^\[/{ if ($0 !~ /^\[(workspace\.package|package)\]/) { in_ws_pkg=0; in_pkg=0 } }
{
if ((in_ws_pkg || in_pkg) && !done && $0 ~ /^version[[:space:]]*=/) {
print "version = \"" ver "\""; done=1; next
}
print
}
' Cargo.toml > Cargo.toml.tmp && mv Cargo.toml.tmp Cargo.toml
echo "New version line:" && awk '/^\[(workspace\.package|package)\]/{in=1; next} /^\[/{in=0} in && /^version[[:space:]]*=/{print; exit}' Cargo.toml || true
# 🔒 Keep lockfile consistent for --frozen without changing deps
- name: Regenerate lockfile for --frozen
shell: bash
working-directory: codex-rs
run: cargo generate-lockfile
# Cache target/ + registries (to skip linking when unchanged)
# Keep target/ across runs so Cargo can no-op when code hasn't changed
- name: Setup Rust Cache (target + registries)
uses: Swatinem/rust-cache@v2
with:
prefix-key: v4-rust
shared-key: codex-${{ matrix.target }}-rust-1.89-target
prefix-key: v5-rust
shared-key: codex-${{ matrix.target }}-toolchain-1.89
workspaces: |
codex-rs -> target
cache-targets: true
cache-workspace-crates: true
cache-on-failure: true
# Stable sccache namespace per toolchain+target
- name: Configure sccache cache keys
shell: bash
run: echo "SCCACHE_GHA_VERSION=${{ matrix.target }}-rust-1.89-v4" >> "$GITHUB_ENV"
# sccache: skip compiles when possible (doesn't skip linking)
- name: Setup sccache (GHA backend)
uses: mozilla-actions/[email protected]
with:
version: v0.10.0
token: ${{ secrets.GITHUB_TOKEN }}
# Enable sccache for Rust compiles
- name: Enable sccache
shell: bash
run: |
@@ -158,18 +118,16 @@ jobs:
echo "SCCACHE_IDLE_TIMEOUT=1800" >> "$GITHUB_ENV"
echo "SCCACHE_CACHE_SIZE=10G" >> "$GITHUB_ENV"
# ---------------------------
# Platform-specific tuning
# ---------------------------
# -------- Platform tuning (minimal, proven) --------
# Linux GNU: mold linker + C/C++ through sccache (clang preferred)
# Linux GNU: use mold if available; prefer system OpenSSL
- name: Linux (gnu) tuning
if: contains(matrix.os, 'ubuntu') && contains(matrix.target, 'gnu')
shell: bash
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y mold || true
sudo apt-get install -y libssl-dev pkg-config mold || true
if command -v clang >/dev/null 2>&1; then
echo 'CC=sccache clang' >> "$GITHUB_ENV"
echo 'CXX=sccache clang++' >> "$GITHUB_ENV"
@@ -177,9 +135,10 @@ jobs:
echo 'CC=sccache gcc' >> "$GITHUB_ENV"
echo 'CXX=sccache g++' >> "$GITHUB_ENV"
fi
echo 'OPENSSL_NO_VENDOR=1' >> "$GITHUB_ENV"
echo 'RUSTFLAGS=-Awarnings -C link-arg=-fuse-ld=mold -C debuginfo=0 -C strip=symbols -C panic=abort' >> "$GITHUB_ENV"
# Linux MUSL: build & link with musl-gcc to avoid glibc symbol leakage
# Linux MUSL: reliable static build via musl-gcc (no glibc symbol leaks)
- name: Linux (musl) tuning
if: contains(matrix.os, 'ubuntu') && contains(matrix.target, 'musl')
shell: bash
@@ -187,9 +146,7 @@ jobs:
set -euo pipefail
sudo apt-get update
sudo apt-get install -y musl-tools pkg-config
# Use musl headers + libs for all C compiles (no sccache wrapper here for reliability)
echo 'CC=musl-gcc' >> "$GITHUB_ENV"
# Ensure rustc links with musl-gcc (avoids __isoc23_strtol glibc symbol)
case "${{ matrix.target }}" in
x86_64-unknown-linux-musl) echo 'CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc' >> "$GITHUB_ENV" ;;
aarch64-unknown-linux-musl) echo 'CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc' >> "$GITHUB_ENV" ;;
@@ -198,32 +155,23 @@ jobs:
echo 'OPENSSL_STATIC=1' >> "$GITHUB_ENV"
echo 'RUSTFLAGS=-Awarnings -C debuginfo=0 -C strip=symbols -C panic=abort' >> "$GITHUB_ENV"
# macOS: Homebrew LLVM + LLD; C/C++ through sccache; link via ld64.lld
- name: macOS toolchain & linker (brew clang + lld via sccache)
# macOS: stick with Apple toolchain to avoid brew overhead; still cache C via sccache
- name: macOS tuning
if: startsWith(matrix.os, 'macos-')
shell: bash
run: |
set -euo pipefail
brew install llvm lld || true
LLVM_PREFIX="$(brew --prefix llvm || true)"
LLD_PREFIX="$(brew --prefix lld || true)"
[[ -n "${LLVM_PREFIX}" && -n "${LLD_PREFIX}" ]] || { echo "brew llvm/lld not found"; exit 1; }
echo "${LLVM_PREFIX}/bin" >> "$GITHUB_PATH"
echo "SDKROOT=$(xcrun --sdk macosx --show-sdk-path)" >> "$GITHUB_ENV"
echo "CC=sccache clang" >> "$GITHUB_ENV"
echo "CXX=sccache clang++" >> "$GITHUB_ENV"
echo "RUSTFLAGS=-Awarnings -Clinker=clang -C link-arg=-fuse-ld=${LLD_PREFIX}/bin/ld64.lld -C debuginfo=0 -C strip=symbols -C panic=abort" >> "$GITHUB_ENV"
echo 'CC=sccache clang' >> "$GITHUB_ENV"
echo 'CXX=sccache clang++' >> "$GITHUB_ENV"
echo 'RUSTFLAGS=-Awarnings -C debuginfo=0 -C strip=symbols -C panic=abort' >> "$GITHUB_ENV"
# Windows: lld-link + prebuilt OpenSSL via vcpkg (cached)
- name: Cache vcpkg (Windows)
# Windows: robust vcpkg with binary caching (x-gha)
- name: Cache vcpkg data (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v4
with:
path: |
~\vcpkg\installed
~\vcpkg\packages
~\vcpkg\downloads
key: vcpkg-openssl-${{ runner.os }}-x64-windows
~\vcpkg
key: vcpkg-root-${{ runner.os }}
- name: Prepare vcpkg (Windows)
if: matrix.os == 'windows-latest'
@@ -232,9 +180,9 @@ jobs:
$Root = Join-Path $env:USERPROFILE 'vcpkg'
if (-not (Test-Path $Root)) { New-Item -ItemType Directory -Path $Root | Out-Null }
if (-not (Test-Path (Join-Path $Root '.git'))) {
git -C $Root init
git -C $Root remote remove origin 2>$null
git -C $Root remote add origin https://github.com/microsoft/vcpkg
if (Test-Path (Join-Path $Root 'src')) { Remove-Item -Recurse -Force (Join-Path $Root 'src') }
git clone https://github.com/microsoft/vcpkg $Root
} else {
git -C $Root fetch --depth 1 origin
git -C $Root checkout -q FETCH_HEAD
}
@@ -243,25 +191,26 @@ jobs:
}
"VCPKG_ROOT=$Root" >> $env:GITHUB_ENV
"VCPKG_DEFAULT_TRIPLET=x64-windows" >> $env:GITHUB_ENV
# Enable true binary caching via GitHub Actions cache service
"VCPKG_FEATURE_FLAGS=manifests,binarycaching" >> $env:GITHUB_ENV
"VCPKG_BINARY_SOURCES=clear;x-gha,readwrite" >> $env:GITHUB_ENV
- name: Enable lld-link (Windows) + install OpenSSL via vcpkg
- name: Windows OpenSSL via vcpkg + linker flags
if: matrix.os == 'windows-latest'
shell: pwsh
run: |
# Faster linker and lean release flags
"RUSTFLAGS=-Clinker=lld-link -C codegen-units=16 -C debuginfo=0 -C strip=symbols -C panic=abort -C link-arg=/OPT:REF -C link-arg=/OPT:ICF -C link-arg=/DEBUG:NONE" >> $env:GITHUB_ENV
# Ensure OpenSSL from vcpkg is used (no vendoring)
& "$env:VCPKG_ROOT\vcpkg.exe" install openssl:x64-windows
"OPENSSL_NO_VENDOR=1" >> $env:GITHUB_ENV
"RUSTFLAGS=-Awarnings -Clinker=lld-link -C codegen-units=16 -C debuginfo=0 -C strip=symbols -C panic=abort -C link-arg=/OPT:REF -C link-arg=/OPT:ICF -C link-arg=/DEBUG:NONE" >> $env:GITHUB_ENV
# Prefetch deps so --frozen works offline (needed for git deps like ratatui)
# Prefetch deps so --frozen works even with git deps
- name: Prefetch dependencies (git + registry)
working-directory: codex-rs
env:
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
run: cargo fetch --locked
# Pass chosen npm version into the Rust build (optional build.rs exposes it)
# Inject the display version without touching Cargo manifests
- name: Export CODE_VERSION for Rust build
shell: bash
run: echo "CODE_VERSION=${{ needs.determine-version.outputs.version }}" >> "$GITHUB_ENV"
@@ -269,17 +218,15 @@ jobs:
- name: Build binaries (with timings)
shell: bash
env:
CARGO_INCREMENTAL: "0" # keep off in CI; release profile + sccache benefits
CARGO_INCREMENTAL: "0" # keep off in CI; release builds + sccache
RUST_BACKTRACE: "1"
run: |
cd codex-rs
cargo build --release --frozen --locked --timings --target ${{ matrix.target }} --bin code --bin code-tui --bin code-exec
- name: sccache stats (human + json)
- name: sccache stats
shell: bash
run: |
sccache --show-stats || true
sccache --show-stats --stats-format=json || true
run: sccache --show-stats || true
- name: Prepare artifacts
shell: bash
@@ -423,4 +370,4 @@ jobs:
files: |
release-assets/*
env:
GITHUB_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -26,7 +26,7 @@ use crate::proto::ProtoCli;
#[clap(
author,
name = "code",
version = env!("CARGO_PKG_VERSION"),
version = option_env!("CODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION")),
// If a sub‑command is given, ignore requirements of the default args.
subcommand_negates_reqs = true,
// The executable is sometimes invoked via a platform‑specific name like
+3 -1
View File
@@ -148,7 +148,9 @@ impl McpConnectionManager {
},
client_info: Implementation {
name: "code-mcp-client".to_owned(),
version: env!("CARGO_PKG_VERSION").to_owned(),
version: option_env!("CODE_VERSION")
.unwrap_or(env!("CARGO_PKG_VERSION"))
.to_owned(),
title: Some("Codex".into()),
},
protocol_version: mcp_types::MCP_SCHEMA_VERSION.to_owned(),
+10 -3
View File
@@ -253,9 +253,16 @@ pub fn built_in_model_providers() -> HashMap<String, ModelProviderInfo> {
wire_api: WireApi::Responses,
query_params: None,
http_headers: Some(
[("version".to_string(), env!("CARGO_PKG_VERSION").to_string())]
.into_iter()
.collect(),
[
(
"version".to_string(),
option_env!("CODE_VERSION")
.unwrap_or(env!("CARGO_PKG_VERSION"))
.to_string(),
),
]
.into_iter()
.collect(),
),
env_http_headers: Some(
[
+1 -1
View File
@@ -1,7 +1,7 @@
const DEFAULT_ORIGINATOR: &str = "codex_cli_rs";
pub fn get_codex_user_agent(originator: Option<&str>) -> String {
let build_version = env!("CARGO_PKG_VERSION");
let build_version = option_env!("CODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
let os_info = os_info::get();
format!(
"{}/{build_version} ({} {}; {}) {}",
@@ -138,11 +138,11 @@ impl EventProcessor for EventProcessorWithHumanOutput {
/// for the session. This mirrors the information shown in the TUI welcome
/// screen.
fn print_config_summary(&mut self, config: &Config, prompt: &str) {
const VERSION: &str = env!("CARGO_PKG_VERSION");
let version = option_env!("CODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
ts_println!(
self,
"Code v{}\n--------",
VERSION
version
);
let entries = create_config_summary_entries(config);
+3 -1
View File
@@ -62,7 +62,9 @@ async fn main() -> Result<()> {
},
client_info: Implementation {
name: "code-mcp-client".to_owned(),
version: env!("CARGO_PKG_VERSION").to_owned(),
version: option_env!("CODE_VERSION")
.unwrap_or(env!("CARGO_PKG_VERSION"))
.to_owned(),
title: Some("Code".to_string()),
},
protocol_version: MCP_SCHEMA_VERSION.to_owned(),
+3 -1
View File
@@ -225,7 +225,9 @@ impl MessageProcessor {
protocol_version: params.protocol_version.clone(),
server_info: mcp_types::Implementation {
name: "code-mcp-server".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
version: option_env!("CODE_VERSION")
.unwrap_or(env!("CARGO_PKG_VERSION"))
.to_string(),
title: Some("Code".to_string()),
},
};
+2 -2
View File
@@ -227,7 +227,7 @@ pub async fn run_main(
#[allow(clippy::print_stderr)]
#[cfg(not(debug_assertions))]
if let Some(latest_version) = updates::get_upgrade_version(&config) {
let current_version = env!("CARGO_PKG_VERSION");
let current_version = option_env!("CODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
let exe = std::env::current_exe()?;
let managed_by_npm = std::env::var_os("CODEX_MANAGED_BY_NPM").is_some();
@@ -287,7 +287,7 @@ fn run_ratatui_app(
use ratatui::text::Line;
use ratatui::text::Span;
let current_version = env!("CARGO_PKG_VERSION");
let current_version = option_env!("CODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
let exe = std::env::current_exe()?;
let managed_by_npm = std::env::var_os("CODEX_MANAGED_BY_NPM").is_some();
+3 -1
View File
@@ -52,6 +52,9 @@ fn append_markdown_with_opener_and_cwd_and_bold(
for seg in split_text_and_fences(markdown_source) {
match seg {
Segment::Text(s) => {
// Rewrite our special file citation tokens into markdown links
// (e.g., vscode://file...). These will later be turned
// into OSC8 hyperlinks by the markdown renderer.
let processed = rewrite_file_citations(&s, file_opener, cwd);
let rendered = if bold_first_sentence {
MarkdownRenderer::render_with_bold_first_sentence(&processed)
@@ -127,7 +130,6 @@ fn rewrite_file_citations<'a>(
})
}
// use shared helper from `line_utils`
// Minimal code block splitting.
// - Recognizes fenced blocks opened by ``` or ~~~ (allowing leading whitespace).
+118 -1
View File
@@ -1,5 +1,6 @@
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use regex_lite::Regex;
/// Custom markdown renderer with full control over spacing and styling
pub struct MarkdownRenderer {
@@ -397,7 +398,9 @@ impl MarkdownRenderer {
fn flush_current_line(&mut self) {
if !self.current_line.is_empty() {
self.lines.push(Line::from(self.current_line.clone()));
// Autolink URLs and markdown links inside the accumulated spans.
let linked = autolink_spans(std::mem::take(&mut self.current_line));
self.lines.push(Line::from(linked));
self.current_line.clear();
}
}
@@ -416,6 +419,120 @@ impl MarkdownRenderer {
}
}
// Turn inline markdown links and bare URLs into OSC 8 hyperlinks.
// This runs late, on the already-styled spans for a line, so it preserves
// bold/italic styling while adding underlines for links and embedding the
// hyperlink escape sequences in the text.
fn autolink_spans(spans: Vec<Span<'static>>) -> Vec<Span<'static>> {
// Patterns: markdown [label](target), explicit http(s) URLs, and plain domains.
// Keep conservative to avoid false positives.
static MD_LINK_RE: once_cell::sync::OnceCell<Regex> = once_cell::sync::OnceCell::new();
static EXPL_URL_RE: once_cell::sync::OnceCell<Regex> = once_cell::sync::OnceCell::new();
static DOMAIN_RE: once_cell::sync::OnceCell<Regex> = once_cell::sync::OnceCell::new();
let md_re = MD_LINK_RE
.get_or_init(|| Regex::new(r"\[([^\]]+)\]\(([^)\s]+)\)").unwrap());
let url_re = EXPL_URL_RE
.get_or_init(|| Regex::new(r"(?i)\bhttps?://[^\s)]+").unwrap());
let dom_re = DOMAIN_RE.get_or_init(|| {
// e.g., apps.shopify.com or foo.example.io/path?x=1
Regex::new(r"\b([a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[\w\-./?%&=#]*)?)")
.unwrap()
});
let mut out: Vec<Span<'static>> = Vec::with_capacity(spans.len());
for s in spans {
let text = s.content.clone();
let mut cursor = 0usize;
let mut changed = false;
// Scan left-to-right, preferring markdown links first (explicit intent),
// then explicit URLs, then bare domains.
while cursor < text.len() {
let after = &text[cursor..];
// 1) markdown links
if let Some(m) = md_re.find(after) {
let start = cursor + m.start();
let end = cursor + m.end();
// Push any plain prefix
if cursor < start {
let mut span = s.clone();
span.content = text[cursor..start].to_string().into();
out.push(span);
}
let caps = md_re.captures(&text[start..end]).unwrap();
let label = caps.get(1).unwrap().as_str();
let target = caps.get(2).unwrap().as_str();
out.push(hyperlink_span(label, target, &s));
cursor = end;
changed = true;
continue;
}
// 2) explicit http(s) URLs
if let Some(m) = url_re.find(after) {
let start = cursor + m.start();
let end = cursor + m.end();
if cursor < start {
let mut span = s.clone();
span.content = text[cursor..start].to_string().into();
out.push(span);
}
let url = &text[start..end];
out.push(hyperlink_span(url, url, &s));
cursor = end;
changed = true;
continue;
}
// 3) bare domain (assume https)
if let Some(m) = dom_re.find(after) {
let start = cursor + m.start();
let end = cursor + m.end();
if cursor < start {
let mut span = s.clone();
span.content = text[cursor..start].to_string().into();
out.push(span);
}
let dom = &text[start..end];
// Skip if it obviously looks like an email or already in a link
if !dom.contains('@') {
let target = format!("https://{dom}");
out.push(hyperlink_span(dom, &target, &s));
cursor = end;
changed = true;
continue;
}
}
// No more matches
break;
}
if changed {
if cursor < text.len() {
let mut span = s.clone();
span.content = text[cursor..].to_string().into();
out.push(span);
}
} else {
out.push(s);
}
}
out
}
fn hyperlink_span(label: &str, target: &str, base: &Span<'static>) -> Span<'static> {
// Use OSC 8 with BEL terminators (widely supported; matches existing code).
let content = format!(
"\u{1b}]8;;{target}\u{7}{label}\u{1b}]8;;\u{7}"
);
let mut style = base.style;
style.add_modifier.insert(Modifier::UNDERLINED);
Span::styled(content, style)
}
#[cfg(test)]
mod tests {
use super::*;
+1 -1
View File
@@ -25,7 +25,7 @@ pub fn get_upgrade_version(config: &Config) -> Option<String> {
});
info.and_then(|info| {
let current_version = env!("CARGO_PKG_VERSION");
let current_version = option_env!("CODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
if is_newer(&info.latest_version, current_version).unwrap_or(false) {
Some(info.latest_version)
} else {