chore(deps): update rust crate self_update to v1 - #44
Merged
Merged
Conversation
renovate
Bot
force-pushed
the
renovate/self_update-1.x
branch
6 times, most recently
from
September 9, 2026 15:58
dc809fd to
72363f1
Compare
RouHim
force-pushed
the
renovate/self_update-1.x
branch
from
September 9, 2026 16:03
72363f1 to
db8ec2e
Compare
Contributor
Author
Edited/Blocked NotificationRenovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR. You can manually request rebase by checking the rebase/retry box above. |
Owner
|
🎉 This PR is included in version 1.20.30 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
0.44.0→1.0.0Release Notes
jaemk/self_update (self_update)
v1.3.0Compare Source
Additive over
1.2.0: a proxy setter for corporate networks, plus an archive-lookup fix. No APIbreaks, no migration needed.
Added
proxy(url)on every backend'sUpdate/ReleaseListbuilder and onDownload: route everyrequest (release listing and asset download alike) through an HTTP proxy, with credentials
allowed in the URL (
http://user:pass@proxy.corp:8080) and sent to the proxy asProxy-Authorization.HTTP_PROXY/HTTPS_PROXY/NO_PROXYalready covered theunauthenticated case; a proxy demanding a password previously forced callers to add
reqwestorureqas a direct dependency purely to build a client with a proxy on it. Applied to the samecrate-built client as
add_root_certificate, so an intercepting proxy that also needs a privateCA is one client. An injected client (
http_client/reqwest_client/ureq_agent) owns itsown proxy config and is unaffected. On reqwest the configured proxy is applied alongside the env
vars (first match wins); on a ureq-only build it replaces the env-var proxy (single proxy slot).
Only HTTP CONNECT proxies are supported.
Error::InvalidProxy { source }: an unparseable proxy URL, surfaced frombuild()/download_to/download_to_async. The password embedded in a proxy URL is redacted from thiserror (including from the wrapped client error) and from every
Debugrendering of the config,so it cannot leak into logs.
Changed
every push to
master. A push that does not bump the version no longer spends a full CI run inrelease.yml(build.ymlalready covers it) and no longer requestsrelease-environmentapproval. The workflow also skips itself on forks, where it could only fail for lack of publish
credentials, and gains a
workflow_dispatchtrigger for re-running a release that failed partway.Fixed
Extract::extract_file(and sobin_path_in_archiveon the update path) now finds an entrystored with a leading
./.tar -czf app.tar.gz -C dir .names every entry that way, and thelookup was an exact match, so such an archive failed with "Could not find the required path in
the archive" even though the file was present. A
./-prefixed request now also matches aplainly-named entry. Only a leading
./is ignored; interior components are still comparedexactly, so a same-named file in a subdirectory cannot be selected by accident. Reported in #27.
Removed
v1.2.0Compare Source
Additive over
1.1.0: one opt-in feature for the ureq client's trust store. No API breaks, nomigration needed.
Added
native-certsfeature: the crate-built ureq client verifies against the OS trust store(
RootCerts::PlatformVerifier) instead of Mozilla's bundled roots (RootCerts::WebPki). Neededbehind a TLS-intercepting corporate proxy, whose CA is installed on the machine and is absent from
the bundled set, so every request otherwise fails to verify. Off by default, so the ureq client's
trust store is unchanged unless asked for. No effect on reqwest (its rustls setup already uses
rustls-platform-verifier) or on an injectedureq::Agent, which owns its own TLS config.Changed
Removed
v1.1.0Compare Source
Additive over
1.0.0: two verification entry points for releases the built-in gates do not cover.No API breaks, no migration needed.
Added
verify_archive(|archive: &Path| -> Result<()>)on every backend'sUpdatebuilder: apre-extraction hook over the downloaded archive, for verification whose subject is the released
file itself (
gh attestation verify,cosign verify-blob). It runs after the checksum,release-digest, and signature gates and before extraction, in bundle mode as well.
verify_binarysees the extracted binary, which has a different digest than the artifact a forge attested, so it
could not host such a check. A rejection is the new
Error::ArchiveVerificationRejected { reason }, distinct fromverify_binary'sError::VerificationRejected, and built byError::archive_verification_rejected(..).checksum_from_asset(name)on every backend'sUpdatebuilder, under thechecksumsfeature:name a sums asset of the same release (e.g.
SHA256SUMS) and the updater fetches it before theartifact download and verifies the artifact against the entry for its file name.
Checksum::from_sums_file(sums, file_name)is the parser behind it, accepting the coreutils textand binary modes, leading path components, the BSD tag form,
#comments, and a whole-file baredigest, with the algorithm taken from the digest's length. A lookup that yields no digest is the
new
Error::ChecksumSourceInvalid { asset, reason }, never a silently skipped check. This is thedigest source for gitlab / gitea / s3, whose APIs publish no per-asset digest.
v1.0.0The stable 1.0.
1.xreleases from here are backwards compatible.Upgrading from 0.x: see the 1.0 migration guide (and its
agent-oriented version for automated tooling), which covers every
break across the release-candidate series.
Upgrading from a release candidate: the API is unchanged since rc.6, but rate-limited responses
are classified differently and credential handling is stricter. A 429, or a 403 reporting a spent
quota or carrying a usable
Retry-After, is nowError::RateLimitedrather thanError::Unauthorized, so amatcharm that keyed onUnauthorizedfor those cases needs aRateLimitedarm (useerr.rate_limit_delay()for the wait).retry/retry_asyncreturn sucha response immediately instead of spending the retry budget. On gitea, a token resolved by
auth_token_from_env()is withheld unless the configured host was acknowledged withallow_auth_host(..)or set explicitly withauth_token(..). A blankauth_token("")is treatedas unset. See the entries below for the full list.
Added
auth_token_from_env()on the github/gitlab/gitea/giteeUpdateandReleaseListbuilders(eight builders in all): read the token from the backend's conventional environment variables
instead of plumbing
std::env::varthrough the application. ReadsGH_TOKENthenGITHUB_TOKEN(the
ghCLI's documented precedence),GITLAB_TOKEN,GITEA_TOKEN,GITEE_TOKEN, using thefirst that is set and non-empty after trimming. An explicit
auth_token(..)always wins, ineither call order: the environment is only a fallback that fills an unset token, so an ambient
*_TOKENcan never displace the credential the application provisioned. Opt-in: the crate neverreads the environment on its own, since the configured API base can be a self-hosted host. With
nothing set the call is a no-op and the request goes out unauthenticated, and it never clears a
token.
CI_JOB_TOKENis deliberately not read on gitlab, even though every GitLab CI job exportsit: this backend sends
Authorization: Bearer, which is not GitLab's job-token mechanism (theJOB-TOKENheader /job_tokenparameter), so reading it would turn a working anonymous fetch ofa public project into a 401/403 inside CI; pass it explicitly with
auth_token(..)if you wantit. The variable set does not change with
api_base_url/host;ghreadsGH_ENTERPRISE_TOKEN/GITHUB_ENTERPRISE_TOKENfor a GitHub Enterprise host and this crate doesnot, so an enterprise base url still needs one of the variables above (or an explicit token).
(#78)
has_auth_token()on the same eight builders: whether an authorization token is configured, fromeither
auth_token(..)orauth_token_from_env(). Reports presence only -- never validity, andnever the value -- so an application can answer "am I about to run authenticated?" without
reimplementing the environment-variable list.
Error::RateLimited { status, url, reset_at, retry_after }: a spent request quota is nowdistinguished from a credential failure. A 429 is always rate limiting (with or without quota
headers); a 403 is rate limiting when it carries a zero remaining-quota header
(
x-ratelimit-remaining/ gitlab'sRateLimit-Remaining) or a usableRetry-After(GitHub'ssecondary rate limit, which answers 403 +
Retry-Afterwhile the primary quota is stillnonzero). A 403 with neither stays
Error::Unauthorized. Both server-supplied wait values areclamped to a 24h ceiling and resolve to
Nonebeyond it.http_status()andurl()cover the newvariant, and
Error::http_status_error_with_headers(status, url, &HeaderMap)gives a customHttpClientthe same classification (the header-blindError::http_status_errorcannot see thequota headers and so never produces
RateLimited). All three built-in client lanes -- reqwest,ureq, and an injected
ureq::Agent-- classify identically.(#78)
Error::rate_limit_delay() -> Option<Duration>: how long to wait before retrying aRateLimitedrequest, measured from now. Prefers the server's
Retry-Afterand otherwise derives the wait fromreset_atminus the current time;Nonewhen the window has already elapsed or nothing is known.This is the accessor to back off with: on GitHub's primary rate limit only
x-ratelimit-resetissent, so reading the raw fields and calling
retry_after.unwrap_or_default()sleeps zero and burnsmore quota.
Directory-bundle installs (macOS
.app):bundle_path_in_archive(..)names the bundle directoryinside the release archive and selects bundle mode, where the whole tree replaces
bundle_install_path(..)instead of one file replacingbin_install_path. The new bundle isstaged in the destination's parent and swapped by rename with the displaced tree stashed, so a
failure restores the original bundle; a running executable inside the bundle is renamed aside
first, so its path holds the new executable afterwards and composes with
restart(). On macOSbundle_install_pathdefaults to the nearest.appancestor of the running executable. Theverify_binaryhook receives the staged bundle root, and the opt-incheck_install_path_writablepreflight probes the bundle's parent directory. AddsError::NoAppBundle(no.appancestor to derive the path from),Error::ConflictingConfig(bundle mode combined with an explicit
bin_install_path/bin_path_in_archive), andError::AppTranslocated(a quarantined app running from a read-only translocated mount). Asymlinked
bundle_install_pathis resolved first, so the tree behind the link is replaced and thelink survives;
bundle_install_pathwithoutbundle_path_in_archiveis aMissingFielderrorrather than a silently discarded path.
(#145)
compression-tar-xzfeature: decode.tar.xz/.txzarchives and plain.xzsingle-fileassets (pure-Rust
lzma-rs, no Cliblzmadependency, so it cross-compiles like the rest of thedefault stack). Opt-in, mirroring
compression-tar-gz. AddsCompression::Xz.(#143)
self_update::verify_signature(archive_path, keys): run the same embedded-signature checkupdate()performs, standalone, for a caller that stages a download itself (e.g. an installerfetching a companion binary before the update loop exists). Takes
impl AsRef<Path>and a&[VerifyingKey]slice; any-of key semantics,.tar.gz/.ziponly (signaturesfeature).(#150)
native-tls-vendoredfeature: build OpenSSL from source and link it statically, for targetswhere a usable system OpenSSL is awkward (musl, some cross-compiles). Implies
native-tls;applies to the reqwest client. (#108)
UpdateStrategyand theupdate_strategy(..)builder setter: control which release the unpinned"latest" path installs when several are newer.
Compatible(default) prefers the newestsemver-compatible release, falling back to the newest overall;
Latestalways selects the newestrelease, even across a major bump. (#152)
Release::release_notes_url()andReleaseBuilder::release_notes_url(..): the release page URL,filled by the github/gitlab/gitea backends from the release's
html_url(_links.selfforgitlab);
Nonefor s3. Theshow_release_notes(bool)builder setter shows it (or the releasebody when no URL is available) in the confirmation prompt.
(#148)
tag_prefix(..)on the github/gitlab/giteaUpdatebuilders: derive the version from amonorepo-style tag such as
myapp-1.2.3(ormyapp-v1.2.3). Defaults to unset, which trims aleading
vas before; when set, tags without the prefix are skipped from the listing rather thanmis-parsed. (#76)
asset_key_pattern(..)on the s3Update/ReleaseListbuilders: a custom regex for deriving(name, version)from object keys, replacing the built-in matcher whose version group onlycaptures a
major.minor.patchtriple. Lets a pre-release key such asmybin-0.1.2-beta-x86_64-unknown-linux-gnuparse as0.1.2-betainstead of0.1.2. Thepattern must define
nameandversionnamed capture groups and is validated atbuild()(
Error::InvalidAssetKeyPattern); a captured version that does not parse as semver skips thekey. Unset keeps the existing matcher unchanged.
(#61)
self_update::restartmodule:restart()andrestart_with(args)relaunch the (alreadyreplaced) executable after an update so a long-running process picks up the new binary
immediately.
restart()reuses the current arguments;restart_with(args)supplies a freshargument list (e.g. to drop an
--upgradeflag so the restarted process does not update again).On unix the process image is replaced with
exec; on windows the new binary is spawned and thecurrent process exits. No feature gate, no new dependencies.
(#62)
self_update::check_interval::UpdateCheckGuard: a small stamp-file guard that throttles how oftenan application checks for updates.
should_check()reports whether the configured interval haselapsed since the last recorded check (a missing, corrupt, or future-dated stamp counts as due);
record_check()stamps the current time via a write-to-temp-then-rename so a concurrent readernever sees a partial stamp. The caller owns the stamp-file path; it is a guard, not a scheduler,
and pulls in no time/date dependency. (#79)
Docs: an "Authentication" section in the crate docs covering both token setters, every backend's
environment variables, the explicit-token precedence rule,
has_auth_token(), and the fact thatthe variable set does not change with the configured host. Authentication is cross-backend, so it
is no longer buried under the rate-limit heading where only a github reader would find it.
(#78)
Docs: a "GitHub rate limits" section in the crate docs covering GitHub's 60/hour (unauthenticated)
vs 5000/hour (authenticated) per-source-IP API limits, which responses classify as
Error::RateLimited, backing off withrate_limit_delay()(with a worked example), that the retryloop short-circuits rather than spending the budget, and how to mitigate (a token, and checking
less often via
UpdateCheckGuard). The "Custom HTTP client" section points a custom transport atError::http_status_error_with_headers, since the header-blindError::http_status_errorcannever report a rate limit. (#78)
Docs: the Features section now names the exact
no HTTP client selectedcompile error aclient-less build (e.g.
default-features = false, features = ["rustls"]) produces, and shows thefix (add a client, e.g.
features = ["ureq", "rustls", "github"]).(#168)
check_install_path_writable(bool)builder setter (defaultfalse) andError::InstallPathNotWritable { path: PathBuf }: opt-in preflight that probesbin_install_pathwritability before the download; only a definite
PermissionDeniedrefusal errors, indeterminateresults proceed. The install step also raises this error on a permission failure, always naming
the path. (#112)
backends::manifest(manifestfeature): fetch and install releases from a staticmanifest.jsonserved by any HTTP endpoint, with no forge-specific API.ManifestSourceimplements
ReleaseSource(andAsyncReleaseSourceunderasync); the facadeUpdate::configure()wraps it with the standard update pipeline. Release entries with anon-semver
versionare skipped with a debug log. Relative asseturlvalues resolve againstthe manifest URL's directory (truncated at the last
/). An assetdigestfield(
sha256:<hex>) maps toReleaseAsset::digest()and plugs into the existing release-digestverification path (
checksumsfeature). No new dependencies.(#74)
giteebackend:backends::gitee::ReleaseList,Update, andAsyncUpdatefor Gitee releases,mirroring the
giteabackend. Default hosthttps://gitee.comwith an optional.host()setterfor enterprise instances. Bearer-token auth via
auth_token. Nameless source-archive assets areskipped with a debug log rather than erroring.
(#121)
Changed
A rate-limited response no longer returns
Error::Unauthorized. A 429, a 403 whose headersreport a spent quota (
x-ratelimit-remaining: 0/ gitlab'sRateLimit-Remaining: 0), or a 403carrying a usable
Retry-Afternow returnsError::RateLimitedinstead.Erroris#[non_exhaustive], so this does not break compilation: code that matchedUnauthorized { status: 403, .. }to detect rate limiting (which the previous docs told users towrite) keeps compiling and silently stops matching, falling through to the wildcard arm. Migration:
match
Error::RateLimited { .. }instead, and take the wait fromrate_limit_delay()rather thanthe fields.
A bare 403 with no quota signal is still
Error::Unauthorized, so a genuine credential failure isunchanged. (#78)
retry/retry_asyncno longer retry a rate-limited request. AnError::RateLimitedreturnsimmediately regardless of the configured
retries, instead of spending the budget on a quota thatis already at zero (and, on GitHub's unauthenticated per-IP budget, shared with everyone behind the
same egress IP). Every other error still consumes the budget as before. The download path retries
through the same loop, so it short-circuits too.
Each backend
Update/ReleaseListbuilder'sDebugoutput redacts the authorization token,rendering it as
"<token>"instead of the value, so logging a builder no longer prints an ambientCI credential. All other fields are still shown.
A credential passed via
request_header("Authorization", ..)(orPRIVATE-TOKEN,Cookie, orany header name ending in
-token, case-insensitive) is now marked sensitive, so it is redactedin a builder's
Debugoutput and kept out of the underlying HTTP client's own header logging, thesame as a token set with
auth_token(..). Previously only theauth_tokenslot was redacted, soa credential passed as a header printed verbatim.
build()logs alog::warn!when a token resolved from the environment would be sent to a hostother than the backend's canonical one (
api.github.com,gitlab.com,gitee.com). Theenvironment variables are conventions of the backend's own service, so an application that exposes
its update url as configuration would otherwise hand
GITHUB_TOKENto an arbitrary host with nosignal. An explicitly-set token is the application's own decision and is never warned about. gitea
has no canonical host, so its rule is stricter: an env-sourced token is withheld at
build()rather than sent, and the request goes out anonymous, unless the configured host was acknowledged
by passing it to
allow_auth_host(..)or by setting the token explicitly withauth_token(..);the warning still fires, naming the host and the remedy. github/gitlab/gitee are unchanged: an
env-sourced token to a non-canonical host still warns and is still sent. On every backend, a host
passed to
allow_auth_hostno longer produces that warning.A blank (empty or all-whitespace)
auth_token(..)is now treated as unset: it no longer blocks theauth_token_from_env()fallback, no longer sends an emptyAuthorizationheader, andhas_auth_token()reportsfalsefor it.Error::http_status_error(429, url)returnsError::RateLimited(with both wait fieldsNone)instead of
Error::HttpStatus, so a customHttpClientthat has no headers to hand over stillreports a 429 as rate limiting. 429 does not need a header to mean "too many requests" (RFC 6585);
401/403 are unchanged on that path, since only a header distinguishes a spent quota from a
credential failure. Use
Error::http_status_error_with_headersto get the full classification.A
Retry-After: 0is no longer treated as a rate-limit signal. A bare 403 carrying a zeroRetry-Afterand no quota header staysError::Unauthorizedinstead of becoming a zero-waitError::RateLimited.An injected
ureq::Agentclassifies a non-2xx response exactly like the crate-built agents. Theagent keeps ureq's default
http_status_as_error(true), whoseStatusCodeerror carries noheaders, so that path could not see the quota headers; the client now applies a per-request
http_status_as_error(false)override, and all three client lanes (reqwest, ureq, injected ureq)produce the same
NotFound/Unauthorized/RateLimited/HttpStatusmapping. Nothing elseabout the injected agent's timeout / TLS / proxy configuration is touched.
A recognized-but-unsupported compression extension now fails loudly instead of silently
installing the still-compressed bytes as the binary: a
.tar.xz/.txz/.xzasset withoutthe
compression-tar-xzfeature returnsError::CompressionNotEnabled("xz")(matching theexisting
.gzhandling), rather than writing the compressed archive to the install path.(#143)
Install-step IO failures now name the install path:
PermissionDeniedbecomesError::InstallPathNotWritable { path }and any other IO error becomesError::Iowith theinstall path embedded in the message (the
ErrorKindis preserved).(#112)
Fixed
Extract::extract_into) now restores symlink entries as real symlinks on unixinstead of writing the link's target path out as a regular file. Materializing the target string
corrupted directory trees that rely on symlinks (for example a macOS
.appbundle whoseFrameworks/*/Versions/Currentlinks are load-bearing for the code signature), so a signed appextracted from a zip failed to launch. Tar extraction already handled symlinks correctly. A
symlink target that would escape the extraction root (an absolute target, or a relative one whose
..components resolve above the destination) is rejected, matching the existing zip-slip defenseon entry names. That per-entry check is lexical, so as a backstop every zip entry's physical
parent is canonicalized after its directories are created and must equal the canonical extraction
root joined with the entry's lexical parent; this rejects a symlinked-parent traversal (an entry
d/sl -> ..followed byd/sl/evil -> ../../x, lexically in-bounds but physically above theroot) that the lexical check alone cannot catch, while descent through real directories is
unaffected. On windows, where creating symlinks needs elevated privileges, symlink entries
keep the previous regular-file behavior.
Extract::extract_filenow errors on a symlink entryrather than writing its target string out as the requested file.
{:?}, andPath'sDebugimplquotes the path and escapes each separator, so on windows the "Current exe" line read
"C:\\Users\\me\\bin\\app.exe", doubled backslashes the user never typed. TheCurrent exeandCurrent bundlelines now print throughPath::display(), so the path appears exactly as theplatform writes it. The
New exe release/New exe download urllines are strings rather thanpaths and keep their existing quoted form.
(#201)
Error::InvalidAssetNamealongside the existing empty /./../ separator / absolute-pathcases. The name is remote-controlled and is echoed into the confirmation block, so a
\ror anESC sequence in it could repaint or hide the lines (including the download url) that the user
reads before authorizing the replacement.
bundle_install_pathis resolved once, before the confirmationprompt, and that single resolved path is what the status block names, what the
check_install_path_writablepreflight probes, and what the swap replaces. It was resolved againafter the prompt, so repointing the link in between would have redirected the replacement to a
tree the user never approved.
Path::display()rather than{:?}, so a windows path inthose messages keeps single separators. Diagnostic
debug!logs keep{:?}, which renders anon-UTF-8 path unambiguously.
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.