Skip to content

PD E2E Generic Server Update Test #29

PD E2E Generic Server Update Test

PD E2E Generic Server Update Test #29

name: PD Generic Server Update
run-name: PD E2E Generic Server Update Test
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
inputs:
pd_repo_options:
default: 'REPO=podman-desktop,FORK=podman-desktop,BRANCH=main'
description: 'Podman Desktop repo, fork and branch'
type: string
required: true
npm_target:
default: 'test:e2e:update:run'
description: 'npm target to run tests'
type: string
required: true
release_version:
description: 'Release tag to use as update target (e.g. v1.28.2). Defaults to latest.'
type: string
default: ''
required: false
permissions:
contents: read
jobs:
win-generic-server-update-e2e:
name: ${{ matrix.os }} generic server update e2e
runs-on: ${{ matrix.os }}
# disable on forks as secrets are not available
if: github.event.repository.fork == false
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
os: [windows-2025]
steps:
- name: Resolve update release version
id: release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_VERSION_INPUT: ${{ github.event.inputs.release_version }}
run: |
if ($env:RELEASE_VERSION_INPUT -ne '') {
$version = $env:RELEASE_VERSION_INPUT
} else {
$version = (gh release view --repo podman-desktop/podman-desktop --json tagName | ConvertFrom-Json).tagName
}
echo "RELEASE_VERSION=$version" >> $env:GITHUB_ENV
echo "version=$version" >> $env:GITHUB_OUTPUT
- name: Set the default env. variables
env:
DEFAULT_PD_REPO_OPTIONS: 'REPO=podman-desktop,FORK=podman-desktop,BRANCH=main'
PD_REPO_OPTIONS: ${{ github.event.inputs.pd_repo_options }}
DEFAULT_NPM_TARGET: 'test:e2e:update:run'
run: |
"NPM_TARGET=${{ github.event.inputs.npm_target || env.DEFAULT_NPM_TARGET }}" >> $env:GITHUB_ENV
$options = if ($env:PD_REPO_OPTIONS) { $env:PD_REPO_OPTIONS } else { $env:DEFAULT_PD_REPO_OPTIONS }
$options -split ',' | ForEach-Object {
$key, $val = $_ -split '=', 2
"PD_${key}=${val}"
} | Add-Content -Path $env:GITHUB_ENV
- name: Checkout podman desktop
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ env.PD_FORK }}/${{ env.PD_REPO }}
ref: ${{ env.PD_BRANCH }}
path: podman-desktop
persist-credentials: false
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
name: Install pnpm
with:
run_install: false
package_json_file: ./podman-desktop/package.json
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: 'podman-desktop/.nvmrc'
cache: 'pnpm'
cache-dependency-path: podman-desktop/pnpm-lock.yaml
- name: Execute pnpm
working-directory: ./podman-desktop
run: pnpm install
- name: Install Playwright browser
working-directory: ./podman-desktop
run: npx playwright install chromium
- name: Cache Windows update release assets
id: cache-assets
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
with:
path: update-release-assets
key: update-release-assets-${{ matrix.os }}-${{ runner.arch }}-${{ steps.release.outputs.version }}
- name: Download Windows update release assets
if: steps.cache-assets.outputs.cache-hit != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
New-Item -ItemType Directory -Force -Path update-release-assets | Out-Null
# Download latest.yml (the update metadata used by electron-updater)
gh release download $env:RELEASE_VERSION `
--repo podman-desktop/podman-desktop `
--pattern "latest.yml" `
--dir update-release-assets `
--clobber
# Parse latest.yml to find exactly which installer files are listed
$content = Get-Content "update-release-assets/latest.yml" -Raw
$fileMatches = [regex]::Matches($content, '(?m)^\s+-\s+url:\s+(.+)$')
$files = $fileMatches | ForEach-Object { $_.Groups[1].Value.Trim() }
Write-Host "Files referenced in latest.yml: $($files -join ', ')"
foreach ($file in $files) {
Write-Host "Downloading installer: $file"
gh release download $env:RELEASE_VERSION `
--repo podman-desktop/podman-desktop `
--pattern $file `
--dir update-release-assets `
--clobber
$blockmap = "$file.blockmap"
Write-Host "Downloading blockmap: $blockmap"
gh release download $env:RELEASE_VERSION `
--repo podman-desktop/podman-desktop `
--pattern $blockmap `
--dir update-release-assets `
--clobber
}
Write-Host "Downloaded assets:"
Get-ChildItem update-release-assets
- name: Adjust/Downgrade local podman desktop version
working-directory: ./podman-desktop
run: node tests/playwright/scripts/version-util.cjs ./package.json --update
- name: Build Podman Desktop locally (win nsis)
working-directory: ./podman-desktop
env:
ELECTRON_ENABLE_INSPECT: true
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pnpm compile:current --win nsis
$runnerArch = $env:RUNNER_ARCH
$unpackedPath = if ($runnerArch -eq 'ARM64') { "dist/win-arm64-unpacked" } else { "dist/win-unpacked" }
echo ("PD_DIST_PATH=" + $unpackedPath) >> $env:GITHUB_ENV
$path = (Join-Path $unpackedPath "Podman Desktop.exe" | Resolve-Path).ProviderPath
echo ("PODMAN_DESKTOP_BINARY=" + $path) >> $env:GITHUB_ENV
- name: Set update server environment variables
run: |
$assetPath = Join-Path (Get-Location) "update-release-assets"
echo "ASSET_PATH=$assetPath" >> $env:GITHUB_ENV
echo "UPDATE_SERVER_URL=http://127.0.0.1:8080" >> $env:GITHUB_ENV
- name: Configure app-update.yml to use local generic server
working-directory: ./podman-desktop
run: |
$updateFile = "$env:PD_DIST_PATH/resources/app-update.yml"
if (-Not (Test-Path $updateFile)) {
Write-Error "app-update.yml not found at $updateFile"
exit 1
}
# Replace GitHub provider with a generic provider pointing at the local server
$content = "provider: generic`nurl: $env:UPDATE_SERVER_URL`nupdaterCacheDirName: podman-desktop-updater`ntimeout: 10000"
Set-Content -Path $updateFile -Value $content -ErrorAction Stop
Write-Host "app-update.yml after update:"
Get-Content $updateFile
- name: Run E2E Update tests
working-directory: ./podman-desktop
env:
UPDATE_PODMAN_DESKTOP: true
run: |
# Start the HTTP server as a PowerShell background job so it lives for the
# full duration of this step. Start-Process children are killed by the GitHub
# Actions runner when a step exits (Windows Job Object cleanup), so we must
# own the server lifetime here. Always bind to 127.0.0.1 — never "localhost" —
# to avoid IPv6 resolution inside Electron's Chromium network stack.
$assetPath = $env:ASSET_PATH
$serverJob = Start-Job -ScriptBlock {
param($path, $port)
python -m http.server $port --bind 127.0.0.1 --directory $path 2>&1
} -ArgumentList $assetPath, 8080
Start-Sleep -Seconds 3
$resp = Invoke-WebRequest "http://127.0.0.1:8080/latest.yml" -UseBasicParsing -ErrorAction Stop
Write-Host "Update server ready — HTTP $($resp.StatusCode)"
# Verify the server is reachable from Node.js (same runtime as E2E/Electron).
# This catches IPv4/IPv6 resolution mismatches that PowerShell won't expose.
node -e @"
const http = require('http');
http.get('http://127.0.0.1:8080/latest.yml', (res) => {
let data = '';
res.on('data', chunk => { data += chunk; });
res.on('end', () => {
console.log('Node.js HTTP check: status', res.statusCode);
console.log('Content preview:', data.slice(0, 300));
process.exit(res.statusCode === 200 ? 0 : 1);
});
}).on('error', err => {
console.error('Node.js HTTP check FAILED:', err.message);
process.exit(1);
});
"@
Write-Host "Node.js HTTP check passed"
try {
echo "$env:PODMAN_DESKTOP_BINARY"
pnpm "$env:NPM_TARGET"
} finally {
Stop-Job $serverJob -ErrorAction SilentlyContinue
Remove-Job $serverJob -ErrorAction SilentlyContinue
}
- name: Create json file with workflow run attributes
if: always()
uses: jsdaniell/create-json@b8e77fa01397ca39cc4a6198cc29a3be5481afef
with:
name: "workflow-run-attributes.json"
json: |
{
"podman-desktop-version-update-target": "${{ env.RELEASE_VERSION }}",
"os": "${{ runner.os }}",
"os-version": "${{ runner.name }}",
"architecture": "${{ runner.arch }}",
"platform": "windows-nt",
"repository": "${{ env.PD_REPO }}",
"fork": "${{ env.PD_FORK }}",
"branch": "${{ env.BRANCH }}"
}
- name: Publish Test Report
uses: mikepenz/action-junit-report@d9f48fc87bc235f7e214acf696ca5abc0a986f16 # v6.4.2
if: always()
with:
fail_on_failure: true
include_passed: true
detailed_summary: true
annotate_only: true
require_tests: true
report_paths: '**/*results.xml'
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: ${{ matrix.os }}-generic-server-update-e2e
path: |
./**/tests/**/output/
!./**/tests/**/traces/raw
**/workflow-run-attributes.json
- name: Notify Slack
if: always()
continue-on-error: true
uses: podman-desktop/e2e/.github/actions/notify-slack@main
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
status: ${{ job.status }}
workflow-name: "${{ github.workflow }} ${{ matrix.os }}"
branch: "$env:PD_BRANCH"
mac-generic-server-update-e2e:
name: ${{ matrix.os }} generic server update e2e
runs-on: ${{ matrix.os }}
# disable on forks as secrets are not available
if: github.event.repository.fork == false
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
os: [macos-26]
steps:
- name: Resolve update release version
id: release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_VERSION_INPUT: ${{ github.event.inputs.release_version }}
run: |
if [ -n "$RELEASE_VERSION_INPUT" ]; then
VERSION="$RELEASE_VERSION_INPUT"
else
VERSION=$(gh release view --repo podman-desktop/podman-desktop --json tagName -q .tagName)
fi
echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Set the default env. variables
env:
DEFAULT_PD_REPO_OPTIONS: 'REPO=podman-desktop,FORK=podman-desktop,BRANCH=main'
PD_REPO_OPTIONS: ${{ github.event.inputs.pd_repo_options }}
DEFAULT_NPM_TARGET: 'test:e2e:update:run'
run: |
echo "NPM_TARGET=${{ github.event.inputs.npm_target || env.DEFAULT_NPM_TARGET }}" >> $GITHUB_ENV
echo "${PD_REPO_OPTIONS:-$DEFAULT_PD_REPO_OPTIONS}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "PD_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
- name: Checkout podman desktop
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ env.PD_FORK }}/${{ env.PD_REPO }}
ref: ${{ env.PD_BRANCH }}
path: podman-desktop
persist-credentials: false
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
name: Install pnpm
with:
run_install: false
package_json_file: ./podman-desktop/package.json
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: 'podman-desktop/.nvmrc'
cache: 'pnpm'
cache-dependency-path: podman-desktop/pnpm-lock.yaml
- name: Execute pnpm
working-directory: ./podman-desktop
run: pnpm install
- name: Install Playwright browser
working-directory: ./podman-desktop
run: npx playwright install chromium
- name: Detect runner architecture
run: |
ARCH=$(uname -m)
if [ "$ARCH" = "arm64" ]; then
echo "RUNNER_NATIVE_ARCH=arm64" >> $GITHUB_ENV
else
echo "RUNNER_NATIVE_ARCH=x64" >> $GITHUB_ENV
fi
- name: Cache macOS update release assets
id: cache-assets
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
with:
path: update-release-assets
key: update-release-assets-${{ matrix.os }}-${{ runner.arch }}-${{ steps.release.outputs.version }}
- name: Download macOS update release assets
if: steps.cache-assets.outputs.cache-hit != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p update-release-assets
# Download the full latest-mac.yml first so we can parse it
gh release download "$RELEASE_VERSION" \
--repo podman-desktop/podman-desktop \
--pattern "latest-mac.yml" \
--dir update-release-assets \
--clobber
# Parse latest-mac.yml to get zip file entries for this architecture.
# Prefer the arch-specific file; fall back to universal.
ARCH="$RUNNER_NATIVE_ARCH"
FILES=$(grep -E '^\s+-\s+url:' update-release-assets/latest-mac.yml \
| sed 's/.*url: //' \
| grep '\.zip$' \
| grep -E "(${ARCH}|universal)" \
| sort)
echo "Files to download for arch=${ARCH}: $FILES"
for file in $FILES; do
echo "Downloading: $file"
gh release download "$RELEASE_VERSION" \
--repo podman-desktop/podman-desktop \
--pattern "$file" \
--dir update-release-assets \
--clobber
echo "Downloading blockmap: ${file}.blockmap"
gh release download "$RELEASE_VERSION" \
--repo podman-desktop/podman-desktop \
--pattern "${file}.blockmap" \
--dir update-release-assets \
--clobber
done
echo "Downloaded assets:"
ls -lh update-release-assets/
- name: Filter latest-mac.yml to arch-specific entries
# Rewrite latest-mac.yml so electron-updater requests only files we
# actually downloaded. Without this it may request zip/dmg variants we
# did not cache, causing a 404 from the local server.
run: |
python3 - <<'PYEOF'
import re, os, sys
arch = os.environ['RUNNER_NATIVE_ARCH'] # 'arm64' or 'x64'
yml_path = 'update-release-assets/latest-mac.yml'
with open(yml_path) as f:
content = f.read()
version = re.search(r'^version:\s+(.+)', content, re.M).group(1).strip()
rel_date = re.search(r'^releaseDate:\s+(.+)', content, re.M).group(1).strip()
# Collect all file entries (url / sha512 / size)
entries = re.findall(
r' - url: (.+)\n sha512: (.+)\n size: (\d+)',
content
)
# Pick arch-specific zip; fall back to universal zip
chosen = [e for e in entries if arch in e[0] and e[0].endswith('.zip')]
if not chosen:
chosen = [e for e in entries if 'universal' in e[0] and e[0].endswith('.zip')]
if not chosen:
print('ERROR: no matching zip entry found in latest-mac.yml', file=sys.stderr)
sys.exit(1)
url, sha, size = chosen[0]
print(f'Using update asset: {url}')
filtered = (
f'version: {version}\n'
f'files:\n'
f' - url: {url}\n'
f' sha512: {sha}\n'
f' size: {size}\n'
f'path: {url}\n'
f'sha512: {sha}\n'
f'releaseDate: {rel_date}\n'
)
with open(yml_path, 'w') as f:
f.write(filtered)
print('Filtered latest-mac.yml:')
print(filtered)
PYEOF
- name: Start local Python HTTP update server
run: |
ASSET_PATH="$(pwd)/update-release-assets"
PORT=8080
python3 -m http.server "$PORT" --bind 127.0.0.1 --directory "$ASSET_PATH" &
SERVER_PID=$!
echo "UPDATE_SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
echo "UPDATE_SERVER_URL=http://127.0.0.1:$PORT" >> $GITHUB_ENV
# Give the server a moment to bind
sleep 3
# Smoke-check that the server is up and serving latest-mac.yml
curl -fs "http://127.0.0.1:$PORT/latest-mac.yml" | head -3
- name: Adjust/Downgrade local podman desktop version
working-directory: ./podman-desktop
run: |
node tests/playwright/scripts/version-util.cjs ./package.json --update
actualVersion=$(jq -r '.version' package.json)
echo "PD_VERSION=$actualVersion" >> $GITHUB_ENV
- name: Build Podman Desktop locally (mac dmg)
working-directory: ./podman-desktop
env:
ELECTRON_ENABLE_INSPECT: true
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pnpm compile:current --mac dmg
dmgPath=$(find ./dist -maxdepth 1 -name "*.dmg" -print -quit)
if [ -z "$dmgPath" ]; then
echo "ERROR: no .dmg produced in ./dist"
exit 1
fi
dmgPath=$(realpath "$dmgPath")
echo "DMG Path: $dmgPath"
hdiutil attach "$dmgPath"
pdVolumePath=$(find /Volumes -name "*$PD_VERSION*")
echo "PD Volume path: $pdVolumePath"
sudo cp -R "$pdVolumePath/Podman Desktop.app" /Applications
appPath="/Applications/Podman Desktop.app"
sudo codesign --force --deep --sign - "$appPath"
codesign --verify --deep --verbose=2 "$appPath"
echo "PD_APP_PATH=$appPath" >> $GITHUB_ENV
echo "PODMAN_DESKTOP_BINARY=$appPath/Contents/MacOS/Podman Desktop" >> $GITHUB_ENV
- name: Configure app-update.yml to use local generic server
working-directory: ./podman-desktop
run: |
UPDATE_FILE="$PD_APP_PATH/Contents/Resources/app-update.yml"
if [ ! -f "$UPDATE_FILE" ]; then
echo "ERROR: app-update.yml not found at $UPDATE_FILE"
exit 1
fi
# Replace GitHub provider with a generic provider pointing at the local server.
# Use printf to avoid heredoc indentation artefacts from YAML block scalars.
printf 'provider: generic\nurl: %s\nupdaterCacheDirName: podman-desktop-updater\ntimeout: 10000\n' \
"$UPDATE_SERVER_URL" | sudo tee "$UPDATE_FILE" > /dev/null
echo "app-update.yml after update:"
cat "$UPDATE_FILE"
- name: Run E2E Update tests
working-directory: ./podman-desktop
env:
UPDATE_PODMAN_DESKTOP: true
run: |
echo "$PODMAN_DESKTOP_BINARY"
pnpm ${{ env.NPM_TARGET }}
- name: Stop local HTTP update server
if: always()
run: |
if [ -n "$UPDATE_SERVER_PID" ]; then
kill "$UPDATE_SERVER_PID" 2>/dev/null || true
fi
- name: Create json file with workflow run attributes
if: always()
uses: jsdaniell/create-json@b8e77fa01397ca39cc4a6198cc29a3be5481afef
with:
name: "workflow-run-attributes.json"
json: |
{
"podman-desktop-version-update-target": "${{ env.RELEASE_VERSION }}",
"os": "${{ runner.os }}",
"os-version": "${{ runner.name }}",
"architecture": "${{ runner.arch }}",
"platform": "darwin",
"repository": "${{ env.PD_REPO }}",
"fork": "${{ env.PD_FORK }}",
"branch": "${{ env.BRANCH }}"
}
- name: Publish Test Report
uses: mikepenz/action-junit-report@d9f48fc87bc235f7e214acf696ca5abc0a986f16 # v6.4.2
if: always()
with:
fail_on_failure: true
include_passed: true
detailed_summary: true
annotate_only: true
require_tests: true
report_paths: '**/*results.xml'
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: ${{ matrix.os }}-generic-server-update-e2e
path: |
./**/tests/**/output/
!./**/tests/**/traces/raw
**/workflow-run-attributes.json
- name: Notify Slack
if: always()
continue-on-error: true
uses: podman-desktop/e2e/.github/actions/notify-slack@main
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
status: ${{ job.status }}
workflow-name: "${{ github.workflow }} ${{ matrix.os }}"
branch: ${{ env.PD_BRANCH }}