diff --git a/.github/actions/setup/README.md b/.github/actions/setup/README.md new file mode 100644 index 0000000..bff6ccd --- /dev/null +++ b/.github/actions/setup/README.md @@ -0,0 +1,41 @@ +# Setup httpprobe + +A GitHub Action that installs the [httpprobe](https://github.com/mrfoh/httpprobe) CLI on a runner and adds it to `PATH`, so subsequent steps can run `httpprobe`. + +## Usage + +```yaml +- uses: mrfoh/httpprobe/.github/actions/setup@v1.2.0 + with: + version: latest +- run: httpprobe run -p ./tests +``` + +Pinning to a specific version is recommended for reproducible builds: + +```yaml +- uses: mrfoh/httpprobe/.github/actions/setup@v1.2.0 + with: + version: v1.2.0 +``` + +## Inputs + +| Name | Required | Default | Description | +| -------------- | -------- | ---------------------- | ------------------------------------------------------------------------ | +| `version` | no | `latest` | Version to install — `latest`, `v1.2.0`, or `1.2.0`. | +| `github-token` | no | `${{ github.token }}` | Token used to query the releases API. Avoids anonymous rate limits. | + +## Outputs + +| Name | Description | +| --------- | ---------------------------------------------------------- | +| `version` | The resolved version that was installed (e.g. `v1.2.0`). | + +## Supported runners + +Linux, macOS, and Windows GitHub-hosted runners on `amd64` and `arm64`. The action also maps `X86` runners to the 386 binary if you use self-hosted 32-bit runners. + +## Marketplace + +This action is intentionally not published to the GitHub Marketplace — Marketplace requires `action.yml` at the repo root, which would conflict with this repo's primary purpose as the CLI itself. Reference the action by its full subdirectory path as shown above. diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000..757751c --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,131 @@ +name: Setup httpprobe +description: Install the httpprobe CLI on a GitHub Actions runner and add it to PATH. +author: mrfoh + +branding: + icon: zap + color: purple + +inputs: + version: + description: 'Version to install (e.g. v1.2.0, 1.2.0, or latest).' + required: false + default: 'latest' + github-token: + description: 'Token used to query the GitHub releases API (avoids anonymous rate limits).' + required: false + default: ${{ github.token }} + +outputs: + version: + description: 'The resolved version that was installed (e.g. v1.2.0).' + value: ${{ steps.resolve.outputs.version }} + +runs: + using: composite + steps: + - name: Resolve version + id: resolve + shell: bash + env: + INPUT_VERSION: ${{ inputs.version }} + GH_TOKEN: ${{ inputs.github-token }} + run: | + set -euo pipefail + if [ "${INPUT_VERSION}" = "latest" ] || [ -z "${INPUT_VERSION}" ]; then + tag=$(curl -fsSL \ + -H "Authorization: Bearer ${GH_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/mrfoh/httpprobe/releases/latest" \ + | grep '"tag_name":' \ + | sed -E 's/.*"([^"]+)".*/\1/') + if [ -z "$tag" ]; then + echo "::error::Could not resolve latest release for mrfoh/httpprobe" + exit 1 + fi + else + tag="${INPUT_VERSION#v}" + tag="v${tag}" + fi + echo "version=${tag}" >> "$GITHUB_OUTPUT" + echo "version_no_v=${tag#v}" >> "$GITHUB_OUTPUT" + + - name: Map platform + id: platform + shell: bash + run: | + set -euo pipefail + case "$RUNNER_OS" in + Linux) os=linux ;; + macOS) os=darwin ;; + Windows) os=windows ;; + *) echo "::error::Unsupported RUNNER_OS=$RUNNER_OS"; exit 1 ;; + esac + case "$RUNNER_ARCH" in + X64) arch=amd64 ;; + ARM64) arch=arm64 ;; + X86) arch=386 ;; + *) echo "::error::Unsupported RUNNER_ARCH=$RUNNER_ARCH"; exit 1 ;; + esac + if [ "$os" = "darwin" ] && [ "$arch" = "386" ]; then + echo "::error::darwin/386 is not built"; exit 1 + fi + echo "os=$os" >> "$GITHUB_OUTPUT" + echo "arch=$arch" >> "$GITHUB_OUTPUT" + if [ "$os" = "windows" ]; then + echo "binary=httpprobe.exe" >> "$GITHUB_OUTPUT" + else + echo "binary=httpprobe" >> "$GITHUB_OUTPUT" + fi + + - name: Download and extract + id: download + shell: bash + env: + TAG: ${{ steps.resolve.outputs.version }} + VERSION_NO_V: ${{ steps.resolve.outputs.version_no_v }} + OS: ${{ steps.platform.outputs.os }} + ARCH: ${{ steps.platform.outputs.arch }} + BINARY: ${{ steps.platform.outputs.binary }} + run: | + set -euo pipefail + asset="httpprobe-v${VERSION_NO_V}_${OS}_${ARCH}.tar.gz" + url="https://github.com/mrfoh/httpprobe/releases/download/${TAG}/${asset}" + # On Windows runners, $RUNNER_TEMP is a Windows path (e.g. D:\a\_temp) that + # confuses tar and other POSIX tools. cygpath -u normalizes it to /d/a/_temp. + if command -v cygpath >/dev/null 2>&1; then + install_dir="$(cygpath -u "$RUNNER_TEMP")/httpprobe-${VERSION_NO_V}" + else + install_dir="${RUNNER_TEMP}/httpprobe-${VERSION_NO_V}" + fi + mkdir -p "$install_dir" + echo "Downloading $url" + if ! curl -fsSL "$url" -o "${install_dir}/${asset}"; then + echo "::error::Failed to download $url" + exit 1 + fi + tar -xzf "${install_dir}/${asset}" -C "$install_dir" + rm -f "${install_dir}/${asset}" + bin_path="${install_dir}/${BINARY}" + if [ ! -f "$bin_path" ]; then + bin_path=$(find "$install_dir" -type f -name "${BINARY}" | head -n1) + fi + if [ -z "$bin_path" ] || [ ! -f "$bin_path" ]; then + echo "::error::Binary $BINARY not found in archive"; exit 1 + fi + chmod +x "$bin_path" + echo "install_dir=$install_dir" >> "$GITHUB_OUTPUT" + + - name: Add to PATH + shell: bash + run: | + # Convert back to Windows form so $GITHUB_PATH appends correctly on win runners. + dir="${{ steps.download.outputs.install_dir }}" + if command -v cygpath >/dev/null 2>&1; then + dir="$(cygpath -w "$dir")" + fi + echo "$dir" >> "$GITHUB_PATH" + + - name: Verify + shell: bash + run: httpprobe --version diff --git a/.github/workflows/test-action.yml b/.github/workflows/test-action.yml new file mode 100644 index 0000000..c8865c2 --- /dev/null +++ b/.github/workflows/test-action.yml @@ -0,0 +1,37 @@ +name: Test Action + +on: + pull_request: + paths: + - '.github/actions/setup/**' + - '.github/workflows/test-action.yml' + push: + branches: [main] + paths: + - '.github/actions/setup/**' + - '.github/workflows/test-action.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + smoke: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Install httpprobe + id: setup + uses: ./.github/actions/setup + with: + version: latest + - name: Print resolved version + shell: bash + run: | + echo "Installed ${{ steps.setup.outputs.version }}" + httpprobe --version + httpprobe --help diff --git a/README.md b/README.md index 68c5e55..1ba9fed 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,19 @@ go build -o httpprobe ./cmd/main.go go install github.com/mrfoh/httpprobe@latest ``` +### GitHub Actions + +Install httpprobe in a workflow and run your tests: + +```yaml +- uses: mrfoh/httpprobe/.github/actions/setup@v1.2.0 + with: + version: latest # or pin to a specific tag like v1.2.0 +- run: httpprobe run -p ./tests +``` + +See the [GitHub Action docs](https://mrfoh.github.io/httpprobe/github-action.html) for inputs, outputs, and matrix examples. + ## Quick Start 1. Create a test definition file (`test.yaml`): diff --git a/docs/github-action.md b/docs/github-action.md new file mode 100644 index 0000000..64535c4 --- /dev/null +++ b/docs/github-action.md @@ -0,0 +1,93 @@ +--- +layout: default +title: GitHub Action +nav_order: 9 +description: Install and run httpprobe in your GitHub Actions workflows +--- + +# GitHub Action +{: .no_toc } + +Install the httpprobe CLI on a GitHub Actions runner with a single step, then use it in any subsequent step. +{: .fs-6 .fw-300 } + +## Table of contents +{: .no_toc .text-delta } + +1. TOC +{:toc} + +--- + +## Quick start + +Add the setup action to any workflow: + +```yaml +- uses: mrfoh/httpprobe/.github/actions/setup@v1.2.0 + with: + version: latest +- run: httpprobe run -p ./tests +``` + +The action downloads the matching binary for the runner's OS and architecture, adds it to `PATH`, and exits. Subsequent steps can invoke `httpprobe` directly. + +For reproducible builds, pin both the action ref and the `version` input to an explicit release: + +```yaml +- uses: mrfoh/httpprobe/.github/actions/setup@v1.2.0 + with: + version: v1.2.0 +``` + +## Inputs + +| Name | Required | Default | Description | +| -------------- | -------- | ---------------------- | ------------------------------------------------------------------- | +| `version` | no | `latest` | Version to install — `latest`, `v1.2.0`, or `1.2.0`. | +| `github-token` | no | `${{ github.token }}` | Token used to query the releases API. Avoids anonymous rate limits. | + +## Outputs + +| Name | Description | +| --------- | ------------------------------------------------------------ | +| `version` | The resolved version that was installed (e.g. `v1.2.0`). | + +## Matrix testing + +The action works on Linux, macOS, and Windows runners on both `amd64` and `arm64`: + +```yaml +jobs: + api-tests: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: mrfoh/httpprobe/.github/actions/setup@v1.2.0 + - run: httpprobe run -p ./tests +``` + +## Pinning to a major tag + +If the repository maintains a floating major tag (e.g. `v1`), you can use it to auto-receive patch releases of the action: + +```yaml +- uses: mrfoh/httpprobe/.github/actions/setup@v1 +``` + +For production workflows where reproducibility matters, prefer pinning to a specific release tag instead. + +## Supported platforms + +| OS | Architectures | +| ------- | -------------------- | +| Linux | amd64, arm64, 386 | +| macOS | amd64, arm64 | +| Windows | amd64, arm64, 386 | + +## Why no Marketplace listing? + +GitHub Marketplace requires `action.yml` to live at the repo root, which would conflict with this repo's identity as the CLI project. Reference the action by its full subdirectory path (`mrfoh/httpprobe/.github/actions/setup@`) instead.