nu_command/network/
version_check.rs1use nu_engine::command_prelude::*;
2use serde::Deserialize;
3use update_informer::{
4 Check, Package, Registry, Result as UpdateResult,
5 http_client::{GenericHttpClient, HttpClient},
6 registry,
7};
8
9use super::tls::tls;
10
11#[derive(Clone)]
12pub struct VersionCheck;
13
14impl Command for VersionCheck {
15 fn name(&self) -> &str {
16 "version check"
17 }
18
19 fn description(&self) -> &str {
20 "Checks to see if you have the latest version of nushell."
21 }
22
23 fn extra_description(&self) -> &str {
24 "If you're running nushell nightly, `version check` will check to see if you are running the latest nightly version. If you are running the nushell release, `version check` will check to see if you're running the latest release version."
25 }
26
27 fn signature(&self) -> Signature {
28 Signature::build("version check")
29 .category(Category::Platform)
30 .input_output_types(vec![(Type::Nothing, Type::String)])
31 }
32
33 fn examples(&self) -> Vec<Example> {
34 vec![Example {
35 description: "Check if you have the latest version of nushell",
36 example: "version check",
37 result: None,
38 }]
39 }
40
41 fn run(
42 &self,
43 _engine_state: &EngineState,
44 _stack: &mut Stack,
45 _call: &Call,
46 _input: PipelineData,
47 ) -> Result<PipelineData, ShellError> {
48 let version_check = check_for_latest_nushell_version();
49 Ok(version_check.into_pipeline_data())
50 }
51}
52
53pub struct NuShellNightly;
54
55impl Registry for NuShellNightly {
56 const NAME: &'static str = "nushell/nightly";
57
58 fn get_latest_version<T: HttpClient>(
59 http_client: GenericHttpClient<T>,
60 pkg: &Package,
61 ) -> UpdateResult<Option<String>> {
62 #[derive(Deserialize, Debug)]
63 struct Response {
64 tag_name: String,
65 }
66
67 let url = format!("https://api.github.com/repos/{}/releases", pkg);
68 let versions = http_client
69 .add_header("Accept", "application/vnd.github.v3+json")
70 .add_header("User-Agent", "update-informer")
71 .get::<Vec<Response>>(&url)?;
72
73 if let Some(v) = versions.first() {
74 let up_through_plus = match v.tag_name.split('+').next() {
78 Some(v) => v,
79 None => &v.tag_name,
80 };
81 return Ok(Some(up_through_plus.to_string()));
82 }
83
84 Ok(None)
85 }
86}
87
88struct NativeTlsHttpClient;
89
90impl HttpClient for NativeTlsHttpClient {
91 fn get<T: serde::de::DeserializeOwned>(
92 url: &str,
93 timeout: std::time::Duration,
94 headers: update_informer::http_client::HeaderMap,
95 ) -> update_informer::Result<T> {
96 let agent = ureq::AgentBuilder::new()
97 .tls_connector(std::sync::Arc::new(tls(false)?))
98 .build();
99
100 let mut req = agent.get(url).timeout(timeout);
101
102 for (header, value) in headers {
103 req = req.set(header, value);
104 }
105
106 let json = req.call()?.into_json()?;
107
108 Ok(json)
109 }
110}
111
112pub fn check_for_latest_nushell_version() -> Value {
113 let current_version = env!("CARGO_PKG_VERSION").to_string();
114
115 let mut rec = Record::new();
116
117 if current_version.contains("nightly") {
118 rec.push("channel", Value::test_string("nightly"));
119
120 let nightly_pkg_name = "nushell/nightly";
121 let informer =
125 update_informer::new(NuShellNightly, nightly_pkg_name, current_version.clone())
126 .http_client(NativeTlsHttpClient)
127 .interval(std::time::Duration::ZERO);
128
129 if let Ok(Some(new_version)) = informer.check_version() {
130 rec.push("current", Value::test_bool(false));
131 rec.push("latest", Value::test_string(format!("{}", new_version)));
132 Value::test_record(rec)
133 } else {
134 rec.push("current", Value::test_bool(true));
135 rec.push("latest", Value::test_string(current_version.clone()));
136 Value::test_record(rec)
137 }
138 } else {
139 rec.push("channel", Value::test_string("release"));
140
141 let normal_pkg_name = "nushell/nushell";
142 let informer =
146 update_informer::new(registry::GitHub, normal_pkg_name, current_version.clone())
147 .interval(std::time::Duration::ZERO);
148
149 if let Ok(Some(new_version)) = informer.check_version() {
150 rec.push("current", Value::test_bool(false));
151 rec.push("latest", Value::test_string(format!("{}", new_version)));
152 Value::test_record(rec)
153 } else {
154 rec.push("current", Value::test_bool(true));
155 rec.push("latest", Value::test_string(current_version.clone()));
156 Value::test_record(rec)
157 }
158 }
159}