Skip to content

Conversation

@sahalbelam
Copy link

@sahalbelam sahalbelam commented Feb 4, 2026

now dags can only be ingest with the name.
user has to define ALT_DAGS_DIR variable in base.yaml
Spec is still working. #1609

Summary by CodeRabbit

  • New Features
    • DAG run enqueue now supports filename-based references: Users can trigger DAG execution by specifying an existing YAML DAG filename, in addition to providing inline specifications. The enqueue endpoint accepts either a spec or filename, but not both.

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review
📝 Walkthrough

Walkthrough

This PR extends the enqueue DAG run endpoint to accept either an inline YAML specification or a filename reference pointing to an existing YAML file. The request body is modified with a new optional filename field and a oneOf constraint, while backend logic handles reading and validating YAML files from disk when a filename is provided.

Changes

Cohort / File(s) Summary
API Generation
api/v2/api.gen.go
Updated EnqueueDAGRunFromSpecJSONBody struct: Spec field changed to pointer type, new optional Filename field added, and union json.RawMessage field added to support union-based unmarshalling.
OpenAPI Specification
api/v2/api.yaml
Modified /dags/{fileName}/enqueue endpoint to accept either inline spec or filename via new optional filename field and oneOf constraint instead of requiring spec alone.
Backend Implementation
internal/service/frontend/api/v2/dagruns.go
Added filename-based YAML loading in EnqueueDAGRunFromSpec: validates mutual exclusivity of spec/filename, reads YAML files from disk with 1 MiB size limit, resolves paths via ALT_DAGS_DIR environment variable, and loads final DAG from either inline or file-based content.
Test Updates
internal/service/frontend/api/v2/dags_test.go
Updated test to pass Spec as pointer type (\&spec) instead of value type to match new struct field signature.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Handler as API Handler
    participant FileSystem as File System
    participant Config as Base Config
    participant DAGLoader as DAG Loader

    Client->>Handler: POST /dags/{fileName}/enqueue<br/>(with Filename)
    Handler->>Handler: Validate: not both spec & filename
    Handler->>Config: Read base config
    Config-->>Handler: Config data
    Handler->>Handler: Resolve ALT_DAGS_DIR from env
    Handler->>FileSystem: Read YAML file<br/>(check .yaml/.yml extension)
    FileSystem-->>Handler: File content (≤1 MiB)
    Handler->>DAGLoader: Load DAG from file content
    DAGLoader-->>Handler: DAG object
    Handler-->>Client: 200 OK (DAG run enqueued)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ingest yaml with filename' accurately describes the main change: adding support for ingesting DAGs via filename instead of only inline spec.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/service/frontend/api/v2/dagruns.go (1)

261-265: ⚠️ Potential issue | 🟡 Minor

Audit metadata should reflect filename-based enqueue.

inline is always true, even when a filename is used, which makes audit logs inaccurate.

🧾 Suggested audit flag fix
-	detailsMap := map[string]any{
-		"dag_name":   dag.Name,
-		"dag_run_id": dagRunId,
-		"inline":     true,
-	}
+	inline := request.Body.Spec != nil
+	detailsMap := map[string]any{
+		"dag_name":   dag.Name,
+		"dag_run_id": dagRunId,
+		"inline":     inline,
+	}
+	if request.Body.Filename != nil {
+		detailsMap["filename"] = *request.Body.Filename
+	}
🤖 Fix all issues with AI agents
In `@internal/service/frontend/api/v2/dagruns.go`:
- Around line 161-166: Update the user-facing error message so it references the
actual API field name `filename` instead of “url”: change the Error returned in
the branch that checks request.Body.Spec and request.Body.Filename (the
conditional using request.Body.Spec == nil && request.Body.Filename == nil ||
request.Body.Spec != nil && request.Body.Filename != nil) to set Message to
something like "either give spec or filename; don't give both" so clients see
the correct field name.
- Around line 193-212: The current code unsafely assumes
obj["env"].([]any)[0].(string) and will panic or miss ALT_DAGS_DIR; change the
logic in the block after yaml.Unmarshal by: validate that obj["env"] exists and
is a []any, iterate over each element, assert each entry is a string, look for a
string that starts with "ALT_DAGS_DIR=" (or split on '=' and compare key),
extract the value into altDagDir when found, and only then trim the prefix and
rebuild filename; if no ALT_DAGS_DIR is found return the existing Error
response; keep using the same variables (obj, altDagDir, filename) and preserve
the yaml.Unmarshal error handling.
🧹 Nitpick comments (1)
api/v2/api.yaml (1)

