Skip to content

Commit 3a0d93c

Browse files
[CAP-9310] move dev command from 'workflows tasks' to 'workflows' (#318)
Move the `dev` command from `render workflows tasks dev` to `render workflows dev` for a shorter, more intuitive path. GitOrigin-RevId: 69a00871171353f4fec04e1f13cc9c9767751c98
1 parent f459cf1 commit 3a0d93c

4 files changed

Lines changed: 214 additions & 209 deletions

File tree

cmd/object.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ The --region flag specifies which region to use. Alternatively, set the
1616
RENDER_REGION environment variable. The --region flag takes precedence
1717
if both are provided.
1818
19-
In local development mode (when running with 'render ea tasks dev' or with the
20-
--local flag), objects are stored in the .render/objects/ directory.
19+
When using the --local flag, objects are stored in the .render/objects/ directory
20+
instead of cloud storage.
2121
2222
Available commands:
2323
list - List objects in storage

cmd/task.go

Lines changed: 0 additions & 205 deletions
Original file line numberDiff line numberDiff line change
@@ -1,186 +1,26 @@
11
package cmd
22

33
import (
4-
"errors"
54
"fmt"
6-
"net/http"
75
"os"
8-
"path/filepath"
9-
"sort"
10-
"strings"
11-
"syscall"
126

13-
"github.com/charmbracelet/lipgloss"
14-
"github.com/gorilla/websocket"
157
"github.com/render-oss/cli/pkg/client"
168
workflows "github.com/render-oss/cli/pkg/client/workflows"
179
"github.com/render-oss/cli/pkg/command"
1810
"github.com/render-oss/cli/pkg/logs"
19-
renderstyle "github.com/render-oss/cli/pkg/style"
2011
"github.com/render-oss/cli/pkg/tasks"
2112
"github.com/render-oss/cli/pkg/text"
2213
"github.com/render-oss/cli/pkg/tui/flows"
2314
"github.com/render-oss/cli/pkg/tui/views"
2415
workflowviews "github.com/render-oss/cli/pkg/tui/views/workflows"
25-
"github.com/render-oss/cli/pkg/workflows/apiserver"
26-
logstore "github.com/render-oss/cli/pkg/workflows/logs"
27-
"github.com/render-oss/cli/pkg/workflows/orchestrator"
28-
"github.com/render-oss/cli/pkg/workflows/store"
29-
"github.com/render-oss/cli/pkg/workflows/taskserver"
3016
"github.com/spf13/cobra"
3117
)
3218

33-
const defaultTaskAPIPort = 8120
34-
3519
var taskCmd = &cobra.Command{
3620
Use: "tasks",
3721
Short: "Manage tasks",
3822
}
3923

40-
var taskDevCmd = &cobra.Command{
41-
Use: "dev -- <command to start a workflow service>",
42-
Short: "Start a workflow service in development mode",
43-
SilenceUsage: true,
44-
Long: `Start a workflow service in development mode for local testing.
45-
46-
This command runs your workflow service locally on port 8120, allowing you to list and run
47-
tasks without deploying to Render. Task runs and their logs are stored in memory, so you
48-
can query them after tasks complete.
49-
50-
The command will spawn a new subprocess with your specified command whenever it needs to
51-
run a task or list the defined tasks.
52-
53-
To interact with the local task server:
54-
• Use the --local flag with other task commands (e.g., 'render tasks list --local')
55-
• Or set RENDER_USE_LOCAL_DEV=true when using the workflow client SDK
56-
57-
To use a different port:
58-
• Specify --port when starting the dev server
59-
• Then use --port with other task commands, or set RENDER_LOCAL_DEV_URL in the SDK
60-
61-
Examples:
62-
render workflows tasks dev -- "go run main.go"
63-
render workflows tasks dev --port 9000 -- "npm start"
64-
render workflows tasks list --local
65-
render workflows taskruns start my-task --local --input='["arg1"]'
66-
`,
67-
RunE: func(cmd *cobra.Command, args []string) error {
68-
ctx := cmd.Context()
69-
var commandArgs []string
70-
if cmd.ArgsLenAtDash() >= 0 {
71-
commandArgs = args[cmd.ArgsLenAtDash():]
72-
}
73-
74-
if len(commandArgs) == 0 {
75-
return errors.New("command is required")
76-
}
77-
78-
debugMode, err := cmd.Flags().GetBool("debug")
79-
if err != nil {
80-
return fmt.Errorf("failed to get debug flag: %w", err)
81-
}
82-
83-
port, err := cmd.Flags().GetInt("port")
84-
if err != nil {
85-
return fmt.Errorf("failed to get port flag: %w", err)
86-
}
87-
88-
socketTracker, err := orchestrator.NewSocketTracker(ctx)
89-
if err != nil {
90-
return err
91-
}
92-
93-
taskServerFactory := taskserver.NewTaskServerFactory()
94-
95-
logs := logstore.NewLogStore()
96-
store := store.NewTaskStore()
97-
var (
98-
pending []string
99-
ready bool
100-
)
101-
102-
statusReporter := orchestrator.NewPrintStatusReporter(
103-
func(format string, args ...any) {
104-
message := fmt.Sprintf(format, args...)
105-
if !ready {
106-
pending = append(pending, message)
107-
return
108-
}
109-
command.Println(cmd, "%s", message)
110-
},
111-
orchestrator.WithStatusReporterTimestamps(debugMode),
112-
orchestrator.WithStatusReporterTaskEnqueued(debugMode),
113-
orchestrator.WithStatusReporterIncludeInputs(true),
114-
)
115-
coordinator := orchestrator.NewCoordinator(
116-
ctx,
117-
store,
118-
orchestrator.NewExec(logs, debugMode, commandArgs[0], commandArgs[1:]...),
119-
socketTracker,
120-
taskServerFactory,
121-
statusReporter,
122-
)
123-
124-
upgrader := &websocket.Upgrader{
125-
Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
126-
http.Error(w, "failed to upgrade http connection to websocket", http.StatusUpgradeRequired)
127-
},
128-
}
129-
130-
api := apiserver.NewHandler(coordinator, store, logs, upgrader)
131-
apiSrv, err := apiserver.Start(api, port)
132-
if err != nil {
133-
if errors.Is(err, syscall.EADDRINUSE) {
134-
return fmt.Errorf("port %d is already in use. Stop the other process or use --port to pick a different one", port)
135-
}
136-
return fmt.Errorf("failed to start server on port %d: %w", port, err)
137-
}
138-
139-
ok := lipgloss.NewStyle().Foreground(renderstyle.ColorOK)
140-
info := lipgloss.NewStyle().Foreground(renderstyle.ColorInfo)
141-
dim := lipgloss.NewStyle().Foreground(renderstyle.ColorDeprioritized)
142-
143-
command.Println(cmd, "%s %s",
144-
ok.Render("Workflow server listening on"),
145-
renderstyle.Bold(fmt.Sprintf("http://localhost:%d", port)),
146-
)
147-
148-
logs.Start(ctx)
149-
150-
registeredTasks, err := coordinator.PopulateTasks(ctx)
151-
if err != nil {
152-
return fmt.Errorf("failed to load tasks: %w", err)
153-
}
154-
155-
command.Println(cmd, "%s", formatTaskSummary(info, registeredTasks, describeWorkflowSource(commandArgs)))
156-
command.Println(cmd, "")
157-
158-
portFlag := ""
159-
if port != defaultTaskAPIPort {
160-
portFlag = fmt.Sprintf(" --port %d", port)
161-
}
162-
command.Println(cmd, "%s", dim.Render("To browse and run tasks, open another terminal and run:"))
163-
command.Println(cmd, " %s", renderstyle.Bold(fmt.Sprintf("render workflows tasks list --local%s", portFlag)))
164-
command.Println(cmd, "")
165-
command.Println(cmd, "%s", dim.Render("To trigger a specific task directly:"))
166-
command.Println(cmd, " %s", renderstyle.Bold(fmt.Sprintf("render workflows taskruns start <task-name> --local%s --input='[\"arg1\"]'", portFlag)))
167-
command.Println(cmd, "")
168-
169-
ready = true
170-
for _, line := range pending {
171-
command.Println(cmd, "%s", line)
172-
}
173-
pending = nil
174-
175-
<-ctx.Done()
176-
177-
apiSrv.Shutdown(ctx)
178-
179-
return nil
180-
},
181-
Args: cobra.MinimumNArgs(1),
182-
}
183-
18424
func NewTaskRunStartCmd(deps flows.WorkflowDeps) *cobra.Command {
18525
cmd := &cobra.Command{
18626
Use: "start [taskID] --input=<json>",
@@ -264,11 +104,7 @@ func init() {
264104
taskCmd.PersistentFlags().Bool("local", false, "Run against the server spawned by the task dev command")
265105
taskCmd.PersistentFlags().Int("port", defaultTaskAPIPort, "Port of the local task server (8120 when not specified)")
266106

267-
taskDevCmd.Flags().Int("port", defaultTaskAPIPort, "Port of the local task server (8120 when not specified)")
268-
taskDevCmd.Flags().Bool("debug", false, "Print detailed workflow task execution events")
269-
270107
WorkflowsCmd.AddCommand(taskCmd)
271-
taskCmd.AddCommand(taskDevCmd)
272108
}
273109

274110
type localDeps struct {
@@ -320,44 +156,3 @@ func getLocalDeps(cmd *cobra.Command, deps flows.WorkflowDeps) (flows.WorkflowDe
320156
}
321157
return deps, local, nil
322158
}
323-
324-
func describeWorkflowSource(commandArgs []string) string {
325-
if len(commandArgs) == 0 {
326-
return ""
327-
}
328-
329-
last := commandArgs[len(commandArgs)-1]
330-
base := filepath.Base(last)
331-
if strings.Contains(base, ".") {
332-
return base
333-
}
334-
335-
return strings.Join(commandArgs, " ")
336-
}
337-
338-
func formatTaskSummary(info lipgloss.Style, tasks []*store.Task, source string) string {
339-
if len(tasks) == 0 {
340-
return fmt.Sprintf("0 tasks found in %s (waiting for registration)", renderstyle.Bold(source))
341-
}
342-
343-
names := make([]string, 0, len(tasks))
344-
for _, task := range tasks {
345-
names = append(names, task.Name)
346-
}
347-
sort.Strings(names)
348-
349-
plural := ""
350-
if len(names) > 1 {
351-
plural = "s"
352-
}
353-
354-
header := fmt.Sprintf("%d task%s found in %s", len(names), plural, renderstyle.Bold(source))
355-
356-
var b strings.Builder
357-
b.WriteString(header)
358-
for _, name := range names {
359-
b.WriteString("\n • ")
360-
b.WriteString(info.Render(name))
361-
}
362-
return b.String()
363-
}

cmd/tasklist.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ In interactive mode, you will be prompted to select a workflow if not provided.
2727
Local Development:
2828
When using the --local flag, you don't need to provide a workflow version ID. Instead,
2929
the command connects to your local dev server (default port 8120) to list tasks from
30-
your running workflow service. Start the dev server with 'render workflows tasks dev' first.
30+
your running workflow service. Start the dev server with 'render workflows dev' first.
3131
3232
Examples:
3333
render workflows tasks list wfv-1234

0 commit comments

Comments
 (0)