Add one-command installers for the jawk CLI executable - #584
Conversation
Installing Jawk as a command-line tool meant downloading the versioned standalone jar from the GitHub releases page by hand and invoking it with java -jar every time, while every other AWK implementation is a plain executable on the PATH. Serve POSIX sh and PowerShell installers from the site root, so `curl -fsSL https://jawk.io/get | sh` (Linux/macOS) and `irm https://jawk.io/get.ps1 | iex` (Windows) install the standalone jar of the latest release plus a small `jawk` launcher on the PATH, verifying the jar's SHA-256 checksum. The launcher locates a JRE (Java 8 or later) at run time — JAWK_JAVA_HOME, JAVA_HOME, java on the PATH, then platform-specific locations — and execs java -jar, so installing or upgrading Java requires no reinstall. JAWK_VERSION pins a specific release. The release workflow now also uploads a version-less jawk-standalone.jar alias and .sha256 checksum files with each GitHub release, giving the installers a stable, API-free download URL; the installers fall back to resolving the latest version through the GitHub API while the newest release predates the alias. Fixes #583 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sh rejects scripts with CRLF line endings, so a Maven site built from a CRLF checkout (Windows with core.autocrlf) would publish a broken jawk.io/get. Pin the file to LF like the mvnw wrapper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c7203a8269
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,181 @@ | |||
| #!/bin/sh | |||
There was a problem hiding this comment.
Add the required license headers to both installers
Both newly added installer files begin directly with executable/documentation content and omit the repository-required license header; add the proper header to get and get.ps1 before distributing them.
AGENTS.md reference: AGENTS.md:L7-L7
Useful? React with 👍 / 👎.
| "%JAWK_JAVA%" -jar "%JAWK_JAR%" %* | ||
| exit /b %ERRORLEVEL% | ||
| '@ | ||
| $shim.Replace('__JAWK_JAR__', $jarPath) | Set-Content -Path $shimPath -Encoding ASCII |
There was a problem hiding this comment.
Preserve non-ASCII paths in the Windows shim
When the default %LOCALAPPDATA% or an overridden install directory contains non-ASCII characters, -Encoding ASCII replaces those characters in the embedded jar path with ?. The generated jawk.cmd then cannot find the jar, so installation is unusable for many Windows usernames; write a suitable Unicode encoding or derive the jar path relative to the shim instead.
Useful? React with 👍 / 👎.
| if defined JAWK_JAVA_HOME if exist "%JAWK_JAVA_HOME%\bin\java.exe" set "JAWK_JAVA=%JAWK_JAVA_HOME%\bin\java.exe" | ||
| if not defined JAWK_JAVA if defined JAVA_HOME if exist "%JAVA_HOME%\bin\java.exe" set "JAWK_JAVA=%JAVA_HOME%\bin\java.exe" | ||
| if not defined JAWK_JAVA for %%J in (java.exe) do if not "%%~$PATH:J" == "" set "JAWK_JAVA=%%~$PATH:J" |
There was a problem hiding this comment.
Validate Java versions before selecting a Windows runtime
When JAWK_JAVA_HOME or JAVA_HOME points to Java 7 or older, these checks select it solely because java.exe exists, even if a compatible Java is available later on PATH. The shim then fails with an unsupported class-version error rather than locating the advertised Java 8+ runtime; apply a version check to each candidate as the POSIX launcher does.
Useful? React with 👍 / 👎.
| # Locates a JRE (Java 8 or later) and runs the Jawk standalone jar. | ||
| # Set JAWK_JAVA_HOME to force a specific Java installation. | ||
|
|
||
| JAWK_JAR="${JAR_PATH}" |
There was a problem hiding this comment.
Shell-escape the configured jar path in the launcher
When the documented JAWK_DATA_DIR override contains shell syntax such as $, backticks, or a double quote, interpolating it directly into the generated script changes the path on the next invocation or can turn part of it into shell commands. Valid custom directories therefore produce a broken or unsafe launcher; serialize JAR_PATH as a shell-safe literal rather than inserting it inside source-level double quotes.
Useful? React with 👍 / 👎.
| INPUT_RELEASE_VERSION: ${{ inputs.releaseVersion }} | ||
|
|
||
| - name: Create GitHub Release | ||
| if: always() && steps.release.outcome == 'success' |
There was a problem hiding this comment.
Stop release publication when checksum generation fails
If the preceding Create standalone jar alias and SHA-256 checksums step exits at its missing-jar guard or fails while creating the files, this condition is still true because always() ignores that failure and steps.release.outcome only reflects the earlier Maven step. The workflow therefore proceeds to publish a GitHub release without the alias/checksums that the installers depend on; require the checksum step to succeed before invoking the release action.
Useful? React with 👍 / 👎.
| # from GitHub, verifies its SHA-256 checksum, and writes a small `jawk` | ||
| # launcher script that locates a JRE (Java 8 or later) at run time. | ||
|
|
||
| set -eu |
There was a problem hiding this comment.
Add automated coverage for both installer implementations
This commit introduces executable download, checksum, installation, launcher-generation, and runtime-selection logic in two new scripts without adding any automated tests for either implementation. Add test coverage for the success and failure paths so installer regressions are exercised by the required Maven test suite rather than only by manual checks.
AGENTS.md reference: AGENTS.md:L19-L19
Useful? React with 👍 / 👎.
- The jawk.cmd shim locates the jar relative to its own directory (%~dp0) instead of embedding an absolute path, so ASCII encoding can never mangle a non-ASCII install path and the installation is relocatable; the shim is normalized to CRLF so cmd finds its labels. - The shim now version-checks each JRE candidate (rejecting 1.0-1.7) and falls through to the next one, matching the POSIX launcher. - The POSIX launcher embeds the jar path as a single-quoted literal, so a JAWK_DATA_DIR containing $, backticks, or double quotes stays inert at launch time. - The GitHub release step is gated on the checksum step succeeding, so a release can no longer be published without the alias and checksum assets the installers rely on. - Both installers carry a short LGPL-3.0-or-later copyright notice (the boxed license header applies to Java sources per the license plugin configuration). Refs #583 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
All review points addressed in a6ecc85:
@codex please review again |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a6ecc854d0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| info "NOTE: ${BIN_DIR} is not on your PATH. Add it with:" | ||
| info "" | ||
| info " export PATH=\"${BIN_DIR}:\${PATH}\"" |
There was a problem hiding this comment.
Shell-quote the printed PATH export
When JAWK_INSTALL_DIR is not already on PATH and contains shell syntax such as a double quote, backticks, or $(), the suggested export command inserts the directory directly inside double quotes. Pasting this advertised “exact line” can execute command substitutions or configure the wrong path; serialize BIN_DIR as a shell-safe literal before printing the command.
Useful? React with 👍 / 👎.
| > irm https://jawk.io/get.ps1 | iex | ||
| > ``` | ||
|
|
||
| The installer downloads the standalone jar of the latest Jawk release from GitHub, verifies its SHA-256 checksum, and installs a `jawk` launcher: |
There was a problem hiding this comment.
Qualify the checksum-verification guarantee
When JAWK_VERSION pins a release that predates checksum assets—including releases the new fallback explicitly supports—both installers warn and proceed without verification, so this unconditional documentation claim gives users a security guarantee the installers do not always provide. Qualify it as verification when a checksum is published, or stop installation when the checksum is unavailable.
AGENTS.md reference: AGENTS.md:L35-L37
Useful? React with 👍 / 👎.
… docs The advertised "exact line" for adding the launcher directory to the PATH interpolated BIN_DIR inside double quotes, so a directory name containing $(...) or backticks would execute when pasted. Serialize it through the same single-quoted-literal helper as the launcher jar path. install.md no longer claims unconditional checksum verification: when a pinned release predates the published .sha256 assets, the installers warn and skip verification, and the page now says so. Refs #583 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Both points addressed in the latest commit:
@codex please review again |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f1f6968b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| - On Linux and macOS, the jar goes to `~/.local/share/jawk/` and the launcher to `~/.local/bin/jawk` — no `sudo` required. The installer warns with the exact line to add if `~/.local/bin` is not on your `PATH`. Set `JAWK_INSTALL_DIR` and `JAWK_DATA_DIR` to override the locations. | ||
| - On Windows, both go under `%LOCALAPPDATA%\Jawk` (override with `JAWK_INSTALL_DIR`), and the installer adds the launcher directory to your user `PATH`. | ||
|
|
||
| At run time, the launcher locates a Java Runtime Environment (Java 8 or later) by checking `JAWK_JAVA_HOME`, then `JAVA_HOME`, then `java` on the `PATH`, then platform-specific locations such as `/usr/libexec/java_home` on macOS and `/usr/lib/jvm` on Linux. If no suitable JRE is found, it prints a message pointing to [Adoptium](https://adoptium.net); installing or upgrading Java later requires no Jawk reinstall. |
There was a problem hiding this comment.
Record the new launcher behavior under Unreleased
This adds user-visible CLI launcher behavior, including automatic runtime selection, but src/site/markdown/behavior-changes.md remains unchanged. Add an Unreleased bullet describing the previous and new behavior and linking #583; otherwise the release workflow will stamp the next version without the required behavior note.
AGENTS.md reference: AGENTS.md:L39-L39
Useful? React with 👍 / 👎.
| # Locates a JRE (Java 8 or later) and runs the Jawk standalone jar. | ||
| # Set JAWK_JAVA_HOME to force a specific Java installation. | ||
|
|
||
| JAWK_JAR=${JAR_PATH_SQ} |
There was a problem hiding this comment.
Resolve relative data directories before embedding them
When the documented JAWK_DATA_DIR override is relative, such as JAWK_DATA_DIR=.jawk, this embeds that relative path verbatim in the launcher. The installation succeeds, but invoking jawk from any other working directory makes the jar lookup resolve against that directory and fail; convert the data directory to an absolute path before generating the launcher.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f1f6968b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| fi | ||
|
|
||
| if fetch_optional "${SUM_URL}" "${TMP_DIR}/jawk-standalone.jar.sha256"; then | ||
| EXPECTED_SUM="$(awk '{ print tolower($1); exit }' "${TMP_DIR}/jawk-standalone.jar.sha256")" |
There was a problem hiding this comment.
Distinguish missing checksums from download errors
When a published checksum cannot be fetched because of a transient network, TLS, or server error, fetch_optional returns false and this branch treats it like a pre-checksum 404, then installs the jar without verification. Fresh evidence in the final revision is this catch-all transport-failure path, which also exists in the PowerShell installer's checksum catch; only skip verification for a confirmed missing asset and abort or retry other failures.
Useful? React with 👍 / 👎.
| VERSION="${JAWK_VERSION:-latest}" | ||
| BIN_DIR="${JAWK_INSTALL_DIR:-${HOME}/.local/bin}" | ||
| DATA_DIR="${JAWK_DATA_DIR:-${XDG_DATA_HOME:-${HOME}/.local/share}/jawk}" | ||
| JAR_PATH="${DATA_DIR}/jawk-standalone.jar" |
There was a problem hiding this comment.
Resolve relative data directories before embedding them
When JAWK_DATA_DIR is relative, such as var/jawk, installation puts the jar relative to the installer's current directory but embeds that same relative path in the launcher. Invoking jawk from any other directory then checks for var/jawk/jawk-standalone.jar beneath the new working directory and exits with “not found”; normalize the configured data directory to an absolute path before constructing JAR_PATH.
Useful? React with 👍 / 👎.
| # locates a JRE (Java 8 or later) at run time, and adds the shim directory | ||
| # to the user PATH. | ||
|
|
||
| $ErrorActionPreference = 'Stop' |
There was a problem hiding this comment.
Restore the caller's PowerShell error preference
With the advertised irm https://jawk.io/get.ps1 | iex invocation, the installer executes in the caller's current scope, as described by Microsoft's PowerShell scope documentation, so this assignment persists after installation. The user's session is consequently left with ErrorActionPreference changed from its usual Continue value to Stop, making unrelated later commands terminate on non-terminating errors; isolate the installer body or restore the previous preference in a finally block.
Useful? React with 👍 / 👎.
Relative JAWK_DATA_DIR/JAWK_INSTALL_DIR overrides are resolved to absolute paths before the launcher and the user PATH entry are written, so `jawk` no longer depends on the working directory it was installed from. The one-command installation is recorded under Unreleased in behavior-changes.md. Refs #583 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Both points addressed in the latest commit:
@codex please review again |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6c4f13632c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| :try_java | ||
| if not exist "%~1" exit /b 0 | ||
| "%~1" -version >nul 2>&1 || exit /b 0 | ||
| "%~1" -version 2>&1 | findstr /r /c:"version .1\.[0-7]\." >nul && exit /b 0 |
There was a problem hiding this comment.
Reject legacy Java in the parent batch context
When JAWK_JAVA_HOME or JAVA_HOME points to Java 7 or older, this pipeline detects the legacy version, but exit /b 0 executes on the pipe's separate right-hand cmd context; the parent :try_java subroutine therefore continues to line 123 and selects the unsupported runtime instead of trying the next candidate. Microsoft's exit documentation specifies that /b exits the current batch context. Fresh evidence in this revision is that the newly added version check places that exit inside the pipeline rather than branching on findstr's status in the parent context.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6c4f13632c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| $installDir = if ($env:JAWK_INSTALL_DIR) { $env:JAWK_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'Jawk' } | ||
| # A relative override must not leave a working-directory-dependent entry on | ||
| # the user PATH: resolve it against the current location. | ||
| if (-not [System.IO.Path]::IsPathRooted($installDir)) { |
There was a problem hiding this comment.
Fully qualify drive-relative Windows install paths
When JAWK_INSTALL_DIR is a Windows drive-relative or root-relative path such as C:Jawk or \Jawk, IsPathRooted returns true even though the path is not fully qualified, so this branch skips normalization. The resulting user PATH entry still depends on the current directory for drive C or on the process's current drive and can stop locating jawk.cmd in a later terminal. Fresh evidence in the revised Windows normalization is its use of rootedness as a proxy for an absolute path; resolve all overrides to a fully qualified provider path instead.
Useful? React with 👍 / 👎.
- Both installers now distinguish a confirmed-missing checksum asset (HTTP 404: warn and skip verification) from transport or server errors, which abort instead of silently disabling verification. The PowerShell installer retries transient failures (Windows PowerShell can drop a reused connection) before giving up. - The PowerShell installer body runs in a script block, so the advertised `irm ... | iex` invocation no longer leaks ErrorActionPreference or helper variables into the caller's session. - Drive-relative and root-relative JAWK_INSTALL_DIR overrides (C:Jawk, \Jawk) are fully qualified with GetFullPath, aligning and restoring the process working directory around the call. - The shim reads the findstr verdict through errorlevel in a separate statement instead of appending exit /b to the pipeline, removing any ambiguity about which cmd context executes the exit. Refs #583 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
All four remaining points addressed in the latest commit:
@codex please review again |
|
Codex Review: Something went wrong. Try again later by commenting “@codex review”. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ecc2258471
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| wget -qO "$1" "${SUM_URL}" 2>/dev/null || _rc=$? | ||
| case "${_rc}" in | ||
| 0) return 0 ;; | ||
| 8) return 1 ;; # server error response: a missing asset with wget |
There was a problem hiding this comment.
Handle non-404 wget failures as verification errors
When curl is unavailable and the checksum request receives a 403, 429, or 5xx response, wget returns 8 and this revised branch treats it as a missing checksum, causing the installer to warn and install the jar without verification. The GNU Wget exit-status documentation defines status 8 as “Server issued an error response,” not specifically a 404; fresh evidence after the earlier review is this new wget-specific mapping, which must distinguish an actual 404 from other HTTP failures to preserve the intended fail-closed behavior.
Useful? React with 👍 / 👎.
wget exit status 8 covers every HTTP error response, so a 403, 429, or 5xx on the checksum download would have been treated as "this release publishes no checksum" and skipped verification. The wget branch now captures --server-response output and only downgrades to the warning when the response is an actual 404; every other failure aborts. Refs #583 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Addressed in the latest commit: the wget branch captures @codex please review again |
|
Codex Review: Didn't find any major issues. 🚀 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Fixes #583
What
src/site/resources/get— POSIX sh installer, published athttps://jawk.io/get:Downloads the standalone jar of the latest release (SHA-256 verified), installs it under
${XDG_DATA_HOME:-~/.local/share}/jawk/, and writes ajawklauncher into~/.local/bin— no sudo. The launcher locates a JRE (Java 8+) at run time (JAWK_JAVA_HOME,JAVA_HOME,javaon the PATH,/usr/libexec/java_homeon macOS,/usr/lib/jvmand sdkman on Linux) andexecsjava -jar, so upgrading Java never requires a reinstall.JAWK_VERSIONpins a release;JAWK_INSTALL_DIR/JAWK_DATA_DIRoverride locations.src/site/resources/get.ps1— Windows installer, published athttps://jawk.io/get.ps1:Installs under
%LOCALAPPDATA%\Jawkwith ajawk.cmdshim and adds the shim directory to the user PATH.release.yml— each GitHub release now also uploads a version-lessjawk-standalone.jaralias and.sha256checksum files, soreleases/latest/download/jawk-standalone.jaris a stable download URL that needs no API call. Until the next release publishes the alias, both installers fall back to resolving the latest version through the GitHub API — sojawk.io/getworks as soon as this lands on the site.Docs — new "One-Command Install" section in
install.md, short Installation section inREADME.md, and a.gitattributesrule pinning the sh installer to LF so a site built from a CRLF checkout can't ship a script thatshrejects.Testing
Both installers were exercised end to end on this machine (Git Bash and Windows PowerShell 5.1) against the real v7.0.01 release:
JAWK_VERSIONinstallsmvn verify sitepasses, and the generated site containsget,get.ps1, and the rendered install page🤖 Generated with Claude Code