App-Ariza.git | t/golden/ | ci-release.yml
# Build a self-contained Example App bundle for every platform
# App::ExampleApp declares in ariza.toml, prove each one runs, and publish
# them on a tag.
#
# Generated by ariza 0.0.1 (`ariza scaffold-ci`) from these
# bundle.platforms:
#
# macos-arm64, linux-x86_64-glibc, windows-x86_64
#
# Re-run scaffold-ci after changing that list: this file is rewritten in
# place, so a hand edit here is an edit you will make twice.
#
# Two triggers, and only one of them publishes anything:
#
# * workflow_dispatch builds and smokes every lane and stops there,
# with an optional `ref` so a recipe can be iterated on a branch.
# A broken lane costs a run, not a tag.
#
# * a pushed release tag (v* or [0-9]*.[0-9]*.[0-9]* — the
# v-prefixed shape humans write, and the bare one mi6 creates)
# does all of that, then publishes the
# archives, then installs the published one on a clean machine.
name: release
on:
workflow_dispatch:
inputs:
ref:
description: >-
Branch, tag or SHA to build. Defaults to the ref the run was
started from.
required: false
type: string
push:
tags:
- 'v*'
- '[0-9]*.[0-9]*.[0-9]*'
permissions:
contents: write
env:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
jobs:
bundle-macos-arm64:
name: bundle macos-arm64
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
# This Raku runs ariza. It is NOT the runtime that ends up in the
# bundle -- ariza downloads the pinned one (2026.07-01) from
# rakudo.org itself, so this lane's Rakudo version has no bearing
# on what a user gets.
- uses: Raku/setup-raku@v1
with:
raku-version: 'latest'
- name: Install ariza
run: zef install --/test 'App::Ariza:ver<0.2.2+>:auth<zef:apogee>'
# Scaffolded by ariza 0.0.1. Before App::Ariza is
# published, or to cut a release against an unreleased ariza,
# install it from the repository instead:
# run: zef install --/test https://github.com/m-doughty/App-Ariza.git
# ariza takes SQLCipher from the build machine's package manager --
# there is no ariza-operated mirror -- and makes the copy it stages
# self-contained, rewriting its OpenSSL dependency to @loader_path
# and re-signing it. `SQLCIPHER_LIB_DIR=<dir>` overrides this if a
# particular build is ever needed instead.
- name: Install SQLCipher
run: brew install sqlcipher
- name: Build the bundle
run: ariza bundle --app=. --platform=macos-arm64 --out-dir=dist-out
# Unpacks the archive somewhere new, with a replaced environment,
# and runs App::ExampleApp's own bundle.smoke commands against it.
- name: Smoke the bundle
run: |
set -euo pipefail
archive=$(ls dist-out/*.tar.gz)
ariza smoke --archive="$archive"
- name: Upload
uses: actions/upload-artifact@v6
with:
name: macos-arm64
path: |
dist-out/*.tar.gz
dist-out/*.tar.gz.sha256
if-no-files-found: error
bundle-linux-x86_64-glibc:
name: bundle linux-x86_64-glibc
runs-on: ubuntu-latest
# Built in manylinux_2_28 (AlmaLinux 8) so the bundle's glibc floor is
# 2.28 -- RHEL 8+, Ubuntu 18.10+, Debian 10+ -- rather than whatever
# ubuntu-latest happens to ship this month. A bundle cannot be older
# than the machine that built it, and this is the oldest still-
# maintained baseline worth targeting.
#
# `container:` rather than `docker run` from the host: glibc 2.28 meets
# the floor GitHub's JavaScript actions need, so checkout and
# upload-artifact run in here unmodified. (Notcurses-Native drives
# docker directly instead, because its musl lane cannot.)
container: quay.io/pypa/manylinux_2_28_x86_64
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
# python3 reads the Rakudo release index below; tcl and
# openssl3-devel build SQLCipher. The image already has ldd, readelf
# and patchelf, which ariza needs to make the staged library
# self-contained and to audit the result.
- name: Install build dependencies
run: dnf install -y python3 tcl openssl3-devel
# Raku/setup-raku does not run in a container -- it installs into the
# runner's tool cache, which is outside this filesystem -- so the
# official rakudo.org archive is fetched by hand. It is resolved out
# of the same JSON release index App::Ariza::Rakudo reads, at the
# same pin (2026.07-01), because there is no URL pattern to
# construct: upstream filenames carry a toolchain suffix.
#
# This Raku runs ariza. The runtime that ends up in the bundle is a
# second copy ariza downloads for itself.
- name: Install Rakudo 2026.07-01
run: |
set -euo pipefail
# rakudo.org rejects urllib's default user agent, so: curl.
curl -fsSL -A 'ariza-ci' https://rakudo.org/dl/rakudo \
-o /tmp/rakudo-index.json
url=$(python3 - <<'PY'
import json
index = json.load(open("/tmp/rakudo-index.json"))
want = [x for x in index
if x.get("platform") == "linux" and x.get("arch") == "x86_64"
and x.get("type") == "archive" and x.get("backend") == "moar"
and x.get("ver") == "2026.07"
and int(x.get("build_rev", -1)) == 1]
if len(want) != 1:
raise SystemExit(
"expected exactly one Rakudo 2026.07-01 linux/x86_64 "
"archive in the release index, found %d" % len(want))
print(want[0]["url"])
PY
)
echo "rakudo: $url"
curl -fsSL "$url" -o /tmp/rakudo.tar.gz
mkdir -p /opt/rakudo
tar xzf /tmp/rakudo.tar.gz -C /opt/rakudo --strip-components=1
echo /opt/rakudo/bin >> "$GITHUB_PATH"
echo /opt/rakudo/share/perl6/site/bin >> "$GITHUB_PATH"
- name: Install ariza
run: zef install --/test 'App::Ariza:ver<0.2.2+>:auth<zef:apogee>'
# Scaffolded by ariza 0.0.1. Before App::Ariza is
# published, or to cut a release against an unreleased ariza,
# install it from the repository instead:
# run: zef install --/test https://github.com/m-doughty/App-Ariza.git
# SQLCipher is built from source at the version ariza pins
# (4.14.0), against this image's OpenSSL, and
# installed where `ldconfig` will find it. ariza then copies the
# library into the bundle along with the libcrypto it needs, and
# points both at $ORIGIN.
#
# 4.14 links OpenSSL 3 (its codec calls EVP_MAC_*, missing from
# EL8's default OpenSSL 1.1), which EL8 parallel-ships under
# /usr/include/openssl3 and /usr/lib64/openssl3 rather than as the
# system OpenSSL. --with-tempstore is that flag's spelling under
# SQLCipher 4.14's autosetup-based configure -- the old --enable-*
# form is gone. --dll-basename=libsqlcipher --soname=legacy restore
# the libsqlcipher.so.0 layout every consumer here expects (the
# library's internal DT_SONAME still reads libsqlite3.so.0; harmless,
# since nothing dlopens it by soname). The ld.so.conf.d line exists
# because EL8's ldconfig does not search /usr/local/lib by default.
#
# Not the distribution package: EPEL's is a 3.34.1-era build with a
# renamed soname, years behind the pin. ariza can find it -- it looks
# for libsqlcipher*.so* when the canonical name is absent -- but
# shipping it to users is a different question from being able to
# stage it.
- name: Build and install SQLCipher 4.14.0
run: |
set -euo pipefail
curl -fsSL \
"https://github.com/sqlcipher/sqlcipher/archive/refs/tags/v4.14.0.tar.gz" \
-o /tmp/sqlcipher.tar.gz
mkdir -p /tmp/sqlcipher-src
tar xzf /tmp/sqlcipher.tar.gz -C /tmp/sqlcipher-src --strip-components=1
cd /tmp/sqlcipher-src
./configure --prefix=/usr/local --with-tempstore=yes \
--dll-basename=libsqlcipher --soname=legacy \
CFLAGS="-DSQLITE_HAS_CODEC -DSQLITE_EXTRA_INIT=sqlcipher_extra_init -DSQLITE_EXTRA_SHUTDOWN=sqlcipher_extra_shutdown -I/usr/include/openssl3" \
LDFLAGS="-L/usr/lib64/openssl3 -lcrypto"
make -j"$(nproc)"
make install
echo /usr/local/lib > /etc/ld.so.conf.d/local.conf
ldconfig
# ldconfig -p is soname-keyed and the soname deliberately says libsqlite3, so ask the filesystem, not the linker cache (same rule as the proof script).
test -f /usr/local/lib/libsqlcipher.so.0
ls -l /usr/local/lib/libsqlcipher*
- name: Build the bundle
run: ariza bundle --app=. --platform=linux-x86_64-glibc --out-dir=dist-out
# Unpacks the archive somewhere new, with a replaced environment,
# and runs App::ExampleApp's own bundle.smoke commands against it.
- name: Smoke the bundle
run: |
set -euo pipefail
archive=$(ls dist-out/*.tar.gz)
ariza smoke --archive="$archive"
- name: Upload
uses: actions/upload-artifact@v6
with:
name: linux-x86_64-glibc
path: |
dist-out/*.tar.gz
dist-out/*.tar.gz.sha256
if-no-files-found: error
bundle-windows-x86_64:
name: bundle windows-x86_64
runs-on: windows-latest
# No `defaults: run: shell: bash` here, on purpose: zef -- both the one
# installing ariza below and the one ariza's own site-install runs
# inside `ariza bundle` -- resolves `tar` off whatever shell launched
# it. Under bash that is Git Bash's MSYS tar, which treats a leading
# `C:` in a path as a remote hostname and aborts extracting into any
# mixed-separator temp path zef hands it. The runner's default pwsh
# instead finds Windows' own bsdtar, which has no such reading of `C:`.
# zef's child processes inherit whatever shell ran the step, so pwsh
# has to be load-bearing for every step that might invoke it, not just
# the ones below that already needed PowerShell for path handling.
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
# This Raku runs ariza. It is NOT the runtime that ends up in the
# bundle -- ariza downloads the pinned one (2026.07-01) from
# rakudo.org itself.
- uses: Raku/setup-raku@v1
with:
raku-version: 'latest'
- name: Install ariza
run: zef install --/test 'App::Ariza:ver<0.2.2+>:auth<zef:apogee>'
# Scaffolded by ariza 0.0.1. Before App::Ariza is
# published, or to cut a release against an unreleased ariza,
# install it from the repository instead:
# run: zef install --/test https://github.com/m-doughty/App-Ariza.git
# Windows has no package manager ariza drives, so it is told where
# the library is: SQLCIPHER_LIB_DIR names the directory holding the
# SQLCipher DLL and beats every other source. (ariza probes MSYS2's
# prefixes and a VCPKG_ROOT tree by itself, but naming the directory
# outright is one less thing to be wrong about.)
#
# MSYS2's UCRT package rather than vcpkg's, and the difference is
# not a preference:
#
# * vcpkg builds with MSVC, so its sqlcipher.dll imports
# vcruntime140.dll -- the Visual C++ Redistributable, which is
# NOT part of Windows. Every CI runner has it installed, so a
# bundle built that way passes every check here and fails to
# load on a clean machine, which is the machine a bundle is for.
# (ariza's PE audit now refuses that bundle outright.) MSYS2's
# mingw-w64-ucrt-x86_64-* packages import ucrtbase.dll instead,
# which Windows has shipped in System32 since Windows 10.
# * The MSYS2 package is prebuilt. The vcpkg port was a fifteen-
# minute source build, which is why this lane used to carry a
# restore/save cache pair around it; both are gone.
# * Its whole runtime closure -- OpenSSL, the mingw runtime --
# lands in ucrt64\bin beside the DLL, which is the search space
# contract App::Ariza::Native relies on to copy dependencies in.
#
# MSYS2 is preinstalled at C:\msys64 on windows runners, so this is
# a package install and nothing else. `-Sy` refreshes the package
# databases first: the image's copy is as old as the image, and
# MSYS2's mirrors keep only current versions, so a stale database
# sends pacman after a file that is no longer there.
- name: Install SQLCipher
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
& C:\msys64\usr\bin\pacman.exe -Sy --noconfirm --needed mingw-w64-ucrt-x86_64-sqlcipher
if ($LASTEXITCODE -ne 0) { throw "pacman failed (exit $LASTEXITCODE)" }
# The name is a wildcard because MSYS2's is libsqlcipher-0.dll,
# where vcpkg's was sqlcipher.dll: ariza accepts either and stages
# whichever it finds under the canonical name. Asserted rather than
# assumed -- a pacman that exits 0 having installed nothing would
# otherwise surface as a confusing failure inside `ariza bundle`.
- name: Locate SQLCipher
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$lib = 'C:\msys64\ucrt64\bin'
if (-not (Test-Path (Join-Path $lib 'libsqlcipher*.dll'))) {
throw "pacman installed no libsqlcipher*.dll in $lib"
}
"SQLCIPHER_LIB_DIR=$lib" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
- name: Build the bundle
run: |
$ErrorActionPreference = 'Stop'
ariza bundle --app=. --platform=windows-x86_64 --out-dir=dist-out
if ($LASTEXITCODE -ne 0) { throw "ariza bundle failed (exit $LASTEXITCODE)" }
# Unpacks the archive somewhere new, with a replaced environment,
# and runs App::ExampleApp's own bundle.smoke commands against it.
- name: Smoke the bundle
run: |
$ErrorActionPreference = 'Stop'
$archive = (Get-ChildItem dist-out\*.tar.gz | Select-Object -First 1).FullName
ariza smoke --archive="$archive"
if ($LASTEXITCODE -ne 0) { throw "ariza smoke failed (exit $LASTEXITCODE)" }
- name: Upload
uses: actions/upload-artifact@v6
with:
name: windows-x86_64
path: |
dist-out/*.tar.gz
dist-out/*.tar.gz.sha256
if-no-files-found: error
publish:
name: publish
needs:
- bundle-macos-arm64
- bundle-linux-x86_64-glibc
- bundle-windows-x86_64
# Tag-gated: everything above this line has already proved itself by
# the time anything is published, and a dispatch run is free to fail.
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- name: Download every lane's artefact
uses: actions/download-artifact@v7
with:
path: downloaded/
- name: Flatten, and build one checksums.txt over the lot
shell: bash
run: |
set -euxo pipefail
mkdir -p release
find downloaded -type f \( -name '*.tar.gz' \
-o -name '*.zip' \
-o -name '*.sha256' \) \
-exec cp {} release/ \;
# ariza writes a `<archive>.sha256` sidecar beside each bundle,
# and install.sh checks it. checksums.txt is those same digests
# in one file, for a human with `sha256sum -c`. Recomputed here
# rather than concatenated, and then checked against the
# sidecars: a release whose digest refuses its own archive is
# worse than one with no digest at all.
cd release
: > ../checksums.txt
for f in *; do
case $f in *.sha256) continue ;; esac
sha256sum "$f" >> ../checksums.txt
done
sort -o ../checksums.txt ../checksums.txt
sha256sum -c -- *.sha256
cd ..
echo '--- checksums.txt ---'
cat checksums.txt
- name: Create the release
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ github.ref_name }}
name: Example App ${{ github.ref_name }}
files: |
release/*
checksums.txt
body: |
Each archive below is a **self-contained bundle**:
Example App, a Rakudo runtime (2026.07-01), every Raku
dependency with its bytecode already compiled, and every native
library the application loads. There is nothing to install
first -- no Raku, no compiler, no package manager. Unpack it
anywhere, run `bin/exampleapp`, and delete the directory when
you are done: that is the uninstall.
### Platforms
- `macos-arm64`
Apple silicon, macOS 11 (Big Sur) or newer.
- `linux-x86_64-glibc`
x86_64, glibc 2.28 or newer: RHEL 8+, Ubuntu 18.10+,
Debian 10+. Not Alpine or any other musl distribution.
- `windows-x86_64`
x86_64, Windows 10 or newer.
A bundle runs on the platform it names and no other. It carries
compiled code, so there is no nearest match to fall back on.
### Installing
```
curl -fsSL https://raw.githubusercontent.com/example-org/App-ExampleApp/HEAD/install.sh | sh
```
That script picks the right archive for the machine it runs on,
verifies its SHA-256, unpacks it under
`$XDG_DATA_HOME/exampleapp/versions/` and links
`~/.local/bin/exampleapp` at it. Windows has `install.ps1`
beside it. Neither needs root, and `uninstall.sh` /
`uninstall.ps1` undo exactly what they did.
### Verifying a download
```
sha256sum -c checksums.txt
```
Or one archive at a time, against the `.sha256` published beside
it. On macOS, `shasum -a 256 -c` reads either.
draft: false
prerelease: false
smoke-installer-macos-arm64:
name: smoke the published installer (macos-arm64)
needs: publish
if: startsWith(github.ref, 'refs/tags/')
# A plain runner, deliberately: no container, no toolchain, nothing
# this repository put there. Whatever the bundle needs, the bundle has
# to carry -- and this, together with its Linux and Windows siblings
# below, is the only coverage that tests that claim against the
# artefact a user will actually download.
#
# It is also the only machine in this workflow with a BSD userland, so
# it is the only place install.sh's BSD branches ever run: `shasum -a
# 256` where Linux has `sha256sum`, and bsdtar rather than GNU tar.
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- name: Download the published macos-arm64 bundle
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p published
# The archive and its digest sidecar, so install.sh has
# something to verify against.
gh release download "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" \
--pattern 'exampleapp-*-macos-arm64.tar.gz*' --dir published
ls -l published
- name: Install it with this repository's own install.sh
run: |
set -euo pipefail
# The committed script, not a raw.githubusercontent URL: this has
# to work for the first release, before that URL serves anything,
# and it is the script this tag actually ships.
archive=$(ls published/*.tar.gz)
sh ./install.sh --url "$PWD/$archive"
- name: Run the installed launcher with a replaced environment
run: |
set -euo pipefail
# `env -i`: the launcher gets HOME, PATH and TERM and nothing
# else, so it cannot pass by borrowing something the runner image
# happens to have lying around.
env -i HOME="$HOME" PATH=/usr/bin:/bin TERM=xterm \
"$HOME/.local/bin/exampleapp" --version
smoke-installer-linux-x86_64-glibc:
name: smoke the published installer (linux-x86_64-glibc)
needs: publish
if: startsWith(github.ref, 'refs/tags/')
# A plain runner, deliberately: no container, no toolchain, nothing
# this repository put there. Whatever the bundle needs, the bundle has
# to carry -- and this, together with its macOS and Windows siblings
# below, is the only coverage that tests that claim against the
# artefact a user will actually download.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Download the published linux-x86_64-glibc bundle
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p published
# The archive and its digest sidecar, so install.sh has
# something to verify against.
gh release download "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" \
--pattern 'exampleapp-*-linux-x86_64-glibc.tar.gz*' --dir published
ls -l published
- name: Install it with this repository's own install.sh
run: |
set -euo pipefail
# The committed script, not a raw.githubusercontent URL: this has
# to work for the first release, before that URL serves anything,
# and it is the script this tag actually ships.
archive=$(ls published/*.tar.gz)
sh ./install.sh --url "$PWD/$archive"
- name: Run the installed launcher with a replaced environment
run: |
set -euo pipefail
# `env -i`: the launcher gets HOME, PATH and TERM and nothing
# else, so it cannot pass by borrowing something the runner image
# happens to have lying around.
env -i HOME="$HOME" PATH=/usr/bin:/bin TERM=xterm \
"$HOME/.local/bin/exampleapp" --version
smoke-installer-windows-x86_64:
name: smoke the published installer (windows-x86_64)
needs: publish
if: startsWith(github.ref, 'refs/tags/')
# A plain runner, deliberately: no SQLCipher from any package manager,
# no setup-raku, nothing this repository put there. Whatever the
# bundle needs, the bundle has to carry -- and this, together with its
# macOS and Linux siblings above, is the only coverage that tests that
# claim against the artefact a user will actually download.
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- name: Download the published windows-x86_64 bundle
env:
GH_TOKEN: ${{ github.token }}
run: |
$ErrorActionPreference = 'Stop'
New-Item -ItemType Directory -Path published -Force | Out-Null
# ariza packages every platform's bundle as .tar.gz, Windows
# included -- there is no `.zip` lane to fall back to -- so this
# is the archive and its digest sidecar, the same pattern the
# macOS and Linux jobs use above.
gh release download $env:GITHUB_REF_NAME --repo $env:GITHUB_REPOSITORY --pattern 'exampleapp-*-windows-x86_64.*' --dir published
if ($LASTEXITCODE -ne 0) { throw "gh release download failed (exit $LASTEXITCODE)" }
Get-ChildItem published
- name: Install it with this repository's own install.ps1
run: |
$ErrorActionPreference = 'Stop'
# The committed script, not a raw.githubusercontent URL: this has
# to work for the first release, before that URL serves anything,
# and it is the script this tag actually ships.
$archives = @(Get-ChildItem published\*.tar.gz)
if ($archives.Count -ne 1) {
throw "expected exactly one .tar.gz in published/, found $($archives.Count)"
}
pwsh -File .\install.ps1 -Url $archives[0].FullName
if ($LASTEXITCODE -ne 0) { throw "install.ps1 failed (exit $LASTEXITCODE)" }
- name: Run the installed launcher with a minimal environment
run: |
$ErrorActionPreference = 'Stop'
$bin = Join-Path $env:LOCALAPPDATA 'Example App\current\bin'
$exe = Join-Path $bin 'exampleapp.cmd'
if (-not (Test-Path -LiteralPath $exe)) {
throw "install.ps1 did not leave $exe behind"
}
# PowerShell has no `env -i`: a fresh process is built by hand
# instead, with its environment cleared and PATH trimmed to the
# system directory plus the install's own bin, so the bundle
# cannot pass by borrowing a toolchain this runner happens to
# have lying around. `pwsh` is resolved before the environment
# is touched, because the child cannot find it once PATH is gone.
$pwsh = (Get-Command pwsh).Source
$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = $pwsh
$psi.Arguments = "-NoProfile -NonInteractive -Command & '$exe' --version"
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.EnvironmentVariables.Clear()
$psi.EnvironmentVariables['PATH'] = "$env:SystemRoot\System32;$bin"
$psi.EnvironmentVariables['USERPROFILE'] = $env:USERPROFILE
$psi.EnvironmentVariables['LOCALAPPDATA'] = $env:LOCALAPPDATA
$psi.EnvironmentVariables['TEMP'] = $env:TEMP
# Windows has mandatory ambient environment that `env -i` on
# POSIX has no analogue for, and stripping it does not make the
# test cleaner — it makes the OS itself malfunction. Launching
# any .cmd resolves the interpreter through ComSpec, so without
# it the batch launcher dies with ERROR_MOD_NOT_FOUND ("The
# specified module could not be found") before the launcher
# runs a line — which is exactly how the first published
# Windows installer smoke failed against a perfectly good
# bundle. SystemRoot and windir are how cmd and half of Win32
# find the OS; PATHEXT is how `moneymoor` resolves to .exe/.cmd
# at all; SystemDrive is cheap insurance of the same kind.
# These five belong to Windows, not to the runner's toolchain,
# so keeping them does not weaken the isolation the cleared
# environment exists to provide.
$psi.EnvironmentVariables['SystemRoot'] = $env:SystemRoot
$psi.EnvironmentVariables['windir'] = $env:windir
$psi.EnvironmentVariables['ComSpec'] = $env:ComSpec
$psi.EnvironmentVariables['PATHEXT'] = $env:PATHEXT
$psi.EnvironmentVariables['SystemDrive'] = $env:SystemDrive
$proc = [System.Diagnostics.Process]::Start($psi)
$out = $proc.StandardOutput.ReadToEnd()
$err = $proc.StandardError.ReadToEnd()
$proc.WaitForExit()
Write-Host $out
if ($err) { Write-Host $err }
if ($proc.ExitCode -ne 0) {
throw "$exe --version exited $($proc.ExitCode)"
}
# The same launch through the compiled runner, when the bundle
# ships one. The .cmd above proves the script path; this proves
# the entry point PATHEXT actually hands a user who types the
# bare command — and unlike the .cmd it involves no cmd.exe and
# no ComSpec, so the two smokes fail independently.
$native = Join-Path $bin 'exampleapp.exe'
if (Test-Path -LiteralPath $native) {
$psi.Arguments = "-NoProfile -NonInteractive -Command & '$native' --version"
$proc = [System.Diagnostics.Process]::Start($psi)
$out = $proc.StandardOutput.ReadToEnd()
$err = $proc.StandardError.ReadToEnd()
$proc.WaitForExit()
Write-Host $out
if ($err) { Write-Host $err }
if ($proc.ExitCode -ne 0) {
throw "$native --version exited $($proc.ExitCode)"
}
}