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
50 changes: 50 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: pages

on:
push:
branches: [main]
paths:
- "site/**"
- "install.sh"
- ".github/workflows/pages.yml"
workflow_dispatch:

# Never publish a stale build over a newer one, but let an in-flight deploy
# finish rather than cancelling it half-way.
concurrency:
group: pages
cancel-in-progress: false

jobs:
deploy:
name: build and deploy the site
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- uses: actions/checkout@v5

- name: Shell-check the installer
run: |
sh -n install.sh
if command -v shellcheck >/dev/null; then shellcheck -s sh install.sh; fi

- name: Assemble the site
run: |
mkdir -p _site
cp -r site/. _site/
# Served at /ratfetch/install.sh — the URL the README advertises.
cp install.sh _site/install.sh

- uses: actions/configure-pages@v5

- uses: actions/upload-pages-artifact@v3
with:
path: _site

- id: deploy
uses: actions/deploy-pages@v4
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,43 @@ Linux only — the collectors are built on `/proc` and `/sys`.

## Install

```sh
curl -fsSL https://tuna4ll.github.io/ratfetch/install.sh | sh
```

That picks the right prebuilt binary for your architecture and libc, verifies
its checksum against the release's `SHA256SUMS`, and installs it to
`~/.local/bin` (or `/usr/local/bin` when run as root). `RATFETCH_VERSION`,
`RATFETCH_INSTALL_DIR` and `RATFETCH_NO_MODIFY_PATH` override the defaults.
The script is [`install.sh`](install.sh) in this repo — read it before piping
it to a shell, as you should with any installer.

Prebuilt for `x86_64` and `aarch64`, gnu and musl.

### Manual setup

Any other architecture, or if you would rather build it yourself. Needs Rust
1.88 or newer.

```sh
cargo install --git https://github.com/tuna4ll/ratfetch
```

From a checkout, which is also how you build it to hack on:

```sh
git clone https://github.com/tuna4ll/ratfetch
cd ratfetch
cargo install --path .
```

Needs Rust 1.88 or newer.
Both put the binary in `~/.cargo/bin`. To keep it out of the cargo tree
entirely, build and place it yourself:

```sh
cargo build --release
install -m 755 target/release/ratfetch ~/.local/bin/
```

## Use

Expand Down
153 changes: 153 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#!/bin/sh
# ratfetch installer.
#
# curl -fsSL https://tuna4ll.github.io/ratfetch/install.sh | sh
#
# Downloads the prebuilt binary for this machine from the latest GitHub
# release, verifies its checksum, and drops it in a bin directory on PATH.
#
# Knobs (all optional):
# RATFETCH_VERSION=v0.1.0 install a specific tag instead of the latest
# RATFETCH_INSTALL_DIR=DIR where the binary lands
# RATFETCH_NO_MODIFY_PATH=1 never touch the shell profile

set -eu

REPO="tuna4ll/ratfetch"
VERSION="${RATFETCH_VERSION:-}"
INSTALL_DIR="${RATFETCH_INSTALL_DIR:-}"

# ANSI only when stderr is a terminal, so piped output stays clean.
if [ -t 2 ]; then
bold=$(printf '\033[1m'); dim=$(printf '\033[2m')
red=$(printf '\033[31m'); green=$(printf '\033[32m'); reset=$(printf '\033[0m')
else
bold=''; dim=''; red=''; green=''; reset=''
fi

say() { printf '%s\n' "${dim}::${reset} $*" >&2; }
ok() { printf '%s\n' "${green}✓${reset} $*" >&2; }
die() { printf '%s\n' "${red}error:${reset} $*" >&2; exit 1; }

need() { command -v "$1" >/dev/null 2>&1 || die "\`$1\` is required but not installed."; }

# --- what are we running on -------------------------------------------------

[ "$(uname -s)" = "Linux" ] || die "ratfetch is Linux only — the collectors read /proc and /sys."

case "$(uname -m)" in
x86_64 | amd64) arch=x86_64 ;;
aarch64 | arm64) arch=aarch64 ;;
*) die "unsupported architecture: $(uname -m). Build from source: cargo install --git https://github.com/$REPO" ;;
esac

# musl systems (Alpine, Void musl, …) cannot run the gnu build; glibc systems
# can run either, and the gnu build is the better citizen there.
libc=gnu
if ldd --version 2>&1 | grep -qi musl; then
libc=musl
else
for loader in /lib/ld-musl-*; do
[ -e "$loader" ] && libc=musl
done
fi

target="${arch}-unknown-linux-${libc}"

# --- how do we fetch --------------------------------------------------------

if command -v curl >/dev/null 2>&1; then
fetch() { curl -fsSL "$1"; }
fetch_to() { curl -fsSL -o "$2" "$1"; }
elif command -v wget >/dev/null 2>&1; then
fetch() { wget -qO- "$1"; }
fetch_to() { wget -qO "$2" "$1"; }
else
die "neither curl nor wget found."
fi

need tar
need mktemp

# --- which version ----------------------------------------------------------

if [ -z "$VERSION" ]; then
say "resolving the latest release"
# One grep over the release JSON is enough; pulling in jq for a single
# field would be a heavier dependency than the whole installer.
# Buffered into a variable first: piping curl straight into grep -m1 kills
# the transfer mid-write and prints a spurious "failure writing output".
latest=$(fetch "https://api.github.com/repos/$REPO/releases/latest" || true)
VERSION=$(printf '%s\n' "$latest" | grep '"tag_name"' | head -1 | cut -d'"' -f4)
[ -n "$VERSION" ] || die "could not resolve the latest release. Set RATFETCH_VERSION=vX.Y.Z."
fi

name="ratfetch-${VERSION}-${target}"
base="https://github.com/$REPO/releases/download/$VERSION"

say "installing ${bold}ratfetch ${VERSION}${reset} for ${bold}${target}${reset}"

# --- download and verify ----------------------------------------------------

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT INT TERM

fetch_to "$base/${name}.tar.gz" "$tmp/${name}.tar.gz" \
|| die "no build published for ${target} in ${VERSION}."

if fetch_to "$base/SHA256SUMS" "$tmp/SHA256SUMS" 2>/dev/null; then
expected=$(grep " ${name}.tar.gz\$" "$tmp/SHA256SUMS" | cut -d' ' -f1 || true)
if [ -n "$expected" ] && command -v sha256sum >/dev/null 2>&1; then
actual=$(sha256sum "$tmp/${name}.tar.gz" | cut -d' ' -f1)
[ "$expected" = "$actual" ] || die "checksum mismatch for ${name}.tar.gz — refusing to install."
ok "checksum verified"
else
say "skipping checksum verification (no entry or no sha256sum)"
fi
fi

tar -xzf "$tmp/${name}.tar.gz" -C "$tmp"
[ -x "$tmp/$name/ratfetch" ] || die "the archive did not contain a ratfetch binary."

# --- pick a destination -----------------------------------------------------

if [ -z "$INSTALL_DIR" ]; then
if [ "$(id -u)" -eq 0 ]; then
INSTALL_DIR=/usr/local/bin
else
INSTALL_DIR="${XDG_BIN_HOME:-$HOME/.local/bin}"
fi
fi

mkdir -p "$INSTALL_DIR" || die "cannot create $INSTALL_DIR."
install -m 755 "$tmp/$name/ratfetch" "$INSTALL_DIR/ratfetch" \
|| die "cannot write to $INSTALL_DIR. Set RATFETCH_INSTALL_DIR to somewhere writable."

ok "installed ${bold}$INSTALL_DIR/ratfetch${reset}"

# --- PATH -------------------------------------------------------------------

case ":$PATH:" in
*":$INSTALL_DIR:"*) on_path=1 ;;
*) on_path=0 ;;
esac

if [ "$on_path" -eq 0 ] && [ "${RATFETCH_NO_MODIFY_PATH:-0}" != "1" ]; then
line="export PATH=\"$INSTALL_DIR:\$PATH\""
added=''
for profile in "$HOME/.bashrc" "$HOME/.zshrc"; do
[ -f "$profile" ] || continue
grep -qF "$line" "$profile" && continue
printf '\n# added by the ratfetch installer\n%s\n' "$line" >> "$profile"
added="$added $(basename "$profile")"
done
if [ -n "$added" ]; then
ok "added $INSTALL_DIR to PATH in$added — open a new shell, or run: $line"
else
say "$INSTALL_DIR is not on your PATH. Add it with: $line"
fi
elif [ "$on_path" -eq 0 ]; then
say "$INSTALL_DIR is not on your PATH. Add it with: export PATH=\"$INSTALL_DIR:\$PATH\""
fi

