Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0ca188b
⬆️ sec(deps): Bump golang.org/x/sys from 0.44.0 to 0.45.0 (#522)
dependabot[bot] May 22, 2026
a33a43f
add pipelines command and its subcommands
sunny2get Apr 28, 2026
4fa89b7
update get command output error handling
sunny2get Apr 28, 2026
3142b78
update to direct http calls
sunny2get Apr 28, 2026
86337aa
updated create and update methods
sunny2get Apr 28, 2026
4ea21e7
add tests
sunny2get Apr 30, 2026
71f2496
add new commands to cover the rest of APIs
sunny2get Apr 30, 2026
4ab12ca
add unit tests
sunny2get Apr 30, 2026
c6031e1
adding environment subcommand
sunny2get May 4, 2026
f530683
update the commands to use new words-pipeline and task
sunny2get May 6, 2026
0ac675c
update dispatch to run
sunny2get May 6, 2026
785106a
remap run --output json to use run_id / covalent_run_id
sunny2get May 8, 2026
a03efb3
sync pipelines CLI with ObjectId migration and updated graph schema
sunny2get May 20, 2026
d21e138
add SKIP_ENVIRONMENTS flag to demo-pipelines task
sunny2get May 20, 2026
99cfae1
[CMPT-5391] remove custom Time type; pipe demo output to output.txt
sunny2get May 22, 2026
b2f5cb7
[CMPT-5391] use time.Time for all pipeline datetime fields
sunny2get May 22, 2026
3d77d0f
[CMPT-5391] align pipelines rendering with workload pattern
sunny2get May 22, 2026
a8f7e0e
[CMPT-5391] move rendering into internal/pipelines to match workload …
sunny2get May 23, 2026
02c9843
[CMPT-5391] align Go structs and tests with pipelines-api actions 052…
sunny2get May 23, 2026
5bfeebe
[CMPT-5391] rename dr pipelines to dr pipeline (singular)
sunny2get May 23, 2026
a3849fa
[CMPT-5391] refactor drapi verbs to use AuthorizeRequest and NewHTTPC…
sunny2get May 23, 2026
ca2a2bd
refactor(drapi): consolidate auth into AuthorizeRequest, fix body lea…
sunny2get May 23, 2026
95e2844
fix(pipeline): update graph response key from "lattice" to "pipeline"
sunny2get May 25, 2026
4d50105
[CMPT-5391] fix variable shadowing in pipeline get command
sunny2get May 25, 2026
00c59d7
[CMPT-5391] rename internal/pipelines to internal/pipeline (singular)
sunny2get May 25, 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
20 changes: 18 additions & 2 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,22 @@ tasks:
cmds:
- ./smoke_test_scripts/run_smoke_test.sh {{if .DR_API_TOKEN}}{{.DR_API_TOKEN}}{{else}}$DR_API_TOKEN{{end}}

demo-pipelines:
desc: "End-to-end demo of every dr pipelines command. Defaults to interactive (keypress per step); pass DELAY=N or `-- --delay=N` for auto-advance. Set SKIP_SCHEDULES=true or SKIP_ENVIRONMENTS=true to skip steps that require k8s/Covalent."
deps: [build]
vars:
DEMO_DIR: '{{default "/Users/sunnypal.sharma/Desktop/tests/pipelines-api" .DEMO_DIR}}'
DELAY_FLAG: '{{if .DELAY}}--delay={{.DELAY}}{{end}}'
env:
DATAROBOT_CLI_FEATURE_PIPELINE: "true"
DATAROBOT_CLI_ENDPOINT: "http://localhost:8100/api/v2"
DATAROBOT_CLI_TOKEN: "local"
DATAROBOT_CLI_SKIP_AUTH: "true"
DEMO_SKIP_SCHEDULES: '{{if eq .SKIP_SCHEDULES "false"}}false{{else}}true{{end}}'
DEMO_SKIP_ENVIRONMENTS: '{{if eq .SKIP_ENVIRONMENTS "false"}}false{{else}}true{{end}}'
cmds:
- DR_BIN="$PWD/dist/dr" DEMO_DIR="{{.DEMO_DIR}}" bash "{{.DEMO_DIR}}/demo.sh" {{.DELAY_FLAG}} {{.CLI_ARGS}} 2>&1 | tee output.txt

smoke-test-self-update:
desc: "Run self-update smoke tests"
cmds:
Expand All @@ -178,8 +194,8 @@ tasks:
cmds:
- echo "📚 Starting documentation server…"
- uv sync
- echo "🌐 Open http://localhost:8000 in your browser"
- uv run mkdocs serve
- echo "🌐 Open http://localhost:8001 in your browser"
- uv run mkdocs serve --dev-addr localhost:8001

copyright:
silent: true
Expand Down
65 changes: 65 additions & 0 deletions cmd/pipeline/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2026 DataRobot, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pipeline

import (
"github.com/datarobot/cli/cmd/pipeline/create"
"github.com/datarobot/cli/cmd/pipeline/del"
"github.com/datarobot/cli/cmd/pipeline/environment"
"github.com/datarobot/cli/cmd/pipeline/get"
"github.com/datarobot/cli/cmd/pipeline/graph"
"github.com/datarobot/cli/cmd/pipeline/input"
"github.com/datarobot/cli/cmd/pipeline/list"
"github.com/datarobot/cli/cmd/pipeline/lock"
"github.com/datarobot/cli/cmd/pipeline/run"
"github.com/datarobot/cli/cmd/pipeline/schedule"
"github.com/datarobot/cli/cmd/pipeline/update"
"github.com/datarobot/cli/cmd/pipeline/version"
"github.com/datarobot/cli/internal/features"
"github.com/spf13/cobra"
)

func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "pipeline",
Aliases: []string{"pipelines"},
GroupID: "core",
Short: "Pipelines API management commands",
Long: `Manage AI/ML pipelines orchestrated by Covalent.

Create, list, inspect, and update pipelines registered with the
DataRobot pipelines service. Sub-commands are also available for managing
input payloads, runs, and recurring schedules.`,
}

features.SetGate(cmd, "pipeline")

cmd.AddCommand(
create.Cmd(),
get.Cmd(),
list.Cmd(),
update.Cmd(),
del.Cmd(),
lock.Cmd(),
version.Cmd(),
graph.Cmd(),
input.Cmd(),
run.Cmd(),
schedule.Cmd(),
environment.Cmd(),
)

return cmd
}
60 changes: 60 additions & 0 deletions cmd/pipeline/cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2026 DataRobot, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pipeline

import (
"testing"

"github.com/datarobot/cli/internal/features"
"github.com/stretchr/testify/assert"
)

func TestCmd_BasicMetadata(t *testing.T) {
cmd := Cmd()

assert.Equal(t, "pipeline", cmd.Use)
assert.Equal(t, "core", cmd.GroupID)
assert.NotEmpty(t, cmd.Short)
assert.NotEmpty(t, cmd.Long)
}

func TestCmd_FeatureGate(t *testing.T) {
cmd := Cmd()

gate, ok := cmd.Annotations[features.AnnotationKey]
assert.True(t, ok, "expected feature-gate annotation to be set")
assert.Equal(t, "pipeline", gate)
}

func TestCmd_HasExpectedSubcommands(t *testing.T) {
cmd := Cmd()

want := map[string]bool{
"create": false,
"list": false,
"get": false,
"update": false,
}

for _, sub := range cmd.Commands() {
if _, ok := want[sub.Name()]; ok {
want[sub.Name()] = true
}
}

for name, found := range want {
assert.True(t, found, "expected subcommand %q to be registered", name)
}
}
97 changes: 97 additions & 0 deletions cmd/pipeline/create/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2026 DataRobot, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package create

import (
"errors"
"fmt"

"github.com/datarobot/cli/internal/auth"
"github.com/datarobot/cli/internal/pipeline"
"github.com/spf13/cobra"
)

func Cmd() *cobra.Command {
var (
description string
mode string
outputFormat pipeline.OutputFormat
fromFile string
)

cmd := &cobra.Command{
Use: "create [<file>]",
Short: "Upload a Python file to create a pipeline.",
Long: `Upload a Python file containing a DataRobot pipeline (one or more tasks) to register a new pipeline.

The pipeline name is extracted from the file and used as the pipeline's resource name.
By default, output is human-readable. Use --output-format json for machine-parseable output.

The path to the Python file can be supplied either as a positional argument
or via the --from-file=<path> flag. Exactly one of the two must be provided.

Example:
dr pipeline create ./my_pipeline.py
dr pipeline create --from-file=./my_pipeline.py
dr pipeline create ./my_pipeline.py --description "First draft" --mode draft
dr pipeline create --from-file=./my_pipeline.py --output-format json`,
Args: cobra.MaximumNArgs(1),
PreRunE: auth.EnsureAuthenticatedE,
RunE: func(_ *cobra.Command, args []string) error {
if mode != "" && mode != pipeline.ModeDraft && mode != pipeline.ModeLocked {
return fmt.Errorf("invalid mode: %s (supported: draft, locked)", mode)
}

filePath, err := resolveFilePath(args, fromFile)
if err != nil {
return err
}

result, err := pipeline.CreatePipeline(filePath, description, mode)
if err != nil {
return err
}

return pipeline.RenderCreateResponse(outputFormat, *result)
},
}

cmd.Flags().StringVar(&description, "description", "", "Optional description for the pipeline")
cmd.Flags().StringVar(&mode, "mode", "", "Pipeline mode: draft (default) or locked")
cmd.Flags().StringVar(&fromFile, "from-file", "", "Path to the Python file to upload, e.g. --from-file=./my_pipeline.py (alternative to the positional argument)")
pipeline.AddOutputFlag(cmd, &outputFormat)

return cmd
}

// resolveFilePath returns the file path supplied either positionally or via
// --from-file. Exactly one of the two must be provided.
func resolveFilePath(args []string, fromFile string) (string, error) {
positional := ""
if len(args) > 0 {
positional = args[0]
}

switch {
case positional != "" && fromFile != "":
return "", errors.New("specify the file either as a positional argument or via --from-file, not both")
case positional != "":
return positional, nil
case fromFile != "":
return fromFile, nil
default:
return "", errors.New("a file path is required (positional argument or --from-file)")
}
}
Loading