Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci-quality-slice.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ jobs:
exit 1
fi

- name: Check console print drift
run: cargo run -p xtask -- repo-consistency no-console-print
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

rust_fmt:
name: Rust format
needs: runner_policy
Expand Down
5 changes: 5 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ ci-validate:
python3 -m unittest discover -s scripts/tests -p 'test_*.py'
just ci-crate-lists
just check-release
just no-console-print
just publish-crates

# Run CI/workspace crate-list consistency checks.
Expand All @@ -481,6 +482,10 @@ ci-crate-lists:
publish-crates:
just with-lld cargo run -p xtask -- repo-consistency publish-crates

# Fail on new println!/eprintln!/write! outside xtask tests (see the baseline file).
no-console-print:
just with-lld cargo run -p xtask -- repo-consistency no-console-print
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

# Shellcheck the explicitly supplied changed shell scripts.
ci-shellcheck *scripts:
shellcheck {{ scripts }}
Expand Down
7 changes: 6 additions & 1 deletion crates/mesh-llm-host-runtime/src/runtime/run_auto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,12 @@ pub(super) async fn run_runtime_cli(

// Finish the release check before startup continues.
if !checked_updates && !options.command_is_update && !options.command_uses_machine_output {
autoupdate::check_for_update(crate::BUILD_VERSION).await;
if let Some(notice) = autoupdate::check_for_update(crate::BUILD_VERSION).await {
let _ = emit_event(OutputEvent::Info {
message: notice.message,
context: None,
});
}
}

let mut config = plugin::load_config(options.config.as_deref())?;
Expand Down
152 changes: 105 additions & 47 deletions crates/mesh-llm-system/src/autoupdate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,57 +46,81 @@ pub struct UpdateCommandOptions<'a> {
pub current_version: &'static str,
}

