fix: daemon download URL and SPA routing on Render - #69
Conversation
- Resolve 404 on daemon download by querying GitHub API for latest tag and constructing the correct versioned asset URL - Add _redirects file for Render static site hosting so client-side routes (e.g. /docs/) fall back to index.html Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR ReviewTwo targeted fixes — both address real problems. Here's my feedback:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes two issues: (1) corrects the daemon download URL to query GitHub API for the actual release tag and construct the proper versioned URL, and (2) adds SPA routing support for Render static hosting via a _redirects file.
Changes:
- Fixed daemon download URL by querying GitHub API for latest release tag instead of using non-existent
/releases/latest/downloadpath - Added
_redirectsfile to support client-side routing in Render deployments
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| web/public/_redirects | Adds Render SPA routing rule to fallback to index.html for all routes |
| fluid-cli/internal/setup/steps.go | Updates stepDownloadDaemon to fetch latest tag from GitHub API and construct correct versioned download URL |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| `TAG=$(curl -fsSL https://api.github.com/repos/aspectrr/fluid.sh/releases/latest | grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4)`, | ||
| fmt.Sprintf("curl -fsSL -o /tmp/fluid-daemon.tar.gz https://github.com/aspectrr/fluid.sh/releases/download/${TAG}/fluid-daemon_${TAG#v}_linux_%s.tar.gz", arch), |
There was a problem hiding this comment.
The Commands field is used for display purposes in the UI (see fluid-cli/internal/tui/onboarding.go:1605), but these commands won't work correctly if run separately since line 79 depends on the TAG variable set in line 78. Each command in the array is displayed independently. Consider combining them into a single command string with && to show the correct sequence, similar to the Execute function implementation on line 88.
| `TAG=$(curl -fsSL https://api.github.com/repos/aspectrr/fluid.sh/releases/latest | grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4)`, | |
| fmt.Sprintf("curl -fsSL -o /tmp/fluid-daemon.tar.gz https://github.com/aspectrr/fluid.sh/releases/download/${TAG}/fluid-daemon_${TAG#v}_linux_%s.tar.gz", arch), | |
| fmt.Sprintf( | |
| `TAG=$(curl -fsSL https://api.github.com/repos/aspectrr/fluid.sh/releases/latest | grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4) && VERSION=${TAG#v} && curl -fsSL -o /tmp/fluid-daemon.tar.gz "https://github.com/aspectrr/fluid.sh/releases/download/${TAG}/fluid-daemon_${VERSION}_linux_%s.tar.gz"`, | |
| arch, | |
| ), |
| arch := runtime.GOARCH | ||
| cmd := fmt.Sprintf( | ||
| "curl -fsSL -o /tmp/fluid-daemon.tar.gz https://github.com/aspectrr/fluid.sh/releases/latest/download/fluid-daemon_linux_%s.tar.gz", | ||
| `TAG=$(curl -fsSL https://api.github.com/repos/aspectrr/fluid.sh/releases/latest | grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4) && VERSION=${TAG#v} && curl -fsSL -o /tmp/fluid-daemon.tar.gz "https://github.com/aspectrr/fluid.sh/releases/download/${TAG}/fluid-daemon_${VERSION}_linux_%s.tar.gz"`, |
There was a problem hiding this comment.
The shell command doesn't validate whether the GitHub API call succeeded or returned valid data. If the API is rate-limited, down, or returns an error, TAG will be empty and the subsequent download URL will be malformed. Consider adding error checking such as checking if TAG is non-empty before proceeding, or using curl's --fail flag combined with checking the exit code before the download step.
| arch := runtime.GOARCH | ||
| cmd := fmt.Sprintf( | ||
| "curl -fsSL -o /tmp/fluid-daemon.tar.gz https://github.com/aspectrr/fluid.sh/releases/latest/download/fluid-daemon_linux_%s.tar.gz", | ||
| `TAG=$(curl -fsSL https://api.github.com/repos/aspectrr/fluid.sh/releases/latest | grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4) && VERSION=${TAG#v} && curl -fsSL -o /tmp/fluid-daemon.tar.gz "https://github.com/aspectrr/fluid.sh/releases/download/${TAG}/fluid-daemon_${VERSION}_linux_%s.tar.gz"`, |
There was a problem hiding this comment.
The GitHub API can be rate-limited (60 requests per hour for unauthenticated requests). Consider handling this scenario gracefully or documenting the limitation. Users running setup multiple times in quick succession might encounter rate limiting errors. Alternative approaches could include: 1) caching the latest tag locally, 2) providing a fallback mechanism, or 3) detecting rate limit errors and providing a helpful error message to users.
- Share single downloadCmd between Commands (display) and Execute - Add [ -n "$TAG" ] guard to fail early if GitHub API returns no tag - Remove redundant arch redeclaration in Execute closure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Review
|
_redirects |
✅ Correct |
| grep pattern space bug | ❌ TAG will always be empty; all installs will fail |
| Silent failure on empty TAG | |
| Commands display readability |
The grep pattern bug is the main concern — it will break every install attempt with the new code path.
Summary
stepDownloadDaemon()was using/releases/latest/download/fluid-daemon_linux_{arch}.tar.gzwhich 404s because actual release assets are namedfluid-daemon_{version}_linux_{arch}.tar.gz. Now queries GitHub API for the latest tag first, then constructs the correct versioned URL. Uses grep+cut instead of jq to avoid extra dependencies on the remote host._redirectsfile for Render static site hosting so client-side routes (e.g./docs/) fall back toindex.htmlinstead of returning "Not Found".Test plan
cd fluid-cli && make test- all tests pass/docs/route works on Render after deploy🤖 Generated with Claude Code