From 26ecbecaae78d9e2469e5717f18aac4a7bfd1c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20Cor=C3=A9?= Date: Tue, 21 Mar 2023 09:33:38 +0100 Subject: [PATCH] Add --dir arg to list a specific location (#26) * Add --dir arg to list a specific location - Add new 'dir' flag. Default to current directory - Add unit tests for flags. Cannot use --dir with --merge. - Fix a bug in git-log when running --merge from outside of the git repo * Rename 'chrooted' to 'pathContains' * Add test for listing from outisde repo * Rename pathContains to isRoot --- cli/agnosticv.go | 95 ++++++++++++++++++++++++++++++--------- cli/agnosticv_test.go | 4 +- cli/git.go | 5 ++- cli/includes.go | 2 +- cli/main_test.go | 29 ++++++++++++ tools/test_new_version.sh | 22 ++++++--- 6 files changed, 126 insertions(+), 31 deletions(-) diff --git a/cli/agnosticv.go b/cli/agnosticv.go index cdb8abb..b2a98e5 100644 --- a/cli/agnosticv.go +++ b/cli/agnosticv.go @@ -35,6 +35,7 @@ var validateFlag bool var versionFlag bool var gitFlag bool var outputFlag string +var dirFlag string // Build info var Version = "development" @@ -64,6 +65,7 @@ func parseFlags(args []string, output io.Writer) controlFlow { flags := flag.NewFlagSet(args[0], flag.ContinueOnError) flags.SetOutput(output) flags.BoolVar(&listFlag, "list", false, "List all the catalog items present in current directory.") + flags.StringVar(&dirFlag, "dir", "", "Directory to use as dir when listing catalog items. Default = current directory.") flags.BoolVar(&validateFlag, "validate", true, "Validate variables against schemas present in .schemas directory.") flags.Var(&relatedFlags, "related", `Use with --list only. Filter output and display only related catalog items. A catalog item is related to FILE if: @@ -142,6 +144,35 @@ need this parameter unless your files are not in a git repository, or if you wan outputFlag = "yaml" } + if mergeFlag != "" && dirFlag != "" { + fmt.Fprintln(output, "You cannot use --merge and --dir simultaneously.") + return controlFlow{true, 2} + } + if dirFlag != "" { + // Ensure dir is a directory + fi, err := os.Stat(dirFlag) + if err != nil { + fmt.Fprintln(output, "Error:", err) + return controlFlow{true, 1} + } + if !fi.IsDir() { + fmt.Fprintln(output, "Error:", dirFlag, "is not a directory") + return controlFlow{true, 2} + } + dirFlag, err = filepath.Abs(dirFlag) + if err != nil { + fmt.Fprintln(output, "Error:", err) + return controlFlow{true, 1} + } + } else { + // Default to current directory + var err error + if dirFlag, err = os.Getwd(); err != nil { + fmt.Fprintln(output, "Error:", err) + return controlFlow{true, 1} + } + } + if rootFlag != "" { if !fileExists(rootFlag) { log.Fatalf("File %s does not exist", rootFlag) @@ -151,22 +182,41 @@ need this parameter unless your files are not in a git repository, or if you wan } else { // init rootFlag by discovering depending on other flags if listFlag { - // use current workdir - var workdir string - if wd, errWorkDir := os.Getwd(); errWorkDir == nil { - workdir = wd + if dirFlag != "" { + rootFlag = findRoot(dirFlag) } else { - logErr.Fatal(errWorkDir) + // use current workdir to find root + var workdir string + if wd, errWorkDir := os.Getwd(); errWorkDir == nil { + workdir = wd + } else { + logErr.Fatal(errWorkDir) + } + rootFlag = findRoot(workdir) } - rootFlag = findRoot(workdir) - } else if mergeFlag != "" { // Use root of the file to merge rootFlag = findRoot(mergeFlag) } } + // Validate rootflag is compatible with other flags + if listFlag { + // Ensure listing will be done inside root + absDir, err := filepath.Abs(dirFlag) + if err != nil { + fmt.Fprintln(output, "Error:", err) + return controlFlow{true, 1} + } + + if !isRoot(rootFlag, absDir) { + fmt.Fprintln(output, "Error: --dir", dirFlag, "is not inside --root", rootFlag) + return controlFlow{true, 2} + } + } + + // Do not perform git operations when listing if listFlag { gitFlag = false @@ -200,7 +250,7 @@ func isPathCatalogItem(root, p string) bool { return false } - if !chrooted(root, p) { + if !isRoot(root, p) { return false } @@ -335,6 +385,7 @@ func findCatalogItems(workdir string, hasFlags []string, relatedFlags []string, if rootFlag == "" { rootFlag = findRoot(workdir) } + err := filepath.Walk(".", func(p string, info os.FileInfo, err error) error { if err != nil { logErr.Printf("%q: %v\n", p, err) @@ -475,10 +526,10 @@ func parentDir(path string) string { return filepath.Dir(currentDir) } -// chrooted function compares strings and returns true if -// path is chrooted in root. +// isRoot function compares strings and returns true if +// path is contained in root. // It's a poor man's chroot -func chrooted(root string, path string) bool { +func isRoot(root string, path string) bool { if root == path { return true } @@ -540,7 +591,7 @@ func nextCommonFile(position string) string { for _, commonFile := range validCommonFileNames { if path.Base(position) == commonFile { // If parent is out of chroot, stop - if !chrooted(rootFlag, parentDir(position)) { + if !isRoot(rootFlag, parentDir(position)) { logDebug.Println("parent of", position, ",", parentDir(position), "is out of chroot", rootFlag) return "" @@ -574,7 +625,7 @@ func nextCommonFile(position string) string { } // If parent is out of chroot, stop - if !chrooted(rootFlag, parentDir(position)) { + if !isRoot(rootFlag, parentDir(position)) { logDebug.Println("parent of", position, ",", parentDir(position), "is out of chroot", rootFlag) return "" @@ -592,20 +643,12 @@ func main() { initConf(rootFlag) initMergeStrategies() - // Save current work directory - var workDir string - if wd, errWorkDir := os.Getwd(); errWorkDir == nil { - workDir = wd - } else { - logErr.Fatal(errWorkDir) - } - if len(schemas) == 0 { initSchemaList() } if listFlag { - catalogItems, err := findCatalogItems(workDir, hasFlags, relatedFlags, orRelatedFlags) + catalogItems, err := findCatalogItems(dirFlag, hasFlags, relatedFlags, orRelatedFlags) if err != nil { logErr.Printf("error walking the path %q: %v\n", ".", err) @@ -629,6 +672,14 @@ func main() { } if mergeFlag != "" { + // Get current work directory + var workDir string + if wd, errWorkDir := os.Getwd(); errWorkDir == nil { + workDir = wd + } else { + logErr.Fatal(errWorkDir) + } + merged, mergeList, err := mergeVars(mergeFlag, mergeStrategies) if err != nil { logErr.Fatal(err) diff --git a/cli/agnosticv_test.go b/cli/agnosticv_test.go index a5de40c..c9a73ed 100644 --- a/cli/agnosticv_test.go +++ b/cli/agnosticv_test.go @@ -37,7 +37,7 @@ func TestParentDir(t *testing.T) { } } -func TestChrooted(t *testing.T) { +func TestPathContains(t *testing.T) { testCases := []struct { root string path string @@ -101,7 +101,7 @@ func TestChrooted(t *testing.T) { } for _, tc := range testCases { - if tc.result != chrooted(tc.root, tc.path) { + if tc.result != isRoot(tc.root, tc.path) { t.Error(tc.root, tc.path, tc.result) } } diff --git a/cli/git.go b/cli/git.go index 2666c81..b67613e 100644 --- a/cli/git.go +++ b/cli/git.go @@ -75,12 +75,15 @@ func findMostRecentCommitCmd(p string, related []Include) *object.Commit { cmd := exec.Command("git", args...) logDebug.Println(cmd) + // Run git log from the directory of the catalog item + cmd.Dir = filepath.Dir(p) + var out bytes.Buffer cmd.Stdout = &out err := cmd.Run() if err != nil { - logErr.Fatal(err) + logErr.Fatal("git log error:", err) } repo, err := git.PlainOpenWithOptions(p, &git.PlainOpenOptions{DetectDotGit: true}) diff --git a/cli/includes.go b/cli/includes.go index 2ee39af..6e7acbc 100644 --- a/cli/includes.go +++ b/cli/includes.go @@ -205,7 +205,7 @@ func resolvePath(root string, includePath string, contextFile string) (string, e } result := filepath.Join(path.Dir(contextFile), filepath.Clean(includePath)) - if !chrooted(root, result) { + if !isRoot(root, result) { return "", ErrorIncludeOutOfChroot } return result, nil diff --git a/cli/main_test.go b/cli/main_test.go index 19c2191..3f65352 100644 --- a/cli/main_test.go +++ b/cli/main_test.go @@ -96,6 +96,35 @@ func TestFlag(t *testing.T) { description: "-output and -merge, yaml output", result: controlFlow{false, 0}, }, + { + args: []string{"agnosticv", + "--merge", "fixtures/test/BABYLON_EMPTY_CONFIG/dev.yaml", + "--dir", "/tmp/repo"}, + description: "-dir and -merge should fail", + result: controlFlow{true, 2}, + }, + { + args: []string{"agnosticv", + "--list", + "--dir", "/tmp/doesntexist"}, + description: "-list and -dir with a dir that doesn't exist", + result: controlFlow{true, 1}, + }, + { + args: []string{"agnosticv", + "--list", + "--dir", "fixtures"}, + description: "-list and -dir", + result: controlFlow{false, 0}, + }, + { + args: []string{"agnosticv", + "--list", + "--root", "fixtures", + "--dir", "."}, + description: "-dir is outside -root", + result: controlFlow{true, 2}, + }, } for _, tc := range testCases { diff --git a/tools/test_new_version.sh b/tools/test_new_version.sh index 00492b3..45aa6b1 100755 --- a/tools/test_new_version.sh +++ b/tools/test_new_version.sh @@ -22,7 +22,7 @@ cd ${1} cli1="${2}" cli2="${3}" -echo -n "Testing listing ......................." +echo -n "listing ......................." diff -u <($cli1 --list) <($cli2 --list) if [ $? != 0 ]; then echo >&2 "Listing is not the same" @@ -32,7 +32,7 @@ echo OK for dir in *; do if [ -d $dir ]; then - echo -n "Testing listing in ${dir}......................." + printf "%-80s" "listing in ${dir}" cd "${dir}" diff -u <($cli1 --list) <($cli2 --list) if [ $? != 0 ]; then @@ -40,6 +40,18 @@ for dir in *; do exit 2 fi echo OK + + if $cli1 --help | grep '\-dir string' -q; then + cd /tmp + printf "%-80s" "from oustide ${dir} with --dir" + diff -u <($cli1 --list --dir "${dir}") <($cli2 --list --dir "${dir}") + if [ $? != 0 ]; then + echo >&2 "Listing is not the same" + exit 2 + fi + echo OK + fi + cd ${1} fi done @@ -50,7 +62,7 @@ if [ $? != 0 ]; then exit 2 fi for ci in $($cli1 --list); do - echo -n "testing merge $ci ......................." + printf "%-80s" "merge $ci" diff -u <($cli1 --merge $ci) <($cli2 --merge $ci) > /dev/null if [ $? != 0 ]; then @@ -61,7 +73,7 @@ for ci in $($cli1 --list); do echo YES - echo -n "testing merge $ci JSON ......................." + printf "%-80s" "merge $ci JSON" if ! $cli2 --merge $ci --output json | jq . > /dev/null; then echo NO else @@ -70,7 +82,7 @@ for ci in $($cli1 --list); do done for fil in $(find -name common.yaml) $(find -name account.yaml) $(find includes -type f); do - echo -n "testing related files $fil ......................." + printf "%-80s" "related files $fil" diff -u <($cli1 --list --related $fil) <($cli2 --list --related $fil) > /dev/null if [ $? != 0 ]; then echo "NO"