Skip to content

Add a user-friendly fda install UX via release binaries#5658

Merged
Karakatiza666 merged 2 commits intomainfrom
install-fda
Feb 23, 2026
Merged

Add a user-friendly fda install UX via release binaries#5658
Karakatiza666 merged 2 commits intomainfrom
install-fda

Conversation

@Karakatiza666
Copy link
Contributor

@Karakatiza666 Karakatiza666 commented Feb 18, 2026

The script sets FELDERA_INSTALL to ~/.feldera by default, installs fda into $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 fda version works, too

Testing:
Tested by modifying the script's download URL so that it downloads the feldera-binaries-*.zip from the last release, and running the script.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 fda binaries 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

- 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 .
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +137
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"
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
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"
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +139 to +174
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
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
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')
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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')

Copilot uses AI. Check for mistakes.
To install to a custom directory:

```bash
curl -fsSL https://feldera.com/install | FDA_VERSION=v0.247.0 FELDERA_INSTALL=/opt/feldera bash
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +20
| Supported platforms |
|---|
| linux-x86_64 |
| linux-aarch64 |


Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
| Supported platforms |
|---|
| linux-x86_64 |
| linux-aarch64 |
Supported platforms:
- linux-x86_64
- linux-aarch64

Copilot uses AI. Check for mistakes.
Comment on lines +128 to +130
unzip -jo "$TMPFILE" fda -d "$TMPDIR"

TMPBIN="$TMPDIR/fda"
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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

Copilot uses AI. Check for mistakes.
# Shell completion hint
printf '\n'
info "To enable shell completions, see:"
info " https://docs.feldera.com/docs/interface/cli#optional-shell-completion"
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +13
```bash
curl -fsSL https://feldera.com/install | bash
```
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@gz gz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blp should have a look at the shell scripting

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(comment below can be fixed by not adding the version here)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets call it install-fda

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

@Karakatiza666 Karakatiza666 Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 |
Copy link
Contributor

@gz gz Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can drop this now


set -eu

GITHUB_REPO="feldera/feldera"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hardcode this so it can't easily be swapped out by something malicious

Comment on lines +98 to +99
DOWNLOAD_URL="https://github.com/$GITHUB_REPO/releases/latest/download/fda-${TARGET}.zip"
info "Installing fda (latest)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

@Karakatiza666 Karakatiza666 Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@gz gz requested a review from blp February 18, 2026 23:27
@blp
Copy link
Member

blp commented Feb 19, 2026

This adds a big shell script. Is it based on one from somewhere else?

I ask for two reasons:

  • If it's based on some other one that has been used by some substantial number of people, then that adds some confidence.
  • If it's based on some other one, then we should credit that one.

@Karakatiza666
Copy link
Contributor Author

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 fda in a matrix of docker containers with different Linux distributions and different shell programs, but I'm not sure this makes sense.

@blp
Copy link
Member

blp commented Feb 20, 2026

This script has one comment for shellcheck suppressions, but not for the other shellcheck warnings (which I think also are false positives):

[blp@sigxcpu dev]$ shellcheck scripts/install-fda.sh 

In scripts/install-fda.sh line 155:
                UPDATED_RC="~/.bashrc"
                            ^-------^ SC2088 (warning): Tilde does not expand in quotes. Use $HOME.


In scripts/install-fda.sh line 158:
                UPDATED_RC="~/.bash_profile"
                            ^-------------^ SC2088 (warning): Tilde does not expand in quotes. Use $HOME.


In scripts/install-fda.sh line 164:
                UPDATED_RC="~/.zshrc"
                            ^------^ SC2088 (warning): Tilde does not expand in quotes. Use $HOME.


In scripts/install-fda.sh line 170:
                printf '\n# Feldera fda CLI\nset -gx FELDERA_INSTALL %s\nset -gx PATH $FELDERA_INSTALL/bin $PATH\n' "$FELDERA_INSTALL" >> "$FISH_CONFIG"
                       ^-- SC2016 (info): Expressions don't expand in single quotes, use double quotes for that.


In scripts/install-fda.sh line 171:
                UPDATED_RC="~/.config/fish/config.fish"
                            ^------------------------^ SC2088 (warning): Tilde does not expand in quotes. Use $HOME.

For more information:
  https://www.shellcheck.net/wiki/SC2088 -- Tilde does not expand in quotes. ...
  https://www.shellcheck.net/wiki/SC2016 -- Expressions don't expand in singl...

Comment on lines +140 to +145
# Check if bin dir is already in PATH
case ":${PATH}:" in
*":${BIN_DIR}:"*)
return
;;
esac
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)":*)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's another case like this in print_success.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation actually wraps the PATH in semicolons, so these edge cases are matched automatically.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you're right, I missed that.

Copy link
Member

@blp blp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have objections to this at this point (thanks, George) but I'll leave it to @gz to approve.

Copy link
Collaborator

@mythical-fred mythical-fred left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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" ]; then

Duplicate 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 that fda is a separate artifact (Gerd's suggestion, you agreed)
  • Simplify resolve_version to use releases/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")

Signed-off-by: Karakatiza666 <bulakh.96@gmail.com>
@Karakatiza666
Copy link
Contributor Author

@mythical-fred I pushed the commit addressing the feedback. Any more comments?

@Karakatiza666 Karakatiza666 added this pull request to the merge queue Feb 23, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 23, 2026
Copy link
Collaborator

@mythical-fred mythical-fred left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/

@mythical-fred
Copy link
Collaborator

@Karakatiza666 Checked the new commit — all good. Approved.

Break up feldera-binaries-* artifact

Other review fixes

Signed-off-by: Karakatiza666 <bulakh.96@gmail.com>
@Karakatiza666
Copy link
Contributor Author

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.

@Karakatiza666 Karakatiza666 added this pull request to the merge queue Feb 23, 2026
Merged via the queue into main with commit d9ff3a6 Feb 23, 2026
1 check passed
@Karakatiza666 Karakatiza666 deleted the install-fda branch February 23, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants