Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@
writability before the download; only a definite `PermissionDenied` refusal errors, indeterminate
results proceed. The install step also raises this error on a permission failure, always naming
the path. ([#112](https://github.com/jaemk/self_update/issues/112))
- `backends::manifest` (`manifest` feature): fetch and install releases from a static
`manifest.json` served by any HTTP endpoint, with no forge-specific API. `ManifestSource`
implements `ReleaseSource` (and `AsyncReleaseSource` under `async`); the facade
`Update::configure()` wraps it with the standard update pipeline. Release entries with a
non-semver `version` are skipped with a debug log. Relative asset `url` values resolve against
the manifest URL's directory (truncated at the last `/`). An asset `digest` field
(`sha256:<hex>`) maps to `ReleaseAsset::digest()` and plugs into the existing release-digest
verification path (`checksums` feature). No new dependencies.
([#74](https://github.com/jaemk/self_update/issues/74))

### Changed
- A recognized-but-unsupported compression extension now fails loudly instead of silently
Expand Down
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ features = [
"github",
"gitlab",
"gitea",
"manifest",
"s3",
]
rustdoc-args = ["--cfg", "docsrs"]
Expand Down Expand Up @@ -91,6 +92,9 @@ progress-bar = ["dep:indicatif"]
github = []
gitlab = []
gitea = []
# Service-agnostic backend: update from a static JSON release manifest served over HTTP(S). Uses
# the already-present serde/serde_json deps, so it pulls in nothing new.
manifest = []
s3 = ["dep:quick-xml"]

# Async update API (tokio-only, reqwest-only). Adds `*_async` verbs alongside the unchanged
Expand Down Expand Up @@ -131,6 +135,10 @@ required-features = ["gitea"]
name = "s3"
required-features = ["s3"]

[[example]]
name = "manifest"
required-features = ["manifest"]

[[example]]
name = "embedded_key"
required-features = ["github", "signatures"]
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ ARCHIVE_FEATURES = archive-tar \
checksums \
s3-auth
# Full feature set for the default `reqwest` client:
REQWEST_FEATURES = github gitlab gitea s3 $(ARCHIVE_FEATURES)
REQWEST_FEATURES = github gitlab gitea manifest s3 $(ARCHIVE_FEATURES)
# Full feature set for the `ureq` client (needs `--no-default-features`):
UREQ_FEATURES = ureq native-tls github gitlab gitea s3 $(ARCHIVE_FEATURES)
UREQ_FEATURES = ureq native-tls github gitlab gitea manifest s3 $(ARCHIVE_FEATURES)
# Full reqwest feature set plus the async API (reqwest-only):
ASYNC_FEATURES = async github gitlab gitea s3 $(ARCHIVE_FEATURES)
ASYNC_FEATURES = async github gitlab gitea manifest s3 $(ARCHIVE_FEATURES)

# The backends, one runnable example each. NOTE: unlike a typical example,
# running one performs a REAL self-update (network + replaces the binary), so
# the `examples` goals BUILD them rather than run them.
SELF_UPDATE_EXAMPLES = github gitlab gitea s3 custom embedded_key
SELF_UPDATE_EXAMPLES = github gitlab gitea manifest s3 custom embedded_key
SELF_UPDATE_EXAMPLE_TARGETS = $(addprefix examples/, $(SELF_UPDATE_EXAMPLES))

EXAMPLE_TARGETS = examples $(SELF_UPDATE_EXAMPLE_TARGETS)
Expand Down
38 changes: 33 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
`self_update` provides updaters for updating rust executables in-place from various release
distribution backends.

Supported backends: **GitHub**, **GitLab**, **Gitea**, and **S3** (Amazon S3, Google GCS,
DigitalOcean Spaces, or any S3-compatible endpoint). Each exposes the same `Update`
(configure -> build -> update) and `ReleaseList` builder API.
Supported backends: **GitHub**, **GitLab**, **Gitea**, **S3** (Amazon S3, Google GCS,
DigitalOcean Spaces, or any S3-compatible endpoint), and **Manifest** (any static file server).
The forge and S3 backends each expose a `ReleaseList` builder alongside the `Update`
(configure -> build -> update) API; the manifest backend exposes `Update` only.

## Quick start

Expand Down Expand Up @@ -78,6 +79,7 @@ The following are opt-in; activate the one(s) your release files need:
* `gitea`: the Gitea Releases backend;
* `s3`: the S3-compatible backend (Amazon S3, GCS, DigitalOcean Spaces, etc.);
* `s3-auth`: sign S3 requests (AWS SigV4) for private buckets; implies `s3`;
* `manifest`: the static-file manifest backend; fetches releases from a `manifest.json` served by any HTTP endpoint; no new dependencies;
* `archive-tar`: support for _tar_ archive format;
* `archive-zip`: support for _zip_ archive format;
* `compression-tar-gz`: support for _gzip_ compression (`.tar.gz`, `.tgz`, plain `.gz`);
Expand All @@ -88,7 +90,7 @@ The following are opt-in; activate the one(s) your release files need:
* `checksums`: verify a downloaded artifact against a SHA-256/SHA-512 checksum before installing it -- automatically against the digest github publishes per release asset, and/or against a known checksum you pass in (e.g. from a `SHA256SUMS` file); see [Checksum verification](#checksum-verification) below;
* `async`: add async (`*_async`) update methods alongside the unchanged blocking API; tokio-only, requires `reqwest` (ureq and reqwest can coexist -- reqwest serves the async path, and the sync API prefers reqwest when both are present); see [Async](#async) below.

`github` is the only backend in the default feature set. The S3 backend requires the `s3` feature; `s3-auth` implies `s3`. `gitlab` and `gitea` each require their own feature.
`github` is the only backend in the default feature set. The S3 backend requires the `s3` feature; `s3-auth` implies `s3`. `gitlab`, `gitea`, and `manifest` each require their own feature.

### Example

Expand Down Expand Up @@ -130,6 +132,27 @@ fn update() -> Result<(), Box<dyn ::std::error::Error>> {
}
```

The `manifest` backend (`manifest` feature) serves releases from a `manifest.json` file hosted
on any static file server. The tool author publishes the manifest at a stable URL; assets may be
absolute URLs or relative paths resolved against that URL. Asset `digest` fields (`sha256:<hex>`)
plug into the existing checksum verification path when the `checksums` feature is on. See
`specs/ref-manifest-backend.md` for the full schema.

```rust
use self_update::cargo_crate_version;

fn update() -> Result<(), Box<dyn std::error::Error>> {
let status = self_update::backends::manifest::Update::configure()
.manifest_url("https://example.net/releases/manifest.json")
.bin_name("app")
.current_version(cargo_crate_version!())
.build()?
.update()?;
println!("Manifest update status: `{}`!", status.version());
Ok(())
}
```

Separate utilities are also exposed (**NOTE**: the following example extracts a `.tar.gz`, which
_requires_ both the `archive-tar` and `compression-tar-gz` features -- `archive-tar` reads the tar
archive and `compression-tar-gz` decodes the gzip layer; see the [features](#features) section
Expand Down Expand Up @@ -364,13 +387,18 @@ so they are reached through their backend modules rather than re-exported at the
* `backends::gitea::ReleaseList`
* `backends::s3::ReleaseList`

The `manifest` backend has no separate `ReleaseList` struct. Its `ManifestSource` is a
`ReleaseSource` implementation that can be used directly, or listing can be driven through the
inherent verbs (`get_latest_release`, `get_newer_releases`, `is_update_available`) on a built
`manifest::Update`.

The custom backend has no `ReleaseList` by design: listing is performed entirely by your
`ReleaseSource` (or `AsyncReleaseSource`) implementation, which already returns
`Release` values directly.

### Custom backends

To update from a host the built-in backends (`github`, `gitlab`, `gitea`, `s3`) don't cover —
To update from a host the built-in backends (`github`, `gitlab`, `gitea`, `s3`, `manifest`) don't cover —
another forge, a private artifact registry, a plain HTTP directory — implement the
`ReleaseSource` trait and drive a full update through the `backends::custom` backend, which reuses
the crate's compare → select-asset → download → verify → extract → install flow. Only
Expand Down
34 changes: 34 additions & 0 deletions examples/manifest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*!
Example updating an executable to the latest version described by a static JSON release manifest.

`cargo run --example manifest --features "manifest archive-tar compression-tar-gz"`

Point `manifest_url` at a JSON document served over HTTP(S) that lists your releases and their
assets (the release artifacts live alongside it on the same static host, or at absolute URLs). No
server-side API is needed — regenerate the manifest at release time. See the
`self_update::backends::manifest` module docs for the schema.
*/

use self_update::cargo_crate_version;

fn run() -> Result<(), Box<dyn ::std::error::Error>> {
let mut builder = self_update::backends::manifest::Update::configure();
builder
.manifest_url("https://example.net/releases/manifest.json")
.bin_name("myapp")
.show_download_progress(true)
//.release_tag("v9.9.10")
//.no_confirm(true)
.current_version(cargo_crate_version!());

let status = builder.build()?.update()?;
println!("Update status: `{}`!", status.version());
Ok(())
}

pub fn main() {
if let Err(e) = run() {
println!("[ERROR] {}", e);
::std::process::exit(1);
}
}
1 change: 1 addition & 0 deletions specs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ design before it can be built). Keep each row's status current with `spec.py set
| Corporate Network Config | pending | [corporate-network-config.md](corporate-network-config.md) |
| Restart After Update | done | [ref-restart.md](ref-restart.md) |
| Update-check Interval Guard | done | [ref-check-interval.md](ref-check-interval.md) |
| Manifest Backend | done | [ref-manifest-backend.md](ref-manifest-backend.md) |

## Conventions

Expand Down
Loading
Loading