class

Krikri::LocalExecutor

Inherits Reference < Object

Constants

DRAIN_GRACE_PERIOD = 200.milliseconds

How long to keep draining stdout/stderr after the process itself has already exited, before giving up on a pipe that hasn't reached EOF.

A shell command shaped like sleep N && daemon & backgrounds a shell that blocks in its own wait() on daemon (a trailing & backgrounds the whole &&-list, and nohup only suppresses SIGHUP - it doesn't exempt a child from its parent's wait()), so if daemon never exits, neither does that shell, and the pipe it's still holding open never reaches EOF. Waiting unconditionally for EOF on our pipes would hang this call forever even though the actual process we spawned already finished - so once the process itself has exited, any pipe still not at EOF gets this much grace before being force-closed and abandoned (a real command's own output is already fully written into the pipe's kernel buffer by the time it exits, so this window only matters for draining that tail, plus the rare writer still running in the background that we've chosen not to wait for).

LEADING_ENV_ASSIGNMENT = /\A[A-Za-z_][A-Za-z0-9_]*=/

A leading NAME=value token (no metacharacters of its own, so the regex above misses it) is shell env-assignment syntax, not part of argv[0] - DEBIAN_FRONTEND=noninteractive apt-get install ... (apt.cr's own real command, no export/; involved) would otherwise get argv-split with "DEBIAN_FRONTEND=noninteractive" as the executable name and crash with ENOENT. Only checked at the start of the string - = inside a later argument (--opt=value) is completely ordinary and not env-assignment syntax.

LEADING_SHELL_BUILTIN = /\A\s*(?:command|type|hash|cd|export|unset|set|alias|unalias|ulimit|umask|exec|eval|source|read|wait|exit|return|shift|trap|builtin|local|declare|typeset|let|\.)(?:\s|\z)/

A command whose first word is a shell BUILTIN with no binary of its own (command -v snap, type apt-get, hash -r, ...) has no metacharacter either, so the argv fast path would exec a file named "command" and fail with ENOENT - plugins probing for a tool with a bare command -v X reported the tool missing on every host. Only the builtins that exist solely as builtins are listed (true, false, test and echo also ship as binaries).

SHELL_METACHARACTERS = /[|<>&;`$*?\[\]{}~\\\n]/

Any of these anywhere in the command string means it actually needs shell semantics (pipes, redirection, substitution, globbing, home-dir expansion, escaping, sequencing) - Ansible's own local/ssh connection plugins make the same call (_low_level_execute_command's executable handling). Absent all of these, splitting into argv and exec'ing directly is behaviorally identical to bash -c but skips forking a whole extra shell process per command.

Class methods

copy_file(src : String, dest : String) : Nil

Copy file locally

Source
dir_exists?(path : String) : Bool

Check if directory exists

Source
exec(command : String, force_shell : Bool = false, env : Hash(String, String) | Nil = nil, raise_spawn_errors : Bool = false) : NamedTuple(exit_code: Int32, stdout: String, stderr: String)

env carries the task's environment: (BasePlugin#remote_exec), applied through the child process's own environment the way Ansible hands the dict to subprocess - never through a string prefix in the command itself, which would put the values in the shell's argv where any local user's ps can read them. Crystal merges a non-nil env over this process's inherited environment (nil values unset), which is exactly the overlay the old export K='V'; prefix produced; with env present the shell path is always taken, because the argv fast path resolves argv[0] with execvp against the PARENT's PATH while a environment: PATH: ... override must govern the lookup (the shell the child became resolves with its own, overridden PATH - the same semantics the export prefix had).

Source
file_exists?(path : String) : Bool

Check if file exists

Source