name: Release
permissions:
contents: write
on: [push]
defaults:
run:
shell: bash --noprofile --norc -CeEuo pipefail {0}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-apple-darwin
os: macos-latest
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
RUSTFLAGS: -C target-feature=+crt-static
- target: x86_64-pc-windows-gnu
os: windows-latest
- target: aarch64-unknown-linux-musl
os: ubuntu-latest
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: aarch64-linux-android
os: ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: run cross-build
uses: ahaoboy/cross-build@v1
with:
bin: eish
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
target: ${{ matrix.target }}
RUSTFLAGS: ${{ matrix.RUSTFLAGS }}
tag: nightly
allowUpdates: true
features: cli
verify-installers:
name: install (${{ matrix.name }})
strategy:
fail-fast: false
matrix:
include:
- name: macos-arm64
os: macos-latest
shells: bash fish
- name: windows
os: windows-latest
shells: bash powershell
- name: ubuntu
os: ubuntu-latest
shells: bash fish
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- name: Install eish
run: cargo install --path . --features cli
- name: Install fish
if: contains(matrix.shells, 'fish')
run: |
case "$(uname -s)" in
Linux)
sudo apt-get update -qq
sudo apt-get install -y -qq fish
;;
Darwin)
brew install fish
;;
*)
echo "::error::fish is only supported on Unix and macOS runners"
exit 1
;;
esac
fish --version
- name: Generate and run installers
env:
SHELLS: ${{ matrix.shells }}
run: |
repos=(
easy-install/easy-install
boa-dev/boa
theMackabu/ant
ahaoboy/es5
quickjs-ng/quickjs:qjs
)
# PowerShell is only exercised on Windows, where both interpreters
# are present: `pwsh` (PowerShell 7+) and `powershell` (5.1). The
# template has separate paths for each — TLS 1.2, `UseBasicParsing`
# and the `$PSEdition` checks only apply to 5.1 — so run both rather
# than letting the newer one stand in for the older.
ps_bins=()
case " $SHELLS " in
*" powershell "*)
for candidate in pwsh powershell; do
if command -v "$candidate" >/dev/null 2>&1; then
ps_bins+=("$candidate")
fi
done
if [ ${#ps_bins[@]} -eq 0 ]; then
echo "::error::no PowerShell interpreter found (tried pwsh, powershell)"
exit 1
fi
echo "PowerShell interpreters: ${ps_bins[*]}"
;;
esac
mkdir -p target/verify
failed=0
checks=0
# Run one installer and require it to leave an executable behind.
# Exiting 0 without installing anything is the failure this job
# exists to catch, so the directory is checked rather than just the
# exit status.
verify() {
local label="$1" dir="$2"
shift 2
checks=$((checks + 1))
rm -rf "$dir"
mkdir -p "$dir"
echo "::group::$label"
if ! "$@"; then
echo "::error::$label: the installer failed"
failed=1
else
# `-type f -print -quit` rather than `| head`, which would trip
# the workflow's `pipefail` through SIGPIPE.
local installed
installed="$(find "$dir" -maxdepth 1 -type f -print -quit || true)"
if [ -z "$installed" ] || [ ! -x "$installed" ]; then
echo "::error::$label: no executable was installed into $dir"
find "$dir" -maxdepth 1 -print || true
failed=1
else
echo "installed $installed"
fi
fi
echo "::endgroup::"
}
for entry in "${repos[@]}"; do
# `owner/repo` or `owner/repo:program`; the suffix names the program
# to install when the release publishes more than one.
repo="${entry%%:*}"
program="${entry#"$repo"}"
program="${program#:}"
name="${repo##*/}"
base="target/verify/$name"
echo "##### $entry on $(uname -s)/$(uname -m), shells: $SHELLS"
for shell in $SHELLS; do
# PowerShell refuses to run a script that does not end in `.ps1`,
# so the file name cannot just be `<repo>.<shell>`.
case "$shell" in
bash) ext=sh ;;
fish) ext=fish ;;
powershell) ext=ps1 ;;
esac
script="$base.$ext"
# `--name` is only passed when the entry actually names a program.
# eish normalises an empty name away, so passing it always would
# work, but it would then show up verbatim in the generated
# header as `--name ''`.
if [ -n "$program" ]; then
ok=0
eish "$repo" --name "$program" --shell "$shell" -o "$script" || ok=1
else
ok=0
eish "$repo" --shell "$shell" -o "$script" || ok=1
fi
if [ "$ok" -ne 0 ]; then
echo "::error::$entry: could not generate the $shell installer"
failed=1
continue
fi
case "$shell" in
bash)
if ! bash -n "$script"; then
echo "::error::$repo: the generated bash is not valid"
failed=1
continue
fi
dir="$PWD/$base-bash"
verify "$repo/bash" "$dir" \
env EI_DIR="$dir" EI_MIN_DISK_SPACE=0 bash "$script"
;;
fish)
if ! fish -n "$script"; then
echo "::error::$repo: the generated fish is not valid"
failed=1
continue
fi
dir="$PWD/$base-fish"
verify "$repo/fish" "$dir" \
env EI_DIR="$dir" EI_MIN_DISK_SPACE=0 fish "$script"
;;
powershell)
for ps in "${ps_bins[@]}"; do
dir="$PWD/$base-$ps"
if [ "$ps" = powershell ]; then
args=(-NoProfile -ExecutionPolicy Bypass -File)
else
args=(-NoProfile -File)
fi
verify "$repo/$ps" "$dir" \
env EI_DIR="$dir" EI_MIN_DISK_SPACE=0 \
"$ps" "${args[@]}" "$script"
done
;;
esac
done
done
echo "ran $checks installer checks"
exit "$failed"