diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 7b9d8e4..1fd8057 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -179,9 +179,40 @@ jobs: Write-Host "Created: dist/$packageName.zip" Get-ChildItem -Path "dist/" + # Pinned and checksummed: this compiler produces the executable users run. + # 6.x rather than 7.x deliberately - packaging/windows/vapourbox.iss is + # written against 6. Installed to its own directory and handed over as + # $ISCC: the runner image already has an ISCC.exe on PATH (a Chocolatey + # shim, version unpinned), and without this that one does the compiling. + - name: Install Inno Setup + shell: pwsh + run: | + $version = "6.7.3" + $sha256 = "9c73c3bae7ed48d44112a0f48e66742c00090bdb5bef71d9d3c056c66e97b732" + $tag = "is-" + ($version -replace '\.', '_') + $setup = Join-Path $env:RUNNER_TEMP "innosetup-$version.exe" + Invoke-WebRequest -Uri "https://github.com/jrsoftware/issrc/releases/download/$tag/innosetup-$version.exe" -OutFile $setup -UseBasicParsing + $actual = (Get-FileHash -Algorithm SHA256 $setup).Hash.ToLower() + if ($actual -ne $sha256) { throw "Inno Setup $version failed its checksum: $actual" } + $dir = Join-Path $env:RUNNER_TEMP "InnoSetup-$version" + $install = Start-Process -FilePath $setup -ArgumentList "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/SP-", "/CURRENTUSER", "/NOICONS", "/DIR=$dir" -Wait -PassThru + if ($install.ExitCode -ne 0) { throw "Inno Setup installer exited with $($install.ExitCode)" } + $iscc = Join-Path $dir "ISCC.exe" + if (-not (Test-Path $iscc)) { throw "Inno Setup $version did not install ISCC.exe to $dir" } + "ISCC=$iscc" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Build installer + shell: pwsh + run: | + $version = "${{ inputs.version }}" + ./Scripts/build-windows-installer.ps1 -Version $version -SourceDir "dist/VapourBox-$version-windows-x64" -OutputDir dist + - name: Upload artifact uses: actions/upload-artifact@v5 with: name: VapourBox-${{ inputs.version }}-windows-x64 - path: dist/VapourBox-${{ inputs.version }}-windows-x64.zip + if-no-files-found: error + path: | + dist/VapourBox-${{ inputs.version }}-windows-x64-setup.exe + dist/VapourBox-${{ inputs.version }}-windows-x64.zip diff --git a/CLAUDE.md b/CLAUDE.md index 0deed68..1b967ec 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -263,8 +263,8 @@ dart run build_runner build # macOS ./Scripts/package-macos.sh --version X.Y.Z [--skip-build] -# Windows -.\Scripts\package-windows.ps1 -Version "X.Y.Z" [-SkipBuild] +# Windows — writes an installer (-setup.exe) and a zip of the same tree +.\Scripts\package-windows.ps1 -Version "X.Y.Z" [-SkipBuild] [-SkipInstaller] # Linux — writes an AppImage (+ its .zsync) and a tarball of the same tree ./Scripts/package-linux.sh --version X.Y.Z [--skip-build] [--skip-appimage] @@ -1439,6 +1439,31 @@ placement and a version skew would change chroma per-OS. without it the app reports "Version file missing" and re-downloads the published bundle over a locally built one. - Show in Folder: `cmd /c explorer /select, ` +- **Ships as an Inno Setup installer, with the zip of the same tree as the + portable option** (`packaging/windows/vapourbox.iss`, compiled by + `Scripts/build-windows-installer.ps1`). The rules that keep it working: + - **Per-user only (`PrivilegesRequired=lowest`), with no all-users option.** + On Windows the app installs deps and add-ons into `deps\` and `addons\` + **next to the executable**, so the install directory must be user-writable; + under `Program Files` the first-launch download fails. Don't add + `PrivilegesRequiredOverridesAllowed` or an admin install without first + moving those directories out of the app folder. + - **Never change `AppId`.** It is how an upgrade finds the existing install; + a new one installs a second copy with its own uninstall entry. + - **Upgrade keeps `deps\`/`addons\`; uninstall removes them** + (`[UninstallDelete]` — the app downloaded them, so the uninstaller doesn't + otherwise know they exist). `[InstallDelete]` clears `templates\` and + `data\` first, because Inno never removes a file a newer version dropped + and `exe_dir\templates` is the first path the worker searches. + - **The installer is unsigned**, like the rest of the Windows build, so + SmartScreen warns on it; the README says how to proceed. + - `build-windows.yml` assembles the package directory inline rather than + calling `package-windows.ps1` (and the two zips differ in layout), which + is why the installer is its own script both call. Inno Setup is pinned to + 6.7.3 by SHA-256 in the workflow and passed to the script as `$env:ISCC` + — the runner image has its own unpinned `ISCC.exe` on `PATH`, which wins + otherwise. The `.iss` is written against 6, not 7. + - Not verifiable from macOS: `build-windows.yml` is the test. ### macOS @@ -1611,7 +1636,8 @@ Notes: | `Scripts/update-version.sh` | Update version across all files (`--app X.Y.Z --deps X.Y.Z --deps-tag deps-vX.Y.Z`) | | `Scripts/check-deps-changed.sh` | Detect if deps changed since last release (exit 0=changed, 1=unchanged) | | `Scripts/package-macos.sh` | Package macOS app | -| `Scripts/package-windows.ps1` | Package Windows app | +| `Scripts/package-windows.ps1` | Package Windows app (installer + zip) | +| `Scripts/build-windows-installer.ps1` | Compile the Inno Setup installer from an assembled package dir | | `Scripts/package-linux.sh` | Package Linux app (AppImage + `.zsync` + tarball) | | `Scripts/package-deps-macos.sh` | Package macOS deps | | `Scripts/package-deps-windows.ps1` | Package Windows deps | diff --git a/README.md b/README.md index 6f26182..7b4e941 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ VapourBox runs [VapourSynth](https://www.vapoursynth.com/), QTGMC and FFmpeg — |----------|------------|------| | macOS (Apple Silicon) | macOS 15 Sequoia | `VapourBox-x.x.x-macos-arm64.dmg` | | macOS (Intel) | macOS 12 Monterey | `VapourBox-x.x.x-macos-x64.dmg` | -| Windows (x64) | Windows 10/11 | `VapourBox-x.x.x-windows-x64.zip` | +| Windows (x64) | Windows 10/11 | `VapourBox-x.x.x-windows-x64-setup.exe` | | Linux (x64) | glibc 2.39 (Ubuntu 24.04, Debian 13) | `VapourBox-x.x.x-linux-x64.AppImage` | | Linux (arm64) | glibc 2.39 (Ubuntu 24.04, Debian 13) | `VapourBox-x.x.x-linux-arm64.AppImage` | @@ -58,11 +58,15 @@ VapourBox is signed with an Apple Developer ID and notarized, so it opens withou
Installing on Windows -1. Download the `.zip`. -2. Extract it anywhere (e.g. `C:\VapourBox`). -3. Run `vapourbox.exe`. +1. Download the `-setup.exe` installer and run it. +2. The installer isn't code-signed, so Windows SmartScreen may say it "protected your PC" — choose **More info → Run anyway**. +3. Launch VapourBox from the Start menu. 4. First launch downloads its processing dependencies (~145 MB). +It installs for the current user only (under `%LOCALAPPDATA%\Programs\VapourBox`), so it never asks for administrator rights. Running a newer installer upgrades in place and keeps the downloaded dependencies; uninstalling from **Settings → Apps** removes them too. Your saved presets are left alone. + +**Portable option:** each release also carries a `.zip` of the same build. Extract it anywhere you can write to (e.g. `C:\VapourBox`, not `Program Files`) and run `vapourbox.exe`. +
diff --git a/Scripts/build-windows-installer.ps1 b/Scripts/build-windows-installer.ps1 new file mode 100644 index 0000000..7b43177 --- /dev/null +++ b/Scripts/build-windows-installer.ps1 @@ -0,0 +1,72 @@ +# Build the VapourBox Windows installer (Inno Setup) from an assembled package +# directory - the same tree the .zip is made from. +# +# Called by package-windows.ps1 and by build-windows.yml, so there is one +# definition of the installer however the package directory was assembled. +# +# Prerequisites: +# - Inno Setup 6 (https://jrsoftware.org/isdl.php). ISCC.exe is found on PATH, +# in the default install locations, or via $env:ISCC. +# +# Usage: .\Scripts\build-windows-installer.ps1 -Version "0.1.0" -SourceDir dist\VapourBox-0.1.0-windows-x64 + +param( + [Parameter(Mandatory = $true)][string]$Version, + [Parameter(Mandatory = $true)][string]$SourceDir, + [string]$OutputDir = "" +) + +$ErrorActionPreference = "Stop" + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$ProjectRoot = Split-Path -Parent $ScriptDir +$IssFile = Join-Path $ProjectRoot "packaging\windows\vapourbox.iss" +if (-not $OutputDir) { $OutputDir = Join-Path $ProjectRoot "dist" } + +if (-not (Test-Path $SourceDir)) { throw "Package directory not found: $SourceDir" } +$SourceDir = (Resolve-Path $SourceDir).Path +New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null +$OutputDir = (Resolve-Path $OutputDir).Path + +# An installer built from an incomplete tree installs cleanly and then fails +# on the first video dropped, so check for the pieces before compiling. +foreach ($required in @( + "vapourbox.exe", + "vapourbox-worker.exe", + "templates\pipeline_template.vpy", + "templates\preview_template.vpy", + "templates\pipe_source.py", + "data\flutter_assets")) { + if (-not (Test-Path (Join-Path $SourceDir $required))) { + throw "Package directory is incomplete: $required is missing from $SourceDir" + } +} + +$Iscc = $null +$Candidates = @( + $env:ISCC, + (Get-Command "ISCC.exe" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source -First 1), + "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe", + "$env:ProgramFiles\Inno Setup 6\ISCC.exe", + "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe" +) +foreach ($candidate in $Candidates) { + if ($candidate -and (Test-Path $candidate)) { $Iscc = $candidate; break } +} +if (-not $Iscc) { + throw "Inno Setup 6 (ISCC.exe) not found. Install it from https://jrsoftware.org/isdl.php, or set `$env:ISCC to its path." +} + +$Installer = Join-Path $OutputDir "VapourBox-$Version-windows-x64-setup.exe" +if (Test-Path $Installer) { Remove-Item $Installer } + +Write-Host "Building installer with $Iscc" -ForegroundColor Yellow +& $Iscc /Qp "/DAppVersion=$Version" "/DSourceDir=$SourceDir" "/DOutputDir=$OutputDir" $IssFile +if ($LASTEXITCODE -ne 0) { throw "Inno Setup failed with exit code $LASTEXITCODE" } +if (-not (Test-Path $Installer)) { throw "Inno Setup reported success but $Installer was not created" } + +$SizeMb = [math]::Round((Get-Item $Installer).Length / 1MB, 1) +$Sha256 = (Get-FileHash -Algorithm SHA256 $Installer).Hash.ToLower() +Write-Host "Installer: $Installer" -ForegroundColor Green +Write-Host "Size: $SizeMb MB" +Write-Host "SHA256: $Sha256" diff --git a/Scripts/ci-build-and-release.sh b/Scripts/ci-build-and-release.sh index f92ccf8..aaf4427 100755 --- a/Scripts/ci-build-and-release.sh +++ b/Scripts/ci-build-and-release.sh @@ -185,7 +185,7 @@ echo " Downloading Linux artifact..." gh run download "$LINUX_RUN_ID" --dir "$DOWNLOAD_DIR" echo " Downloaded artifacts:" -find "$DOWNLOAD_DIR" -type f \( -name "*.zip" -o -name "*.dmg" -o -name "*.tar.gz" -o -name "*.AppImage" -o -name "*.AppImage.zsync" -o -name "*.sha256.json" \) | while read -r f; do +find "$DOWNLOAD_DIR" -type f \( -name "*.zip" -o -name "*.dmg" -o -name "*.tar.gz" -o -name "*-setup.exe" -o -name "*.AppImage" -o -name "*.AppImage.zsync" -o -name "*.sha256.json" \) | while read -r f; do SIZE=$(du -sh "$f" | cut -f1) echo " $(basename "$f") ($SIZE)" done @@ -195,7 +195,7 @@ echo "" echo -e "${BLUE}[4/4] Uploading to release ${RELEASE_TAG}...${NC}" UPLOAD_COUNT=0 -find "$DOWNLOAD_DIR" -type f \( -name "*.zip" -o -name "*.dmg" -o -name "*.tar.gz" -o -name "*.AppImage" -o -name "*.AppImage.zsync" -o -name "*.sha256.json" \) | while read -r f; do +find "$DOWNLOAD_DIR" -type f \( -name "*.zip" -o -name "*.dmg" -o -name "*.tar.gz" -o -name "*-setup.exe" -o -name "*.AppImage" -o -name "*.AppImage.zsync" -o -name "*.sha256.json" \) | while read -r f; do BASENAME=$(basename "$f") echo " Uploading $BASENAME..." gh release upload "$RELEASE_TAG" "$f" --clobber diff --git a/Scripts/package-windows.ps1 b/Scripts/package-windows.ps1 index f1b5351..a122cc0 100644 --- a/Scripts/package-windows.ps1 +++ b/Scripts/package-windows.ps1 @@ -1,16 +1,19 @@ # Package VapourBox App for Windows (without dependencies) -# Creates a standalone zip file with the app only +# Creates an installer (the primary download) and a standalone zip (the +# portable option) from the same tree, with the app only. # Dependencies are packaged separately with package-deps-windows.ps1 # # Prerequisites: # - Flutter SDK installed # - Rust toolchain installed +# - Inno Setup 6, for the installer (or pass -SkipInstaller) # -# Usage: .\Scripts\package-windows.ps1 -Version "0.1.0" +# Usage: .\Scripts\package-windows.ps1 -Version "0.1.0" [-SkipBuild] [-SkipInstaller] param( [string]$Version = "1.0.0", - [switch]$SkipBuild = $false + [switch]$SkipBuild = $false, + [switch]$SkipInstaller = $false ) $ErrorActionPreference = "Stop" @@ -157,6 +160,14 @@ if (Test-Path $ZipFile) { } Compress-Archive -Path "$PackageDir\*" -DestinationPath $ZipFile -CompressionLevel Optimal +# Create installer +if (-not $SkipInstaller) { + Write-Host "Creating installer..." -ForegroundColor Yellow + & (Join-Path $ScriptDir "build-windows-installer.ps1") -Version $Version -SourceDir $PackageDir -OutputDir $DistDir +} else { + Write-Host "Skipping installer (-SkipInstaller)" -ForegroundColor Gray +} + # Calculate sizes $PackageSize = (Get-ChildItem -Recurse $PackageDir | Measure-Object -Property Length -Sum).Sum / 1MB $ZipSize = (Get-Item $ZipFile).Length / 1MB @@ -170,5 +181,6 @@ Write-Host "" Write-Host "Package size: $([math]::Round($PackageSize, 1)) MB" Write-Host "Zip size: $([math]::Round($ZipSize, 1)) MB" Write-Host "" -Write-Host "To distribute, share the zip file:" +Write-Host "To distribute, share the installer (VapourBox-$Version-windows-x64-setup.exe)" +Write-Host "or the zip file:" Write-Host " $ZipFile" diff --git a/Scripts/release.sh b/Scripts/release.sh index 9ab32ff..fa95031 100755 --- a/Scripts/release.sh +++ b/Scripts/release.sh @@ -303,14 +303,14 @@ RELEASE_NOTES="## VapourBox $APP_VERSION - TODO: Add release notes ### Downloads -- **Windows**: \`VapourBox-$APP_VERSION-windows-x64.zip\` +- **Windows**: \`VapourBox-$APP_VERSION-windows-x64-setup.exe\` (or the portable \`.zip\`) - **macOS (Apple Silicon)**: \`VapourBox-$APP_VERSION-macos-arm64.zip\` (contains signed & notarized DMG) - **macOS (Intel)**: \`VapourBox-$APP_VERSION-macos-x64.zip\` (contains signed & notarized DMG) - **Linux (x64)**: \`VapourBox-$APP_VERSION-linux-x64.AppImage\` (or the \`.tar.gz\`) - **Linux (arm64)**: \`VapourBox-$APP_VERSION-linux-arm64.AppImage\` (or the \`.tar.gz\`) ### Installation -- **Windows**: Extract zip and run \`vapourbox.exe\` +- **Windows**: Run the installer — or extract the zip and run \`vapourbox.exe\` - **macOS**: Open the DMG and drag VapourBox to Applications - **Linux**: \`chmod +x\` the AppImage and run it — or extract the tarball and run \`./vapourbox\` @@ -345,7 +345,7 @@ echo -e "${GREEN}╚════════════════════ echo "" echo "Next steps:" echo " 1. Wait for Windows and Linux builds to complete (if using GitHub Actions)" -echo " 2. Upload the Windows zip and the Linux AppImages (each with its .zsync) and tarballs to the draft release" +echo " 2. Upload the Windows installer and zip, and the Linux AppImages (each with its .zsync) and tarballs to the draft release" echo " 3. Test the release builds" echo " 4. Edit release notes" echo " 5. Publish the release" diff --git a/app/test/packaging_test.dart b/app/test/packaging_test.dart index 3fcf067..4c4ef4d 100644 --- a/app/test/packaging_test.dart +++ b/app/test/packaging_test.dart @@ -80,6 +80,63 @@ void main() { }); } + // The Windows installer is built from the same tree as the zip, by a script + // of its own that both the local packaging script and CI call. + group('Windows installer', () { + final iss = _read(root, ['packaging', 'windows', 'vapourbox.iss']); + final installer = _read(root, ['Scripts', 'build-windows-installer.ps1']); + + test('installs per-user, because deps download beside the executable', () { + // dependency_manager.dart puts deps\ next to the exe on Windows; an + // install under Program Files makes that download fail. + final deps = + _read(root, ['app', 'lib', 'services', 'dependency_manager.dart']); + expect(deps, contains("path.join(appDir, 'deps', 'windows-x64')"), + reason: 'if deps no longer live beside the exe, the per-user ' + 'restriction below can be revisited'); + expect(iss, contains('PrivilegesRequired=lowest')); + expect(iss, isNot(contains('PrivilegesRequiredOverridesAllowed'))); + }); + + test('uninstall removes what the app downloaded; upgrade does not', () { + final uninstall = iss.split('[UninstallDelete]').last; + expect(uninstall, contains(r'{app}\deps')); + expect(uninstall, contains(r'{app}\addons')); + final installDelete = + iss.split('[InstallDelete]').last.split('[Files]').first; + expect(installDelete, isNot(contains(r'{app}\deps'))); + expect(installDelete, isNot(contains(r'{app}\addons'))); + expect(installDelete, contains(r'{app}\templates')); + }); + + test('one filename across the .iss, the script, CI and the upload', () { + expect( + iss, + contains( + 'OutputBaseFilename={#AppName}-{#AppVersion}-windows-x64-setup')); + expect(installer, contains(r'VapourBox-$Version-windows-x64-setup.exe')); + + final workflow = + _read(root, ['.github', 'workflows', 'build-windows.yml']); + expect(workflow, contains('build-windows-installer.ps1')); + expect(workflow, contains('-windows-x64-setup.exe\n')); + expect(workflow, contains('-windows-x64.zip\n')); + + final local = _read(root, ['Scripts', 'package-windows.ps1']); + expect(local, contains('build-windows-installer.ps1')); + + final upload = _read(root, ['Scripts', 'ci-build-and-release.sh']); + final finds = upload + .split('\n') + .where((l) => l.startsWith('find ') && l.contains('*.zip')) + .toList(); + expect(finds, isNotEmpty); + for (final line in finds) { + expect(line, contains('"*-setup.exe"')); + } + }); + }); + // The Linux AppImage embeds update information naming a `.zsync` on the // latest GitHub release. Three files have to agree on that name, and nothing // fails at build time if they don't — the updater just finds nothing. diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 0b54570..22c4740 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -262,9 +262,18 @@ the Windows resource compiler read them at build time. ### Windows ```powershell -.\Scripts\package-windows.ps1 -Version "1.0.0" [-SkipBuild] +.\Scripts\package-windows.ps1 -Version "1.0.0" [-SkipBuild] [-SkipInstaller] ``` -Output: `dist/VapourBox-1.0.0-windows-x64.zip` +Output, both from the same tree: + +- `dist/VapourBox-1.0.0-windows-x64-setup.exe` — the installer, the primary download +- `dist/VapourBox-1.0.0-windows-x64.zip` — the portable option + +The installer needs [Inno Setup 6](https://jrsoftware.org/isdl.php) (`ISCC.exe` on +`PATH`, in its default location, or named by `$env:ISCC`); `-SkipInstaller` builds +the zip alone. It is defined by `packaging/windows/vapourbox.iss` and compiled by +`Scripts/build-windows-installer.ps1`, which can also be run directly against an +already-assembled package directory. ### Linux ```bash diff --git a/packaging/windows/vapourbox.iss b/packaging/windows/vapourbox.iss new file mode 100644 index 0000000..f1fa387 --- /dev/null +++ b/packaging/windows/vapourbox.iss @@ -0,0 +1,97 @@ +; VapourBox Windows installer (Inno Setup 6). +; +; Built by Scripts\build-windows-installer.ps1, which passes: +; /DAppVersion=X.Y.Z /DSourceDir= /DOutputDir= +; SourceDir is the same tree the .zip is made from, so the two cannot drift. + +#ifndef AppVersion + #error AppVersion is not defined - build with Scripts\build-windows-installer.ps1 +#endif +#ifndef SourceDir + #error SourceDir is not defined - build with Scripts\build-windows-installer.ps1 +#endif +#ifndef OutputDir + #error OutputDir is not defined - build with Scripts\build-windows-installer.ps1 +#endif + +#define AppName "VapourBox" +#define AppExe "vapourbox.exe" +#define AppUrl "https://github.com/StuartCameronCode/VapourBox" + +[Setup] +; Identifies the app to Windows across versions: an upgrade replaces the +; install with the same AppId. NEVER change it - a new one installs a second +; copy alongside, with its own uninstall entry. +AppId={{720387E0-A990-4A2A-AEE8-754B04E64620} +AppName={#AppName} +AppVersion={#AppVersion} +AppVerName={#AppName} {#AppVersion} +AppPublisher=Stuart Cameron +AppPublisherURL={#AppUrl} +AppSupportURL={#AppUrl}/issues +AppUpdatesURL={#AppUrl}/releases + +; Per-user, no elevation, and no option to install for all users. This is +; load-bearing, not a convenience: on Windows the app downloads its processing +; dependencies and add-ons into deps\ and addons\ NEXT TO THE EXECUTABLE, so +; the install directory has to stay writable by the user. Under Program Files +; the first-launch download would fail. With "lowest", {autopf} resolves to +; %LOCALAPPDATA%\Programs. +PrivilegesRequired=lowest +DefaultDirName={autopf}\{#AppName} +DisableProgramGroupPage=yes +UsePreviousAppDir=yes + +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible +MinVersion=10.0 + +OutputDir={#OutputDir} +OutputBaseFilename={#AppName}-{#AppVersion}-windows-x64-setup +SetupIconFile=..\..\app\windows\runner\resources\app_icon.ico +UninstallDisplayIcon={app}\{#AppExe} +UninstallDisplayName={#AppName} +WizardStyle=modern +Compression=lzma2/max +SolidCompression=yes + +; Text, not VersionInfoVersion: that one must be four integers, and a version +; like 1.2.3-rc1 would fail the compile. +VersionInfoCompany=Stuart Cameron +VersionInfoDescription={#AppName} Setup +VersionInfoProductName={#AppName} +VersionInfoTextVersion={#AppVersion} +VersionInfoProductTextVersion={#AppVersion} + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +[InstallDelete] +; Inno only adds and overwrites, so a file dropped from a newer version would +; survive an upgrade. That matters most for templates\, the first place the +; worker looks: a stale module left there is a silent wrong-version bug. +; deps\ and addons\ are deliberately NOT here - they are the user's downloads +; and survive upgrades. +Type: filesandordirs; Name: "{app}\templates" +Type: filesandordirs; Name: "{app}\data" + +[Files] +; The .bat launcher is for the zip; the installer makes real shortcuts. +Source: "{#SourceDir}\*"; DestDir: "{app}"; Excludes: "*.bat"; Flags: ignoreversion recursesubdirs createallsubdirs + +[Icons] +Name: "{autoprograms}\{#AppName}"; Filename: "{app}\{#AppExe}" +Name: "{autodesktop}\{#AppName}"; Filename: "{app}\{#AppExe}"; Tasks: desktopicon + +[Run] +Filename: "{app}\{#AppExe}"; Description: "{cm:LaunchProgram,{#AppName}}"; Flags: nowait postinstall skipifsilent + +[UninstallDelete] +; Downloaded by the app after install, so the uninstaller does not know about +; them and would otherwise leave several hundred MB behind. User presets +; (%USERPROFILE%\.vapourbox) are the user's own files and are left alone. +Type: filesandordirs; Name: "{app}\deps" +Type: filesandordirs; Name: "{app}\addons"