forked from grafana/tanka
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(tooling): import analysis (grafana#10)
Adds a tool (tk tool imports) to get a list of all files, a file transitively imports. Optionally allows to check whether a git commit changed one of those imports. BREAKING: tk debug jpath is now tk tool jpath
- Loading branch information
Showing
8 changed files
with
280 additions
and
49 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,7 +68,7 @@ func main() { | |
rootCmd.AddCommand( | ||
evalCmd(), | ||
initCmd(), | ||
debugCmd(), | ||
toolCmd(), | ||
) | ||
|
||
// completion | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/sh0rez/tanka/pkg/jpath" | ||
"github.com/sh0rez/tanka/pkg/jsonnet" | ||
) | ||
|
||
func toolCmd() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Short: "handy utilities for working with jsonnet", | ||
Use: "tool", | ||
} | ||
cmd.AddCommand(jpathCmd()) | ||
cmd.AddCommand(importsCmd()) | ||
return cmd | ||
} | ||
|
||
func jpathCmd() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Short: "print information about the jpath", | ||
Use: "jpath", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
pwd, err := os.Getwd() | ||
if err != nil { | ||
return err | ||
} | ||
path, base, root, err := jpath.Resolve(pwd) | ||
if err != nil { | ||
log.Fatalln("Resolving JPATH:", err) | ||
} | ||
fmt.Println("main:", filepath.Join(base, "main.jsonnet")) | ||
fmt.Println("rootDir:", root) | ||
fmt.Println("baseDir:", base) | ||
fmt.Println("jpath:", path) | ||
return nil | ||
}, | ||
} | ||
return cmd | ||
} | ||
|
||
func importsCmd() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "imports [file]", | ||
Short: "list all transitive imports of a file", | ||
Args: cobra.ExactArgs(1), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
var modFiles []string | ||
if cmd.Flag("check").Changed { | ||
var err error | ||
modFiles, err = gitChangedFiles(cmd.Flag("check").Value.String()) | ||
if err != nil { | ||
log.Fatalln("invoking git:", err) | ||
} | ||
} | ||
|
||
f, err := filepath.Abs(args[0]) | ||
if err != nil { | ||
log.Fatalln("Opening file:", err) | ||
} | ||
|
||
deps, err := jsonnet.TransitiveImports(f) | ||
if err != nil { | ||
log.Fatalln("resolving imports:", err) | ||
} | ||
|
||
// include main.jsonnet as well | ||
deps = append(deps, f) | ||
|
||
root, err := gitRoot() | ||
if err != nil { | ||
log.Fatalln("invoking git:", err) | ||
} | ||
if modFiles != nil { | ||
for _, m := range modFiles { | ||
mod := filepath.Join(root, m) | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
for _, dep := range deps { | ||
if mod == dep { | ||
fmt.Printf("Rebuild required. File `%s` imports `%s`, which has been changed in `%s`.\n", args[0], dep, cmd.Flag("check").Value.String()) | ||
os.Exit(16) | ||
} | ||
} | ||
} | ||
fmt.Printf("Rebuild not required, because no imported files have been changed in `%s`.\n", cmd.Flag("check").Value.String()) | ||
os.Exit(0) | ||
} | ||
|
||
s, err := json.Marshal(deps) | ||
if err != nil { | ||
log.Fatalln("Formatting:", err) | ||
} | ||
fmt.Println(string(s)) | ||
}, | ||
} | ||
|
||
cmd.Flags().StringP("check", "c", "", "git commit hash to check against") | ||
|
||
return cmd | ||
} | ||
|
||
func gitRoot() (string, error) { | ||
s, err := git("rev-parse", "--show-toplevel") | ||
return strings.TrimRight(s, "\n"), err | ||
} | ||
|
||
func gitChangedFiles(sha string) ([]string, error) { | ||
f, err := git("diff-tree", "--no-commit-id", "--name-only", "-r", sha) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return strings.Split(f, "\n"), nil | ||
} | ||
|
||
func git(argv ...string) (string, error) { | ||
cmd := exec.Command("git", argv...) | ||
cmd.Stderr = os.Stderr | ||
var buf bytes.Buffer | ||
cmd.Stdout = &buf | ||
if err := cmd.Run(); err != nil { | ||
return "", err | ||
} | ||
return buf.String(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package jsonnet | ||
|
||
import ( | ||
"path/filepath" | ||
) | ||
|
||
// TransitiveImports returns a slice with all files this file imports plus downstream imports | ||
func TransitiveImports(filename string) ([]string, error) { | ||
imports := map[string][]string{} | ||
if err := VisitImportsFile(filename, func(who, what string) error { | ||
if imports[who] == nil { | ||
imports[who] = []string{} | ||
} | ||
imports[who] = append(imports[who], what) | ||
return nil | ||
}); err != nil { | ||
return nil, err | ||
} | ||
|
||
deps := map[string]*File{} | ||
for k, v := range imports { | ||
deps[k] = &File{Imports: v} | ||
} | ||
|
||
for _, d := range deps { | ||
resolveTransitives(d, deps) | ||
} | ||
|
||
for _, d := range deps { | ||
d.Dependencies = uniqueStringSlice(d.Dependencies) | ||
} | ||
|
||
return deps[filepath.Base(filename)].Dependencies, nil | ||
} | ||
|
||
// File represents a jsonnet file that may import other files | ||
type File struct { | ||
// List of files this file imports | ||
Imports []string | ||
// Full list of transitive imports | ||
Dependencies []string | ||
} | ||
|
||
func resolveTransitives(f *File, deps map[string]*File) { | ||
// already resolved | ||
if len(f.Dependencies) != 0 { | ||
return | ||
} | ||
|
||
for _, i := range f.Imports { | ||
f.Dependencies = append(f.Dependencies, i) | ||
|
||
// import has no dependencies | ||
if deps[i] == nil { | ||
continue | ||
} | ||
|
||
// import dependencies have not yet been resolved | ||
if len(deps[i].Dependencies) == 0 { | ||
resolveTransitives(deps[i], deps) | ||
} | ||
|
||
f.Dependencies = append(f.Dependencies, deps[i].Dependencies...) | ||
} | ||
} | ||
|
||
func uniqueStringSlice(s []string) []string { | ||
seen := make(map[string]struct{}, len(s)) | ||
j := 0 | ||
for _, v := range s { | ||
if _, ok := seen[v]; ok { | ||
continue | ||
} | ||
seen[v] = struct{}{} | ||
s[j] = v | ||
j++ | ||
} | ||
return s[:j] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters