diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index de315c146f5..6c43465905c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -188,10 +188,18 @@ jobs: # functionality. We should avoid clobbering or re-building windows installers in most cases, # For build-only, the .exe is saved in the workflow artifacts for a human # to judge. - - name: Build + - name: Build the MSI id: build run: | - Push-Location contrib\win-installer + contrib\win-installer\build.ps1 ` + -Version ${{steps.getversion.outputs.version}} ` + -LocalReleaseDirPath ${{ github.workspace }}\release-artifacts ` + -Architecture ${{ matrix.arch }} + Exit $LASTEXITCODE + - name: Build the bundle (legacy) + id: build-legacy + run: | + Push-Location contrib\win-installer-legacy .\build.ps1 ${{steps.getversion.outputs.version}} prod ${{ github.workspace }}\release-artifacts $code = $LASTEXITCODE if ($code -eq 2) { @@ -208,26 +216,40 @@ jobs: Push-Location contrib\win-installer Get-ChildItem Pop-Location - - name: Rename the installer + Push-Location contrib\win-installer-legacy + Get-ChildItem + Pop-Location + - name: Rename the MSI run: | Push-Location contrib\win-installer + Copy-Item -Path podman-${{steps.getversion.outputs.version}}.msi -Destination podman-installer-windows-${{ matrix.arch }}.msi + Pop-Location + - name: Rename the bundle (legacy) + run: | + Push-Location contrib\win-installer-legacy Copy-Item -Path podman-${{steps.getversion.outputs.version}}-setup.exe -Destination podman-installer-windows-${{ matrix.arch }}.exe Pop-Location - - name: Upload the installer + - name: Upload the MSI + uses: actions/upload-artifact@v4 + with: + name: win-msi-${{ matrix.arch }} + path: | + .\contrib\win-installer\podman-installer-windows-${{ matrix.arch }}.msi + - name: Upload the bundle (legacy) uses: actions/upload-artifact@v4 with: name: win-installer-${{ matrix.arch }} path: | - .\contrib\win-installer\podman-installer-windows-${{ matrix.arch }}.exe + .\contrib\win-installer-legacy\podman-installer-windows-${{ matrix.arch }}.exe # For backwards compatibility, we also upload the amd64 windows # installer using the old name - - name: Upload the installer (legacy) + - name: Upload the bundle with the old name (legacy) uses: actions/upload-artifact@v4 if: ${{ matrix.arch == 'amd64' }} with: name: win-installer path: | - .\contrib\win-installer\podman-${{steps.getversion.outputs.version}}-setup.exe + .\contrib\win-installer-legacy\podman-${{steps.getversion.outputs.version}}-setup.exe release: name: Create Release @@ -266,6 +288,8 @@ jobs: mv mac-installers/* release-artifacts mv win-installer-amd64/* release-artifacts mv win-installer-arm64/* release-artifacts + mv win-msi-amd64/* release-artifacts + mv win-msi-arm64/* release-artifacts pushd release-artifacts sha256sum * > shasums popd diff --git a/.github/workflows/upload-win-installer.yml b/.github/workflows/upload-win-installer.yml index 60bebe68dad..6e310bd0757 100644 --- a/.github/workflows/upload-win-installer.yml +++ b/.github/workflows/upload-win-installer.yml @@ -64,7 +64,7 @@ jobs: - name: Check id: check run: | - Push-Location contrib\win-installer + Push-Location contrib\win-installer-legacy .\check.ps1 ${{steps.getversion.outputs.version}} $code = $LASTEXITCODE if ($code -eq 2) { @@ -108,7 +108,7 @@ jobs: id: build if: steps.check.outputs.already-exists != 'true' || steps.actual_dryrun.outputs.dryrun == 'true' run: | - Push-Location contrib\win-installer + Push-Location contrib\win-installer-legacy .\build.ps1 ${{steps.getversion.outputs.version}} prod $code = $LASTEXITCODE if ($code -eq 2) { @@ -125,7 +125,7 @@ jobs: name: installer path: | ${{ steps.check.outputs.upload_asset_name }} - .\contrib\win-installer\shasums + .\contrib\win-installer-legacy\shasums - name: Upload if: >- steps.actual_dryrun.outputs.dryrun == 'false' && @@ -134,7 +134,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - Push-Location contrib\win-installer + Push-Location contrib\win-installer-legacy $version = "${{ steps.getversion.outputs.version }}" if ($version[0] -ne "v") { $version = "v$version" diff --git a/build_windows.md b/build_windows.md index 8b7d865b3ef..03d1061f138 100644 --- a/build_windows.md +++ b/build_windows.md @@ -9,7 +9,7 @@ Windows. - [Requirements](#requirements) - [OS requirements](#os-requirements) - [Git and go](#git-and-go) - - [Pandoc](#pandoc) + - [Pandoc (optional)](#pandoc-optional) - [.NET SDK](#net-sdk) - [Virtualization Provider](#virtualization-provider) - [WSL](#wsl) @@ -25,9 +25,10 @@ Windows. - [Build and test the Podman Windows installer](#build-and-test-the-podman-windows-installer) - [Build the Windows installer](#build-the-windows-installer) - [Test the Windows installer](#test-the-windows-installer) - - [Build and test the standalone `podman.msi` file](#build-and-test-the-standalone-podmanmsi-file) - - [Verify the installation](#verify-the-installation) + - [Run the Windows installer automated tests](#run-the-windows-installer-automated-tests) + - [Verify the installation](#verify-the-installation) - [Uninstall and clean-up](#uninstall-and-clean-up) + - [Retrieve Podman installed products](#retrieve-podman-installed-products) - [Validate changes before submitting a PR](#validate-changes-before-submitting-a-pr) - [winmake lint](#winmake-lint) - [winmake validatepr](#winmake-validatepr) @@ -86,9 +87,15 @@ winget install -e Microsoft.DotNet.SDK.8 used too and can be installed using `dotnet install`: ```pwsh -dotnet tool install --global wix +dotnet tool install --global wix --version 5.0.2 ``` +:information_source: Because WiX Toolset has changed its licensing model when v6 +was released, [Podman still uses the WiX Toolset +v5.0.2](https://github.com/containers/podman/issues/27042) +and we recommend using it as well for local development (although it's not +strictly required). + ### Virtualization Provider Running Podman on Windows requires a virtualization provider. The supported @@ -125,7 +132,7 @@ Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All After running this command, a restart of the Windows machine is required. :information_source: Configure the VM provider used by podman (Hyper-V or WSL) -in the file `%PROGRAMDATA%/containers/containers.conf`. +in the file `%APPDATA%/containers/containers.conf`. [More on that later](#create-a-configuration-file-optional). ## Get the source code @@ -242,9 +249,9 @@ To test some particular configurations of Podman, create a `containers.conf` file: ``` -New-Item -ItemType Directory $env:PROGRAMDATA\containers\ -New-Item -ItemType File $env:PROGRAMDATA\containers\containers.conf -notepad $env:PROGRAMDATA\containers\containers.conf +New-Item -ItemType Directory $env:APPDATA\containers\ +New-Item -ItemType File $env:APPDATA\containers\containers.conf +notepad $env:APPDATA\containers\containers.conf ``` For example, to test with Hyper-V as the virtualization provider, use the @@ -288,15 +295,15 @@ To learn how to use the Podman client, refer to its ## Build and test the Podman Windows installer -The Podman Windows installer (e.g., `podman-5.1.0-dev-setup.exe`) is a bundle -that includes an msi package (`podman.msi`). It's built using the -[WiX Toolset](https://wixtoolset.org/) and the -[PanelSwWixExtension](https://github.com/nirbar/PanelSwWixExtension/tree/master5) -WiX extension. The source code is in the folder `contrib\win-installer`. +The Podman Windows installer (e.g., `podman-5.7.0.msi`) is an MSI package +built using the [WiX Toolset](https://wixtoolset.org/) v5. The installer +supports installation at both user scope (per-user) and machine scope +(per-machine/administrator). The source code is in the folder +`contrib\win-installer`. ### Build the Windows installer -To build the installation bundle, run the following command: +To build the MSI installer, run the following command: ```pwsh .\winmake.ps1 installer @@ -306,14 +313,15 @@ To build the installation bundle, run the following command: required before running this command. Locate the installer in the `contrib\win-installer` folder (relative to checkout -root) with a name like `podman-5.2.0-dev-setup.exe`. +root) with a name like `podman-5.7.0.msi`, where `5.7.0` is the version of +Podman on the local branch. The `installer` target of `winmake.ps1` runs the script -`contrib\win-installer\build.ps1` that, in turns, executes: -- `dotnet build podman.wixproj`: builds `podman.msi` from the WiX source files `podman.wxs`, - `pages.wxs`, `podman-ui.wxs` and `welcome-install-dlg.wxs`. -- `dotnet build podman-setup.wixproj`: builds `podman-setup.exe` file from - [WiX Burn bundle](https://wixtoolset.org/docs/tools/burn/) `burn.wxs`. +`contrib\win-installer\build.ps1` that, in turn, executes: + +- `dotnet build podman.wixproj`: builds `podman.msi` from the WiX source files + `wix\podman-main.wxs`, `wix\podman-ui-main.wxs`, and + `wix\podman-ui-welcome-dlg.wxs`. ### Test the Windows installer @@ -321,108 +329,105 @@ Double-click on the Windows installer to run it. To get the installation logs with debug information, running it via the command line is recommended: ```pwsh -contrib\win-installer\podman-5.1.0-dev-setup.exe /install /log podman-setup.log +msiexec /package contrib\win-installer\podman-5.7.0.msi /l*v podman-msi.log ``` -It generates the files `podman-setup.log` and `podman-setup_000_Setup.log`, -which include detailed installation information, in the current directory. +It generates the file `podman-msi.log`, which includes detailed installation +information, in the current directory. -Run it in `quiet` mode to automate the installation and avoid interacting with -the GUI. Open the terminal **as an administrator**, add the `/quiet` option, and -set the bundle variable `MachineProvider` (`wsl` or `hyperv`): +The MSI installer supports both user-scope and machine-scope installations: -```pwsh -contrib\win-installer\podman-5.1.0-dev-setup.exe /install ` - /log podman-setup.log /quiet ` - MachineProvider=wsl -``` +- **User scope (per-user)**: No administrator privileges required. Files are + installed in the user's profile directory, and the PATH is updated only for + the current user. This is the default scope. -:information_source: If uninstallation fails, the installer may end up in an -inconsistent state. Podman results as uninstalled, but some install packages are -still tracked in the Windows registry and will affect further tentative to -re-install Podman. When this is the case, trying to re-install Podman results in -the installer returning zero (success) but no action is executed. The trailing -packages `GID` can be found in installation logs: +- **Machine scope (per-machine)**: Requires administrator privileges. Files are + installed in `Program Files`, and the PATH is updated for all users. -``` -Detected related package: {} -``` +To run an automated installation in quiet, non-interactive mode and set the +machine provider (`wsl` or `hyperv`), use the following command: -To fix this problem remove the related packages: +**User scope installation** (no administrator required): ```pwsh -msiexec /x "{}" +msiexec /package contrib\win-installer\podman-5.7.0.msi /l*v podman-msi.log ` + /quiet MSIINSTALLPERUSER=1 MACHINE_PROVIDER=wsl ``` -#### Run the Windows installer automated tests - -The following command executes a number of tests of the windows installer. Running -it requires an administrator terminal. +**Machine scope installation** (requires administrator terminal): ```pwsh -.\winmake.ps1 installertest [wsl|hyperv] +msiexec /package contrib\win-installer\podman-5.7.0.msi /l*v podman-msi.log ` + /quiet ALLUSERS=1 MACHINE_PROVIDER=wsl ``` -### Build and test the standalone `podman.msi` file - -Building and testing the standalone `podman.msi` package during development may -be useful. Even if this package is not published as a standalone file when -Podman is released (it's included in the `podman-setup.exe` bundle), it can be -faster to build and test that rather than the full bundle during the development -phase. - -Run the command `dotnet build` to build the standalone `podman.msi` file: +:information_source: If uninstallation fails, the installer may end up in an +inconsistent state. Podman results as uninstalled, but the MSI package is still +tracked in the Windows registry and will affect further attempts to reinstall +Podman. When this is the case, trying to reinstall Podman results in the +installer returning zero (success) but no action is executed. The package +`GUID` can be found in the installation logs: -```pwsh -Push-Location .\contrib\win-installer\ -dotnet build podman.wixproj /property:DefineConstants="VERSION=9.9.9" -o . -Pop-Location +```log +Product: Podman CLI -- Installation completed successfully. +ProductCode: {} ``` -It creates the file `.\contrib\win-installer\en-US\podman.msi`. Test it using the -[Microsoft Standard Installer](https://learn.microsoft.com/en-us/windows/win32/msi/standard-installer-command-line-options) -command line tool: +To fix this problem, remove the tracked package: ```pwsh -msiexec /package contrib\win-installer\en-US\podman.msi /l*v podman-msi.log +msiexec /x "{}" ``` -To run it in quiet, non-interactive mode, open the terminal **as an -administrator**, add the `/quiet` option, and set the MSI property -`MACHINE_PROVIDER` (`wsl` or `hyperv`): +#### Run the Windows installer automated tests + +The following command executes a number of tests of the windows installer. Running +it requires an administrator terminal. ```pwsh -msiexec /package contrib\win-installer\en-US\podman.msi /l*v podman-msi.log /quiet MACHINE_PROVIDER=wsl +.\winmake.ps1 installertest [wsl|hyperv] ``` -:information_source: `podman.msi` GUI dialogs, defined in the file -`contrib\win-installer\welcome-install-dlg.wxs`, are distinct from the installation bundle -`podman-setup.exe` GUI dialogs, defined in -`contrib\win-installer\podman-theme.xml`. - -### Verify the installation +#### Verify the installation -Inspect the msi installation log `podman-msi.log` (or -`podman-setup_000_Setup.log` if testing with the bundle) to verify that the +Inspect the MSI installation log `podman-msi.log` to verify that the installation was successful: ```pwsh Select-String -Path "podman-msi.log" -Pattern "Installation success or error status: 0" ``` -These commands too are helpful to check the installation: +The following commands are helpful to check the installation: + +**For machine scope (per-machine) installations:** ```pwsh # Check the copy of the podman client in the Podman folder -Test-Path -Path "$ENV:PROGRAMFILES\RedHat\Podman\podman.exe" +Test-Path -Path "$ENV:PROGRAMFILES\Podman\podman.exe" # Check the generation of the podman configuration file Test-Path -Path "$ENV:PROGRAMDATA\containers\containers.conf.d\99-podman-machine-provider.conf" # Check that the installer configured the right provider Get-Content "$ENV:PROGRAMDATA\containers\containers.conf.d\99-podman-machine-provider.conf" | Select -Skip 1 | ConvertFrom-StringData | % { $_.provider } # Check the creation of the registry key -Test-Path -Path "HKLM:\SOFTWARE\Red Hat\Podman" -Get-ItemProperty "HKLM:\SOFTWARE\Red Hat\Podman" InstallDir -# Check the podman.exe is in the $PATH +Test-Path -Path "HKLM:\SOFTWARE\Podman" +Get-ItemProperty "HKLM:\SOFTWARE\Podman" InstallDir +# Check that podman.exe is in the $PATH +$env:PATH | Select-String -Pattern "Podman" +``` + +**For user scope (per-user) installations:** + +```pwsh +# Check the copy of the podman client in the Podman folder +Test-Path -Path "$ENV:LOCALAPPDATA\Programs\Podman\podman.exe" +# Check the generation of the podman configuration file +Test-Path -Path "$ENV:APPDATA\containers\containers.conf.d\99-podman-machine-provider.conf" +# Check that the installer configured the right provider +Get-Content "$ENV:APPDATA\containers\containers.conf.d\99-podman-machine-provider.conf" | Select -Skip 1 | ConvertFrom-StringData | % { $_.provider } +# Check the creation of the registry key +Test-Path -Path "HKCU:\SOFTWARE\Podman" +Get-ItemProperty "HKCU:\SOFTWARE\Podman" InstallDir +# Check that podman.exe is in the $PATH $env:PATH | Select-String -Pattern "Podman" ``` @@ -437,44 +442,125 @@ $ENV:CONTAINERS_MACHINE_PROVIDER='hyperv'; .\contrib\cirrus\win-installer-main.p ### Uninstall and clean-up -Podman can be uninstalled from the Windows Control Panel or running the -following command from a terminal **as an administrator**: +Podman can be uninstalled from the Windows Control Panel or using the MSI +uninstall command. Administrator privileges will be required if Podman was +installed for the machine, rather than for a user: ```pwsh -contrib\win-installer\podman-5.1.0-dev-setup.exe /uninstall /quiet /log podman-setup-uninstall.log +msiexec /x contrib\win-installer\podman-5.7.0.msi /quiet /l*v podman-msi-uninstall.log ``` The uninstaller does not delete some folders. Clean them up manually: +**For machine scope installations:** + ```pwsh $extraFolders = @( "$ENV:PROGRAMDATA\containers\" "$ENV:LOCALAPPDATA\containers\" - "$env:USERPROFILE.config\containers\" - "$env:USERPROFILE.local\share\containers\" - ) -$extraFolders | ForEach-Object {Remove-Item -Recurse -Force $PSItem} + "$env:USERPROFILE\.config\containers\" + "$env:USERPROFILE\.local\share\containers\" +) +$extraFolders | ForEach-Object {Remove-Item -Recurse -Force $PSItem -ErrorAction SilentlyContinue} +``` + +**For user scope installations:** + +```pwsh +$extraFolders = @( + "$ENV:APPDATA\containers\" + "$ENV:LOCALAPPDATA\containers\" + "$env:USERPROFILE\.config\containers\" + "$env:USERPROFILE\.local\share\containers\" +) +$extraFolders | ForEach-Object {Remove-Item -Recurse -Force $PSItem -ErrorAction SilentlyContinue} ``` The following commands are helpful to verify that the uninstallation was successful: +**For machine scope installations:** + ```pwsh # Inspect the uninstallation log for a success message -Select-String -Path "podman-setup-uninstall_000_Setup.log" -Pattern "Removal success or error status: 0" +Select-String -Path "podman-msi-uninstall.log" -Pattern "Removal success or error status: 0" # Check that the uninstaller removed Podman resources $foldersToCheck = @( - "$ENV:PROGRAMFILES\RedHat\Podman\podman.exe" - "HKLM:\SOFTWARE\Red Hat\Podman" + "$ENV:PROGRAMFILES\Podman\podman.exe" + "HKLM:\SOFTWARE\Podman" "$ENV:PROGRAMDATA\containers\" - "$env:USERPROFILE.config\containers\" - "$env:USERPROFILE.local\share\containers\" - "$ENV:LOCALAPPDATA\containers\" + "$env:USERPROFILE\.config\containers\" + "$env:USERPROFILE\.local\share\containers\" + "$ENV:APPDATA\containers\" "$ENV:PROGRAMDATA\containers\containers.conf.d\99-podman-machine-provider.conf" ) $foldersToCheck | ForEach-Object {Test-Path -Path $PSItem} ``` +**For user scope installations:** + +```pwsh +# Inspect the uninstallation log for a success message +Select-String -Path "podman-msi-uninstall.log" -Pattern "Removal success or error status: 0" +# Check that the uninstaller removed Podman resources +$foldersToCheck = @( + "$ENV:PROGRAMFILES\Podman\podman.exe" + "HKCU:\SOFTWARE\Podman" + "$ENV:APPDATA\containers\" + "$env:USERPROFILE\.config\containers\" + "$env:USERPROFILE\.local\share\containers\" + "$ENV:LOCALAPPDATA\containers\" + "$ENV:APPDATA\containers\containers.conf.d\99-podman-machine-provider.conf" +) +$foldersToCheck | ForEach-Object {Test-Path -Path $PSItem} +``` + +#### Retrieve Podman installed products + +MSI uninstallations can leave the package tracked in the Windows registry. This +can cause issues when trying to reinstall Podman. The following command can be +used to retrieve the package information: + +```pwsh +$Installer = New-Object -ComObject WindowsInstaller.Installer +$InstallerProducts = $Installer.ProductsEx("", "", 7) +$InstalledProducts = ForEach($Product in $InstallerProducts){ + try { + $ProductCode = $Product.ProductCode() + $LocalPackage = try { $Product.InstallProperty("LocalPackage") } catch { "Unknown" } + $VersionString = try { $Product.InstallProperty("VersionString") } catch { "Unknown" } + $ProductName = $Product.InstallProperty("ProductName") + + [PSCustomObject]@{ + ProductCode = $ProductCode + LocalPackage = $LocalPackage + VersionString = $VersionString + ProductName = $ProductName + } + } + catch { + Write-Warning "Failed to process product: $($_.Exception.Message)" + # Skip this product and continue + continue + } +} +$InstalledProducts | Where-Object {$_.ProductName -match "podman"} +``` + +This command returns a list of installed Podman products: + +```log +ProductCode LocalPackage VersionString ProductName +----------- ------------ ------------- ----------- +{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} Unknown Unknown Podman +``` + +The product code can be used to uninstall the Podman package: + +```pwsh +msiexec /x "{}" +``` + ## Validate changes before submitting a PR The script `winmake.ps1` has a couple of targets to check the source code @@ -499,7 +585,6 @@ tools: pip install pre-commit ``` - ### winmake validatepr Target `validatepr` performs a more exhaustive validation but takes diff --git a/contrib/cirrus/win-installer-main.ps1 b/contrib/cirrus/win-installer-main.ps1 index bd921a58998..e87053fc513 100644 --- a/contrib/cirrus/win-installer-main.ps1 +++ b/contrib/cirrus/win-installer-main.ps1 @@ -3,41 +3,71 @@ . $PSScriptRoot\win-lib.ps1 . $PSScriptRoot\..\win-installer\utils.ps1 -if ($Env:CI -eq "true") { +if ($Env:CIRRUS_CI -eq "true") { $WIN_INST_FOLDER = "$ENV:CIRRUS_WORKING_DIR\repo\contrib\win-installer" $RELEASE_DIR = "$ENV:CIRRUS_WORKING_DIR\repo" } else { $WIN_INST_FOLDER = "$PSScriptRoot\..\win-installer" $ENV:WIN_INST_VER = "9.9.9" - $RELEASE_DIR = "$PSScriptRoot\..\..\contrib\win-installer\current" + $RELEASE_DIR = Resolve-Path -Path "$PSScriptRoot\..\..\contrib\win-installer\current" if ($null -eq $ENV:CONTAINERS_MACHINE_PROVIDER) { $ENV:CONTAINERS_MACHINE_PROVIDER = 'wsl' } } -$ENV:LATEST_GH_RELEASE_ID = "199677288" # v5.4.0 -Push-Location $WIN_INST_FOLDER +$ENV:PODMAN_ARCH = Get-Current-Architecture # on Cirrus CI this should always be amd64 -# Build and test the windows installer +# To get the a GitHub release ID run the following command: +# curl -s -H "Accept: application/vnd.github+json" \ +# https://api.github.com/repos/containers/podman/releases/tags/v5.6.2 | jq '.id' +$ENV:LATEST_GH_RELEASE_ID = "251232431" # v5.6.2 +$NEXT_WIN_INST_VER="9.9.10" # Download the previous installer to test a major update - if (!$env:PREV_SETUP_EXE_PATH) { $env:PREV_SETUP_EXE_PATH = Get-Podman-Setup-From-GitHub $ENV:LATEST_GH_RELEASE_ID } +######################################################### +# Build and test the new windows installer msi package +######################################################### + +$installer_folder = Resolve-Path -Path "$WIN_INST_FOLDER" + +# Note: consumes podman-remote-release-windows_amd64.zip from repo.tar.zst +Run-Command "${installer_folder}\build.ps1 -Version `"$Env:WIN_INST_VER`" -Architecture `"$ENV:PODMAN_ARCH`" -LocalReleaseDirPath `"$RELEASE_DIR`"" + +# Build a v9.9.10 installer to test an update from current to next version +Run-Command "${installer_folder}\build.ps1 -Version `"$NEXT_WIN_INST_VER`" -Architecture `"$ENV:PODMAN_ARCH`" -LocalReleaseDirPath `"$RELEASE_DIR`"" + +# Run the installer silently and WSL/HyperV install options disabled (prevent reboots) +$command = "${installer_folder}\test.ps1 " +$command += "-scenario all " +$command += "-provider $ENV:CONTAINERS_MACHINE_PROVIDER " +$command += "-msiPath `"${installer_folder}\podman-$ENV:WIN_INST_VER.msi`" " +$command += "-nextMsiPath `"${installer_folder}\podman-$NEXT_WIN_INST_VER.msi`" " +$command += "-previousSetupExePath `"$env:PREV_SETUP_EXE_PATH`"" +Run-Command "${command}" + +######################################################### +# Build and test the legacy windows installer setup bundle +######################################################### + +$installer_folder = Resolve-Path -Path "$WIN_INST_FOLDER-legacy" + +Push-Location "${installer_folder}" + # Note: consumes podman-remote-release-windows_amd64.zip from repo.tar.zst Run-Command ".\build.ps1 $Env:WIN_INST_VER dev `"$RELEASE_DIR`"" # Build a v9.9.10 installer to test an update from current to next version -$NEXT_WIN_INST_VER="9.9.10" Run-Command ".\build.ps1 `"$NEXT_WIN_INST_VER`" dev `"$RELEASE_DIR`"" Pop-Location # Run the installer silently and WSL/HyperV install options disabled (prevent reboots) -$command = "$WIN_INST_FOLDER\test-installer.ps1 " +$command = "${installer_folder}\test-installer.ps1 " $command += "-scenario all " $command += "-provider $ENV:CONTAINERS_MACHINE_PROVIDER " -$command += "-setupExePath `"$WIN_INST_FOLDER\podman-$ENV:WIN_INST_VER-dev-setup.exe`"" -$command += "-previousSetupExePath `"$env:PREV_SETUP_EXE_PATH`"" -$command += "-nextSetupExePath `"$WIN_INST_FOLDER\podman-$NEXT_WIN_INST_VER-dev-setup.exe`"" +$command += "-setupExePath `"${installer_folder}\podman-$ENV:WIN_INST_VER-dev-setup.exe`" " +$command += "-previousSetupExePath `"$env:PREV_SETUP_EXE_PATH`" " +$command += "-nextSetupExePath `"${installer_folder}\podman-$NEXT_WIN_INST_VER-dev-setup.exe`" " Run-Command "${command}" diff --git a/contrib/win-installer-legacy/.gitignore b/contrib/win-installer-legacy/.gitignore new file mode 100644 index 00000000000..36e5b12c6dc --- /dev/null +++ b/contrib/win-installer-legacy/.gitignore @@ -0,0 +1,13 @@ +artifacts/ +current/ +docs/ +en-US/ +fetch/ +podman-*setup.exe +engine.exe +obj/ +shasums +*.wixobj +*.wixpdb +*.log +*.msi diff --git a/contrib/win-installer/README.md b/contrib/win-installer-legacy/README.md similarity index 78% rename from contrib/win-installer/README.md rename to contrib/win-installer-legacy/README.md index 8fb85175f93..b85d28e0828 100644 --- a/contrib/win-installer/README.md +++ b/contrib/win-installer-legacy/README.md @@ -49,15 +49,28 @@ contrib\win-installer\podman-9.9.9-dev-setup.exe /x /log contrib\win-installer\p ## retrieve installed podman msi package information ```pwsh -$Installer = New-Object -ComObject WindowsInstaller.Installer; -$InstallerProducts = $Installer.ProductsEx("", "", 7); +$Installer = New-Object -ComObject WindowsInstaller.Installer +$InstallerProducts = $Installer.ProductsEx("", "", 7) $InstalledProducts = ForEach($Product in $InstallerProducts){ - [PSCustomObject]@{ProductCode = $Product.ProductCode(); - LocalPackage = $Product.InstallProperty("LocalPackage"); - VersionString = $Product.InstallProperty("VersionString"); - ProductName = $Product.InstallProperty("ProductName") - } -}; + try { + $ProductCode = $Product.ProductCode() + $LocalPackage = try { $Product.InstallProperty("LocalPackage") } catch { "Unknown" } + $VersionString = try { $Product.InstallProperty("VersionString") } catch { "Unknown" } + $ProductName = $Product.InstallProperty("ProductName") + + [PSCustomObject]@{ + ProductCode = $ProductCode + LocalPackage = $LocalPackage + VersionString = $VersionString + ProductName = $ProductName + } + } + catch { + Write-Warning "Failed to process product: $($_.Exception.Message)" + # Skip this product and continue + continue + } +} $InstalledProducts | Where-Object {$_.ProductName -match "podman"} ``` diff --git a/contrib/win-installer-legacy/build.ps1 b/contrib/win-installer-legacy/build.ps1 new file mode 100644 index 00000000000..4294384e405 --- /dev/null +++ b/contrib/win-installer-legacy/build.ps1 @@ -0,0 +1,156 @@ +function ExitOnError() { + if ($LASTEXITCODE -ne 0) { + Exit 1 + } +} + +function SignItem() { + param( + [Parameter(Mandatory)] + [string[]]$fileNames + ) + + foreach ($val in $ENV:APP_ID, $ENV:TENANT_ID, $ENV:CLIENT_SECRET, $ENV:CERT_NAME) { + if (!$val) { + Write-Host "Skipping signing (no config)" + Return + } + } + + CheckCommand AzureSignTool.exe "AzureSignTool" + + AzureSignTool.exe sign -du "https://github.com/containers/podman" ` + -kvu "https://$ENV:VAULT_ID.vault.azure.net" ` + -kvi $ENV:APP_ID ` + -kvt $ENV:TENANT_ID ` + -kvs $ENV:CLIENT_SECRET ` + -kvc $ENV:CERT_NAME ` + -tr http://timestamp.digicert.com $fileNames + + ExitOnError +} + +function CheckCommand() { + param( + [Parameter(Mandatory)] + [string] $cmd, + [Parameter(Mandatory)] + [string] $description + ) + + if (! (Get-Command $cmd -errorAction SilentlyContinue)) { + Write-Host "Required dep `"$description`" is not installed" + Exit 1 + } +} + +function CheckRequirements() { + CheckCommand "wix" "WiX Toolset" + CheckCommand "go" "Golang" +} + +if ($args.Count -lt 1 -or $args[0].Length -lt 1) { + Write-Host "Usage: " $MyInvocation.MyCommand.Name " [dev|prod] [release_dir]" + Write-Host + Write-Host 'Uses Env Vars: ' + Write-Host ' $ENV:FETCH_BASE_URL - GitHub Repo Address to locate release on' + Write-Host ' $ENV:V531_SETUP_EXE_PATH - Path to v5.3.1 setup.exe used to build the patch' + Write-Host ' $ENV:PODMAN_ARCH - Installer target platform (x64 or arm64)' + Write-Host 'Env Settings for signing (optional)' + Write-Host ' $ENV:VAULT_ID' + Write-Host ' $ENV:APP_ID' + Write-Host ' $ENV:TENANT_ID' + Write-Host ' $ENV:CLIENT_SECRET' + Write-Host ' $ENV:CERT_NAME' + Write-Host + Write-Host "Example: Download and build from the official Github release (dev output): " + Write-Host " .\build.ps1 4.2.0" + Write-Host + Write-Host "Example: Build a dev build from a pre-download release " + Write-Host " .\build.ps1 4.2.0 dev fetchdir" + Write-Host + + Exit 1 +} + +# Pre-set to standard locations in-case build env does not refresh paths +$Env:Path="$Env:Path;C:\Users\micro\mingw64\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin;;C:\Program Files\Go\bin;C:\Program Files\dotnet" + +CheckRequirements + +$version = $args[0] + +if ($version[0] -eq "v") { + $version = $version.Substring(1) +} + +$suffix = "-dev" +if ($args.Count -gt 1 -and $args[1] -eq "prod") { + $suffix = "" +} + +$releaseDir = "" +if ($args.Count -gt 2) { + $releaseDir = $args[2] +} + +Push-Location $PSScriptRoot +.\process-release.ps1 $version $releaseDir +if ($LASTEXITCODE -eq 2) { + Write-Host "Skip signaled, relaying skip" + Exit 2 +} +if ($ENV:INSTVER -eq "") { + Write-Host "process-release did not define an install version!" + Exit 1 +} + +$installerPlatform = "" +if ($null -eq $ENV:PODMAN_ARCH -or "" -eq $ENV:PODMAN_ARCH -or "amd64" -eq $ENV:PODMAN_ARCH) { + $installerPlatform = "x64" +} elseif ($ENV:PODMAN_ARCH -eq "arm64") { + $installerPlatform = "arm64" +} else { + Write-Host "Unknown architecture $ENV:PODMAN_ARCH. Valid options are amd64 or arm64." + Exit 1 +} + +SignItem @("artifacts/win-sshproxy.exe", + "artifacts/podman.exe") +$gvExists = Test-Path "artifacts/gvproxy.exe" +if ($gvExists) { + SignItem @("artifacts/gvproxy.exe") + Remove-Item Env:\UseGVProxy -ErrorAction SilentlyContinue +} else { + $env:UseGVProxy = "Skip" +} + +# Retaining for possible future additions +# $pExists = Test-Path "artifacts/policy.json" +# if ($pExists) { +# Remove-Item Env:\IncludePolicyJSON -ErrorAction SilentlyContinue +# } else { +# $env:IncludePolicyJSON = "Skip" +# } +if (Test-Path ./obj) { + Remove-Item ./obj -Recurse -Force -Confirm:$false +} +dotnet build podman.wixproj /property:DefineConstants="VERSION=$ENV:INSTVER" /property:InstallerPlatform="$installerPlatform" -o .; ExitOnError +SignItem @("en-US\podman.msi") + +dotnet build podman-setup.wixproj /property:DefineConstants="VERSION=$ENV:INSTVER" /property:InstallerPlatform="$installerPlatform" -o .; ExitOnError +wix burn detach podman-setup.exe -engine engine.exe; ExitOnError +SignItem @("engine.exe") + +$file = "podman-$version$suffix-setup.exe" +wix burn reattach -engine engine.exe podman-setup.exe -o $file; ExitOnError +SignItem @("$file") + +if (Test-Path -Path shasums) { + $hash = (Get-FileHash -Algorithm SHA256 $file).Hash.ToLower() + Write-Output "$hash $file" | Out-File -Append -FilePath shasums +} + +Write-Host "Complete" +Get-ChildItem "podman-$version$suffix-setup.exe" +Pop-Location diff --git a/contrib/win-installer/burn.wxs b/contrib/win-installer-legacy/burn.wxs similarity index 100% rename from contrib/win-installer/burn.wxs rename to contrib/win-installer-legacy/burn.wxs diff --git a/contrib/win-installer/check.ps1 b/contrib/win-installer-legacy/check.ps1 similarity index 100% rename from contrib/win-installer/check.ps1 rename to contrib/win-installer-legacy/check.ps1 diff --git a/contrib/win-installer/podman-setup.wixproj b/contrib/win-installer-legacy/podman-setup.wixproj similarity index 100% rename from contrib/win-installer/podman-setup.wixproj rename to contrib/win-installer-legacy/podman-setup.wixproj diff --git a/contrib/win-installer/podman-theme.wxl b/contrib/win-installer-legacy/podman-theme.wxl similarity index 100% rename from contrib/win-installer/podman-theme.wxl rename to contrib/win-installer-legacy/podman-theme.wxl diff --git a/contrib/win-installer/podman-theme.xml b/contrib/win-installer-legacy/podman-theme.xml similarity index 100% rename from contrib/win-installer/podman-theme.xml rename to contrib/win-installer-legacy/podman-theme.xml diff --git a/contrib/win-installer/podman-ui.wxs b/contrib/win-installer-legacy/podman-ui.wxs similarity index 100% rename from contrib/win-installer/podman-ui.wxs rename to contrib/win-installer-legacy/podman-ui.wxs diff --git a/contrib/win-installer/podman.sln b/contrib/win-installer-legacy/podman.sln similarity index 100% rename from contrib/win-installer/podman.sln rename to contrib/win-installer-legacy/podman.sln diff --git a/contrib/win-installer/podman.wixproj b/contrib/win-installer-legacy/podman.wixproj similarity index 100% rename from contrib/win-installer/podman.wixproj rename to contrib/win-installer-legacy/podman.wixproj diff --git a/contrib/win-installer/podman.wxs b/contrib/win-installer-legacy/podman.wxs similarity index 100% rename from contrib/win-installer/podman.wxs rename to contrib/win-installer-legacy/podman.wxs diff --git a/contrib/win-installer/process-release.ps1 b/contrib/win-installer-legacy/process-release.ps1 similarity index 100% rename from contrib/win-installer/process-release.ps1 rename to contrib/win-installer-legacy/process-release.ps1 diff --git a/contrib/win-installer/resources/podman-banner.png b/contrib/win-installer-legacy/resources/podman-banner.png similarity index 100% rename from contrib/win-installer/resources/podman-banner.png rename to contrib/win-installer-legacy/resources/podman-banner.png diff --git a/contrib/win-installer/resources/podman-banner2.png b/contrib/win-installer-legacy/resources/podman-banner2.png similarity index 100% rename from contrib/win-installer/resources/podman-banner2.png rename to contrib/win-installer-legacy/resources/podman-banner2.png diff --git a/contrib/win-installer/resources/podman-dialog.png b/contrib/win-installer-legacy/resources/podman-dialog.png similarity index 100% rename from contrib/win-installer/resources/podman-dialog.png rename to contrib/win-installer-legacy/resources/podman-dialog.png diff --git a/contrib/win-installer/resources/podman-logo.ico b/contrib/win-installer-legacy/resources/podman-logo.ico similarity index 100% rename from contrib/win-installer/resources/podman-logo.ico rename to contrib/win-installer-legacy/resources/podman-logo.ico diff --git a/contrib/win-installer/resources/podman-logo.png b/contrib/win-installer-legacy/resources/podman-logo.png similarity index 100% rename from contrib/win-installer/resources/podman-logo.png rename to contrib/win-installer-legacy/resources/podman-logo.png diff --git a/contrib/win-installer/resources/podman-sidebar.png b/contrib/win-installer-legacy/resources/podman-sidebar.png similarity index 100% rename from contrib/win-installer/resources/podman-sidebar.png rename to contrib/win-installer-legacy/resources/podman-sidebar.png diff --git a/contrib/win-installer/test-installer.ps1 b/contrib/win-installer-legacy/test-installer.ps1 similarity index 99% rename from contrib/win-installer/test-installer.ps1 rename to contrib/win-installer-legacy/test-installer.ps1 index d61b4f317b7..d3b3d965e3d 100644 --- a/contrib/win-installer/test-installer.ps1 +++ b/contrib/win-installer-legacy/test-installer.ps1 @@ -35,7 +35,7 @@ param ( [switch]$skipConfigFileCreation=$false ) -. $PSScriptRoot\utils.ps1 +. $PSScriptRoot\..\win-installer\utils.ps1 $MachineConfPath = "$env:ProgramData\containers\containers.conf.d\99-podman-machine-provider.conf" $PodmanFolderPath = "$env:ProgramFiles\RedHat\Podman" diff --git a/contrib/win-installer/welcome-install-dlg.wxs b/contrib/win-installer-legacy/welcome-install-dlg.wxs similarity index 100% rename from contrib/win-installer/welcome-install-dlg.wxs rename to contrib/win-installer-legacy/welcome-install-dlg.wxs diff --git a/contrib/win-installer/.gitignore b/contrib/win-installer/.gitignore new file mode 100644 index 00000000000..36e5b12c6dc --- /dev/null +++ b/contrib/win-installer/.gitignore @@ -0,0 +1,13 @@ +artifacts/ +current/ +docs/ +en-US/ +fetch/ +podman-*setup.exe +engine.exe +obj/ +shasums +*.wixobj +*.wixpdb +*.log +*.msi diff --git a/contrib/win-installer/build.ps1 b/contrib/win-installer/build.ps1 index 42ba85fcde4..998e8155e3d 100644 --- a/contrib/win-installer/build.ps1 +++ b/contrib/win-installer/build.ps1 @@ -1,154 +1,226 @@ -function ExitOnError() { - if ($LASTEXITCODE -ne 0) { - Exit 1 - } -} - -function SignItem() { - param( - [Parameter(Mandatory)] - [string[]]$fileNames - ) - - foreach ($val in $ENV:APP_ID, $ENV:TENANT_ID, $ENV:CLIENT_SECRET, $ENV:CERT_NAME) { - if (!$val) { - Write-Host "Skipping signing (no config)" - Return - } - } - - CheckCommand AzureSignTool.exe "AzureSignTool" - - AzureSignTool.exe sign -du "https://github.com/containers/podman" ` - -kvu "https://$ENV:VAULT_ID.vault.azure.net" ` - -kvi $ENV:APP_ID ` - -kvt $ENV:TENANT_ID ` - -kvs $ENV:CLIENT_SECRET ` - -kvc $ENV:CERT_NAME ` - -tr http://timestamp.digicert.com $fileNames - - ExitOnError -} - -function CheckCommand() { - param( - [Parameter(Mandatory)] - [string] $cmd, - [Parameter(Mandatory)] - [string] $description - ) - - if (! (Get-Command $cmd -errorAction SilentlyContinue)) { - Write-Host "Required dep `"$description`" is not installed" - Exit 1 - } -} - -function CheckRequirements() { - CheckCommand "wix" "WiX Toolset" - CheckCommand "go" "Golang" -} - -if ($args.Count -lt 1 -or $args[0].Length -lt 1) { - Write-Host "Usage: " $MyInvocation.MyCommand.Name " [dev|prod] [release_dir]" - Write-Host - Write-Host 'Uses Env Vars: ' - Write-Host ' $ENV:FETCH_BASE_URL - GitHub Repo Address to locate release on' - Write-Host ' $ENV:V531_SETUP_EXE_PATH - Path to v5.3.1 setup.exe used to build the patch' - Write-Host ' $ENV:PODMAN_ARCH - Installer target platform (x64 or arm64)' - Write-Host 'Env Settings for signing (optional)' - Write-Host ' $ENV:VAULT_ID' - Write-Host ' $ENV:APP_ID' - Write-Host ' $ENV:TENANT_ID' - Write-Host ' $ENV:CLIENT_SECRET' - Write-Host ' $ENV:CERT_NAME' - Write-Host - Write-Host "Example: Download and build from the official Github release (dev output): " - Write-Host " .\build.ps1 4.2.0" - Write-Host - Write-Host "Example: Build a dev build from a pre-download release " - Write-Host " .\build.ps1 4.2.0 dev fetchdir" - Write-Host - +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Build Podman Windows MSI installer + +.DESCRIPTION + This script builds a Podman MSI installer for Windows using WiX Toolset. + The release artifacts (podman.exe, gvproxy.exe, win-sshproxy.exe and + docs) are either downloaded from GitHub or copied from a local directory, + extracted, signed (if signing credentials are available), and packaged + into an MSI installer. + +.PARAMETER Version + Podman version to build (e.g., "5.6.2" or "v5.6.2"). This will be the + version of the MSI installer to be built. + +.PARAMETER Architecture + Target architecture for the installer. Valid values: 'amd64', 'arm64'. + Default: 'amd64' + +.PARAMETER LocalReleaseDirPath + Optional path to a local directory containing the release zip file + (podman-remote-release-windows_.zip). If not specified, the + release will be downloaded from GitHub. + +.PARAMETER RemoteReleaseRepoUrl + URL of the Podman release repository to download from. Default is the + official Podman GitHub repository. This parameter is primarily used by + the Podman release scripts. + +.EXAMPLE + .\build.ps1 -Version 5.6.2 + Download and build amd64 MSI from the official GitHub release + +.EXAMPLE + .\build.ps1 -Version 5.6.2 -Architecture arm64 + Download and build arm64 MSI from the official GitHub release + +.EXAMPLE + .\build.ps1 -Version 5.6.2 -LocalReleaseDirPath .\current + Build an MSI from a pre-downloaded release in the current directory + +.EXAMPLE + .\build.ps1 -Version 5.6.2 -Architecture arm64 ` + -LocalReleaseDirPath .\current + Build an arm64 MSI from a local release directory + +.NOTES + Requirements: + - .NET SDK (https://dotnet.microsoft.com/download) + - WiX Toolset (install: dotnet tool install --global wix) + + Environment Variables for signing (optional): + $ENV:VAULT_ID $ENV:APP_ID $ENV:TENANT_ID + $ENV:CLIENT_SECRET $ENV:CERT_NAME +#> + +param( + [Parameter( + Mandatory = $true, + ParameterSetName = 'LocalReleaseDirPath', + Position = 0, + HelpMessage = 'Podman version to build (MSI installer version)' + )] + [ValidateNotNullOrEmpty()] + [ValidatePattern('^v?([0-9]+\.[0-9]+\.[0-9]+)(-.*)?$')] + [string]$Version, + + [Parameter( + Mandatory = $false, + Position = 1, + HelpMessage = 'Target Architecture' + )] + [ValidateSet('amd64', 'arm64')] + [string]$Architecture = 'amd64', + + [Parameter( + Mandatory = $false, + Position = 2, + HelpMessage = 'Path to pre-downloaded release zip file' + )] + [ValidateScript({ Test-Path $_ -PathType Container })] + [string]$LocalReleaseDirPath = '', + + [Parameter( + Mandatory = $false, + Position = 3, + HelpMessage = 'URL of the Podman release repository' + )] + [string]$RemoteReleaseRepoUrl = 'https://github.com/containers/podman' +) + +. $PSScriptRoot\utils.ps1 + +# Strip leading 'v' from version if present +$Version = $Version.TrimStart('v') + +################################################################################ +# REQUIREMENTS CHECK +################################################################################ +Write-Host 'Checking requirements (dotnet and wix)' + +# Check if .NET SDK is installed +if (! (Get-Command 'dotnet' -errorAction SilentlyContinue)) { + Write-Error "Required dep `".NET SDK`" is not installed. " ` + + 'Please install it from https://dotnet.microsoft.com/download' Exit 1 } -# Pre-set to standard locations in-case build env does not refresh paths -$Env:Path="$Env:Path;C:\Users\micro\mingw64\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin;;C:\Program Files\Go\bin;C:\Program Files\dotnet" - -CheckRequirements - -$version = $args[0] - -if ($version[0] -eq "v") { - $version = $version.Substring(1) +# Check if WiX Toolset is installed +Invoke-Expression 'dotnet tool list --global wix' ` + -ErrorAction SilentlyContinue | Out-Null +if ($LASTEXITCODE -ne 0) { + Write-Error "Required dep `"Wix Toolset`" is not installed. " ` + + 'Please install it with the command ' ` + + "`"dotnet tool install --global wix`"" + Exit 1 } -$suffix = "-dev" -if ($args.Count -gt 1 -and $args[1] -eq "prod") { - $suffix = "" +################################################################################ +# COPY OR DOWNLOAD RELEASE ARTIFACTS AND SIGN THEM +################################################################################ +Write-Host 'Deleting old directories if they exist (''docs'', ''artifacts'' ' ` + 'and ''fetch'')' +foreach ($dir in 'docs', 'artifacts', 'fetch') { + Remove-Item -Force -Recurse -Path $PSScriptRoot\$dir ` + -ErrorAction SilentlyContinue | Out-Null } -$releaseDir = "" -if ($args.Count -gt 2) { - $releaseDir = $args[2] +Write-Host 'Creating empty ''fetch'' and ''artifacts'' directories' +foreach ($dir in 'fetch', 'artifacts') { + New-Item $PSScriptRoot\$dir -ItemType Directory | Out-Null } -.\process-release.ps1 $version $releaseDir -if ($LASTEXITCODE -eq 2) { - Write-Host "Skip signaled, relaying skip" - Exit 2 +$ProgressPreference = 'SilentlyContinue'; + +if (!$LocalReleaseDirPath) { + $releaseZipUrl = "$RemoteReleaseRepoUrl/releases/download/" ` + + "v$Version/podman-remote-release-windows_$Architecture.zip" + Write-Host "Downloading $releaseZipUrl" + DownloadReleaseFile -url $releaseZipUrl ` + -outputFile "$PSScriptRoot\fetch\release.zip" ` + -failOnError + DownloadReleaseFile ` + -url "$RemoteReleaseRepoUrl/releases/download/v$Version/shasums" ` + -outputFile "$PSScriptRoot\shasums" } -if ($ENV:INSTVER -eq "") { - Write-Host "process-release did not define an install version!" - Exit 1 +else { + $sourceZip = "$LocalReleaseDirPath\" ` + + "podman-remote-release-windows_$Architecture.zip" + Write-Host "Copying $sourceZip to $PSScriptRoot\fetch\release.zip" + Copy-Item -Path $sourceZip ` + -Destination "$PSScriptRoot\fetch\release.zip" ` + -ErrorAction Stop } -$installerPlatform = "" -if ($null -eq $ENV:PODMAN_ARCH -or "" -eq $ENV:PODMAN_ARCH -or "amd64" -eq $ENV:PODMAN_ARCH) { - $installerPlatform = "x64" -} elseif ($ENV:PODMAN_ARCH -eq "arm64") { - $installerPlatform = "arm64" -} else { - Write-Host "Unknown architecture $ENV:PODMAN_ARCH. Valid options are amd64 or arm64." - Exit 1 +Write-Host "Expanding the podman release zip file to $PSScriptRoot\fetch" +Expand-Archive -Path $PSScriptRoot\fetch\release.zip ` + -DestinationPath $PSScriptRoot\fetch ` + -ErrorAction Stop +ExitOnError + +Write-Host -NoNewline 'Copying artifacts: ' +Foreach ($fileName in 'podman.exe', 'win-sshproxy.exe', 'gvproxy.exe') { + Write-Host -NoNewline "$fileName, " + Get-ChildItem -Path "$PSScriptRoot\fetch\" ` + -Filter "$fileName" ` + -Recurse | ` + Copy-Item -Container:$false ` + -Destination "$PSScriptRoot\artifacts\" ` + -ErrorAction Stop + ExitOnError } -SignItem @("artifacts/win-sshproxy.exe", - "artifacts/podman.exe") -$gvExists = Test-Path "artifacts/gvproxy.exe" -if ($gvExists) { - SignItem @("artifacts/gvproxy.exe") - Remove-Item Env:\UseGVProxy -ErrorAction SilentlyContinue -} else { - $env:UseGVProxy = "Skip" +Write-Host 'docs' +Get-ChildItem -Path $PSScriptRoot\fetch\ -Filter 'docs' -Recurse | ` + Copy-Item -Recurse ` + -Container:$false ` + -Destination "$PSScriptRoot\docs" ` + -ErrorAction Stop +ExitOnError + +Write-Host 'Deleting the ''fetch'' folder' +Remove-Item -Force -Recurse -Path $PSScriptRoot\fetch ` + -ErrorAction SilentlyContinue | Out-Null + +SignItem @("$PSScriptRoot\artifacts\win-sshproxy.exe", + "$PSScriptRoot\artifacts\podman.exe", + "$PSScriptRoot\artifacts\gvproxy.exe") +ExitOnError + +################################################################################ +# BUILD THE MSI +################################################################################ +dotnet clean $PSScriptRoot\wix\podman.wixproj +Remove-Item $PSScriptRoot\wix\obj -Recurse -Force -Confirm:$false ` + -ErrorAction 'Ignore' + +$archMap = @{ + 'amd64' = 'x64' + 'arm64' = 'arm64' } +$installerPlatform = $archMap[$Architecture] -# Retaining for possible future additions -# $pExists = Test-Path "artifacts/policy.json" -# if ($pExists) { -# Remove-Item Env:\IncludePolicyJSON -ErrorAction SilentlyContinue -# } else { -# $env:IncludePolicyJSON = "Skip" -# } -if (Test-Path ./obj) { - Remove-Item ./obj -Recurse -Force -Confirm:$false -} -dotnet build podman.wixproj /property:DefineConstants="VERSION=$ENV:INSTVER" /property:InstallerPlatform="$installerPlatform" -o .; ExitOnError -SignItem @("en-US\podman.msi") +Write-Host 'Building the MSI...' +dotnet build $PSScriptRoot\wix\podman.wixproj ` + /property:DefineConstants="VERSION=$Version" ` + /property:InstallerPlatform="$installerPlatform" ` + /property:OutputName="podman-$Version" ` + -o $PSScriptRoot +ExitOnError -dotnet build podman-setup.wixproj /property:DefineConstants="VERSION=$ENV:INSTVER" /property:InstallerPlatform="$installerPlatform" -o .; ExitOnError -wix burn detach podman-setup.exe -engine engine.exe; ExitOnError -SignItem @("engine.exe") +$msiName = "podman-$Version.msi" -$file = "podman-$version$suffix-setup.exe" -wix burn reattach -engine engine.exe podman-setup.exe -o $file; ExitOnError -SignItem @("$file") +SignItem @("$PSScriptRoot\$msiName") -if (Test-Path -Path shasums) { - $hash = (Get-FileHash -Algorithm SHA256 $file).Hash.ToLower() - Write-Output "$hash $file" | Out-File -Append -FilePath shasums +if (Test-Path -Path $PSScriptRoot\shasums) { + $hash = (Get-FileHash -Algorithm SHA256 ` + $PSScriptRoot\$msiName).Hash.ToLower() + Write-Output "$hash $msiName" | ` + Out-File -Append -FilePath $PSScriptRoot\shasums } -Write-Host "Complete" -Get-ChildItem "podman-$version$suffix-setup.exe" +Write-Host 'Complete' +Get-ChildItem "$PSScriptRoot\$msiName" diff --git a/contrib/win-installer/test.ps1 b/contrib/win-installer/test.ps1 new file mode 100644 index 00000000000..f8827f8f5d7 --- /dev/null +++ b/contrib/win-installer/test.ps1 @@ -0,0 +1,783 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Automated test script for the Podman Windows MSI installer. + +.DESCRIPTION + This script provides automated end-to-end tests for the Podman Windows MSI + installer. It supports testing installation, update, and configuration + scenarios for both user and machine scopes, as well as for different + virtualization providers (WSL and Hyper-V). + The script validates the presence and correctness of installed files, + registry keys, and configuration files. + +.PARAMETER scenario + The test scenario to execute. Supported values: + - test-objects-exist + - test-objects-exist-not + - installation-green-field + - installation-skip-config-creation-flag + - installation-with-pre-existing-podman-exe + - update-from-current-to-next + - update-from-current-to-next-with-modified-config + - update-from-current-to-next-with-removed-config + - update-from-legacy-to-current + - all + +.PARAMETER msiPath + Path to the current Podman MSI installer to test. + +.PARAMETER nextMsiPath + Path to the next version Podman MSI installer (for update scenarios). + +.PARAMETER previousSetupExePath + Path to the legacy Podman setup EXE installer (for legacy update scenarios). + +.PARAMETER provider + The virtualization provider to test with. Supported values: 'wsl', 'hyperv'. Default: 'wsl'. + +.PARAMETER skipConfigFileCreation + Switch to skip creation of the configuration file during installation. + +.PARAMETER scope + Installation scope. Supported values: 'user', 'machine'. Default: 'user'. + +.EXAMPLE + .\test.ps1 -scenario all -msiPath "C:\path\to\podman.msi" -nextMsiPath "C:\path\to\next\podman.msi" -scope user + +.NOTES + - When using '-scope machine', this script must be run as Administrator. + - This script is intended for use by Podman developers and CI pipelines to validate installer behavior. + - See build_windows.md for more information and usage examples. +#> + +param ( + [Parameter(Mandatory)] + [ValidateSet('test-objects-exist', 'test-objects-exist-not', 'installation-green-field', 'installation-skip-config-creation-flag', 'installation-with-pre-existing-podman-exe', + #"update-from-prev-to-current", "update-from-prev-to-current-with-modified-config", "update-from-prev-to-current-with-removed-config", + 'update-from-current-to-next', 'update-from-current-to-next-with-modified-config', 'update-from-current-to-next-with-removed-config', + 'update-from-legacy-to-current', + 'all')] + [string]$scenario, + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string]$msiPath, + # [ValidateScript({Test-Path $_ -PathType Leaf})] + # [string]$previousMsiPath, + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string]$nextMsiPath, + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string]$previousSetupExePath, + [ValidateSet('wsl', 'hyperv')] + [string]$provider = 'wsl', + [switch]$skipConfigFileCreation = $false, + [ValidateSet('machine', 'user')] + [string]$scope = 'user' +) + +. $PSScriptRoot\utils.ps1 + +# Check if running as administrator when testing machine scope installation +if ($scope -eq 'machine') { + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) + $isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + if (-not $isAdmin) { + throw 'The -scope machine parameter requires the script to be run as Administrator. Please run PowerShell as Administrator and try again.' + } +} + +# Get the architecture of the current OS +$osArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture +$arch = switch ($osArch) { + 'X64' { 'amd64' } + 'Arm64' { 'arm64' } + default { throw "Unsupported architecture: $osArch" } +} + +# 99-podman-machine-provider.conf file path +$MachineConfPathPerMachine = "$env:ProgramData\containers\containers.conf.d\99-podman-machine-provider.conf" +$MachineConfPathPerMachineLegacy = $MachineConfPathPerMachine +$MachineConfPathPerUser = "$env:AppData\containers\containers.conf.d\99-podman-machine-provider.conf" +# Podman application folder path +$PodmanFolderPathPerMachine = "$env:ProgramFiles\Podman" +$PodmanFolderPathPerMachineLegacy = "$env:ProgramFiles\RedHat\Podman" +$PodmanFolderPathPerUser = "$env:LocalAppData\Programs\Podman" +# Podman.executable file path +$PodmanExePathPerMachine = "$PodmanFolderPathPerMachine\podman.exe" +$PodmanExePathPerMachineLegacy = "$PodmanFolderPathPerMachineLegacy\podman.exe" +$PodmanExePathPerUser = "$PodmanFolderPathPerUser\podman.exe" + +$WindowsPathsToTestPerMachine = @($PodmanExePathPerMachine, + "$PodmanFolderPathPerMachine\win-sshproxy.exe", + 'HKLM:\SOFTWARE\Podman') +$WindowsPathsToTestLegacy = @($PodmanExePathPerMachineLegacy, + "$PodmanFolderPathPerMachineLegacy\win-sshproxy.exe", + 'HKLM:\SOFTWARE\Red Hat\Podman') +$WindowsPathsToTestPerUser = @($PodmanExePathPerUser, + "$PodmanFolderPathPerUser\win-sshproxy.exe", + 'HKCU:\SOFTWARE\Podman') + +function Install-Podman-Bundle { + param ( + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string]$setupExePath = $script:previousSetupExePath + ) + if ($script:skipConfigFileCreation) { $skipConfigFileCreationVar = '1' } else { $skipConfigFileCreationVar = '0' } + + Write-Host "Running the installer ($setupExePath)..." + Write-Host "(provider=`"$script:provider`", SkipConfigFileCreation=`"$skipConfigFileCreationVar`")" + $ret = Start-Process -Wait ` + -PassThru "$setupExePath" ` + -ArgumentList "/install /quiet ` + MachineProvider=${script:provider} ` + SkipConfigFileCreation=${skipConfigFileCreationVar} ` + /log $PSScriptRoot\podman-setup.log" + if ($ret.ExitCode -ne 0) { + Write-Host 'Install failed, dumping log' + Get-Content $PSScriptRoot\podman-setup.log + throw "Exit code is $($ret.ExitCode)" + } + Write-Host "Installation completed successfully!`n" +} + +function Install-Podman-Package { + param ( + [Parameter(Mandatory)] + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string]$msiPath, + [string]$msiProperties, + [switch]$shouldFail = $false + ) + + Write-Host "Running the installer ($msiPath)..." + if ($msiProperties) { + Write-Host "MSI Properties: $msiProperties" + } + else { + Write-Host 'MSI Properties: none' + } + $msiExecArgs = "/package $msiPath /quiet /l*v $PSScriptRoot\podman-msi.log $msiProperties" + Write-Host "msiexec $msiExecArgs" + $ret = Start-Process -Wait ` + -PassThru 'msiexec' ` + -ArgumentList $msiExecArgs + if ($ret.ExitCode -ne 0 -and -not $shouldFail) { + Write-Host 'Install failed, dumping log' + Get-Content $PSScriptRoot\podman-msi.log + throw "Exit code is $($ret.ExitCode)" + } + if ($ret.ExitCode -eq 0 -and $shouldFail) { + Write-Host 'Install completed successfully but a failure was expected, dumping log' + Get-Content $PSScriptRoot\podman-msi.log + throw "Exit code is $($ret.ExitCode) but a failure was expected" + } + if ($shouldFail) { + Write-Host "Installation failed as expected!`n" + } + else { + Write-Host "Installation completed successfully!`n" + } +} + +function Install-Podman-Package-with-Explicit-Properties { + param ( + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string]$msiPath = $script:msiPath, + [ValidateSet('machine', 'user')] + [string]$scope = $script:scope, + [switch]$skipConfigFileCreation = $script:skipConfigFileCreation + ) + + # MACHINE_PROVIDER + $MACHINE_PROVIDER_PROP = "MACHINE_PROVIDER=$script:provider" + # SKIP_CONFIG_FILE_CREATION + if ($skipConfigFileCreation) { + $SKIP_CONFIG_FILE_CREATION_PROP = 'SKIP_CONFIG_FILE_CREATION=1' + } + else { + $SKIP_CONFIG_FILE_CREATION_PROP = 'SKIP_CONFIG_FILE_CREATION=0' + } + # ALLUSERS or MSIINSTALLPERUSER + if ($scope -eq 'machine') { + $ALLUSERS_OR_MSIINSTALLPERUSER_PROP = 'ALLUSERS=1' + } + else { + $ALLUSERS_OR_MSIINSTALLPERUSER_PROP = 'MSIINSTALLPERUSER=1' + } + + Install-Podman-Package -msiPath $msiPath -msiProperties "${MACHINE_PROVIDER_PROP} ${ALLUSERS_OR_MSIINSTALLPERUSER_PROP} ${SKIP_CONFIG_FILE_CREATION_PROP}" +} + +function Update-Podman-Package { + param ( + [ValidateSet('From-Previous', 'To-Next', 'From-Previous-Legacy', 'To-Next-Scope-Switch')] + [string]$mode = 'From-Previous', + [ValidateSet('none', 'switch-provider', 'delete-config-file')] + [string]$configurationUpdate = 'none' + ) + + # Step 1: Install the initial version + switch ($mode) { + # There is no "previous package" yet so this scenario cannot be tested. + # This block, and those in STEP 3 and 5, should be commented out after the + # release of dual scope MSI. + # 'From-Previous' { + # Install-Podman-Package-with-Explicit-Properties -msiPath $msiPreviousPath + # Test-Installation -scope $script:scope + # } + 'To-Next' { + Install-Podman-Package-with-Explicit-Properties + Test-Installation + } + 'From-Previous-Legacy' { + Install-Podman-Bundle + Test-Installation -scope 'machine-legacy' + } + 'To-Next-Scope-Switch' { + if ($script:scope -eq 'machine') { + $newScope = 'user' + } + else { + $newScope = 'machine' + } + Install-Podman-Package-with-Explicit-Properties -scope $newScope + Test-Installation -scope $newScope + } + } + + # Step 2: Make some changes to the configration file if requested + $newProvider = $script:provider + $configFileRemoved = $false + switch ($configurationUpdate) { + 'switch-provider' { + $newProvider = Switch-Podman-Machine-Conf-Content -scope $script:scope + } + 'delete-config-file' { + Remove-Podman-Machine-Conf -scope $script:scope + $configFileRemoved = $true + } + } + + # Step 3: Install the next version + $msiProperties = '' + if ($script:scope -eq 'machine') { + $msiProperties = 'ALLUSERS=1' + } + switch ($mode) { + # 'From-Previous' { + # Install-Podman-Package -msiPath $script:msiPath + # Test-Installation -scope $script:scope -expectedConfiguredProvider $newProvider -configFileExistNot:$configFileRemoved + # } + 'To-Next' { + Install-Podman-Package -msiPath $script:nextMsiPath -msiProperties $msiProperties + Test-Installation -scope $script:scope -expectedConfiguredProvider $newProvider -configFileExistNot:$configFileRemoved + } + 'From-Previous-Legacy' { + Install-Podman-Package -shouldFail -msiPath $script:msiPath -msiProperties $msiProperties + } + 'To-Next-Scope-Switch' { + Install-Podman-Package -shouldFail -msiPath $script:nextMsiPath -msiProperties $msiProperties + } + } + + # Step 4: Check that the changes to the configuration file are persisted + switch ($configurationUpdate) { + 'switch-provider' { + Test-Podman-Machine-Conf-Content -expected $newProvider -scope $script:scope + } + 'delete-config-file' { + Test-Podman-Machine-Conf-Exist-Not -scope $script:scope -configFileRemoved:$configFileRemoved + } + } + + # Step 5: Uninstall + switch ($mode) { + # 'From-Previous' { + # Uninstall-Podman-Package -msiPath $msiPreviousPath -scope $script:scope + # Test-Uninstallation -scope $script:scope + # } + 'To-Next' { + Uninstall-Podman-Package -msiPath $script:nextMsiPath + Test-Uninstallation -scope $script:scope + } + 'From-Previous-Legacy' { + Uninstall-Podman-Bundle -setupExePath $script:previousSetupExePath + Test-Uninstallation -scope 'machine-legacy' + } + 'To-Next-Scope-Switch' { + if ($script:scope -eq 'machine') { + $newScope = 'user' + } + else { + $newScope = 'machine' + } + Uninstall-Podman-Package -msiPath $script:msiPath + Test-Uninstallation -scope $newScope + } + } +} + +function Test-Podman-Objects-Exist { + param ( + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + Write-Host "Verifying that podman files, folders and registry entries exist...(scope=$scope)" + if ($scope -eq 'machine') { + $WindowsPathsToTest = $WindowsPathsToTestPerMachine + } + elseif ($scope -eq 'machine-legacy') { + $WindowsPathsToTest = $WindowsPathsToTestLegacy + } + else { + $WindowsPathsToTest = $WindowsPathsToTestPerUser + } + $WindowsPathsToTest | ForEach-Object { + if (! (Test-Path -Path $_) ) { + throw "Expected $_ but doesn't exist" + } + } + Write-Host "Verification was successful!`n" +} + +function Test-Podman-Machine-Conf-Exist { + param ( + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + if ($scope -eq 'machine') { + $MachineConfPath = $MachineConfPathPerMachine + } + elseif ($scope -eq 'machine-legacy') { + $MachineConfPath = $MachineConfPathPerMachineLegacy + } + else { + $MachineConfPath = $MachineConfPathPerUser + } + Write-Host "Verifying that $MachineConfPath exist...(scope=$scope)" + if (! (Test-Path -Path $MachineConfPath) ) { + throw "Expected $MachineConfPath but doesn't exist" + } + Write-Host "Verification was successful!`n" +} + +function Test-Podman-Machine-Conf-Content { + [CmdletBinding(PositionalBinding = $false)] + param ( + [ValidateSet('wsl', 'hyperv')] + [string]$expected = $script:provider, + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + Write-Host "Verifying that the machine provider configuration is correct...(scope=$scope)" + if ($scope -eq 'machine') { + $MachineConfPath = $MachineConfPathPerMachine + } + elseif ($scope -eq 'machine-legacy') { + $MachineConfPath = $MachineConfPathPerMachineLegacy + } + else { + $MachineConfPath = $MachineConfPathPerUser + } + $machineProvider = Get-Content $MachineConfPath | Select-Object -Skip 1 | ConvertFrom-StringData | ForEach-Object { $_.provider } + if ( $machineProvider -ne "`"$expected`"" ) { + throw "Expected `"$expected`" as default machine provider but got $machineProvider" + } + Write-Host "Verification was successful!`n" +} + +function Uninstall-Podman-Bundle { + param ( + # [Parameter(Mandatory)] + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string]$setupExePath + ) + Write-Host "Running the uninstaller ($setupExePath)..." + $ret = Start-Process -Wait ` + -PassThru "$setupExePath" ` + -ArgumentList "/uninstall ` + /quiet /log $PSScriptRoot\podman-setup-uninstall.log" + if ($ret.ExitCode -ne 0) { + Write-Host 'Uninstall failed, dumping log' + Get-Content $PSScriptRoot\podman-setup-uninstall.log + throw "Exit code is $($ret.ExitCode)" + } + Write-Host "The uninstallation completed successfully!`n" +} + +function Uninstall-Podman-Package { + param ( + [Parameter(Mandatory)] + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string]$msiPath + ) + Write-Host "Running the uninstaller ($msiPath)..." + $ret = Start-Process -Wait ` + -PassThru 'msiexec' ` + -ArgumentList "/uninstall $msiPath /quiet /l*v $PSScriptRoot\podman-msi-uninstall.log" + if ($ret.ExitCode -ne 0) { + Write-Host 'Uninstall failed, dumping log' + Get-Content $PSScriptRoot\podman-msi-uninstall.log + throw "Exit code is $($ret.ExitCode)" + } + Write-Host "The uninstallation completed successfully!`n" +} + +function Test-Podman-Objects-Exist-Not { + param ( + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + Write-Host "Verifying that podman files, folders and registry entries don't exist...(scope=$scope)" + if ($scope -eq 'machine') { + $WindowsPathsToTest = $WindowsPathsToTestPerMachine + } + elseif ($scope -eq 'machine-legacy') { + $WindowsPathsToTest = $WindowsPathsToTestLegacy + } + else { + $WindowsPathsToTest = $WindowsPathsToTestPerUser + } + $WindowsPathsToTest | ForEach-Object { + if ( Test-Path -Path $_ ) { + throw "Path $_ is present" + } + } + Write-Host "Verification was successful!`n" +} + +function Test-Podman-Machine-Conf-Exist-Not { + param ( + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + if ($scope -eq 'machine') { + $MachineConfPath = $MachineConfPathPerMachine + } + elseif ($scope -eq 'machine-legacy') { + $MachineConfPath = $MachineConfPathPerMachineLegacy + } + else { + $MachineConfPath = $MachineConfPathPerUser + } + Write-Host "Verifying that $MachineConfPath doesn't exist...(scope=$scope)" + if ( Test-Path -Path $MachineConfPath ) { + throw "Path $MachineConfPath is present" + } + Write-Host "Verification was successful!`n" +} + +function New-Fake-Podman-Exe { + param ( + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + if ($scope -eq 'machine') { + $PodmanFolderPath = $PodmanFolderPathPerMachine + $PodmanExePath = $PodmanExePathPerMachine + } + elseif ($scope -eq 'machine-legacy') { + $PodmanFolderPath = $PodmanFolderPathPerMachineLegacy + $PodmanExePath = $PodmanExePathPerMachineLegacy + } + else { + $PodmanFolderPath = $PodmanFolderPathPerUser + $PodmanExePath = $PodmanExePathPerUser + } + Write-Host "Creating a fake $PodmanExePath...(scope=$scope)" + New-Item -ItemType Directory -Path $PodmanFolderPath -Force -ErrorAction Stop | out-null + New-Item -ItemType File -Path $PodmanExePath -ErrorAction Stop | out-null + Write-Host "Creation successful!`n" +} + +function Switch-Podman-Machine-Conf-Content { + param ( + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + $currentProvider = $script:provider + if ( $currentProvider -eq 'wsl' ) { $newProvider = 'hyperv' } else { $newProvider = 'wsl' } + if ($scope -eq 'machine') { + $MachineConfPath = $MachineConfPathPerMachine + } + elseif ($scope -eq 'machine-legacy') { + $MachineConfPath = $MachineConfPathPerMachineLegacy + } + else { + $MachineConfPath = $MachineConfPathPerUser + } + Write-Host "Editing $MachineConfPath content (was $currentProvider, will be $newProvider)..." + "[machine]`nprovider=`"$newProvider`"" | Out-File -FilePath $MachineConfPath -ErrorAction Stop + Write-Host "Edit successful!`n" + return $newProvider +} + +function Remove-Podman-Machine-Conf { + param ( + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + if ($scope -eq 'machine') { + $MachineConfPath = $MachineConfPathPerMachine + } + elseif ($scope -eq 'machine-legacy') { + $MachineConfPath = $MachineConfPathPerMachineLegacy + } + else { + $MachineConfPath = $MachineConfPathPerUser + } + Write-Host "Deleting $MachineConfPath..." + Remove-Item -Path $MachineConfPath -ErrorAction Stop | out-null + Write-Host "Deletion successful!`n" +} + +function Test-Installation { + [CmdletBinding(PositionalBinding = $false)] + param ( + [ValidateSet('wsl', 'hyperv')] + [string]$expectedConfiguredProvider, + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope, + [switch]$configFileExistNot = $false + ) + + Test-Podman-Objects-Exist -scope $scope + + if ($configFileExistNot) { + Test-Podman-Machine-Conf-Exist-Not -scope $scope + } + else { + Test-Podman-Machine-Conf-Exist -scope $scope + if ($expectedConfiguredProvider) { + Test-Podman-Machine-Conf-Content -expected $expectedConfiguredProvider -scope $scope + } + else { + Test-Podman-Machine-Conf-Content -scope $scope + } + } +} + +function Test-Installation-No-Config { + param ( + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + Test-Podman-Objects-Exist -scope $scope + Test-Podman-Machine-Conf-Exist-Not -scope $scope +} + +function Test-Uninstallation { + param ( + [ValidateSet('machine-legacy', 'machine', 'user')] + [string]$scope = $script:scope + ) + Test-Podman-Objects-Exist-Not -scope $scope + Test-Podman-Machine-Conf-Exist-Not -scope $scope +} + +# SCENARIOS +function Start-Scenario-Installation-Green-Field { + Write-Host "`n===========================================" + Write-Host ' Running scenario: Installation-Green-Field' + Write-Host '===========================================' + Install-Podman-Package-with-Explicit-Properties -msiPath $script:msiPath -scope $script:scope + Test-Installation + Uninstall-Podman-Package -msiPath $script:msiPath + Test-Uninstallation +} + +function Start-Scenario-Installation-Skip-Config-Creation-Flag { + Write-Host "`n=========================================================" + Write-Host ' Running scenario: Installation-Skip-Config-Creation-Flag' + Write-Host '=========================================================' + Install-Podman-Package-with-Explicit-Properties -msiPath $script:msiPath -scope $script:scope -skipConfigFileCreation:$true + Test-Installation-No-Config + Uninstall-Podman-Package -msiPath $script:msiPath + Test-Uninstallation +} + +function Start-Scenario-Installation-With-Pre-Existing-Podman-Exe { + Write-Host "`n============================================================" + Write-Host ' Running scenario: Installation-With-Pre-Existing-Podman-Exe' + Write-Host '============================================================' + New-Fake-Podman-Exe + Install-Podman-Package-with-Explicit-Properties -msiPath $script:msiPath -scope $script:scope + Test-Installation-No-Config + Uninstall-Podman-Package -msiPath $script:msiPath + Test-Uninstallation + + # remove the Podman folder created by `New-Fake-Podman-Exe` that + # otherwise would remain after the uninstallation + if ($script:scope -eq 'machine') { + Remove-Item -Path $PodmanFolderPathPerMachine -Recurse -Force + } + elseif ($script:scope -eq 'machine-legacy') { + Remove-Item -Path $PodmanFolderPathPerMachineLegacy -Recurse -Force + } + else { + Remove-Item -Path $PodmanFolderPathPerUser -Recurse -Force + } +} + +# function Start-Scenario-Update-From-Prev-To-Current { +# Write-Host "`n======================================================" +# Write-Host " Running scenario: Update-From-Prev-To-Current" +# Write-Host "======================================================" +# Update-Podman-Package -mode "From-Previous" -configurationUpdate "none" +# } + +# function Start-Scenario-Update-From-Prev-To-Current-With-Modified-Config { +# Write-Host "`n==============================================================" +# Write-Host " Running scenario: Update-From-Prev-To-Current-With-Modified-Config" +# Write-Host "==============================================================" +# Update-Podman-Package -mode "From-Previous" -configurationUpdate "switch-provider" +# } + +# function Start-Scenario-Update-From-Prev-To-Current-With-Removed-Config { +# Write-Host "`n==============================================================" +# Write-Host " Running scenario: Update-From-Prev-To-Current-With-Removed-Config" +# Write-Host "==============================================================" +# Update-Podman-Package -mode "From-Previous" -configurationUpdate "delete-config-file" +# } + +function Start-Scenario-Update-From-Current-To-Next { + Write-Host "`n======================================================" + Write-Host ' Running scenario: Update-From-Current-To-Next' + Write-Host '======================================================' + Update-Podman-Package -mode 'To-Next' -configurationUpdate 'none' +} + +function Start-Scenario-Update-From-Current-To-Next-With-Modified-Config { + Write-Host "`n==============================================================" + Write-Host ' Running scenario: Update-From-Current-To-Next-With-Modified-Config' + Write-Host '==============================================================' + Update-Podman-Package -mode 'To-Next' -configurationUpdate 'switch-provider' +} + +function Start-Scenario-Update-From-Current-To-Next-With-Removed-Config { + Write-Host "`n==============================================================" + Write-Host ' Running scenario: Update-From-Current-To-Next-With-Removed-Config' + Write-Host '==============================================================' + Update-Podman-Package -mode 'To-Next' -configurationUpdate 'delete-config-file' +} + +function Start-Scenario-Update-From-Legacy-To-Current { + Write-Host "`n======================================================" + Write-Host ' Running scenario: Update-From-Legacy-To-Current' + Write-Host '======================================================' + Update-Podman-Package -mode 'From-Previous-Legacy' -configurationUpdate 'none' +} + +switch ($script:scenario) { + 'test-objects-exist' { + Test-Podman-Objects-Exist + } + 'test-objects-exist-not' { + Test-Podman-Objects-Exist-Not + } + 'installation-green-field' { + if (!$script:msiPath) { + throw "Current installer path is not defined. Use '-msiPath ' to define it." + } + Start-Scenario-Installation-Green-Field + } + 'installation-skip-config-creation-flag' { + if (!$script:msiPath) { + throw "Current installer path is not defined. Use '-msiPath ' to define it." + } + Start-Scenario-Installation-Skip-Config-Creation-Flag + } + 'installation-with-pre-existing-podman-exe' { + if (!$script:msiPath) { + throw "Current installer path is not defined. Use '-msiPath ' to define it." + } + Start-Scenario-Installation-With-Pre-Existing-Podman-Exe + } + # 'update-from-prev-to-current' { + # if (!$script:msiPath) { + # throw "Current installer path is not defined. Use '-msiPath ' to define it." + # } + # if (!$script:previousMsiPath) { + # $script:previousMsiPath = Get-Latest-Podman-MSI-From-GitHub -arch $script:arch + # } + # Start-Scenario-Update-From-Prev-To-Current + # } + # 'update-from-prev-to-current-with-modified-config' { + # if (!$script:msiPath) { + # throw "Current installer path is not defined. Use '-msiPath ' to define it." + # } + # if (!$script:previousMsiPath) { + # $script:previousMsiPath = Get-Latest-Podman-MSI-From-GitHub -arch $script:arch + # } + # Start-Scenario-Update-From-Prev-To-Current-With-Modified-Config + # } + # 'update-from-prev-to-current-with-removed-config' { + # if (!$script:msiPath) { + # throw "Current installer path is not defined. Use '-msiPath ' to define it." + # } + # if (!$script:previousMsiPath) { + # $script:previousMsiPath = Get-Latest-Podman-MSI-From-GitHub -arch $script:arch + # } + # Start-Scenario-Update-From-Prev-To-Current-With-Removed-Config + # } + 'update-from-current-to-next' { + if (!$script:msiPath) { + throw "Current installer path is not defined. Use '-msiPath ' to define it." + } + if (!$script:nextMsiPath) { + throw "Next version installer path is not defined. Use '-nextMsiPath ' to define it." + } + Start-Scenario-Update-From-Current-To-Next + } + 'update-from-current-to-next-with-modified-config' { + if (!$script:msiPath) { + throw "Current installer path is not defined. Use '-msiPath ' to define it." + } + if (!$script:nextMsiPath) { + throw "Next version installer path is not defined. Use '-nextMsiPath ' to define it." + } + Start-Scenario-Update-From-Current-To-Next-With-Modified-Config + } + 'update-from-current-to-next-with-removed-config' { + if (!$script:msiPath) { + throw "Current installer path is not defined. Use '-msiPath ' to define it." + } + if (!$script:nextMsiPath) { + throw "Next version installer path is not defined. Use '-nextMsiPath ' to define it." + } + Start-Scenario-Update-From-Current-To-Next-With-Removed-Config + } + 'update-from-legacy-to-current' { + if (!$script:msiPath) { + throw "Current installer path is not defined. Use '-msiPath ' to define it." + } + if (!$script:previousSetupExePath) { + $script:previousSetupExePath = Get-Latest-Podman-Setup-From-GitHub -arch $script:arch + } + Start-Scenario-Update-From-Legacy-To-Current + } + 'all' { + if (!$script:msiPath) { + throw "Current installer path is not defined. Use '-msiPath ' to define it." + } + if (!$script:nextMsiPath) { + throw "Next version installer path is not defined. Use '-nextMsiPath ' to define it." + } + # if (!$script:previousMsiPath) { + # $script:previousMsiPath = Get-Latest-Podman-MSI-From-GitHub -arch $script:arch + # } + if (!$script:previousSetupExePath) { + $script:previousSetupExePath = Get-Latest-Podman-Setup-From-GitHub -arch $script:arch + } + Start-Scenario-Installation-Green-Field + Start-Scenario-Installation-Skip-Config-Creation-Flag + Start-Scenario-Installation-With-Pre-Existing-Podman-Exe + # Start-Scenario-Update-From-Prev-To-Current + # Start-Scenario-Update-From-Prev-To-Current-With-Modified-Config + # Start-Scenario-Update-From-Prev-To-Current-With-Removed-Config + Start-Scenario-Update-From-Current-To-Next + Start-Scenario-Update-From-Current-To-Next-With-Modified-Config + Start-Scenario-Update-From-Current-To-Next-With-Removed-Config + Start-Scenario-Update-From-Legacy-To-Current + } +} diff --git a/contrib/win-installer/utils.ps1 b/contrib/win-installer/utils.ps1 index 7d08b220d7b..59ca043dc12 100644 --- a/contrib/win-installer/utils.ps1 +++ b/contrib/win-installer/utils.ps1 @@ -1,23 +1,113 @@ #!/usr/bin/env pwsh function Get-Latest-Podman-Setup-From-GitHub { - return Get-Podman-Setup-From-GitHub "latest" + param( + [ValidateSet("amd64", "arm64")] + [string] $arch = "amd64" + ) + return Get-Podman-Setup-From-GitHub "latest" $arch } function Get-Podman-Setup-From-GitHub { param( [Parameter(Mandatory)] - [string] $version + [string] $version, + [ValidateSet("amd64", "arm64")] + [string] $arch = "amd64" ) - Write-Host "Downloading the $version Podman windows setup from GitHub..." + Write-Host "Downloading the $arch $version Podman windows setup from GitHub..." $apiUrl = "https://api.github.com/repos/containers/podman/releases/$version" $response = Invoke-RestMethod -Uri $apiUrl -Headers @{"User-Agent"="PowerShell"} -ErrorAction Stop - $downloadUrl = $response.assets[0].browser_download_url - Write-Host "Downloading URL: $downloadUrl" $latestTag = $response.tag_name - $destinationPath = "$PSScriptRoot\podman-$latestTag-setup.exe" + Write-Host "Looking for an asset named ""podman-installer-windows-$arch.exe""" + $downloadAsset = $response.assets | Where-Object { $_.name -eq "podman-installer-windows-$arch.exe" } | Select-Object -First 1 + if (-not $downloadAsset) { + # remove the first char from $latestTag if it is a "v" + if ($latestTag[0] -eq "v") { + $newLatestTag = $latestTag.Substring(1) + } + Write-Host "Not found. Looking for an asset named ""podman-$newLatestTag-setup.exe""" + $downloadAsset = $response.assets | Where-Object { $_.name -eq "podman-$newLatestTag-setup.exe" } | Select-Object -First 1 + } + $downloadUrl = $downloadAsset.browser_download_url + Write-Host "Downloading URL: $downloadUrl" + $destinationPath = "$PSScriptRoot\podman-${latestTag}-setup.exe" Write-Host "Destination Path: $destinationPath" Invoke-WebRequest -Uri $downloadUrl -OutFile $destinationPath Write-Host "Command completed successfully!`n" return $destinationPath } + +function DownloadReleaseFile { + param( + [Parameter(Mandatory)] + [string]$url, + [Parameter(Mandatory)] + [string]$outputFile, + [Parameter(Mandatory=$false, Position=3, HelpMessage="Fail if `Invoke-WebRequest` fails, silently continue if not specified")] + [switch]$failOnError = $false + ) + $ProgressPreference = 'SilentlyContinue'; + try { + Invoke-WebRequest -UseBasicParsing -ErrorAction Stop -Uri $url -OutFile $outputFile + } Catch { + if ($FailOnError) { + if ($_.Exception.Response.StatusCode -eq 404) { + Write-Error "URL not available $url" + Exit 2 + } + + throw $_.Exception + } + } +} + +function ExitOnError() { + if ($LASTEXITCODE -ne 0) { + Exit 1 + } +} + +function SignItem() { + param( + [Parameter(Mandatory)] + [string[]]$fileNames + ) + + foreach ($val in $ENV:APP_ID, $ENV:TENANT_ID, $ENV:CLIENT_SECRET, $ENV:CERT_NAME) { + if (!$val) { + Write-Host 'Skipping signing (no config)' + Return + } + } + + CheckCommand AzureSignTool.exe 'AzureSignTool' + + AzureSignTool.exe sign -du 'https://github.com/containers/podman' ` + -kvu "https://$ENV:VAULT_ID.vault.azure.net" ` + -kvi $ENV:APP_ID ` + -kvt $ENV:TENANT_ID ` + -kvs $ENV:CLIENT_SECRET ` + -kvc $ENV:CERT_NAME ` + -tr http://timestamp.digicert.com $fileNames + + ExitOnError +} + +function Get-Current-Architecture { + $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture + if ($arch -eq 'X64') { + return 'amd64' + } elseif ($arch -eq 'Arm64') { + return 'arm64' + } else { + throw "Unsupported architecture: $arch" + } +} + +# Pre-set to standard locations in-case build env does not refresh paths +$Env:Path = "$Env:Path;" + ` + 'C:\Users\micro\mingw64\bin;' + ` + 'C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin;' + ` + ';C:\Program Files\Go\bin;' + ` + 'C:\Program Files\dotnet' diff --git a/contrib/win-installer/wix/podman-main.wxs b/contrib/win-installer/wix/podman-main.wxs new file mode 100644 index 00000000000..bf7b6a5abcd --- /dev/null +++ b/contrib/win-installer/wix/podman-main.wxs @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/win-installer/wix/podman-ui-main.wxs b/contrib/win-installer/wix/podman-ui-main.wxs new file mode 100644 index 00000000000..c3122cfa999 --- /dev/null +++ b/contrib/win-installer/wix/podman-ui-main.wxs @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/win-installer/wix/podman-ui-welcome-dlg.wxs b/contrib/win-installer/wix/podman-ui-welcome-dlg.wxs new file mode 100644 index 00000000000..deaa305521b --- /dev/null +++ b/contrib/win-installer/wix/podman-ui-welcome-dlg.wxs @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/win-installer/wix/podman.wixproj b/contrib/win-installer/wix/podman.wixproj new file mode 100644 index 00000000000..9f99a1056e5 --- /dev/null +++ b/contrib/win-installer/wix/podman.wixproj @@ -0,0 +1,20 @@ + + + false + + + + ManFiles + INSTALLDIR + true + + + + + + + + + + + diff --git a/contrib/win-installer/wix/resources/podman-banner.png b/contrib/win-installer/wix/resources/podman-banner.png new file mode 100644 index 00000000000..e5070db986b Binary files /dev/null and b/contrib/win-installer/wix/resources/podman-banner.png differ diff --git a/contrib/win-installer/wix/resources/podman-dialog.png b/contrib/win-installer/wix/resources/podman-dialog.png new file mode 100644 index 00000000000..946bd095ade Binary files /dev/null and b/contrib/win-installer/wix/resources/podman-dialog.png differ diff --git a/contrib/win-installer/wix/resources/podman-logo.ico b/contrib/win-installer/wix/resources/podman-logo.ico new file mode 100644 index 00000000000..cb1dab6a75c Binary files /dev/null and b/contrib/win-installer/wix/resources/podman-logo.ico differ diff --git a/winmake.ps1 b/winmake.ps1 index 957695c8934..748d916c58e 100644 --- a/winmake.ps1 +++ b/winmake.ps1 @@ -1,16 +1,16 @@ #!/usr/bin/env powershell -[CmdletBinding(PositionalBinding=$false)] +[CmdletBinding(PositionalBinding = $false)] param ( - [ValidateSet("amd64", "arm64")] - [Alias("arch")] + [ValidateSet('amd64', 'arm64')] + [Alias('arch')] [string]$architecture = $( - $defaultArchitecture = "amd64" - $arch = try {& go env GOARCH} catch { + $defaultArchitecture = 'amd64' + $arch = try { & go env GOARCH } catch { Write-Warning "Failed retriving the host architecture, using default ($defaultArchitecture). Is Go installed?" return $defaultArchitecture } - if ($arch -cnotin @("arm64", "amd64")) { + if ($arch -cnotin @('arm64', 'amd64')) { Write-Warning "Unsupported architecture $arch. Using default ($defaultArchitecture)." return $defaultArchitecture } @@ -22,8 +22,8 @@ param ( . ./contrib/cirrus/win-lib.ps1 # Targets -function Podman-Remote{ - New-Item -ItemType Directory -Force -Path "./bin/windows" +function Podman-Remote { + New-Item -ItemType Directory -Force -Path './bin/windows' $buildInfo = Get-Date -UFormat %s -Millisecond 0 $buildInfo = "-X github.com/containers/podman/v5/libpod/define.buildInfo=$buildInfo " @@ -34,8 +34,8 @@ function Podman-Remote{ Run-Command "go build --ldflags `"$commit $buildInfo `" --tags `"$remotetags`" --o ./bin/windows/podman.exe ./cmd/podman/." } -function Make-Clean{ - $paths= @( +function Make-Clean { + $paths = @( # Files generated by the `podman` target "$PSScriptRoot\bin\windows" # Files generated by the `installer` target @@ -43,12 +43,22 @@ function Make-Clean{ "$PSScriptRoot\contrib\win-installer\artifacts" "$PSScriptRoot\contrib\win-installer\current" "$PSScriptRoot\contrib\win-installer\docs" - "$PSScriptRoot\contrib\win-installer\en-us" "$PSScriptRoot\contrib\win-installer\fetch" - "$PSScriptRoot\contrib\win-installer\obj" + "$PSScriptRoot\contrib\win-installer\wix\obj" "$PSScriptRoot\contrib\win-installer\*.log" - "$PSScriptRoot\contrib\win-installer\*.exe" + "$PSScriptRoot\contrib\win-installer\*.msi" "$PSScriptRoot\contrib\win-installer\*.wixpdb" + "$PSScriptRoot\contrib\win-installer\shasums" + # Files generated by the `installer-legacy` target + "$PSScriptRoot\contrib\win-installer-legacy\artifacts" + "$PSScriptRoot\contrib\win-installer-legacy\current" + "$PSScriptRoot\contrib\win-installer-legacy\docs" + "$PSScriptRoot\contrib\win-installer-legacy\en-us" + "$PSScriptRoot\contrib\win-installer-legacy\fetch" + "$PSScriptRoot\contrib\win-installer-legacy\obj" + "$PSScriptRoot\contrib\win-installer-legacy\*.log" + "$PSScriptRoot\contrib\win-installer-legacy\*.exe" + "$PSScriptRoot\contrib\win-installer-legacy\*.wixpdb" # Files generated by the Documentation target "$PSScriptRoot\docs\build\remote\podman-*.html" "$PSScriptRoot\docs\build\remote\podman-for-windows.html" @@ -57,7 +67,8 @@ function Make-Clean{ foreach ($path in $paths) { if (Test-Path -Path $path -PathType Container) { Remove-Item $path -Recurse -Force -Confirm:$false - } elseif (Test-Path -Path $path -PathType Leaf) { + } + elseif (Test-Path -Path $path -PathType Leaf) { Remove-Item $path -Force -Confirm:$false } } @@ -65,22 +76,23 @@ function Make-Clean{ function Local-Unit { Build-Ginkgo - $skippackages="hack,internal\domain\infra\abi,internal\domain\infra\tunnel,libpod\lock\shm,pkg\api\handlers\libpod,pkg\api\handlers\utils,pkg\bindings," - $skippackages+="pkg\domain\infra\abi,pkg\emulation,pkg\machine\apple,pkg\machine\applehv,pkg\machine\e2e,pkg\machine\libkrun," - $skippackages+="pkg\machine\provider,pkg\machine\proxyenv,pkg\machine\qemu,pkg\specgen\generate,pkg\systemd,test\e2e,test\utils,cmd\rootlessport," - $skippackages+="pkg\pidhandle" + $skippackages = 'hack,internal\domain\infra\abi,internal\domain\infra\tunnel,libpod\lock\shm,pkg\api\handlers\libpod,pkg\api\handlers\utils,pkg\bindings,' + $skippackages += 'pkg\domain\infra\abi,pkg\emulation,pkg\machine\apple,pkg\machine\applehv,pkg\machine\e2e,pkg\machine\libkrun,' + $skippackages += 'pkg\machine\provider,pkg\machine\proxyenv,pkg\machine\qemu,pkg\specgen\generate,pkg\systemd,test\e2e,test\utils,cmd\rootlessport,' + $skippackages += 'pkg\pidhandle' if ($null -eq $ENV:GINKGOTIMEOUT) { $ENV:GINKGOTIMEOUT = '--timeout=15m' } Run-Command "./bin/ginkgo.exe -vv -r --tags `"$remotetags`" ${ENV:GINKGOTIMEOUT} --trace --no-color --skip-package `"$skippackages`"" } function Local-Machine { param ( - [string]$files + [string]$files ); Build-Ginkgo if ($files) { - $files = "--focus-file ""$files""" - } elseif ($FOCUS_FILE) { + $files = "--focus-file ""$files""" + } + elseif ($FOCUS_FILE) { $files = "--focus-file ""$FOCUS_FILE"" --silence-skips" } if ($FOCUS) { @@ -97,25 +109,27 @@ function Win-SSHProxy { [string]$Version ); - New-Item -ItemType Directory -Force -Path "./bin/windows" + New-Item -ItemType Directory -Force -Path './bin/windows' if (-Not $Version) { - $match = Select-String -Path "$PSScriptRoot\go.mod" -Pattern "github.com/containers/gvisor-tap-vsock\s+(v[\d\.]+)" + $match = Select-String -Path "$PSScriptRoot\go.mod" -Pattern 'github.com/containers/gvisor-tap-vsock\s+(v[\d\.]+)' $Version = $match.Matches.Groups[1].Value } Write-Host "Downloading gvproxy version $version" - if ($architecture -eq "amd64") { - curl.exe -sSL -o "./bin/windows/gvproxy.exe" --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/gvproxy-windowsgui.exe" - curl.exe -sSL -o "./bin/windows/win-sshproxy.exe" --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/win-sshproxy.exe" - } else { - curl.exe -sSL -o "./bin/windows/gvproxy.exe" --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/gvproxy-windows-arm64.exe" - curl.exe -sSL -o "./bin/windows/win-sshproxy.exe" --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/win-sshproxy-arm64.exe" + if ($architecture -eq 'amd64') { + curl.exe -sSL -o './bin/windows/gvproxy.exe' --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/gvproxy-windowsgui.exe" + curl.exe -sSL -o './bin/windows/win-sshproxy.exe' --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/win-sshproxy.exe" + } + else { + curl.exe -sSL -o './bin/windows/gvproxy.exe' --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/gvproxy-windows-arm64.exe" + curl.exe -sSL -o './bin/windows/win-sshproxy.exe' --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/win-sshproxy-arm64.exe" } } -function Installer{ +function Installer { param ( [string]$version, - [string]$suffix = "dev" + [string]$suffix = 'dev', + [string]$installerPath = "$PSScriptRoot\contrib\win-installer" ); Write-Host "Building the windows installer for $architecture" @@ -130,15 +144,15 @@ function Installer{ if (!(Test-Path -Path $PSItem -PathType Leaf)) { Write-Host "$PSItem not found." Write-Host "Make 'podman', 'win-gvproxy' and 'docs' (or 'docs-using-podman') before making the installer:" - Write-Host " .\winmake.ps1 podman-remote" - Write-Host " .\winmake.ps1 win-gvproxy" - Write-Host " .\winmake.ps1 docs or .\winmake.ps1 docs-using-podman" + Write-Host ' .\winmake.ps1 podman-remote' + Write-Host ' .\winmake.ps1 win-gvproxy' + Write-Host ' .\winmake.ps1 docs or .\winmake.ps1 docs-using-podman' Exit 1 } } # Create the ZIP file with the full client distribution - $zipFileDest = "$PSScriptRoot\contrib\win-installer\current" + $zipFileDest = "$installerPath\current" Build-Distribution-Zip-File -destinationPath $zipFileDest if (-Not $version) { @@ -146,20 +160,21 @@ function Installer{ $version = Get-Podman-Version } - # Run \contrib\win-installer\build.ps1 - Push-Location $PSScriptRoot\contrib\win-installer - $ENV:PODMAN_ARCH = $architecture # This is used by the "build.ps1" script - Run-Command ".\build.ps1 $version $suffix `"$zipFileDest`"" - Pop-Location + # Run build.ps1 + if ($installerPath -eq "$PSScriptRoot\contrib\win-installer-legacy") { + $ENV:PODMAN_ARCH = $architecture # This is used by the "build.ps1" script + Run-Command "$installerPath\build.ps1 $version $suffix `"$zipFileDest`"" + } + else { + Run-Command "$installerPath\build.ps1 -Version $version -Architecture $architecture -LocalReleaseDirPath `"$zipFileDest`"" + } } -function Test-Installer{ +function Test-Installer { param ( [string]$version, - [ValidateSet("dev", "prod")] - [string]$flavor = "dev", - [ValidateSet("wsl", "hyperv")] - [string]$provider = "wsl" + [ValidateSet('wsl', 'hyperv')] + [string]$provider = 'wsl' ); if (-Not $version) { @@ -167,42 +182,82 @@ function Test-Installer{ $version = Get-Podman-Version } - if ($flavor -eq "prod") { - $suffix = "" - } else { - $suffix = "-dev" + $msiPath = "$PSScriptRoot\contrib\win-installer\podman-${version}.msi" + if (!(Test-Path -Path $msiPath -PathType Leaf)) { + Write-Host "MSI executable not found in path $msiPath." + Write-Host "Make 'installer' before making the installer test:" + Write-Host ' .\winmake.ps1 installer' + Exit 1 + } + + $nextMsiPath = "$PSScriptRoot\contrib\win-installer\podman-9.9.9.msi" + if (!(Test-Path -Path $nextMsiPath -PathType Leaf)) { + Write-Host 'The automated tests include testing the upgrade from current version to a future version.' + Write-Host 'That requires a version 9.9.9 of the installer:' + Write-Host ' .\winmake.ps1 installer 9.9.9' + Write-Host 'Build it and retry running installertest.' + Exit 1 } - $setupExePath = "$PSScriptRoot\contrib\win-installer\podman-${version}${suffix}-setup.exe" + $command = "$PSScriptRoot\contrib\win-installer\test.ps1" + $command += ' -scenario all' + $command += " -provider $provider" + $command += " -msiPath $msiPath" + $command += " -nextMsiPath $nextMsiPath" + Run-Command "${command}" +} + +function Test-Installer-Legacy { + param ( + [string]$version, + [ValidateSet('dev', 'prod')] + [string]$flavor = 'dev', + [ValidateSet('wsl', 'hyperv')] + [string]$provider = 'wsl' + ); + + if (-Not $version) { + # Get Podman version from local source code + $version = Get-Podman-Version + } + + if ($flavor -eq 'prod') { + $suffix = '' + } + else { + $suffix = '-dev' + } + + $setupExePath = "$PSScriptRoot\contrib\win-installer-legacy\podman-${version}${suffix}-setup.exe" if (!(Test-Path -Path $setupExePath -PathType Leaf)) { Write-Host "Setup executable not found in path $setupExePath." Write-Host "Make 'installer' before making the installer test:" - Write-Host " .\winmake.ps1 installer" + Write-Host ' .\winmake.ps1 installer-legacy' Exit 1 } - $nextSetupExePath = "$PSScriptRoot\contrib\win-installer\podman-9.9.9-dev-setup.exe" + $nextSetupExePath = "$PSScriptRoot\contrib\win-installer-legacy\podman-9.9.9-dev-setup.exe" if (!(Test-Path -Path $nextSetupExePath -PathType Leaf)) { - Write-Host "The automated tests include testing the upgrade from current version to a future version." - Write-Host "That requires a version 9.9.9 of the installer:" - Write-Host " .\winmake.ps1 installer 9.9.9" - Write-Host "Build it and retry running installertest." + Write-Host 'The automated tests include testing the upgrade from current version to a future version.' + Write-Host 'That requires a version 9.9.9 of the installer:' + Write-Host ' .\winmake.ps1 installer-legacy 9.9.9' + Write-Host 'Build it and retry running installertest.' Exit 1 } - $command = "$PSScriptRoot\contrib\win-installer\test-installer.ps1" - $command += " -scenario all" + $command = "$PSScriptRoot\contrib\win-installer-legacy\test-installer.ps1" + $command += ' -scenario all' $command += " -provider $provider" $command += " -setupExePath $setupExePath" $command += " -nextSetupExePath $nextSetupExePath" Run-Command "${command}" } -function Documentation{ - Write-Host "Generating the documentation artifacts" +function Documentation { + Write-Host 'Generating the documentation artifacts' # Check that pandoc is installed - if (!(Get-Command -Name "pandoc" -ErrorAction SilentlyContinue)) { - Write-Host "Pandoc not found. Pandoc is required to convert the documentation Markdown files into HTML files." + if (!(Get-Command -Name 'pandoc' -ErrorAction SilentlyContinue)) { + Write-Host 'Pandoc not found. Pandoc is required to convert the documentation Markdown files into HTML files.' Write-Host "Alternatively, use '.\winmake docs-using-podman' to use a container to run pandoc and generate the documentation." Exit 1 } @@ -222,8 +277,8 @@ function Documentation{ # The whole podman git repository is bind mounted in the container at /podman. # The documentation is generated by running the command `make podman-remote-windows-docs`. # The generated documentation is stored in the directory docs/build/remote. -function DocumentationUsingPodman{ - Write-Host "Generating documentation artifacts" +function DocumentationUsingPodman { + Write-Host 'Generating documentation artifacts' # Check that podman has been built $podmanClient = "${PSScriptRoot}\bin\windows\podman.exe" if (!(Test-Path -Path $podmanClient -PathType Leaf)) { @@ -238,29 +293,29 @@ function DocumentationUsingPodman{ } # Check that the podman machine is running $state = (& ${podmanClient} machine info -f json | ConvertFrom-Json).Host.MachineState - if ($state -ne "Running") { - Write-Host "Podman machine is not running. Start the machine before running the validate script." + if ($state -ne 'Running') { + Write-Host 'Podman machine is not running. Start the machine before running the validate script.' Exit 1 } - Write-Host "Building the image to generate the documentation" + Write-Host 'Building the image to generate the documentation' Run-Command "${podmanClient} build --build-arg TARGET_OS=windows -t podman-docs-generator ${PSScriptRoot}/docs" - Write-Host "Starting the container to run the documentation build" + Write-Host 'Starting the container to run the documentation build' Run-Command "${podmanClient} run -t --rm -v ${PSScriptRoot}:/podman podman-docs-generator" } -function Validate{ - $podmanExecutable = "podman" +function Validate { + $podmanExecutable = 'podman' $podmanSrcVolumeMount = "${PSScriptRoot}:/go/src/github.com/containers/podman" # All files bind mounted from a Windows host are marked as executable. # That makes the pre-commit hook "check-executables-have-shebangs" fail. # Setting the environment variable "SKIP=check-executables-have-shebangs" # allow to skip that pre-commit hook. - $podmanEnvVariable = "-e SKIP=check-executables-have-shebangs" + $podmanEnvVariable = '-e SKIP=check-executables-have-shebangs' $podmanRunArgs = "--rm -v $podmanSrcVolumeMount --security-opt label=disable -t -w /go/src/github.com/containers/podman $podmanEnvVariable" - $validateImage = "quay.io/libpod/validatepr:latest" - $validateCommand = "make .validatepr" + $validateImage = 'quay.io/libpod/validatepr:latest' + $validateCommand = 'make .validatepr' # Check that podman is installed if (!(Get-Command -Name $podmanExecutable -ErrorAction SilentlyContinue)) { @@ -277,59 +332,59 @@ function Validate{ # Check that the podman machine is running $state = (podman machine info -f json | ConvertFrom-Json).Host.MachineState - if ($state -ne "Running") { - Write-Host "Podman machine is not running. Start the machine before running the validate script." + if ($state -ne 'Running') { + Write-Host 'Podman machine is not running. Start the machine before running the validate script.' Exit 1 } Run-Command "$podmanExecutable run $podmanRunArgs $validateImage $validateCommand" } -function Lint{ +function Lint { # Check that golangci-lint is installed - if (!(Get-Command -Name "golangci-lint" -ErrorAction SilentlyContinue)) { - Write-Host "The tool ""golangci-lint"" not found. Install https://golangci-lint.run/ before running the lint script." + if (!(Get-Command -Name 'golangci-lint' -ErrorAction SilentlyContinue)) { + Write-Host 'The tool "golangci-lint" not found. Install https://golangci-lint.run/ before running the lint script.' Exit 1 } # Check that pre-commit is installed - if (!(Get-Command -Name "pre-commit" -ErrorAction SilentlyContinue)) { - Write-Host "The tool ""pre-commit"" not found. Install https://pre-commit.com/ before running the lint script." + if (!(Get-Command -Name 'pre-commit' -ErrorAction SilentlyContinue)) { + Write-Host 'The tool "pre-commit" not found. Install https://pre-commit.com/ before running the lint script.' Exit 1 } - Run-Command "pre-commit run --all-files" + Run-Command 'pre-commit run --all-files' Run-Command "golangci-lint run --timeout=10m --build-tags=`"$remotetags`" $PSScriptRoot\cmd\podman" } # Helpers -function Build-Ginkgo{ +function Build-Ginkgo { if (Test-Path -Path ./bin/ginkgo.exe -PathType Leaf) { return } - Write-Host "Building Ginkgo" - Run-Command "go build -o ./bin/ginkgo.exe ./vendor/github.com/onsi/ginkgo/v2/ginkgo" + Write-Host 'Building Ginkgo' + Run-Command 'go build -o ./bin/ginkgo.exe ./vendor/github.com/onsi/ginkgo/v2/ginkgo' } -function Git-Commit{ +function Git-Commit { # git is not installed by default on windows, # so if we can't get the commit, we don't include this info - Get-Command git -ErrorAction SilentlyContinue | out-null - if(!$?){ + Get-Command git -ErrorAction SilentlyContinue | out-null + if (!$?) { return } $commit = git rev-parse HEAD $dirty = git status --porcelain --untracked-files=no - if ($dirty){ + if ($dirty) { $commit = "$commit-dirty" } return $commit } -function Build-Distribution-Zip-File{ +function Build-Distribution-Zip-File { param ( [string]$destinationPath - ); + ); $binariesFolder = "$PSScriptRoot\bin\windows" $documentationFolder = "$PSScriptRoot\docs\build\remote\" $zipFile = "$destinationPath\podman-remote-release-windows_$architecture.zip" @@ -355,23 +410,23 @@ function Build-Distribution-Zip-File{ Remove-Item -Recurse -Force -Path "$tempFolder" } -function Get-Podman-Version{ +function Get-Podman-Version { $versionSrc = "$PSScriptRoot\test\version\" $versionBin = "$PSScriptRoot\test\version\version.exe" Run-Command "go build --o `"$versionBin`" `"$versionSrc`"" $version = Invoke-Expression "$versionBin" # Remove the '-dev' suffix from the version - $version = $version -replace "-.*", "" + $version = $version -replace '-.*', '' return $version } # Init script $target = $params[0] -$remotetags = "remote exclude_graphdriver_btrfs containers_image_openpgp" +$remotetags = 'remote exclude_graphdriver_btrfs containers_image_openpgp' switch ($target) { - {$_ -in '', 'podman-remote', 'podman'} { + { $_ -in '', 'podman-remote', 'podman' } { Podman-Remote } 'localunit' { @@ -386,7 +441,7 @@ switch ($target) { 'clean' { Make-Clean } - {$_ -in 'win-sshproxy', 'win-gvproxy'} { + { $_ -in 'win-sshproxy', 'win-gvproxy' } { if ($params.Count -gt 1) { $ref = $params[1] } @@ -395,17 +450,35 @@ switch ($target) { 'installer' { if ($params.Count -gt 1) { Installer -version $params[1] - } else { + } + else { Installer } } 'installertest' { if ($params.Count -gt 1) { Test-Installer -provider $params[1] - } else { + } + else { Test-Installer } } + 'installer-legacy' { + if ($params.Count -gt 1) { + Installer -version $params[1] -installerPath $PSScriptRoot\contrib\win-installer-legacy + } + else { + Installer -installerPath $PSScriptRoot\contrib\win-installer-legacy + } + } + 'installertest-legacy' { + if ($params.Count -gt 1) { + Test-Installer-Legacy -provider $params[1] + } + else { + Test-Installer-Legacy + } + } 'docs' { Documentation } @@ -419,39 +492,39 @@ switch ($target) { Lint } default { - Write-Host "Usage: " $MyInvocation.MyCommand.Name " [options] [<-architecture|-arch>=]" + Write-Host 'Usage: ' $MyInvocation.MyCommand.Name ' [options] [<-architecture|-arch>=]' Write-Host - Write-Host "Example: Build podman-remote " - Write-Host " .\winmake podman-remote" + Write-Host 'Example: Build podman-remote ' + Write-Host ' .\winmake podman-remote' Write-Host - Write-Host "Example: Run all unit tests " - Write-Host " .\winmake localunit" + Write-Host 'Example: Run all unit tests ' + Write-Host ' .\winmake localunit' Write-Host - Write-Host "Example: Run all machine tests " - Write-Host " .\winmake localmachine" + Write-Host 'Example: Run all machine tests ' + Write-Host ' .\winmake localmachine' Write-Host - Write-Host "Example: Run specfic machine tests " - Write-Host " .\winmake localmachine "basic_test.go"" + Write-Host 'Example: Run specfic machine tests ' + Write-Host ' .\winmake localmachine 'basic_test.go"" Write-Host - Write-Host "Example: Download win-gvproxy and win-sshproxy helpers" - Write-Host " .\winmake win-gvproxy" + Write-Host 'Example: Download win-gvproxy and win-sshproxy helpers' + Write-Host ' .\winmake win-gvproxy' Write-Host - Write-Host "Example: Build the windows installer" - Write-Host " .\winmake installer" + Write-Host 'Example: Build the windows installer' + Write-Host ' .\winmake installer' Write-Host - Write-Host "Example: Run windows installer tests" - Write-Host " .\winmake installertest hyperv" + Write-Host 'Example: Run windows installer tests' + Write-Host ' .\winmake installertest hyperv' Write-Host - Write-Host "Example: Generate the documentation artifacts" - Write-Host " .\winmake docs" + Write-Host 'Example: Generate the documentation artifacts' + Write-Host ' .\winmake docs' Write-Host - Write-Host "Example: Generate the documentation artifacts by running pandoc in a container" - Write-Host " .\winmake docs-using-podman" + Write-Host 'Example: Generate the documentation artifacts by running pandoc in a container' + Write-Host ' .\winmake docs-using-podman' Write-Host - Write-Host "Example: Validate code changes before submitting a PR" - Write-Host " .\winmake validatepr" + Write-Host 'Example: Validate code changes before submitting a PR' + Write-Host ' .\winmake validatepr' Write-Host - Write-Host "Example: Run linters" - Write-Host " .\winmake lint" + Write-Host 'Example: Run linters' + Write-Host ' .\winmake lint' } }