Skip to content

Commit

Permalink
feat: include runfiles, and env passing across commands
Browse files Browse the repository at this point in the history
  • Loading branch information
nxtcoder17 committed Oct 6, 2024
1 parent b6e473a commit 06abe13
Show file tree
Hide file tree
Showing 13 changed files with 267 additions and 78 deletions.
72 changes: 51 additions & 21 deletions cmd/run/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
Expand All @@ -15,9 +14,18 @@ import (
"github.com/urfave/cli/v3"
)

var Version string = "0.0.1"
var (
Version string = "0.0.1"
runfileNames []string = []string{
"Runfile",
"Runfile.yml",
"Runfile.yaml",
}
)

func main() {
logger := logging.NewSlogLogger(logging.SlogOptions{})

cmd := cli.Command{
Name: "run",
Version: Version,
Expand Down Expand Up @@ -65,20 +73,26 @@ func main() {
for k := range runfile.Tasks {
fmt.Fprintf(c.Root().Writer, "%s\n", k)
}

m, err := runfile.ParseIncludes()
if err != nil {
panic(err)
}

for k, v := range m {
for tn := range v.Runfile.Tasks {
fmt.Fprintf(c.Root().Writer, "%s:%s\n", k, tn)
}
}
},
Action: func(ctx context.Context, c *cli.Command) error {
parallel := c.Bool("parallel")
watch := c.Bool("watch")
debug := c.Bool("debug")

logger := logging.NewSlogLogger(logging.SlogOptions{
ShowCaller: debug,
ShowDebugLogs: debug,
SetAsDefaultLogger: true,
})

if c.Args().Len() < 1 {
return fmt.Errorf("missing argument, at least one argument is required")
if c.NArg() == 0 {
c.Command("help").Run(ctx, nil)
return nil
}

runfilePath, err := locateRunfile(c)
Expand Down Expand Up @@ -124,6 +138,12 @@ func main() {
return fmt.Errorf("parallel and watch can't be set together")
}

logger := logging.NewSlogLogger(logging.SlogOptions{
ShowCaller: debug,
ShowDebugLogs: debug,
SetAsDefaultLogger: true,
})

return rf.Run(runfile.NewContext(ctx, logger), runfile.RunArgs{
Tasks: args,
ExecuteInParallel: parallel,
Expand All @@ -146,29 +166,39 @@ func main() {
}()

if err := cmd.Run(ctx, os.Args); err != nil {
log.Fatal(err)
logger.Error(err.Error())
os.Exit(1)
}
}

func locateRunfile(c *cli.Command) (string, error) {
var runfilePath string
switch {
case c.IsSet("file"):
runfilePath = c.String("file")
return c.String("file"), nil
default:
dir, err := os.Getwd()
if err != nil {
return "", err
}
for {
_, err := os.Stat(filepath.Join(dir, "Runfile"))
if err != nil {
dir = filepath.Dir(dir)
continue

oldDir := ""

for oldDir != dir {
for _, fn := range runfileNames {
if _, err := os.Stat(filepath.Join(dir, fn)); err != nil {
if !os.IsNotExist(err) {
return "", err
}
continue
}

return filepath.Join(dir, fn), nil
}
runfilePath = filepath.Join(dir, "Runfile")
break

oldDir = dir
dir = filepath.Dir(dir)
}

return "", fmt.Errorf("failed to locate your nearest Runfile")
}
return runfilePath, nil
}
10 changes: 10 additions & 0 deletions docs/includes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## Includes one runfile into another

```yaml
includes:
file1:
runfile: ./run1/Runfile
# dir: ../
run2:
runfile: ./run2/Runfile
```
17 changes: 17 additions & 0 deletions examples/Runfile → examples/Runfile.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# vim: set ft=yaml:
version: 0.0.1

includes:
file1:
runfile: ./run1/Runfile
# dir: ../
run2:
runfile: ./run2/Runfile

tasks:
cook:
env:
Expand All @@ -10,6 +17,11 @@ tasks:
sh: echo -n "hello"
k4:
required: true
k5:
default:
# value: "this is default value"
# sh: echo this should be the default value
gotmpl: len "asdfadf"
dotenv:
- ../.secrets/env
cmd:
Expand All @@ -20,6 +32,7 @@ tasks:
# - echo "value of k3 is '$k3'"
# - echo "value of key_id (from .dotenv) is '$key_id', ${#key_id}"
- echo "hello from cook"
- echo "k5 is $k5"

clean:
name: clean
Expand Down Expand Up @@ -47,6 +60,10 @@ tasks:
- console.log("hello from laundry")
eat:
name: eat
env:
item: asdfasfd
requires:
- gotmpl: gt (len "sdfsdfas") 5
cmd:
- echo "eat"
sleep:
Expand Down
6 changes: 6 additions & 0 deletions examples/run1/Runfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 0.0.1

tasks:
echo:
cmd:
- echo "hello from run1"
6 changes: 6 additions & 0 deletions examples/run2/Runfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 0.0.1

tasks:
echo:
cmd:
- echo "hello from run2"
19 changes: 19 additions & 0 deletions pkg/functions/maps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package functions

func MapMerge[K comparable, V any](items ...map[K]V) map[K]V {
result := make(map[K]V)
for i := range items {
for k, v := range items[i] {
result[k] = v
}
}
return result
}

func MapKeys[K comparable, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
9 changes: 9 additions & 0 deletions pkg/runfile/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
)

type Context struct {
Verbose bool

Task string
Runfile string

Expand All @@ -24,6 +26,13 @@ func (c Context) WithErr(err error) Context {
}

func (c Context) ToString() string {
if !c.Verbose {
if c.message != nil {
return fmt.Sprintf("[%s] %s, got err: %v", c.Task, *c.message, c.err)
}
return fmt.Sprintf("[%s] got err: %v", c.Task, c.err)
}

m := map[string]string{
"task": c.Task,
"runfile": c.Runfile,
Expand Down
66 changes: 55 additions & 11 deletions pkg/runfile/run.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package runfile

import (
"context"
"fmt"
"io"
"log/slog"
"os"
Expand All @@ -22,7 +22,7 @@ type cmdArgs struct {
stderr io.Writer
}

func createCommand(ctx context.Context, args cmdArgs) *exec.Cmd {
func createCommand(ctx Context, args cmdArgs) *exec.Cmd {
if args.shell == nil {
args.shell = []string{"sh", "-c"}
}
Expand All @@ -40,31 +40,55 @@ func createCommand(ctx context.Context, args cmdArgs) *exec.Cmd {
cargs := append(args.shell[1:], args.cmd)
c := exec.CommandContext(ctx, shell, cargs...)
c.Dir = args.workingDir
c.Env = args.env
c.Env = append(os.Environ(), args.env...)
c.Stdout = args.stdout
c.Stderr = args.stderr
return c
}

func (rf *Runfile) runTask(ctx context.Context, taskName string) error {
pt, err := ParseTask(ctx, rf, taskName)
type runTaskArgs struct {
taskName string
envOverrides map[string]string
}

func (rf *Runfile) runTask(ctx Context, args runTaskArgs) error {
logger := ctx.Logger.With("runfile", rf.attrs.RunfilePath, "task", args.taskName, "env:overrides", args.envOverrides)
logger.Debug("running task")
task, ok := rf.Tasks[args.taskName]
if !ok {
return errors.TaskNotFound{Context: errors.Context{Runfile: rf.attrs.RunfilePath, Task: args.taskName}}
}

task.Name = args.taskName
if task.Env == nil {
task.Env = make(EnvVar)
}
for k, v := range args.envOverrides {
task.Env[k] = v
}
pt, err := ParseTask(ctx, rf, &task)
if err != nil {
return err
}

// slog.Default().Info("parsing", "task", pt)
// envVars := append(pt.Environ, args.envOverrides...)
ctx.Debug("debugging env", "pt.environ", pt.Env, "overrides", args.envOverrides, "task", args.taskName)

for _, command := range pt.Commands {
if command.Run != "" {
if err := rf.runTask(ctx, command.Run); err != nil {
if err := rf.runTask(ctx, runTaskArgs{
taskName: command.Run,
envOverrides: pt.Env,
// envOverrides: append(pt.Environ, args.envOverrides...),
}); err != nil {
return err
}
continue
}

cmd := createCommand(ctx, cmdArgs{
shell: pt.Shell,
env: pt.Environ,
env: ToEnviron(pt.Env),
cmd: command.Command,
workingDir: pt.WorkingDir,
})
Expand All @@ -85,7 +109,21 @@ type RunArgs struct {
}

func (rf *Runfile) Run(ctx Context, args RunArgs) error {
ctx.Debug("run", "tasks", args.Tasks)
includes, err := rf.ParseIncludes()
if err != nil {
return err
}

for _, taskName := range args.Tasks {
for k, v := range includes {
for tn := range v.Runfile.Tasks {
if taskName == fmt.Sprintf("%s:%s", k, tn) {
return v.Runfile.runTask(ctx, runTaskArgs{taskName: tn})
}
}
}

task, ok := rf.Tasks[taskName]
if !ok {
return errors.TaskNotFound{Context: errors.Context{
Expand All @@ -96,17 +134,23 @@ func (rf *Runfile) Run(ctx Context, args RunArgs) error {

// INFO: adding parsed KVs as environments to the specified tasks
for k, v := range args.KVs {
if task.Env == nil {
task.Env = EnvVar{}
}
task.Env[k] = v
}

rf.Tasks[taskName] = task
}

if args.ExecuteInParallel {
slog.Default().Debug("running in parallel mode", "tasks", args.Tasks)
g := new(errgroup.Group)

for _, tn := range args.Tasks {
for _, _tn := range args.Tasks {
tn := _tn
g.Go(func() error {
return rf.runTask(ctx, tn)
return rf.runTask(ctx, runTaskArgs{taskName: tn})
})
}

Expand All @@ -119,7 +163,7 @@ func (rf *Runfile) Run(ctx Context, args RunArgs) error {
}

for _, tn := range args.Tasks {
if err := rf.runTask(ctx, tn); err != nil {
if err := rf.runTask(ctx, runTaskArgs{taskName: tn}); err != nil {
return err
}
}
Expand Down
Loading

0 comments on commit 06abe13

Please sign in to comment.