Skip to content

Commit 50f4110

Browse files
feat: add ability to not auto update into a marked version (#908)
1 parent f59b224 commit 50f4110

File tree

12 files changed

+104
-39
lines changed

12 files changed

+104
-39
lines changed

crates/fig_desktop/src/install.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ pub async fn run_install(ctx: Arc<Context>, ignore_immediate_update: bool) {
115115
use tokio::time::timeout;
116116
// Check for updates but timeout after 3 seconds to avoid making the user wait too long
117117
// todo: don't download the index file twice
118-
match timeout(Duration::from_secs(3), check_for_updates(true)).await {
118+
match timeout(Duration::from_secs(3), check_for_updates(true, true)).await {
119119
Ok(Ok(Some(_))) => {
120120
crate::update::check_for_update(true, true).await;
121121
},

crates/fig_desktop/src/tray.rs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
use std::borrow::Cow;
22

33
use cfg_if::cfg_if;
4+
use fig_install::index::get_file_type;
45
use fig_install::{
56
InstallComponents,
67
UpdateOptions,
78
};
8-
use fig_os_shim::{
9-
Context,
10-
Os,
11-
};
9+
use fig_os_shim::Context;
1210
use fig_remote_ipc::figterm::FigtermState;
1311
use fig_util::consts::PRODUCT_NAME;
1412
use fig_util::manifest::{
1513
FileType,
1614
Variant,
17-
bundle_metadata,
18-
manifest,
1915
};
2016
use fig_util::url::USER_MANUAL;
2117
use muda::{
@@ -101,6 +97,7 @@ fn tray_update(proxy: &EventLoopProxy) {
10197
ignore_rollout: true,
10298
interactive: true,
10399
relaunch_dashboard: true,
100+
is_auto_update: false,
104101
},
105102
)
106103
.await;
@@ -140,24 +137,15 @@ fn tray_update(proxy: &EventLoopProxy) {
140137
/// continuing.
141138
///
142139
/// Returns `true` if we should continue with updating, `false` otherwise.
143-
///
144-
/// Currently only the Linux flow gets affected, since some bundles (eg, `AppImage`) are able to
145-
/// update and others (packages like `deb`) cannot.
146140
async fn should_continue_with_update(ctx: &Context, proxy: &EventLoopProxy) -> bool {
147-
if !(ctx.platform().os() == Os::Linux && manifest().variant == Variant::Full) {
148-
return true;
149-
}
150-
151-
match fig_install::check_for_updates(true).await {
141+
match fig_install::check_for_updates(true, false).await {
152142
Ok(Some(pkg)) => {
153-
let file_type = bundle_metadata(&ctx)
143+
let file_type = get_file_type(ctx, &Variant::Full)
154144
.await
155-
.map_err(|err| error!(?err, "Failed to get bundle metadata"))
156-
.ok()
157-
.flatten()
158-
.map(|md| md.packaged_as);
159-
// Only AppImage is able to self-update.
160-
if file_type == Some(FileType::AppImage) {
145+
.map_err(|err| error!(?err, "Failed to get file type"))
146+
.ok();
147+
// Only AppImage and dmg is able to self-update.
148+
if file_type == Some(FileType::AppImage) || file_type == Some(FileType::Dmg) {
161149
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
162150
proxy
163151
.send_event(

crates/fig_desktop/src/update.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ pub async fn check_for_update(show_webview: bool, relaunch_dashboard: bool) -> b
101101
ignore_rollout: false,
102102
interactive: show_webview,
103103
relaunch_dashboard,
104+
is_auto_update: true,
104105
})
105106
.await
106107
{

crates/fig_desktop_api/src/requests/update.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ pub async fn update_application(request: UpdateApplicationRequest) -> RequestRes
2020
ignore_rollout: request.ignore_rollout.unwrap_or(true),
2121
interactive: request.interactive.unwrap_or(true),
2222
relaunch_dashboard: request.relaunch_dashboard.unwrap_or(true),
23+
is_auto_update: false,
2324
},
2425
));
2526
RequestResult::success()
2627
}
2728

2829
pub async fn check_for_updates(_request: CheckForUpdatesRequest) -> RequestResult {
29-
fig_install::check_for_updates(true)
30+
fig_install::check_for_updates(true, false)
3031
.await
3132
.map(|res| {
3233
Box::new(ServerOriginatedSubMessage::CheckForUpdatesResponse(

crates/fig_install/src/index.rs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,15 @@ impl Index {
101101
/// than the currently installed version*. This is useful to check if an update exists for the
102102
/// given target and variant without filtering on file type, e.g. in the case of Linux desktop
103103
/// bundles.
104+
#[allow(clippy::too_many_arguments)]
104105
pub fn find_next_version(
105106
&self,
106107
target_triple: &TargetTriple,
107108
variant: &Variant,
108109
file_type: Option<&FileType>,
109110
current_version: &str,
110111
ignore_rollout: bool,
112+
is_auto_update: bool,
111113
threshold_override: Option<u8>,
112114
) -> Result<Option<UpdatePackage>, Error> {
113115
if !self.supported.iter().any(|support| {
@@ -137,6 +139,7 @@ impl Index {
137139
Some(rollout) => rollout.start <= right_now,
138140
None => true,
139141
})
142+
.filter(|version| !is_auto_update || !version.disable_autoupdate)
140143
.collect::<Vec<&RemoteVersion>>();
141144

142145
valid_versions.sort_unstable_by(|lhs, rhs| lhs.version.cmp(&rhs.version));
@@ -252,20 +255,22 @@ struct Support {
252255
file_type: Option<FileType>,
253256
}
254257

255-
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
258+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
256259
pub(crate) struct RemoteVersion {
257260
pub version: Version,
258261
pub rollout: Option<Rollout>,
259262
pub packages: Vec<Package>,
263+
#[serde(default)]
264+
pub disable_autoupdate: bool,
260265
}
261266

262-
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
267+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
263268
pub(crate) struct Rollout {
264269
start: u64,
265270
end: u64,
266271
}
267272

268-
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
273+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
269274
#[serde(rename_all = "camelCase")]
270275
pub struct Package {
271276
#[serde(deserialize_with = "deser_enum_other")]
@@ -306,7 +311,7 @@ pub struct UpdatePackage {
306311
pub cli_path: Option<String>,
307312
}
308313

309-
#[derive(Deserialize, Serialize, PartialEq, Eq, EnumString, Debug, Display)]
314+
#[derive(Deserialize, Serialize, PartialEq, Eq, EnumString, Debug, Clone, Display)]
310315
#[serde(rename_all = "camelCase")]
311316
#[strum(serialize_all = "camelCase")]
312317
pub enum PackageArchitecture {
@@ -360,14 +365,21 @@ pub async fn check_for_updates(
360365
variant: &Variant,
361366
file_type: Option<&FileType>,
362367
ignore_rollout: bool,
368+
is_auto_update: bool,
363369
) -> Result<Option<UpdatePackage>, Error> {
364370
const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
365-
pull(&channel)
366-
.await?
367-
.find_next_version(target_triple, variant, file_type, CURRENT_VERSION, ignore_rollout, None)
371+
pull(&channel).await?.find_next_version(
372+
target_triple,
373+
variant,
374+
file_type,
375+
CURRENT_VERSION,
376+
ignore_rollout,
377+
is_auto_update,
378+
None,
379+
)
368380
}
369381

370-
pub(crate) async fn get_file_type(ctx: &Context, variant: &Variant) -> Result<FileType, Error> {
382+
pub async fn get_file_type(ctx: &Context, variant: &Variant) -> Result<FileType, Error> {
371383
match ctx.platform().os() {
372384
fig_os_shim::Os::Mac => Ok(FileType::Dmg),
373385
fig_os_shim::Os::Linux => match variant {
@@ -429,6 +441,7 @@ mod tests {
429441
&Variant::Full,
430442
Some(FileType::Dmg).as_ref(),
431443
false,
444+
false,
432445
)
433446
.await
434447
.unwrap();
@@ -515,7 +528,8 @@ mod tests {
515528
"sha256": "5a6abea56bfa91bd58d49fe40322058d0efea825f7e19f7fb7db1c204ae625b6",
516529
"size": 76836772,
517530
}
518-
]
531+
],
532+
"disable_autoupdate": true,
519533
},
520534
{
521535
"version": "2.0.0",
@@ -560,6 +574,11 @@ mod tests {
560574

561575
assert_eq!(index.versions.len(), 4);
562576

577+
assert!(
578+
!index.versions[1].disable_autoupdate,
579+
"missing disable_autoupdate field should default to false"
580+
);
581+
563582
// check the 1.0.0 entry matches
564583
assert_eq!(index.versions[2], RemoteVersion {
565584
version: Version::new(1, 0, 0),
@@ -588,6 +607,7 @@ mod tests {
588607
cli_path: None,
589608
}
590609
],
610+
disable_autoupdate: true,
591611
});
592612
}
593613

@@ -604,6 +624,7 @@ mod tests {
604624
Some(&FileType::TarZst),
605625
"1.2.1",
606626
true,
627+
false,
607628
None,
608629
)
609630
.unwrap();
@@ -619,6 +640,7 @@ mod tests {
619640
Some(&FileType::TarZst),
620641
"1.2.0",
621642
true,
643+
false,
622644
None,
623645
)
624646
.unwrap()
@@ -635,6 +657,7 @@ mod tests {
635657
Some(&FileType::TarZst),
636658
"1.2.1",
637659
true,
660+
false,
638661
None,
639662
);
640663
assert!(next.is_err());
@@ -649,10 +672,50 @@ mod tests {
649672
None,
650673
"1.0.5",
651674
true,
675+
false,
652676
None,
653677
)
654678
.unwrap()
655679
.expect("should have update package");
656680
assert_eq!(next.version.to_string().as_str(), "1.2.1");
657681
}
682+
683+
#[test]
684+
fn index_autoupdate_does_not_update_into_disabled() {
685+
let mut index = load_test_index();
686+
687+
let next = index
688+
.find_next_version(
689+
&TargetTriple::X86_64UnknownLinuxGnu,
690+
&Variant::Full,
691+
None,
692+
"1.0.5",
693+
true,
694+
true,
695+
None,
696+
)
697+
.unwrap()
698+
.expect("should have update package");
699+
assert_eq!(next.version.to_string().as_str(), "1.2.0");
700+
701+
// Push a newer update that does not have autoupdate disabled
702+
let mut last = index.versions.last().cloned().unwrap();
703+
last.version = Version::from_str("2.0.0").unwrap();
704+
last.disable_autoupdate = false;
705+
index.versions.push(last);
706+
707+
let next = index
708+
.find_next_version(
709+
&TargetTriple::X86_64UnknownLinuxGnu,
710+
&Variant::Full,
711+
None,
712+
"1.0.5",
713+
true,
714+
true,
715+
None,
716+
)
717+
.unwrap()
718+
.expect("should have update package");
719+
assert_eq!(next.version.to_string().as_str(), "2.0.0");
720+
}
658721
}

crates/fig_install/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ pub fn get_max_channel() -> Channel {
144144
.unwrap()
145145
}
146146

147-
pub async fn check_for_updates(ignore_rollout: bool) -> Result<Option<UpdatePackage>, Error> {
147+
pub async fn check_for_updates(ignore_rollout: bool, is_auto_update: bool) -> Result<Option<UpdatePackage>, Error> {
148148
let manifest = manifest();
149149
let ctx = Context::new();
150150
let file_type = match (&manifest.variant, ctx.platform().os()) {
@@ -157,6 +157,7 @@ pub async fn check_for_updates(ignore_rollout: bool) -> Result<Option<UpdatePack
157157
&manifest.variant,
158158
file_type.as_ref(),
159159
ignore_rollout,
160+
is_auto_update,
160161
)
161162
.await
162163
}
@@ -177,6 +178,8 @@ pub struct UpdateOptions {
177178
pub interactive: bool,
178179
/// If to relaunch into dashboard after update (false will launch in background)
179180
pub relaunch_dashboard: bool,
181+
/// Whether or not the update is being invoked automatically without the user's approval
182+
pub is_auto_update: bool,
180183
}
181184

182185
/// Attempt to update if there is a newer version of Fig
@@ -187,10 +190,11 @@ pub async fn update(
187190
ignore_rollout,
188191
interactive,
189192
relaunch_dashboard,
193+
is_auto_update,
190194
}: UpdateOptions,
191195
) -> Result<bool, Error> {
192196
info!("Checking for updates...");
193-
if let Some(update) = check_for_updates(ignore_rollout).await? {
197+
if let Some(update) = check_for_updates(ignore_rollout, is_auto_update).await? {
194198
info!("Found update: {}", update.version);
195199
debug!("Update info: {:?}", update);
196200

crates/fig_install/test_files/test-index.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@
487487

488488
{
489489
"version": "1.2.1",
490+
"disable_autoupdate": true,
490491
"packages": [
491492
{
492493
"kind": "deb",

crates/fig_util/src/manifest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub enum ManagedBy {
4949

5050
/// The target triplet, describes a platform on which the project is build for. Note that this also
5151
/// includes "fake" targets like `universal-apple-darwin` as provided by [Tauri](https://tauri.app/v1/guides/building/macos/#binary-targets)
52-
#[derive(Deserialize, Serialize, PartialEq, Eq, EnumString, Debug, Display)]
52+
#[derive(Deserialize, Serialize, PartialEq, Eq, EnumString, Debug, Clone, Display)]
5353
pub enum TargetTriple {
5454
#[serde(rename = "universal-apple-darwin")]
5555
#[strum(serialize = "universal-apple-darwin")]

crates/figterm/src/update.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub fn check_for_update(context: &Context) {
5555
}
5656

5757
tokio::spawn(async {
58-
match fig_install::check_for_updates(false).await {
58+
match fig_install::check_for_updates(false, true).await {
5959
Ok(Some(pkg)) => {
6060
if let Err(err) = fig_settings::state::set_value(UPDATE_AVAILABLE_KEY, pkg.version.to_string()) {
6161
warn!(?err, "Error setting {UPDATE_AVAILABLE_KEY}: {err}");

crates/q_cli/src/cli/debug/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ pub enum DebugSubcommand {
218218
#[arg(short = 'r', long)]
219219
enable_rollout: bool,
220220
#[arg(short, long)]
221+
is_auto_update: bool,
222+
#[arg(short, long)]
221223
override_threshold: Option<u8>,
222224
#[arg(short, long)]
223225
file_type: String,
@@ -748,6 +750,7 @@ impl DebugSubcommand {
748750
variant,
749751
version: current_version,
750752
enable_rollout,
753+
is_auto_update,
751754
override_threshold,
752755
file_type,
753756
} => {
@@ -765,6 +768,7 @@ impl DebugSubcommand {
765768
Some(&FileType::from_str(file_type)?),
766769
current_version,
767770
!enable_rollout,
771+
*is_auto_update,
768772
*override_threshold,
769773
);
770774

0 commit comments

Comments
 (0)