printf '\n%s\n' "${green}ratfetch ${VERSION} is ready.${reset} Run ${bold}ratfetch${reset} to start, or ${bold}ratfetch --once${reset} for a single frame." >&2
125 changes: 125 additions & 0 deletions site/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ratfetch — a system fetch that does not stop at one frame</title>
<meta name="description" content="A live, always-updating system fetch for the terminal. Install with one command.">
<style>
:root {
color-scheme: light dark;
--bg: #faf9f7;
--fg: #1b1a18;
--muted: #6c6a66;
--line: #e2ded8;
--card: #ffffff;
--accent: #b4531f;
--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #131211; --fg: #ececea; --muted: #9a968f;
--line: #2a2825; --card: #1b1a18; --accent: #e08a4e;
}
}
* { box-sizing: border-box; }
body {
margin: 0; background: var(--bg); color: var(--fg);
font: 16px/1.6 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
-webkit-font-smoothing: antialiased;
}
main { max-width: 46rem; margin: 0 auto; padding: 4.5rem 1.5rem 6rem; }
h1 { font-family: var(--mono); font-size: 2.1rem; margin: 0 0 .4rem; letter-spacing: -.02em; }
h1 span { color: var(--accent); }
.tagline { color: var(--muted); font-size: 1.05rem; margin: 0 0 2.5rem; }
h2 { font-size: .8rem; text-transform: uppercase; letter-spacing: .09em;
color: var(--muted); font-weight: 600; margin: 3rem 0 .9rem; }
.cmd {
display: flex; align-items: center; gap: .75rem;
background: var(--card); border: 1px solid var(--line); border-radius: 10px;
padding: .9rem 1rem; font-family: var(--mono); font-size: .92rem; overflow-x: auto;
}
.cmd code { white-space: nowrap; }
.cmd .prompt { color: var(--accent); user-select: none; }
button {
margin-left: auto; flex: none; font: inherit; font-size: .8rem;
background: transparent; color: var(--muted); border: 1px solid var(--line);
border-radius: 6px; padding: .3rem .6rem; cursor: pointer;
}
button:hover { color: var(--fg); border-color: var(--muted); }
ul { padding-left: 1.1rem; }
li { margin: .35rem 0; }
li b { font-weight: 600; }
pre {
background: var(--card); border: 1px solid var(--line); border-radius: 10px;
padding: 1rem; overflow-x: auto; font-family: var(--mono); font-size: .88rem; margin: 0;
}
a { color: var(--accent); }
img { max-width: 100%; border: 1px solid var(--line); border-radius: 10px; }
footer { margin-top: 4rem; padding-top: 1.5rem; border-top: 1px solid var(--line);
color: var(--muted); font-size: .88rem; }
.note { color: var(--muted); font-size: .88rem; margin-top: .7rem; }
</style>
</head>
<body>
<main>
<h1>rat<span>fetch</span></h1>
<p class="tagline">A system fetch that does not stop at one frame. Linux, terminal, live.</p>

<div class="cmd">
<span class="prompt">$</span>
<code id="one-liner">curl -fsSL https://tuna4ll.github.io/ratfetch/install.sh | sh</code>
<button type="button" id="copy">copy</button>
</div>
<p class="note">
Detects your architecture and libc, verifies the release checksum, and installs into
<code>~/.local/bin</code> (or <code>/usr/local/bin</code> as root).
Prefer to read it first? <a href="install.sh">install.sh</a>.
</p>

<h2>What it does</h2>
<ul>
<li><b>Live, not a snapshot.</b> Everything re-reads on a configurable interval.</li>
<li><b>526 logos</b>, vendored from fastfetch with their original colour palettes.</li>
<li><b>Four tabs</b> — overview, processes, filesystems, interfaces.</li>
<li><b>13 themes</b>, plus per-role colour overrides.</li>
<li><b>No <code>sysinfo</code> crate.</b> <code>/proc</code> and <code>/sys</code> are read directly.</li>
</ul>

<img src="https://github.com/user-attachments/assets/eadefa47-c392-423c-9c4d-99c015d117f6"
alt="ratfetch running in a terminal: logo, info table, meters, graphs and a process table" loading="lazy">

<h2>Other ways to install</h2>
<pre># a specific version, or a different directory
RATFETCH_VERSION=v0.1.0 sh -c "$(curl -fsSL https://tuna4ll.github.io/ratfetch/install.sh)"
RATFETCH_INSTALL_DIR=~/bin sh -c "$(curl -fsSL https://tuna4ll.github.io/ratfetch/install.sh)"

# manual setup, from source — needs Rust 1.88+
cargo install --git https://github.com/tuna4ll/ratfetch

# or from a checkout
git clone https://github.com/tuna4ll/ratfetch
cd ratfetch &amp;&amp; cargo install --path .</pre>

<h2>Then</h2>
<pre>ratfetch # the live TUI
ratfetch --once # one frame, like a classic fetch tool
ratfetch --theme gruvbox-dark
ratfetch --generate-config # a fully commented config.toml</pre>

<footer>
<a href="https://github.com/tuna4ll/ratfetch">github.com/tuna4ll/ratfetch</a> ·
MIT · Linux only
</footer>
</main>
<script>
document.getElementById('copy').addEventListener('click', async (e) => {
try {
await navigator.clipboard.writeText(document.getElementById('one-liner').textContent);
e.target.textContent = 'copied';
setTimeout(() => { e.target.textContent = 'copy'; }, 1500);
} catch { e.target.textContent = 'press ⌘/ctrl+c'; }
});
</script>
</body>
</html>