1614-1622: Constrain filename to allowed basename + extension.

The backend only accepts .yaml/.yml; the schema currently allows any string. Adding a pattern reduces invalid requests and clarifies the contract.

📄 Suggested schema constraint
                 filename:
                   type: string
                   description: "Filename for the yaml file"
+                  pattern: "^[a-zA-Z0-9._-]+\\.(ya?ml)$"

Comment on lines 161 to 166
if request.Body.Spec == nil && request.Body.Filename == nil || request.Body.Spec != nil && request.Body.Filename != nil{
return nil, &Error{
HTTPStatus: http.StatusBadRequest,
Code: api.ErrorCodeBadRequest,
Message: "either give spec or url; don't give both",
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix user-facing error text to match the field name.

The error says “url” but the API uses filename, which is confusing for clients.

✏️ Suggested text fix
-			Message:    "either give spec or url; don't give both",
+			Message:    "either give spec or filename; don't give both",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if request.Body.Spec == nil && request.Body.Filename == nil || request.Body.Spec != nil && request.Body.Filename != nil{
return nil, &Error{
HTTPStatus: http.StatusBadRequest,
Code: api.ErrorCodeBadRequest,
Message: "either give spec or url; don't give both",
}
if request.Body.Spec == nil && request.Body.Filename == nil || request.Body.Spec != nil && request.Body.Filename != nil{
return nil, &Error{
HTTPStatus: http.StatusBadRequest,
Code: api.ErrorCodeBadRequest,
Message: "either give spec or filename; don't give both",
}
🤖 Prompt for AI Agents
In `@internal/service/frontend/api/v2/dagruns.go` around lines 161 - 166, Update
the user-facing error message so it references the actual API field name
`filename` instead of “url”: change the Error returned in the branch that checks
request.Body.Spec and request.Body.Filename (the conditional using
request.Body.Spec == nil && request.Body.Filename == nil || request.Body.Spec !=
nil && request.Body.Filename != nil) to set Message to something like "either
give spec or filename; don't give both" so clients see the correct field name.

Comment on lines 193 to 212
obj := make(map[string]any)
err = yaml.Unmarshal(baseData,&obj)
if err != nil {
return nil, fmt.Errorf("error unmarshalling base config: %w", err)
}

altDagDir := obj["env"].([]any)[0].(string)
fmt.Println("ALDAGDIR",altDagDir)

if strings.HasPrefix(altDagDir, "ALT_DAGS_DIR=") {
altDagDir = strings.TrimPrefix(altDagDir, "ALT_DAGS_DIR=")
filename = fmt.Sprintf("%s/%s", altDagDir, filepath.Base(filename))
} else {
return nil, &Error{
HTTPStatus: http.StatusBadRequest,
Code: api.ErrorCodeBadRequest,
Message: "Make sure ALT_DAGS_DIR is set in the config file",
}
}
const maxSpecBytes = 1 << 20 // 1 MiB (tune or reuse config)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard ALT_DAGS_DIR extraction to avoid panics and missed configs.

obj["env"].([]any)[0].(string) will panic if env is missing/empty/not a slice, and it only inspects the first entry (so ALT_DAGS_DIR can be missed). Parse the env list safely and search for the key before constructing the path.

🔧 Safer env parsing and ALT_DAGS_DIR lookup
-		obj := make(map[string]any)
-		err = yaml.Unmarshal(baseData,&obj) 
+		obj := make(map[string]any)
+		err = yaml.Unmarshal(baseData, &obj)
 		if err != nil {
 			return nil, fmt.Errorf("error unmarshalling base config: %w", err)
 		}
-
-		altDagDir := obj["env"].([]any)[0].(string)
-		fmt.Println("ALDAGDIR",altDagDir)
-
-		if strings.HasPrefix(altDagDir, "ALT_DAGS_DIR=") {
-			altDagDir = strings.TrimPrefix(altDagDir, "ALT_DAGS_DIR=")
-			filename = fmt.Sprintf("%s/%s", altDagDir, filepath.Base(filename))
-		} else {
-			return nil, &Error{
-				HTTPStatus: http.StatusBadRequest,
-				Code:       api.ErrorCodeBadRequest,
-				Message:    "Make sure ALT_DAGS_DIR is set in the config file",
-			}
-		}
+		envRaw, ok := obj["env"].([]any)
+		if !ok || len(envRaw) == 0 {
+			return nil, &Error{
+				HTTPStatus: http.StatusBadRequest,
+				Code:       api.ErrorCodeBadRequest,
+				Message:    "Make sure ALT_DAGS_DIR is set in the config file",
+			}
+		}
+		var altDagDir string
+		for _, v := range envRaw {
+			if s, ok := v.(string); ok && strings.HasPrefix(s, "ALT_DAGS_DIR=") {
+				altDagDir = strings.TrimPrefix(s, "ALT_DAGS_DIR=")
+				break
+			}
+		}
+		if altDagDir == "" {
+			return nil, &Error{
+				HTTPStatus: http.StatusBadRequest,
+				Code:       api.ErrorCodeBadRequest,
+				Message:    "Make sure ALT_DAGS_DIR is set in the config file",
+			}
+		}
+		filename = filepath.Join(altDagDir, filepath.Base(filename))
🤖 Prompt for AI Agents
In `@internal/service/frontend/api/v2/dagruns.go` around lines 193 - 212, The
current code unsafely assumes obj["env"].([]any)[0].(string) and will panic or
miss ALT_DAGS_DIR; change the logic in the block after yaml.Unmarshal by:
validate that obj["env"] exists and is a []any, iterate over each element,
assert each entry is a string, look for a string that starts with
"ALT_DAGS_DIR=" (or split on '=' and compare key), extract the value into
altDagDir when found, and only then trim the prefix and rebuild filename; if no
ALT_DAGS_DIR is found return the existing Error response; keep using the same
variables (obj, altDagDir, filename) and preserve the yaml.Unmarshal error
handling.

altDagDir := obj["env"].([]any)[0].(string)
fmt.Println("ALDAGDIR",altDagDir)

if strings.HasPrefix(altDagDir, "ALT_DAGS_DIR=") {
Copy link
Collaborator

@yottahmd yottahmd Feb 5, 2026

Choose a reason for hiding this comment

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

I'm thinking we should use ALT_DAGS_DIR to be set in SearchPaths field of DAG store.

SearchPaths []string // Additional search paths for DAG files

The fix would be adding ALT_DAGS_DIR to config package, update docs for the new environment variable, and adding ALT_DAGS_DIR value to the SearchPaths. In this case there's no need to fix this API. What do you think?

Copy link

@ghansham ghansham Feb 5, 2026

Choose a reason for hiding this comment

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

Yes that will be a much cleaner and systematic approach. Will work on that

@sahalisro-blip
Copy link
Contributor

for this, config.yaml will have one more variable altDagDir under paths: which will be used for locating the alternate dags
right @yottahmd ?

@yottahmd
Copy link
Collaborator

yottahmd commented Feb 5, 2026

@sahalisro-blip Yes, I think that's correct.

|---------------------|---------|-------------|
| `DAGU_HOME` | - | Base directory that overrides all path configurations |
| `DAGU_DAGS_DIR` | `~/.config/dagu/dags` | Directory for DAG definitions |
| `DAGU_ALT_DAGS_DIR` | - | Additional directory to search for DAG definitions |
Copy link

Choose a reason for hiding this comment

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

Please add default path

~/.config/dagu/alt_dags

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants