Skip to content

feat(build): fetch Railpack versions dynamically from GitHub releases#4070

Open
s24407-pj wants to merge 8 commits into
Dokploy:canaryfrom
s24407-pj:feat/dynamic-railpack-versions
Open

feat(build): fetch Railpack versions dynamically from GitHub releases#4070
s24407-pj wants to merge 8 commits into
Dokploy:canaryfrom
s24407-pj:feat/dynamic-railpack-versions

Conversation

@s24407-pj

@s24407-pj s24407-pj commented Mar 25, 2026

Copy link
Copy Markdown

Summary

  • Removes the hardcoded RAILPACK_VERSIONS array and replaces it with a tRPC query (getRailpackVersions) that fetches releases directly from the GitHub API
  • Versions are only fetched when the Railpack build type is selected, and cached for 24 hours to avoid hitting GitHub rate limits
  • The "Latest" badge is now dynamic — always reflects the actual latest release
  • Strips v prefix from manually entered versions before saving to prevent build failures
  • Fixed a bug where switching to Railpack for the first time would silently save the hardcoded "0.15.4" instead of the actual latest version

🤖 Generated with Claude Code

Greptile Summary

This PR replaces the hardcoded RAILPACK_VERSIONS array with a live tRPC query that fetches releases from the GitHub API, adds client-side 24-hour caching, strips the v prefix on save, and handles the error and loading states in the UI. The core approach is sound, though the server-side rate-limit concern raised in prior review threads remains unaddressed.

Confidence Score: 3/5

Needs attention before merging — an unresolved P1 (no server-side caching means all users share the 60 req/hr GitHub rate limit) and missing per_page cap can make the feature unreliable on busy instances.

Previous-thread P1 issues (server-side rate-limit exhaustion, GitHub pagination capped at 30 results) are still open in this revision. With multiple unresolved P1s the score is pulled below the 4/5 ceiling to 3/5.

apps/dokploy/server/api/routers/application.ts — getRailpackVersions endpoint needs server-side caching and per_page=100 on the GitHub request.

Reviews (2): Last reviewed commit: "[autofix.ci] apply automated fixes" | Re-trigger Greptile

s24407-pj and others added 3 commits March 25, 2026 16:59
Replace the hardcoded RAILPACK_VERSIONS array with a tRPC query that
fetches releases from the GitHub API. Versions are cached for 24 hours
and only fetched when the Railpack build type is selected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ection

- Change Zod schema default from hardcoded "0.15.4" to null so Zod does
  not silently override the form field before the fetched versions arrive
- Set form value to latest fetched version when no version is selected
- Strip v prefix from manually entered versions before saving
- Fix form reset loop by splitting useEffect into separate concerns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@s24407-pj s24407-pj requested a review from Siumauricio as a code owner March 25, 2026 16:45
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. enhancement New feature or request labels Mar 25, 2026
@dosubot

dosubot Bot commented Mar 25, 2026

Copy link
Copy Markdown

Related Documentation

1 document(s) may need updating based on files changed in this PR:

Dokploy's Space

