Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
55ae30a
feat: add branch deployment selector UI for project admins
ericpgreen2 Mar 9, 2026
a5bfbb8
refactor: move branch selector into avatar dropdown menu
ericpgreen2 Mar 10, 2026
9fe8766
feat: migrate branch selector from query params to `@branch` path seg…
ericpgreen2 Mar 10, 2026
43af2e4
feat: move branch selector to ProjectHeader as breadcrumb pill
ericpgreen2 Mar 11, 2026
658edce
test: add unit tests for branch-utils
ericpgreen2 Mar 11, 2026
fe65af0
fix: use `isPending` instead of `isLoading` for TanStack Query v5 mut…
ericpgreen2 Mar 11, 2026
33d1ef9
fix: harden branch deployment UI
ericpgreen2 Mar 11, 2026
4fa8fb7
refactor: consolidate branch code into `features/branches/`
ericpgreen2 Mar 11, 2026
63ca57c
fix: remove stale "Back to production" banner references in comments
ericpgreen2 Mar 11, 2026
2fbbca2
refactor: extract `handleBranchNavigation` from project layout
ericpgreen2 Mar 11, 2026
46212ee
refactor: simplify branch-utils and remove duplicate state
ericpgreen2 Mar 11, 2026
6543e45
fix: show branch selector on stopped/errored deployment pages
ericpgreen2 Mar 11, 2026
0a5b394
Cloud editing: edit session lifecycle, iframe embed, and single-edito…
ericpgreen2 Feb 19, 2026
b00fa8c
Cloud editing: replace iframe with native shared components
ericpgreen2 Feb 23, 2026
86f84fc
feat: replace Edit tab with header button + Dev Environments status page
ericpgreen2 Mar 11, 2026
e7d20da
feat: unify edit flow with `@branch` URL scheme
ericpgreen2 Mar 11, 2026
00b9bfe
fix: reset SSE retry counter on pause and fresh connections
ericpgreen2 Mar 11, 2026
0607289
feat: render ProjectHeader on edit pages instead of custom toolbar
ericpgreen2 Mar 11, 2026
e9d390f
refactor: collapse edit toolbar into ProjectHeader
ericpgreen2 Mar 11, 2026
d627bc1
fix: stop perpetual `ListResources` polling on branch deployments
ericpgreen2 Mar 12, 2026
9210918
fix: pass JWT to SSE connection in cloud editing
ericpgreen2 Mar 12, 2026
8ba46f1
fix: CodeMirror line number gutter recreated on every update
ericpgreen2 Mar 12, 2026
5c75205
fix: hide empty footer in cloud editing navigation
ericpgreen2 Mar 12, 2026
675921b
refactor: render ProjectHeader in edit layout with `editContext` prop
ericpgreen2 Mar 12, 2026
041ba63
fix: resolve blank page and broken button in branch deployment UX
ericpgreen2 Mar 12, 2026
ed55955
fix: keep BranchSelector status in sync with deployment changes
ericpgreen2 Mar 12, 2026
f708890
refactor: extract LoadingSpinner component and show branch-aware message
ericpgreen2 Mar 12, 2026
75be877
refactor: edit session UX refinements and polling optimization
ericpgreen2 Mar 12, 2026
46ca9ac
fix: fail fast on non-retryable provisioning errors and improve error UX
ericpgreen2 Mar 12, 2026
306fe19
refactor: remove dead code from legacy iframe edit session
ericpgreen2 Mar 13, 2026
ae1c265
fix: grant repo permissions for branch JWTs and improve editing UX
ericpgreen2 Mar 13, 2026
af880a2
fix: keep SSE connection alive in cloud editor to prevent save failures
ericpgreen2 Mar 13, 2026
2ad0e20
refactor: polish Edit button dropdown and show it on dashboard pages
ericpgreen2 Mar 13, 2026
daf7ef7
refactor: unify deployments page with slot awareness and edit UX polish
ericpgreen2 Mar 13, 2026
8f01735
fix: show info banners on project-scoped pages when viewing a branch
ericpgreen2 Mar 13, 2026
36ce7f9
fix: show current branch deployment on Status Overview page
ericpgreen2 Mar 13, 2026
5a22bb1
fix: cloud editing UX improvements and deployment reliability
ericpgreen2 Mar 16, 2026
b36ba6a
merge: resolve conflicts with main
ericpgreen2 Mar 24, 2026
5c6626e
Fix deployment start/stop UI transitions and add start/stop actions t…
ericpgreen2 Mar 24, 2026
fa24c05
Merge branch 'main' into ericgreen/cloud-editing-mvp
ericpgreen2 Mar 26, 2026
79b0b4c
Redesign deployments status page
ericpgreen2 Mar 26, 2026
5df3040
fix: Svelte 5 migration and refetch interval improvements
ericpgreen2 Mar 26, 2026
30b6ed8
Merge branch 'ericgreen/branch-selector-ui' into ericgreen/cloud-edit…
ericpgreen2 Apr 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions admin/jobs/river/reconcile_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package river
import (
"context"
"errors"
"strings"
"time"

"github.com/jackc/pgx/v5"
"github.com/rilldata/rill/admin"
Expand All @@ -24,6 +26,12 @@ type ReconcileDeploymentWorker struct {
admin *admin.Service
}

// NextRetryAt uses exponential backoff starting at 15s: ~15s, ~30s, ~60s, ~120s.
// This keeps total retry duration under ~4 minutes so users aren't stuck waiting.
func (w *ReconcileDeploymentWorker) NextRetryAt(job *river.Job[ReconcileDeploymentArgs]) time.Time {
return time.Now().Add(15 * time.Second * time.Duration(1<<(job.Attempt-1)))
}

// NewReconcileDeploymentWorker creates a new ReconcileDeploymentWorker. Only to be used in tests to trigger the worker directly.
func NewReconcileDeploymentWorker(admin *admin.Service) *ReconcileDeploymentWorker {
return &ReconcileDeploymentWorker{
Expand Down Expand Up @@ -74,8 +82,14 @@ func (w *ReconcileDeploymentWorker) Work(ctx context.Context, job *river.Job[Rec
}

// Initialize the deployment (by provisioning a runtime and creating an instance on it)
err := w.admin.StartDeploymentInner(ctx, depl)
if err != nil {
if err := w.admin.StartDeploymentInner(ctx, depl); err != nil {
// Check if this is a non-retryable error (fail fast instead of waiting for all retries)
if isNonRetryable(err) || job.Attempt >= job.MaxAttempts {
if _, dbErr := w.admin.DB.UpdateDeploymentStatus(ctx, depl.ID, database.DeploymentStatusErrored, err.Error()); dbErr != nil {
w.admin.Logger.Error("reconcile deployment: failed to set errored status", observability.ZapCtx(ctx), zap.Error(dbErr))
}
return river.JobCancel(err)
}
return err
}
}
Expand Down Expand Up @@ -115,12 +129,24 @@ func (w *ReconcileDeploymentWorker) Work(ctx context.Context, job *river.Job[Rec
// Delete the deployment and all its resources.
err := w.admin.StopDeploymentInner(ctx, depl)
if err != nil {
if job.Attempt >= job.MaxAttempts {
if _, dbErr := w.admin.DB.UpdateDeploymentStatus(ctx, depl.ID, database.DeploymentStatusErrored, err.Error()); dbErr != nil {
w.admin.Logger.Error("reconcile deployment: failed to set errored status during deletion", observability.ZapCtx(ctx), zap.Error(dbErr))
}
return river.JobCancel(err)
}
return err
}

// Delete the deployment
err = w.admin.DB.DeleteDeployment(ctx, depl.ID)
if err != nil {
if job.Attempt >= job.MaxAttempts {
if _, dbErr := w.admin.DB.UpdateDeploymentStatus(ctx, depl.ID, database.DeploymentStatusErrored, err.Error()); dbErr != nil {
w.admin.Logger.Error("reconcile deployment: failed to set errored status during deletion", observability.ZapCtx(ctx), zap.Error(dbErr))
}
return river.JobCancel(err)
}
return err
}

Expand Down Expand Up @@ -153,3 +179,12 @@ func (w *ReconcileDeploymentWorker) Work(ctx context.Context, job *river.Job[Rec

return nil
}

// isNonRetryable returns true for errors that won't resolve with retries,
// such as capacity limits or configuration errors.
func isNonRetryable(err error) bool {
msg := err.Error()
return strings.Contains(msg, "no runtimes found with sufficient available slots") ||
strings.Contains(msg, "Invalid environment") ||
strings.Contains(msg, "not a valid version")
}
2 changes: 1 addition & 1 deletion admin/jobs/river/river.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func (c *Client) ReconcileDeployment(ctx context.Context, deploymentID string) (
res, err := c.riverClient.Insert(ctx, ReconcileDeploymentArgs{
DeploymentID: deploymentID,
}, &river.InsertOpts{
MaxAttempts: 25, // Last retry, ~3 weeks after first run
MaxAttempts: 5, // Retries at ~15s, 30s, 60s, 120s (see NextRetryAt override)
UniqueOpts: river.UniqueOpts{
ByArgs: true,
ByState: []rivertype.JobState{
Expand Down
2 changes: 2 additions & 0 deletions admin/server/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@
}
if depl.Environment == "dev" {
instancePermissions = append(instancePermissions,
runtime.ReadInstance,
runtime.ReadOLAP,
runtime.ReadProfiling,
runtime.ReadRepo,
Expand Down Expand Up @@ -1031,3 +1032,4 @@

return attr, userID, forProjPerms.ReadProd, nil
}

Check failure on line 1035 in admin/server/deployment.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gci)
10 changes: 9 additions & 1 deletion admin/server/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (

const devDeplTTL = 6 * time.Hour

const devSlots = 8
const devSlots = 4

const prodDeplTTL = 14 * 24 * time.Hour

Expand Down Expand Up @@ -456,6 +456,13 @@ func (s *Server) GetProject(ctx context.Context, req *adminv1.GetProjectRequest)
runtime.EditTrigger,
)
}
if req.Branch != "" && permissions.ManageDev {
instancePermissions = append(
instancePermissions,
runtime.ReadRepo,
runtime.EditRepo,
)
}

var systemPermissions []runtime.Permission
if req.IssueSuperuserToken {
Expand Down Expand Up @@ -2178,6 +2185,7 @@ func (s *Server) projToDTO(p *database.Project, orgName string) *adminv1.Project
Provisioner: p.Provisioner,
ProdVersion: p.ProdVersion,
ProdSlots: int64(p.ProdSlots),
DevSlots: int64(p.DevSlots),
PrimaryBranch: p.PrimaryBranch,
Subpath: p.Subpath,
GitRemote: safeStr(p.GitRemote),
Expand Down
Loading
Loading