Skip to content

Commit

Permalink
feat: add ability to implement your own registry (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
mgrachev authored Mar 24, 2022
1 parent 93d3ec6 commit 30dedb3
Show file tree
Hide file tree
Showing 18 changed files with 273 additions and 142 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ jobs:
id: toolchain
with:
profile: minimal
toolchain: nightly
# FIXME: https://github.com/briansmith/ring/issues/1469
toolchain: nightly-2022-03-22
override: true
- name: Chown cargo registry because of caching
run: sudo chown -R runner ~/.cargo/registry
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning].

## [Unreleased]
### 🚀 Added

### ⚙️ Changed

### 🛠 Fixed

## [v0.5.0] - 2022-03-24
### 🚀 Added
- Add ability to implement your own registry to check updates [#37]
- Add `stale` action [#33]
- Add dependabot [#28]

Expand Down Expand Up @@ -45,6 +53,7 @@ and this project adheres to [Semantic Versioning].
- Check updates on GitHub [#8]
- Check updates on Crates.io [#1]

[#37]: https://github.com/mgrachev/update-informer/pull/37
[#33]: https://github.com/mgrachev/update-informer/pull/33
[#28]: https://github.com/mgrachev/update-informer/pull/28
[#27]: https://github.com/mgrachev/update-informer/pull/27
Expand All @@ -64,6 +73,7 @@ and this project adheres to [Semantic Versioning].
[#8]: https://github.com/mgrachev/update-informer/pull/8
[#1]: https://github.com/mgrachev/update-informer/pull/1

[v0.5.0]: https://github.com/mgrachev/update-informer/releases/tag/v0.5.0
[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
Expand Down
12 changes: 10 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
[package]
name = "update-informer"
version = "0.4.0"
version = "0.5.0"
edition = "2021"
authors = ["Mikhail Grachev <[email protected]>"]
description = "Update informer for CLI applications"
homepage = "https://github.com/mgrachev/update-informer"
repository = "https://github.com/mgrachev/update-informer"
documentation = "https://docs.rs/update-informer"
license = "MIT"
readme = "README.md"
keywords = ["cli", "update", "informer", "notifier", "github"]
categories = ["command-line-interface"]
include = ["/src", "README.md"]

[dependencies]
directories = "4.0"
Expand All @@ -20,7 +24,11 @@ ureq = { version = "2.4", features = ["json"] }

[dev-dependencies]
mockito = "0.31.0"
once_cell = "1.9.0"
once_cell = "1.10.0"

# Used in examples of documentation
reqwest = { version = "0.11", features = ["blocking"] }
colored = "2.0.0"

[features]
default = ["crates"]
Expand Down
90 changes: 61 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ It checks for a new version on Crates.io, GitHub and PyPI 🚀

## Benefits
* Support of **Crates.io**, **GitHub** and **PyPI**.
* Ability to **implement** your **own registry** to check updates.
* Configurable **check frequency** and **request timeout**.
* **Minimum dependencies** - only [directories], [ureq], [semver] and [serde].

Expand All @@ -39,34 +40,34 @@ Add `update-informer` to `Cargo.toml`:

```toml
[dependencies]
update-informer = "0.4.0"
update-informer = "0.5.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.4.0", default_features = false, features = ["github"] }
update-informer = { version = "0.5.0", default_features = false, features = ["github"] }
```

Available features:

Name | Default?
---|---
cargo | Yes
github | No
pypi | No
| Name | Default? |
|--------|----------|
| cargo | Yes |
| github | No |
| pypi | No |

## Crates.io

To check for a new version on Crates.io, use the `UpdateInformer::check_version` function.<br>
This function takes the project name and current version as well as check interval:

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

let informer = UpdateInformer::new(Crates, "crate_name", "0.1.0");
let informer = update_informer::new(registry::Crates, "crate_name", "0.1.0");
if let Ok(Some(version)) = informer.check_version() {
println!("New version is available: {}", version);
}
Expand All @@ -75,11 +76,11 @@ if let Ok(Some(version)) = informer.check_version() {
Also, you can take the name and version of the project from `Cargo` using environment variables:

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

let name = env!("CARGO_PKG_NAME");
let version = env!("CARGO_PKG_VERSION");
UpdateInformer::new(Crates, name, version).check_version();
update_informer::new(registry::Crates, name, version).check_version();
```

## Interval
Expand All @@ -89,11 +90,11 @@ By default, the interval is 24 hours, but you can change it:

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

const EVERY_HOUR: Duration = Duration::from_secs(60 * 60);

let informer = UpdateInformer::new(Crates, "crate_name", "0.1.0").interval(EVERY_HOUR);
let informer = update_informer::new(registry::Crates, "crate_name", "0.1.0").interval(EVERY_HOUR);
informer.check_version(); // The check will start only after an hour
```

Expand All @@ -105,9 +106,9 @@ In order not to cache requests, use a zero interval:

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

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

Expand All @@ -117,11 +118,11 @@ You can also change the request timeout. By default, it is 5 seconds:

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

const THIRTY_SECONDS: Duration = Duration::from_secs(30);

let informer = UpdateInformer::new(Crates, "crate_name", "0.1.0").timeout(THIRTY_SECONDS);
let informer = update_informer::new(registry::Crates, "crate_name", "0.1.0").timeout(THIRTY_SECONDS);
informer.check_version();
```

Expand All @@ -130,9 +131,9 @@ You can also change the request timeout. By default, it is 5 seconds:
To check for a new version on GitHub (note that the project name must contain the owner):

```rust
use update_informer::{registry::GitHub, Check, UpdateInformer};
use update_informer::{registry, Check};

let informer = UpdateInformer::new(GitHub, "owner/repo", "0.1.0");
let informer = update_informer::new(registry::GitHub, "owner/repo", "0.1.0");
informer.check_version();
```

Expand All @@ -141,28 +142,54 @@ informer.check_version();
To check for a new version on PyPI:

```rust
use update_informer::{registry::PyPI, Check, UpdateInformer};
use update_informer::{registry, Check};

let informer = UpdateInformer::new(PyPI, "package_name", "0.1.0");
let informer = update_informer::new(registry::PyPI, "package_name", "0.1.0");
informer.check_version();
```

## Implementing your own registry

You can implement your own registry to check updates. For example:

```rust
use std::time::Duration;
use update_informer::{registry, Check, Package, Registry, Result};

struct YourOwnRegistry;

impl Registry for YourOwnRegistry {
const NAME: &'static str = "your_own_registry";

fn get_latest_version(pkg: &Package, _timeout: Duration) -> Result<Option<String>> {
let url = format!("https://your_own_registry.com/{}/latest-version", pkg);
let result = reqwest::blocking::get(url)?.text()?;
let version = result.trim().to_string();

Ok(Some(version))
}
}

let informer = update_informer::new(YourOwnRegistry, "package_name", "0.1.0");
informer.check_version();
```

## Example

<details>
<summary>
A real example of using <code>update_informer</code> with <a href="https://github.com/mackwic/colored">colored</a> crate
A real example of using <code>update_informer</code> with <a href="https://github.com/mackwic/colored">colored</a> crate.
</summary>

```rust
use colored::*;
use update_informer::{registry::Crates, Check, UpdateInformer};
use update_informer::{registry, Check};

fn main() {
let pkg_name = env!("CARGO_PKG_NAME");
let current_version = env!("CARGO_PKG_VERSION");

let informer = UpdateInformer::new(Crates, pkg_name, current_version);
let informer = update_informer::new(registry::Crates, pkg_name, current_version);
if let Ok(Some(version)) = informer.check_version() {
let msg = format!(
"A new release of {pkg_name} is available: v{current_version} -> {new_version}",
Expand Down Expand Up @@ -196,16 +223,16 @@ In order not to check for updates in tests, you can use the `FakeUpdateInformer:
Example of usage in unit tests:

```rust
use update_informer::{registry::Crates, Check, FakeUpdateInformer, UpdateInformer};
use update_informer::{registry, Check};

let name = "crate_name";
let version = "0.1.0";

#[cfg(not(test))]
let informer = UpdateInformer::new(Crates, name, version);
let informer = update_informer::new(registry::Crates, name, version);

#[cfg(test)]
let informer = FakeUpdateInformer::new(Crates, name, version, "1.0.0");
let informer = update_informer::fake(registry::Crates, name, version, "1.0.0");

if let Ok(Some(version)) = informer.check_version() {
println!("New version is available: {}", version);
Expand All @@ -224,11 +251,16 @@ stub_check_version = []
Then use this feature flag in your code and integration tests:

```rust
use update_informer::{registry, Check};

let name = "crate_name";
let version = "0.1.0";

#[cfg(not(feature = "stub_check_version"))]
let informer = UpdateInformer::new(Crates, name, version);
let informer = update_informer::new(registry::Crates, name, version);

#[cfg(feature = "stub_check_version")]
let informer = FakeUpdateInformer::new(Crates, name, version, "1.0.0");
let informer = update_informer::fake(registry::Crates, name, version, "1.0.0");

informer.check_version();
```
Expand Down
5 changes: 3 additions & 2 deletions examples/crates.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::time::Duration;
use update_informer::{registry::Crates, Check, UpdateInformer};
use update_informer::{registry, Check};

fn main() {
let pkg_name = "update-informer";
let current_version = "0.1.0";

let informer = UpdateInformer::new(Crates, pkg_name, current_version).interval(Duration::ZERO);
let informer =
update_informer::new(registry::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
5 changes: 3 additions & 2 deletions examples/github.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::time::Duration;
use update_informer::{registry::GitHub, Check, UpdateInformer};
use update_informer::{registry, Check};

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::ZERO);
let informer =
update_informer::new(registry::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
5 changes: 3 additions & 2 deletions examples/pypi.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::time::Duration;
use update_informer::{registry::PyPI, Check, UpdateInformer};
use update_informer::{registry, Check};

fn main() {
let pkg_name = "filprofiler";
let current_version = "2022.1.0";

let informer = UpdateInformer::new(PyPI, pkg_name, current_version).interval(Duration::ZERO);
let informer =
update_informer::new(registry::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
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[toolchain]
channel = "1.58.1"
channel = "1.59.0"
components = ["rustfmt", "clippy"]
4 changes: 2 additions & 2 deletions src/http.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::Error;
use crate::Result;
use serde::de::DeserializeOwned;
use std::time::Duration;

Expand All @@ -23,7 +23,7 @@ impl Client {
}

/// Sends a request and parses it to JSON
pub(crate) fn call<T: DeserializeOwned>(self) -> Result<T, Error> {
pub(crate) fn call<T: DeserializeOwned>(self) -> Result<T> {
let json = self.request.call()?.into_json()?;

Ok(json)
Expand Down
Loading

0 comments on commit 30dedb3

Please sign in to comment.