Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions .github/actions/setup-wright/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Set up Wright
description: Install the Wright CLI from the verified release distribution and cache only the executable

inputs:
version:
description: Exact Wright version (for example 0.28.0) or `latest`
required: false
default: latest
cache:
description: Cache the verified Wright executable with the Actions cache service
required: false
default: 'true'

outputs:
version:
description: Concrete Wright version that was installed
value: ${{ steps.resolve.outputs.version }}
cache-hit:
description: '`true` when a valid cached executable avoided the release download'
value: ${{ steps.verify.outputs.valid }}

runs:
using: composite
steps:
- name: Resolve Wright release
id: resolve
shell: bash
env:
WRIGHT_VERSION: ${{ inputs.version }}
run: |
"$GITHUB_ACTION_PATH/setup-wright.sh" resolve

- name: Restore Wright executable
id: restore
if: inputs.cache == 'true'
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.resolve.outputs.cache-dir }}
key: ${{ steps.resolve.outputs.cache-key }}

- name: Verify cached executable
id: verify
shell: bash
env:
WRIGHT_SETUP_DIR: ${{ steps.resolve.outputs.cache-dir }}
WRIGHT_SETUP_VERSION: ${{ steps.resolve.outputs.version }}
WRIGHT_SETUP_TARGET: ${{ steps.resolve.outputs.target }}
CACHE_HIT: ${{ steps.restore.outputs.cache-hit }}
run: |
if [[ "$CACHE_HIT" == true ]]; then
"$GITHUB_ACTION_PATH/setup-wright.sh" verify-cache
else
echo "valid=false" >> "$GITHUB_OUTPUT"
fi

- name: Download and verify Wright release
if: steps.verify.outputs.valid != 'true'
shell: bash
env:
WRIGHT_SETUP_DIR: ${{ steps.resolve.outputs.cache-dir }}
WRIGHT_SETUP_VERSION: ${{ steps.resolve.outputs.version }}
WRIGHT_SETUP_TARGET: ${{ steps.resolve.outputs.target }}
WRIGHT_SETUP_EXT: ${{ steps.resolve.outputs.archive-ext }}
run: |
"$GITHUB_ACTION_PATH/setup-wright.sh" install

# Only a freshly verified executable on a true miss is saved. An invalid
# entry under the same immutable key is not overwritten; see docs/setup-wright-action.md.
- name: Save Wright executable
if: inputs.cache == 'true' && steps.restore.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.resolve.outputs.cache-dir }}
key: ${{ steps.resolve.outputs.cache-key }}

- name: Add Wright to PATH
shell: bash
env:
WRIGHT_SETUP_DIR: ${{ steps.resolve.outputs.cache-dir }}
run: |
"$GITHUB_ACTION_PATH/setup-wright.sh" activate
138 changes: 138 additions & 0 deletions .github/actions/setup-wright/setup-wright.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env bash
# Helper for the setup-wright composite Action (#717).
#
# Subcommands: resolve | verify-cache | install | activate
# Environment:
# WRIGHT_VERSION exact version or "latest" (resolve)
# WRIGHT_INSTALL_BASE_URL release distribution base URL (test hook; defaults
# to the R2 contract in docs/release.md)
# WRIGHT_SETUP_OS / WRIGHT_SETUP_ARCH override platform detection (tests)
# WRIGHT_SETUP_DIR cache directory holding the verified executable
# WRIGHT_SETUP_VERSION concrete version (verify-cache, install)
# WRIGHT_SETUP_TARGET target triple (install)
# GITHUB_OUTPUT / GITHUB_PATH standard Actions files

set -euo pipefail

BASE_URL="${WRIGHT_INSTALL_BASE_URL:-https://install.wrightkit.dev/wright}"
# Bump when the cached layout (executable plus .sha256 sidecar) changes.
CACHE_LAYOUT="v1"
SEMVER='^[0-9]+\.[0-9]+\.[0-9]+$'

fail() {
echo "::error::setup-wright: $*" >&2
exit 1
}

sha256_of() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | awk '{ print $1 }'
else
shasum -a 256 "$1" | awk '{ print $1 }'
fi
}

output() {
echo "$1=$2" >> "${GITHUB_OUTPUT:?GITHUB_OUTPUT is not set}"
}

exe_name() {
case "$WRIGHT_SETUP_TARGET" in *windows*) echo wright.exe ;; *) echo wright ;; esac
}

