Commit Graph
340 Commits
Author SHA1 Message Date
JeremyandClaude Opus 5.5 73a824c4dc feat(versions): go back to an older version of a launcher-installed AppImage
An AppImage the launcher installed is renamed into place whole, and the old file is removed
only after that, the same as an update. Going back to a version chosen by name now takes
that route too (Downgrade::KeepOldCopy), with no older package fetched first. It needs the
launcher's own record of an AppImage: a user's own AppImage, an unrecorded one and the
tarball install (whose bin and share swap one at a time) are still refused.

Not run on a real Linux desktop; the linux module only compiles there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:01:23 -04:00
VoiceLessQ 014422ad15 fix(packaging): declare the launcher's real window class in its .desktop file (#1026)
The window sets its app id (Wayland) and WM_CLASS (X11) to
ai.storyteller.artcraft-launcher, but the entry said
StartupWMClass=artcraft-launcher. Desktops that find the entry by its
file name were fine; an entry under another name (AppImageLauncher,
Gear Lever, a hand-made one) never matched, so the dock showed a
generic icon and the launcher could not be pinned.
2026-10-30 22:00:18 -04:00
VoiceLessQ d95ae4b5be fix(install): name the code in the pkexec 127 error
127 also covers other pkexec errors and a wrapped installer exiting 127, so
say which code came back and list the likely causes instead of naming one.
2026-10-30 21:58:07 -04:00
VoiceLessQ cbc6fe60d9 fix(install): stop reading pkexec exit 127 as a cancel (#737)
pkexec exits 126 when the prompt is dismissed. 127 is its default code and
also covers not authorized and no authentication agent, so a desktop with no
polkit agent ended an install as a quiet "Install cancelled" with no prompt.
2026-10-30 21:58:07 -04:00
VoiceLessQ 361e3e82f4 fix(http): read long release lists up to a 16 MiB cap (#1021)
get_api shared the 4 MiB limit for small answers, and SolidCraft's list of 67
releases is 5.4 to 6.5 MB. The request failed every time, so Choose version
showed an error and no versions. Other small requests keep 4 MiB.
2026-10-30 21:57:43 -04:00
VoiceLessQ e1ada85d02 test(ui): compare the table date with its whole width
The check for more than 110 px passed at any column from 111 to 129 px, where
the date is still cut. Lay out the same text and require its full width.
2026-10-30 21:55:09 -04:00
VoiceLessQ c911793006 fix(ui): widen the table's released column so dates show whole (#1038, #754)
Dates ran to 130 px in Brazilian Portuguese and 108 px in Chinese and
Japanese, past the 104 px column, and were cut with an ellipsis. The
column is now 132 px; Author gives up 16 px ("Tangency Labs" still fits)
and the app column 12 px.
2026-10-30 21:55:09 -04:00
Dylan M. Taylor effb212ae5 Merge pull request #1053 from Jeremy8776/feat/version-pins
feat(versions): apps respect the version the launcher keeps them on
2026-10-30 20:55:24 -04:00
skylar taylor-barrick 0c8f9c08f8 Merge pull request #1058 from benandez/fix/solidcraft-macos-team
fix(macos): install SolidCraft, checked against its own developer's signature
2026-10-30 17:31:56 -06:00
skylar fc6708641f fix(macos): reject missing teams outside test builds 2026-10-30 17:19:24 -06:00
skylar 2f5a86aa8d fix(macos): verify staged app trust before replacement 2026-10-30 16:49:35 -06:00
JeremyandClaude Opus 5.5 2738185596 feat(versions): the shared version folder exists before any app starts
Flatpak mounts an app's read-only grant for ArtCraft/versions only when the
folder exists as the app starts. The launcher now makes it on start, and its
Flatpak grants xdg-data/ArtCraft/versions with :create, which also covers an
XDG_DATA_HOME outside home, where --filesystem=home does not reach.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:08:38 +00:00
JeremyandClaude Opus 5.5 b1d557a173 build(deps): craft-update with the version-choice file in a host folder
craft-update 9817c81 moves versions.json to ArtCraft/versions under the host's
per-user data folder, resolved from inside the Flatpak too, so a Flatpak
launcher writes where host apps and granted Flatpak apps read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 21:56:42 +00:00
Martin BallandClaude Opus 5.5 627598eb7d fix(macos): install SolidCraft, checked against its own developer's signature
SolidCraft's macOS build has been Developer ID signed and notarized since 0.27 (its README),
but the launcher still treated it as unsigned (`installs_on_mac: false`), so on a Mac it
offered the update and then said no macOS version was available.

`installs_on_mac` becomes `mac_team`: the Apple team whose signature the build must carry.
Official apps keep ArtCraft's (DJ6XS33FX8); SolidCraft gets its own (G5QA5U5X2S, read from
the signature on its 0.27.1 macOS CLI). `None` still means the launcher installs no DMG, and
an install that reaches the DMG path without a team is now refused, not left unchecked.

Not run on a real Mac: the macos module only compiles there, and the DMG's own signature
still needs `codesign -dv` on a Mac to confirm the team matches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 15:37:08 +00:00
JeremyandClaude Opus 5.5 1167239dfe feat(versions): every app the launcher opens is told where the version choices are
Only tarball apps on Linux were given CRAFT_VERSIONS_FILE. Now every launch
route passes it: Windows (exe and MSI) and AppImages through the process
environment, Flatpak apps through `flatpak run --env=`, and anything the
Flatpak launcher starts on the host through `flatpak-spawn --env=`. macOS
apps open through Launch Services with the real home, so they find the file
at its default path; `open` is left unchanged since not every macOS version
takes an environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:00:55 +00:00
JeremyandClaude Opus 5.5 54bbc2ab9e feat(versions): tell the apps which version the launcher keeps them on
A version chosen in the launcher lived only in its own settings, so an app's
own update check (craft-update's Updater) still offered the latest release on
every start, and accepting it undid the downgrade.

The backend now writes craft_update::pins' shared file on every Configure:
each app's track from the settings, stable ones removed, entries a newer
launcher wrote kept. The first Configure after an upgrade moves choices saved
earlier. A tarball app on Linux, whose data folder points into its own home,
is told the real file through CRAFT_VERSIONS_FILE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:00:54 +00:00
JeremyandClaude Opus 5.5 b43955db51 build(deps): craft-update with the shared version-choice file
Pins craft-update to storytold/craft-libs#449 (0ada678) for craft_update::pins.
Bump to the merged commit once that PR lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:00:54 +00:00
Dylan M. Taylor 3fad1d643f deps: bump tar, zip, roxmltree, rustls-webpki and symphonia to their latest versions (#1036)
- `rustls-webpki`: 0.103.15 → 0.103.16
- `zip`: 8.6.0, 9.1.0 → 9.1.3

Requirement `zip` 8 → 9. The portable-ZIP check now treats an entry name that doesn't decode as unsafe, like one with `\\` or `:`. That code is Windows-only and wasn't compiled here (Linux); Windows CI is the check.
2026-10-29 22:21:27 -04:00
aiongg 8ffceca5f0 Merge pull request #1015 from Jeremy8776/feat/macos-downgrade
feat(versions): go back to an older version on macOS
2026-10-29 16:46:33 -04:00
JeremyandClaude Opus 5.5 e60a042b48 feat(versions): go back to an older version of a macOS app, keeping the old copy aside
A Mac update already moves the installed .app aside, renames the new bundle in and moves
the old one back if that fails. Going back to a version chosen by name now uses the same
swap for a DMG over a Mac app install, so no older package has to be found and fetched
first. Downgrade becomes an enum: Reinstall (the Windows Installer route, unchanged) and
KeepOldCopy (macOS). Each is let through only for the install it can undo; anything else
is still refused before anything is downloaded.

Not run on a real Mac: the macos module only compiles there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 20:28:23 +00:00
skylar taylor-barrick c1d0d881a3 Release 0.10.0 v0.10.0 2026-10-29 13:26:57 -06:00
skylar taylor-barrick 570e101341 Add version selection and Windows downgrade recovery (#1007) 2026-10-29 13:25:43 -06:00
skylar taylor-barrick 73cd75f310 Fix RISC-V glibc requirements in Linux packages (#1010) 2026-10-29 13:25:39 -06:00
JeremyandClaude Opus 5.5 e77c72aff5 fix(versions): no separate-prompt fallback for an all-users downgrade; one queue turn per downgrade
Two review findings, both ways for the 'app left uninstalled' trap to come back:
- If the one-prompt elevated session could not start, an all-users downgrade fell back to a
  prompt per step. It now stops before anything is removed and says nothing was changed.
- The step-by-step route (per-user copies) took a separate installer-queue turn per step, so
  another queued install, or a cancel while waiting, could stop the put-back after the copy
  was removed. Remove, install and put-back now run in one turn, with nothing to wait on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:25:48 +00:00
skylar taylor-barrick 8165feb8e0 test(packaging): explain metadata assertion failures 2026-10-29 12:24:50 -06:00
skylar taylor-barrick 323c8760a3 fix(packaging): declare the build target glibc floor 2026-10-29 12:22:41 -06:00
JeremyandClaude Opus 5.5 a7491e9d15 fix(versions): put the previous version back when removing a second registration fails
An app can be registered more than once (per-user and all-users). If removing one succeeded
and the next failed, both downgrade routes stopped there, said nothing had changed, and never
put the previous version back. Now a removal that fails after another succeeded reinstalls
the previous version, in the elevated script and the step-by-step route, and says so with
outcomes of its own. A first removal that fails still changes nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:13:10 +00:00
JeremyandClaude Opus 5.5 45e2b6ec7e feat(versions): a quiet note when a kept version is behind; colour only the new version in an update
A version kept on purpose shows 'v0.16.0 · latest 0.17.0' in its card strip and the table,
with the note in plain grey: it informs without counting as an update (no badge, Update
all or notification). The published list for each kept app is fetched once in the
background, through the same request and cache as the version menu.

When an update is waiting, only the new version takes the app's colour ('0.16.0 →' stays
plain). The table cell stays one label in two colours, so it still reads as one piece.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:46 +00:00
JeremyandClaude Opus 5.5 46cffe6527 fix(versions): one permission prompt for a whole Windows downgrade
A real run showed three separate prompts are fragile: they look identical, so a refused
install was followed by a refused put-back (read as a retry), leaving FilmCraft uninstalled.

For an all-users copy, removal, install and put-back now run in one elevated session, as
Update all's batch does, with both checked packages held unchanged meanwhile. Saying no
changes nothing; once it is yes, the put-back needs no second answer. The session's exit
code says which step stopped it. Product codes must have the exact {GUID} shape to reach
it. Per-user copies need no permission and keep the step-by-step route, which is also the
fallback when the elevated session cannot start.

The generated script is tested for real with cmd.exe standing in for msiexec. Those tests
take a lock: in parallel the stand-in sometimes never started (out-of-range code, no
marker), which the script rightly treated as a failed step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:46 +00:00
JeremyandClaude Opus 5.5 ab9aad6ca7 feat(versions): go back to an older Windows Installer version, putting the newer one back on failure
The app packages refuse to install over a newer copy, and the launcher's own guard refused
every downgrade. A version chosen by name below the installed one now:
1. finds the installed version's published release and downloads and checks both packages
   before anything changes (without them, nothing is removed);
2. removes the installed copy;
3. installs the chosen version in the same folder and context, and reinstalls the previous
   version if that fails or permission is refused.

A feed still can't send an app back. Other kinds of install refuse a downgrade plainly.
ReleaseSource::chosen's default now answers pins and nightly from versions(), so a pinned
app's check reports its pinned release and a newer one is never offered as an update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:46 +00:00
JeremyandClaude Opus 5.5 5840c5e367 feat(versions): a version chosen in the dialog is a draft until Install
Closing the Choose version dialog without installing (Close, Escape or a click outside)
puts back the choice it had when it opened and checks again, so the card returns to Open.
Install from the dialog keeps the new choice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:45 +00:00
JeremyandClaude Opus 5.5 5803c0f304 test(versions): closing the dialog keeps the chosen version on the card
Lifts the version tests' fake backend into a helper (version_menu) and covers picking an
older release, pressing Close, and finding Install version on the card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:45 +00:00
JeremyandClaude Opus 5.5 270a6cac26 feat(versions): label the newest release in place instead of a separate Latest release row
The newest stable release that installs here reads 'v0.14.0 (latest release)', and choosing
it follows new releases as the old row did. The plain 'Latest release' row only shows while
the list is loading or unavailable, so that choice is never out of reach.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:45 +00:00
JeremyandClaude Opus 5.5 36818cc387 style(versions): version rows touch so the selected highlight fills its slot
With a gap between rows, only the selected row drew a box, covering 42 of its 52 px slot,
so the space around it looked uneven. Rows keep their 42 px height.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:45 +00:00
JeremyandClaude Opus 5.5 c9c0b5c35a style(versions): drop the divider between Nightly and the releases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:45 +00:00
JeremyandClaude Opus 5.5 e78145cb7c fix(versions): show a missing nightly inline as 'Nightly (none published yet)'
The disabled Nightly row carries the reason in brackets instead of a separate grey line
under it, so the menu no longer opens with a large block before the releases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:44 +00:00
JeremyandClaude Opus 5.5 81d6aec2dd feat(versions): install the chosen version from the Choose version dialog
Choosing a version keeps the dialog open. An Install version {tag} button sits at its
bottom right, enabled once that exact release has been checked (Checking... while a
nightly is looked up, Installed when it is the installed copy). Install from the dialog
may open the folder dialog in its place, which dialogs() now keeps instead of
restoring the old one. The help line keeps only the files warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:44 +00:00
JeremyandClaude Opus 5.5 41ca7ef5bb feat(versions): choose an app or launcher version: latest, nightly or an exact release
Each app's menu gets "Choose version", and Settings > Updates gets a "Launcher version"
dropdown. Choosing a version closes the dialog, re-checks that exact release, and turns the
card's main action into "Install version {tag}" until that copy is installed.

- Nightly means an explicit -nightly prerelease tag with a published checksum, never any beta.
- A pinned tag never falls back to another release, and an install re-checks the track, so a
  stale stable result cannot install as nightly.
- A release asset must sit under its own repository, tag and file name.
- Older launcher versions are a manual download beside the pin; self-update only moves forward.
- The version list is the newest 100 releases from GitHub (one page), cached for 15 minutes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:43 +00:00
Dylan M. Taylor 032a1224d3 Show PdfCraft in red (#975)
PdfCraft's app colour changed from green to red (storytold/pdfcraft#5245). The catalog
colours become #e8204f (app), #b70228 (light ink) and #f23a62 (dark ink), matching the
website, and assets/app-icons/pdfcraft.png is the new 128 px icon, copied unmodified.
The status-green comment no longer names PdfCraft as its source; the value is unchanged.
2026-10-29 12:13:44 -04:00
Dylan M. Taylor 9fabc13be2 Focus app descriptions on features and workflows (#1000) 2026-10-29 12:10:04 -04:00
Jeremy b827fed74a feat(install): one permission prompt for an Update all of all-users apps on Windows (#974)
* feat(install): one permission prompt for an Update all of all-users apps

Updates of apps installed for all users wait until each has downloaded and
checked its package, then run in one elevated PowerShell session: one UAC
prompt for the batch. A batch of one keeps the direct msiexec route. If
PowerShell cannot start, each package runs with its own prompt.

* fix(review): keep each checked package unchangeable until its batch runs; fall back when the batch is too long

The batch's permission prompt names PowerShell, not the package's publisher, so a
package swapped in the download folder between its check and the elevated
msiexec would go unnoticed (CWE-367). Each all-users package is now opened
sharing only reading, checked again through that handle against the checksum it
was downloaded against, and held until its call ends.

A batch whose script is too long for one command line now updates each app with
its own prompt instead of failing every job.

* fix(review): a shared-run failure says "in one go", not "together"

The fallback runs each package by itself but still gives the run one result, so
"handling 3 apps together" was wrong there. The marker the backend uses to check
what landed is unchanged in role; only its wording.

* fix(review): a failed launch of the elevated session falls back instead of cancelling

The outer script read every Start-Process failure as a declined prompt, so a
session that could not start cancelled the whole batch. A declined prompt
carries the Windows error 1223 in its exception chain; only that is a cancel.
Anything else exits with a code no msiexec run returns, and the launcher then
updates each app with its own prompt.
2026-10-29 00:05:28 -04:00
Jeremy 67aefb33c0 Install new Windows apps for everyone in Program Files\ArtCraft, and show that folder as the default (#972)
* fix(installer): install new Windows apps for everyone, in Program Files\ArtCraft\<App>

A fresh MSI install now asks Windows Installer for the machine-wide context
(ALLUSERS=1) instead of taking the package's own default, which for a
dual-scope package is per-user (%LOCALAPPDATA%). Updates are unchanged: they
keep the context of the copy they replace. README row restored to Program Files.

* fix(ui): show the real default install folder, not just "System default"

The install-folder fields (the per-app Install dialog and Settings'
Default install folder) now show the folder a fresh install really
uses: Program Files\ArtCraft for a Windows MSI (machine-wide, from
the ALLUSERS=1 scope), the launcher's own apps folder for a portable
ZIP, /Applications on macOS. The host answers through the new
Host::default_install_folder, computed by native install::default_folder;
where no single default exists (web build, Linux system packages,
FreeBSD) the field keeps saying "System default". The same value is
the start folder of Browse, so it no longer starts at a literal
"System default" string.

* fix(review): the default folder follows the package and the installer

CodeRabbit review of #972: the Windows default now follows the selected
package's architecture (an x86 MSI picked because a release ships no
x64 one installs under Program Files (x86)), and answers nothing when
the architecture is unknown, so the field says "System default"
rather than guessing. The macOS default comes from the installer's own
applications_dir, so a machine where /Applications is not writable
shows ~/Applications. Browse now starts at the nearest folder that
exists, so a default created only by the first install
(Program Files\ArtCraft) no longer falls back to the picker's
unrelated start location. The desktop host memoises its answer because
the macOS path probes the disk and the fields redraw often.

* fix(review): keep the Linux and macOS builds of default_folder warning-free

The ubuntu Clippy gate caught what the dev machine cannot check (ring
needs a Linux C compiler): the Arch import is Windows-only, the macOS
arm consumes its unused parameters, and the Linux test calls the
three-argument signature.

* fix(ci): import Arch on every target, default_folder's signature uses it everywhere

0f5bd68 gated the import to Windows but the signature names Arch on all
targets, so the Linux clippy job failed with E0425.

* fix(review): the folder hint matches the real install and touches nothing

CodeRabbit review of #972.

- The install dialog's default follows the package the backend will really
  install: the preferred formats first, then every other format this
  platform installs, as choose_kinds does. A Portable ZIP preference with a
  release that ships only an MSI now shows Program Files, not the apps
  folder. The choice lives in the new install_target module.
- Settings covers every app, so it shows a folder only when every app that
  can be installed shares one; a 32-bit-only app beside a 64-bit one leaves
  it at "System default" instead of using the launcher's architecture.
- The macOS hint no longer borrows the installer's probe, which wrote a
  fixed-name file in /Applications (following a planted link) and created
  ~/Applications when it fell back. Both now use write_probe (fresh name,
  create_new), and only the installer creates ~/Applications.
- The desktop host caches each question, since Settings now asks per app.
2026-10-28 21:50:54 -04:00
Dylan M. Taylor 46761082e6 feat: add SolidCraft as a community app (Official / Community badges) (#963)
* feat: add SolidCraft as a community app, with Official and Community badges

Add AppKind (Official | Community) to the catalog and a bundle id override, since
SolidCraft's Flatpak and desktop id is io.tangencylabs.solidcraft. The grid card shows
the kind as a badge, and the table has a Type column. Strings are translated in the
seven complete catalogs.

Community apps are not under storytold/. AGENTS.md reserves that decision for a
maintainer; this PR needs that approval before it merges.

* details: add the SolidCraft page and fix its attribution licence

The page has no screenshots yet: the pictures repository has none for SolidCraft.
ATTRIBUTION.md now records the MIT OR Apache-2.0 licence that SolidCraft's README gives.

* fix: pass fmt, clippy, tests and wasm for the SolidCraft change

- Show each app's publisher as a lowercase byline ("by ArtCraft", "by Tangency Labs")
  in place of the Official and Community words, with a publisher field on CraftApp.
- Translate the byline, the By column and the SolidCraft tagline in every complete catalog.
- Format the catalog and details tables with rustfmt, and fix two needless borrows.

* fix: address review comments; Author column and shorter SolidCraft tagline

- Detect SolidCraft's MSI by its own publisher ("SolidCraft contributors"), recorded per
  app as CraftApp::msi_publisher.
- Look up missing Flatpak versions for every catalog bundle id, not only ai.storyteller.*.
- Keep SolidCraft's MIT and Apache-2.0 license texts next to its icon.
- Table: an Author column (plain text) after Released, before the buttons.
- SolidCraft's tagline is "Parametric 3D CAD": the card's byline names Tangency Labs.

* detect: find SolidCraft's Flatpak under its older ids

SolidCraft's Flatpak shipped as ai.storyteller.solvecraft, then ai.storyteller.solidcraft,
before io.tangencylabs.solidcraft. Add CraftApp::former_bundles and bundle_ids() (current
first), match them in the Flatpak list and the version lookup, and open and remove a
Flatpak under the id it was found under.

* fix: address CodeRabbit review (removal, Flatpak migration, macOS, website)

- Remove a deb/rpm/pacman install by the package name detection found, not the app id.
- After a Flatpak update from one of an app's older ids, uninstall the old copy (keeping
  its ~/.var/app data, which SolidCraft's first launch copies its settings from), so it
  doesn't reappear once the new one is removed.
- macOS: don't install SolidCraft from its macOS build, which carries no Developer ID
  signature to check (CraftApp::installs_on_mac); the Developer ID check for official apps
  is unchanged. A copy that's there is still detected and opened, under its current or a
  former bundle id.
- SolidCraft's Website link goes to solidcraft.io (CraftApp::site).

* docs(AGENTS): allow tangency-labs/solidcraft as an approved community app

Downloads come from storytold/<app>/releases or, for an approved community app, its own repo:
today only tangency-labs/solidcraft, approved by the project owner on 2026-10-28. Its repo is
pinned in the catalog; the release feed can't move an app out of storytold/. Community apps
get the same SHA256SUMS check, and macOS installs still need a verifiable Developer ID. The
rule that agents never land or approve another third-party app is unchanged.
2026-10-28 18:51:51 -04:00
Dylan M. Taylor deccd0ac1e Honor CARGO_BUILD_TARGET in FreeBSD packaging for aarch64 release builds (#958) 2026-10-28 15:35:30 -04:00
Jeremy 157f605d6c fix(installer): Open fails with "X isn't where it was installed (X)" on MSI installs (#954)
* fix(installer): open an MSI app whose exe is outside its record's scope; never show a bare name as its location

* style: rustfmt the bare-name launch message

* fix(installer): keep the launch fallback out of the MSI update folder; show the bare-name message only for Windows MSI installs

* fix(installer): look for the exe in the folder the package recorded, in any hive, before guessing default folders

* fix(installer): open the fallback copy whose version is the selected record's

* fix(installer): compare the recorded exe to the stored location with one shared spelling

* fix(installer): let a version match see every root's copy, not only the first
2026-10-28 14:08:07 -04:00
Brandon ThomasandClaude Opus 5.5 9613c285dd Release 0.9.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.9.0
2026-10-28 12:25:41 -04:00
aiongg af8d35e6a0 Merge pull request #925 from Jeremy8776/feat/install-scope-policy
Windows installation policy: ArtCraft Launcher
2026-10-27 18:04:33 -04:00
Jeremy 38361c33f1 fix(installer): stop the fallback on a doubly-registered copy; check the launcher restore in the execute sequence
- 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.
2026-10-27 21:34:48 +00:00
Jeremy ed34ecc898 fix(installer): keep updating when the Windows Installer lookup cannot run, and elevate custom protected folders
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.
2026-10-27 21:13:30 +00:00
aiongg e4f26d24fe ci: use storytold/craft-common-actions at the merged installer-policy commit 2026-10-27 20:52:02 +00:00