Conversation
…unction asset to an older revision
🦋 Changeset detectedLatest commit: 942071a The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| mutationFn: async ({ deploymentId, slug, type }: RedeploySourceParams) => { | ||
| const activeDeployment = activeResult?.deployment; | ||
| if (!activeDeployment) { | ||
| throw new Error("No active deployment found."); | ||
| } |
There was a problem hiding this comment.
🚩 Removed safety check: active deployment must match latest deployment
The old useRedeployFunction (useRedeployFunction.tsx:38-42, now deleted) had an explicit guard: if (activeDeployment.id !== latestDeployment.id) throw new Error(...). This prevented redeployment when a newer deployment was in progress. The new useRedeploySource intentionally removes this check to support redeploying from any older deployment. This is a behavioral change that could allow evolving the active deployment while a newer deployment is building. Worth confirming this is the intended design.
Was this helpful? React with 👍 or 👎 to provide feedback.
| return fullDeployment.externalMcps?.find((m) => m.slug === sourceSlug) | ||
| ?.id; |
There was a problem hiding this comment.
🟡 DeploymentsForExternalMCPSource query uses per-row PK instead of stable content identifier, breaking version deduplication
The DeploymentsForExternalMCPSource SQL query uses ema.id as asset_id for the LAG-based deduplication filter (WHERE asset_id IS DISTINCT FROM prev_asset_id). However, ema.id is the auto-generated primary key of external_mcp_attachments, which is unique per row — every deployment clone creates a new record with a new id (see CloneDeploymentExternalMCPs at server/internal/deployments/queries.sql:788). Unlike deployments_openapiv3_assets and deployments_functions which have a stable asset_id referencing an uploaded file in the assets table, external_mcp_attachments has no such column (server/database/schema.sql:1276-1293). This means the LAG comparison will always find different IDs, and the deduplication filter is a no-op — every deployment containing the MCP source is returned, defeating the purpose of showing only version-change boundaries.
This also affects the client side: in SourceDeploymentsPanel.tsx:243, activeAssetId for external MCP is dep.externalMcps?.find((m) => m.slug === sourceSlug)?.id, which will never match deployment.assetId from a different deployment's SourceDeploymentSummary, making isCurrentVersion always false.
Was this helpful? React with 👍 or 👎 to provide feedback.
| func (s *Service) DeploymentsForSource(ctx context.Context, form *gen.DeploymentsForSourcePayload) (*gen.DeploymentsForSourceResult, error) { | ||
| authCtx, ok := contextvalues.GetAuthContext(ctx) | ||
| if !ok || authCtx == nil || authCtx.ProjectID == nil { | ||
| return nil, oops.C(oops.CodeUnauthorized) | ||
| } | ||
|
|
||
| var cursor uuid.NullUUID | ||
| if form.Cursor != nil { | ||
| c, err := uuid.Parse(*form.Cursor) | ||
| if err != nil { | ||
| return nil, oops.E(oops.CodeBadRequest, err, "invalid cursor").Log(ctx, s.logger) | ||
| } | ||
| cursor = uuid.NullUUID{UUID: c, Valid: true} | ||
| } | ||
|
|
||
| type row struct { | ||
| ID uuid.UUID | ||
| AssetID uuid.UUID | ||
| Status string | ||
| CreatedAt pgtype.Timestamptz | ||
| ToolCount int64 | ||
| } | ||
|
|
||
| var rows []row | ||
|
|
||
| switch form.Kind { | ||
| case "openapi": | ||
| r, err := s.repo.DeploymentsForOpenAPISource(ctx, repo.DeploymentsForOpenAPISourceParams{ | ||
| Slug: form.Slug, | ||
| ProjectID: *authCtx.ProjectID, | ||
| Cursor: cursor, | ||
| }) | ||
| if err != nil { | ||
| return nil, oops.E(oops.CodeUnexpected, err, "list deployments for openapi source").Log(ctx, s.logger) | ||
| } | ||
| for _, v := range r { | ||
| rows = append(rows, row{ID: v.ID, AssetID: v.AssetID, Status: v.Status, CreatedAt: v.CreatedAt, ToolCount: v.ToolCount}) | ||
| } | ||
| case "function": | ||
| r, err := s.repo.DeploymentsForFunctionSource(ctx, repo.DeploymentsForFunctionSourceParams{ | ||
| Slug: form.Slug, | ||
| ProjectID: *authCtx.ProjectID, | ||
| Cursor: cursor, | ||
| }) | ||
| if err != nil { | ||
| return nil, oops.E(oops.CodeUnexpected, err, "list deployments for function source").Log(ctx, s.logger) | ||
| } | ||
| for _, v := range r { | ||
| rows = append(rows, row{ID: v.ID, AssetID: v.AssetID, Status: v.Status, CreatedAt: v.CreatedAt, ToolCount: v.ToolCount}) | ||
| } | ||
| case "externalmcp": | ||
| r, err := s.repo.DeploymentsForExternalMCPSource(ctx, repo.DeploymentsForExternalMCPSourceParams{ | ||
| Slug: form.Slug, | ||
| ProjectID: *authCtx.ProjectID, | ||
| Cursor: cursor, | ||
| }) | ||
| if err != nil { | ||
| return nil, oops.E(oops.CodeUnexpected, err, "list deployments for external mcp source").Log(ctx, s.logger) | ||
| } | ||
| for _, v := range r { | ||
| rows = append(rows, row{ID: v.ID, AssetID: v.AssetID, Status: v.Status, CreatedAt: v.CreatedAt, ToolCount: v.ToolCount}) | ||
| } | ||
| default: | ||
| return nil, oops.E(oops.CodeBadRequest, nil, "invalid source kind").Log(ctx, s.logger) | ||
| } | ||
|
|
||
| items := make([]*gen.SourceDeploymentSummary, 0, len(rows)) | ||
| for _, r := range rows { | ||
| items = append(items, &gen.SourceDeploymentSummary{ | ||
| ID: r.ID.String(), | ||
| AssetID: r.AssetID.String(), | ||
| Status: r.Status, | ||
| CreatedAt: r.CreatedAt.Time.Format(time.RFC3339), | ||
| ToolCount: r.ToolCount, | ||
| }) | ||
| } | ||
|
|
||
| var nextCursor *string | ||
| limit := 50 | ||
| if len(items) >= limit+1 { | ||
| nextCursor = &items[limit].ID | ||
| items = items[:limit] | ||
| } | ||
|
|
||
| return &gen.DeploymentsForSourceResult{ | ||
| NextCursor: nextCursor, | ||
| Items: items, | ||
| }, nil | ||
| } |
There was a problem hiding this comment.
🚩 No tests added for the new DeploymentsForSource endpoint
The CONTRIBUTING.md states 'Add tests for all new contributions.' The new DeploymentsForSource server method in impl.go:352-440 and the three new SQL queries have no corresponding test coverage. Given the complexity of the LAG-based deduplication logic and the three different source type branches, tests would be especially valuable here to catch edge cases (empty results, cursor pagination, the deduplication behavior).
Was this helpful? React with 👍 or 👎 to provide feedback.
disintegrator
left a comment
There was a problem hiding this comment.
Directionally this feature is important to have but I'm not qualified to say if the way the database is queried for it is a good idea or not.
If you can share an investigation into that with a coding agent then I'd appreciate it.
Adds a feature to enable redeploying an individual function asset to an older revision