Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
mvnw text eol=lf
gradlew text eol=lf

# The sh installer served from the documentation site must keep Unix line endings
# everywhere, or a site built from a CRLF checkout would ship a script that sh rejects
src/site/resources/get text eol=lf

# These are explicitly windows files and should use crlf
*.bat text eol=crlf
*.cmd text eol=crlf
208 changes: 208 additions & 0 deletions .github/workflows/installers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
name: Installer Smoke Tests

# Exercises the one-command installers served from the documentation site
# (src/site/resources/get and get.ps1) against the real latest GitHub release.
# The weekly run also guards against GitHub-side changes (release asset URL
# behavior) that the installers depend on.

on:
pull_request:
branches: [ "main" ]
paths:
- src/site/resources/get
- src/site/resources/get.ps1
- .github/workflows/installers.yml
push:
branches: [ "main" ]
paths:
- src/site/resources/get
- src/site/resources/get.ps1
- .github/workflows/installers.yml
schedule:
# Weekly, Monday 06:37 UTC
- cron: "37 6 * * 1"
workflow_dispatch:

permissions:
contents: read

jobs:
probe:
name: Probe the latest release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.release.outputs.version }}
available: ${{ steps.release.outputs.available }}
steps:
- name: Look for the standalone jar alias in the latest release
id: release
env:
GH_TOKEN: ${{ github.token }}
run: |
release_json="$(gh api repos/${{ github.repository }}/releases/latest)"
version="$(printf '%s' "${release_json}" | jq -r '.tag_name | ltrimstr("v")')"
available="$(printf '%s' "${release_json}" | jq -r '[.assets[].name] | index("ipmi-java-standalone.jar") != null')"
echo "Latest release: ${version} (publishes ipmi-java-standalone.jar: ${available})"
if [ "${available}" != "true" ]; then
echo "::notice::Release v${version} publishes no ipmi-java-standalone.jar (the command-line client ships with 2.0.00 and later): skipping the installer smoke tests."
fi
echo "version=${version}" >> "${GITHUB_OUTPUT}"
echo "available=${available}" >> "${GITHUB_OUTPUT}"

posix:
name: POSIX installer on ubuntu-latest
needs: probe
if: needs.probe.outputs.available == 'true'
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.probe.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v7

- name: Set up Temurin JDK 17
uses: actions/setup-java@v6
with:
distribution: temurin
java-version: "17"

- name: Install the latest release
run: |
sh src/site/resources/get 2>&1 | tee install.log
test -x "${HOME}/.local/bin/ipmicli" || { echo "ERROR: the launcher was not installed at ~/.local/bin/ipmicli" >&2; exit 1; }
grep -Fq 'Checksum verified.' install.log || { echo "ERROR: the installer did not verify the checksum." >&2; exit 1; }

- name: Check the version and help output of the launcher
run: |
out="$("${HOME}/.local/bin/ipmicli" --version)"
[ "${out}" = "ipmi-java ${VERSION}" ] || { echo "ERROR: expected 'ipmi-java ${VERSION}' but got '${out}'." >&2; exit 1; }
# The launcher exports IPMICLI_PROGRAM_NAME so the usage names the
# ipmicli command instead of "java -jar ...".
help="$("${HOME}/.local/bin/ipmicli" --help)"
printf '%s\n' "${help}" | grep -q '^ ipmicli -h <host> probe' \
|| { echo "ERROR: the help output does not name the ipmicli launcher." >&2; exit 1; }
if printf '%s\n' "${help}" | grep -Fq 'java -jar'; then
echo "ERROR: the help of the installed launcher mentions 'java -jar'." >&2
exit 1
fi

- name: Reinstall in place a pinned version with IPMICLI_VERSION
run: |
IPMICLI_VERSION="${VERSION}" sh src/site/resources/get
out="$("${HOME}/.local/bin/ipmicli" --version)"
[ "${out}" = "ipmi-java ${VERSION}" ] || { echo "ERROR: expected 'ipmi-java ${VERSION}' but got '${out}'." >&2; exit 1; }

- name: Exit 127 with the Adoptium message when no JRE is findable
run: |
# Hide every JRE the launcher can discover: IPMICLI_JAVA_HOME/JAVA_HOME
# and PATH go away with env -i, ~/.sdkman with a fake HOME, and the
# /usr/lib/jvm well-known root is moved aside for the duration.
mkdir -p "${RUNNER_TEMP}/nohome"
if [ -d /usr/lib/jvm ]; then sudo mv /usr/lib/jvm /usr/lib/jvm.hidden; fi
set +e
err="$(env -i HOME="${RUNNER_TEMP}/nohome" PATH=/nonexistent /bin/sh "${HOME}/.local/bin/ipmicli" --version 2>&1 >/dev/null)"
rc=$?
set -e
if [ -d /usr/lib/jvm.hidden ]; then sudo mv /usr/lib/jvm.hidden /usr/lib/jvm; fi
printf '%s\n' "${err}"
[ "${rc}" -eq 127 ] || { echo "ERROR: expected exit code 127 but got ${rc}." >&2; exit 1; }
printf '%s\n' "${err}" | grep -Fq 'adoptium.net' \
|| { echo "ERROR: the error message does not point to adoptium.net." >&2; exit 1; }

