name: Windows Installer Packaging (Inno Setup) on: release: types: [published] workflow_dispatch: inputs: tag_name: description: 'Release Tag (e.g., v1.0.0). Leave empty to use "latest".' required: false default: '' jobs: package-windows: runs-on: windows-latest permissions: contents: write steps: - name: Checkout Repository uses: actions/checkout@v4 - name: Get Version from Cargo.toml id: get_version shell: pwsh run: | $VersionLine = Get-Content Cargo.toml | Select-String '^version\s*=\s*"(.*)"' if ($VersionLine.Matches.Success) { $Version = $VersionLine.Matches.Groups[1].Value Write-Host "Found version in Cargo.toml: $Version" "version=$Version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append } else { Write-Error "Could not find version in Cargo.toml" exit 1 } - name: Determine Release Tag id: get_tag shell: pwsh env: GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | $TAG = "${{ inputs.tag_name }}" if (-not $TAG) { $TAG = "${{ github.event.release.tag_name }}" } if (-not $TAG) { Write-Host "No tag provided, fetching latest release..." $TAG = $(gh release view --json tagName --jq .tagName) } if (-not $TAG) { Write-Error "Could not determine release tag." exit 1 } Write-Host "Using Tag: $TAG" "tag=$TAG" | Out-File -FilePath $env:GITHUB_OUTPUT -Append - name: Download Release Assets env: GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | $TAG = "${{ steps.get_tag.outputs.tag }}" $Version = "${{ steps.get_version.outputs.version }}" # Download the raw binaries gh release download $TAG -p "mikusays-windows-x86_64-$Version.exe" gh release download $TAG -p "mikusays-windows-aarch64-$Version.exe" # Rename to standard names for the Inno Script mv "mikusays-windows-x86_64-$Version.exe" "mikusays-windows-x86_64.exe" mv "mikusays-windows-aarch64-$Version.exe" "mikusays-windows-aarch64.exe" - name: Generate Inno Setup Script shell: pwsh run: | $Version = "${{ steps.get_version.outputs.version }}" $InstallerName = "mikusays-windows-inst-any-$Version" Write-Host "Generating Inno Setup script for version: $Version" $issContent = @' [Setup] AppName=Mikusays AppVersion=__VERSION__ AppCopyright=2025 xxanqw AppPublisher=xxanqw AppPublisherURL=https://xxanqw.pp.ua/ AppSupportURL=https://github.com/xxanqw/mikusays/issues AppUpdatesURL=https://github.com/xxanqw/mikusays/releases/latest AppId={{A0ED7972-D08B-48CB-8331-729D1001FF16} DefaultDirName={localappdata}\Programs\mikusays DefaultGroupName=Mikusays DisableProgramGroupPage=yes PrivilegesRequired=lowest ; -- Output Configuration -- OutputBaseFilename=__OUTFILE__ OutputDir=. Compression=lzma2/ultra64 SolidCompression=yes ; -- Architecture Configuration -- ArchitecturesInstallIn64BitMode=x64compatible arm64 ArchitecturesAllowed=x64compatible arm64 ChangesEnvironment=yes [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Files] Source: "mikusays-windows-x86_64.exe"; DestDir: "{app}"; DestName: "mikusays.exe"; Check: IsX64; Flags: ignoreversion Source: "mikusays-windows-aarch64.exe"; DestDir: "{app}"; DestName: "mikusays.exe"; Check: IsARM64; Flags: ignoreversion [Icons] Name: "{group}\Mikusays"; Filename: "{app}\mikusays.exe" Name: "{group}\Uninstall Mikusays"; Filename: "{uninstallexe}" [Code] function IsX64: Boolean; begin Result := Is64BitInstallMode and (ProcessorArchitecture = paX64); end; function IsARM64: Boolean; begin Result := Is64BitInstallMode and (ProcessorArchitecture = paARM64); end; const VC_REDIST_X64_URL = 'https://aka.ms/vs/17/release/vc_redist.x64.exe'; VC_REDIST_ARM64_URL = 'https://aka.ms/vs/17/release/vc_redist.arm64.exe'; var DownloadPage: TDownloadWizardPage; procedure InitializeWizard; begin DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil); end; function NextButtonClick(CurPageID: Integer): Boolean; var RedistUrl, RedistFile: String; ResultCode: Integer; begin Result := True; if CurPageID = wpReady then begin DownloadPage.Clear; if IsARM64 then RedistUrl := VC_REDIST_ARM64_URL else RedistUrl := VC_REDIST_X64_URL; RedistFile := 'vc_redist.exe'; DownloadPage.Add(RedistUrl, RedistFile, ''); DownloadPage.Show; try try DownloadPage.Download; Exec(ExpandConstant('{tmp}\' + RedistFile), '/install /quiet /norestart', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); except if MsgBox('Download of Visual C++ Redistributable failed: ' + GetExceptionMessage + #13#10#13#10 + 'Do you want to continue installation anyway?', mbError, MB_YESNO) = IDNO then Result := False; end; finally DownloadPage.Hide; end; end; end; function NeedsAddPath(Param: string): boolean; var OrigPath: string; begin if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath) then begin Result := True; exit; end; Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0; end; procedure RemovePath(Param: string); var OrigPath: string; P: Integer; begin if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath) then exit; P := Pos(';' + Param + ';', OrigPath); if P > 0 then begin Delete(OrigPath, P + 1, Length(Param) + 1); RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath); exit; end; if Pos(Param + ';', OrigPath) = 1 then begin Delete(OrigPath, 1, Length(Param) + 1); RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath); exit; end; P := Pos(';' + Param, OrigPath); if (P > 0) and (P = Length(OrigPath) - Length(Param)) then begin Delete(OrigPath, P, Length(Param) + 1); RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath); end; end; procedure CurStepChanged(CurStep: TSetupStep); var OrigPath: string; begin if CurStep = ssPostInstall then begin if NeedsAddPath(ExpandConstant('{app}')) then begin if RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath) then begin if OrigPath = '' then OrigPath := ExpandConstant('{app}') else OrigPath := OrigPath + ';' + ExpandConstant('{app}'); end else OrigPath := ExpandConstant('{app}'); RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath); end; end; end; procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); begin if CurUninstallStep = usUninstall then begin RemovePath(ExpandConstant('{app}')); end; end; '@ $issContent = $issContent.Replace("__VERSION__", $Version) $issContent = $issContent.Replace("__OUTFILE__", $InstallerName) Set-Content -Path "installer.iss" -Value $issContent - name: List workspace contents for debug shell: pwsh run: | Write-Host "-- Workspace Contents --" Get-ChildItem -File | ForEach-Object { Write-Host $_.Name } - name: Compile Installer shell: cmd run: '"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" installer.iss' - name: Upload Installer to Release env: GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | $TAG = "${{ steps.get_tag.outputs.tag }}" $Version = "${{ steps.get_version.outputs.version }}" # With OutputDir=. the file is in root $UploadFile = "mikusays-windows-inst-any-$Version.exe" if (-not (Test-Path $UploadFile)) { Write-Error "Could not find compiled installer at $UploadFile" Get-ChildItem exit 1 } gh release upload $TAG $UploadFile --clobber