├── .github └── workflows │ ├── standard-go-test.yml │ └── standard-stale.yml ├── .gitignore ├── .goreleaser.yml ├── .goreleaser.yml.plush ├── LICENSE ├── Makefile ├── README.md ├── SHOULDERS.md ├── go.mod ├── go.sum ├── main.go ├── shoulders └── shoulders.go └── version.go /.github/workflows/standard-go-test.yml: -------------------------------------------------------------------------------- 1 | name: Standard Test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | 8 | jobs: 9 | call-standard-test: 10 | name: Test 11 | uses: gobuffalo/.github/.github/workflows/go-test.yml@v1 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /.github/workflows/standard-stale.yml: -------------------------------------------------------------------------------- 1 | name: Standard Autocloser 2 | 3 | on: 4 | schedule: 5 | - cron: "30 1 * * *" 6 | 7 | jobs: 8 | call-standard-autocloser: 9 | name: Autocloser 10 | uses: gobuffalo/.github/.github/workflows/stale.yml@v1 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | doc 4 | tmp 5 | pkg 6 | *.pid 7 | coverage 8 | coverage.data 9 | build/* 10 | *.pbxuser 11 | *.mode1v3 12 | .svn 13 | profile 14 | .console_history 15 | .sass-cache/* 16 | solr/ 17 | .jhw-cache/ 18 | jhw.* 19 | *.sublime* 20 | node_modules/ 21 | dist/ 22 | generated/ 23 | .vendor/ 24 | bin/* 25 | gin-bin 26 | .idea/ 27 | .vscode 28 | shoulders.bin 29 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Code generated by github.com/gobuffalo/release. DO NOT EDIT. 2 | # Edit .goreleaser.yml.plush instead 3 | 4 | builds: 5 | - 6 | goos: 7 | - darwin 8 | - linux 9 | - windows 10 | env: 11 | - CGO_ENABLED=0 12 | main: main.go 13 | 14 | checksum: 15 | name_template: 'checksums.txt' 16 | 17 | snapshot: 18 | name_template: "{{ .Tag }}-next" 19 | 20 | changelog: 21 | sort: asc 22 | filters: 23 | exclude: 24 | - '^docs:' 25 | - '^test:' 26 | 27 | brew: 28 | github: 29 | owner: gobuffalo 30 | name: homebrew-tap 31 | 32 | -------------------------------------------------------------------------------- /.goreleaser.yml.plush: -------------------------------------------------------------------------------- 1 | builds: 2 | - 3 | goos: 4 | - darwin 5 | - linux 6 | - windows 7 | env: 8 | - CGO_ENABLED=0 9 | main: main.go 10 | 11 | checksum: 12 | name_template: 'checksums.txt' 13 | 14 | snapshot: 15 | name_template: "{{ .Tag }}-next" 16 | 17 | changelog: 18 | sort: asc 19 | filters: 20 | exclude: 21 | - '^docs:' 22 | - '^test:' 23 | <%= if (brew) { %> 24 | brew: 25 | github: 26 | owner: gobuffalo 27 | name: homebrew-tap 28 | <% } %> 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Mark Bates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TAGS ?= "" 2 | GO_BIN ?= go 3 | 4 | install: 5 | $(GO_BIN) install -tags ${TAGS} -v ./... 6 | 7 | build: 8 | $(GO_BIN) build -v -o shoulders.bin . 9 | 10 | test: 11 | $(GO_BIN) test -race -cover -tags ${TAGS} ./... 12 | 13 | lint: 14 | go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest 15 | golangci-lint run --enable-all 16 | 17 | update: 18 | -rm go.* 19 | $(GO_BIN) mod init github.com/gobuffalo/shoulders 20 | $(GO_BIN) mod tidy 21 | make test 22 | make install 23 | shoulders -w 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shoulders 2 | 3 | Inspired by the [SHOULDERS.md](https://github.com/gobuffalo/buffalo/blob/master/SHOULDERS.md) several projects have started to create their own implementations. This tool will create a custom SHOULDERS.md file for any given Go project allowing OSS maintainers to recognize those who's OSS contributions helped them. 4 | 5 | ## Installation 6 | 7 | ```console 8 | $ go install github.com/gobuffalo/shoulders@latest 9 | ``` 10 | 11 | ## Usage 12 | 13 | When run without any flags the `shoulders` command will print the `SHOULDERS.md` to the `STDOUT`. 14 | 15 | ```console 16 | $ shoulders 17 | # github.com/gobuffalo/shoulders Stands on the Shoulders of Giants 18 | 19 | github.com/gobuffalo/shoulders does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants, this project would not be possible. Please make sure to check them out and thank them for all of their hard work. 20 | 21 | Thank you to the following **GIANTS**: 22 | 23 | * [github.com/yuin/goldmark](https://godoc.org/github.com/yuin/goldmark) 24 | * [golang.org/x/crypto](https://godoc.org/golang.org/x/crypto) 25 | * [golang.org/x/mod](https://godoc.org/golang.org/x/mod) 26 | * [golang.org/x/net](https://godoc.org/golang.org/x/net) 27 | * [golang.org/x/sync](https://godoc.org/golang.org/x/sync) 28 | * [golang.org/x/sys](https://godoc.org/golang.org/x/sys) 29 | * [golang.org/x/text](https://godoc.org/golang.org/x/text) 30 | * [golang.org/x/tools](https://godoc.org/golang.org/x/tools) 31 | * [golang.org/x/xerrors](https://godoc.org/golang.org/x/xerrors) 32 | ``` 33 | 34 | ### The `-w` (Write) Flag 35 | 36 | To write the `SHOULDERS.md` file to disk use the `-w` flag. 37 | 38 | ```console 39 | $ shoulders -w 40 | ``` 41 | 42 | ### The `-n` (Name) Flag 43 | 44 | By default the "name" of the project is the current package name. To change that use the `-n` flag. 45 | 46 | ```console 47 | $ shoulders -n shoulders 48 | # shoulders Stands on the Shoulders of Giants 49 | 50 | shoulder does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants, this project would not be possible. Please make sure to check them out and thank them for all of their hard work. 51 | 52 | <...> 53 | ``` 54 | 55 | ### The `-j` (JSON) Flag 56 | 57 | To get a JSON array of the dependencies of the project use the `-j` flag. 58 | 59 | ```console 60 | $ shoulders -j 61 | ["github.com/yuin/goldmark","golang.org/x/crypto","golang.org/x/mod","golang.org/x/net","golang.org/x/sync","golang.org/x/sys","golang.org/x/text","golang.org/x/tools","golang.org/x/xerrors"] 62 | ``` 63 | 64 | -------------------------------------------------------------------------------- /SHOULDERS.md: -------------------------------------------------------------------------------- 1 | # github.com/gobuffalo/shoulders Stands on the Shoulders of Giants 2 | 3 | github.com/gobuffalo/shoulders does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants, this project would not be possible. Please make sure to check them out and thank them for all of their hard work. 4 | 5 | Thank you to the following **GIANTS**: 6 | 7 | * [github.com/yuin/goldmark](https://godoc.org/github.com/yuin/goldmark) 8 | * [golang.org/x/crypto](https://godoc.org/golang.org/x/crypto) 9 | * [golang.org/x/mod](https://godoc.org/golang.org/x/mod) 10 | * [golang.org/x/net](https://godoc.org/golang.org/x/net) 11 | * [golang.org/x/sync](https://godoc.org/golang.org/x/sync) 12 | * [golang.org/x/sys](https://godoc.org/golang.org/x/sys) 13 | * [golang.org/x/text](https://godoc.org/golang.org/x/text) 14 | * [golang.org/x/tools](https://godoc.org/golang.org/x/tools) 15 | * [golang.org/x/xerrors](https://godoc.org/golang.org/x/xerrors) 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gobuffalo/shoulders 2 | 3 | go 1.17 4 | 5 | require golang.org/x/tools v0.1.12 6 | 7 | require ( 8 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect 9 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 2 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 3 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 4 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= 5 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 6 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 7 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 8 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 9 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 10 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 11 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 12 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 13 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 14 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 15 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= 16 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 17 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 18 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 19 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 20 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 21 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 22 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 23 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 24 | golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= 25 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 26 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 27 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "log" 9 | "os" 10 | "strings" 11 | 12 | "github.com/gobuffalo/shoulders/shoulders" 13 | ) 14 | 15 | var flags = struct { 16 | Write bool 17 | JSON bool 18 | Name string 19 | Version bool 20 | }{} 21 | 22 | func init() { 23 | flag.StringVar(&flags.Name, "n", "", "name of the project") 24 | flag.BoolVar(&flags.Write, "w", false, "write SHOULDERS.md to disk") 25 | flag.BoolVar(&flags.JSON, "j", false, "print JSON of format of the dep list") 26 | flag.BoolVar(&flags.Version, "v", false, "print version of shoulders") 27 | flag.Parse() 28 | } 29 | 30 | func main() { 31 | flag.Parse() 32 | 33 | if flags.Version { 34 | fmt.Printf("shoulders %s\n", Version) 35 | return 36 | } 37 | 38 | view, err := shoulders.New() 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | flags.Name = strings.TrimSpace(flags.Name) 44 | if len(flags.Name) > 0 { 45 | view.Name = flags.Name 46 | } 47 | 48 | if flags.JSON { 49 | deps, err := view.DepList() 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | if err := json.NewEncoder(os.Stdout).Encode(deps); err != nil { 54 | log.Fatal(err) 55 | } 56 | return 57 | } 58 | var w io.Writer = os.Stdout 59 | if flags.Write { 60 | w, err = os.Create("SHOULDERS.md") 61 | if err != nil { 62 | log.Fatal(err) 63 | } 64 | } 65 | if err := view.Write(w); err != nil { 66 | log.Fatal(err) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /shoulders/shoulders.go: -------------------------------------------------------------------------------- 1 | package shoulders 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "html/template" 8 | "io" 9 | "os/exec" 10 | "strings" 11 | "sync" 12 | 13 | "golang.org/x/tools/go/packages" 14 | ) 15 | 16 | type View struct { 17 | Name string 18 | depsOnce sync.Once 19 | depsErr error 20 | deps []string 21 | currentPkg string 22 | pkgOnce sync.Once 23 | pkgErr error 24 | } 25 | 26 | func New() (*View, error) { 27 | v := &View{} 28 | 29 | n, err := v.CurrentPkg() 30 | if err != nil { 31 | return nil, err 32 | } 33 | v.Name = n 34 | return v, nil 35 | } 36 | 37 | func (v *View) Write(w io.Writer) error { 38 | t := template.New("SHOULDERS.md") 39 | t, err := t.Parse(strings.TrimSpace(shouldersTemplate)) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | data := struct { 45 | *View 46 | Deps []string 47 | }{ 48 | View: v, 49 | } 50 | 51 | deps, err := v.DepList() 52 | if err != nil { 53 | return err 54 | } 55 | data.Deps = deps 56 | return t.Execute(w, data) 57 | } 58 | 59 | func CurrentPkg() (string, error) { 60 | v, err := New() 61 | if err != nil { 62 | return "", err 63 | } 64 | return v.CurrentPkg() 65 | } 66 | 67 | func (v *View) CurrentPkg() (string, error) { 68 | (&v.pkgOnce).Do(func() { 69 | cfg := &packages.Config{} 70 | pkgs, err := packages.Load(cfg, ".") 71 | if err != nil { 72 | v.pkgErr = err 73 | return 74 | } 75 | if packages.PrintErrors(pkgs) > 0 { 76 | v.pkgErr = fmt.Errorf("many, many errors") 77 | return 78 | } 79 | 80 | if len(pkgs) >= 1 { 81 | v.currentPkg = pkgs[0].ID 82 | } else { 83 | v.pkgErr = fmt.Errorf("could not determine current package") 84 | } 85 | }) 86 | return v.currentPkg, v.pkgErr 87 | } 88 | 89 | func (v *View) DepList() ([]string, error) { 90 | (&v.depsOnce).Do(func() { 91 | c := exec.Command("go", "env", "GOMOD") 92 | b, err := c.CombinedOutput() 93 | if err != nil { 94 | v.depsErr = err 95 | return 96 | } 97 | b = bytes.TrimSpace(b) 98 | if len(b) == 0 { 99 | v.deps, v.depsErr = v.execList("go", "list", "-f", "{{if not .Standard}}{{.ImportPath}}{{end}}", "-deps") 100 | return 101 | } 102 | v.deps, v.depsErr = v.execList("go", "list", "-m", "-f", "{{.Path}}", "all") 103 | }) 104 | return v.deps, v.depsErr 105 | } 106 | 107 | func (v *View) execList(name string, args ...string) ([]string, error) { 108 | pkg, err := v.CurrentPkg() 109 | if err != nil { 110 | return nil, err 111 | } 112 | 113 | c := exec.Command(name, args...) 114 | r, err := c.StdoutPipe() 115 | if err != nil { 116 | return nil, err 117 | } 118 | defer r.Close() 119 | 120 | wg := &sync.WaitGroup{} 121 | var list []string 122 | wg.Add(1) 123 | go func() { 124 | defer wg.Done() 125 | scan := bufio.NewScanner(r) 126 | for scan.Scan() { 127 | l := strings.TrimSpace(scan.Text()) 128 | if len(l) == 0 { 129 | continue 130 | } 131 | if strings.Contains(l, "/internal/") { 132 | continue 133 | } 134 | if strings.HasPrefix(l, pkg) { 135 | continue 136 | } 137 | list = append(list, l) 138 | } 139 | }() 140 | 141 | if err := c.Run(); err != nil { 142 | return nil, err 143 | } 144 | 145 | wg.Wait() 146 | return list, nil 147 | } 148 | 149 | func DepList() ([]string, error) { 150 | v, err := New() 151 | if err != nil { 152 | return nil, err 153 | } 154 | return v.DepList() 155 | } 156 | 157 | var shouldersTemplate = ` 158 | # {{.Name}} Stands on the Shoulders of Giants 159 | 160 | {{.Name}} does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants, this project would not be possible. Please make sure to check them out and thank them for all of their hard work. 161 | 162 | Thank you to the following **GIANTS**: 163 | 164 | {{ range $v := .Deps}}* [{{$v}}](https://godoc.org/{{$v}}) 165 | {{ end }} 166 | ` 167 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const Version = "v1.2.1" 4 | --------------------------------------------------------------------------------