-
Notifications
You must be signed in to change notification settings - Fork 43
Fixes #295: Updated the Run IDs dashboard to show the Latest Run IDs #325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,11 +1,13 @@ | ||||||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import React, { useState, useEffect, useMemo } from 'react'; | ||||||||||||||||||||||||||||||
| import { formatDistanceToNow } from 'date-fns'; | ||||||||||||||||||||||||||||||
| import { apiService } from '@/services/api'; | ||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||
| CurrentStatesResponse, | ||||||||||||||||||||||||||||||
| StatesByRunIdResponse, | ||||||||||||||||||||||||||||||
| StateListItem | ||||||||||||||||||||||||||||||
| StateListItem, | ||||||||||||||||||||||||||||||
| RunSummary | ||||||||||||||||||||||||||||||
| } from '@/types/state-manager'; | ||||||||||||||||||||||||||||||
| import { GraphVisualization } from './GraphVisualization'; | ||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||
|
|
@@ -25,7 +27,7 @@ interface StatesByRunIdProps { | |||||||||||||||||||||||||||||
| namespace: string; | ||||||||||||||||||||||||||||||
| apiKey: string; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // my new function | ||||||||||||||||||||||||||||||
| export const StatesByRunId: React.FC<StatesByRunIdProps> = ({ | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Remove personal/commentary noise. Drop “my new function” from production code. -// my new function 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| namespace, | ||||||||||||||||||||||||||||||
| apiKey | ||||||||||||||||||||||||||||||
|
|
@@ -43,6 +45,13 @@ export const StatesByRunId: React.FC<StatesByRunIdProps> = ({ | |||||||||||||||||||||||||||||
| return m; | ||||||||||||||||||||||||||||||
| }, [currentStates]); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const sortedRunIds = React.useMemo(() => { | ||||||||||||||||||||||||||||||
| if (!currentStates?.run_ids) return []; | ||||||||||||||||||||||||||||||
| return [...currentStates.run_ids].sort((a, b) => | ||||||||||||||||||||||||||||||
| new Date(b.created_at).getTime() - new Date(a.created_at).getTime() | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| }, [currentStates?.run_ids]); | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The dependency array for
Suggested change
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+54
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Memoization is fine; guard against invalid dates. If - return [...currentStates.run_ids].sort((a, b) =>
- new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
- );
+ return [...currentStates.run_ids].sort((a, b) => {
+ const tb = Date.parse(b.created_at ?? '') || 0;
+ const ta = Date.parse(a.created_at ?? '') || 0;
+ return tb - ta;
+ });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| const loadCurrentStates = async () => { | ||||||||||||||||||||||||||||||
| setIsLoading(true); | ||||||||||||||||||||||||||||||
| setError(null); | ||||||||||||||||||||||||||||||
|
|
@@ -53,7 +62,7 @@ export const StatesByRunId: React.FC<StatesByRunIdProps> = ({ | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Auto-select the first run ID if available | ||||||||||||||||||||||||||||||
| if (data.run_ids.length > 0 && !selectedRunId) { | ||||||||||||||||||||||||||||||
| setSelectedRunId(data.run_ids[0]); | ||||||||||||||||||||||||||||||
| setSelectedRunId(data.run_ids[0].run_id); | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line selects the first run ID from the API response (
Suggested change
|
||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
Comment on lines
64
to
66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Auto-select the latest run, not the first array element. Current code assumes server returns latest first. Sort locally to pick the newest. - // Auto-select the first run ID if available
- if (data.run_ids.length > 0 && !selectedRunId) {
- setSelectedRunId(data.run_ids[0].run_id);
- }
+ // Auto-select the latest run ID if available
+ if (data.run_ids.length > 0 && !selectedRunId) {
+ const latest = [...data.run_ids].sort(
+ (a, b) => Date.parse(b.created_at) - Date.parse(a.created_at)
+ )[0];
+ setSelectedRunId(latest.run_id);
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||
| setError(err instanceof Error ? err.message : 'Failed to load current states'); | ||||||||||||||||||||||||||||||
|
|
@@ -177,6 +186,28 @@ export const StatesByRunId: React.FC<StatesByRunIdProps> = ({ | |||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||
| {/* my react component for latest run id's */} | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Comment tone. “my react component for latest run id's” → concise, neutral comment or remove. Also fix plural/possessive: “IDs”. -{/* my react component for latest run id's */}
+{/* Latest Run IDs */}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| <div className="mt-4"> | ||||||||||||||||||||||||||||||
| <h3 className="text-lg font-semibold mb-2">Latest Run ID</h3> | ||||||||||||||||||||||||||||||
| <div className="max-h-60 overflow-y-auto border rounded-lg p-2"> | ||||||||||||||||||||||||||||||
| {sortedRunIds.length > 0 ? ( | ||||||||||||||||||||||||||||||
| <ul className="divide-y divide-gray-200"> | ||||||||||||||||||||||||||||||
| {sortedRunIds.map((run) => ( | ||||||||||||||||||||||||||||||
| <li key={run.run_id} className="py-2"> | ||||||||||||||||||||||||||||||
| <div className="flex justify-between items-center"> | ||||||||||||||||||||||||||||||
| <span className="font-mono text-sm">{run.run_id}</span> | ||||||||||||||||||||||||||||||
| <span className="text-xs text-gray-500"> | ||||||||||||||||||||||||||||||
| {formatDistanceToNow(new Date(run.created_at), { addSuffix: true })} | ||||||||||||||||||||||||||||||
| </span> | ||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||
| </li> | ||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||
| </ul> | ||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||
| <p className="text-sm text-gray-500">No run IDs found</p> | ||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||
|
Comment on lines
+190
to
+210
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Make “Latest Run ID” list actionable. Let users click a recent item to select it; improves UX. - {sortedRunIds.map((run) => (
- <li key={run.run_id} className="py-2">
+ {sortedRunIds.map((run) => (
+ <li
+ key={run.run_id}
+ className="py-2 cursor-pointer hover:bg-gray-50 rounded"
+ onClick={() => setSelectedRunId(run.run_id)}
+ >
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| {/* Run ID Selector */} | ||||||||||||||||||||||||||||||
| {currentStates && currentStates.run_ids.length > 0 && ( | ||||||||||||||||||||||||||||||
|
|
@@ -189,19 +220,19 @@ export const StatesByRunId: React.FC<StatesByRunIdProps> = ({ | |||||||||||||||||||||||||||||
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | ||||||||||||||||||||||||||||||
| {currentStates.run_ids.map((runId) => ( | ||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||
| key={runId} | ||||||||||||||||||||||||||||||
| onClick={() => setSelectedRunId(runId)} | ||||||||||||||||||||||||||||||
| key={runId.run_id} | ||||||||||||||||||||||||||||||
| onClick={() => setSelectedRunId(runId.run_id)} | ||||||||||||||||||||||||||||||
| className={`p-4 rounded-lg border-2 transition-colors ${ | ||||||||||||||||||||||||||||||
| selectedRunId === runId | ||||||||||||||||||||||||||||||
| selectedRunId === runId.run_id | ||||||||||||||||||||||||||||||
| ? 'border-[#031035] bg-[#031035]/5' | ||||||||||||||||||||||||||||||
| : 'border-gray-200 hover:border-gray-300' | ||||||||||||||||||||||||||||||
| }`} | ||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||
| <div className="text-sm font-medium text-gray-900 truncate"> | ||||||||||||||||||||||||||||||
| {runId} | ||||||||||||||||||||||||||||||
| {runId.run_id} | ||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||
| <div className="text-xs text-gray-500 mt-1"> | ||||||||||||||||||||||||||||||
| {countsByRunId.get(runId) ?? 0} states | ||||||||||||||||||||||||||||||
| {countsByRunId.get(runId.run_id) ?? 0} states | ||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,26 +18,34 @@ import { | |||||||||||||||
| } from '@/types/state-manager'; | ||||||||||||||||
|
|
||||||||||||||||
| const API_BASE_URL = process.env.NEXT_PUBLIC_EXOSPHERE_STATE_MANAGER_URL || 'http://localhost:8000'; | ||||||||||||||||
|
|
||||||||||||||||
| const DEFAULT_API_KEY = process.env.NEXT_PUBLIC_EXOSPHERE_API_KEY || ''; | ||||||||||||||||
| class ApiService { | ||||||||||||||||
|
Comment on lines
20
to
22
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Optional: remove DEFAULT_API_KEY duplication. You use -const DEFAULT_API_KEY = process.env.NEXT_PUBLIC_EXOSPHERE_API_KEY || '';
+const DEFAULT_API_KEY = process.env.NEXT_PUBLIC_EXOSPHERE_API_KEY ?? '';And in 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
| private async makeRequest<T>( | ||||||||||||||||
| endpoint: string, | ||||||||||||||||
| options: RequestInit = {} | ||||||||||||||||
| ): Promise<T> { | ||||||||||||||||
| const url = `${API_BASE_URL}${endpoint}`; | ||||||||||||||||
|
|
||||||||||||||||
| const headers = new Headers(options.headers); | ||||||||||||||||
| headers.set('Content-Type', 'application/json'); | ||||||||||||||||
| headers.set('x-api-key', process.env.NEXT_PUBLIC_EXOSPHERE_API_KEY || ''); | ||||||||||||||||
|
|
||||||||||||||||
| const response = await fetch(url, { | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||
| ...options.headers, | ||||||||||||||||
| }, | ||||||||||||||||
| ...options, | ||||||||||||||||
| headers, | ||||||||||||||||
| }); | ||||||||||||||||
|
Comment on lines
+29
to
36
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: env key overrides provided apiKey; header case inconsistencies.
Fix: only set a default if not already provided; standardize on - const headers = new Headers(options.headers);
- headers.set('Content-Type', 'application/json');
- headers.set('x-api-key', process.env.NEXT_PUBLIC_EXOSPHERE_API_KEY || '');
+ const headers = new Headers(options.headers);
+ headers.set('Content-Type', 'application/json');
+ if (!headers.has('x-api-key') && DEFAULT_API_KEY) {
+ headers.set('x-api-key', DEFAULT_API_KEY);
+ }
🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| if (!response.ok) { | ||||||||||||||||
| throw new Error(`API request failed: ${response.status} ${response.statusText}`); | ||||||||||||||||
| const errorData = await response.json().catch(() => ({})); | ||||||||||||||||
| console.error('API Error:', { | ||||||||||||||||
| status: response.status, | ||||||||||||||||
| statusText: response.statusText, | ||||||||||||||||
| url, | ||||||||||||||||
| errorData | ||||||||||||||||
| }); | ||||||||||||||||
| throw new Error(errorData.detail || `API request failed: ${response.statusText}`); | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
38
to
47
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Error body may be non-JSON. Good JSON fallback. Consider capturing - const errorData = await response.json().catch(() => ({}));
+ let errorData: any = {};
+ try { errorData = await response.json(); } catch {
+ try { errorData = { detail: await response.text() }; } catch {}
+ }
🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| return response.json(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
|
|
@@ -52,7 +60,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'PUT', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change to use I recommend the following improvements for maintainability:
|
||||||||||||||||
| }, | ||||||||||||||||
|
Comment on lines
+63
to
64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Use caller apiKey (fallback to default) consistently. Apply across all methods for consistency and to honor the passed - 'X-API-Key': DEFAULT_API_KEY,
+ 'x-api-key': apiKey || DEFAULT_API_KEY,Make the same change in every method setting the header (registerNodes, getGraphTemplate, createStates, enqueueStates, executeState, getSecrets, listRegisteredNodes, listGraphTemplates, getCurrentStates, getStatesByRunId, getGraphStructure). After this, Also applies to: 99-100, 117-118, 134-135, 152-153, 169-170, 185-186, 200-201, 216-217, 232-233, 248-249 |
||||||||||||||||
| body: JSON.stringify(request), | ||||||||||||||||
| } | ||||||||||||||||
|
|
@@ -88,7 +96,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'GET', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| } | ||||||||||||||||
| ); | ||||||||||||||||
|
|
@@ -106,7 +114,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'POST', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| body: JSON.stringify(request), | ||||||||||||||||
| } | ||||||||||||||||
|
|
@@ -123,7 +131,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'POST', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| body: JSON.stringify(request), | ||||||||||||||||
| } | ||||||||||||||||
|
|
@@ -141,7 +149,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'POST', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| body: JSON.stringify(request), | ||||||||||||||||
| } | ||||||||||||||||
|
|
@@ -158,7 +166,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'GET', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| } | ||||||||||||||||
| ); | ||||||||||||||||
|
|
@@ -174,7 +182,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'GET', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| } | ||||||||||||||||
| ); | ||||||||||||||||
|
|
@@ -189,7 +197,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'GET', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| } | ||||||||||||||||
| ); | ||||||||||||||||
|
|
@@ -205,7 +213,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'GET', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| } | ||||||||||||||||
| ); | ||||||||||||||||
|
|
@@ -221,7 +229,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'GET', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| } | ||||||||||||||||
| ); | ||||||||||||||||
|
|
@@ -237,7 +245,7 @@ class ApiService { | |||||||||||||||
| { | ||||||||||||||||
| method: 'GET', | ||||||||||||||||
| headers: { | ||||||||||||||||
| 'X-API-Key': apiKey, | ||||||||||||||||
| 'X-API-Key': DEFAULT_API_KEY, | ||||||||||||||||
| }, | ||||||||||||||||
| } | ||||||||||||||||
| ); | ||||||||||||||||
|
|
||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,14 +1,34 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| FROM python:3.12-slim-bookworm | ||||||||||||||||||||||||||||||||||||||||||||||
| COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Install curl for downloading uv | ||||||||||||||||||||||||||||||||||||||||||||||
| RUN apt-get update && apt-get install -y curl ca-certificates \ | ||||||||||||||||||||||||||||||||||||||||||||||
| && rm -rf /var/lib/apt/lists/* | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+4
to
+5
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Harden apt install. Use -RUN apt-get update && apt-get install -y curl ca-certificates \
- && rm -rf /var/lib/apt/lists/*
+RUN set -eux; \
+ apt-get update; \
+ apt-get install -y --no-install-recommends curl ca-certificates; \
+ rm -rf /var/lib/apt/lists/*📝 Committable suggestion
Suggested change
🧰 Tools🪛 Hadolint (2.12.0)[warning] 4-4: Pin versions in apt get install. Instead of (DL3008) [info] 4-4: Avoid additional packages by specifying (DL3015) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Download uv binary based on target architecture | ||||||||||||||||||||||||||||||||||||||||||||||
| ARG TARGETARCH | ||||||||||||||||||||||||||||||||||||||||||||||
| RUN case "$TARGETARCH" in \ | ||||||||||||||||||||||||||||||||||||||||||||||
| amd64) ARCH=x86_64 ;; \ | ||||||||||||||||||||||||||||||||||||||||||||||
| arm64) ARCH=aarch64 ;; \ | ||||||||||||||||||||||||||||||||||||||||||||||
| *) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \ | ||||||||||||||||||||||||||||||||||||||||||||||
| esac && \ | ||||||||||||||||||||||||||||||||||||||||||||||
| curl -L "https://github.com/astral-sh/uv/releases/latest/download/uv-${ARCH}-unknown-linux-musl.tar.gz" \ | ||||||||||||||||||||||||||||||||||||||||||||||
| | tar -xz && \ | ||||||||||||||||||||||||||||||||||||||||||||||
| mv uv-${ARCH}-unknown-linux-musl/uv /usr/local/bin/ && \ | ||||||||||||||||||||||||||||||||||||||||||||||
| mv uv-${ARCH}-unknown-linux-musl/uvx /usr/local/bin/ && \ | ||||||||||||||||||||||||||||||||||||||||||||||
| rm -rf uv-${ARCH}-unknown-linux-musl | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+9
to
+18
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 🛠️ Refactor suggestion Ensure pipefail and tighten the download/extract step. Without -ARG TARGETARCH
-RUN case "$TARGETARCH" in \
+ARG TARGETARCH
+SHELL ["/bin/bash", "-o", "pipefail", "-c"]
+RUN set -eux; case "${TARGETARCH}" in \
amd64) ARCH=x86_64 ;; \
arm64) ARCH=aarch64 ;; \
*) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \
esac && \
- curl -L "https://github.com/astral-sh/uv/releases/latest/download/uv-${ARCH}-unknown-linux-musl.tar.gz" \
- | tar -xz && \
- mv uv-${ARCH}-unknown-linux-musl/uv /usr/local/bin/ && \
- mv uv-${ARCH}-unknown-linux-musl/uvx /usr/local/bin/ && \
- rm -rf uv-${ARCH}-unknown-linux-musl
+ curl -fsSL "https://github.com/astral-sh/uv/releases/latest/download/uv-${ARCH}-unknown-linux-musl.tar.gz" \
+ | tar -xz && \
+ install -m 0755 "uv-${ARCH}-unknown-linux-musl/uv" /usr/local/bin/uv && \
+ install -m 0755 "uv-${ARCH}-unknown-linux-musl/uvx" /usr/local/bin/uvx && \
+ rm -rf "uv-${ARCH}-unknown-linux-musl"Optionally verify checksum/signature of the downloaded tarball for supply-chain safety. 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Hadolint (2.12.0)[info] 9-9: Double quote to prevent globbing and word splitting. (SC2086) [warning] 9-9: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check (DL4006) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| WORKDIR /api-server | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Copy dependency files first (for caching) | ||||||||||||||||||||||||||||||||||||||||||||||
| COPY pyproject.toml uv.lock ./ | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Install dependencies using uv | ||||||||||||||||||||||||||||||||||||||||||||||
| RUN uv sync --locked | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+22
to
26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Consider deterministic, offline-friendly sync. If CI relies on network, 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Copy app source code | ||||||||||||||||||||||||||||||||||||||||||||||
| COPY . . | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| EXPOSE 8000 | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| CMD ["uv", "run", "run.py", "--mode", "production", "--workers", "4"] | ||||||||||||||||||||||||||||||||||||||||||||||
| # Start the app | ||||||||||||||||||||||||||||||||||||||||||||||
| CMD ["uv", "run", "run.py", "--mode", "production", "--workers", "4"] | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment appears to be a temporary note. It's best to remove such comments from the final code to keep it clean.