cmd_resolve() {
local os="${WRIGHT_SETUP_OS:-${RUNNER_OS:-}}" arch="${WRIGHT_SETUP_ARCH:-${RUNNER_ARCH:-}}" target ext
case "$os/$arch" in
Linux/X64) target=x86_64-unknown-linux-gnu; ext=tar.gz ;;
macOS/X64) target=x86_64-apple-darwin; ext=tar.gz ;;
macOS/ARM64) target=aarch64-apple-darwin; ext=tar.gz ;;
Windows/X64) target=x86_64-pc-windows-msvc; ext=zip ;;
*) fail "unsupported platform $os/$arch (supported: Linux X64, macOS X64, macOS ARM64, Windows X64)" ;;
esac

local version="${WRIGHT_VERSION:-latest}"
version="${version#v}"
if [[ "$version" == latest ]]; then
version="$(curl -fsSL "$BASE_URL/latest/version" | tr -d '[:space:]')" \
|| fail "could not read $BASE_URL/latest/version; pin an exact version"
version="${version#v}"
fi
[[ "$version" =~ $SEMVER ]] || fail "invalid Wright version '$version' (expected MAJOR.MINOR.PATCH or 'latest')"

output version "$version"
output target "$target"
output archive-ext "$ext"
output cache-key "setup-wright-$CACHE_LAYOUT-$version-$target"
output cache-dir "${RUNNER_TEMP:?RUNNER_TEMP is not set}/setup-wright/$CACHE_LAYOUT/$version/$target"
}

# A restored directory is trusted only if its executable matches the sidecar
# SHA-256 written at install time and reports the requested version.
cmd_verify_cache() {
local dir="${WRIGHT_SETUP_DIR:?}" version="${WRIGHT_SETUP_VERSION:?}" exe
exe="$(WRIGHT_SETUP_TARGET="${WRIGHT_SETUP_TARGET:?}" exe_name)"
if [[ -f "$dir/$exe" && -f "$dir/$exe.sha256" ]] \
&& [[ "$(sha256_of "$dir/$exe")" == "$(awk 'NR == 1 { print $1 }' "$dir/$exe.sha256")" ]] \
&& "$dir/$exe" --version 2>/dev/null | grep -Fq "$version"; then
output valid true
return
fi
if [[ -e "$dir" ]]; then
echo "::warning::setup-wright: ignoring invalid cached Wright $version; downloading the release instead"
rm -rf "$dir"
fi
output valid false
}

cmd_install() {
local work
local dir="${WRIGHT_SETUP_DIR:?}" version="${WRIGHT_SETUP_VERSION:?}" target="${WRIGHT_SETUP_TARGET:?}" ext="${WRIGHT_SETUP_EXT:?}"
local name="wright-$version-$target" archive exe expected actual
archive="$name.$ext"
exe="$(exe_name)"
work="$(mktemp -d)"
WORK_DIR="$work"
trap 'rm -rf "$WORK_DIR"' EXIT

curl -fsSL -o "$work/$archive" "$BASE_URL/releases/$version/$archive" \
|| fail "failed to download $BASE_URL/releases/$version/$archive (does release $version exist?)"
curl -fsSL -o "$work/$archive.sha256" "$BASE_URL/releases/$version/$archive.sha256" \
|| fail "failed to download the checksum for $archive"

expected="$(awk 'NR == 1 { print $1 }' "$work/$archive.sha256")"
[[ "$expected" =~ ^[0-9a-fA-F]{64}$ ]] || fail "invalid checksum file for $archive"
actual="$(sha256_of "$work/$archive")"
[[ "$(printf %s "$actual" | tr A-F a-f)" == "$(printf %s "$expected" | tr A-F a-f)" ]] \
|| fail "checksum mismatch for $archive (published $expected, got $actual); nothing was installed"

if [[ "$ext" == zip ]]; then
# Git Bash's GNU tar cannot read zip; Windows ships bsdtar, which needs native paths.
"${SYSTEMROOT:-C:/Windows}/System32/tar.exe" -xf "$(cygpath -w "$work/$archive")" -C "$(cygpath -w "$work")" \
|| fail "failed to extract $archive"
else
tar -xf "$work/$archive" -C "$work" || fail "failed to extract $archive"
fi
[[ -f "$work/$name/$exe" ]] || fail "unexpected archive layout: $name/$exe missing from $archive"

rm -rf "$dir"
mkdir -p "$dir"
cp "$work/$name/$exe" "$dir/$exe"
chmod +x "$dir/$exe"
sha256_of "$dir/$exe" > "$dir/$exe.sha256"
"$dir/$exe" --version | grep -Fq "$version" \
|| { rm -rf "$dir"; fail "installed executable does not report version $version"; }
}

cmd_activate() {
local dir="${WRIGHT_SETUP_DIR:?}"
if command -v cygpath >/dev/null 2>&1; then dir="$(cygpath -w "$dir")"; fi
echo "$dir" >> "${GITHUB_PATH:?GITHUB_PATH is not set}"
}

case "${1:-}" in
resolve) cmd_resolve ;;
verify-cache) cmd_verify_cache ;;
install) cmd_install ;;
activate) cmd_activate ;;
*) fail "usage: setup-wright.sh resolve|verify-cache|install|activate" ;;
esac
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
cli: ${{ steps.filter.outputs.rust_core }}
dist: ${{ steps.filter.outputs.dist }}
agent_guide: ${{ steps.filter.outputs.agent_guide }}
setup_action: ${{ steps.filter.outputs.setup_action }}
ui: ${{ steps.filter.outputs.ui }}
steps:
- name: Checkout
Expand All @@ -49,6 +50,10 @@ jobs:
- 'agent-guide/**'
- 'schemas/**'
- '.github/workflows/**'
setup_action:
- '.github/actions/setup-wright/**'
- 'scripts/test-setup-wright.sh'
- '.github/workflows/**'
agent_guide:
- 'agent-guide/**'
- 'crates/wright-cli/src/agent.rs'
Expand All @@ -75,6 +80,57 @@ jobs:
- 'schemas/**'
- 'Cargo.lock'
- '.github/workflows/**'
setup-wright-action:
name: setup-wright Action (${{ matrix.os }})
needs: paths
if: needs.paths.outputs.setup_action == 'true'
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-15, windows-latest]
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Test the helper script
if: runner.os != 'Windows'
run: scripts/test-setup-wright.sh

# Consumes the latest published release, so it needs no Wright build.
- name: Install Wright through the Action
id: first
uses: ./.github/actions/setup-wright

- name: Move the installed copy aside so the cache must restore it
shell: bash
run: mv "$RUNNER_TEMP/setup-wright" "$RUNNER_TEMP/setup-wright-first"

- name: Install Wright again from the cache
id: second
uses: ./.github/actions/setup-wright
with:
version: ${{ steps.first.outputs.version }}

# A fork PR has a read-only cache token, so it cannot seed the cache.
- name: Check the second install used the cache
if: github.event.pull_request.head.repo.fork != true
shell: bash
env:
CACHE_HIT: ${{ steps.second.outputs.cache-hit }}
VERSION: ${{ steps.first.outputs.version }}
run: |
[[ "$CACHE_HIT" == true ]] || { echo "::error::expected a cache hit"; exit 1; }
wright --version | grep -Fq "$VERSION"

- name: Run Wright as an ordinary step
shell: bash
run: |
printf 'rule ("setup smoke") {\n event {\n Ongoing - Global;\n }\n actions {\n Disable Inspector Recording;\n }\n}\n' > "$RUNNER_TEMP/smoke.ws"
wright check "$RUNNER_TEMP/smoke.ws"
grep -Fq 'PASS' "$GITHUB_STEP_SUMMARY"

agent-guide:
name: Agent guide sync
needs: paths
Expand Down Expand Up @@ -680,6 +736,7 @@ jobs:
- dist-cli-build
- dist-validation
- agent-guide
- setup-wright-action
- ui
runs-on: ubuntu-latest
timeout-minutes: 5
Expand All @@ -706,6 +763,7 @@ jobs:
"dist-cli-build": ($p.dist == "true"),
"dist-validation": ($p.dist == "true"),
"agent-guide": ($p.agent_guide == "true"),
"setup-wright-action": ($p.setup_action == "true"),
"ui": ($p.ui == "true")
}')
for job in $(jq -r 'keys[]' <<<"$required"); do
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ On Windows, use the PowerShell installer at
`https://install.wrightkit.dev/wright/install.ps1`. Archives, the nightly
channel, and building from source are covered in [`docs/release.md`](docs/release.md).

In GitHub Actions, use the cached
[`setup-wright` Action](docs/setup-wright-action.md).

Update with `wright update`.

## Usage
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ Issue contract.
- [Lessons](LESSONS.md): short queue of reusable failure classes from merged
work, pruned once promoted into tests or standards.
- [Release/distribution](release.md): packaging and publication contract.
- [`setup-wright` Action](setup-wright-action.md): installing and caching the
Wright CLI in GitHub Actions.
- [Agent-team governance](agent-team.md): role/authority coordination where
still applicable.

Expand Down
Loading
Loading