Skip to content

chore(deps): update rust crate self_update to v1 - #44

Merged
RouHim merged 1 commit into
mainfrom
renovate/self_update-1.x
Sep 9, 2026
Merged

RouHim merged 1 commit into
mainfrom
renovate/self_update-1.x

Conversation

@renovate

@renovate renovate Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Type Update Change
self_update dependencies major 0.44.01.0.0

Release Notes

jaemk/self_update (self_update)

v1.3.0

Compare Source

Additive over 1.2.0: a proxy setter for corporate networks, plus an archive-lookup fix. No API
breaks, no migration needed.

Added
  • proxy(url) on every backend's Update / ReleaseList builder and on Download: route every
    request (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 as
    Proxy-Authorization. HTTP_PROXY / HTTPS_PROXY / NO_PROXY already covered the
    unauthenticated case; a proxy demanding a password previously forced callers to add reqwest or
    ureq as a direct dependency purely to build a client with a proxy on it. Applied to the same
    crate-built client as add_root_certificate, so an intercepting proxy that also needs a private
    CA is one client. An injected client (http_client / reqwest_client / ureq_agent) owns its
    own 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 from build() /
    download_to / download_to_async. The password embedded in a proxy URL is redacted from this
    error (including from the wrapped client error) and from every Debug rendering of the config,
    so it cannot leak into logs.
Changed
  • The publish workflow now runs only when the crate version is not yet on crates.io, instead of on
    every push to master. A push that does not bump the version no longer spends a full CI run in
    release.yml (build.yml already covers it) and no longer requests release-environment
    approval. The workflow also skips itself on forks, where it could only fail for lack of publish
    credentials, and gains a workflow_dispatch trigger for re-running a release that failed partway.
Fixed
  • Extract::extract_file (and so bin_path_in_archive on the update path) now finds an entry
    stored with a leading ./. tar -czf app.tar.gz -C dir . names every entry that way, and the
    lookup 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 a
    plainly-named entry. Only a leading ./ is ignored; interior components are still compared
    exactly, so a same-named file in a subdirectory cannot be selected by accident. Reported in #​27.
Removed

v1.2.0

Compare Source

Additive over 1.1.0: one opt-in feature for the ureq client's trust store. No API breaks, no
migration needed.

Added
  • native-certs feature: the crate-built ureq client verifies against the OS trust store
    (RootCerts::PlatformVerifier) instead of Mozilla's bundled roots (RootCerts::WebPki). Needed
    behind 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 injected ureq::Agent, which owns its own TLS config.
Changed
Removed

v1.1.0

Compare 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's Update builder: a
    pre-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_binary
    sees 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 from verify_binary's
    Error::VerificationRejected, and built by Error::archive_verification_rejected(..).

  • checksum_from_asset(name) on every backend's Update builder, under the checksums feature:
    name a sums asset of the same release (e.g. SHA256SUMS) and the updater fetches it before the
    artifact 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 text
    and binary modes, leading path components, the BSD tag form, # comments, and a whole-file bare
    digest, 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 the
    digest source for gitlab / gitea / s3, whose APIs publish no per-asset digest.

v1.0.0

The stable 1.0. 1.x releases 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 now Error::RateLimited rather than
Error::Unauthorized, so a match arm that keyed on Unauthorized for those cases needs a
RateLimited arm (use err.rate_limit_delay() for the wait). retry / retry_async return such
a 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 with
allow_auth_host(..) or set explicitly with auth_token(..). A blank auth_token("") is treated
as unset. See the entries below for the full list.

Added
  • auth_token_from_env() on the github/gitlab/gitea/gitee Update and ReleaseList builders
    (eight builders in all): read the token from the backend's conventional environment variables
    instead of plumbing std::env::var through the application. Reads GH_TOKEN then GITHUB_TOKEN
    (the gh CLI's documented precedence), GITLAB_TOKEN, GITEA_TOKEN, GITEE_TOKEN, using the
    first that is set and non-empty after trimming. An explicit auth_token(..) always wins, in
    either call order: the environment is only a fallback that fills an unset token, so an ambient
    *_TOKEN can never displace the credential the application provisioned. Opt-in: the crate never
    reads 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_TOKEN is deliberately not read on gitlab, even though every GitLab CI job exports
    it: this backend sends Authorization: Bearer, which is not GitLab's job-token mechanism (the
    JOB-TOKEN header / job_token parameter), so reading it would turn a working anonymous fetch of
    a public project into a 401/403 inside CI; pass it explicitly with auth_token(..) if you want
    it. The variable set does not change with api_base_url / host; gh reads
    GH_ENTERPRISE_TOKEN / GITHUB_ENTERPRISE_TOKEN for a GitHub Enterprise host and this crate does
    not, 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, from
    either auth_token(..) or auth_token_from_env(). Reports presence only -- never validity, and
    never 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 now
    distinguished 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's RateLimit-Remaining) or a usable Retry-After (GitHub's
    secondary rate limit, which answers 403 + Retry-After while the primary quota is still
    nonzero). A 403 with neither stays Error::Unauthorized. Both server-supplied wait values are
    clamped to a 24h ceiling and resolve to None beyond it. http_status() and url() cover the new
    variant, and Error::http_status_error_with_headers(status, url, &HeaderMap) gives a custom
    HttpClient the same classification (the header-blind Error::http_status_error cannot see the
    quota 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 a RateLimited
    request, measured from now. Prefers the server's Retry-After and otherwise derives the wait from
    reset_at minus the current time; None when 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-reset is
    sent, so reading the raw fields and calling retry_after.unwrap_or_default() sleeps zero and burns
    more quota.

  • Directory-bundle installs (macOS .app): bundle_path_in_archive(..) names the bundle directory
    inside the release archive and selects bundle mode, where the whole tree replaces
    bundle_install_path(..) instead of one file replacing bin_install_path. The new bundle is
    staged 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 macOS
    bundle_install_path defaults to the nearest .app ancestor of the running executable. The
    verify_binary hook receives the staged bundle root, and the opt-in
    check_install_path_writable preflight probes the bundle's parent directory. Adds
    Error::NoAppBundle (no .app ancestor to derive the path from), Error::ConflictingConfig
    (bundle mode combined with an explicit bin_install_path / bin_path_in_archive), and
    Error::AppTranslocated (a quarantined app running from a read-only translocated mount). A
    symlinked bundle_install_path is resolved first, so the tree behind the link is replaced and the
    link survives; bundle_install_path without bundle_path_in_archive is a MissingField error
    rather than a silently discarded path.
    (#​145)

  • compression-tar-xz feature: decode .tar.xz / .txz archives and plain .xz single-file
    assets (pure-Rust lzma-rs, no C liblzma dependency, so it cross-compiles like the rest of the
    default stack). Opt-in, mirroring compression-tar-gz. Adds Compression::Xz.
    (#​143)

  • self_update::verify_signature(archive_path, keys): run the same embedded-signature check
    update() performs, standalone, for a caller that stages a download itself (e.g. an installer
    fetching a companion binary before the update loop exists). Takes impl AsRef<Path> and a
    &[VerifyingKey] slice; any-of key semantics, .tar.gz / .zip only (signatures feature).
    (#​150)

  • native-tls-vendored feature: build OpenSSL from source and link it statically, for targets
    where a usable system OpenSSL is awkward (musl, some cross-compiles). Implies native-tls;
    applies to the reqwest client. (#​108)

  • UpdateStrategy and the update_strategy(..) builder setter: control which release the unpinned
    "latest" path installs when several are newer. Compatible (default) prefers the newest
    semver-compatible release, falling back to the newest overall; Latest always selects the newest
    release, even across a major bump. (#​152)

  • Release::release_notes_url() and ReleaseBuilder::release_notes_url(..): the release page URL,
    filled by the github/gitlab/gitea backends from the release's html_url (_links.self for
    gitlab); None for s3. The show_release_notes(bool) builder setter shows it (or the release
    body when no URL is available) in the confirmation prompt.
    (#​148)

  • tag_prefix(..) on the github/gitlab/gitea Update builders: derive the version from a
    monorepo-style tag such as myapp-1.2.3 (or myapp-v1.2.3). Defaults to unset, which trims a
    leading v as before; when set, tags without the prefix are skipped from the listing rather than
    mis-parsed. (#​76)

  • asset_key_pattern(..) on the s3 Update/ReleaseList builders: a custom regex for deriving
    (name, version) from object keys, replacing the built-in matcher whose version group only
    captures a major.minor.patch triple. Lets a pre-release key such as
    mybin-0.1.2-beta-x86_64-unknown-linux-gnu parse as 0.1.2-beta instead of 0.1.2. The
    pattern must define name and version named capture groups and is validated at build()
    (Error::InvalidAssetKeyPattern); a captured version that does not parse as semver skips the
    key. Unset keeps the existing matcher unchanged.
    (#​61)

  • self_update::restart module: restart() and restart_with(args) relaunch the (already
    replaced) 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 fresh
    argument list (e.g. to drop an --upgrade flag 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 the
    current process exits. No feature gate, no new dependencies.
    (#​62)

  • self_update::check_interval::UpdateCheckGuard: a small stamp-file guard that throttles how often
    an application checks for updates. should_check() reports whether the configured interval has
    elapsed 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 reader
    never 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 that
    the 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 with rate_limit_delay() (with a worked example), that the retry
    loop 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 at
    Error::http_status_error_with_headers, since the header-blind Error::http_status_error can
    never report a rate limit. (#​78)

  • Docs: the Features section now names the exact no HTTP client selected compile error a
    client-less build (e.g. default-features = false, features = ["rustls"]) produces, and shows the
    fix (add a client, e.g. features = ["ureq", "rustls", "github"]).
    (#​168)

  • check_install_path_writable(bool) builder setter (default false) and
    Error::InstallPathNotWritable { path: PathBuf }: opt-in preflight that probes bin_install_path
    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)

  • 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)

  • gitee backend: backends::gitee::ReleaseList, Update, and AsyncUpdate for Gitee releases,
    mirroring the gitea backend. Default host https://gitee.com with an optional .host() setter
    for enterprise instances. Bearer-token auth via auth_token. Nameless source-archive assets are
    skipped with a debug log rather than erroring.
    (#​121)

Changed
  • A rate-limited response no longer returns Error::Unauthorized. A 429, a 403 whose headers
    report a spent quota (x-ratelimit-remaining: 0 / gitlab's RateLimit-Remaining: 0), or a 403
    carrying a usable Retry-After now returns Error::RateLimited instead. Error is
    #[non_exhaustive], so this does not break compilation: code that matched
    Unauthorized { status: 403, .. } to detect rate limiting (which the previous docs told users to
    write) keeps compiling and silently stops matching, falling through to the wildcard arm. Migration:
    match Error::RateLimited { .. } instead, and take the wait from rate_limit_delay() rather than
    the fields.

    // before
    Err(Error::Unauthorized { status: 403, .. }) => back_off(),
    // after (write the variant with a trailing `..`; both wait fields are `Option`s)
    Err(err @ Error::RateLimited { .. }) => match err.rate_limit_delay() {
        Some(wait) => std::thread::sleep(wait),
        None => reschedule(),
    },

    A bare 403 with no quota signal is still Error::Unauthorized, so a genuine credential failure is
    unchanged. (#​78)

  • retry / retry_async no longer retry a rate-limited request. An Error::RateLimited returns
    immediately regardless of the configured retries, instead of spending the budget on a quota that
    is 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 / ReleaseList builder's Debug output redacts the authorization token,
    rendering it as "<token>" instead of the value, so logging a builder no longer prints an ambient
    CI credential. All other fields are still shown.

  • A credential passed via request_header("Authorization", ..) (or PRIVATE-TOKEN, Cookie, or
    any header name ending in -token, case-insensitive) is now marked sensitive, so it is redacted
    in a builder's Debug output and kept out of the underlying HTTP client's own header logging, the
    same as a token set with auth_token(..). Previously only the auth_token slot was redacted, so
    a credential passed as a header printed verbatim.

  • build() logs a log::warn! when a token resolved from the environment would be sent to a host
    other than the backend's canonical one (api.github.com, gitlab.com, gitee.com). The
    environment variables are conventions of the backend's own service, so an application that exposes
    its update url as configuration would otherwise hand GITHUB_TOKEN to an arbitrary host with no
    signal. 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 with auth_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_host no longer produces that warning.

  • A blank (empty or all-whitespace) auth_token(..) is now treated as unset: it no longer blocks the
    auth_token_from_env() fallback, no longer sends an empty Authorization header, and
    has_auth_token() reports false for it.

  • Error::http_status_error(429, url) returns Error::RateLimited (with both wait fields None)
    instead of Error::HttpStatus, so a custom HttpClient that has no headers to hand over still
    reports 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_headers to get the full classification.

  • A Retry-After: 0 is no longer treated as a rate-limit signal. A bare 403 carrying a zero
    Retry-After and no quota header stays Error::Unauthorized instead of becoming a zero-wait
    Error::RateLimited.

  • An injected ureq::Agent classifies a non-2xx response exactly like the crate-built agents. The
    agent keeps ureq's default http_status_as_error(true), whose StatusCode error carries no
    headers, 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 / HttpStatus mapping. Nothing else
    about 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 / .xz asset without
    the compression-tar-xz feature returns Error::CompressionNotEnabled("xz") (matching the
    existing .gz handling), rather than writing the compressed archive to the install path.
    (#​143)

  • Install-step IO failures now name the install path: PermissionDenied becomes
    Error::InstallPathNotWritable { path } and any other IO error becomes Error::Io with the
    install path embedded in the message (the ErrorKind is preserved).
    (#​112)

Fixed
  • Zip extraction (Extract::extract_into) now restores symlink entries as real symlinks on unix
    instead 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 .app bundle whose
    Frameworks/*/Versions/Current links are load-bearing for the code signature), so a signed app
    extracted 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 defense
    on 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 by d/sl/evil -> ../../x, lexically in-bounds but physically above the
    root) 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_file now errors on a symlink entry
    rather than writing its target string out as the requested file.
  • The update confirmation block printed the install path with {:?}, and Path's Debug impl
    quotes 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. The Current exe and
    Current bundle lines now print through Path::display(), so the path appears exactly as the
    platform writes it. The New exe release / New exe download url lines are strings rather than
    paths and keep their existing quoted form.
    (#​201)
  • A server-supplied asset name containing a control character is now rejected as
    Error::InvalidAssetName alongside the existing empty / . / .. / separator / absolute-path
    cases. The name is remote-controlled and is echoed into the confirmation block, so a \r or an
    ESC sequence in it could repaint or hide the lines (including the download url) that the user
    reads before authorizing the replacement.
  • In bundle mode, a symlinked bundle_install_path is resolved once, before the confirmation
    prompt, and that single resolved path is what the status block names, what the
    check_install_path_writable preflight probes, and what the swap replaces. It was resolved again
    after the prompt, so repointing the link in between would have redirected the replacement to a
    tree the user never approved.
  • Rollback failures log paths through Path::display() rather than {:?}, so a windows path in
    those messages keeps single separators. Diagnostic debug! logs keep {:?}, which renders a
    non-UTF-8 path unambiguously.

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 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.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate
renovate Bot force-pushed the renovate/self_update-1.x branch 6 times, most recently from dc809fd to 72363f1 Compare September 9, 2026 15:58
@RouHim
RouHim force-pushed the renovate/self_update-1.x branch from 72363f1 to db8ec2e Compare September 9, 2026 16:03
@renovate

renovate Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Edited/Blocked Notification

Renovate 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.

⚠️ Warning: custom changes will be lost.

@RouHim
RouHim merged commit 0a1ff5d into main Sep 9, 2026
11 checks passed
@RouHim

RouHim commented Sep 9, 2026

Copy link
Copy Markdown
Owner

🎉 This PR is included in version 1.20.30 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@RouHim RouHim added the released label Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant