Skip to content

Commit

Permalink
feat: add ability to not use cache files (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
mgrachev authored Feb 21, 2022
1 parent 73a46a3 commit 129fdd6
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 54 deletions.
5 changes: 5 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coverage:
status:
project:
default:
target: 75%
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### 🛠 Fixed

## [v0.4.0] - 2022-02-21
### 🚀 Added
- Add ability to not use cache files [#27](https://github.com/mgrachev/update-informer/pull/27)

## [v0.3.0] - 2022-02-19
### 🚀 Added
- Add cargo features [#26](https://github.com/mgrachev/update-informer/pull/26)
Expand Down Expand Up @@ -39,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Check updates on GitHub [#8](https://github.com/mgrachev/update-informer/pull/8)
- Check updates on Crates.io [#1](https://github.com/mgrachev/update-informer/pull/1)

[v0.4.0]: https://github.com/mgrachev/update-informer/releases/tag/v0.4.0
[v0.3.0]: https://github.com/mgrachev/update-informer/releases/tag/v0.3.0
[v0.2.0]: https://github.com/mgrachev/update-informer/releases/tag/v0.2.0
[v0.1.0]: https://github.com/mgrachev/update-informer/releases/tag/v0.1.0
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "update-informer"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
authors = ["Mikhail Grachev <[email protected]>"]
description = "Update informer for CLI applications"
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ Add `update-informer` to `Cargo.toml`:

```toml
[dependencies]
update-informer = "0.3.0"
update-informer = "0.4.0"
```

By default, `update-informer` can only check on Crates.io.
To enable support for other registries, use `features`:

```toml
[dependencies]
update-informer = { version = "0.3.0", default_features = false, features = ["github"] }
update-informer = { version = "0.4.0", default_features = false, features = ["github"] }
```

Available features:
Expand Down Expand Up @@ -97,6 +97,20 @@ let informer = UpdateInformer::new(Crates, "crate_name", "0.1.0").interval(EVERY
informer.check_version(); // The check will start only after an hour
```

## Cache file

By default, `update-informer` creates a file in the cache directory to avoid spam requests to the registry API.

In order not to cache requests, use a zero interval:

```rust
use std::time::Duration;
use update_informer::{registry::Crates, Check, UpdateInformer};

let informer = UpdateInformer::new(Crates, "crate_name", "0.1.0").interval(Duration::ZERO);
informer.check_version();
```

## Request timeout

You can also change the request timeout. By default, it is 5 seconds:
Expand Down
3 changes: 1 addition & 2 deletions examples/crates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ fn main() {
let pkg_name = "update-informer";
let current_version = "0.1.0";

let informer =
UpdateInformer::new(Crates, pkg_name, current_version).interval(Duration::default());
let informer = UpdateInformer::new(Crates, pkg_name, current_version).interval(Duration::ZERO);

if let Ok(Some(new_version)) = informer.check_version() {
println!("A new release of {pkg_name} is available: v{current_version} -> {new_version}");
Expand Down
3 changes: 1 addition & 2 deletions examples/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ fn main() {
let pkg_name = "dotenv-linter/dotenv-linter";
let current_version = "3.1.0";

let informer =
UpdateInformer::new(GitHub, pkg_name, current_version).interval(Duration::default());
let informer = UpdateInformer::new(GitHub, pkg_name, current_version).interval(Duration::ZERO);

if let Ok(Some(new_version)) = informer.check_version() {
println!("A new release of {pkg_name} is available: v{current_version} -> {new_version}");
Expand Down
3 changes: 1 addition & 2 deletions examples/pypi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ fn main() {
let pkg_name = "filprofiler";
let current_version = "2022.1.0";

let informer =
UpdateInformer::new(PyPI, pkg_name, current_version).interval(Duration::default());
let informer = UpdateInformer::new(PyPI, pkg_name, current_version).interval(Duration::ZERO);

if let Ok(Some(new_version)) = informer.check_version() {
println!("A new release of {pkg_name} is available: v{current_version} -> {new_version}");
Expand Down
100 changes: 58 additions & 42 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//!
//! ```toml
//! [dependencies]
//! update-informer = { version = "0.3.0", default_features = false, features = ["github"] }
//! update-informer = { version = "0.4.0", default_features = false, features = ["github"] }
//! ```
//!
//! Available features:
Expand Down Expand Up @@ -61,6 +61,21 @@
//! let informer = UpdateInformer::new(Crates, "crate_name", "0.1.0").interval(EVERY_HOUR);
//! informer.check_version(); // The check will start only after an hour
//! ```
//!
//! ## Cache file
//!
//! By default, `update-informer` creates a file in the cache directory to avoid spam requests to the registry API.
//!
//! In order not to cache requests, use a zero interval:
//!
//! ```rust
//! use std::time::Duration;
//! use update_informer::{registry::Crates, Check, UpdateInformer};
//!
//! let informer = UpdateInformer::new(Crates, "crate_name", "0.1.0").interval(Duration::ZERO);
//! informer.check_version();
//! ```
//!
//! ## Request timeout
//!
//! You can also change the request timeout. By default, it is 5 seconds:
Expand Down Expand Up @@ -275,22 +290,31 @@ impl<R: Registry, N: AsRef<str>, V: AsRef<str>> Check for UpdateInformer<R, N, V
/// ```
fn check_version(&self) -> Result<Option<Version>, Error> {
let pkg = Package::new(self.name.as_ref());
let latest_version_file = VersionFile::new(R::NAME, &pkg, self.version.as_ref())?;
let last_modified = latest_version_file.last_modified()?;

let latest_version = if last_modified >= self.interval {
// This is needed to update mtime of the file
latest_version_file.recreate_file()?;

// If the interval is zero, don't use the cache file
let latest_version = if self.interval.is_zero() {
match R::get_latest_version(&pkg, self.timeout)? {
Some(v) => {
latest_version_file.write_version(&v)?;
v
}
Some(v) => v,
None => return Ok(None),
}
} else {
latest_version_file.get_version()?
let latest_version_file = VersionFile::new(R::NAME, &pkg, self.version.as_ref())?;
let last_modified = latest_version_file.last_modified()?;

if last_modified >= self.interval {
// This is needed to update mtime of the file
latest_version_file.recreate_file()?;

match R::get_latest_version(&pkg, self.timeout)? {
Some(v) => {
latest_version_file.write_version(&v)?;
v
}
None => return Ok(None),
}
} else {
latest_version_file.get_version()?
}
};

let latest_version = Version::parse(latest_version)?;
Expand Down Expand Up @@ -374,7 +398,7 @@ impl<V: AsRef<str>> Check for FakeUpdateInformer<V> {
#[cfg(test)]
mod tests {
use super::*;
use crate::registry::{Crates, GitHub};
use crate::registry::Crates;
use crate::test_helper::within_test_dir;
use mockito::Mock;
use std::fs;
Expand All @@ -383,17 +407,6 @@ mod tests {
const CURRENT_VERSION: &str = "3.1.0";
const LATEST_VERSION: &str = "3.1.1";

fn mock_github(pkg: &str) -> Mock {
let pkg = Package::new(pkg);
let (mock, _) = crate::test_helper::mock_github(
&pkg,
200,
"tests/fixtures/registry/github/release.json",
);

mock
}

fn mock_crates(pkg: &str) -> Mock {
let pkg = Package::new(pkg);
let (mock, _) = crate::test_helper::mock_crates(
Expand Down Expand Up @@ -421,34 +434,20 @@ mod tests {
within_test_dir(|_| {
let _mock = mock_crates(PKG_NAME);
let informer =
UpdateInformer::new(Crates, PKG_NAME, LATEST_VERSION).interval(Duration::default());
UpdateInformer::new(Crates, PKG_NAME, LATEST_VERSION).interval(Duration::ZERO);
let result = informer.check_version();

assert!(result.is_ok());
assert_eq!(result.unwrap(), None);
});
}

#[test]
fn check_version_on_github_test() {
within_test_dir(|_| {
let _mock = mock_github("owner/repo");
let informer = UpdateInformer::new(GitHub, "owner/repo", CURRENT_VERSION)
.interval(Duration::default());
let result = informer.check_version();
let version = Version::parse(LATEST_VERSION).expect("parse version");

assert!(result.is_ok());
assert_eq!(result.unwrap(), Some(version));
});
}

#[test]
fn check_version_on_crates_test() {
within_test_dir(|_| {
let _mock = mock_crates(PKG_NAME);
let informer = UpdateInformer::new(Crates, PKG_NAME, CURRENT_VERSION)
.interval(Duration::default());
let informer =
UpdateInformer::new(Crates, PKG_NAME, CURRENT_VERSION).interval(Duration::ZERO);
let result = informer.check_version();
let version = Version::parse(LATEST_VERSION).expect("parse version");

Expand Down Expand Up @@ -486,6 +485,21 @@ mod tests {
});
}

#[test]
fn do_not_create_version_file_test() {
within_test_dir(|version_file| {
assert!(!version_file.exists());

let _mock = mock_crates(PKG_NAME);
let informer =
UpdateInformer::new(Crates, PKG_NAME, CURRENT_VERSION).interval(Duration::ZERO);
let result = informer.check_version();

assert!(result.is_ok());
assert!(!version_file.exists());
});
}

#[test]
fn check_version_with_string_name_test() {
within_test_dir(|_| {
Expand Down Expand Up @@ -523,7 +537,9 @@ mod tests {
#[test]
fn fake_check_version_test() {
let version = "1.0.0";
let informer = FakeUpdateInformer::new(Crates, PKG_NAME, CURRENT_VERSION, version);
let informer = FakeUpdateInformer::new(Crates, PKG_NAME, CURRENT_VERSION, version)
.interval(Duration::ZERO)
.timeout(Duration::ZERO);
let result = informer.check_version();
let version = Version::parse(version).expect("parse version");

Expand Down
7 changes: 5 additions & 2 deletions src/test_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ static LOCK: Lazy<Mutex<()>> = once_cell::sync::Lazy::new(|| Mutex::default());

pub(crate) fn within_test_dir(f: fn(path: PathBuf)) {
// To avoid problems when working in parallel with the file system
let _m = LOCK.lock().expect("unlock mutex");
let mutex = LOCK.lock().expect("unlock mutex");

let test_dir: PathBuf = std::env::temp_dir().join("update-informer-test");
fs::create_dir_all(&test_dir).expect("create test dir");
Expand All @@ -25,7 +25,7 @@ pub(crate) fn within_test_dir(f: fn(path: PathBuf)) {
if let Err(e) = result {
// If we panic while holding the mutex, it becomes poisoned, and future
// tests fail in a unexpected way. So release lock before the panic.
drop(_m);
drop(mutex);
panic::resume_unwind(e);
}
}
Expand All @@ -37,19 +37,22 @@ pub(crate) fn mock_crates(pkg: &Package, status: usize, data_path: &str) -> (Moc
(mock_http(&mock_path, status, &data), data)
}

#[cfg(feature = "github")]
pub(crate) fn mock_github(pkg: &Package, status: usize, data_path: &str) -> (Mock, String) {
let mock_path = format!("/repos/{}/releases/latest", pkg);
let data = fs::read_to_string(data_path).expect("read file to string");

(mock_http(&mock_path, status, &data), data)
}

#[cfg(feature = "pypi")]
pub(crate) fn mock_pypi(pkg: &Package, status: usize, data_path: &str) -> (Mock, String) {
let mock_path = format!("/pypi/{}/json", pkg);
let data = fs::read_to_string(data_path).expect("read file to string");

(mock_http(&mock_path, status, &data), data)
}

pub(crate) fn mock_http(path: &str, status: usize, body: &str) -> Mock {
mock("GET", path)
.with_status(status)
Expand Down
2 changes: 1 addition & 1 deletion src/version_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl<'a> VersionFile<'a> {
Ok(meta) => meta,
Err(e) if e.kind() == ErrorKind::NotFound => {
self.write_version(&self.version)?;
return Ok(Duration::default());
return Ok(Duration::ZERO);
}
Err(e) => return Err(e.into()),
};
Expand Down

0 comments on commit 129fdd6

Please sign in to comment.