pub async fn check_for_update(current_version: &str) {
enum NoticeGuidance {
RunUpdateCommand,
ReinstallScript,
DownloadReleases,
}

/// A newer-version notice for callers to surface through their own output
/// facility; this crate performs no console I/O of its own.
#[derive(Clone, Debug)]
pub struct UpdateNotice {
pub message: String,
}

fn notice_message(current_version: &str, latest_version: &str, guidance: NoticeGuidance) -> String {
match guidance {
NoticeGuidance::RunUpdateCommand => format!(
"✨ New version: v{current_version} -> v{latest_version}. Run 'mesh-llm update'."
),
NoticeGuidance::ReinstallScript => format!(
"✨ New version: v{current_version} -> v{latest_version}. Reinstall with: curl -fsSL {} | bash",
release_fetch::INSTALL_SCRIPT_URL
),
NoticeGuidance::DownloadReleases => format!(
"✨ New version: v{current_version} -> v{latest_version}. Download from {RELEASES_URL}"
),
}
}

/// Checks for a newer release and returns a notice when one is available.
///
/// Returns `None` when the platform has no release assets, no release could be
/// fetched, or the local version is not older than the latest release. Callers
/// are responsible for presenting [`UpdateNotice::message`] through their own
/// output facility.
pub async fn check_for_update(current_version: &str) -> Option<UpdateNotice> {
if !platform_has_release_assets() {
return;
return None;
}
if let Some(release) = latest_release_info().await {
if !version_newer(&release.version, current_version) {
return;
let Some(release) = latest_release_info().await else {
return None;
};
if !version_newer(&release.version, current_version) {
return None;
}
// Determine whether this is a bundle install and, if so, whether the
// specific installed flavor's asset is present in the new release.
let bundle_asset = std::env::current_exe().ok().and_then(|exe| {
let (_, flavor) = bundle_install_dir(&exe, None)?;
current_release_target(flavor).and_then(|target| {
resolve_release_asset_name(&release, target, ReleaseAssetPreference::StableFirst)
})
});
let has_matching_bundle_asset = bundle_asset
.as_ref()
.is_some_and(|asset| release_fetch::release_has_asset(&release, asset));
let guidance = if has_matching_bundle_asset {
NoticeGuidance::RunUpdateCommand
} else {
// Either not a bundle install, or the installed flavor's asset is not
// published in the new release — fall back to generic guidance.
if !release_has_any_platform_asset(&release, std::env::consts::OS, std::env::consts::ARCH) {
return None;
}
// Determine whether this is a bundle install and, if so, whether the
// specific installed flavor's asset is present in the new release.
let bundle_asset = std::env::current_exe().ok().and_then(|exe| {
let (_, flavor) = bundle_install_dir(&exe, None)?;
current_release_target(flavor).and_then(|target| {
resolve_release_asset_name(&release, target, ReleaseAssetPreference::StableFirst)
})
});
match bundle_asset {
Some(ref asset) if release_fetch::release_has_asset(&release, asset) => {
eprintln!(
"✨ New version: v{current_version} -> v{}. Run 'mesh-llm update'.",
release.version
);
}
_ => {
// Either not a bundle install, or the installed flavor's asset
// is not published in the new release — fall back to generic guidance.
#[cfg(not(windows))]
if release_has_any_platform_asset(
&release,
std::env::consts::OS,
std::env::consts::ARCH,
) {
eprintln!(
"✨ New version: v{current_version} -> v{}. Reinstall with: curl -fsSL {INSTALL_SCRIPT_URL} | bash",
release.version
);
}
#[cfg(windows)]
if release_has_any_platform_asset(
&release,
std::env::consts::OS,
std::env::consts::ARCH,
) {
eprintln!(
"✨ New version: v{current_version} -> v{}. Download from {RELEASES_URL}",
release.version
);
}
}
#[cfg(not(windows))]
{
NoticeGuidance::ReinstallScript
}
}
#[cfg(windows)]
{
NoticeGuidance::DownloadReleases
}
};
Some(UpdateNotice {
message: notice_message(current_version, &release.version, guidance),
})
}

pub async fn maybe_auto_update(options: AutoUpdateOptions) -> Result<bool> {
Expand Down Expand Up @@ -429,4 +453,38 @@ mod tests {

let _ = std::fs::remove_dir_all(dir);
}

#[test]
fn test_notice_message_run_update_command() {
assert_eq!(
notice_message("0.76.0-rc3", "0.99.0", NoticeGuidance::RunUpdateCommand),
"✨ New version: v0.76.0-rc3 -> v0.99.0. Run 'mesh-llm update'."
);
}

#[test]
fn test_notice_message_reinstall_script() {
let install_script_url = release_fetch::INSTALL_SCRIPT_URL;
assert_eq!(
notice_message("0.75.2", "1.4.0", NoticeGuidance::ReinstallScript),
format!(
"✨ New version: v0.75.2 -> v1.4.0. Reinstall with: curl -fsSL {install_script_url} | bash"
)
);
}

#[test]
fn test_notice_message_download_releases() {
assert_eq!(
notice_message("0.75.2", "1.4.0", NoticeGuidance::DownloadReleases),
format!("✨ New version: v0.75.2 -> v1.4.0. Download from {RELEASES_URL}")
);
}

#[test]
fn test_update_notice_carries_complete_user_facing_text() {
let message = notice_message("0.1.0", "9.9.9", NoticeGuidance::RunUpdateCommand);
let UpdateNotice { message: carried } = UpdateNotice { message };
assert!(carried.starts_with("✨ New version: v0.1.0 -> v9.9.9."));
}
}
117 changes: 117 additions & 0 deletions tools/xtask/data/console_print_allowlist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
{
"crates/llama-spec-bench/src/main.rs": 14,
"crates/mesh-client/src/models/catalog.rs": 3,
"crates/mesh-client/tests/runtime_thread_leak.rs": 1,
"crates/mesh-llm-commands/src/agent_cli.rs": 31,
"crates/mesh-llm-commands/src/auth.rs": 102,
"crates/mesh-llm-commands/src/auth/tests.rs": 3,
"crates/mesh-llm-commands/src/config.rs": 8,
"crates/mesh-llm-commands/src/gpus.rs": 8,
"crates/mesh-llm-commands/src/gpus/tune/benchmark/mod.rs": 1,
"crates/mesh-llm-commands/src/gpus/tune/benchmark_progress.rs": 5,
"crates/mesh-llm-commands/src/model_package.rs": 68,
"crates/mesh-llm-commands/src/plugin.rs": 29,
"crates/mesh-llm-commands/src/runtime_native.rs": 11,
"crates/mesh-llm-commands/src/runtime_native/formatters.rs": 54,
"crates/mesh-llm-commands/src/setup/command.rs": 4,
"crates/mesh-llm-commands/src/setup/summary.rs": 12,
"crates/mesh-llm-commands/src/skills.rs": 14,
"crates/mesh-llm-commands/src/terminal.rs": 2,
"crates/mesh-llm-commands/src/uninstall.rs": 5,
"crates/mesh-llm-events/src/terminal_progress.rs": 3,
"crates/mesh-llm-ffi/tests/live_sdk_smoke.rs": 1,
"crates/mesh-llm-host-runtime/src/inference/skippy/materialization/package_download.rs": 7,
"crates/mesh-llm-host-runtime/src/inference/skippy/resolver/native_mtp_tests.rs": 1,
"crates/mesh-llm-host-runtime/src/models/catalog.rs": 8,
"crates/mesh-llm-host-runtime/src/models/maintenance.rs": 37,
"crates/mesh-llm-host-runtime/src/models/remote_catalog.rs": 1,
"crates/mesh-llm-host-runtime/src/models/resolve/mod.rs": 2,
"crates/mesh-llm-host-runtime/src/models/search.rs": 1,
"crates/mesh-llm-host-runtime/src/network/discovery.rs": 2,
"crates/mesh-llm-host-runtime/src/network/nostr/keys.rs": 6,
"crates/mesh-llm-host-runtime/src/network/nostr/publish.rs": 9,
"crates/mesh-llm-host-runtime/src/runtime/interactive.rs": 2,
"crates/mesh-llm-host-runtime/src/runtime/local_split/tests.rs": 4,
"crates/mesh-llm-host-runtime/src/runtime/tracing_writer.rs": 1,
"crates/mesh-llm-identity/src/keychain.rs": 4,
"crates/mesh-llm-runtime-install/src/discovery.rs": 2,
"crates/mesh-llm-system/src/autoupdate.rs": 11,
"crates/mesh-llm-system/src/benchmark_prompts.rs": 1,
"crates/mesh-llm-test-harness/src/bin/spawn-fixture.rs": 1,
"crates/mesh-llm-tui/src/terminal_progress.rs": 3,
"crates/mesh-llm/src/commands/discover.rs": 37,
"crates/mesh-llm/src/commands/doctor.rs": 5,
"crates/mesh-llm/src/commands/download.rs": 4,
"crates/mesh-llm/src/commands/models/formatters.rs": 1,
"crates/mesh-llm/src/commands/models/formatters_console.rs": 62,
"crates/mesh-llm/src/commands/models/mod.rs": 55,
"crates/mesh-llm/src/commands/plugin_cli.rs": 4,
"crates/mesh-llm/src/commands/runtime.rs": 21,
"crates/mesh-llm/src/lib.rs": 3,
"crates/mesh-llm/src/main.rs": 2,
"crates/mesh-llm/tests/virtual_llm_injection.rs": 16,
"crates/mesh-mixture-of-agents/tests/diag_small_pool.rs": 11,
"crates/mesh-mixture-of-agents/tests/eval_openrouter.rs": 71,
"crates/mesh-native-serving-plugin-host/src/lib.rs": 1,
"crates/mesh-native-serving-plugin-host/src/plugin_dispatch.rs": 8,
"crates/metrics-server/src/fixture.rs": 1,
"crates/metrics-server/src/server.rs": 1,
"crates/model-hf/tests/hf_download.rs": 11,
"crates/model-package/src/bin/queue-unsloth-layer-packages.rs": 32,
"crates/skippy-bench/src/chat_corpus.rs": 1,
"crates/skippy-bench/src/distributed.rs": 1,
"crates/skippy-bench/src/evals.rs": 2,
"crates/skippy-bench/src/evals/doctor.rs": 8,
"crates/skippy-bench/src/evals/registry.rs": 13,
"crates/skippy-bench/src/evals/run.rs": 1,
"crates/skippy-bench/src/evals/sync.rs": 2,
"crates/skippy-bench/src/focused_runtime.rs": 2,
"crates/skippy-bench/src/local_single.rs": 1,
"crates/skippy-bench/src/local_split.rs": 4,
"crates/skippy-bench/src/main.rs": 2,
"crates/skippy-bench/src/token_lengths.rs": 1,
"crates/skippy-bench/src/verify_window_local.rs": 1,
"crates/skippy-correctness/src/glm_dsa_trace.rs": 1,
"crates/skippy-correctness/src/main.rs": 2,
"crates/skippy-correctness/src/native_mtp_openai/reporting.rs": 1,
"crates/skippy-correctness/src/runner/native_mtp.rs": 1,
"crates/skippy-correctness/src/runner/stage_fa_parity.rs": 1,
"crates/skippy-correctness/tests/parity_models/mod.rs": 1,
"crates/skippy-model-package/src/glm_dsa_generation_policy.rs": 1,
"crates/skippy-model-package/src/inspect.rs": 1,
"crates/skippy-model-package/src/main.rs": 4,
"crates/skippy-model-package/src/package.rs": 1,
"crates/skippy-model-package/src/progress.rs": 3,
"crates/skippy-model-package/src/tests.rs": 2,
"crates/skippy-model-package/src/validate.rs": 3,
"crates/skippy-model-package/src/write.rs": 1,
"crates/skippy-prompt/src/prompt_cli/binary_repl.rs": 19,
"crates/skippy-prompt/src/prompt_cli/generation.rs": 19,
"crates/skippy-prompt/src/prompt_cli/history.rs": 5,
"crates/skippy-prompt/src/prompt_cli/launch.rs": 12,
"crates/skippy-prompt/src/prompt_cli/live_session.rs": 1,
"crates/skippy-prompt/src/prompt_cli/logs.rs": 8,
"crates/skippy-prompt/src/prompt_cli/remote_sync.rs": 8,
"crates/skippy-prompt/src/prompt_cli/stage_config.rs": 4,
"crates/skippy-prompt/src/prompt_cli/wire_messages.rs": 18,
"crates/skippy-quantize/src/output.rs": 9,
"crates/skippy-quantize/src/type_catalog.rs": 2,
"crates/skippy-runtime/src/tests.rs": 6,
"crates/skippy-server/src/binary_transport/binary_messaging.rs": 6,
"crates/skippy-server/src/binary_transport/binary_messaging/reply.rs": 3,
"crates/skippy-server/src/binary_transport/direct_return.rs": 4,
"crates/skippy-server/src/binary_transport/preconnect.rs": 2,
"crates/skippy-server/src/binary_transport/socket.rs": 3,
"crates/skippy-server/src/binary_transport/stage_execution.rs": 1,
"crates/skippy-server/src/frontend/embedded_generation/lifecycle.rs": 1,
"crates/skippy-server/src/frontend/generation/persistent_lanes.rs": 3,
"crates/skippy-server/src/frontend/generation/server.rs": 2,
"crates/skippy-server/src/frontend/linear_proposal.rs": 1,
"crates/skippy-server/src/frontend/tests/multimodal.rs": 4,
"crates/skippy-server/src/http.rs": 1,
"crates/skippy-server/src/kv_integration/config.rs": 3,
"crates/skippy-server/src/main.rs": 1,
"crates/skippy-server/src/runtime_state.rs": 6,
"crates/skippy-server/src/runtime_state/lane_lifecycle.rs": 1,
"crates/skippy-server/src/telemetry.rs": 1
}
8 changes: 7 additions & 1 deletion tools/xtask/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod attestation;
mod command;
mod installer_fixtures;
mod no_console_print;
mod publish_consistency;
mod release_targets;
mod repo_consistency;
Expand Down Expand Up @@ -33,6 +34,11 @@ fn run() -> DynResult<()> {
[command, scope] if command == "repo-consistency" && scope == "test-all-rust-crate-coverage" => {
repo_consistency::check_test_all_coverage_command()
}
[command, scope, rest @ ..]
if command == "repo-consistency" && scope == "no-console-print" =>
{
no_console_print::check_no_console_print_command(rest)
}
[command, scope, rest @ ..]
if command == "release-attestation" && scope == "generate-keypair" =>
{
Expand All @@ -47,7 +53,7 @@ fn run() -> DynResult<()> {
attestation::inspect_release_attestation(rest)
}
_ => Err(
"usage:\n cargo run -p xtask -- repo-consistency release-targets\n cargo run -p xtask -- repo-consistency ci-crate-lists\n cargo run -p xtask -- repo-consistency publish-crates\n cargo run -p xtask -- repo-consistency test-all-rust-crate-coverage\n cargo run -p xtask -- release-attestation generate-keypair --private-key-out <path> --public-key-out <path>\n cargo run -p xtask -- release-attestation stamp --binary <path> --signing-key-file <path> [--node-version <semver>] [--build-id <id>] [--commit <sha>] [--target-triple <triple>] [--protocol-min <n>] [--protocol-max <n>]\n cargo run -p xtask -- release-attestation inspect --binary <path> [--public-key-file <path>] [--json]"
"usage:\n cargo run -p xtask -- repo-consistency release-targets\n cargo run -p xtask -- repo-consistency ci-crate-lists\n cargo run -p xtask -- repo-consistency publish-crates\n cargo run -p xtask -- repo-consistency test-all-rust-crate-coverage\n cargo run -p xtask -- repo-consistency no-console-print [--regen]\n cargo run -p xtask -- release-attestation generate-keypair --private-key-out <path> --public-key-out <path>\n cargo run -p xtask -- release-attestation stamp --binary <path> --signing-key-file <path> [--node-version <semver>] [--build-id <id>] [--commit <sha>] [--target-triple <triple>] [--protocol-min <n>] [--protocol-max <n>]\n cargo run -p xtask -- release-attestation inspect --binary <path> [--public-key-file <path>] [--json]"
.to_string()
.into(),
),
Expand Down
Loading
Loading