Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Inno Setup installer script
|
||||
|
||||
## What is `windows-installer.iss`?
|
||||
|
||||
On Windows, programs are conventionally installed using an installer, also known as an installation wizard.
|
||||
The installer is a single executable that takes care of:
|
||||
* Creating a directory to store the program's files
|
||||
* Downloading assets
|
||||
* Initializing registry entries
|
||||
* Creating a desktop icon
|
||||
* ... and more, depending on the application's needs.
|
||||
|
||||
|
||||
`windows-installer.iss` is an **Inno Setup script**:
|
||||
a configuration file for building a Warp installer.
|
||||
The Inno Setup Compiler takes a script file and generates an installer executable.
|
||||
This is roughly equivalent to the bundling process on MacOS.
|
||||
|
||||
|
||||
## How to edit the installer
|
||||
|
||||
See the Inno Setup documentation: [Inno Setup Help](https://jrsoftware.org/ishelp/).
|
||||
This script can be edited manually using any code editor.
|
||||
However, it requires the Inno Setup compiler to be turned into a `.exe` file.
|
||||
|
||||
|
||||
## How to compile this installer
|
||||
|
||||
First, ensure you've set up your environment.
|
||||
* Download and install the [Inno Setup Compiler](https://jrsoftware.org/isdl.php).
|
||||
* Run `cargo build` to ensure the installer uses the latest version of Warp.
|
||||
|
||||
### Option 1: Use the CLI
|
||||
1. Add the Inno Setup Command-line Compiler executable to your shell path.
|
||||
By default, it is located at `C:\Program Files (x86)\Inno Setup 6\ISCC.exe`.
|
||||
2. Compile the installer:
|
||||
```shell
|
||||
iscc .\script\windows\windows-installer.iss`.
|
||||
```
|
||||
3. Run the generated executable:
|
||||
```shell
|
||||
.\script\windows\Output\Warp-Windows-Setup.exe`.
|
||||
```
|
||||
|
||||
The script begins with a series of preprocessor definitions.
|
||||
From the command line, use the `/D` flag to emulate preprocessor definitions
|
||||
and override the hardcoded defaults.
|
||||
Usage: `iscc <script path> /D<name>[=<value>]`
|
||||
|
||||
The following constants can be overwritten:
|
||||
* `MyAppVersion` (default: `0.1.0`)
|
||||
* `MyAppExeName` (default: `warp.exe`)
|
||||
* `ReleaseChannel` (default: `dev`)
|
||||
* `TargetProfileDir` (default: `debug`)
|
||||
|
||||
### Option 2: Use the GUI
|
||||
1. Open the Inno Setup application and select this script.
|
||||
2. Click the "compile" button. This will generate an installer executable in a directory called `Output` at the same level as this script.
|
||||
2. To run the installer, click the "run" button in Inno Setup.
|
||||
|
||||
|
||||
## Using icons
|
||||
|
||||
Windows has its own icon file format that bundles together multiple icon sizes.
|
||||
App icons are located in `app/channels/<channel_name>/icon/no-padding`.
|
||||
The `.ico` files are generated using imagemagick:
|
||||
|
||||
```shell
|
||||
convert 16x16.png 32x32.png 48x48.png 64x64.png 256x256.png icon.ico
|
||||
```
|
||||
|
||||
Note that sizes above 256x256 are not supported.
|
||||
See the [Inno Setup docs](https://jrsoftware.org/ishelp/index.php?topic=setup_setupiconfile).
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env powershell
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Git for Windows can be installed system-wide (Program Files) or per-user (LOCALAPPDATA\Programs\Git).
|
||||
$gitBinCandidates = @(
|
||||
"$env:PROGRAMFILES\Git\bin",
|
||||
"$env:LOCALAPPDATA\Programs\Git\bin"
|
||||
)
|
||||
$gitBinDir = $gitBinCandidates | Where-Object { Test-Path -PathType Container $_ } | Select-Object -First 1
|
||||
if (-not $gitBinDir) {
|
||||
Write-Error 'Git for Windows is required. Please install it at:'
|
||||
Write-Error 'https://gitforwindows.org/'
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Get-Command -Name cargo -Type Application -ErrorAction SilentlyContinue)) {
|
||||
Write-Output 'Installing rust...'
|
||||
Invoke-WebRequest -Uri 'https://win.rustup.rs/x86_64' -OutFile "$env:Temp\rustup-init.exe"
|
||||
& "$env:Temp\rustup-init.exe"
|
||||
Write-Output 'Please start a new terminal session so that cargo is in your PATH'
|
||||
exit 1
|
||||
}
|
||||
|
||||
# A bash executable should come with Git for Windows
|
||||
& "$gitBinDir\bash.exe" "$PWD\script\install_cargo_test_deps"
|
||||
|
||||
# Needed in wasm compilation for parsing the version of wasm-bindgen
|
||||
winget install jqlang.jq
|
||||
|
||||
# CMake is needed to build some dependencies, e.g.: sentry-contrib-native.
|
||||
winget install -e --id Kitware.CMake
|
||||
|
||||
# We use InnoSetup to build our release bundle installer.
|
||||
winget install -e --id JRSoftware.InnoSetup
|
||||
|
||||
# If we don't see gcloud command, try adding the install location to the PATH.
|
||||
if (-not (Get-Command -Name gcloud -Type Application -ErrorAction SilentlyContinue)) {
|
||||
$env:PATH += ";$env:LOCALAPPDATA\Google\Cloud SDK\google-cloud-sdk\bin"
|
||||
}
|
||||
|
||||
# If we still don't see it, install it.
|
||||
if (-not (Get-Command -Name gcloud -Type Application -ErrorAction SilentlyContinue)) {
|
||||
(New-Object Net.WebClient).DownloadFile('https://dl.google.com/dl/cloudsdk/channels/rapid/GoogleCloudSDKInstaller.exe', "$env:Temp\GoogleCloudSDKInstaller.exe")
|
||||
Start-Process "$env:Temp\GoogleCloudSDKInstaller.exe" -Wait
|
||||
}
|
||||
|
||||
[string]$identityToken = gcloud auth print-identity-token
|
||||
if ($identityToken.Trim().Length -eq 0) {
|
||||
Write-Output 'gcloud CLI authentication missing. Press enter to continue...'
|
||||
Read-Host
|
||||
gcloud auth login
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env powershell
|
||||
#
|
||||
# Bundle the application for release.
|
||||
|
||||
Param (
|
||||
# Build dev bundles by default.
|
||||
[Switch]$DEBUG_BUILD = $False,
|
||||
|
||||
[Alias('check-only')]
|
||||
[Switch]$CHECK_ONLY,
|
||||
|
||||
[ValidateSet('local', 'dev', 'preview', 'stable')]
|
||||
[String]$CHANNEL = 'dev',
|
||||
|
||||
[Alias('release-tag')]
|
||||
[String]$RELEASE_TAG = '',
|
||||
[String]$FEATURES = 'release_bundle,crash_reporting,gui',
|
||||
|
||||
# Builds only the Warp binary, skips the installer.
|
||||
[Switch]$SKIP_BUILD_INSTALLER = $False,
|
||||
# Builds only the installer, skips the Warp binary. Use this if the Warp
|
||||
# binary has already been built.
|
||||
[Switch]$SKIP_BUILD_BINARY = $False,
|
||||
|
||||
[ValidateSet('x64', 'arm64')]
|
||||
[String]$ARCH = '',
|
||||
|
||||
# A signtool command for Inno Setup to sign the setup engine and uninstaller.
|
||||
# Uses $f as the file placeholder, e.g.:
|
||||
# 'signtool.exe sign /fd SHA256 ... $f'
|
||||
# When empty, the installer is built without signing.
|
||||
[Alias('sign-tool-cmd')]
|
||||
[String]$SIGN_TOOL_CMD = ''
|
||||
)
|
||||
|
||||
if ($RELEASE_TAG) {
|
||||
$env:GIT_RELEASE_TAG = $RELEASE_TAG
|
||||
}
|
||||
|
||||
# Use provided ARCH parameter if set, otherwise detect from system
|
||||
if (-not $ARCH) {
|
||||
if ($env:PROCESSOR_ARCHITECTURE -eq 'AMD64') {
|
||||
$ARCH = 'x64'
|
||||
} elseif ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') {
|
||||
$ARCH = 'arm64'
|
||||
} else {
|
||||
throw "Unsupported processor architecture: $env:PROCESSOR_ARCHITECTURE"
|
||||
}
|
||||
}
|
||||
|
||||
if ($ARCH -eq 'arm64') {
|
||||
$FILE_ENDING = 'Setup-arm64'
|
||||
$PLATFORM_TARGET = 'aarch64-pc-windows-msvc'
|
||||
} else {
|
||||
# If x64, then we just use the filename "WarpSetup.exe" for example
|
||||
$FILE_ENDING = 'Setup'
|
||||
$PLATFORM_TARGET = 'x86_64-pc-windows-msvc'
|
||||
}
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$WORKSPACE_ROOT_DIR = $(Get-Location).Path
|
||||
$CARGO_TARGET_DIR = $WORKSPACE_ROOT_DIR + '\target'
|
||||
$WINDOWS_INSTALLER_DIR = $WORKSPACE_ROOT_DIR + '\script\windows'
|
||||
|
||||
if ($DEBUG_BUILD) {
|
||||
$CARGO_PROFILE = 'dev'
|
||||
} elseif (("$CHANNEL" -eq 'local') -or ("$CHANNEL" -eq 'dev')) {
|
||||
# For dev bundles, we want to enable debug assertions to
|
||||
# catch violations that would otherwise silently pass in
|
||||
# a normal release build (e.g. in stable).
|
||||
$CARGO_PROFILE = 'rltoda'
|
||||
} else {
|
||||
$CARGO_PROFILE = 'rlto'
|
||||
}
|
||||
|
||||
if ($CARGO_PROFILE -eq 'dev') {
|
||||
$CARGO_TARGET_OUTPUT_DIR = "$CARGO_TARGET_DIR" + '\' + $PLATFORM_TARGET + '\debug'
|
||||
} else {
|
||||
$CARGO_TARGET_OUTPUT_DIR = "$CARGO_TARGET_DIR" + '\' + $PLATFORM_TARGET + '\' + "$CARGO_PROFILE"
|
||||
}
|
||||
$BUNDLE_ID = "dev.warp.$app_name"
|
||||
|
||||
# Update parameters based on the target release channel.
|
||||
#
|
||||
# APP_NAME here must match the value used in Rust as the
|
||||
# application name; see app/src/channel.rs.
|
||||
#
|
||||
# WARP_BIN is the name of the binary produced by cargo;
|
||||
# BINARY_NAME is the desired name of the binary in the final package.
|
||||
if ("$CHANNEL" -eq 'local') {
|
||||
$WARP_BIN = 'warp'
|
||||
$BINARY_NAME = 'warp.exe'
|
||||
$APP_NAME = 'WarpLocal'
|
||||
$FEATURES = "$FEATURES,nld_improvements"
|
||||
} elseif ("$CHANNEL" -eq 'dev') {
|
||||
$WARP_BIN = 'dev'
|
||||
$BINARY_NAME = 'dev.exe'
|
||||
$APP_NAME = 'WarpDev'
|
||||
$FEATURES = "$FEATURES,agent_mode_debug,nld_improvements"
|
||||
} elseif ("$CHANNEL" -eq 'preview') {
|
||||
$WARP_BIN = 'preview'
|
||||
$BINARY_NAME = 'preview.exe'
|
||||
$APP_NAME = 'WarpPreview'
|
||||
$FEATURES = "$FEATURES,preview_channel,nld_improvements"
|
||||
} elseif ("$CHANNEL" -eq 'stable') {
|
||||
$WARP_BIN = 'stable'
|
||||
$BINARY_NAME = 'warp.exe'
|
||||
$APP_NAME = 'Warp'
|
||||
# TODO(vorporeal): Remove this once we get tests passing with this default enabled.
|
||||
$FEATURES = "$FEATURES,nld_improvements"
|
||||
}
|
||||
|
||||
$BINARY_PATH = "$CARGO_TARGET_OUTPUT_DIR\$BINARY_NAME"
|
||||
$BUNDLE_ID = "dev.warp.$APP_NAME"
|
||||
$INSTALLER_OUTPUT_DIR = "$WINDOWS_INSTALLER_DIR\Output"
|
||||
$INSTALLER_NAME = "$($APP_NAME)$($FILE_ENDING)"
|
||||
$INSTALLER_PATH = "$($INSTALLER_OUTPUT_DIR)\$($INSTALLER_NAME).exe"
|
||||
$PDB_PATH = "$CARGO_TARGET_OUTPUT_DIR\$WARP_BIN.pdb"
|
||||
|
||||
# The CARGO_FULL_PROFILE environment variable is read by the `cargo` build
|
||||
# script (`app/build.rs`) to determine where to place `conpty.dll`.
|
||||
if ($DEBUG_BUILD) {
|
||||
$env:CARGO_FULL_PROFILE = 'debug'
|
||||
} else {
|
||||
$env:CARGO_FULL_PROFILE = $CARGO_PROFILE
|
||||
}
|
||||
|
||||
# If we only want to check that compilation will succeed, perform the checks
|
||||
# then exit. We use this script to invoke `cargo check` to ensure that we are
|
||||
# using the same feature flags and profile that we would be using in production.
|
||||
if ($CHECK_ONLY) {
|
||||
cargo check -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES" --target $PLATFORM_TARGET
|
||||
if (-Not $?) {
|
||||
Write-Error "Failed to verify Warp $WARP_BIN compilation with profile $CARGO_PROFILE"
|
||||
exit 1
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-Not $SKIP_BUILD_BINARY) {
|
||||
Write-Output "Building Warp for channel $CHANNEL and bundle id $BUNDLE_ID"
|
||||
$env:CARGO_BIN_NAME = $CHANNEL
|
||||
$env:WARP_APP_NAME = $APP_NAME
|
||||
cargo build -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES" --target $PLATFORM_TARGET
|
||||
if (-Not $?) {
|
||||
Write-Error "Failed to build Warp $WARP_BIN binary with profile $CARGO_PROFILE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If we desire an executable name different from the cargo bin, rename it.
|
||||
if ("$WARP_BIN.exe" -ne $BINARY_NAME) {
|
||||
$binarySource = "$CARGO_TARGET_OUTPUT_DIR\$WARP_BIN.exe"
|
||||
Write-Output "Renaming executable $WARP_BIN.exe to $BINARY_NAME"
|
||||
Move-Item -Path "$binarySource" -Destination "$BINARY_PATH" -Force
|
||||
}
|
||||
}
|
||||
|
||||
if ($SKIP_BUILD_INSTALLER) {
|
||||
# If this is being run within a GitHub action, set an output variable with the
|
||||
# location of the binary so it can be referenced by subsequent actions.
|
||||
if ($env:GITHUB_ACTIONS -eq 'true') {
|
||||
Write-Output '::echo::on'
|
||||
"target_profile_dir=$CARGO_TARGET_OUTPUT_DIR" >> "$env:GITHUB_OUTPUT"
|
||||
"binary_path=$BINARY_PATH" >> "$env:GITHUB_OUTPUT"
|
||||
Write-Output '::echo::off'
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Output "Built for $ARCH with executable at $BINARY_PATH"
|
||||
|
||||
# Prepare bundled resources
|
||||
$BUNDLED_RESOURCES_DIR = "$CARGO_TARGET_OUTPUT_DIR\resources"
|
||||
Write-Output "Preparing bundled resources..."
|
||||
& "$WINDOWS_INSTALLER_DIR\prepare_bundled_resources.ps1" -DestinationDir "$BUNDLED_RESOURCES_DIR" -Channel "$CHANNEL" -CargoProfile "$CARGO_PROFILE"
|
||||
if (-Not $?) {
|
||||
Write-Error "Failed to prepare bundled resources"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Output 'Building Warp installer'
|
||||
$ISCC_ARGS = @(
|
||||
"$WINDOWS_INSTALLER_DIR\windows-installer.iss",
|
||||
"/DReleaseChannel=$CHANNEL",
|
||||
"/DMyAppExeName=$BINARY_NAME",
|
||||
"/DTargetProfileDir=$CARGO_TARGET_OUTPUT_DIR",
|
||||
"/DMyAppName=$APP_NAME",
|
||||
"/DMyAppVersion=$env:GIT_RELEASE_TAG",
|
||||
"/DArch=$ARCH",
|
||||
"/DOutputName=$INSTALLER_NAME"
|
||||
)
|
||||
# Also accept the sign tool command via env var
|
||||
if (-not $SIGN_TOOL_CMD -and $env:SIGN_TOOL_CMD) {
|
||||
$SIGN_TOOL_CMD = $env:SIGN_TOOL_CMD
|
||||
}
|
||||
if ($SIGN_TOOL_CMD) {
|
||||
$ISCC_ARGS += '/DSIGN_TOOL=1'
|
||||
$ISCC_ARGS += "/Scodesign=$SIGN_TOOL_CMD"
|
||||
}
|
||||
& ISCC @ISCC_ARGS
|
||||
if (-Not $?) {
|
||||
Write-Error "Failed to build $APP_NAME installer"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If this is being run within a GitHub action, set an output variable with the
|
||||
# location of the installer so it can be referenced by subsequent actions.
|
||||
if ($env:GITHUB_ACTIONS -eq 'true') {
|
||||
Write-Output '::echo::on'
|
||||
$INSTALLER_PATH = $INSTALLER_PATH -replace '\\', '/'
|
||||
"installer_path=$INSTALLER_PATH" >> "$env:GITHUB_OUTPUT"
|
||||
"pdb_file_path=$PDB_PATH" >> "$env:GITHUB_OUTPUT"
|
||||
Write-Output '::echo::off'
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
[Code]
|
||||
{ Adapted from https://stackoverflow.com/a/46609047 }
|
||||
|
||||
const
|
||||
SystemEnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
|
||||
UserEnvironmentKey = 'Environment';
|
||||
|
||||
{ Get the appropriate registry key and root based on install mode. }
|
||||
procedure GetEnvironmentKeyInfo(var RootKey: Integer; var SubKey: string);
|
||||
begin
|
||||
if IsAdminInstallMode then begin
|
||||
RootKey := HKEY_LOCAL_MACHINE;
|
||||
SubKey := SystemEnvironmentKey;
|
||||
end else begin
|
||||
RootKey := HKEY_CURRENT_USER;
|
||||
SubKey := UserEnvironmentKey;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Add path to environment PATH variable. }
|
||||
procedure EnvAddPath(Path: string);
|
||||
var
|
||||
Paths: string;
|
||||
RootKey: Integer;
|
||||
SubKey: string;
|
||||
begin
|
||||
{ Get the appropriate registry location }
|
||||
GetEnvironmentKeyInfo(RootKey, SubKey);
|
||||
|
||||
{ Retrieve current path (use empty string if entry not exists) }
|
||||
if not RegQueryStringValue(RootKey, SubKey, 'Path', Paths)
|
||||
then Paths := '';
|
||||
|
||||
{ Skip if string already found in path }
|
||||
if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit;
|
||||
|
||||
{ Append string to the end of the path variable }
|
||||
Paths := Paths + ';'+ Path +';'
|
||||
|
||||
{ Overwrite (or create if missing) path environment variable }
|
||||
if RegWriteStringValue(RootKey, SubKey, 'Path', Paths)
|
||||
then Log(Format('Added [%s] to PATH: [%s]', [Path, Paths]))
|
||||
else Log(Format('Error adding [%s] to PATH: [%s]', [Path, Paths]));
|
||||
end;
|
||||
|
||||
{ Remove path from environment PATH variable. }
|
||||
procedure EnvRemovePath(Path: string);
|
||||
var
|
||||
Paths: string;
|
||||
P: Integer;
|
||||
RootKey: Integer;
|
||||
SubKey: string;
|
||||
begin
|
||||
{ Get the appropriate registry location }
|
||||
GetEnvironmentKeyInfo(RootKey, SubKey);
|
||||
|
||||
{ Skip if registry entry not exists }
|
||||
if not RegQueryStringValue(RootKey, SubKey, 'Path', Paths) then
|
||||
exit;
|
||||
|
||||
{ Skip if string not found in path }
|
||||
P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
|
||||
if P = 0 then exit;
|
||||
|
||||
{ Update path variable }
|
||||
Delete(Paths, P - 1, Length(Path) + 1);
|
||||
|
||||
{ Overwrite path environment variable }
|
||||
if RegWriteStringValue(RootKey, SubKey, 'Path', Paths)
|
||||
then Log(Format('Removed [%s] from PATH: [%s]', [Path, Paths]))
|
||||
else Log(Format('Error removing [%s] from PATH: [%s]', [Path, Paths]));
|
||||
end;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env powershell
|
||||
#
|
||||
# Install all dependencies required to build Warp on Windows.
|
||||
|
||||
# Install Rust + cargo.
|
||||
bash (((Get-Location).path) + '\script\install_rust')
|
||||
|
||||
# Install various build-time dependencies through cargo.
|
||||
bash (((Get-Location).path) + '\script\install_cargo_build_deps')
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,183 @@
|
||||
#
|
||||
# Prepares bundled resources for distribution on Windows.
|
||||
#
|
||||
# This script copies resources that should be bundled with Warp into a
|
||||
# destination directory. It is used by the Windows build script.
|
||||
#
|
||||
# Usage:
|
||||
# prepare_bundled_resources.ps1 <destination_directory>
|
||||
#
|
||||
# Arguments:
|
||||
# destination_directory: The directory where resources should be installed.
|
||||
# Resources will be copied to subdirectories within
|
||||
# this path (e.g., $DEST_DIR\skills).
|
||||
|
||||
Param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[String]$DestinationDir,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]$Channel = '',
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]$CargoProfile = ''
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$RepoRoot = (Get-Item "$ScriptDir\..\.." | Select-Object -ExpandProperty FullName)
|
||||
$ResourcesSource = Join-Path $RepoRoot 'resources'
|
||||
|
||||
# Validate that the source resources directory exists
|
||||
if (-Not (Test-Path $ResourcesSource -PathType Container)) {
|
||||
Write-Error "Resources directory not found at $ResourcesSource"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Create the destination directory if it doesn't exist
|
||||
if (-Not (Test-Path $DestinationDir)) {
|
||||
New-Item -ItemType Directory -Path $DestinationDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# Copy bundled resources
|
||||
$BundledSource = Join-Path $ResourcesSource 'bundled'
|
||||
if (Test-Path $BundledSource -PathType Container) {
|
||||
$BundledDestination = Join-Path $DestinationDir 'bundled'
|
||||
Write-Output "Copying bundled resources to $BundledDestination"
|
||||
if (Test-Path $BundledDestination -PathType Container) {
|
||||
Remove-Item -Path $BundledDestination -Recurse -Force
|
||||
}
|
||||
Copy-Item -Path $BundledSource -Destination $BundledDestination -Recurse -Force
|
||||
} else {
|
||||
Write-Warning "No bundled directory found at $BundledSource"
|
||||
}
|
||||
|
||||
if ($env:GIT_RELEASE_TAG) {
|
||||
$VersionMetadataDir = Join-Path (Join-Path $DestinationDir 'bundled') 'metadata'
|
||||
$VersionMetadataPath = Join-Path $VersionMetadataDir 'version.json'
|
||||
Write-Output "Writing bundled Warp version metadata to $VersionMetadataPath"
|
||||
if (-Not (Test-Path $VersionMetadataDir -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $VersionMetadataDir -Force | Out-Null
|
||||
}
|
||||
|
||||
@{ warp_version = $env:GIT_RELEASE_TAG } |
|
||||
ConvertTo-Json |
|
||||
Set-Content -Path $VersionMetadataPath -Encoding utf8
|
||||
}
|
||||
|
||||
# Copy channel-gated skills matching the current release channel.
|
||||
$GatedSource = Join-Path (Join-Path $RepoRoot 'resources') 'channel-gated-skills'
|
||||
$DestSkills = Join-Path (Join-Path $DestinationDir 'bundled') 'skills'
|
||||
|
||||
if ($Channel -and (Test-Path $GatedSource -PathType Container)) {
|
||||
Write-Output "Copying channel-gated skills for channel '$Channel'..."
|
||||
|
||||
# Error out if a stable/ gate directory exists.
|
||||
$StableDir = Join-Path $GatedSource 'stable'
|
||||
if (Test-Path $StableDir -PathType Container) {
|
||||
Write-Error "Found a 'stable/' directory in $GatedSource. The stable channel does not use gated skills. Move stable-ready skills to resources/skills/ instead."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Gate labels ordered from most-inclusive to least-inclusive.
|
||||
$GateOrder = @('dogfood', 'preview')
|
||||
|
||||
# Map the release channel to its gate label.
|
||||
switch ($Channel) {
|
||||
'local' { $Gate = 'dogfood' }
|
||||
'dev' { $Gate = 'dogfood' }
|
||||
'preview' { $Gate = 'preview' }
|
||||
default {
|
||||
Write-Output " Channel '$Channel' has no gated skills, skipping"
|
||||
$Gate = $null
|
||||
}
|
||||
}
|
||||
|
||||
if ($Gate) {
|
||||
# Build the set of included gates (progressive: this gate and all after it).
|
||||
$GateIndex = [array]::IndexOf($GateOrder, $Gate)
|
||||
$IncludedGates = $GateOrder[$GateIndex..($GateOrder.Length - 1)]
|
||||
|
||||
foreach ($GateDir in Get-ChildItem -Path $GatedSource -Directory | Sort-Object Name) {
|
||||
if ($GateDir.Name -notin $IncludedGates) {
|
||||
$Skills = (Get-ChildItem -Path $GateDir.FullName -Directory | Sort-Object Name | ForEach-Object { $_.Name }) -join ', '
|
||||
Write-Output " Skipping gate '$($GateDir.Name)' (channel '$Channel') - would include: $Skills"
|
||||
continue
|
||||
}
|
||||
|
||||
foreach ($SkillDir in Get-ChildItem -Path $GateDir.FullName -Directory | Sort-Object Name) {
|
||||
$Dest = Join-Path $DestSkills $SkillDir.Name
|
||||
Write-Output " Copying gated skill: $($SkillDir.Name) (gate: $($GateDir.Name))"
|
||||
Copy-Item -Path $SkillDir.FullName -Destination $Dest -Recurse -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Generate third-party license attribution.
|
||||
#
|
||||
# Additional (non-Cargo) third-party license files to include in the output.
|
||||
# When adding a new third-party component to the bundle, add its license file
|
||||
# to the repo alongside the component and add an entry here.
|
||||
# Cross-platform components:
|
||||
$AdditionalLicenses = @(
|
||||
@{ Name = 'Hack Font'; License = 'MIT'; Path = 'app\assets\bundled\fonts\hack\LICENSE.md' },
|
||||
@{ Name = 'Roboto Font'; License = 'SIL Open Font License'; Path = 'app\assets\bundled\fonts\roboto\LICENSE.txt' },
|
||||
@{ Name = 'bash-preexec'; License = 'MIT'; Path = 'app\assets\bundled\bootstrap\bash-preexec-LICENSE.md' },
|
||||
@{ Name = 'Claude API Skill'; License = 'Apache-2.0'; Path = 'resources\bundled\skills\claude-api\LICENSE.txt' },
|
||||
@{ Name = 'rudder-sdk-rust'; License = 'MIT'; Path = 'app\src\server\telemetry\LICENSE-RUDDER-SDK-RUST.txt' },
|
||||
@{ Name = 'Windows Terminal'; License = 'MIT'; Path = 'app\assets\windows\LICENSE-WINDOWS-TERMINAL' },
|
||||
@{ Name = 'GitHub Desktop'; License = 'MIT'; Path = 'app\src\code_review\GITHUB-DESKTOP-LICENSE' }
|
||||
)
|
||||
# Windows-only components:
|
||||
$AdditionalLicenses += @(
|
||||
@{ Name = 'OpenConsole / ConPTY (Windows Terminal)'; License = 'MIT'; Path = 'app\assets\windows\LICENSE-WINDOWS-TERMINAL' },
|
||||
@{ Name = 'DirectX Shader Compiler'; License = 'NCSA'; Path = 'app\assets\windows\LICENSE-DXC' }
|
||||
)
|
||||
|
||||
$LicensesOutput = Join-Path $DestinationDir 'THIRD_PARTY_LICENSES.txt'
|
||||
Write-Output "Generating third-party licenses at $LicensesOutput"
|
||||
cargo about generate --workspace --manifest-path "$RepoRoot\Cargo.toml" -c "$RepoRoot\about.toml" -o "$LicensesOutput" "$RepoRoot\about.hbs"
|
||||
if (-Not $?) {
|
||||
Write-Error 'Failed to generate third-party licenses'
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Append additional (non-Cargo) third-party licenses.
|
||||
foreach ($entry in $AdditionalLicenses) {
|
||||
$LicenseFile = Join-Path $RepoRoot $entry.Path
|
||||
if (-Not (Test-Path $LicenseFile)) {
|
||||
Write-Error "License file not found: $LicenseFile"
|
||||
exit 1
|
||||
}
|
||||
Add-Content -Path $LicensesOutput -Value ''
|
||||
Add-Content -Path $LicensesOutput -Value "$($entry.Name) ($($entry.License))"
|
||||
Add-Content -Path $LicensesOutput -Value ('-' * 80)
|
||||
Get-Content -Path $LicenseFile | Add-Content -Path $LicensesOutput
|
||||
Add-Content -Path $LicensesOutput -Value ''
|
||||
}
|
||||
|
||||
# Generate settings JSON schema unless explicitly skipped.
|
||||
if ($env:SKIP_SETTINGS_SCHEMA -ne '1') {
|
||||
$SchemaOutput = Join-Path $DestinationDir 'settings_schema.json'
|
||||
Write-Output "Generating settings schema at $SchemaOutput"
|
||||
|
||||
$SchemaCmd = @('run')
|
||||
if ($CargoProfile) {
|
||||
$SchemaCmd += @('--profile', $CargoProfile)
|
||||
}
|
||||
$SchemaCmd += @('--manifest-path', (Join-Path $RepoRoot 'Cargo.toml'), '--bin', 'generate_settings_schema', '--')
|
||||
if ($Channel) {
|
||||
$SchemaCmd += @('--channel', $Channel)
|
||||
}
|
||||
$SchemaCmd += $SchemaOutput
|
||||
|
||||
& cargo @SchemaCmd
|
||||
if (-Not $?) {
|
||||
Write-Error 'Failed to generate settings schema'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "Successfully prepared bundled resources in $DestinationDir"
|
||||
@@ -0,0 +1,250 @@
|
||||
; Script generated by the Inno Setup Script Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
#include "environment.iss"
|
||||
|
||||
#define MyAppPublisher "Denver Technologies, Inc."
|
||||
#define MyAppURL "https://www.warp.dev/"
|
||||
#ifndef MyAppName
|
||||
#define MyAppName "WarpDev"
|
||||
#endif
|
||||
#ifndef MyAppVersion
|
||||
#define MyAppVersion "0.1.0"
|
||||
#endif
|
||||
#ifndef MyAppExeName
|
||||
#define MyAppExeName "dev.exe"
|
||||
#endif
|
||||
#ifndef ReleaseChannel
|
||||
#define ReleaseChannel "dev"
|
||||
#endif
|
||||
#ifndef TargetProfileDir
|
||||
#define TargetProfileDir "target\release-lto-debug_assertions"
|
||||
#endif
|
||||
#define AssetsDir "..\..\app\assets\windows"
|
||||
|
||||
// The mutex name must match what the Rust app creates in single_instance_manager.rs:
|
||||
#define ChannelPascalCase \
|
||||
(ReleaseChannel == "stable") ? "Stable" : \
|
||||
((ReleaseChannel == "dev") ? "Dev" : \
|
||||
((ReleaseChannel == "preview") ? "Preview" : \
|
||||
((ReleaseChannel == "local") ? "Local" : \
|
||||
((ReleaseChannel == "integration") ? "Integration" : \
|
||||
"Unknown"))))
|
||||
#define AppMutexName "Local\Warp" + ChannelPascalCase + "_SingleInstance"
|
||||
|
||||
|
||||
[Setup]
|
||||
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
AppId=warp-terminal-{#ReleaseChannel}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppVerName={#MyAppName} {#MyAppVersion}
|
||||
UninstallDisplayName={#MyAppName}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={autopf}\{#MyAppName}
|
||||
ArchitecturesAllowed={#Arch}
|
||||
ArchitecturesInstallIn64BitMode={#Arch}
|
||||
DisableProgramGroupPage=yes
|
||||
; The following line defaults the installer to use non administrative install mode (install for current user only).
|
||||
PrivilegesRequired=lowest
|
||||
; Allow the user to choose administrative install mode (install for all users).
|
||||
PrivilegesRequiredOverridesAllowed=dialog
|
||||
OutputBaseFilename={#OutputName}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
WizardSmallImageFile="installer-images\warp-logo.bmp"
|
||||
WizardImageFile="installer-images\warp-banner.bmp"
|
||||
SetupIconFile="..\..\app\channels\{#ReleaseChannel}\icon\no-padding\icon.ico"
|
||||
UninstallDisplayIcon="{app}\icon.ico"
|
||||
; Force close previous Warp if it hasn't shut down yet.
|
||||
; In the update flow we already warn the user if they have something running and make them confirm
|
||||
; before running this installer. Therefore, we are good to force close Warp without fear of losing
|
||||
; unsaved work.
|
||||
; VSCode does something similar:
|
||||
; https://github.com/microsoft/vscode/blob/aac9914f93551f894b8df1e4680bd847e7636be3/build/win32/code.iss#L41
|
||||
CloseApplications=force
|
||||
; For manual installs: if Warp is running, show a dialog prompting the user to close it
|
||||
; before Setup proceeds. Returned empty for background updates so the check is skipped.
|
||||
; TODO(andy) uncomment this after the 4/22 release
|
||||
;AppMutex={code:GetAppMutex}
|
||||
SetupMutex={#AppMutexName}Setup
|
||||
; Version 1809 / Build 18362 is required for ConPTY. See https://github.com/microsoft/vscode-docs/blob/9d736b662fdde3fed17d8bc2ed70bfea4ae20636/docs/supporting/troubleshoot-terminal-launch.md?plain=1#L66/
|
||||
MinVersion=10.0.18362
|
||||
; Tell Windows Explorer to reload the environment so that path changes take effect.
|
||||
ChangesEnvironment=true
|
||||
; Sign the setup engine and uninstaller so that the temporary bootstrapper
|
||||
; extracted to %TEMP% is Authenticode-signed. This prevents Microsoft Defender
|
||||
; ASR rule D4F940AB from blocking the installer in enterprise environments.
|
||||
; The sign tool command is supplied via ISCC /S on the command line; when not
|
||||
; defined (e.g. local dev builds) signing is skipped.
|
||||
#ifdef SIGN_TOOL
|
||||
SignTool=codesign
|
||||
SignedUninstaller=yes
|
||||
#endif
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
|
||||
|
||||
[Files]
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
Source: "{#TargetProfileDir}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#AssetsDir}\{#Arch}\conpty.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#AssetsDir}\{#Arch}\OpenConsole.exe"; DestDir: "{app}\{#Arch}"; Flags: ignoreversion
|
||||
Source: "..\..\app\channels\{#ReleaseChannel}\icon\no-padding\icon.ico"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#AssetsDir}\{#Arch}\vcruntime140.dll"; DestDir: "{app}"
|
||||
Source: "{#AssetsDir}\{#Arch}\vcruntime140_1.dll"; DestDir: "{app}"
|
||||
Source: "{#AssetsDir}\{#Arch}\msvcp140.dll"; DestDir: "{app}"
|
||||
Source: "..\..\app\assets\bundled\bootstrap\pwsh.ps1"; DestDir: "{app}"
|
||||
Source: "{#AssetsDir}\{#Arch}\dxcompiler.dll"; DestDir: "{app}"
|
||||
Source: "{#AssetsDir}\{#Arch}\dxil.dll"; DestDir: "{app}"
|
||||
Source: "{#TargetProfileDir}\resources\*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs
|
||||
|
||||
[Registry]
|
||||
Root: HKCU; Subkey: "SOFTWARE\Warp.dev\{#MyAppName}"; Flags: uninsdeletekey
|
||||
; cleanup "Open Warp Here" registry entries
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}"; Flags: deletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}"; Flags: deletekey
|
||||
; Add "Open Warp in new tab" to directory context menu
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Tab"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new tab"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Tab"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Tab\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_tab?path=%1"""
|
||||
; Add "Open Warp in new tab" to directory background context menu
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Tab"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new tab"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Tab"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Tab\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_tab?path=%V"""
|
||||
; Add "Open Warp in new window" to directory context menu
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Window"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new window"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Window"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Window\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_window?path=%1"""
|
||||
; Add "Open Warp in new window" to directory background context menu
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Window"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new window"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Window"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Window\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_window?path=%V"""
|
||||
|
||||
[Tasks]
|
||||
Name: addToPath; Description: "Add Warp to PATH"
|
||||
|
||||
[UninstallDelete]
|
||||
Type: filesandordirs; Name: "{userappdata}\warp\{#MyAppName}"
|
||||
Type: filesandordirs; Name: "{localappdata}\warp\{#MyAppName}"
|
||||
Type: filesandordirs; Name: "{app}\bin"
|
||||
|
||||
[Icons]
|
||||
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\icon.ico"; AppUserModelID: "dev.warp.{#MyAppName}"
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\icon.ico"; AppUserModelID: "dev.warp.{#MyAppName}"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: postinstall runhidden nowait
|
||||
|
||||
[Code]
|
||||
function IsNotStable(): Boolean;
|
||||
begin
|
||||
#if ReleaseChannel == "stable"
|
||||
Result := False;
|
||||
#else
|
||||
Result := True;
|
||||
#endif
|
||||
end;
|
||||
|
||||
{ Returns true when the installer was launched by Warp's auto-update code.
|
||||
The auto-update path passes /update=1 on the command line and /NOCLOSEAPPLICATIONS
|
||||
so that the installer does not forcibly kill the running Warp process. Instead we
|
||||
wait for Warp to exit naturally by polling the app mutex below. }
|
||||
function IsBackgroundUpdate(): Boolean;
|
||||
begin
|
||||
Result := ExpandConstant('{param:update|false}') <> 'false';
|
||||
end;
|
||||
|
||||
{ For background updates, return an empty mutex name so that Inno Setup skips its
|
||||
built-in "application is running" dialog - we handle the wait ourselves. For manual
|
||||
installs, return the real mutex name so the user is prompted to close Warp first. }
|
||||
function GetAppMutex(Value: string): string;
|
||||
begin
|
||||
if IsBackgroundUpdate() then
|
||||
Result := ''
|
||||
else
|
||||
Result := '{#AppMutexName}';
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
var
|
||||
BinDir: string;
|
||||
CmdScriptName: string;
|
||||
CmdScriptPath: string;
|
||||
CmdScriptContent: string;
|
||||
WaitCounter: Integer;
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
{ Background update: the installer was launched while Warp was still running.
|
||||
We passed /NOCLOSEAPPLICATIONS so Inno won't kill it. Wait here - before any
|
||||
files are touched - for Warp to release its single-instance mutex, which
|
||||
happens as part of normal process exit. }
|
||||
if CurStep = ssInstall then
|
||||
begin
|
||||
if IsBackgroundUpdate() then
|
||||
begin
|
||||
Log('Background update: waiting for Warp to exit (mutex: {#AppMutexName})...');
|
||||
WaitCounter := 0;
|
||||
while CheckForMutexes('{#AppMutexName}') and (WaitCounter < 30) do
|
||||
begin
|
||||
Sleep(500);
|
||||
WaitCounter := WaitCounter + 1;
|
||||
end;
|
||||
if CheckForMutexes('{#AppMutexName}') then
|
||||
begin
|
||||
Log('Warp mutex still held after timeout; force-killing remaining processes.');
|
||||
{ Kill by image name. {#MyAppExeName} (e.g. warp.exe, dev.exe) is unique
|
||||
enough that collateral damage is not a concern. OpenConsole.exe is NOT
|
||||
killed by name because it is shared with Windows Terminal; instead we
|
||||
rely on Warp's Job Object (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) to
|
||||
cascade-kill any child OpenConsole.exe processes when warp.exe dies. }
|
||||
Exec('taskkill.exe', '/f /im {#MyAppExeName}', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
if ResultCode <> 0 then
|
||||
Log('force-kill failed for {#MyAppExeName} (exit code: ' + IntToStr(ResultCode) + ')');
|
||||
Sleep(1000);
|
||||
end
|
||||
else
|
||||
Log('Warp has exited; proceeding with file installation.');
|
||||
end;
|
||||
end;
|
||||
|
||||
{ After a successful install, write a helper script for running the Warp CLI. }
|
||||
{ We use this to add a "warp-" prefix (e.g. "warp-preview.cmd" vs. "preview.exe") }
|
||||
if CurStep = ssPostInstall then begin
|
||||
{ Add Warp to PATH if requested }
|
||||
if IsTaskSelected('addToPath') then
|
||||
EnvAddPath(ExpandConstant('{app}\bin'));
|
||||
|
||||
BinDir := ExpandConstant('{app}\bin');
|
||||
if not DirExists(BinDir) then
|
||||
CreateDir(BinDir);
|
||||
|
||||
{ Determine the channel-specific script name. }
|
||||
#if ReleaseChannel == "stable"
|
||||
CmdScriptName := 'oz.cmd'
|
||||
#else
|
||||
CmdScriptName := 'oz-{#ReleaseChannel}.cmd';
|
||||
#endif
|
||||
|
||||
{ Create the helper CMD script }
|
||||
CmdScriptPath := BinDir + '\' + CmdScriptName;
|
||||
CmdScriptContent := '@echo off' + #13#10 +
|
||||
'set "WARP_CLI_MODE=1"' + #13#10 +
|
||||
'"' + ExpandConstant('{app}\{#MyAppExeName}') + '" %*' + #13#10;
|
||||
|
||||
SaveStringToFile(CmdScriptPath, CmdScriptContent, False);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||
begin
|
||||
if CurUninstallStep = usPostUninstall then
|
||||
EnvRemovePath(ExpandConstant('{app}\bin'));
|
||||
end;
|
||||
Reference in New Issue
Block a user