Skip to content

Commit 6cbc8d0

Browse files
authored
Enhance iii-cli update functionality and documentation (#1)
- Updated README.md to clarify update commands for iii-cli and managed binaries. - Modified cli.rs to reflect new self-update commands and improve help text. - Implemented self-update functionality in update.rs to allow iii-cli to update itself. - Adjusted main.rs to handle self-update logic and notify users after updating. - Added SELF_SPEC for iii-cli in registry.rs to manage its update process and ensure platform support. - Included tests for SELF_SPEC to validate its properties and ensure it is not part of the binary registry.
1 parent 1b93183 commit 6cbc8d0

File tree

5 files changed

+160
-9
lines changed

5 files changed

+160
-9
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,20 @@ Output example:
124124

125125
### Update Binaries
126126

127-
Update all installed binaries to their latest versions:
127+
Update iii-cli and all installed binaries to their latest versions:
128128

129129
```bash
130130
iii-cli update
131131
```
132132

133-
Update a specific binary:
133+
Update only iii-cli itself:
134+
135+
```bash
136+
iii-cli update self
137+
iii-cli update iii-cli
138+
```
139+
140+
Update a specific managed binary:
134141

135142
```bash
136143
iii-cli update console

src/cli.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use clap::{Parser, Subcommand};
55
name = "iii-cli",
66
about = "Unified CLI dispatcher for iii tools",
77
version,
8-
after_help = "COMMANDS:\n console Launch the iii web console\n create Create a new iii project from a template\n motia Create a new Motia project from a template\n start Start the iii process communication engine\n update Update managed binaries to their latest versions\n list Show installed binaries and their versions"
8+
after_help = "COMMANDS:\n console Launch the iii web console\n create Create a new iii project from a template\n motia Create a new Motia project from a template\n start Start the iii process communication engine\n update Update iii-cli and managed binaries to their latest versions\n list Show installed binaries and their versions\n\nSELF-UPDATE:\n iii-cli update Update iii-cli + all managed binaries\n iii-cli update self Update only iii-cli\n iii-cli update iii-cli Update only iii-cli\n iii-cli update console Update only iii-console"
99
)]
1010
pub struct Cli {
1111
/// Disable background update and advisory checks
@@ -62,10 +62,11 @@ pub enum Commands {
6262
args: Vec<String>,
6363
},
6464

65-
/// Update managed binaries to their latest versions
65+
/// Update iii-cli and managed binaries to their latest versions
6666
Update {
67-
/// Specific command or binary to update (e.g., "console", "create").
68-
/// If omitted, updates all installed binaries.
67+
/// Specific command or binary to update (e.g., "console", "self").
68+
/// Use "self" or "iii-cli" to update only iii-cli.
69+
/// If omitted, updates iii-cli and all installed binaries.
6970
#[arg(name = "command")]
7071
target: Option<String>,
7172
},

src/main.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ async fn handle_update(target: Option<&str>) -> i32 {
229229
}
230230

231231
let results = match target {
232+
Some("iii-cli" | "self") => {
233+
// Self-update only
234+
vec![update::self_update(&client, &mut app_state).await]
235+
}
232236
Some(cmd) => {
233237
// Update specific binary
234238
let spec = match registry::resolve_binary_for_update(cmd) {
@@ -241,15 +245,30 @@ async fn handle_update(target: Option<&str>) -> i32 {
241245
vec![update::update_binary(&client, spec, &mut app_state).await]
242246
}
243247
None => {
244-
// Update all
248+
// Update all (includes self-update)
245249
eprintln!(" Checking all binaries for updates...");
246250
update::update_all(&client, &mut app_state).await
247251
}
248252
};
249253

250254
// Print results
255+
let mut self_updated = false;
251256
for result in &results {
252257
update::print_update_result(result);
258+
if let Ok(update::UpdateResult::Updated { binary, .. }) = result {
259+
if binary == "iii-cli" {
260+
self_updated = true;
261+
}
262+
}
263+
}
264+
265+
// Print restart note after self-update
266+
if self_updated {
267+
eprintln!();
268+
eprintln!(
269+
" {} iii-cli has been updated. Restart your shell or run the command again to use the new version.",
270+
"note:".cyan(),
271+
);
253272
}
254273

255274
// Save state

src/registry.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,24 @@ pub struct CommandMapping {
2424
pub binary_subcommand: Option<&'static str>,
2525
}
2626

27+
/// Specification for iii-cli itself (the dispatcher).
28+
/// Kept separate from REGISTRY because iii-cli is not a dispatched binary.
29+
pub static SELF_SPEC: BinarySpec = BinarySpec {
30+
name: "iii-cli",
31+
repo: "iii-hq/iii-cli",
32+
has_checksum: true,
33+
supported_targets: &[
34+
"aarch64-apple-darwin",
35+
"x86_64-apple-darwin",
36+
"x86_64-pc-windows-msvc",
37+
"aarch64-pc-windows-msvc",
38+
"x86_64-unknown-linux-gnu",
39+
"x86_64-unknown-linux-musl",
40+
"aarch64-unknown-linux-gnu",
41+
],
42+
commands: &[],
43+
};
44+
2745
/// The compiled-in binary registry
2846
pub static REGISTRY: &[BinarySpec] = &[
2947
BinarySpec {
@@ -226,4 +244,37 @@ mod tests {
226244
assert!(spec.has_checksum);
227245
}
228246

247+
#[test]
248+
fn test_self_spec_fields() {
249+
assert_eq!(SELF_SPEC.name, "iii-cli");
250+
assert_eq!(SELF_SPEC.repo, "iii-hq/iii-cli");
251+
assert!(SELF_SPEC.has_checksum);
252+
assert!(SELF_SPEC.commands.is_empty());
253+
}
254+
255+
#[test]
256+
fn test_self_spec_supported_targets() {
257+
assert!(SELF_SPEC.supported_targets.contains(&"aarch64-apple-darwin"));
258+
assert!(SELF_SPEC.supported_targets.contains(&"x86_64-apple-darwin"));
259+
assert!(SELF_SPEC.supported_targets.contains(&"x86_64-unknown-linux-gnu"));
260+
assert!(SELF_SPEC.supported_targets.contains(&"x86_64-unknown-linux-musl"));
261+
assert!(SELF_SPEC.supported_targets.contains(&"aarch64-unknown-linux-gnu"));
262+
assert!(SELF_SPEC.supported_targets.contains(&"x86_64-pc-windows-msvc"));
263+
assert!(SELF_SPEC.supported_targets.contains(&"aarch64-pc-windows-msvc"));
264+
assert_eq!(SELF_SPEC.supported_targets.len(), 7);
265+
}
266+
267+
#[test]
268+
fn test_self_spec_not_in_registry() {
269+
for spec in REGISTRY {
270+
assert_ne!(spec.name, "iii-cli", "iii-cli should not be in REGISTRY");
271+
}
272+
}
273+
274+
#[test]
275+
fn test_self_spec_platform_support() {
276+
let result = crate::platform::check_platform_support(&SELF_SPEC);
277+
assert!(result.is_ok());
278+
}
279+
229280
}

src/update.rs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,85 @@ pub async fn update_binary(
194194
})
195195
}
196196

197-
/// Update all installed binaries.
197+
/// Update iii-cli itself to the latest version.
198+
pub async fn self_update(
199+
client: &reqwest::Client,
200+
state: &mut AppState,
201+
) -> Result<UpdateResult, UpdateError> {
202+
let spec = &registry::SELF_SPEC;
203+
204+
platform::check_platform_support(spec)?;
205+
206+
eprintln!(" Checking for updates to {}...", spec.name);
207+
208+
let release = github::fetch_latest_release(client, spec).await?;
209+
let latest_version = github::parse_release_version(&release.tag_name)
210+
.map_err(|e| UpdateError::VersionParse(e.to_string()))?;
211+
212+
let current_version = Version::parse(env!("CARGO_PKG_VERSION"))
213+
.expect("CARGO_PKG_VERSION is always valid semver");
214+
215+
if current_version >= latest_version {
216+
return Ok(UpdateResult::AlreadyUpToDate {
217+
binary: spec.name.to_string(),
218+
version: current_version,
219+
});
220+
}
221+
222+
let asset_name = platform::asset_name(spec.name);
223+
let asset = github::find_asset(&release, &asset_name).ok_or_else(|| {
224+
UpdateError::Github(IiiGithubError::Network(
225+
crate::error::NetworkError::AssetNotFound {
226+
binary: spec.name.to_string(),
227+
platform: platform::current_target().to_string(),
228+
},
229+
))
230+
})?;
231+
232+
let checksum_url = if spec.has_checksum {
233+
let checksum_name = platform::checksum_asset_name(spec.name);
234+
github::find_asset(&release, &checksum_name)
235+
.map(|a| a.browser_download_url.clone())
236+
} else {
237+
None
238+
};
239+
240+
eprintln!(
241+
" Updating {} to v{}...",
242+
spec.name,
243+
latest_version
244+
);
245+
246+
// Install to the standard managed location (~/.local/bin/iii-cli),
247+
// consistent with install.sh and other managed binaries.
248+
let target_path = platform::binary_path(spec.name);
249+
250+
download::download_and_install(
251+
client,
252+
spec,
253+
asset,
254+
checksum_url.as_deref(),
255+
&target_path,
256+
)
257+
.await?;
258+
259+
state.record_install(spec.name, latest_version.clone(), asset_name);
260+
261+
Ok(UpdateResult::Updated {
262+
binary: spec.name.to_string(),
263+
from: Some(current_version),
264+
to: latest_version,
265+
})
266+
}
267+
268+
/// Update all installed binaries (including iii-cli itself).
198269
pub async fn update_all(
199270
client: &reqwest::Client,
200271
state: &mut AppState,
201272
) -> Vec<Result<UpdateResult, UpdateError>> {
273+
// Self-update first
274+
let mut results = vec![self_update(client, state).await];
275+
202276
let specs: Vec<&BinarySpec> = registry::all_binaries()
203277
.into_iter()
204278
.filter(|spec| {
@@ -208,7 +282,6 @@ pub async fn update_all(
208282
})
209283
.collect();
210284

211-
let mut results = Vec::new();
212285
for spec in specs {
213286
results.push(update_binary(client, spec, state).await);
214287
}

0 commit comments

Comments
 (0)