Skip to content

Commit f76753b

Browse files
ralyodioclaude
andauthored
feat: curl | sh installer (#14)
Adds install.sh at the repo root so users install the CLI with one command: curl -fsSL https://raw.githubusercontent.com/profullstack/transcoder/master/install.sh | sh It installs @profullstack/transcoder under ~/.local (no root), fetches a checksum-verified official Node into ~/.local/share/transcoder when the machine has none new enough, writes a shim per bin, and an uninstall.sh. Generated from the W3BS npm CLI installer template so the tool can be listed at https://w3bs.org/packages. The README's install section now leads with the curl line. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 53064c5 commit f76753b

2 files changed

Lines changed: 175 additions & 0 deletions

File tree

‎README.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,17 @@ sudo pacman -S ffmpeg
148148

149149
## Installation
150150

151+
```bash
152+
curl -fsSL https://raw.githubusercontent.com/profullstack/transcoder/master/install.sh | sh
153+
```
154+
155+
That installs the `transcoder`, `install-ffmpeg` and `install-imagemagick` commands under
156+
`~/.local` (no root; it brings its own Node if yours is older than 20). It does not build
157+
FFmpeg or ImageMagick: run `install-ffmpeg` / `install-imagemagick` yourself if you need
158+
them. Remove it with `sh ~/.local/share/transcoder/uninstall.sh`.
159+
160+
For the library, add it to your project:
161+
151162
```bash
152163
npm install @profullstack/transcoder
153164
# or

‎install.sh‎

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/bin/sh
2+
# transcoder installer.
3+
#
4+
# curl -fsSL https://raw.githubusercontent.com/profullstack/transcoder/master/install.sh | sh
5+
#
6+
# Installs @profullstack/transcoder (commands: transcoder install-ffmpeg install-imagemagick) under your home directory. No root, no system
7+
# package manager, no system files touched. If this machine has no Node 20+,
8+
# an official Node build is fetched (checksum verified) into the same private directory
9+
# and used from there. Run this again to update. Remove with:
10+
# sh ~/.local/share/transcoder/uninstall.sh
11+
#
12+
# sh -s -- --version X install a specific release
13+
# sh -s -- --prefix DIR install root (default: ~/.local)
14+
#
15+
# Listed in the W3BS catalog (https://w3bs.org/packages): `w3bs install transcoder` runs
16+
# this same script and `w3bs update` re-runs it.
17+
#
18+
# Generated from the W3BS npm CLI installer template; keep the variables below in sync
19+
# with package.json. Nothing runs until the last line, so a truncated download is inert.
20+
21+
main() {
22+
set -eu
23+
PKG="@profullstack/transcoder"
24+
TOOL="transcoder"
25+
URL="https://raw.githubusercontent.com/profullstack/transcoder/master/install.sh"
26+
NODE_MAJOR="20"
27+
PREFIX="${INSTALL_PREFIX:-$HOME/.local}"
28+
VERSION=""
29+
NODE_CHOICE="${INSTALL_NODE:-auto}"
30+
31+
while [ $# -gt 0 ]; do
32+
case "$1" in
33+
--version) VERSION="${2:?--version needs a value}"; shift ;;
34+
--prefix) PREFIX="${2:?--prefix needs a value}"; shift ;;
35+
-h | --help) echo "Usage: curl -fsSL $URL | sh [-s -- --version X --prefix DIR]"; return 0 ;;
36+
*) echo "$TOOL installer: unknown option $1" >&2; return 64 ;;
37+
esac
38+
shift
39+
done
40+
41+
say() { printf '%s\n' "$*"; }
42+
fail() { printf '%s installer: %s\n' "$TOOL" "$*" >&2; exit 1; }
43+
44+
case "$(uname -s)" in
45+
Linux) OS=linux ;;
46+
Darwin) OS=darwin ;;
47+
*) fail "unsupported operating system $(uname -s); Linux and macOS are supported" ;;
48+
esac
49+
case "$(uname -m)" in
50+
x86_64 | amd64) ARCH=x64 ;;
51+
aarch64 | arm64) ARCH=arm64 ;;
52+
*) fail "unsupported CPU $(uname -m)" ;;
53+
esac
54+
if command -v curl >/dev/null 2>&1; then
55+
fetch() { curl --proto '=https' --tlsv1.2 -fsSL "$1" -o "$2"; }
56+
elif command -v wget >/dev/null 2>&1; then
57+
fetch() { wget --https-only -q "$1" -O "$2"; }
58+
else
59+
fail "curl or wget is required"
60+
fi
61+
62+
SHARE="$PREFIX/share/$TOOL"
63+
BIN="$PREFIX/bin"
64+
mkdir -p "$SHARE" "$BIN"
65+
66+
node_major() { "$1" -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0; }
67+
NODE=""
68+
NODE_KIND=""
69+
if [ "$NODE_CHOICE" != private ] && command -v node >/dev/null 2>&1 && [ "$(node_major node)" -ge "$NODE_MAJOR" ]; then
70+
NODE="$(command -v node)"
71+
NODE_KIND=system
72+
elif [ -x "$SHARE/node/bin/node" ] && [ "$(node_major "$SHARE/node/bin/node")" -ge "$NODE_MAJOR" ]; then
73+
NODE="$SHARE/node/bin/node"
74+
NODE_KIND=private
75+
fi
76+
if [ -z "$NODE" ]; then
77+
say "No Node $NODE_MAJOR+ here; fetching an official Node 24 into $SHARE/node ..."
78+
TMP="$(mktemp -d)"
79+
trap 'rm -rf "$TMP"' EXIT
80+
fetch "https://nodejs.org/dist/latest-v24.x/SHASUMS256.txt" "$TMP/SHASUMS256.txt" || fail "cannot reach nodejs.org"
81+
TARBALL="$(grep -o "node-v24\.[0-9.]*-$OS-$ARCH\.tar\.gz" "$TMP/SHASUMS256.txt" | head -n 1 || true)"
82+
[ -n "$TARBALL" ] || fail "nodejs.org has no Node 24 build for $OS-$ARCH"
83+
fetch "https://nodejs.org/dist/latest-v24.x/$TARBALL" "$TMP/$TARBALL" || fail "cannot download $TARBALL"
84+
if command -v sha256sum >/dev/null 2>&1; then SUM="$(sha256sum "$TMP/$TARBALL" | cut -d ' ' -f 1)"
85+
else SUM="$(shasum -a 256 "$TMP/$TARBALL" | cut -d ' ' -f 1)"; fi
86+
grep -q "^$SUM $TARBALL\$" "$TMP/SHASUMS256.txt" || fail "checksum mismatch for $TARBALL"
87+
rm -rf "$SHARE/node" && mkdir -p "$SHARE/node"
88+
tar -xzf "$TMP/$TARBALL" -C "$SHARE/node" --strip-components=1 || fail "cannot unpack $TARBALL"
89+
NODE="$SHARE/node/bin/node"
90+
NODE_KIND=private
91+
fi
92+
if [ "$NODE_KIND" = private ]; then NPM="$SHARE/node/bin/npm"; else NPM="$(dirname "$NODE")/npm"; [ -x "$NPM" ] || NPM=npm; fi
93+
# npm runs package scripts with the Node in PATH; make that the chosen one.
94+
PATH="$(dirname "$NODE"):$PATH"
95+
export PATH
96+
97+
SPEC="$PKG"
98+
[ -n "$VERSION" ] && SPEC="$PKG@$VERSION"
99+
say "Installing $SPEC into $SHARE ..."
100+
rm -rf "$SHARE/node_modules" "$SHARE/package.json" "$SHARE/package-lock.json"
101+
cd "$SHARE"
102+
printf '{ "name": "%s-install", "private": true }\n' "$TOOL" > package.json
103+
"$NPM" install --silent --no-audit --no-fund --omit=dev "$SPEC" >/dev/null 2>&1 ||
104+
"$NPM" install --no-audit --no-fund --omit=dev "$SPEC" || fail "npm could not install $SPEC"
105+
PKG_DIR="$SHARE/node_modules/$PKG"
106+
[ -f "$PKG_DIR/package.json" ] || fail "$SPEC installed but $PKG_DIR is missing"
107+
108+
# One shim per entry in the package's own `bin` map: a script, not a symlink, so the
109+
# command always runs this copy with the chosen Node.
110+
BINS="$("$NODE" -e '
111+
const p = require(process.argv[1] + "/package.json");
112+
const b = typeof p.bin === "string" ? { [p.name.split("/").pop()]: p.bin } : (p.bin || {});
113+
for (const [k, v] of Object.entries(b)) if (/^[A-Za-z0-9._-]+$/.test(k)) console.log(k + "\t" + v);
114+
' "$PKG_DIR")"
115+
[ -n "$BINS" ] || fail "$SPEC declares no commands"
116+
PATHS="$SHARE"
117+
NAMES=""
118+
OLDIFS="$IFS"
119+
IFS='
120+
'
121+
for line in $BINS; do
122+
name="${line%% *}"
123+
rel="${line#* }"
124+
entry="$PKG_DIR/$rel"
125+
[ -f "$entry" ] || fail "$SPEC is missing $rel"
126+
case "$rel" in
127+
*.sh) # a shell-script command (install-ffmpeg, install-imagemagick): run it by its own shebang
128+
chmod 0755 "$entry"
129+
printf '#!/bin/sh\n# %s, installed by the %s installer (%s).\nexec "%s" "$@"\n' "$name" "$TOOL" "$URL" "$entry" > "$BIN/$name" ;;
130+
*) printf '#!/bin/sh\n# %s, installed by the %s installer (%s).\nexec "%s" "%s" "$@"\n' "$name" "$TOOL" "$URL" "$NODE" "$entry" > "$BIN/$name" ;;
131+
esac
132+
chmod 0755 "$BIN/$name"
133+
PATHS="$PATHS
134+
$BIN/$name"
135+
NAMES="$NAMES $name"
136+
done
137+
IFS="$OLDIFS"
138+
139+
INSTALLED="$("$NODE" -p "require('$PKG_DIR/package.json').version" 2>/dev/null || echo unknown)"
140+
{
141+
printf '{\n "package": "%s",\n "version": "%s",\n "installer": "%s",\n' "$PKG" "$INSTALLED" "$URL"
142+
printf ' "installedAt": "%s",\n "node": "%s",\n "paths": [\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$NODE_KIND"
143+
printf '%s\n' "$PATHS" | sed 's/.*/ "&",/' | sed '$ s/,$//'
144+
printf ' ]\n}\n'
145+
} > "$SHARE/manifest.json"
146+
{
147+
echo '#!/bin/sh'
148+
echo "# Removes $TOOL. Written by its installer."
149+
echo 'set -eu'
150+
printf '%s\n' "$PATHS" | sed 's|.*|rm -rf "&"|'
151+
echo "echo '$TOOL removed.'"
152+
} > "$SHARE/uninstall.sh"
153+
chmod 0755 "$SHARE/uninstall.sh"
154+
155+
say ""
156+
say "Installed $PKG $INSTALLED:$NAMES (in $BIN)"
157+
case ":$PATH:" in
158+
*":$BIN:"*) ;;
159+
*) say "Add $BIN to your PATH: echo 'export PATH=\"$BIN:\$PATH\"' >> ~/.profile" ;;
160+
esac
161+
say "Update by running the installer again; remove with: sh $SHARE/uninstall.sh"
162+
}
163+
164+
main "$@"

0 commit comments

Comments
 (0)