From 1d479733bcbeb630557cd07f721d4510e989934c Mon Sep 17 00:00:00 2001 From: Flu0r1ne Date: Sun, 19 Sep 2021 00:27:55 -0500 Subject: Add list command, copy for bash command, and ability to run non-directory commands anywhere --- adapters/bash/adapter.go | 32 ++++++++++++++-- adapters/bash/config.go | 1 + cmd/planr/main.go | 30 +++++++++------ cmd/planr/sub/cli.go | 14 +++++++ cmd/planr/sub/evaluate.go | 28 +++++++++++--- cmd/planr/sub/list.go | 13 +++++++ copy.go | 96 +++++++++++++++++++++++++++++++++++++++++++++++ runner.go | 13 ++++++- version.go | 2 +- 9 files changed, 206 insertions(+), 23 deletions(-) create mode 100644 cmd/planr/sub/list.go create mode 100644 copy.go diff --git a/adapters/bash/adapter.go b/adapters/bash/adapter.go index 8c7713c..cfa2032 100644 --- a/adapters/bash/adapter.go +++ b/adapters/bash/adapter.go @@ -3,13 +3,14 @@ package bash import ( "context" "errors" + "fmt" "log" "os" "os/exec" "path" "strings" "time" - "fmt" + "io/ioutil" "golang.flu0r1ne.net/planr" ) @@ -42,11 +43,34 @@ func (a *Adapter) Init(ctx planr.PipelineContext) { a.buildDir = ctx.AdapterDir } +func createTmpDir(dir string) string { + name, err := ioutil.TempDir(dir, "tmpbuild.*") + + if err != nil { + log.Fatal(err) + } + + return name +} + +func removeTmpDir(dir string) { + if err := os.RemoveAll(dir); err != nil { + log.Fatal(err) + } +} + func (adapter Adapter) Build(tcs []planr.TestCase) { } -func executeScriptedTest(builddir, testdir string, tc planr.TestCase) planr.TestResult { +func executeScriptedTest(builddir, testdir, srcdir string, tc planr.TestCase) planr.TestResult { cfg := tc.AdapterConfig().(*Config) + tmpdir := createTmpDir(builddir) + defer removeTmpDir(tmpdir) + + if err := planr.RecursiveCopyDir(srcdir, tmpdir); err != nil { + log.Fatalf("Could not copy sources: %v", err) + } + timeout := time.Duration(cfg.Timeout) * time.Millisecond ctx, cancel := context.WithTimeout(context.Background(), timeout) @@ -60,7 +84,7 @@ func executeScriptedTest(builddir, testdir string, tc planr.TestCase) planr.Test cmd := exec.CommandContext(ctx, "bash", path) - cmd.Dir = builddir + cmd.Dir = tmpdir if out, err := cmd.CombinedOutput(); err != nil { result.Status = planr.RUNTIME_FAILURE @@ -91,7 +115,7 @@ func (adapter Adapter) Evaluate(tcs []planr.TestCase) [] planr.TestResult { c := make(chan planr.TestResult, 0) for i := range tcs { go func(i int) { - c <- executeScriptedTest(adapter.buildDir, adapter.dirs.Tests(), tcs[i]) + c <- executeScriptedTest(adapter.buildDir, adapter.dirs.Tests(), adapter.dirs.Src(), tcs[i]) }(i) } diff --git a/adapters/bash/config.go b/adapters/bash/config.go index aaa405a..7e6ca3b 100644 --- a/adapters/bash/config.go +++ b/adapters/bash/config.go @@ -33,6 +33,7 @@ func (c *Config) finalize(path string) { if c.Timeout == 0 { c.Timeout = DEFAULT_TIMEOUT; } + } func finalizeConfigs(tcs []planr.TestCase) { diff --git a/cmd/planr/main.go b/cmd/planr/main.go index 2243203..e2ed19f 100644 --- a/cmd/planr/main.go +++ b/cmd/planr/main.go @@ -80,29 +80,37 @@ func main() { dieUsage() } + subcommand := flag.Arg(0) + subargs := flag.Args()[1:] + + switch subcommand { + case "version": + fmt.Printf("%s\n", planr.VERSION) + os.Exit(0) + case "help", "-h", "-help", "--help": + printUsage(os.Stdout) + os.Exit(0) + case "build", "evaluate", "eval", "clean", "config", "list": + break + default: + fmt.Fprintf(os.Stderr, "unrecognized command %s\n", subcommand) + dieUsage() + } + runner := getConfiguredRunner() cfg := planr.DecodeConfig(runner.ConfigDir()) - subcommand := flag.Arg(0) - subargs := flag.Args()[1:] - switch subcommand { - case "version": - fmt.Printf("%s\n", planr.VERSION) case "build": sub.Build(runner, subargs, cfg) case "evaluate", "eval": sub.Evaluate(runner, subargs, cfg) + case "list": + sub.List(runner, subargs, cfg) case "clean": sub.Clean(runner, subargs) case "config": sub.Config(runner, subargs) - case "help", "-h", "-help", "--help": - printUsage(os.Stdout) - default: - fmt.Fprintf(os.Stderr, "unrecognized command %s\n", subcommand) - dieUsage() } - } diff --git a/cmd/planr/sub/cli.go b/cmd/planr/sub/cli.go index 5defa96..9e1099c 100644 --- a/cmd/planr/sub/cli.go +++ b/cmd/planr/sub/cli.go @@ -82,6 +82,20 @@ func (opt PrintOpts) HasFlag(flag PrintOpts) bool { return (opt & flag) == flag } +func tcPrint(tc planr.TestCase) { + title := "" + + if tc.Config.Title != "" { + title = tc.Config.Title + } else { + title = tc.Cname + } + + col_title.Println(title) + pprintLabeled("id", tc.Cname) + fmt.Println() +} + func tcPprint(tr planr.TestResult, opt PrintOpts) { tcStatusLine(tr) diff --git a/cmd/planr/sub/evaluate.go b/cmd/planr/sub/evaluate.go index 4f5e4e1..3f1718f 100644 --- a/cmd/planr/sub/evaluate.go +++ b/cmd/planr/sub/evaluate.go @@ -2,9 +2,10 @@ package sub import ( "encoding/json" + "flag" "fmt" + "io/ioutil" "log" - "flag" "golang.flu0r1ne.net/planr" ) @@ -30,26 +31,39 @@ func prettyPrint(results gradingResults, verbose, summarize bool) { } } -func jsonPrint(results gradingResults) { +func jsonPrint(results gradingResults, file string) { res, err := json.Marshal(results) if err != nil { log.Fatalf("Error printing JSON: %v\n", err) } - fmt.Println(string(res)) + if file == "" { + fmt.Println(string(res)) + return + } + + + if err := ioutil.WriteFile(file, res, 0664); err != nil { + log.Fatalf("Could not write JSON file %v", err) + } } func Evaluate(runner planr.Runner, params []string, cfg *planr.Config) { f := flag.NewFlagSet("evaluate", flag.ExitOnError) jsonOutput := f.Bool("json", false, "print json output") + jsonFile := f.String("json_to_file", "", "export json to a file") extra := f.Bool("extra", false, "print extra grading information") dieIncompatibleVersion(cfg) f.Parse(params) + if *jsonFile != "" && *jsonOutput { + log.Fatalf("-json and -json_to_file are mutually exclusive") + } + tcs := runner.CollectCases() // Filter those tests which patch IDs in params @@ -80,9 +94,11 @@ func Evaluate(runner planr.Runner, params []string, cfg *planr.Config) { Score: planr.Score(trs), } - if *jsonOutput { - jsonPrint(results) - } else { + if *jsonOutput || *jsonFile != "" { + jsonPrint(results, *jsonFile) + } + + if ! *jsonOutput { prettyPrint(results, *extra, summarizeScore) } } diff --git a/cmd/planr/sub/list.go b/cmd/planr/sub/list.go new file mode 100644 index 0000000..911a7e6 --- /dev/null +++ b/cmd/planr/sub/list.go @@ -0,0 +1,13 @@ +package sub + +import ( + "golang.flu0r1ne.net/planr" +) + +func List(runner planr.Runner, params []string, cfg * planr.Config) { + tcs := runner.CollectCases() + + for _, tc := range tcs { + tcPrint(tc) + } +} diff --git a/copy.go b/copy.go new file mode 100644 index 0000000..72d9635 --- /dev/null +++ b/copy.go @@ -0,0 +1,96 @@ +package planr + +import ( + "fmt" + "os" + "path/filepath" + "io" +) + + +func Exists(filePath string) bool { + if _, err := os.Stat(filePath); os.IsNotExist(err) { + return false + } + + return true +} + +func CreateIfNotExists(dir string, perm os.FileMode) error { + if Exists(dir) { + return nil + } + + if err := os.MkdirAll(dir, perm); err != nil { + return fmt.Errorf("failed to create directory: '%s', error: '%s'", dir, err.Error()) + } + + return nil +} + +func CopySymlink(src, dst string, perm os.FileMode) error { + link, err := os.Readlink(src) + if err != nil { + return err + } + + if err := os.Chmod(src, perm); err != nil { + return err + } + + return os.Symlink(link, dst) +} + +func Copy(srcFile, dstFile string, perm os.FileMode) error { + src, err := os.Open(srcFile) + if err != nil { + return err + } + + defer src.Close() + + dst, err := os.OpenFile(dstFile, os.O_CREATE | os.O_WRONLY | os.O_TRUNC, perm) + if err != nil { + return err + } + + defer dst.Close() + + _, err = io.Copy(dst, src) + if err != nil { + return err + } + + return nil +} + + +func RecursiveCopyDir(source, destination string) error { + err := filepath.Walk(source, func(path string, info os.FileInfo, err error) error { + relPath, err := filepath.Rel(source, path) + + if err != nil { + return err + } + + dst := filepath.Join(destination, relPath) + src := filepath.Join(source, relPath) + + mode := info.Mode() + + if relPath == "" { + return nil + } + + switch info.Mode() & os.ModeType { + case os.ModeDir: + return CreateIfNotExists(dst, mode) + case os.ModeSymlink: + return CopySymlink(src, dst, mode) + default: + return Copy(src, dst, mode) + } + }); + + return err +} diff --git a/runner.go b/runner.go index 2f86d2e..11fceab 100644 --- a/runner.go +++ b/runner.go @@ -92,6 +92,10 @@ func (r Runner) CollectCases() []TestCase { } func (r Runner) Build(tcs []TestCase) { + wd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } if !directoryExists(r.dirs.Build()) { r.dirs.MkBuild() @@ -107,9 +111,16 @@ func (r Runner) Build(tcs []TestCase) { adapter.Build(cases) } + + safeCd(wd) } func (r Runner) Evaluate(tcs []TestCase) []TestResult { + wd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + testSets := r.groupByAdapter(tcs) results := make([]TestResult, 0) @@ -131,7 +142,7 @@ func (r Runner) Evaluate(tcs []TestCase) []TestResult { sort.Sort(ByReadIdx(results)) - safeCd(r.dirs.Config()) + safeCd(wd) return results } diff --git a/version.go b/version.go index 72a80a2..907dd02 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,3 @@ package planr -const VERSION = "0.1.5" +const VERSION = "0.1.6" -- cgit v1.2.3