6 Commits
Author SHA1 Message Date
Joseph MattielloandClaude Opus 5.5 5364fd7462 hotfix: fetch the install scripts from master, not develop
Same change as #1087 on develop, applied to master so that installs and
install.sh's --auto-update fetch the released script now rather than at the
next release.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-23 21:33:40 -04:00
Joseph MattielloandClaude Sonnet 5.5 46b10a4599 build(app): macOS ships the zip only; drop the DMG from packaging and the installer
The zip holds the notarized, stapled Virtual Jaguar.app as its only top-level
entry; CI unpacks it and re-verifies signature and staple. install.sh loses its
DMG path (--app-format dmg is now rejected) and its tests follow.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 00:27:58 -04:00
Joseph MattielloandClaude Sonnet 5.5 a20867bd2d fix(tools): installer review fixes and #839 package layout
- install.ps1: the interactive channel choice is a local assignment ($script:
  missed the parameter under & ([scriptblock]::Create(...))); silence progress
  for 5.1; portable zip is one top folder, its CONTENTS go to the destination;
  setup default args are /VERYSILENT /NORESTART /SUPPRESSMSGBOXES and the Inno
  AppId uninstall key is checked first.
- install.sh: AppImage menu entry and icon use io.github.libretro.VirtualJaguar
  (hicolor tree extracted from the AppImage); FUSE probe checks /sbin/ldconfig
  and multiarch dirs without globs (zsh); a nightly receipt beats the project
  version Info.plist/dpkg report, so a release is still offered over a nightly.
- tests and CI: portable file:// URLs, narrower dry-run snapshots, 5.1-safe
  child-process error handling, nightly-vs-release checks, documented
  PSScriptAnalyzer exclusions.

Refs #840

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-12 23:27:41 -04:00
Joseph MattielloandClaude Sonnet 5.5 a4d8635beb feat(tools): install.sh/install.ps1 install and update the core, the app, or both
install.sh gains --what core|app|both (and --core/--app/--both), --app-dest,
--app-format, --force, --allow-unsigned and the VJ_INSTALL_BASE_URL /
VJ_INSTALL_RETROARCH / VJ_INSTALL_NO_TTY overrides.  The app is the macOS zip
or DMG (/Applications, else ~/Applications), the Linux AppImage
(~/.local/bin + .desktop) or the .deb via apt/dpkg, picked from the userland,
not the kernel.  Updates are detected from the machine (Info.plist, dpkg, the
AppImage path) with an install receipt as a hash cache for nightlies.  Every
download, core included, is checked against SHA256SUMS.txt; a channel with no
app build says so instead of erroring.  Unattended runs stay core-only unless
this script installed the app.  Gatekeeper protections are never stripped
silently.  Also: --auto-update off no longer reinstalls first, and --help works
under zsh.

install.ps1 is the Windows twin (5.1 and 7, irm | iex friendly): core into the
cores folder discovered from retroarch.cfg / registry with the word size read
from the retroarch.exe PE header, app as the portable zip (+ Start-menu
shortcut) or the silent setup.exe, -DryRun, -Force, SHA-256 verification.

test/installer/ holds hermetic suites for both over a fake file:// release
tree; .github/workflows/installer.yml runs them (plus shellcheck and
PSScriptAnalyzer) on Linux, macOS and Windows PowerShell 5.1 / 7.

Refs #840

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-12 23:11:30 -04:00
Joseph MattielloandClaude Opus 5 71c1535047 tools(install): route file-mutating calls through a guarded helper
Consistency with the repo's convention for the other guard scripts, raised in
review on #625.

rm/cp/mv are commonly aliased to the interactive -i variants. A prompt with
nothing attached to answer it does not fail -- it blocks forever, which is the
worst failure mode for an installer someone is running over SSH.

Aliases do not expand in a non-interactive script, so this was not a live bug
here. It is still the convention, and the reason it exists (a hung `rm -i` once
sat for nine hours in this repo) applies to anything that might later be
sourced, run under `sh -i`, or copied as a starting point.

New as_root helper picks the right form per path:

  - no sudo needed  -> `command cp`, which bypasses any alias or shell function
  - sudo needed     -> plain `sudo cp`, which needs no guard because sudo execs
                       the binary and never a shell alias, and could not take
                       one anyway since `command` is a shell builtin

The two remaining bare `cp` mentions are inside printed undo instructions --
those are text a human types, where `command cp` would only confuse.

Verified the guard actually does something: with rm/cp shadowed by prompting
stubs, the unguarded call hits the stub and the guarded one does not.

  syntax and build selection under sh, bash, zsh, dash   identical
  fresh install / re-install with backup                 works, RetroArch loads it
  error paths (bad --dest, --auto-update off)            unchanged

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 20:11:42 -04:00
Joseph MattielloandClaude Opus 5 f7ba81eef0 tools: add a cross-platform core installer, and link it from the site
Users are hand-picking .so files off the releases page and getting it wrong.
The failure is silent: RetroArch says only "Failed to open libretro core".

The specific trap is that builds are named by board AND word size -- rpi4 is
32-bit armhf, rpi4_64 is aarch64 -- and `uname -m` is the wrong way to choose.
Raspberry Pi OS shipped a 64-bit kernel with a 32-bit userland for years, so
uname reports aarch64 while every binary on the system is 32-bit. The core has
to match RETROARCH, not the kernel.

tools/install.sh reads the word size and machine out of the frontend's own
binary (ELF class + e_machine on Linux, the running slice on macOS) and aborts
before writing anything if the downloaded core does not match.

Finds the core directory by reading RetroArch's own libretro_directory out of
retroarch.cfg rather than carrying a per-distro table of paths. That is
authoritative everywhere, including the read-only-root distributions where a
guessed path would simply be wrong, and it does not rot when a distro moves
things. RetroPie is the one special case, because it keeps each core in its own
package directory that emulators.cfg points at instead.

POSIX sh, safe to pipe into a shell. Offers latest/nightly/pinned-tag, an
optional systemd-timer or cron auto-updater, --dry-run, --list, and --yes.
Prompts read /dev/tty because stdin is the script when piped; with no tty it
takes defaults rather than hanging.

Testing found two bugs worth recording:

  - Under zsh -- macOS's default shell, so a likely invocation -- `for a in
    $archs` does not word-split, so the universal-binary slice picker never
    matched and silently chose x86_64 on an Apple Silicon Mac. Verified: sh
    said macos-arm64 and zsh said macos-x86_64 on the same machine. Now a
    split-free `case`, and all four shells agree.
  - Piped into a shell, $0 is the shell name and not a file, so --help ran
    `sed` on "sh" and the re-run hints printed "sh --dest ...". Both now go
    through a SELF that falls back to the curl one-liner.

Verified: ELF detection agrees with file(1) on all 6 real Linux builds across
i386/arm/x86_64/aarch64; all 14 board+wordsize combinations map to assets that
exist in the release; syntax clean under sh, bash, zsh and dash, which all
choose the same build; discovery finds the real RetroArch on this machine by
config; install, re-install with backup, and the post-install load check all
work end to end -- RetroArch loaded the freshly downloaded core.

build_site.py gains {{RAW_URL}}, derived from REPO_URL so the curl one-liner on
the site cannot drift from the repository the rest of the page points at.

Supersedes the RetroPie-only script this replaces, which was never released.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:44:41 -04:00