8000 [#925] feat: Support nix shell by yottahmd · Pull Request #951 · dagu-org/dagu · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[#925] feat: Support nix shell #951

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related email 8000 s.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions internal/digraph/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@ func buildStep(ctx BuildContext, def stepDef) (*Step, error) {
Name: def.Name,
Description: def.Description,
Shell: def.Shell,
ShellPackages: def.Packages,
Script: def.Script,
Stdout: def.Stdout,
Stderr: def.Stderr,
Expand Down
3 changes: 3 additions & 0 deletions internal/digraph/spec.go → internal/digraph/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ type stepDef struct {
Command any
// Shell is the shell to run the command. Default is `$SHELL` or `sh`.
Shell string
// Packages is the list of packages to install.
// This is used only when the shell is `nix-shell`.
Packages []string
// Script is the script to run.
Script string
// Stdout is the file to write the stdout.
Expand Down
61 changes: 58 additions & 3 deletions internal/digraph/executor/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ type commandConfig struct {
Script string
ShellCommand string
ShellCommandArgs string
ShellPackages []string
Stdout io.Writer
Stderr io.Writer
}
Expand All @@ -108,16 +109,28 @@ func (cfg *commandConfig) newCmd(ctx context.Context, scriptFile string) (*exec.
var cmd *exec.Cmd
switch {
case cfg.Command != "" && scriptFile != "":
cmd = exec.CommandContext(cfg.Ctx, cfg.Command, append(cfg.Args, scriptFile)...) // nolint: gosec
builder := &shellCommandBuilder{
Command: cfg.Command,
Args: cfg.Args,
ShellCommand: cfg.ShellCommand,
ShellCommandArgs: cfg.ShellCommandArgs,
Packages: cfg.ShellPackages,
Script: scriptFile,
}
cmd = builder.Build(ctx)

case cfg.ShellCommand != "" && scriptFile != "":
// If script is provided ignore the shell command args

cmd = exec.CommandContext(cfg.Ctx, cfg.ShellCommand, scriptFile) // nolint: gosec

case cfg.ShellCommand != "" && cfg.ShellCommandArgs != "":
// nolint: gosec
cmd = exec.CommandContext(cfg.Ctx, cfg.ShellCommand, "-c", cfg.ShellCommandArgs)
builder := &shellCommandBuilder{
ShellCommand: cfg.ShellCommand,
ShellCommandArgs: cfg.ShellCommandArgs,
Packages: cfg.ShellPackages,
}
cmd = builder.Build(ctx)

default:
cmd = createDirectCommand(cfg.Ctx, cfg.Command, cfg.Args, scriptFile)
Expand Down Expand Up @@ -154,6 +167,42 @@ func exitCodeFromError(err error) int {
return exitCode
}

type shellCommandBuilder struct {
Command string
Args []string
ShellCommand string
ShellCommandArgs string
Packages []string
Script string
}

func (b *shellCommandBuilder) Build(ctx context.Context) *exec.Cmd {
switch b.ShellCommand {
case "nix-shell":
var args []string

// If the shell command is nix-shell, we need to pass the packages as arguments
for _, pkg := range b.Packages {
args = append(args, "-p", pkg)
}
args = append(args, "--pure", "--run")

if b.Command != "" && b.Script != "" {
return exec.CommandContext(ctx, b.Command, append(args, b.Script)...) // nolint: gosec
}

// Construct the command with the shell command and the packages
return exec.CommandContext(ctx, b.ShellCommand, append(args, b.ShellCommandArgs)...) // nolint: gosec
}

if b.Command != "" && b.Script != "" {
return exec.CommandContext(ctx, b.Command, append(b.Args, b.Script)...) // nolint: gosec
}

// nolint: gosec
return exec.CommandContext(ctx, b.ShellCommand, "-c", b.ShellCommandArgs)
}

func newCommand(ctx context.Context, step digraph.Step) (Executor, error) {
if len(step.Dir) > 0 && !fileutil.FileExists(step.Dir) {
return nil, fmt.Errorf("directory does not exist: %s", step.Dir)
Expand All @@ -179,6 +228,7 @@ func createCommandConfig(ctx context.Context, step digraph.Step) (*commandConfig
Script: step.Script,
ShellCommand: shellCommand,
ShellCommandArgs: shellCmdArgs,
ShellPackages: step.ShellPackages,
}, nil
}

Expand All @@ -199,6 +249,11 @@ func setupScript(_ context.Context, step digraph.Step) (string, error) {
return "", fmt.Errorf("failed to sync script file: %w", err)
}

// Add execute permissions to the script file
if err = os.Chmod(file.Name(), 0755); err != nil { // nolint: gosec
return "", fmt.Errorf("failed to set execute permissions on script file: %w", err)
}

return file.Name(), nil
}

Expand Down
2 changes: 2 additions & 0 deletions internal/digraph/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type Step struct {
Description string `json:"description,omitempty"`
// Shell is the shell program to execute the command. This is optional.
Shell string `json:"shell,omitempty"`
// ShellPackages is the list of packages to install. This is used only when the shell is `nix-shell`.
ShellPackages []string `json:"shellPackages,omitempty"`
// Dir is the working directory for the step.
Dir string `json:"dir,omitempty"`
// ExecutorConfig contains the configuration for the executor.
Expand Down
7 changes: 7 additions & 0 deletions schemas/dag.schema.json
75B8
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,13 @@
"type": "string",
"description": "Specific shell to use for executing the command. Defaults to $SHELL or sh if not specified."
},
"packages": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of packages to install before executing the step. Useful for ensuring dependencies are available. It's only available when the shell is nix-shell."
},
"script": {
"type": "string",
"description": "Multi-line script content that will be executed. Gets piped into the command if specified, otherwise uses default shell."
Expand Down
0