Skip to content

[CMPT-6969] feat(pipeline): add dr pipeline clone command - #719

Open
sunny2get wants to merge 1 commit into
datarobot-oss:mainfrom
sunny2get:sunny/CMPT-6969-pipeline-clone
Open

[CMPT-6969] feat(pipeline): add dr pipeline clone command#719
sunny2get wants to merge 1 commit into
datarobot-oss:mainfrom
sunny2get:sunny/CMPT-6969-pipeline-clone

Conversation

@sunny2get

@sunny2get sunny2get commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds dr pipeline clone <pipeline-id> [--name], mirroring the new pipelines-api endpoint POST /api/v2/pipelines/{id}/clone. Clones a pipeline into a new draft, copying its source code, description, latest input params, and assigned image. The name defaults to Clone of <source name> and is overridable via --name; the server auto-suffixes on collision so repeated clones never fail.

Draft — depends on the pipelines-api clone endpoint (datarobot/pipelines-api#234). Merge/release after that lands.

Changes

  • internal/pipeline: ClonePipeline(pipelineID, name)POST .../clone with a JSON body (name omitempty), mirroring LockPipeline.
  • cmd/pipeline/clone: cobra subcommand following the write-command pattern — returns fmt.Errorf("clone pipeline: %w", err) and renders via RenderCreateResponse. No read-command friendly-404 handler, so JSON stdout is never corrupted (consistent with [CFX-7097] Fix 404 error handlers corrupting JSON stdout #707).
  • Tests: internal/pipeline/clone_test.go (httptest: default name omits body key, --name override, 404) + cmd/pipeline/clone/cmd_test.go (JSON/human render, invalid-output + blank-name rejection).
  • docs/commands/pipeline.md: clone row.

Test

go build ./..., go vet, gofmt clean; go test ./cmd/pipeline/clone/... ./internal/pipeline/... passes.

🤖 Generated with Claude Code


Note

Low Risk
Additive CLI and API client for a new write endpoint; no changes to auth or existing pipeline flows beyond registering a subcommand.

Overview
Adds dr pipeline clone <pipeline-id> so users can duplicate an existing pipeline into a new draft via POST /api/v2/pipelines/{id}/clone, copying source, description, latest input, and image as described in the command help.

The client sends an optional --name in JSON (name omitted when unset so the API can default to Clone of <source>); whitespace-only --name is rejected locally. Success output reuses RenderCreateResponse (same shape as create/lock). The subcommand is registered under dr pipeline, includes auth, output format, and telemetry, and is documented in the pipeline command table.

internal/pipeline.ClonePipeline mirrors LockPipeline’s JSON POST pattern. Tests cover default vs override name, 404 handling, rendering, and flag validation.

Reviewed by Cursor Bugbot for commit cc6277a. Configure here.

@datarobot-pr-review-router

Copy link
Copy Markdown

🎫 Jira: CMPT-6969 — feat(pipelines): add clone-pipeline endpoint

@datarobot-pr-review-router

Copy link
Copy Markdown

👋 Thanks so much for contributing to the DataRobot community!

As a quick heads-up on how our team handles reviews: if you're still iterating on
this code or running tests, please feel free to convert this to a Draft PR.
We rely heavily on GitHub Drafts to give contributors a stress-free sandbox to experiment!

Once everything is finalized and you're ready for feedback, just click "Ready for review"
and the maintainers will be notified to jump in. (And if this PR is already 100% ready
to go, no action needed, we'll take a look soon!)

@sunny2get
sunny2get force-pushed the sunny/CMPT-6969-pipeline-clone branch from 456f377 to cc6277a Compare July 28, 2026 14:38
@sunny2get
sunny2get marked this pull request as ready for review July 28, 2026 14:39
@sunny2get
sunny2get requested a review from a team as a code owner July 28, 2026 14:39
@sunny2get
sunny2get requested review from ajalon1 and Copilot July 28, 2026 14:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new dr pipeline clone <pipeline-id> [--name] write-style subcommand that calls the pipelines API clone endpoint to duplicate an existing pipeline into a new draft.

Changes:

  • Added internal/pipeline.ClonePipeline() to POST /api/v2/pipelines/{id}/clone with an optional JSON name.
  • Added cmd/pipeline/clone cobra subcommand with --name validation, output-format support, and telemetry properties.
  • Added client + command rendering tests, and updated pipeline command documentation.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
internal/pipeline/pipeline.go Adds the API client method for the clone endpoint.
internal/pipeline/clone_test.go Adds httptest coverage for request/response behavior (default vs overridden name, 404).
cmd/pipeline/cmd.go Registers the new pipeline clone subcommand.
cmd/pipeline/clone/cmd.go Implements the cobra command, flag validation, rendering, and telemetry wiring.
cmd/pipeline/clone/cmd_test.go Adds tests for JSON/human rendering and flag/output-format validation.
docs/commands/pipeline.md Documents the new dr pipeline clone command in the pipeline command table.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/pipeline/pipeline.go Outdated
Comment on lines +273 to +276
endpoint, err := config.GetEndpointURL("/api/v2/pipelines/" + pipelineID + "/clone")
if err != nil {
return nil, err
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied — ClonePipeline now url.PathEscapes the pipeline id before building the endpoint (mirrors internal/workload/workload.go).

Comment on lines +42 to +60
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{
"id": "p-2",
"name": "Clone of wf",
"mode": "draft",
"taskNames": ["e1"],
"createdAt": "2026-04-29T10:00:00Z"
}`))
}))

defer srv.Close()

installEndpoint(t, srv.URL)

got, err := ClonePipeline("p-1", "")
require.NoError(t, err)
assert.Equal(t, "p-2", got.PipelineID)
assert.Equal(t, "Clone of wf", got.Name)
assert.Equal(t, "draft", got.Mode)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A clone always returns a fresh draft, so the server sends no version/status (same as POST /pipelines) — the fixture omits them deliberately. I made that explicit: the test now asserts got.Version == 0 / got.Status == "". Adding version:1/status:READY would misrepresent the clone as a locked v1.

Comment on lines +30 to +38
func sample() pipeline.CreateResponse {
return pipeline.CreateResponse{
PipelineID: "p-2",
Name: "Clone of wf",
Mode: "draft",
TaskNames: []string{"e1", "e2"},
CreatedAt: time.Date(2026, 4, 30, 10, 0, 0, 0, time.UTC),
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same rationale — the clone is a draft, so Version/Status are intentionally zero-valued (documented with a comment on the fixture). Populating them would portray the clone as locked, which it never is.

@sunny2get
sunny2get force-pushed the sunny/CMPT-6969-pipeline-clone branch from cc6277a to a346d69 Compare July 28, 2026 14:50
Add `dr pipeline clone <pipeline-id> [--name]` mirroring the new
pipelines-api POST /api/v2/pipelines/{id}/clone endpoint. Clones a
pipeline into a new draft (source, description, latest input params,
assigned image). Name defaults to "Clone of <source name>" and is
overridable via --name; the server auto-suffixes on collision.

- internal/pipeline: ClonePipeline (POST .../clone, JSON body with
  omitempty name) mirroring LockPipeline.
- cmd/pipeline/clone: cobra command following the write-command pattern
  (returns fmt.Errorf on error, renders via RenderCreateResponse); JSON
  output stays uncorrupted (no read-command friendly-404 handler), per
  datarobot-oss#707.
- Tests: internal httptest coverage (default/override/404) + command
  render + flag-validation tests.
- docs/commands/pipeline.md: clone row, plus the fan-out `run task`
  node-id error-table update (404/409 causes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sunny2get
sunny2get force-pushed the sunny/CMPT-6969-pipeline-clone branch from a346d69 to 5bbee15 Compare July 28, 2026 14:56
@sunny2get

Copy link
Copy Markdown
Contributor Author

/approve-smoke-tests

@github-actions

Copy link
Copy Markdown
Contributor

🔐 Fork PR smoke tests triggered by @sunny2get

⚠️ Security Notice: This will run tests with access to repository secrets.

What happens next:

  1. Security scans will run automatically (Trivy, gosec)
  2. If security scans pass, smoke tests will run
  3. Results will be posted as PR comments

⚠️ Important: Review the PR code carefully before approving!

@github-actions

Copy link
Copy Markdown
Contributor

🔐 Fork smoke tests started by maintainer

⏳ Security scans passed. Running smoke tests...

Commit: 5bbee150f9d7a8a2555cf493c5f00f3bca0ffc7a
View run

@github-actions

Copy link
Copy Markdown
Contributor

All smoke tests passed! (Fork PR)

✅ Security Scan: success
✅ Linux: success
✅ Windows: success

View run details

@datarobot-pr-review-router

Copy link
Copy Markdown

Code Ownership

Compute Services

  • cmd/pipeline/clone/cmd.go
  • cmd/pipeline/clone/cmd_test.go
  • cmd/pipeline/cmd.go
  • docs/commands/pipeline.md
  • internal/pipeline/clone_test.go
  • internal/pipeline/pipeline.go

Review requested from the teams above. Labels will be removed automatically upon approval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants