diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a72fe4d..c304450 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,6 +63,7 @@ jobs: task-version: '3.50.0' task-retries: '3' setup-gomplate: true + setup-jq: true - name: Verify Setup run: | @@ -117,6 +118,12 @@ jobs: echo "::group::Verify Gomplate Installation" gomplate --version echo "::endgroup::" + echo "::group::Verify Task Installation" + task --version + echo "::endgroup::" + echo "::group::Verify JQ Installation" + jq --version + echo "::endgroup::" test-egress-policy: name: Test Egress Policy Input @@ -463,3 +470,56 @@ jobs: echo "Expected gomplate version ${EXPECTED_VERSION}, got: ${ACTUAL}" exit 1 fi + + test-setup-jq: + name: Test Setup JQ + runs-on: ubuntu-latest + steps: + - name: Harden Runner + id: harden-runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Run Setup JQ Action + uses: ./ + with: + setup-jq: true + + - name: Verify JQ Installation + run: | + jq --version + + test-setup-jq-custom-version: + name: Test Setup JQ (custom version) + runs-on: ubuntu-latest + steps: + - name: Harden Runner + id: harden-runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Run Setup JQ Action (non-default version) + uses: ./ + with: + setup-jq: true + jq-version: 1.8.0 + + - name: Verify JQ Version Matches Input + env: + EXPECTED_VERSION: 1.8.0 + run: | + ACTUAL="$(jq --version)" + ESCAPED_EXPECTED_VERSION="$(printf '%s\n' "${EXPECTED_VERSION}" | sed 's/[][(){}.^$*+?|\\]/\\&/g')" + echo "jq reported: ${ACTUAL}" + if ! echo "${ACTUAL}" | grep -qE "(^jq-|[^0-9.])${ESCAPED_EXPECTED_VERSION}([^0-9.]|$)"; then + echo "Expected jq version ${EXPECTED_VERSION}, got: ${ACTUAL}" + exit 1 + fi diff --git a/README.md b/README.md index b2fd06b..f75e7cf 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Common steps for initializing a job for GitHub actions. This composite action co - Security hardening with Step Security's Harden Runner (configurable egress policy) - Repository checkout with configurable options - Multi-language support (Node.js, Java, Python, Go, Rust, Swift) -- Build tool setup (Gradle, Task, gomplate) +- Build tool setup (Gradle, Task, gomplate, jq) - Automatic caching for dependencies and build artifacts ## Usage @@ -135,6 +135,21 @@ Common steps for initializing a job for GitHub actions. This composite action co > [!NOTE] > `setup-gomplate` currently installs the Linux AMD64 gomplate release artifact. +**jq** + +| Input | Description | Required | Default | +|------------|-----------------------------------|----------|---------| +| setup-jq | Whether to setup jq | No | false | +| jq-version | Desired jq version (e.g. 1.8.1) | No | 1.8.1 | + +> [!NOTE] +> `setup-jq` downloads the requested jq release directly from +> `github.com/jqlang/jq` (with sha256 verification) and installs it to +> `/usr/local/bin/jq`. Supports Linux and macOS on `amd64`/`arm64`. +> jq <1.8 truncates `jq --version` to `jq-X.Y` even on patch releases — the +> binary itself is the exact requested version, but the reported version +> string cannot distinguish 1.7.0 from 1.7.1. + ### Outputs **Checkout Outputs** diff --git a/action.yml b/action.yml index 04737bc..2e69b45 100644 --- a/action.yml +++ b/action.yml @@ -155,6 +155,14 @@ inputs: description: 'Number of retries for Task setup in case of failure, default is 3.' required: false default: '3' + setup-jq: + description: 'Whether to setup jq' + required: false + default: 'false' + jq-version: + description: 'Desired jq version (e.g. 1.8.1)' + required: false + default: '1.8.1' # expose outputs from the sub-actions outputs: @@ -474,6 +482,64 @@ runs: echo "${EXPECTED_SHA} /tmp/gomplate" | sha256sum -c - || { echo "Gomplate checksum verification failed"; exit 1; } sudo install -m 755 /tmp/gomplate /usr/local/bin/gomplate + - name: Set Up jq Parameters + id: setup-jq-params + if: ${{ inputs.setup-jq == 'true' }} + shell: bash + env: + JQ_VERSION: ${{ inputs.jq-version }} + run: | + echo "::group::Setting up jq" + echo "Runner OS: ${RUNNER_OS}" + echo "Runner Arch: ${RUNNER_ARCH}" + echo "Desired Version: ${JQ_VERSION}" + echo "::endgroup::" + + - name: Install JQ + id: setup-jq + if: ${{ inputs.setup-jq == 'true' }} + shell: bash + env: + JQ_VERSION: ${{ inputs.jq-version }} + run: | + if [[ ! "${JQ_VERSION}" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then + echo "Invalid jq-version: '${JQ_VERSION}' (expected MAJOR.MINOR[.PATCH], e.g. 1.8.1)" + exit 1 + fi + + case "${RUNNER_OS}" in + Linux) JQ_OS="linux" ;; + macOS) JQ_OS="macos" ;; + *) echo "Unsupported runner OS for jq setup: ${RUNNER_OS}"; exit 1 ;; + esac + + case "${RUNNER_ARCH}" in + X64) JQ_ARCH="amd64" ;; + ARM64) JQ_ARCH="arm64" ;; + *) echo "Unsupported runner arch for jq setup: ${RUNNER_ARCH}"; exit 1 ;; + esac + + JQ_ASSET="jq-${JQ_OS}-${JQ_ARCH}" + JQ_RELEASE_URL="https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}" + + echo "::group::Download jq ${JQ_VERSION}" + curl -sSfL "${JQ_RELEASE_URL}/${JQ_ASSET}" \ + -o /tmp/jq || { echo "Failed to download jq binary"; exit 1; } + curl -sSfL "${JQ_RELEASE_URL}/sha256sum.txt" \ + -o /tmp/jq_checksums.txt || { echo "Failed to download jq checksums"; exit 1; } + EXPECTED_SHA="$(awk -v a="${JQ_ASSET}" '$2 == a { print $1 }' /tmp/jq_checksums.txt)" + if [ -z "${EXPECTED_SHA}" ]; then + echo "Unable to find checksum for ${JQ_ASSET}" + exit 1 + fi + echo "${EXPECTED_SHA} /tmp/jq" | shasum -a 256 -c - || { echo "jq checksum verification failed"; exit 1; } + sudo install -m 755 /tmp/jq /usr/local/bin/jq + echo "::endgroup::" + + echo "::group::Show JQ Version" + jq --version + echo "::endgroup::" + branding: icon: 'arrow-up-right' color: 'green'