README /dokploy/blob/canary/apps/api/README.md
View Suggested Changes
@@ -113,6 +113,41 @@
   -F "zip=@/path/to/your/app.zip" \
   -F "dropBuildPath=optional/build/path"
 ```
+
+## Application Endpoints
+
+### application.getRailpackVersions
+
+Fetches available Railpack versions dynamically from the GitHub API.
+
+**Type:** Protected procedure query (requires authentication)
+
+**Parameters:** None
+
+**Response:**
+Returns an array of version strings (tag names from GitHub releases with the 'v' prefix stripped):
+```json
+[
+  "0.15.4",
+  "0.15.3",
+  "0.15.2"
+]
+```
+
+**Error Responses:**
+- `500` - INTERNAL_SERVER_ERROR: "Failed to fetch Railpack versions from GitHub" if the GitHub API request fails
+
+**Caching:**
+When calling from the client, use a `staleTime` of 24 hours (1000 * 60 * 60 * 24) to avoid hitting GitHub API rate limits.
+
+**Example:**
+```typescript
+const { data: railpackVersions } = api.application.getRailpackVersions.useQuery(undefined, {
+  staleTime: 1000 * 60 * 60 * 24, // 24 hours
+});
+```
+
+**Data Source:** https://api.github.com/repos/railwayapp/railpack/releases
 
 ## Search Endpoints
 

[Accept] [Decline]

Note: You must be authenticated to accept/decline updates.

How did I do? Any feedback?  Join Discord

Comment on lines +1083 to +1096
getRailpackVersions: protectedProcedure.query(async () => {
const res = await fetch(
"https://api.github.com/repos/railwayapp/railpack/releases",
{ headers: { Accept: "application/vnd.github+json" } },
);
if (!res.ok) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Failed to fetch Railpack versions from GitHub",
});
}
const releases = (await res.json()) as Array<{ tag_name: string }>;
return releases.map((r) => r.tag_name.replace(/^v/, ""));
}),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 No server-side caching — GitHub rate limits will be hit quickly

The endpoint makes a fresh GitHub API call on every invocation. GitHub's unauthenticated rate limit is 60 requests/hour per IP address. Because the Dokploy server makes these calls (all users share one IP), a busy instance with multiple users opening the Railpack build tab can exhaust this limit very quickly, causing every subsequent call to fail with a 403/429.

The client-side staleTime: 1000 * 60 * 60 * 24 only helps within a single user's session — it won't help when different users open the page, or when the server restarts and all client caches are cold.

A simple fix is to add a server-side in-memory cache (e.g. a module-level variable with a timestamp) or use a Cache-Control response header from GitHub to drive conditional requests. At minimum, a GITHUB_TOKEN environment variable should be optionally read and forwarded so authenticated rate limits (5 000 req/hour) can be used:

const res = await fetch(
    "https://api.github.com/repos/railwayapp/railpack/releases",
    {
        headers: {
            Accept: "application/vnd.github+json",
            ...(process.env.GITHUB_TOKEN
                ? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }
                : {}),
        },
    },
);

Comment on lines 215 to 218
railpackVersion:
data.buildType === BuildType.railpack
? data.railpackVersion || "0.15.4"
? (data.railpackVersion || railpackVersions?.[0] || "").replace(/^v/, "")
: null,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Empty string saved as version if API is still loading or failed

If the user switches to Railpack and submits before getRailpackVersions resolves (or if the GitHub API request fails entirely), data.railpackVersion is null (the schema default) and railpackVersions?.[0] is undefined. The expression evaluates to "".replace(/^v/, "")"", which silently saves an empty string as the build version, causing the next build to fail without a clear error.

The Save button should be disabled while versions are loading, or a validation should reject an empty version:

Suggested change
railpackVersion:
data.buildType === BuildType.railpack
? data.railpackVersion || "0.15.4"
? (data.railpackVersion || railpackVersions?.[0] || "").replace(/^v/, "")
: null,
? (data.railpackVersion || railpackVersions?.[0] || null)?.replace(/^v/, "") ?? null

Pairing this with disabling the Save button while isLoadingRailpackVersions is true would fully close the gap.

Comment on lines +158 to +162
const { data: railpackVersions, isLoading: isLoadingRailpackVersions } =
api.application.getRailpackVersions.useQuery(undefined, {
enabled: buildType === BuildType.railpack,
staleTime: 1000 * 60 * 60 * 24, // 24 hours
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 No error state handled for getRailpackVersions

isError is not destructured from the query. When the GitHub API call fails (rate-limited, network error, etc.), isLoadingRailpackVersions flips back to false but railpackVersions remains undefined. The SelectTrigger becomes enabled again, the dropdown renders no options, and there is no error message anywhere — the user sees a blank selector with no explanation.

At minimum, destructure isError and show an error message:

const {
    data: railpackVersions,
    isLoading: isLoadingRailpackVersions,
    isError: isErrorRailpackVersions,
} = api.application.getRailpackVersions.useQuery(undefined, {
    enabled: buildType === BuildType.railpack,
    staleTime: 1000 * 60 * 60 * 24,
});

Then in the SelectTrigger (or just below the Select), show feedback when isErrorRailpackVersions is true so users understand they can still enter a version manually.

Comment on lines +1084 to +1086
const res = await fetch(
"https://api.github.com/repos/railwayapp/railpack/releases",
{ headers: { Accept: "application/vnd.github+json" } },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 GitHub API pagination — only first 30 releases returned

The /releases endpoint defaults to per_page=30. Railpack already has more than 20 releases listed in the old hardcoded array, so future releases beyond 30 would be silently omitted. Adding ?per_page=100 would cover the foreseeable future without requiring pagination logic.

Suggested change
const res = await fetch(
"https://api.github.com/repos/railwayapp/railpack/releases",
{ headers: { Accept: "application/vnd.github+json" } },
"https://api.github.com/repos/railwayapp/railpack/releases?per_page=100",

@pompydev

pompydev commented Apr 8, 2026

Copy link
Copy Markdown

#4022 won't be necessary once this PR gets merged

s24407-pj and others added 3 commits April 15, 2026 17:45
Add validation to ensure that a railpack version is provided when the build type is set to railpack. Update the submission logic to normalize the railpack version and disable the submit button if the version is missing. This enhances user experience by preventing submission with invalid data.
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants