Skip to content

Commit

Permalink
Merge pull request 'feat: adds dotenv support for tasks' (#2) from fe…
Browse files Browse the repository at this point in the history
…at/dotenv-support into master

Reviewed-on: https://codeberg.org/nxtcoder17/Runfile/pulls/2
  • Loading branch information
nxtcoder17 committed Sep 16, 2024
2 parents dc024fb + 1d93202 commit f5a0bf1
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 25 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.direnv
bin/
.secrets/
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ go install github.com/nxtcoder17/runfile/cmd/run@latest
```

## Usage

### Runfile

Create a `Runfile` in the root of your project, and add tasks to it.
Expand All @@ -23,16 +23,28 @@ Create a `Runfile` in the root of your project, and add tasks to it.
- [x] Run tasks
- [x] Run tasks with Key-Value environment variables
- [x] Run tasks with dynamic environment variables (by shell execution)
- [x] Run tasks with dotenv files as their environment variables
- [ ] Running tasks in different working directory [reference](https://taskfile.dev/reference/schema/#task)
- [ ] Running tasks with watch mode
- [ ] Running tasks in parallel

### Example

```yaml
version: 0.0.1

tasks:
build:
cmd:
- go build -o bin/run ./cmd/run
test:
cmd:
- go test ./...
env:
key1: value1
key2: value2
key3:
sh: echo -n "hello"
dotenv:
- .secrets/env # load dotenv file
cmd:
- echo "value of key1 is '$key1'"
- echo "value of key2 is '$key2'"
- echo "value of key3 is '$key3'"
- echo "value of key4 is '$key4'" # assuming key4 is defined in .secrets/env
```
8 changes: 6 additions & 2 deletions Runfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ version: 0.0.1

tasks:
build:
# dir: ./cmd/run
cmd:
- |+
echo "building ..."
go build -o bin/run ./cmd/run
go build -o bin/run -ldflags="-s -w" -tags urfave_cli_no_docs cmd/run/main.go
echo "DONE"
example:
cmd:
- |+
run -f ./examples/Runfile cook
19 changes: 14 additions & 5 deletions examples/Runfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# vim: set ft=yaml:

version: 0.0.1

tasks:
Expand All @@ -8,17 +7,27 @@ tasks:
k1: v1
k2: 'f"\( asfsadfssdfas asfd $Asdfasdfa'
k3:
sh: echo "hello"
sh: echo -n "hello"
dotenv:
- .secrets/env
cmd:
- echo "cook"
- "echo k1: $k1, k2: $k2, k3: $k3"
- echo "hi hello"
- echo "value of k1 is '$k1'"
- echo "value of k2 is '$k2'"
- echo "value of k3 is '$k3'"
- echo "value of key_id (from .dotenv) is '$key_id', ${#key_id}"

clean:
name: clean
shell: ["python", "-c"]
dotenv:
- .secrets/env
cmd:
- |+
import secrets
print(secrets.token_hex(32))
import os
print(os.environ['key_id'])
# print(secrets.token_hex(32))
laundry:
name: laundry
shell: ["node", "-e"]
Expand Down
36 changes: 36 additions & 0 deletions pkg/runfile/parser.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package runfile

import (
"bufio"
"fmt"
"os"
"strconv"
"strings"

"sigs.k8s.io/yaml"
)
Expand All @@ -18,3 +22,35 @@ func ParseRunFile(file string) (*RunFile, error) {
}
return &runfile, nil
}

// parseDotEnv parses the .env file and returns a slice of strings as in os.Environ()
func parseDotEnv(files ...string) ([]string, error) {
results := make([]string, 0, 5)

for i := range files {
f, err := os.Open(files[i])
if err != nil {
return nil, err
}

s := bufio.NewScanner(f)
for s.Scan() {
s2 := strings.SplitN(s.Text(), "=", 2)
if len(s2) != 2 {
continue
}
s, _ := strconv.Unquote(string(s2[1]))

// os.Setenv(s2[0], s2[1])
os.Setenv(s2[0], s)
results = append(results, s2[0])
}
}

for i := range results {
v := os.Getenv(results[i])
results[i] = fmt.Sprintf("%s=%v", results[i], v)
}

return results, nil
}
28 changes: 16 additions & 12 deletions pkg/runfile/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

type runArgs struct {
shell []string
env map[string]string
env []string // [key=value, key=value, ...]
cmd string

stdout io.Writer
Expand Down Expand Up @@ -39,15 +39,10 @@ func runInShell(ctx context.Context, args runArgs) error {
// f.WriteString(args.cmd)
// f.Close()

environ := os.Environ()
for k, v := range args.env {
environ = append(environ, fmt.Sprintf("%s=%v", k, v))
}

// cargs := append(args.shell[1:], f.Name())
cargs := append(args.shell[1:], args.cmd)
c := exec.CommandContext(ctx, shell, cargs...)
c.Env = environ
c.Env = args.env
c.Stdout = args.stdout
c.Stderr = args.stderr
return c.Run()
Expand All @@ -59,11 +54,11 @@ func (r *RunFile) Run(ctx context.Context, taskName string) error {
return fmt.Errorf("task %s not found", taskName)
}

env := make(map[string]string, len(task.Env))
env := make([]string, len(task.Env))
for k, v := range task.Env {
switch v := v.(type) {
case string:
env[k] = v
env = append(env, fmt.Sprintf("%s=%s", k, v))
case map[string]any:
shcmd, ok := v["sh"]
if !ok {
Expand All @@ -79,22 +74,31 @@ func (r *RunFile) Run(ctx context.Context, taskName string) error {

if err := runInShell(ctx, runArgs{
shell: task.Shell,
env: env,
env: os.Environ(),
cmd: s,
stdout: value,
}); err != nil {
return err
}
env[k] = value.String()
env = append(env, fmt.Sprintf("%s=%v", k, value.String()))
default:
panic(fmt.Sprintf("env %s is not a string (%T)", k, v))
}
}

// parsing dotenv
s, err := parseDotEnv(task.DotEnv...)
if err != nil {
return err
}

// INFO: keys from task.Env will override those coming from dotenv files, when duplicated
env = append(s, env...)

for _, cmd := range task.Commands {
runInShell(ctx, runArgs{
shell: task.Shell,
env: env,
env: append(os.Environ(), env...),
cmd: cmd,
})
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/runfile/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ type RunFile struct {
}

type TaskSpec struct {
// load env vars from [.env](https://www.google.com/search?q=sample+dotenv+files&udm=2) files
DotEnv []string `json:"dotenv"`
Env map[string]any `json:"env"`
Commands []string `json:"cmd"`
Shell []string `json:"shell"`
Expand Down

0 comments on commit f5a0bf1

Please sign in to comment.