Files
pebrel/.github/workflows/release.yml
T
kuddev 97f829844d ci: stop repeating the native test matrix inside the stable release run
The release run re-invoked the Full native tests workflow and waited for it
before aggregating packages. That workflow already runs for every pull
request, merge group and main push, so the repeat added no coverage while
its macOS Intel job queued behind the Intel package build and became the
critical path (60 minutes end to end, packages done after 28). Aggregation
now depends on the packaging jobs only; each package still runs its own
runtime conformance and the aggregate still validates every report.
2026-09-22 21:46:08 +08:00

730 lines
32 KiB
YAML

name: Pebrel stable release
run-name: >-
Pebrel ${{ inputs.version || github.ref_name }} stable release
on:
push:
tags: ["v*.*.*"]
workflow_dispatch:
inputs:
version:
description: Stable Cargo package version to build and publish
required: true
type: string
publish:
description: Create the public GitHub Release after every package check passes
required: true
default: false
type: boolean
windows_only:
description: Build only the missing Windows package, retaining other platform artifacts
type: boolean
default: false
source_commit:
description: Exact previously tested source commit for Windows-only packaging
type: string
default: ""
validated_run:
description: Existing stable or Full native tests run whose Windows tests passed for source_commit
type: string
default: ""
macos_signing:
description: macOS signing mode; Developer ID also requires notarization secrets
required: true
type: choice
options: [adhoc, developer-id]
default: adhoc
concurrency:
group: stable-release-${{ github.ref }}-${{ inputs.windows_only || false }}
cancel-in-progress: false
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: "1"
RUST_TOOLCHAIN: "1.97.1"
PYTHONUTF8: "1"
MACOS_SIGNING: ${{ inputs.macos_signing || 'adhoc' }}
jobs:
prepare:
name: Validate stable release inputs
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.metadata.outputs.version }}
asset_version: ${{ steps.metadata.outputs.asset_version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Resolve and validate package metadata
id: metadata
env:
REQUESTED_VERSION: ${{ inputs.version }}
EVENT_NAME: ${{ github.event_name }}
REF_NAME: ${{ github.ref_name }}
run: |
version="$(python3 scripts/preview_release.py --print-package-version nebula_app/Cargo.toml)"
if [[ -n "$REQUESTED_VERSION" && "$REQUESTED_VERSION" != "$version" ]]; then
echo "requested version $REQUESTED_VERSION does not match Cargo package version $version" >&2
exit 1
fi
if [[ "$EVENT_NAME" == push && "$REF_NAME" != "v$version" ]]; then
echo "release tag $REF_NAME does not match Cargo package version $version" >&2
exit 1
fi
VERSION="$version" python3 - <<'PY'
import os
from scripts.stable_release import validate_version
validate_version(os.environ["VERSION"])
if "-" in os.environ["VERSION"] or "+" in os.environ["VERSION"]:
raise SystemExit("stable releases require a plain semver without prerelease or build suffix")
PY
python3 - <<'PY'
from pathlib import Path
from scripts.stable_release import validate_changelog, validate_notes
import subprocess
version = subprocess.check_output(
["python3", "scripts/preview_release.py", "--print-package-version", "nebula_app/Cargo.toml"],
text=True,
).strip()
validate_notes(Path(f"docs/release-notes/v{version}.md"), version)
validate_changelog(Path("CHANGELOG.md"), version)
PY
{
echo "version=$version"
echo "asset_version=$version"
} >>"$GITHUB_OUTPUT"
- name: Check architecture and release helper contracts
run: |
python3 scripts/check_architecture.py
python3 scripts/check_platform_cfg.py
python3 -m unittest \
scripts.tests.test_stable_release \
scripts.tests.test_ci_native_tests \
scripts.tests.test_preview_release \
scripts.tests.test_macos_dmg \
scripts.tests.test_ai_session_probe \
scripts.tests.test_platform_cfg \
scripts.tests.test_windows_resources \
scripts.tests.test_pebrel_branding \
scripts.tests.test_app_icons \
scripts.tests.test_git_attributes \
scripts.tests.test_xplat
bash -n scripts/package-linux.sh scripts/package-macos.sh
python3 -m py_compile scripts/stable_release.py scripts/preview_release.py
linux:
name: Linux x86_64 stable package
if: ${{ !inputs.windows_only }}
needs: prepare
runs-on: ubuntu-22.04
timeout-minutes: 120
env:
PACKAGE_VERSION: ${{ needs.prepare.outputs.version }}
LINUXDEPLOY_VERSION: 1-alpha-20251107-1
LINUXDEPLOY_SHA256: c20cd71e3a4e3b80c3483cef793cda3f4e990aca14014d23c544ca3ce1270b4d
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install build, package, and headless runtime dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
appstream build-essential cmake curl desktop-file-utils dpkg-dev \
imagemagick libasound2-dev libdbus-1-dev libegl1-mesa-dev \
libfontconfig1-dev libfreetype6-dev libgl1-mesa-dev libudev-dev \
libvulkan1 libwayland-dev libx11-dev libx11-xcb-dev \
libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
libxcb-xinput-dev libxcb-xkb-dev libxcb1-dev libxi-dev \
libxkbcommon-dev libxkbcommon-x11-dev mesa-vulkan-drivers \
patchelf pkg-config weston xauth xvfb zsh
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
- uses: ./.github/actions/rust-cache
id: rust-cache
with:
key: release
target: target/release
legacy-key: linux
- name: Build GPUI release binary
run: cargo build --locked --release -p nebula --bin pebrel --features gpui-shell --timings
- name: Save compiled workload
if: ${{ always() && steps.rust-cache.outcome == 'success' && steps.rust-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: target/release
key: ${{ steps.rust-cache.outputs.cache-key }}
- name: Download pinned linuxdeploy
env:
LINUXDEPLOY: ${{ runner.temp }}/linuxdeploy-x86_64.AppImage
run: |
curl --fail --location --retry 3 --retry-all-errors --show-error \
--output "$LINUXDEPLOY" \
"https://github.com/linuxdeploy/linuxdeploy/releases/download/$LINUXDEPLOY_VERSION/linuxdeploy-x86_64.AppImage"
echo "$LINUXDEPLOY_SHA256 $LINUXDEPLOY" | sha256sum --check --strict
chmod 0755 "$LINUXDEPLOY"
echo "LINUXDEPLOY=$LINUXDEPLOY" >>"$GITHUB_ENV"
- name: Package stable Linux assets
run: |
SOURCE_DATE_EPOCH="$(git show -s --format=%ct "$GITHUB_SHA")"
export SOURCE_DATE_EPOCH
bash scripts/package-linux.sh \
--channel stable \
--binary target/release/pebrel \
--version "$PACKAGE_VERSION" \
--output-directory dist \
--linuxdeploy "$LINUXDEPLOY" \
--force
- name: Run AppImage conformance under X11
env:
NEBULA_RESIZE_TRACE: "1"
run: |
xvfb-run -a -s "-screen 0 1600x1000x24" \
python3 scripts/conformance/run.py \
--app "dist/Pebrel-v$PACKAGE_VERSION-linux-x64.AppImage" \
--platform linux-x86_64-appimage \
--output dist/conformance/linux-appimage-report.json
- name: Run AppImage conformance under Wayland
env:
NEBULA_RESIZE_TRACE: "1"
run: |
xvfb-run -a -s "-screen 0 1600x1000x24" bash -euo pipefail <<'WAYLAND_SESSION'
runtime_directory="$RUNNER_TEMP/pebrel-wayland-runtime"
mkdir -p "$runtime_directory" dist/conformance
chmod 0700 "$runtime_directory"
XDG_RUNTIME_DIR="$runtime_directory" weston --backend=x11-backend.so \
--use-pixman --no-config --socket=wayland-pebrel --width=1600 --height=1000 \
--idle-time=0 --log=dist/conformance/weston.log &
weston_pid=$!
trap 'kill "$weston_pid" >/dev/null 2>&1 || true; wait "$weston_pid" >/dev/null 2>&1 || true' EXIT
for _ in $(seq 1 100); do
[[ -S "$runtime_directory/wayland-pebrel" ]] && break
kill -0 "$weston_pid" >/dev/null 2>&1 || exit 1
sleep 0.1
done
test -S "$runtime_directory/wayland-pebrel"
DISPLAY='' XDG_RUNTIME_DIR="$runtime_directory" WAYLAND_DISPLAY=wayland-pebrel \
python3 scripts/conformance/run.py \
--app "dist/Pebrel-v$PACKAGE_VERSION-linux-x64.AppImage" \
--platform linux-x86_64-appimage-wayland \
--output dist/conformance/linux-appimage-wayland-report.json
WAYLAND_SESSION
- name: Install and run Debian conformance
env:
NEBULA_RESIZE_TRACE: "1"
run: |
sudo apt-get install -y "./dist/Pebrel-v$PACKAGE_VERSION-linux-x64.deb"
test "$(dpkg-query -W -f='${Status}' pebrel)" = "install ok installed"
xvfb-run -a -s "-screen 0 1600x1000x24" \
python3 scripts/conformance/run.py --app /usr/bin/pebrel \
--platform linux-x86_64-deb --output dist/conformance/linux-deb-report.json
- name: Upload Linux stable assets
if: always()
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: stable-package-linux-x64
path: |
dist/*.AppImage
dist/*.deb
dist/*.tar.gz
if-no-files-found: error
retention-days: 14
- name: Upload Linux conformance evidence
if: always()
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: stable-evidence-linux-x64
path: dist/conformance
if-no-files-found: warn
retention-days: 14
macos:
name: macOS ${{ matrix.architecture }} stable package
if: ${{ !inputs.windows_only }}
needs: prepare
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- runner: macos-26
architecture: aarch64
native_architecture: arm64
asset_architecture: arm64
- runner: macos-26-intel
architecture: x86_64
native_architecture: x86_64
asset_architecture: x64
runs-on: ${{ matrix.runner }}
env:
PACKAGE_VERSION: ${{ needs.prepare.outputs.version }}
MACOSX_DEPLOYMENT_TARGET: "14.0"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Verify native runner architecture
run: test "$(uname -m)" = "${{ matrix.native_architecture }}"
- name: Require the modern AppKit SDK for native window controls
run: |
sdk_version="$(xcrun --sdk macosx --show-sdk-version)"
test "${sdk_version%%.*}" -ge 26
- name: Test native DMG capacity and mounted contents
run: python3 -m unittest scripts.tests.test_macos_dmg -v
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
- uses: ./.github/actions/rust-cache
id: rust-cache
with:
key: release
target: target/release
legacy-key: macos
- name: Build GPUI release binary
run: cargo build --locked --release -p nebula --bin pebrel --features gpui-shell --timings
- name: Save compiled workload
if: ${{ always() && steps.rust-cache.outcome == 'success' && steps.rust-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: target/release
key: ${{ steps.rust-cache.outputs.cache-key }}
- name: Import Developer ID signing credentials
if: ${{ env.MACOS_SIGNING == 'developer-id' }}
env:
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: |
export SIGNING_DIRECTORY="$RUNNER_TEMP/pebrel-signing"
mkdir -m 0700 "$SIGNING_DIRECTORY"
python3 - <<'PY'
import base64
import os
from pathlib import Path
required = ["APPLE_CERTIFICATE_BASE64", "APPLE_CERTIFICATE_PASSWORD", "APPLE_SIGNING_IDENTITY",
"APPLE_API_KEY_BASE64", "APPLE_API_KEY_ID", "APPLE_API_ISSUER"]
if any(not os.environ.get(name) for name in required):
raise SystemExit("missing required Developer ID signing secret")
root = Path(os.environ["SIGNING_DIRECTORY"])
for name, filename in [("APPLE_CERTIFICATE_BASE64", "certificate.p12"), ("APPLE_API_KEY_BASE64", "AuthKey.p8")]:
path = root / filename
path.write_bytes(base64.b64decode(os.environ[name], validate=True))
path.chmod(0o600)
PY
keychain="$SIGNING_DIRECTORY/release.keychain-db"
password="$(openssl rand -hex 32)"
echo "::add-mask::$password"
security create-keychain -p "$password" "$keychain"
security set-keychain-settings -lut 21600 "$keychain"
security unlock-keychain -p "$password" "$keychain"
security import "$SIGNING_DIRECTORY/certificate.p12" -k "$keychain" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$password" "$keychain"
xcrun notarytool store-credentials pebrel-release --keychain "$keychain" \
--key "$SIGNING_DIRECTORY/AuthKey.p8" --key-id "$APPLE_API_KEY_ID" --issuer "$APPLE_API_ISSUER"
echo "PEBREL_SIGNING_KEYCHAIN=$keychain" >>"$GITHUB_ENV"
- name: Package stable macOS DMG
env:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
run: |
signing_args=()
if [[ "$MACOS_SIGNING" == developer-id ]]; then
test -n "$PEBREL_SIGNING_KEYCHAIN"
signing_args=(--sign-identity "$APPLE_SIGNING_IDENTITY" --notary-profile pebrel-release --signing-keychain "$PEBREL_SIGNING_KEYCHAIN")
fi
bash scripts/package-macos.sh \
--channel stable \
--binary target/release/pebrel \
--version "$PACKAGE_VERSION" \
--architecture "${{ matrix.architecture }}" \
--build-number "$GITHUB_RUN_NUMBER" \
--output-directory dist --force "${signing_args[@]}"
- name: Mount DMG and run conformance
env:
NEBULA_RESIZE_TRACE: "1"
run: |
dmg="dist/Pebrel-v$PACKAGE_VERSION-macos-${{ matrix.asset_architecture }}.dmg"
mount_point="$RUNNER_TEMP/pebrel-stable-${{ matrix.architecture }}"
mkdir -p "$mount_point"
hdiutil attach "$dmg" -nobrowse -readonly -mountpoint "$mount_point"
trap 'hdiutil detach "$mount_point" >/dev/null 2>&1 || hdiutil detach "$mount_point" -force >/dev/null 2>&1 || true' EXIT
app="$mount_point/Pebrel.app"
codesign --verify --deep --strict --verbose=2 "$app"
plutil -lint "$app/Contents/Info.plist"
test "$(plutil -extract CFBundleIdentifier raw -o - "$app/Contents/Info.plist")" = "io.github.kuddev.pebrel"
test "$(lipo -archs "$app/Contents/MacOS/pebrel")" = "${{ matrix.native_architecture }}"
python3 scripts/conformance/run.py --app "$app" --platform "macos-${{ matrix.architecture }}" \
--output "dist/conformance/macos-${{ matrix.architecture }}-report.json"
python3 scripts/conformance/macos_launch.py --app "$app" \
--output "dist/conformance/macos-${{ matrix.architecture }}-launch.json"
- name: Remove temporary signing credentials
if: ${{ always() && env.MACOS_SIGNING == 'developer-id' }}
run: |
if [[ -f "$RUNNER_TEMP/pebrel-signing/release.keychain-db" ]]; then
security delete-keychain "$RUNNER_TEMP/pebrel-signing/release.keychain-db"
fi
rm -rf -- "$RUNNER_TEMP/pebrel-signing"
- name: Upload macOS stable asset
if: always()
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: stable-package-macos-${{ matrix.asset_architecture }}
path: dist/*.dmg
if-no-files-found: error
retention-days: 14
- name: Upload macOS conformance evidence
if: always()
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: stable-evidence-macos-${{ matrix.architecture }}
path: dist/conformance
if-no-files-found: warn
retention-days: 14
windows:
name: Windows x64 stable package
needs: prepare
runs-on: windows-2022
timeout-minutes: 120
permissions:
contents: read
actions: read
env:
PACKAGE_VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Retain runtime preparation tool for the selected source
shell: pwsh
run: |
Copy-Item scripts/prepare-windows-runtime.ps1 "$env:RUNNER_TEMP/prepare-windows-runtime.ps1"
Copy-Item scripts/conformance/windows_standard_user.py "$env:RUNNER_TEMP/windows-standard-user.py"
New-Item -ItemType Directory -Path "$env:RUNNER_TEMP/runtime-tests" -Force | Out-Null
Copy-Item scripts/tests/windows-runtime.tests.ps1 "$env:RUNNER_TEMP/runtime-tests/windows-runtime.tests.ps1"
- name: Verify the previous Windows test result
if: ${{ inputs.source_commit != '' }}
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
SELECTED_COMMIT: ${{ inputs.source_commit }}
VALIDATED_RUN: ${{ inputs.validated_run }}
WINDOWS_ONLY: ${{ inputs.windows_only }}
run: |
if ($env:WINDOWS_ONLY -ne 'true' -or $env:SELECTED_COMMIT -notmatch '^[0-9a-f]{40}$' -or $env:VALIDATED_RUN -notmatch '^[0-9]+$') {
throw 'Selecting old source requires Windows-only mode, an exact commit, and a validated run.'
}
$headers = @{ Authorization = "Bearer $env:GH_TOKEN"; Accept = 'application/vnd.github+json'; 'X-GitHub-Api-Version' = '2022-11-28' }
$run = Invoke-RestMethod -Uri "https://api.github.com/repos/$env:GITHUB_REPOSITORY/actions/runs/$env:VALIDATED_RUN" -Headers $headers -ErrorAction Stop
if ($run.head_sha -ne $env:SELECTED_COMMIT) { throw 'Validated run source differs.' }
$jobs = Invoke-RestMethod -Uri "https://api.github.com/repos/$env:GITHUB_REPOSITORY/actions/runs/$env:VALIDATED_RUN/jobs?per_page=100" -Headers $headers -ErrorAction Stop
$windows = @($jobs.jobs | Where-Object { $_.name -in @('Windows x64 stable package', 'Native tests (windows-x64)') -or $_.name -match 'Tests \(windows-2022\)$' })
$tests = @($windows.steps | Where-Object { $_.name -in @('Test workspace and native harness', 'Test complete workspace and native harness') -and $_.conclusion -eq 'success' })
if ($tests.Count -ne 1) { throw 'Prior Windows native tests did not pass.' }
- uses: actions/checkout@v4
if: ${{ inputs.source_commit != '' }}
with:
ref: ${{ inputs.source_commit }}
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
- uses: ./.github/actions/rust-cache
id: rust-cache
with:
key: release
target: target/release
legacy-key: windows
if: ${{ inputs.source_commit == '' }}
- uses: Swatinem/rust-cache@v2
if: ${{ inputs.source_commit != '' }}
with:
cache-on-failure: true
- name: Prepare pinned Windows console runtime
shell: pwsh
run: |
& "$env:RUNNER_TEMP/prepare-windows-runtime.ps1" -Destination assets/windows/conhost
& "$env:RUNNER_TEMP/runtime-tests/windows-runtime.tests.ps1" -ArchivePath (Join-Path ([IO.Path]::GetTempPath()) 'pebrel-conpty-source-v1.5.0.zip')
- name: Test workspace and native harness
if: ${{ inputs.windows_only && inputs.source_commit == '' }}
run: python scripts/ci_native_tests.py
- name: Build GPUI release binary
shell: pwsh
run: |
if (Test-Path scripts/build-windows-product.ps1) {
./scripts/build-windows-product.ps1 -Configuration release -TargetDirectory target
} else {
cargo build --locked --release -p nebula --bin pebrel -p nebula_hook --bin pebrel-hook --features nebula/gpui-shell
if ($LASTEXITCODE -ne 0) { throw 'Windows product build failed' }
}
- name: Save compiled workload
if: ${{ always() && steps.rust-cache.outcome == 'success' && steps.rust-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: target/release
key: ${{ steps.rust-cache.outputs.cache-key }}
- name: Retain built Windows runtime before packaging
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: stable-runtime-windows-x64
path: |
target/release/pebrel.exe
target/release/pebrel-hook.exe
target/release/conpty.dll
target/release/OpenConsole.exe
if-no-files-found: error
retention-days: 30
- name: Test Windows packaging scripts
shell: pwsh
run: |
./scripts/tests/installer.tests.ps1 -TargetDirectory target
./scripts/tests/package-release.tests.ps1 -TargetDirectory target
- name: Package ZIP and installer
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
./scripts/package-release.ps1 -Version $env:PACKAGE_VERSION -PackageBrand Pebrel -OutputDirectory dist -TargetDirectory target -Force
./scripts/build-installer.ps1 -Version $env:PACKAGE_VERSION -PackageBrand Pebrel -OutputDirectory dist -TargetDirectory target -Force
- name: Run Windows conformance
shell: pwsh
env:
NEBULA_RESIZE_TRACE: "1"
SELECTED_COMMIT: ${{ inputs.source_commit || github.sha }}
run: |
$actual = git rev-parse HEAD
if ($actual -ne $env:SELECTED_COMMIT) { throw 'Checked-out source differs from the requested build.' }
$env:GITHUB_SHA = $actual
python "$env:RUNNER_TEMP/windows-standard-user.py" scripts/conformance/run.py --app target/release/pebrel.exe --platform windows-x86_64 --output dist/conformance/windows-report.json
exit $LASTEXITCODE
- name: Upload Windows stable assets
if: always()
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: stable-package-windows-x64
path: |
dist/Pebrel-v${{ needs.prepare.outputs.version }}-windows-x64.zip
dist/Pebrel-v${{ needs.prepare.outputs.version }}-windows-x64-setup.exe
if-no-files-found: error
retention-days: 14
- name: Upload Windows conformance evidence
if: always()
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: stable-evidence-windows-x64
path: dist/conformance
if-no-files-found: warn
retention-days: 14
windows-arm64:
name: Windows ARM64 stable portable package
if: ${{ !inputs.windows_only }}
needs: prepare
runs-on: windows-11-arm
timeout-minutes: 120
env:
PACKAGE_VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
- name: Require native Windows ARM64
shell: pwsh
run: |
if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -ne 'Arm64') {
throw 'ARM64 packaging requires an ARM64 host.'
}
$compiler = rustc -vV
if ($LASTEXITCODE -ne 0 -or $compiler -notcontains 'host: aarch64-pc-windows-msvc') {
throw 'ARM64 packaging requires a native ARM64 compiler.'
}
- uses: ./.github/actions/rust-cache
id: rust-cache
with:
key: release
target: target/release
- name: Prepare and verify pinned ARM64 console runtime
shell: pwsh
run: |
./scripts/prepare-windows-runtime.ps1 -Destination assets/windows/conhost -Architecture arm64
./scripts/tests/windows-runtime.tests.ps1 -Architecture arm64
./scripts/tests/windows-package-architecture.tests.ps1
- name: Build native ARM64 application and hook
shell: pwsh
run: ./scripts/build-windows-product.ps1 -Configuration release -TargetDirectory target
- name: Save compiled workload
if: ${{ always() && steps.rust-cache.outcome == 'success' && steps.rust-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: target/release
key: ${{ steps.rust-cache.outputs.cache-key }}
- name: Package native ARM64 ZIP
shell: pwsh
run: ./scripts/package-release.ps1 -Version $env:PACKAGE_VERSION -Architecture arm64 -OutputDirectory dist -TargetDirectory target
- name: Keep Windows PowerShell alive on the ARM64 runner image
shell: pwsh
run: ./scripts/conformance/windows_arm64_module_path.ps1
- name: Run ARM64 Windows conformance
shell: pwsh
env:
NEBULA_RESIZE_TRACE: "1"
NEBULA_BOOT_TRACE: "1"
run: |
python scripts/conformance/windows_standard_user.py scripts/conformance/run.py --app target/release/pebrel.exe --platform windows-aarch64 --output dist/conformance/windows-arm64-report.json
exit $LASTEXITCODE
- name: Upload Windows ARM64 stable asset
if: always()
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: stable-package-windows-arm64
path: dist/Pebrel-v${{ needs.prepare.outputs.version }}-windows-arm64.zip
if-no-files-found: error
retention-days: 14
- name: Upload Windows ARM64 conformance evidence
if: always()
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: stable-evidence-windows-arm64
path: dist/conformance
if-no-files-found: warn
retention-days: 14
aggregate:
name: Verify stable assets and upload complete set
if: ${{ !inputs.windows_only }}
needs: [prepare, linux, macos, windows, windows-arm64]
runs-on: ubuntu-22.04
env:
PACKAGE_VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- uses: actions/download-artifact@v4
with:
pattern: stable-package-*
merge-multiple: true
path: release-dist
- uses: actions/download-artifact@v4
with:
pattern: stable-evidence-*
merge-multiple: true
path: stable-evidence
- name: Validate conformance evidence and stable manifest
env:
SOURCE_COMMIT: ${{ github.sha }}
run: |
python3 - <<'PY'
import os
from pathlib import Path
from scripts.preview_release import validate_evidence
validate_evidence(Path("stable-evidence"), os.environ["SOURCE_COMMIT"], windows_arm64=True)
PY
python3 scripts/stable_release.py release-dist \
--version "$PACKAGE_VERSION" \
--mark-platform-previews \
--notes "docs/release-notes/v$PACKAGE_VERSION.md" \
--changelog CHANGELOG.md \
--write-checksums release-dist/SHA256SUMS \
--write-notes release-dist/RELEASE_NOTES.md
- name: Upload complete stable release set
uses: actions/upload-artifact@v4
with:
compression-level: 0
name: Pebrel-v${{ needs.prepare.outputs.version }}
path: release-dist
if-no-files-found: error
retention-days: 30
publish:
name: Publish verified GitHub Release
if: ${{ !inputs.windows_only && (github.event_name == 'push' || inputs.publish == true) }}
needs: [prepare, aggregate]
runs-on: ubuntu-22.04
permissions:
contents: write
env:
PACKAGE_VERSION: ${{ needs.prepare.outputs.version }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- uses: actions/download-artifact@v4
with:
name: Pebrel-v${{ needs.prepare.outputs.version }}
path: release-dist
- name: Create public stable Release
run: |
tag="v$PACKAGE_VERSION"
if gh release view "$tag" >/dev/null 2>&1; then
echo "refusing to replace existing Release: $tag" >&2
exit 1
fi
existing_tag_response="$RUNNER_TEMP/stable-existing-tag.json"
existing_tag_error="$RUNNER_TEMP/stable-existing-tag-error.log"
if gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$tag" >"$existing_tag_response" 2>"$existing_tag_error"; then
existing_tag_ref="$(jq -er '.object | select(.type == "tag" or .type == "commit") | .type + " " + .sha' "$existing_tag_response")"
elif jq -e '(.status | tostring) == "404"' "$existing_tag_response" >/dev/null; then
existing_tag_ref=""
else
cat "$existing_tag_error" >&2
exit 1
fi
existing_tag_commit=""
if [[ -n "$existing_tag_ref" ]]; then
read -r existing_tag_type existing_tag_object <<<"$existing_tag_ref"
if [[ "$existing_tag_type" == tag ]]; then
existing_tag_commit="$(gh api "repos/$GITHUB_REPOSITORY/git/tags/$existing_tag_object" --jq .object.sha)"
else
existing_tag_commit="$existing_tag_object"
fi
fi
if [[ -n "$existing_tag_commit" && "$existing_tag_commit" != "$GITHUB_SHA" ]]; then
echo "tag $tag already points to $existing_tag_commit, expected $GITHUB_SHA" >&2
exit 1
fi
gh release create "$tag" \
release-dist/*.AppImage release-dist/*.deb release-dist/*.dmg \
release-dist/*.tar.gz release-dist/*.zip release-dist/*-setup.exe \
release-dist/SHA256SUMS \
--target "$GITHUB_SHA" --title "Pebrel $PACKAGE_VERSION" \
--notes-file release-dist/RELEASE_NOTES.md
gh api "repos/$GITHUB_REPOSITORY/releases/tags/$tag" >"$RUNNER_TEMP/stable-release.json"
tag_ref="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$tag" --jq '.object.type + " " + .object.sha')"
read -r tag_type tag_object <<<"$tag_ref"
if [[ "$tag_type" == tag ]]; then
tag_commit="$(gh api "repos/$GITHUB_REPOSITORY/git/tags/$tag_object" --jq .object.sha)"
else
tag_commit="$tag_object"
fi
python3 scripts/stable_release.py release-dist --version "$PACKAGE_VERSION" \
--verify-release "$RUNNER_TEMP/stable-release.json" \
--commit "$GITHUB_SHA" --tag-commit "$tag_commit"