Skip to content

Commit

Permalink
feat: add deprecator
Browse files Browse the repository at this point in the history
This utility adds deprecation comments to all exported types in a
given module. It's intended to be used by Boxo maintainers when moving
repos into Boxo.

For example, if your module is "github.com/ipfs/go-unixfs", then
checkout that repo and, in its root, run:

deprecator --path github.com/ipfs/boxo/ipld/unixfs

Then on the func "github.com/ipfs/go-unixfs/mod.NewDagModifier", the
deprecator will add the following comment:

// Deprecated: use github.com/ipfs/boxo/ipld/unixfs/mod.NewDagModifier
  • Loading branch information
guseggert committed May 12, 2023
1 parent 33e3f0c commit 9c9d9aa
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 0 deletions.
17 changes: 17 additions & 0 deletions cmd/deprecator/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module github.com/ipfs/boxo/cmd/deprecator

go 1.19

require (
github.com/dave/dst v0.27.2
github.com/urfave/cli/v2 v2.25.3
)

require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/tools v0.1.12 // indirect
)
18 changes: 18 additions & 0 deletions cmd/deprecator/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/dave/dst v0.27.2 h1:4Y5VFTkhGLC1oddtNwuxxe36pnyLxMFXT51FOzH8Ekc=
github.com/dave/dst v0.27.2/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc=
github.com/dave/jennifer v1.5.0 h1:HmgPN93bVDpkQyYbqhCHj5QlgvUkvEOzMyEvKLgCRrg=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/urfave/cli/v2 v2.25.3 h1:VJkt6wvEBOoSjPFQvOkv6iWIrsJyCrKGtCtxXWwmGeY=
github.com/urfave/cli/v2 v2.25.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
182 changes: 182 additions & 0 deletions cmd/deprecator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"go/parser"
"go/token"
"io"
"io/fs"
"log"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/dave/dst"
"github.com/dave/dst/decorator"
"github.com/urfave/cli/v2"
)

func main() {
app := &cli.App{
Name: "deprecator",
Usage: "Adds deprecation comments to all exported types in the module, with pointers to a new location. You should run this in your module root.",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "path",
Usage: "the new package path that the deprecated message should point users to",
Required: true,
},
},
Action: func(ctx *cli.Context) error {
newPkgPath := ctx.String("path")
wd, err := os.Getwd()
if err != nil {
return fmt.Errorf("getting working dir: %w", err)
}
fileToPackage, err := buildFileToPackage(wd)
if err != nil {
panic(err)
}

modPath, err := getModulePath(wd)
if err != nil {
panic(err)
}

fset := token.NewFileSet()
return filepath.Walk(wd, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() || filepath.Ext(path) != ".go" {
return nil
}

addComment := func(name string, decs *dst.Decorations) {
oldPkg := fileToPackage[path]
newPkg := strings.Replace(oldPkg, modPath, newPkgPath, 1)
newSym := newPkg + "." + name
comment := fmt.Sprintf("// Deprecated: use %s", newSym)
if len(decs.All()) > 0 {
decs.Append("//")
}
decs.Append(comment)
}

file, err := decorator.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
panic(err)
}

if _, ok := fileToPackage[path]; !ok {
// this happens in the case of e.g. test files, which we want to skip
return nil
}

dst.Inspect(file, func(n dst.Node) bool {
switch x := n.(type) {
case *dst.GenDecl:
if x.Tok == token.CONST || x.Tok == token.VAR || x.Tok == token.TYPE {
for _, spec := range x.Specs {
switch s := spec.(type) {
case *dst.ValueSpec:
// if parenthesized, put a comment above each exported type in the group
if x.Lparen {
for _, name := range s.Names {
if !name.IsExported() {
continue
}
addComment(name.Name, &s.Decs.Start)
}
} else {
name := s.Names[0]
if !name.IsExported() {
continue
}
addComment(name.Name, &x.Decs.Start)
}
case *dst.TypeSpec:
name := s.Name
if !name.IsExported() {
continue
}
addComment(name.Name, &x.Decs.Start)
}
}
}
case *dst.FuncDecl:
// don't add notices to methods
if x.Name.IsExported() && x.Recv == nil {
addComment(x.Name.Name, &x.Decs.Start)
}
}
return true
})
outFile, err := os.Create(path)
if err != nil {
panic(err)
}
defer outFile.Close()
err = decorator.Fprint(outFile, file)
if err != nil {
panic(err)
}

return nil
})
},
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}

type pkg struct {
Dir string
ImportPath string
GoFiles []string
}

func getModulePath(dir string) (string, error) {
cmd := exec.Command("go", "list", "-m")
cmd.Dir = dir
stdout := &bytes.Buffer{}
cmd.Stdout = stdout
err := cmd.Run()
if err != nil {
return "", err
}
return strings.TrimSpace(stdout.String()), nil
}

func buildFileToPackage(dir string) (map[string]string, error) {
cmd := exec.Command("go", "list", "-json", "./...")
cmd.Dir = dir
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
cmd.Stdout = stdout
cmd.Stderr = stderr
err := cmd.Run()
if err != nil {
return nil, err
}
dec := json.NewDecoder(stdout)
fileToPackage := map[string]string{}
for {
var p pkg
err := dec.Decode(&p)
if err == io.EOF {
return fileToPackage, nil
}
if err != nil {
return nil, err
}
for _, f := range p.GoFiles {
fileToPackage[filepath.Join(p.Dir, f)] = p.ImportPath
}
}
}

0 comments on commit 9c9d9aa

Please sign in to comment.