Skip to content

Commit

Permalink
can specify the output format as text or json (#106)
Browse files Browse the repository at this point in the history
previously we would print the contents of the updated formatted build files to stdout as text. It can be useful for linters to be able to output as json for easier parsing especially when multiple files are updated at once.
  • Loading branch information
goddenrich authored Apr 9, 2024
1 parent 010421f commit 61ed21a
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.55.2
version: v1.57.2

60 changes: 42 additions & 18 deletions cmd/puku/puku.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ var opts = struct {
} `positional-args:"true"`
} `command:"fmt" description:"Format build files in the provided paths"`
Sync struct {
Write bool `short:"w" long:"write" description:"Whether to write the files back or just print them to stdout"`
Format string `short:"f" long:"format" choice:"json" choice:"text" default:"text" description:"output format when outputting to stdout"` //nolint
Write bool `short:"w" long:"write" description:"Whether to write the files back or just print them to stdout"`
} `command:"sync" description:"Synchronises the go.mod to the third party build file"`
Lint struct {
Args struct {
Format string `short:"f" long:"format" choice:"json" choice:"text" default:"text" description:"output format when outputting to stdout"` //nolint
Args struct {
Paths []string `positional-arg-name:"packages" description:"The packages to process"`
} `positional-args:"true"`
} `command:"lint" description:"Lint build files in the provided paths"`
Expand All @@ -44,6 +46,7 @@ var opts = struct {
} `command:"watch" description:"Watch build files in the provided paths and update them when needed"`
Migrate struct {
Write bool `short:"w" long:"write" description:"Whether to write the files back or just print them to stdout"`
Format string `short:"f" long:"format" choice:"json" choice:"text" default:"text" description:"output format when outputting to stdout"` //nolint
ThirdPartyDirs []string `long:"third_party_dir" description:"Directories to find go_module rules to migrate"`
UpdateGoMod bool `short:"g" long:"update_go_mod" description:"Update the go mod with the module(s) being migrated"`
Args struct {
Expand All @@ -52,8 +55,9 @@ var opts = struct {
} `command:"migrate" description:"Migrates from go_module to go_repo"`
Licenses struct {
Update struct {
Write bool `short:"w" long:"write" description:"Whether to write the files back or just print them to stdout"`
Args struct {
Format string `short:"f" long:"format" choice:"json" choice:"text" default:"text" description:"output format when outputting to stdout"` //nolint
Write bool `short:"w" long:"write" description:"Whether to write the files back or just print them to stdout"`
Args struct {
Paths []string `positional-arg-name:"packages" description:"The packages to process"`
} `positional-args:"true"`
} `command:"update" description:"Updates licences in the given paths"`
Expand All @@ -67,30 +71,37 @@ puku is a tool used to generate and update Go targets in build files
var log = logging.GetLogger()

var funcs = map[string]func(conf *config.Config, plzConf *please.Config, orignalWD string) int{
"fmt": func(conf *config.Config, plzConf *please.Config, orignalWD string) int {
"fmt": func(_ *config.Config, plzConf *please.Config, orignalWD string) int {
paths := work.MustExpandPaths(orignalWD, opts.Fmt.Args.Paths)
if err := generate.Update(true, plzConf, paths...); err != nil {
if err := generate.Update(plzConf, paths...); err != nil {
log.Fatalf("%v", err)
}
return 0
},
"sync": func(conf *config.Config, plzConf *please.Config, orignalWD string) int {
"sync": func(_ *config.Config, plzConf *please.Config, _ string) int {
g := graph.New(plzConf.BuildFileNames())
if err := sync.Sync(plzConf, g, opts.Sync.Write); err != nil {
log.Fatalf("%v", err)
if opts.Sync.Write {
if err := sync.Sync(plzConf, g); err != nil {
log.Fatalf("%v", err)
}
} else {
if err := sync.SyncToStdout(opts.Sync.Format, plzConf, g); err != nil {
log.Fatalf("%v", err)
}
}
return 0
},
"lint": func(conf *config.Config, plzConf *please.Config, orignalWD string) int {
"lint": func(_ *config.Config, plzConf *please.Config, orignalWD string) int {

paths := work.MustExpandPaths(orignalWD, opts.Lint.Args.Paths)
if err := generate.Update(false, plzConf, paths...); err != nil {
if err := generate.UpdateToStdout(opts.Lint.Format, plzConf, paths...); err != nil {
log.Fatalf("%v", err)
}
return 0
},
"watch": func(conf *config.Config, plzConf *please.Config, orignalWD string) int {
"watch": func(_ *config.Config, plzConf *please.Config, orignalWD string) int {
paths := work.MustExpandPaths(orignalWD, opts.Watch.Args.Paths)
if err := generate.Update(true, plzConf, paths...); err != nil {
if err := generate.Update(plzConf, paths...); err != nil {
log.Fatalf("%v", err)
}

Expand All @@ -105,15 +116,28 @@ var funcs = map[string]func(conf *config.Config, plzConf *please.Config, orignal
paths = []string{conf.GetThirdPartyDir()}
}
paths = work.MustExpandPaths(orignalWD, paths)
if err := migrate.Migrate(conf, plzConf, opts.Migrate.Write, opts.Migrate.UpdateGoMod, opts.Migrate.Args.Modules, paths); err != nil {
log.Fatalf("%v", err)
if opts.Migrate.Write {
if err := migrate.Migrate(conf, plzConf, opts.Migrate.UpdateGoMod, opts.Migrate.Args.Modules, paths); err != nil {
log.Fatalf("%v", err)
}
} else {
if err := migrate.MigrateToStdout(opts.Migrate.Format, conf, plzConf, opts.Migrate.UpdateGoMod, opts.Migrate.Args.Modules, paths); err != nil {
log.Fatalf("%v", err)
}
}
return 0
},
"update": func(conf *config.Config, plzConf *please.Config, orignalWD string) int {
"update": func(_ *config.Config, plzConf *please.Config, orignalWD string) int {
paths := work.MustExpandPaths(orignalWD, opts.Licenses.Update.Args.Paths)
if err := licences.New(proxy.New(proxy.DefaultURL), graph.New(plzConf.BuildFileNames())).Update(paths, opts.Licenses.Update.Write); err != nil {
log.Fatalf("%v", err)
l := licences.New(proxy.New(proxy.DefaultURL), graph.New(plzConf.BuildFileNames()))
if opts.Licenses.Update.Write {
if err := l.Update(paths); err != nil {
log.Fatalf("%v", err)
}
} else {
if err := l.UpdateToStdout(opts.Licenses.Update.Format, paths); err != nil {
log.Fatalf("%v", err)
}
}
return 0
},
Expand Down
34 changes: 20 additions & 14 deletions generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,28 +70,24 @@ func newUpdater(conf *please.Config) *updater {
return newUpdaterWithGraph(g, conf)
}

func Update(write bool, plzConf *please.Config, paths ...string) error {
func Update(plzConf *please.Config, paths ...string) error {
u := newUpdater(plzConf)

conf, err := config.ReadConfig(".")
if err != nil {
if err := u.update(paths...); err != nil {
return err
}
u.paths = paths

if err := u.readAllModules(conf); err != nil {
return fmt.Errorf("failed to read third party rules: %v", err)
}
return u.graph.FormatFiles()
}

if err := u.update(conf); err != nil {
func UpdateToStdout(format string, plzConf *please.Config, paths ...string) error {
u := newUpdater(plzConf)
if err := u.update(paths...); err != nil {
return err
}

return u.graph.FormatFiles(write, os.Stdout)
return u.graph.FormatFilesWithWriter(os.Stdout, format)
}

func (u *updater) readAllModules(conf *config.Config) error {
return filepath.WalkDir(conf.GetThirdPartyDir(), func(path string, info fs.DirEntry, err error) error {
return filepath.WalkDir(conf.GetThirdPartyDir(), func(path string, info fs.DirEntry, _ error) error {
for _, buildFileName := range u.plzConf.BuildFileNames() {
if info.Name() == buildFileName {
file, err := u.graph.LoadFile(filepath.Dir(path))
Expand Down Expand Up @@ -144,7 +140,17 @@ func (u *updater) readModules(file *build.File) error {
}

// update loops through the provided paths, updating and creating any build rules it finds.
func (u *updater) update(conf *config.Config) error {
func (u *updater) update(paths ...string) error {
conf, err := config.ReadConfig(".")
if err != nil {
return err
}
u.paths = paths

if err := u.readAllModules(conf); err != nil {
return fmt.Errorf("failed to read third party rules: %v", err)
}

for _, path := range u.paths {
conf, err := config.ReadConfig(path)
if err != nil {
Expand Down
56 changes: 38 additions & 18 deletions graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package graph

import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -122,12 +123,24 @@ func (g *Graph) loadFile(path string) (*build.File, error) {
return build.ParseBuild(validFilename, nil)
}

func (g *Graph) FormatFiles(write bool, out io.Writer) error {
func (g *Graph) FormatFilesWithWriter(out io.Writer, format string) error {
if err := g.ensureVisibilities(); err != nil {
return err
}
for _, file := range g.files {
if err := saveAndFormatBuildFile(file, write, out); err != nil {
if err := writeFormattedBuildFile(file, out, format); err != nil {
return err
}
}
return nil
}

func (g *Graph) FormatFiles() error {
if err := g.ensureVisibilities(); err != nil {
return err
}
for _, file := range g.files {
if err := saveFormattedBuildFile(file); err != nil {
return err
}
}
Expand Down Expand Up @@ -218,24 +231,11 @@ func checkVisibility(target labels.Label, visibilities []string) bool {
return false
}

func saveAndFormatBuildFile(buildFile *build.File, write bool, out io.Writer) error {
func writeFormattedBuildFile(buildFile *build.File, out io.Writer, format string) error {
if len(buildFile.Stmt) == 0 {
return nil
}

if write {
f, err := os.Create(buildFile.Path)
if err != nil {
return err
}
defer f.Close()

_, err = f.Write(build.FormatWithoutRewriting(buildFile))
return err
}

target := build.FormatWithoutRewriting(buildFile)

actual, err := os.ReadFile(buildFile.Path)
if err != nil {
if !os.IsNotExist(err) {
Expand All @@ -245,9 +245,29 @@ func saveAndFormatBuildFile(buildFile *build.File, write bool, out io.Writer) er
}

if !bytes.Equal(target, actual) {
_, err := out.Write(target)
switch format {
case "text":
_, err := out.Write(target)
return err
case "json":
e := json.NewEncoder(out)
return e.Encode(struct{ Path, Content string }{Path: buildFile.Path, Content: string(target)})
}
}
return nil
}

func saveFormattedBuildFile(buildFile *build.File) error {
if len(buildFile.Stmt) == 0 {
return nil
}

f, err := os.Create(buildFile.Path)
if err != nil {
return err
}
defer f.Close()

return nil
_, err = f.Write(build.FormatWithoutRewriting(buildFile))
return err
}
2 changes: 1 addition & 1 deletion graph/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ go_library(
})

bs := new(bytes.Buffer)
err = g.FormatFiles(false, bs)
err = g.FormatFilesWithWriter(bs, "text")
require.NoError(t, err)

fooT := edit.FindTargetByName(g.files["foo"], "foo")
Expand Down
19 changes: 16 additions & 3 deletions licences/licences.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,21 @@ func getLicences(modPaths []string) (map[string][]string, error) {
return ret, nil
}

func (l *Licenses) Update(paths []string, write bool) error {
func (l *Licenses) Update(paths []string) error {
if err := l.update(paths); err != nil {
return err
}
return l.graph.FormatFiles()
}

func (l *Licenses) UpdateToStdout(format string, paths []string) error {
if err := l.update(paths); err != nil {
return err
}
return l.graph.FormatFilesWithWriter(os.Stdout, format)
}

func (l *Licenses) update(paths []string) error {
var mods []string
rules := make(map[string]*build.Rule)

Expand Down Expand Up @@ -115,8 +129,7 @@ func (l *Licenses) Update(paths []string, write bool) error {
rules[mod].SetAttr("licences", edit.NewStringList(license))
}
}

return l.graph.FormatFiles(write, os.Stdout)
return nil
}

func (l *Licenses) Get(mod, ver string) ([]string, error) {
Expand Down
2 changes: 1 addition & 1 deletion licences/licences_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ go_repo(
graph: g,
}

err = l.Update([]string{"third_party/go"}, false)
err = l.UpdateToStdout("text", []string{"third_party/go"})
require.NoError(t, err)

testify := edit.FindTargetByName(thirdPartFile, "testify")
Expand Down
24 changes: 19 additions & 5 deletions migrate/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,32 @@ type migrator struct {
licences *licences.Licenses
}

func Migrate(conf *config.Config, plzConf *please.Config, write, updateGoMod bool, modules, paths []string) error {
func newMigrator(plzConf *please.Config, conf *config.Config) *migrator {
g := graph.New(plzConf.BuildFileNames())
m := &migrator{
return &migrator{
plzConf: plzConf,
graph: g,
thirdPartyFolder: conf.GetThirdPartyDir(),
moduleRules: map[string]*moduleParts{},
licences: licences.New(proxy.New(proxy.DefaultURL), g),
existingRepoRules: map[string]*build.Rule{},
}
}

func Migrate(conf *config.Config, plzConf *please.Config, updateGoMod bool, modules, paths []string) error {
m := newMigrator(plzConf, conf)
if err := m.migrate(modules, paths, updateGoMod); err != nil {
return err
}
return m.graph.FormatFiles()
}

return m.migrate(modules, paths, write, updateGoMod)
func MigrateToStdout(format string, conf *config.Config, plzConf *please.Config, updateGoMod bool, modules, paths []string) error { //nolint
m := newMigrator(plzConf, conf)
if err := m.migrate(modules, paths, updateGoMod); err != nil {
return err
}
return m.graph.FormatFilesWithWriter(os.Stdout, format)
}

// pkgRule represents the rule expr in a pkg
Expand Down Expand Up @@ -125,7 +139,7 @@ func binaryAlias(module, thirdPartyDir string, part *pkgRule) (*build.Rule, erro
return rule, nil
}

func (m *migrator) migrate(modules, paths []string, write, updateGoMod bool) error {
func (m *migrator) migrate(modules, paths []string, updateGoMod bool) error {
// Read all the BUILD files under the provided paths to find go_module and go_mod_download rules
for _, path := range paths {
f, err := m.graph.LoadFile(path)
Expand All @@ -144,7 +158,7 @@ func (m *migrator) migrate(modules, paths []string, write, updateGoMod bool) err
if err := m.replaceRulesForModules(updateGoMod, modules); err != nil {
return err
}
return m.graph.FormatFiles(write, os.Stdout)
return nil
}

// replaceRulesForModules takes a list of modules and replaces those modules and their dependencies
Expand Down
Loading

0 comments on commit 61ed21a

Please sign in to comment.