# eish
Generate self-contained installation scripts for binaries published as GitHub
release assets.
`eish` inspects a release, works out which asset belongs to which platform, and
renders an `install.sh`, `install.fish` or `install.ps1` with that mapping baked
in. The generated script needs nothing but `curl`/`wget`, `tar` and `unzip` — it
never calls the GitHub API, so it keeps working when the API is rate limited or
the machine is behind a flaky network.
```sh
cargo install --path .
eish quickjs-ng/quickjs --name qjs > install.sh
eish quickjs-ng/quickjs@v0.16.2 --shell powershell --name qjs > install.ps1
eish quickjs-ng/quickjs --shell fish --proxy xget --name qjs > install.fish
```
## Usage
```text
eish <SPEC> [OPTIONS]
<SPEC> owner/repo, owner/repo@tag, or a full GitHub URL
jsdelivr | statically [default: github]
--tag <TAG> tag to install, overrides `@tag`
-b, --binary <BINARY> executable name inside the archive
--name <NAME> install this program, when the release holds several
--target <TARGET> target triple the script defaults to
--dir <DIR> default install directory [default: ~/.ei]
--type <TYPE> release | file [default: release]
-o, --output <PATH> write to a file instead of stdout
--asset <FILE> bundle an asset explicitly (repeatable)
--list list the supported platforms and exit
-v, --verbose explain what eish is doing
-q, --quiet errors only
```
`eish --help` lists the rest (`--ref`, `--release-json`, `--api-base`,
`--min-disk-space`).
Everything except the script goes to stderr, so stdout stays clean to pipe:
```sh
```
A plain run is silent. `-v` explains each step and `-vv` adds detail.
### Common cases
```sh
# Pin a version
eish owner/repo@v1.2.3 > install.sh
# Install from a file you already have, with no network access
curl -LO https://github.com/quickjs-ng/quickjs/releases/latest/download/qjs-linux-x86_64
eish quickjs-ng/quickjs --name qjs > install.sh
./install.sh --file ./qjs-linux-x86_64
# Generate without touching the network at all
eish owner/repo --asset tool-linux-amd64 --asset tool-macos-arm64 > install.sh
```
A release can hold several programs, and an installer can only install one. So
`eish` refuses rather than guessing, and the error lists the programs to choose
from — best-covered first, with how many platforms each one supports:
```sh
eish quickjs-ng/quickjs --name qjs > install.sh
eish quickjs-ng/quickjs --name qjsc > install-qjsc.sh
```
A release with a single program needs no `--name`. The match is on the program a
file is *for*, not a prefix — `qjsc`'s assets start with `qjs` but belong only to
`qjsc`. A name that appears in no asset is rejected, and so is a `--file` whose
name belongs to a program you did not choose; both messages list the names that
would work.
## The generated script
It opens with the command that produced it, so a file found in someone's CI can
be traced back and regenerated:
```sh
#!/usr/bin/env bash
#
# Installer for quickjs-ng/quickjs
#
# This file was generated by eish <VERSION> <COMMIT> and is not meant to be
# edited: the next time it is regenerated, any change made here is lost.
#
# The complete command that produced this file:
#
# eish quickjs-ng/quickjs --proxy xget --name qjs
```
The command is the real one, taken from the arguments `eish` was called with —
including things the script itself has no trace of, such as `--release-json`. It
is safe to commit: credentials come from the environment or a helper, never from
the command line.
The version line carries the commit too (as `0.2.0 a1b2c3d`), so a script can be
traced to the exact build that wrote it rather than just the release. A
`-modified` suffix means the build came from an uncommitted tree.
At install time it detects the platform (OS, architecture and libc), looks it up
in an embedded table, falls back to a compatible build if the exact one was not
published, then downloads, unpacks, installs into `~/.ei` and updates `PATH`.
Every option has a matching environment variable — `EI_DIR`, `EI_PROXY`,
`EI_TARGET`, `EI_FILE`, … — and `--help` documents them:
```sh
EI_DIR=/usr/local/bin ./install.sh
```
## Credentials
GitHub allows 60 anonymous API requests per hour per IP. There is no `--token`
flag — a secret on the command line ends up in your shell history and in `ps`
output. Instead `eish` uses credentials you already have:
1. `GITHUB_TOKEN` or `GH_TOKEN`
2. `gh auth token` (GitHub CLI)
3. `git credential fill` (Git Credential Manager)
So either run `gh auth login`, or set `GITHUB_TOKEN` in CI. `-v` reports which
source was used.
## Supported
Formats: `.tar.gz`, `.tgz`, `.tar.xz`, `.txz`, `.tar.bz2`, `.tbz2`, `.zip`,
`.gz`, `.exe`, and bare binaries such as `qjs-linux-x86_64`. Checksums, `.msi`,
`.deb` and similar are ignored.
Targets: Linux (glibc and musl), macOS, Windows, Android and the BSDs, across
x86_64, aarch64, armv7, arm, i686, riscv64gc, loongarch64, powerpc64le and
s390x. Asset names are matched by the
[`guess-target`](https://github.com/ahaoboy/guess-target) crate, so informal
names like `jq-linux-amd64` work as well as full Rust triples.
## Library
```rust
use eish::{Client, InstallSpec, Proxy, Shell};
let mut spec = InstallSpec::new("easy-install", "easy-install")
.with_shell(Shell::Bash)
.with_proxy(Proxy::Xget);
let release = Client::new().release("easy-install", "easy-install", None)?;
spec.apply_release_checked(&release)?;
std::fs::write("install.sh", spec.render()?)?;
```
The `cli` feature (on by default) builds the binary and pulls in `clap`. For the
library alone: `eish = { version = "0.1", default-features = false }`.
## Development
```sh
cargo test
cargo clippy --all-targets -- -D warnings
cargo fmt --all -- --check
```
## License
MIT OR Apache-2.0.