Skip to content

0.9.5

Choose a tag to compare

@github-actions github-actions released this 02 Aug 21:43
· 10 commits to dev since this release
a5cf52f

What's Changed

add: file log next to Update.exe, with an Open logs folder button

  • The app now keeps a readable log, and About has an Open logs folder button that lands you on
    it. When an update misbehaves there is finally something to look at instead of guesswork.
  • Crashes are recorded too — the unhandled-exception handler was an empty TODO.

Where the file goes, and why it matters

Services/AppLog writes WikeloContractor.log into the install root, beside Update.exe — not
beside the executable. The exe lives in current\, which Velopack replaces wholesale on every update
("Backing up current dir… Replacing current dir with…"), so a log written there is destroyed at the
one moment it is needed: right after an update went wrong. A dev run has nothing above it, so it
falls back to the binaries' folder.

AppLog is static and dependency-free on purpose: VelopackApp.Build().Run() is the first line of
startup and the update hooks it handles run and exit before the host is ever built.

What ends up in it

  • FileLoggerProvider routes the host's ILogger output there, from Information up — Debug/Trace
    from the host and HttpClient would bury the app's own lines, and this file is meant to be read by
    a person.
  • VelopackFileLogger adds Velopack's managed half (locator decisions, "app is out-dated").
  • MirrorUpdaterLog copies Update.exe's own log out of the hidden %LocalAppData%\velopack\.
    That half cannot be redirected — ApplyUpdatesAndRestart builds its command line without --log
    and it is the half that carries the apply steps, so one findable folder now holds the whole story.
    Skipped in a dev run, where there is no updater history and the file is ~130 KB per launch.

Logging never throws: a read-only directory or a locked file loses a line rather than breaking the
app. Rotates at 1 MB keeping one previous file.

Services/AppVersion is extracted so the log banner and the About page resolve the version once
instead of twice.

fix: portable update left two launchers and stale docs claimed it never updates

  • Updating a portable install used to leave two executables side by side —
    Wikelo Contractor.exe from the download and WikeloContractor.exe created by the updater. A
    shortcut to the first one kept starting the old, stale launcher after every update.

vpk pack names the portable launcher after --packTitle, while Update.exe re-creates it from
--mainExe, so a title with a space could never match. Confirmed from the updater's own log:

Extracting stub 'WikeloContractor_ExecutionStub.exe' to '...\WikeloContractor.exe'

and reproduced end to end by applying 0.9.4 onto a clean 0.9.1 in a scratch directory. This is
upstream bug velopack/velopack#982, filed against the pinned 1.2.0 and fixed in PR #985 — but
released only in the 1.2.110-* prerelease, so --packTitle is set equal to the --mainExe base name
instead, which is the fix that works on stable. The constraint is written next to the value, since
the obvious "improvement" is to restore the prettier spaced title.

Existing installs keep the orphaned Wikelo Contractor.exe until it is deleted by hand; the updater
does not remove it.

Documentation was wrong about portable updates

CLAUDE.md, PLAN.md and a release.yml comment all claimed portable builds do not auto-update because
IsInstalled is false. They do: Velopack treats the .portable + current\ + Update.exe layout as
installed, and Check for updates really applies a new release there — verified in the field and in
a scratch reproduction. IsInstalled is false only in a dev run. That mistake mattered: it made the
launcher bug look cosmetic when it actually bites every user who updates, and it propped up the
"auto-update is moot while unsigned" line in the signing rationale, which is also removed.

add: in-game overlay with global hotkeys (Phase 4)

A small always-on-top HUD listing up to ten pinned inventory items, so counts
stay accurate during a session without alt-tabbing out of Star Citizen.

  • Pin items from the Inventory page (per-row button, Overlay N/10 counter, a
    reset-all button); PinnedItemsService is the fourth JSON store, an ordered
    name list where the order is the slot.
  • Two configurable modifier patterns plus a digit drive +1/-1 per slot, rather
    than twenty separate bindings; plus show/hide and interactive-mode toggles,
    captured in Settings through the new HotkeyBox control.
  • Interop/NativeMethods is the repo's first and only P/Invoke surface;
    HotkeyService owns Win32 on a message-only HwndSource and reports partial
    registration failure instead of rolling it back. OverlayService decides what a
    press means and is the only piece that knows a window exists, which is what
    makes the whole overlay testable through IHotkeyService.Pressed.
  • InventoryStore hardened first: hotkey auto-repeat (~30 edits/s) against the
    old unlocked, fixed-temp-path, fire-and-forget write would have thrown
    IOException into a swallowed task and lost writes. Now a debounced write
    behind a semaphore, with Flush()/FlushAsync() and AppLog on failure.
  • Overlay corners are square and its height follows content: on a transparent
    window a rounded corner anti-aliases against the backdrop, and a restored
    fixed height silently clipped the tenth row.
  • Hotkeys do not reach an unelevated app while an elevated game is foreground
    (UIPI) — AppElevation detects it and Settings offers Restart as administrator.
    No injection, no keyboard hook, no reading game memory.

344 tests green.

fix: sourcing guide reload test was a coin flip on write-time granularity

Editing_a_fragment_refreshes_every_guide_including_it writes the fragment
twice back to back. SourcingGuideService fingerprints files by path plus
LastWriteTimeUtc, and Windows advances that on the ~15.6 ms clock tick, so both
writes usually land in the same tick and the service correctly concludes
nothing changed. Measured on the dev machine: 151 of 200 back-to-back writes
get an identical timestamp. Debug passed only because parsing and asserting
between the writes straddled a tick often enough; a Release CI run did not.

Forces a distinct timestamp after the second write, the same idiom
CatalogImageOverrideServiceTests already uses for this exact hazard. The test
is about the reload signature covering the _shared folders, so removing the
filesystem race does not weaken it.