windows:
name: Windows installer with ${{ matrix.shell }}
needs: probe
if: needs.probe.outputs.available == 'true'
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
# powershell is Windows PowerShell 5.1, pwsh is PowerShell 7.
shell: [ powershell, pwsh ]
defaults:
run:
shell: ${{ matrix.shell }}
env:
VERSION: ${{ needs.probe.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v7

- name: Set up Temurin JDK 17
uses: actions/setup-java@v6
with:
distribution: temurin
java-version: "17"

- name: Install the latest release
run: |
iex (Get-Content -Raw src/site/resources/get.ps1)
$jarPath = Join-Path $env:LOCALAPPDATA 'ipmicli\ipmi-java-standalone.jar'
if (-not (Test-Path $jarPath)) { throw "The jar was not installed at $jarPath." }
$shimPath = Join-Path $env:LOCALAPPDATA 'ipmicli\bin\ipmicli.cmd'
if (-not (Test-Path $shimPath)) { throw "The ipmicli.cmd shim was not installed at $shimPath." }
$binDir = Join-Path $env:LOCALAPPDATA 'ipmicli\bin'
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
if (-not (($userPath -split ';') -contains $binDir)) { throw "$binDir was not added to the user PATH." }

- name: Check the version and help output of the shim
run: |
$shim = Join-Path $env:LOCALAPPDATA 'ipmicli\bin\ipmicli.cmd'
$out = & $shim --version
if ($LASTEXITCODE -ne 0) { throw "ipmicli.cmd --version exited with $LASTEXITCODE." }
if ("$out" -ne "ipmi-java $env:VERSION") { throw "Expected 'ipmi-java $env:VERSION' but got '$out'." }
# The shim sets IPMICLI_PROGRAM_NAME so the usage names the ipmicli
# command instead of "java -jar ...".
$help = & $shim --help | ForEach-Object { "$_" }
if ($LASTEXITCODE -ne 0) { throw "ipmicli.cmd --help exited with $LASTEXITCODE." }
if (-not ($help | Where-Object { $_ -match '^ ipmicli -h <host> probe' })) { throw 'The help output does not name the ipmicli launcher.' }
if (($help -join "`n") -match 'java -jar') { throw "The help of the installed shim mentions 'java -jar'." }

- name: Honor IPMICLI_JAVA_HOME when no other JRE is findable
run: |
$shim = Join-Path $env:LOCALAPPDATA 'ipmicli\bin\ipmicli.cmd'
$realJavaHome = $env:JAVA_HOME
Remove-Item Env:JAVA_HOME -ErrorAction SilentlyContinue
$env:Path = "$env:SystemRoot\System32;$env:SystemRoot"
$ErrorActionPreference = 'Continue'
# Without any JRE hint the shim must fail with the Adoptium message...
$lines = & $shim --version 2>&1 | ForEach-Object { "$_" }
if ($LASTEXITCODE -ne 127) { throw "Expected exit code 127 without a JRE but got $LASTEXITCODE." }
if (($lines -join "`n") -notmatch 'adoptium\.net') { throw "The error message does not point to adoptium.net." }
# ...and must succeed through IPMICLI_JAVA_HOME alone.
$env:IPMICLI_JAVA_HOME = $realJavaHome
$out = & $shim --version
if ($LASTEXITCODE -ne 0) { throw "ipmicli.cmd exited with $LASTEXITCODE despite IPMICLI_JAVA_HOME." }
if ("$out" -ne "ipmi-java $env:VERSION") { throw "Expected 'ipmi-java $env:VERSION' through IPMICLI_JAVA_HOME but got '$out'." }
exit 0

- name: Reject a pre-8 JRE
run: |
# Compile a fake java.exe that reports version 1.7, the newest version
# the shim must reject.
$fakeHome = Join-Path $env:RUNNER_TEMP 'fake-java7'
New-Item -ItemType Directory -Force -Path (Join-Path $fakeHome 'bin') | Out-Null
$source = @'
using System;
class FakeJava {
static int Main(string[] args) {
Console.Error.WriteLine("java version \"1.7.0_80\"");
Console.Error.WriteLine("Java(TM) SE Runtime Environment (build 1.7.0_80-b15)");
return 0;
}
}
'@
$sourcePath = Join-Path $fakeHome 'FakeJava.cs'
Set-Content -Path $sourcePath -Value $source
& "$env:SystemRoot\Microsoft.NET\Framework64\v4.0.30319\csc.exe" /nologo /out:"$fakeHome\bin\java.exe" $sourcePath
if ($LASTEXITCODE -ne 0) { throw "Could not compile the fake java.exe." }

$shim = Join-Path $env:LOCALAPPDATA 'ipmicli\bin\ipmicli.cmd'
Remove-Item Env:JAVA_HOME -ErrorAction SilentlyContinue
$env:Path = "$env:SystemRoot\System32;$env:SystemRoot"
$env:IPMICLI_JAVA_HOME = $fakeHome
$ErrorActionPreference = 'Continue'
$lines = & $shim --version 2>&1 | ForEach-Object { "$_" }
if ($LASTEXITCODE -ne 127) { throw "Expected exit code 127 with a Java 7 IPMICLI_JAVA_HOME but got $LASTEXITCODE." }
if (($lines -join "`n") -notmatch 'adoptium\.net') { throw "The error message does not point to adoptium.net." }
exit 0
24 changes: 24 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,27 @@ jobs:
autoRelease: false
jdkVersion: "17"
secrets: inherit

# The shared release workflow uploads the versioned jars only. The version-less alias gives the installers of the
# documentation site (src/site/resources/get and get.ps1) a stable download URL
# (releases/latest/download/ipmi-java-standalone.jar), and the .sha256 files let them verify the download.
standalone-assets:
name: Upload the standalone jar alias and checksums
needs: release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Upload ipmi-java-standalone.jar and the SHA-256 checksums to v${{ inputs.releaseVersion }}
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RELEASE_VERSION: ${{ inputs.releaseVersion }}
run: |
jar="ipmi-java-${RELEASE_VERSION}-standalone.jar"
gh release download "v${RELEASE_VERSION}" --pattern "${jar}"
cp "${jar}" ipmi-java-standalone.jar
sha256sum "${jar}" > "${jar}.sha256"
sha256sum ipmi-java-standalone.jar > ipmi-java-standalone.jar.sha256
gh release upload "v${RELEASE_VERSION}" --clobber \
ipmi-java-standalone.jar "${jar}.sha256" ipmi-java-standalone.jar.sha256
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,36 @@ A client owns one RMCP+ session, opened by the first operation and closed by `cl

## Command-line client

Every build also produces a self-contained executable jar with a command-line client, `ipmicli`, that exposes the same operations ([download the latest](https://github.com/metricshub/ipmi-java/releases/latest), Java 8 or later, no other file needed):
The library comes with a command-line client, `ipmicli`, that exposes the same operations. Install it with one command (Java 8 or later required):

```text
target/ipmi-java-<version>.jar
target/ipmi-java-<version>-standalone.jar
```shell
curl -fsSL https://metricshub.org/ipmi-java/get | sh
```

On Windows (PowerShell):

```powershell
irm https://metricshub.org/ipmi-java/get.ps1 | iex
```

See the [installation page](https://metricshub.org/ipmi-java/installation.html#command-line-client) for details. The client is a self-contained executable jar (`target/ipmi-java-<version>-standalone.jar`, also on the [releases page](https://github.com/metricshub/ipmi-java/releases/latest)), which `java -jar` runs as well.

What the BMC offers, before any login:

```bash
java -jar ipmi-java-<version>-standalone.jar -h bmc.example.com probe
ipmicli -h bmc.example.com probe
```

The sensors, with the password read from a file:

```bash
java -jar ipmi-java-<version>-standalone.jar -h bmc.example.com -u monitor -pf password.txt sensors
ipmicli -h bmc.example.com -u monitor -pf password.txt sensors
```

The FRU inventory as JSON Lines:

```bash
java -jar ipmi-java-<version>-standalone.jar -h bmc.example.com -u monitor -pf password.txt fru --json
ipmicli -h bmc.example.com -u monitor -pf password.txt fru --json
```

Subcommands: `probe`, `info` (the identity of the BMC), `chassis`, `power status|on|off|cycle|reset|soft|diag`, `identify`, `sensors`, `fru`, `sel` (the System Event Log, `--since <id>` for the new entries), `sol` (an interactive Serial over LAN console) and `raw` (any request, like `ipmitool raw`: `raw 0x2c 0x01 0xdc 0x01`). Diagnostics go to stderr, the exit code says what failed (77 authentication, 69 unreachable, 124 timeout...). See the [Command-Line Client](https://metricshub.org/ipmi-java/cli.html) page for the options, the output formats and the `ipmitool` equivalents.
Expand Down Expand Up @@ -108,6 +115,8 @@ It is strongly recommended to only use [GitHub Actions "Release to Maven Central

* Manually trigger the "Release to Maven Central" workflow
* Specify the version being released and the next version number (SNAPSHOT)
* The workflow creates the GitHub release, then adds the `ipmi-java-standalone.jar` alias and the `.sha256` checksums
that the `ipmicli` installers download (if that last job fails, re-run it: the installers fail until it succeeds)
* Publish the pending deployment on the [Central Portal](https://central.sonatype.com/publishing/deployments)
(the workflow uploads and validates it, but does not publish it automatically)
* Merge the PR that has been created to prepare the next version
Expand Down
31 changes: 22 additions & 9 deletions src/site/markdown/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,31 @@ description: Manual of the ipmicli command-line client of the IPMI Java Client -
<!-- MACRO{toc|fromDepth=2|toDepth=3|id=toc} -->

Every release ships a self-contained executable jar that bundles the library and a command-line
client: download `${project.artifactId}-${project.version}-standalone.jar` from the
[latest release](https://github.com/metricshub/ipmi-java/releases/latest) and run it with Java 8
or later. This page is its manual.
client, **`ipmicli`**. This page is its manual. Install it with one command (Java 8 or later is
required):

> [!TABS]
> * Linux / macOS
> ```shell-session
> $ curl -fsSL https://metricshub.org/ipmi-java/get | sh
> ```
> * Windows (PowerShell)
> ```powershell
> irm https://metricshub.org/ipmi-java/get.ps1 | iex
> ```

The installer verifies the checksum of the jar and puts an `ipmicli` launcher on the `PATH`; see
[Installation](installation.html#command-line-client) to inspect the script first, pin a version
or change the directories.

```text
java -jar ${project.artifactId}-${project.version}-standalone.jar [options] <subcommand> [subcommand options]
ipmicli [options] <subcommand> [subcommand options]
```

The command is called **`ipmicli`** in this page and in its own usage. Give it that name with a
shell alias (`alias ipmicli='java -jar /path/to/${project.artifactId}-${project.version}-standalone.jar'`,
or a `doskey ipmicli=java -jar ... $*` macro on Windows) until the one-command installer
([#163](https://github.com/metricshub/ipmi-java/issues/163)) puts a launcher on the `PATH`. The
jar needs no other file: the library has no dependency.
Without the installer, download `${project.artifactId}-${project.version}-standalone.jar` from the
[latest release](https://github.com/metricshub/ipmi-java/releases/latest) and run it with
`java -jar ${project.artifactId}-${project.version}-standalone.jar` wherever this page says
`ipmicli`. The jar needs no other file: the library has no dependency.

The CLI is a thin layer over the [`IpmiClient`](index.html#quick-start) API: every subcommand is
one operation of the client, with the same defaults, the same
Expand Down Expand Up @@ -481,6 +493,7 @@ The sysexits values of the MetricsHub command-line clients:
| `74` | Local I/O failure (stdout closed). |
| `77` | Authentication or authorization failure: unknown user, wrong password or BMC key, privilege level refused at login, insufficient privilege (`D4h`, with the `-L` hint). |
| `124` | Timeout: the deadline of the subcommand expired, or a message of the session got no reply after all the tries. |
| `127` | The launcher of the [installer](installation.html#command-line-client) found no Java 8 or later (install one from [Adoptium](https://adoptium.net), or set `IPMICLI_JAVA_HOME`), or no jar (run the installer again). |

The mapping follows the [exceptions](timeouts-and-errors.html#exceptions) of the client:
`IpmiConnectionException` 69, `IpmiAuthenticationException` 77, `IpmiTimeoutException` 124,
Expand Down
7 changes: 4 additions & 3 deletions src/site/markdown/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ boards) over **IPMI 2.0 over LAN (RMCP+)**, on UDP port 623. It lets a Java appl
OEM network functions included), or through the low-level connector
([Low-Level API](low-level-api.html)).

The same operations are available from the command line with the **`ipmicli`** client of the
standalone jar ([Command-Line Client](cli.html)):
The same operations are available from the command line with the **`ipmicli`** client
([Command-Line Client](cli.html)), installed with `curl -fsSL https://metricshub.org/ipmi-java/get | sh`
(`irm https://metricshub.org/ipmi-java/get.ps1 | iex` on Windows, see [Installation](installation.html#command-line-client)):

```text
java -jar ipmi-java-${project.version}-standalone.jar -h bmc.example.com -u monitor -pf pw.txt sensors
ipmicli -h bmc.example.com -u monitor -pf pw.txt sensors
```

The library is the IPMI engine of [MetricsHub](https://metricshub.com) hardware monitoring: its
Expand Down
Loading
Loading