Add a user-friendly fda install UX via release binaries#5658
Add a user-friendly fda install UX via release binaries#5658Karakatiza666 merged 2 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a user-friendly installation experience for the fda CLI tool by introducing a curl-to-bash installer script, similar to installation patterns used by popular tools like Rust and Node.js. The installer downloads pre-built binaries for Linux platforms, making it significantly faster and more accessible than the previous Cargo-based installation method.
Changes:
- New install script (
install-fda.sh) that detects platform, downloads binaries, and configures PATH automatically - CI workflow updated to package standalone
fdabinaries separately from the full binary bundle for faster downloads - Documentation reorganized to promote the quick install method while maintaining Cargo installation instructions for other platforms
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 12 comments.
| File | Description |
|---|---|
| scripts/install-fda.sh | New installer script that downloads fda binary, installs to ~/.feldera/bin, and adds to PATH |
| docs.feldera.com/docs/interface/cli.md | Reorganized to show quick Linux install first, moved Cargo installation to separate section |
| .github/workflows/ci-release.yml | Extracts fda binaries from full bundle and packages them separately as fda-*.zip artifacts |
.github/workflows/ci-release.yml
Outdated
| - name: Extract standalone fda binaries | ||
| run: | | ||
| for arch in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do | ||
| unzip -jo "feldera-binaries-${arch}.zip" fda -d . |
There was a problem hiding this comment.
The unzip command extracts 'fda' from the archive, but there's no error handling if the 'fda' binary is not found in the zip file. This could occur if the upstream feldera-binaries-*.zip file structure changes. Consider adding error checking after the unzip command to ensure the binary was successfully extracted before attempting to create the new zip file.
| unzip -jo "feldera-binaries-${arch}.zip" fda -d . | |
| if ! unzip -jo "feldera-binaries-${arch}.zip" fda -d .; then | |
| echo "Error: failed to extract 'fda' from feldera-binaries-${arch}.zip" | |
| exit 1 | |
| fi | |
| if [ ! -f fda ]; then | |
| echo "Error: 'fda' binary not found after extracting from feldera-binaries-${arch}.zip" | |
| exit 1 | |
| fi |
| download_and_install() { | ||
| TMPDIR=$(mktemp -d) | ||
| # shellcheck disable=SC2064 | ||
| trap "rm -rf '$TMPDIR'" EXIT | ||
|
|
||
| TMPFILE="$TMPDIR/fda.zip" | ||
|
|
||
| info "Downloading from $DOWNLOAD_URL" | ||
| HTTP_CODE=$(curl -fSL --progress-bar -o "$TMPFILE" -w "%{http_code}" "$DOWNLOAD_URL") || { | ||
| case "$HTTP_CODE" in | ||
| 404) | ||
| if [ -n "${FDA_VERSION:-}" ]; then | ||
| err "version ${FDA_VERSION} not found. Check available versions at https://github.com/$GITHUB_REPO/releases" | ||
| else | ||
| err "release asset not found. The latest release may not include standalone fda binaries yet." | ||
| fi | ||
| ;; | ||
| *) | ||
| err "download failed (HTTP $HTTP_CODE)" | ||
| ;; | ||
| esac | ||
| } | ||
|
|
||
| info "Extracting fda binary" | ||
| unzip -jo "$TMPFILE" fda -d "$TMPDIR" | ||
|
|
||
| TMPBIN="$TMPDIR/fda" | ||
| chmod +x "$TMPBIN" | ||
|
|
||
| mkdir -p "$BIN_DIR" | ||
| mv "$TMPBIN" "$BIN_DIR/fda" | ||
|
|
||
| info "Installed fda to $BIN_DIR/fda" | ||
| } |
There was a problem hiding this comment.
The script downloads and executes a binary from GitHub without verifying its integrity using checksums or signatures. While HTTPS provides transport security, consider adding checksum verification or GPG signature validation to ensure the binary hasn't been tampered with. This is especially important for a script designed to be run via curl piped to bash.
scripts/install-fda.sh
Outdated
| fish) | ||
| FISH_CONFIG="$HOME/.config/fish/config.fish" | ||
| if [ -d "$HOME/.config/fish" ] || mkdir -p "$HOME/.config/fish"; then | ||
| printf '\n# Feldera fda CLI\nset -gx FELDERA_INSTALL %s\nset -gx PATH $FELDERA_INSTALL/bin $PATH\n' "$FELDERA_INSTALL" >> "$FISH_CONFIG" |
There was a problem hiding this comment.
The PATH modification for fish shell uses set -gx PATH $FELDERA_INSTALL/bin $PATH which prepends the new path. However, this is inconsistent with bash/zsh which use export PATH="$FELDERA_INSTALL/bin:$PATH". The fish syntax is correct, but for consistency and to follow fish best practices, consider using fish_add_path command if available (fish 3.2.0+), or use the explicit form: set -gx PATH "$FELDERA_INSTALL/bin" $PATH with quotes for consistency.
| setup_path() { | ||
| # Check if bin dir is already in PATH | ||
| case ":${PATH}:" in | ||
| *":${BIN_DIR}:"*) | ||
| return | ||
| ;; | ||
| esac | ||
|
|
||
| SHELL_NAME="$(basename "${SHELL:-/bin/sh}")" | ||
| RC_LINES="export FELDERA_INSTALL=\"$FELDERA_INSTALL\"\nexport PATH=\"\$FELDERA_INSTALL/bin:\$PATH\"" | ||
| UPDATED_RC="" | ||
|
|
||
| case "$SHELL_NAME" in | ||
| bash) | ||
| if [ -f "$HOME/.bashrc" ]; then | ||
| printf '\n# Feldera fda CLI\n%b\n' "$RC_LINES" >> "$HOME/.bashrc" | ||
| UPDATED_RC="~/.bashrc" | ||
| elif [ -f "$HOME/.bash_profile" ]; then | ||
| printf '\n# Feldera fda CLI\n%b\n' "$RC_LINES" >> "$HOME/.bash_profile" | ||
| UPDATED_RC="~/.bash_profile" | ||
| fi | ||
| ;; | ||
| zsh) | ||
| if [ -f "$HOME/.zshrc" ] || [ -d "$HOME" ]; then | ||
| printf '\n# Feldera fda CLI\n%b\n' "$RC_LINES" >> "$HOME/.zshrc" | ||
| UPDATED_RC="~/.zshrc" | ||
| fi | ||
| ;; | ||
| fish) | ||
| FISH_CONFIG="$HOME/.config/fish/config.fish" | ||
| if [ -d "$HOME/.config/fish" ] || mkdir -p "$HOME/.config/fish"; then | ||
| printf '\n# Feldera fda CLI\nset -gx FELDERA_INSTALL %s\nset -gx PATH $FELDERA_INSTALL/bin $PATH\n' "$FELDERA_INSTALL" >> "$FISH_CONFIG" | ||
| UPDATED_RC="~/.config/fish/config.fish" | ||
| fi | ||
| ;; | ||
| esac |
There was a problem hiding this comment.
The script modifies shell configuration files (.bashrc, .zshrc, etc.) without checking if the PATH configuration already exists. This could result in duplicate entries being added if the script is run multiple times. Consider checking if the configuration lines already exist in the file before appending them, similar to how the script checks if BIN_DIR is already in PATH at lines 141-145.
scripts/install-fda.sh
Outdated
| API_RESPONSE=$(curl -fsSL "https://api.github.com/repos/$GITHUB_REPO/releases/latest" 2>/dev/null) || true | ||
| if [ -n "$API_RESPONSE" ]; then | ||
| # Extract tag_name from JSON without jq | ||
| VERSION=$(printf '%s' "$API_RESPONSE" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') |
There was a problem hiding this comment.
The script uses printf '%s' "$API_RESPONSE" which is good practice to avoid interpretation of escape sequences, but the sed command that follows uses [[:space:]] character class which may not be POSIX compliant in all sed implementations. Consider using a more portable pattern like [ ] (space and tab) or testing on different platforms to ensure compatibility.
| VERSION=$(printf '%s' "$API_RESPONSE" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') | |
| VERSION=$(printf '%s' "$API_RESPONSE" | sed -n 's/.*"tag_name"[ ]*:[ ]*"\([^"]*\)".*/\1/p') |
| To install to a custom directory: | ||
|
|
||
| ```bash | ||
| curl -fsSL https://feldera.com/install | FDA_VERSION=v0.247.0 FELDERA_INSTALL=/opt/feldera bash |
There was a problem hiding this comment.
In line 34, when both FDA_VERSION and FELDERA_INSTALL environment variables are provided together, the documentation example shows them on the same line. However, this usage pattern should be clarified - the user needs to ensure both variables are passed to bash. Consider adding a note or showing the alternative syntax with separate variable exports to avoid confusion.
| | Supported platforms | | ||
| |---| | ||
| | linux-x86_64 | | ||
| | linux-aarch64 | | ||
|
|
||
|
|
There was a problem hiding this comment.
The table formatting for supported platforms is inconsistent with typical markdown table conventions. A table with only one column and no header row could be formatted as a simple list instead. Consider using a proper table with a header or converting to a bulleted list for better readability.
| | Supported platforms | | |
| |---| | |
| | linux-x86_64 | | |
| | linux-aarch64 | | |
| Supported platforms: | |
| - linux-x86_64 | |
| - linux-aarch64 |
| unzip -jo "$TMPFILE" fda -d "$TMPDIR" | ||
|
|
||
| TMPBIN="$TMPDIR/fda" |
There was a problem hiding this comment.
The unzip command extracts 'fda' from the archive but doesn't verify if the file was successfully extracted. If the zip file is corrupted or doesn't contain a file named 'fda', the script will fail at line 131 when trying to chmod a non-existent file. Consider adding error handling after the unzip command to provide a clearer error message if extraction fails.
| unzip -jo "$TMPFILE" fda -d "$TMPDIR" | |
| TMPBIN="$TMPDIR/fda" | |
| if ! unzip -jo "$TMPFILE" fda -d "$TMPDIR"; then | |
| err "failed to extract fda from the downloaded archive; the download may be corrupt or incompatible" | |
| exit 1 | |
| fi | |
| TMPBIN="$TMPDIR/fda" | |
| if [ ! -f "$TMPBIN" ]; then | |
| err "fda binary not found in the downloaded archive; the download may be corrupt or incompatible" | |
| exit 1 | |
| fi |
| # Shell completion hint | ||
| printf '\n' | ||
| info "To enable shell completions, see:" | ||
| info " https://docs.feldera.com/docs/interface/cli#optional-shell-completion" |
There was a problem hiding this comment.
The URL in line 205 references 'docs/interface/cli#optional-shell-completion' but the actual documentation section header is '### Optional: Shell completion' which would generate the anchor '#optional-shell-completion'. Verify that this URL works correctly and matches the actual documentation structure.
| ```bash | ||
| curl -fsSL https://feldera.com/install | bash | ||
| ``` |
There was a problem hiding this comment.
The curl -fsSL https://feldera.com/install | bash pattern executes a remotely downloaded shell script without any integrity verification, creating a supply-chain risk where compromise of feldera.com (or the GitHub-backed script endpoint) leads to arbitrary code execution on users’ machines. An attacker who can tamper with the served installer (via DNS/BGP hijack, web server compromise, or misconfiguration) can run arbitrary commands under the user’s account as soon as they copy-paste this one-liner. Consider changing the install flow to download the script or binary first and verify it (e.g., via a pinned version, checksum, or signature) before execution, and avoid recommending curl | bash in documentation.
| feldera-binaries-v${{ env.CURRENT_VERSION }}-aarch64-unknown-linux-gnu.zip | ||
| feldera-binaries-v${{ env.CURRENT_VERSION }}-x86_64-unknown-linux-gnu.zip | ||
| fda-x86_64-unknown-linux-gnu.zip | ||
| fda-aarch64-unknown-linux-gnu.zip |
There was a problem hiding this comment.
it's a good change, can you also drop v${{ env.CURRENT_VERSION }} from all the other artifacts (it's not necessary because in github the version is already in the URL to the artifact
.github/workflows/ci-release.yml
Outdated
There was a problem hiding this comment.
(comment below can be fixed by not adding the version here)
There was a problem hiding this comment.
@gz related: since we need to package fda separately, is there still a case for feldera-binaries-* that contains everything? We may release other artifacts in the observable future? (e.g. profile visualizer .html app)
There was a problem hiding this comment.
makes sense you can name the other one pipeline-mnager-v.... since it's the only binary left once you remove fda
| To install a specific version, pass the release git tag to the install script: | ||
|
|
||
| ```bash | ||
| curl -fsSL https://feldera.com/install | FDA_VERSION=v0.247.0 bash |
There was a problem hiding this comment.
how do you get it deployed to feldera.com?
it would probably be easier to make it docs.feldera.com/install-fda because if you just put the script in the docs folder here it will be copied/updated from feldera/feldera to docs as part of the existing CI job
There was a problem hiding this comment.
I planned to separately add a page feldera.com/install that redirects the user to raw.githubusercontent.com . It is superficially nicer (and easier to remember - basically a convention) to have that URL rather than curl -fsSL docs.feldera.com/install-fda | bash, but I like the idea of hosting the script via the docs website rather than via raw.githubusercontent
|
|
||
| | Supported platforms | | ||
| |---| | ||
| | linux-x86_64 | |
There was a problem hiding this comment.
it also requires a minimum GLIBC version (you should be able to find out doing readelf --version-info ./fda | grep GLIBC_
| ``` | ||
|
|
||
| ### Binary installation | ||
| ### From release binaries |
scripts/install-fda.sh
Outdated
|
|
||
| set -eu | ||
|
|
||
| GITHUB_REPO="feldera/feldera" |
There was a problem hiding this comment.
hardcode this so it can't easily be swapped out by something malicious
scripts/install-fda.sh
Outdated
| DOWNLOAD_URL="https://github.com/$GITHUB_REPO/releases/latest/download/fda-${TARGET}.zip" | ||
| info "Installing fda (latest)" |
There was a problem hiding this comment.
I dont think you need this as the fallback, using latest already works and is supported you can just use latest in case no version is specified
| info "Installed fda to $BIN_DIR/fda" | ||
| } | ||
|
|
||
| setup_path() { |
There was a problem hiding this comment.
it would probably be better to put it in an existing path like /usr/local/bin than having make our own path writable. how do other install scripts solve this? maybe @blp has some advice
There was a problem hiding this comment.
Actually, this seems to be a convention in a lot of modern tools - e.g. Bun, Claude, Flyctl etc. As a bonus, it keeps the fda (~/.feldera/bin) together with the default storage dir for pipeline-manager (~/.feldera/data)
There was a problem hiding this comment.
Adding our own path is what a lot of tools do these days.
Personally I'm surprised that, for a single binary, we don't just recommend that people download that into a binary directory.
|
This adds a big shell script. Is it based on one from somewhere else? I ask for two reasons:
|
|
You could say that. I generated it using Claude Code. I could come up with an automated test for the script that tries to install |
|
This script has one comment for shellcheck suppressions, but not for the other shellcheck warnings (which I think also are false positives): |
| # Check if bin dir is already in PATH | ||
| case ":${PATH}:" in | ||
| *":${BIN_DIR}:"*) | ||
| return | ||
| ;; | ||
| esac |
There was a problem hiding this comment.
I think that this will only find the directory if it's in the middle of the path, but we add it at the beginning of the path. I think that we need to add a case to the expression to cover that, something like:
"$(BIN_DIR)":* | *:"$(BIN_DIR)" | *:"$(BIN_DIR)":*)
There was a problem hiding this comment.
There's another case like this in print_success.
There was a problem hiding this comment.
This implementation actually wraps the PATH in semicolons, so these edge cases are matched automatically.
mythical-fred
left a comment
There was a problem hiding this comment.
The structure of the script is solid — set -eu, cleanup via trap, platform/arch detection, clear error messages, good platform error messaging. A few things before merge.
Zsh setup has a logic bug
if [ -f "$HOME/.zshrc" ] || [ -d "$HOME" ]; then[ -d "$HOME" ] is always true, so this branch always runs and always appends to ~/.zshrc (creating it if it doesn't exist) — even on a bash-only machine. The bash branch only writes to existing files. Fix:
if [ -f "$HOME/.zshrc" ]; thenDuplicate RC entries on reinstall
setup_path guards against adding to PATH if $BIN_DIR is already in the current shell's $PATH. But if the user runs the installer twice in the same shell session before sourcing their rc file, $BIN_DIR isn't in $PATH yet, so the rc file gets the export appended twice. A simple guard: check if the rc file already contains FELDERA_INSTALL before appending.
Glibc minimum version
Gerd raised this in his review — the binary requires a minimum glibc version and it's not documented. Worth a line in the docs table next to the platform list, e.g. linux-x86_64 (glibc ≥ X.YY). readelf --version-info ./fda | grep GLIBC_ will give you the floor.
Open items from Gerd's Feb 18 review that don't appear in the commit
- Drop
v${{ env.CURRENT_VERSION }}from the artifact filenames in the release workflow (version is already in the GitHub release URL) - Rename
feldera-binaries-*→pipeline-manager-*now thatfdais a separate artifact (Gerd's suggestion, you agreed) - Simplify
resolve_versionto usereleases/latest/download/directly when no version is specified — skip the GitHub API call entirely (Gerd: "you can just use latest in case no version is specified")
e2612ad to
f805128
Compare
Signed-off-by: Karakatiza666 <bulakh.96@gmail.com>
f805128 to
689fbd4
Compare
|
@mythical-fred I pushed the commit addressing the feedback. Any more comments? |
mythical-fred
left a comment
There was a problem hiding this comment.
All my blockers are resolved — LGTM.\n\n- Zsh logic bug fixed ✓\n- Duplicate RC entries guarded with grep -q FELDERA_INSTALL for all three shells ✓\n- glibc ≥ 2.39 documented in the CLI page ✓\n- Version suffix dropped from artifact filenames (except the JAR, noted in the commit message) ✓\n- feldera-binaries-* → pipeline-manager-* ✓\n- resolve_version simplified to releases/latest/download/ ✓
|
@Karakatiza666 Checked the new commit — all good. Approved. |
689fbd4 to
712844c
Compare
Break up feldera-binaries-* artifact Other review fixes Signed-off-by: Karakatiza666 <bulakh.96@gmail.com>
712844c to
64aeee3
Compare
|
As a last change moved this script to be served as docs.feldera.com/install-fda. The install URL will still be feldera.com/install, but hosting the script directly off of our domain is nicer. |
The script sets FELDERA_INSTALL to ~/.feldera by default, installs
fdainto $FELDERA_INSTALL/bin and adds it to PATH.The binary gets packaged in a separate .zip for a faster download.
To use this change I will need to add a page to feldera.com that redirects to the raw.githubusercontent URL of the new install script. Overwriting with a different
fdaversion works, tooTesting:
Tested by modifying the script's download URL so that it downloads the feldera-binaries-*.zip from the last release, and running the script.