- The uninstall-record fallback read the deduplicated install list, so a copy
registered per-user and per-machine reported only the machine record and the
update went ahead. It now reads every record of the product code and gives no
scope when they disagree, so the update stops as the probe path does.
- The launcher policy check returned before looking at InstallExecuteSequence.
It now requires CraftRestoreLauncherFolder after AppSearch and before
CostInitialize, so silent updates cannot lose the folder.
- Launcher self-detection also looks in ArtCraft\Launcher (the folder the MSI
uses) and reads its folder from the publisher-named registry key.
- README notes that a custom folder the account cannot write installs for
everyone, with an administrator prompt.
Two review findings on the MSI update path.
- The update context came from a PowerShell and Windows Installer COM probe,
and any failure to run it blocked the update with the real error dropped
(Constrained Language Mode, AppLocker or WDAC, a slow first start). The error
is now logged. When the probe cannot run at all, the context comes from the
uninstall record the copy was found by (HKLM is per-machine, the account's
own HKCU is per-user). A probe that ran and gave no answer, or a copy
registered for both contexts, still stops the update.
- A fresh install into a custom folder under Program Files passed no scope, so a
package that defaults to per-user could not write there. A fresh install into
a folder this account cannot write now asks for the per-machine context, and
Windows prompts for permission.
Look for the executable in the recorded folder first, then in App Paths and the usual folders of the
record's own hive and registry view, so a per-machine entry never stands in for a per-user record.
Craft app MSIs are becoming dual-scope packages that default to a per-user
install. An update must land in the same Windows Installer context as the
copy it replaces: a per-machine copy needs elevation and writes HKLM
registrations, and a per-user one must not silently duplicate itself
per-machine (or the other way round). The context is read from the product
code's registration and passed to msiexec (ALLUSERS=1 for per-machine, the
documented ALLUSERS=2 + MSIINSTALLPERUSER=1 for per-user). A fresh install
passes nothing and takes the package's own default, so the current
per-machine-only packages keep behaving exactly as before.
Detection now also reads the per-user places a dual-scope package records
itself: the HKCU InstallDir, App Paths under HKCU, and the user's own
Programs folder.
Picks up the update prompt fix that draws its close mark and version pill
without glyphs a font may lack, so the prompt no longer takes the app down
when a bundled face has no ✕ or →.
Part of tracking issue storytold/craft-libs#319🤖 Generated with Codebuff
Co-authored-by: Codebuff <noreply@codebuff.com>
* refactor: delegate self_update to craft-update
* ci: point craft-update patch to dylanmtaylor/craft-libs branch
* nix: set allowBuiltinFetchGit for git dependencies
* chore(deps): update craft-update commit
* build: pin craft-update to storytold/craft-libs fe61243 and drop the temporary patch
* fix(update): share craft_update::Stop so pause and cancel reach the self-update
* build: pin craft-update to the dirs-free, zip 9 build
Satisfies cargo-deny: no MPL-2.0 option-ext, no zip 8 advisories, and a versioned git dependency instead of a wildcard.
* build: pin craft-update to craft-libs main (b9ba47f)
* Migrate Launcher to egui 0.37 and preserve current-main behavior
* egui037: record final published pins and local qualification
* fix(ui): gate CJK language drawing until fonts are installed
* Document migrated launcher UI contracts and regression invariants
The 0.8.0 release's macOS job failed with 'The validate action failed! Error 68' (NSURLErrorDomain -1005 from api.apple-cloudkit.com) after both notarizations were Accepted. Same helper as VectorCraft's packaging/macos/package.sh (craftrules learnings/ci-and-release.md).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Redact Bearer tokens with multiple spaces and add regression test.
- Explicitly disable proxies on direct and direct_download ureq agents.
- Add direct_download agent with download timeout profile for loopback downloads.
- Preserve proxy username, password, and NO_PROXY exclusions in proxy::pick().
- Uses entry.size() rather than header.size() to account for PAX size headers in cumulative unpack limits.
- Defers directory extraction until child entries are written to avoid restrictive directory modes preventing child file creation.
- Returns error when tree file enumeration exceeds limits or fails to read directory.
- Supports both /usr/local and ~/.local install prefixes based on needs_root.
- Validates constructed fallback release URLs with safe_release_html_url to prevent command injection from malformed tags.
- Rejects empty, '.' and '..' path components in RawApp::into_app and RawRepoRename::into_rename.
Resolves F-019 (CWE-400), F-020 (CWE-117), and F-021 (CWE-117).
- Enforces per-byte read deadlines and socket timeouts during header parsing.
- Bounds maximum line length (8 KiB) and maximum total headers size (64 KiB) to prevent slowloris/DoS.
- Sanitizes request method and target before logging to stderr to prevent ANSI escape sequence injection.
Resolves F-016 (CWE-522) and F-017 (CWE-287).
- Replaces cleartext transmission of persistent bearer token with ephemeral mutual challenge-response.
- Client initiates handshake with ephemeral client nonce without disclosing token.
- Server proves possession of per-user token via SHA-256 HMAC-style proof before client discloses client proof.
- Uses constant-time equality check to prevent timing leaks.
- Replayed or stale auth proofs across handshakes are rejected via fresh server nonces.
with_no_window_it_sleeps_until_woken read Instant::now() after spawning
the thread whose 200 ms pause it measures, so a runner that held the
test thread back between the two failed started.elapsed() >= pause
(seen on main's ubuntu CI). Read the clock first.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
reads_binary_version_when_the_cli_uses_the_display_name_or_a_former_id
failed now and then: another test thread that forks while the script is
still open for writing holds that descriptor until it execs, and running
the script in that window fails with ETXTBSY. Both binary_version tests
now retry a miss for up to a second.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#720 lists the languages by their own name, so 简体中文 is now the last
row of the language picker, below the fold of the popup's scroll area.
The pointer click that kittest's click() simulates landed outside the
visible part, nothing was chosen, and the Chinese test failed on main on
both Linux (no font: language stayed "en") and Windows. Clicking via
AccessKit doesn't need the row on screen; the picker itself is fine,
people scroll it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The close button parks the launcher in the tray by design, but to
someone who doesn't know that, a window that vanishes with nothing to
show for it looks hung, and the reports end with killing the process
(#45, #215).
The first time the window closes to the tray, a system notification
says the launcher is still running, that it's in the tray, and that
Settings can turn this off. `LauncherUi::hidden_to_tray` sends it
through the host and records it in `Settings::tray_hint_shown`, so it
happens once per install. The desktop shell calls it from the
close-request path only, for both ways of going to the tray; a window
hidden from the tray icon, or started hidden at login, gets no hint.
Two strings, with rows in all ten complete catalogs. A UI test checks
the hint is sent once and recorded.
Co-authored-by: YangXZ2008 <141027290+YangXZ2008@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>