├── .github
└── workflows
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .goreleaser.yml
├── Dockerfile.vangen
├── LICENSE
├── Makefile
├── README.md
├── cloudbuild.yaml
├── config.go
├── config_test.go
├── example
├── vangen.json
└── vangen
│ ├── index.html
│ ├── pkg1
│ └── index.html
│ ├── pkg2
│ ├── index.html
│ ├── subpkg1
│ │ └── subsubpkg1
│ │ │ └── index.html
│ └── subpkg2
│ │ ├── subsubpkg1
│ │ └── index.html
│ │ ├── subsubpkg2
│ │ └── index.html
│ │ └── subsubpkg3
│ │ └── index.html
│ └── pkg3
│ ├── index.html
│ └── subpkg1
│ └── index.html
├── generate_index.go
├── generate_index_test.go
├── generate_package.go
├── generate_package_test.go
├── go.mod
├── go.sum
└── main.go
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags:
4 | - '*'
5 | jobs:
6 | goreleaser:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - uses: actions/setup-go@v2
11 | - uses: goreleaser/goreleaser-action@v2
12 | with:
13 | version: latest
14 | args: release --rm-dist
15 | env:
16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 | jobs:
3 | test:
4 | runs-on: ubuntu-latest
5 | steps:
6 | - uses: actions/checkout@v2
7 | - uses: actions/setup-go@v2
8 | - run: go test -cover ./...
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vangen
2 | dist/
3 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | builds:
2 | - binary: vangen
3 | goos:
4 | - linux
5 | - darwin
6 | - windows
7 | goarch:
8 | - amd64
9 |
10 | archives:
11 | - format: tar.gz
12 | files:
13 | - LICENSE
14 | format_overrides:
15 | - goos: windows
16 | format: zip
17 |
--------------------------------------------------------------------------------
/Dockerfile.vangen:
--------------------------------------------------------------------------------
1 | FROM scratch
2 | COPY vangen vangen
3 | ENTRYPOINT ["/vangen"]
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Leigh McCulloch
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 | .PHONY: test build release
2 |
3 | test:
4 | go test -cover
5 | go vet
6 | ./vangen -config=example/vangen.json -out=example/vangen
7 |
8 | build:
9 | go build
10 |
11 | release:
12 | goreleaser
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vangen
2 | 
3 | 
4 | [](https://goreportcard.com/report/github.com/leighmcculloch/vangen)
5 | [](https://pkg.go.dev/github.com/leighmcculloch/vangen)
6 |
7 | Vangen is a tool for generating static HTML for Go vanity import paths.
8 |
9 | Go vanity import paths work by serving a HTML file that tells the `go get` tool where to download the source from. You can still host the source code at Github, BitBucket, but the vanity URL gives you portability and other benefits.
10 |
11 | ## Why
12 | * Maintain Go vanity import paths with a simple definition file `vangen.json`.
13 | * Host Go vanity import paths using static hosting. No need for Google AppEngine, Heroku, etc. Host the files on Github Pages, AWS S3, Google Cloud Storage, etc.
14 |
15 | ## Install
16 |
17 | ### Source
18 |
19 | ```
20 | go get 4d63.com/vangen
21 | ```
22 |
23 | ### Linux
24 |
25 | ```
26 | curl -sSL https://github.com/leighmcculloch/vangen/releases/download/v1.1.3/vangen_1.1.3_linux_amd64.tar.gz | tar xz -C /usr/local/bin vangen
27 | ```
28 |
29 | ### Mac
30 |
31 | ```
32 | curl -sSL https://github.com/leighmcculloch/vangen/releases/download/v1.1.3/vangen_1.1.3_darwin_amd64.tar.gz | tar xz -C /usr/local/bin vangen
33 | ```
34 |
35 | ### Windows
36 |
37 | [Download the executable](https://github.com/leighmcculloch/vangen/releases/download/v1.1.3/vangen_1.1.3_windows_amd64.zip), and save it to your path.
38 |
39 | ## Usage
40 |
41 | 1. Create a `vangen.json` (see examples below)
42 | 2. Run `vangen`
43 | 3. Host the files outputted in `vangen/` at your domain
44 | 4. Try it out with `go get [domain]/[package]`
45 |
46 | ```
47 | $ vangen -help
48 | Vangen is a tool for generating static HTML for hosting Go repositories at a vanity import path.
49 |
50 | Usage:
51 |
52 | vangen [-config=vangen.json] [-out=vangen/]
53 |
54 | Flags:
55 |
56 | -config filename
57 | vangen json configuration filename (default "vangen.json")
58 | -help
59 | print this help list
60 | -out directory
61 | output directory that static files will be written to (default "vangen/")
62 | -verbose
63 | print verbose output when run
64 | -version
65 | print program version
66 | ```
67 |
68 | ## Examples
69 |
70 | ### Minimal
71 |
72 | The repository `type` and `source` properties will be set automatically when `url` begins with `https://github.com` or `https://gitlab.com`. Below is a minimal config for a project hosted on GitHub.
73 |
74 | ```json
75 | {
76 | "domain": "4d63.com",
77 | "repositories": [
78 | {
79 | "prefix": "optional",
80 | "subs": [
81 | "template"
82 | ],
83 | "url": "https://github.com/leighmcculloch/go-optional"
84 | }
85 | ]
86 | }
87 | ```
88 |
89 | ### All fields
90 |
91 | ```json
92 | {
93 | "domain": "4d63.com",
94 | "docsDomain": "pkg.go.dev",
95 | "repositories": [
96 | {
97 | "prefix": "optional",
98 | "subs": [
99 | "template"
100 | ],
101 | "type": "git",
102 | "hidden": false,
103 | "url": "https://github.com/leighmcculloch/go-optional",
104 | "source": {
105 | "home": "https://github.com/leighmcculloch/go-optional",
106 | "dir": "https://github.com/leighmcculloch/go-optional/tree/master{/dir}",
107 | "file": "https://github.com/leighmcculloch/go-optional/blob/master{/dir}/{file}#L{line}"
108 | },
109 | "website": {
110 | "url": "https://github.com/leighmcculoch/go-optional"
111 | }
112 | }
113 | ]
114 | }
115 | ```
116 |
--------------------------------------------------------------------------------
/cloudbuild.yaml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: 'golang'
3 | args: ['go', 'build']
4 | - name: 'gcr.io/cloud-builders/docker'
5 | args: ['build', '-t', 'gcr.io/$PROJECT_ID/vangen', '-f', 'Dockerfile.vangen', '.']
6 | images:
7 | - 'gcr.io/$PROJECT_ID/vangen'
8 |
--------------------------------------------------------------------------------
/config.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "io"
6 | "io/ioutil"
7 | "path"
8 | "sort"
9 | )
10 |
11 | type config struct {
12 | Domain string `json:"domain"`
13 | DocsDomain string `json:"docsDomain"`
14 | Index bool `json:"index"`
15 | Repositories []repository `json:"repositories"`
16 | }
17 |
18 | type repository struct {
19 | Prefix string `json:"prefix"`
20 | Subs []sub `json:"subs"`
21 | Type string `json:"type"`
22 | URL string `json:"url"`
23 | Main bool `json:"main"`
24 | Hidden bool `json:"hidden"`
25 | SourceURLs sourceURLs `json:"source"`
26 | Website website `json:"website"`
27 | }
28 |
29 | func (r repository) PrefixPath() string {
30 | if r.Prefix == "" {
31 | return ""
32 | } else {
33 | return "/" + r.Prefix
34 | }
35 | }
36 |
37 | func (r repository) Packages() []string {
38 | pkgs := []string{r.Prefix}
39 | for i := range r.Subs {
40 | pkgs = append(pkgs, r.SubPath(i))
41 | }
42 | return pkgs
43 | }
44 |
45 | func (r repository) SubPath(i int) string {
46 | return path.Join(r.Prefix, r.Subs[i].Name)
47 | }
48 |
49 | type sub struct {
50 | Name string
51 | Hidden bool
52 | }
53 |
54 | func (s *sub) UnmarshalJSON(raw []byte) error {
55 | *s = sub{}
56 |
57 | err := json.Unmarshal(raw, &s.Name)
58 | if err == nil {
59 | return nil
60 | }
61 |
62 | subWithTags := struct {
63 | Name string `json:"name"`
64 | Hidden bool `json:"hidden"`
65 | }{}
66 | err = json.Unmarshal(raw, &subWithTags)
67 | if err != nil {
68 | return err
69 | }
70 | *s = sub(subWithTags)
71 | return nil
72 | }
73 |
74 | type sourceURLs struct {
75 | Home string `json:"home"`
76 | Dir string `json:"dir"`
77 | File string `json:"file"`
78 | }
79 |
80 | type website struct {
81 | URL string `json:"url"`
82 | }
83 |
84 | func parseConfig(r io.Reader) (config, error) {
85 | bytes, err := ioutil.ReadAll(r)
86 | if err != nil {
87 | return config{}, err
88 | }
89 |
90 | var c config
91 | err = json.Unmarshal(bytes, &c)
92 | if err != nil {
93 | return config{}, err
94 | }
95 |
96 | sort.Slice(c.Repositories, func(i, j int) bool {
97 | return c.Repositories[i].Prefix < c.Repositories[j].Prefix
98 | })
99 |
100 | return c, nil
101 | }
102 |
--------------------------------------------------------------------------------
/config_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "reflect"
5 | "strings"
6 | "testing"
7 | )
8 |
9 | func TestParseConfigNoIndex(t *testing.T) {
10 | r := strings.NewReader(`{}`)
11 |
12 | c, err := parseConfig(r)
13 | if err != nil {
14 | t.Fatal(err)
15 | }
16 |
17 | if g, w := c.Index, false; g != w {
18 | t.Errorf("Got index %#v, want %#v", g, w)
19 | }
20 | }
21 |
22 | func TestParseConfigIndex(t *testing.T) {
23 | r := strings.NewReader(`{
24 | "index": true
25 | }`)
26 |
27 | c, err := parseConfig(r)
28 | if err != nil {
29 | t.Fatal(err)
30 | }
31 |
32 | if g, w := c.Index, true; g != w {
33 | t.Errorf("Got index %#v, want %#v", g, w)
34 | }
35 | }
36 |
37 | func TestParseConfigPackages(t *testing.T) {
38 | r := strings.NewReader(`{
39 | "repositories": [
40 | {
41 | "prefix": "foo",
42 | "subs": [
43 | "bar",
44 | "car",
45 | "car/dar",
46 | "ear/far"
47 | ]
48 | }
49 | ]
50 | }`)
51 |
52 | c, err := parseConfig(r)
53 | if err != nil {
54 | t.Fatal(err)
55 | }
56 | p := c.Repositories[0].Packages()
57 |
58 | e := []string{
59 | "foo",
60 | "foo/bar",
61 | "foo/car",
62 | "foo/car/dar",
63 | "foo/ear/far",
64 | }
65 |
66 | if !reflect.DeepEqual(p, e) {
67 | t.Errorf("Got packages %#v, want %#v", p, e)
68 | }
69 | }
70 |
71 | func TestParseConfigHiddenPackages(t *testing.T) {
72 | r := strings.NewReader(`{
73 | "repositories": [
74 | {
75 | "prefix": "foo",
76 | "subs": [
77 | "bar",
78 | "car",
79 | { "name": "car/dar", "hidden": true },
80 | { "name": "ear/far", "hidden": false }
81 | ]
82 | }
83 | ]
84 | }`)
85 |
86 | c, err := parseConfig(r)
87 | if err != nil {
88 | t.Fatal(err)
89 | }
90 | s := c.Repositories[0].Subs
91 |
92 | e := []sub{
93 | {Name: "bar"},
94 | {Name: "car"},
95 | {Name: "car/dar", Hidden: true},
96 | {Name: "ear/far", Hidden: false},
97 | }
98 |
99 | if !reflect.DeepEqual(s, e) {
100 | t.Errorf("Got packages %#v, want %#v", s, e)
101 | }
102 | }
103 |
104 | func TestParseConfigGithubMinimal(t *testing.T) {
105 | r := strings.NewReader(`{
106 | "domain": "4d63.com",
107 | "repositories": [
108 | {
109 | "prefix": "optional",
110 | "subs": [
111 | "template"
112 | ],
113 | "url": "https://github.com/leighmcculloch/go-optional"
114 | }
115 | ]
116 | }`)
117 |
118 | e := config{
119 | Domain: "4d63.com",
120 | Repositories: []repository{
121 | {
122 | Prefix: "optional",
123 | Subs: []sub{
124 | {Name: "template"},
125 | },
126 | URL: "https://github.com/leighmcculloch/go-optional",
127 | },
128 | },
129 | }
130 |
131 | c, err := parseConfig(r)
132 | if err != nil {
133 | t.Fatal(err)
134 | }
135 |
136 | if !reflect.DeepEqual(c, e) {
137 | t.Errorf("Got config %#v, want %#v", c, e)
138 | }
139 | }
140 |
141 | func TestParseConfigGithubComplete(t *testing.T) {
142 | r := strings.NewReader(`{
143 | "domain": "4d63.com",
144 | "repositories": [
145 | {
146 | "prefix": "optional",
147 | "subs": [
148 | "template"
149 | ],
150 | "type": "git",
151 | "url": "https://github.com/leighmcculloch/go-optional",
152 | "source": {
153 | "home": "https://github.com/leighmcculloch/go-optional",
154 | "dir": "https://github.com/leighmcculloch/go-optional/tree/master{/dir}",
155 | "file": "https://github.com/leighmcculloch/go-optional/blob/master{/dir}/{file}#L{line}"
156 | },
157 | "website": {
158 | "url": "https://github.com/leighmcculloch/go-optional"
159 | }
160 | }
161 | ]
162 | }`)
163 |
164 | e := config{
165 | Domain: "4d63.com",
166 | Repositories: []repository{
167 | {
168 | Prefix: "optional",
169 | Subs: []sub{
170 | {Name: "template"},
171 | },
172 | Type: "git",
173 | URL: "https://github.com/leighmcculloch/go-optional",
174 | SourceURLs: sourceURLs{
175 | Home: "https://github.com/leighmcculloch/go-optional",
176 | Dir: "https://github.com/leighmcculloch/go-optional/tree/master{/dir}",
177 | File: "https://github.com/leighmcculloch/go-optional/blob/master{/dir}/{file}#L{line}",
178 | },
179 | Website: website{
180 | URL: "https://github.com/leighmcculloch/go-optional",
181 | },
182 | },
183 | },
184 | }
185 |
186 | c, err := parseConfig(r)
187 | if err != nil {
188 | t.Fatal(err)
189 | }
190 |
191 | if !reflect.DeepEqual(c, e) {
192 | t.Errorf("Got config %#v, want %#v", c, e)
193 | }
194 | }
195 |
196 | func TestParseConfigGitlabMinimal(t *testing.T) {
197 | r := strings.NewReader(`{
198 | "domain": "4d63.com",
199 | "repositories": [
200 | {
201 | "prefix": "optional",
202 | "subs": [
203 | "template"
204 | ],
205 | "url": "https://gitlab.com/leighmcculloch/go-optional"
206 | }
207 | ]
208 | }`)
209 |
210 | e := config{
211 | Domain: "4d63.com",
212 | Repositories: []repository{
213 | {
214 | Prefix: "optional",
215 | Subs: []sub{
216 | {Name: "template"},
217 | },
218 | URL: "https://gitlab.com/leighmcculloch/go-optional",
219 | },
220 | },
221 | }
222 |
223 | c, err := parseConfig(r)
224 | if err != nil {
225 | t.Fatal(err)
226 | }
227 |
228 | if !reflect.DeepEqual(c, e) {
229 | t.Errorf("Got config %#v, want %#v", c, e)
230 | }
231 | }
232 |
233 | func TestParseConfigGitlabComplete(t *testing.T) {
234 | r := strings.NewReader(`{
235 | "domain": "4d63.com",
236 | "repositories": [
237 | {
238 | "prefix": "optional",
239 | "subs": [
240 | "template"
241 | ],
242 | "type": "git",
243 | "url": "https://gitlab.com/leighmcculloch/go-optional",
244 | "source": {
245 | "home": "https://gitlab.com/leighmcculloch/go-optional",
246 | "dir": "https://gitlab.com/leighmcculloch/go-optional/tree/master{/dir}",
247 | "file": "https://gitlab.com/leighmcculloch/go-optional/blob/master{/dir}/{file}#L{line}"
248 | },
249 | "website": {
250 | "url": "https://gitlab.com/leighmcculloch/go-optional"
251 | }
252 | }
253 | ]
254 | }`)
255 |
256 | e := config{
257 | Domain: "4d63.com",
258 | Repositories: []repository{
259 | {
260 | Prefix: "optional",
261 | Subs: []sub{
262 | {Name: "template"},
263 | },
264 | Type: "git",
265 | URL: "https://gitlab.com/leighmcculloch/go-optional",
266 | SourceURLs: sourceURLs{
267 | Home: "https://gitlab.com/leighmcculloch/go-optional",
268 | Dir: "https://gitlab.com/leighmcculloch/go-optional/tree/master{/dir}",
269 | File: "https://gitlab.com/leighmcculloch/go-optional/blob/master{/dir}/{file}#L{line}",
270 | },
271 | Website: website{
272 | URL: "https://gitlab.com/leighmcculloch/go-optional",
273 | },
274 | },
275 | },
276 | }
277 |
278 | c, err := parseConfig(r)
279 | if err != nil {
280 | t.Fatal(err)
281 | }
282 |
283 | if !reflect.DeepEqual(c, e) {
284 | t.Errorf("Got config %#v, want %#v", c, e)
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/example/vangen.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "example.com",
3 | "docsDomain": "pkg.go.dev",
4 | "index": true,
5 | "repositories": [
6 | {
7 | "prefix": "pkg1",
8 | "url": "https://github.com/leighmcculloch/go-pkg1"
9 | },
10 | {
11 | "prefix": "pkg2",
12 | "subs": [
13 | "subpkg1/subsubpkg1",
14 | "subpkg2/subsubpkg1",
15 | "subpkg2/subsubpkg2",
16 | "subpkg2/subsubpkg3"
17 | ],
18 | "url": "https://github.com/leighmcculloch/go-pkg2",
19 | "main": true
20 | },
21 | {
22 | "prefix": "pkg3",
23 | "subs": [
24 | "subpkg1"
25 | ],
26 | "type": "git",
27 | "url": "https://example.com/repositories/go-pkg3",
28 | "source": {
29 | "home": "https://example.com/repositories/go-pkg3",
30 | "dir": "https://example.com/repositories/go-pkg3/browse{/dir}",
31 | "file": "https://example.com/repositories/go-pkg3/show{/dir}/{file}#L{line}"
32 | },
33 | "website": {
34 | "url": "https://example.com/readme/go-pkg3"
35 | }
36 | }
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/example/vangen/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | example.com Go Modules
6 |
11 |
12 |
13 |
14 |
15 |
example.com Go Modules
16 |
17 |
Tools:
18 |
19 |
24 |
25 |
Libraries:
26 |
27 |
28 | -
29 | pkg1
30 |
31 | -
32 | pkg3
33 |
34 |
35 |
36 |
37 |
38 | Generated by
vangen.
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/example/vangen/pkg1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | example.com/pkg1
6 |
7 |
8 |
15 |
16 |
17 |
18 |
example.com/pkg1
19 |
go get example.com/pkg1
20 |
import "example.com/pkg1"
21 | Home:
https://pkg.go.dev/example.com/pkg1
22 | Source:
https://github.com/leighmcculloch/go-pkg1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/example/vangen/pkg2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | example.com/pkg2
6 |
7 |
8 |
15 |
16 |
17 |
18 |
example.com/pkg2
19 |
go get example.com/pkg2
20 |
import "example.com/pkg2"
21 | Home:
https://pkg.go.dev/example.com/pkg2
22 | Source:
https://github.com/leighmcculloch/go-pkg2
23 | Sub-packages:
24 |
25 |
--------------------------------------------------------------------------------
/example/vangen/pkg2/subpkg1/subsubpkg1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | example.com/pkg2/subpkg1/subsubpkg1
6 |
7 |
8 |
15 |
16 |
17 |
18 |
example.com/pkg2/subpkg1/subsubpkg1
19 |
go get example.com/pkg2/subpkg1/subsubpkg1
20 |
import "example.com/pkg2/subpkg1/subsubpkg1"
21 | Home:
https://pkg.go.dev/example.com/pkg2/subpkg1/subsubpkg1
22 | Source:
https://github.com/leighmcculloch/go-pkg2
23 | Sub-packages:
24 |
25 |
--------------------------------------------------------------------------------
/example/vangen/pkg2/subpkg2/subsubpkg1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | example.com/pkg2/subpkg2/subsubpkg1
6 |
7 |
8 |
15 |
16 |
17 |
18 |
example.com/pkg2/subpkg2/subsubpkg1
19 |
go get example.com/pkg2/subpkg2/subsubpkg1
20 |
import "example.com/pkg2/subpkg2/subsubpkg1"
21 | Home:
https://pkg.go.dev/example.com/pkg2/subpkg2/subsubpkg1
22 | Source:
https://github.com/leighmcculloch/go-pkg2
23 | Sub-packages:
24 |
25 |
--------------------------------------------------------------------------------
/example/vangen/pkg2/subpkg2/subsubpkg2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | example.com/pkg2/subpkg2/subsubpkg2
6 |
7 |
8 |
15 |
16 |
17 |
18 |
example.com/pkg2/subpkg2/subsubpkg2
19 |
go get example.com/pkg2/subpkg2/subsubpkg2
20 |
import "example.com/pkg2/subpkg2/subsubpkg2"
21 | Home:
https://pkg.go.dev/example.com/pkg2/subpkg2/subsubpkg2
22 | Source:
https://github.com/leighmcculloch/go-pkg2
23 | Sub-packages:
24 |
25 |
--------------------------------------------------------------------------------
/example/vangen/pkg2/subpkg2/subsubpkg3/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | example.com/pkg2/subpkg2/subsubpkg3
6 |
7 |
8 |
15 |
16 |
17 |
18 |
example.com/pkg2/subpkg2/subsubpkg3
19 |
go get example.com/pkg2/subpkg2/subsubpkg3
20 |
import "example.com/pkg2/subpkg2/subsubpkg3"
21 | Home:
https://pkg.go.dev/example.com/pkg2/subpkg2/subsubpkg3
22 | Source:
https://github.com/leighmcculloch/go-pkg2
23 | Sub-packages:
24 |
25 |
--------------------------------------------------------------------------------
/example/vangen/pkg3/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | example.com/pkg3
6 |
7 |
8 |
15 |
16 |
17 |
18 |
example.com/pkg3
19 |
go get example.com/pkg3
20 |
import "example.com/pkg3"
21 | Home:
https://example.com/readme/go-pkg3
22 | Source:
https://example.com/repositories/go-pkg3
23 | Sub-packages:
24 |
25 |
--------------------------------------------------------------------------------
/example/vangen/pkg3/subpkg1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | example.com/pkg3/subpkg1
6 |
7 |
8 |
15 |
16 |
17 |
18 |
example.com/pkg3/subpkg1
19 |
go get example.com/pkg3/subpkg1
20 |
import "example.com/pkg3/subpkg1"
21 | Home:
https://example.com/readme/go-pkg3
22 | Source:
https://example.com/repositories/go-pkg3
23 | Sub-packages:
24 |
25 |
--------------------------------------------------------------------------------
/generate_index.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "html/template"
6 | "io"
7 | )
8 |
9 | func generate_index(w io.Writer, domain string, r []repository) error {
10 | const html = `
11 |
12 |
13 |
14 | {{.Domain}} Go Modules
15 |
20 |
21 |
22 |
23 |
24 |
{{.Domain}} Go Modules
25 |
26 |
Tools:
27 |
28 |
29 | {{range $_, $r := .MainRepositories -}}
30 | -
31 | {{$r.Prefix}}
32 | {{if .Subs -}}
{{end -}}
33 | {{range $_, $s := .Subs -}}{{if not $s.Hidden -}}- {{$s.Name}}
{{end -}}{{end -}}
34 | {{if .Subs -}}
{{end -}}
35 |
36 | {{end -}}
37 |
38 |
39 |
Libraries:
40 |
41 |
42 | {{range $_, $r := .PackageRepositories -}}{{if not $r.Hidden -}}
43 | -
44 | {{$r.Prefix}}
45 | {{if .Subs -}}
{{end -}}
46 | {{range $_, $s := .Subs -}}{{if not $s.Hidden -}}- {{$s.Name}}
{{end -}}{{end -}}
47 | {{if .Subs -}}
{{end -}}
48 |
49 | {{end }}{{end -}}
50 |
51 |
52 |
53 |
54 | Generated by
vangen.
55 |
56 |
57 |
58 | `
59 |
60 | tmpl, err := template.New("").Parse(html)
61 | if err != nil {
62 | return fmt.Errorf("error loading template: %v", err)
63 | }
64 |
65 | mainRepositories := []repository{}
66 | packageRepositories := []repository{}
67 | for _, r := range r {
68 | if r.Main {
69 | mainRepositories = append(mainRepositories, r)
70 | } else {
71 | packageRepositories = append(packageRepositories, r)
72 | }
73 | }
74 |
75 | data := struct {
76 | Domain string
77 | MainRepositories []repository
78 | PackageRepositories []repository
79 | }{
80 | Domain: domain,
81 | MainRepositories: mainRepositories,
82 | PackageRepositories: packageRepositories,
83 | }
84 |
85 | err = tmpl.ExecuteTemplate(w, "", data)
86 | if err != nil {
87 | return fmt.Errorf("generating template: %v", err)
88 | }
89 |
90 | return nil
91 | }
92 |
--------------------------------------------------------------------------------
/generate_index_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 |
7 | "github.com/sergi/go-diff/diffmatchpatch"
8 | )
9 |
10 | func TestGenerateIndex(t *testing.T) {
11 | testCases := []struct {
12 | description string
13 | domain string
14 | r []repository
15 | expectedOut string
16 | expectedErr error
17 | }{
18 | {
19 | description: "basic",
20 | domain: "example.com",
21 | r: []repository{
22 | {
23 | Prefix: "pkg1",
24 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
25 | Main: true,
26 | },
27 | {
28 | Prefix: "pkg2",
29 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2/subsubpkg1"}},
30 | },
31 | {
32 | Prefix: "pkg3",
33 | Hidden: true,
34 | },
35 | },
36 | expectedOut: `
37 |
38 |
39 |
40 | example.com Go Modules
41 |
46 |
47 |
48 |
49 |
50 |
example.com Go Modules
51 |
52 |
Tools:
53 |
54 |
59 |
60 |
Libraries:
61 |
62 |
67 |
68 |
69 |
70 | Generated by
vangen.
71 |
72 |
73 |
74 | `,
75 | expectedErr: nil,
76 | },
77 | {
78 | description: "hidden sub-package",
79 | domain: "example.com",
80 | r: []repository{
81 | {
82 | Prefix: "pkg1",
83 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}, {Name: "subpkg3", Hidden: true}},
84 | Main: true,
85 | },
86 | {
87 | Prefix: "pkg2",
88 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2/subsubpkg1"}, {Name: "subpkg2/subsubpkg2", Hidden: true}},
89 | },
90 | },
91 | expectedOut: `
92 |
93 |
94 |
95 | example.com Go Modules
96 |
101 |
102 |
103 |
104 |
105 |
example.com Go Modules
106 |
107 |
Tools:
108 |
109 |
110 | -
111 | pkg1
112 |
113 |
114 |
115 |
Libraries:
116 |
117 |
118 | -
119 | pkg2
120 |
121 |
122 |
123 |
124 |
125 | Generated by
vangen.
126 |
127 |
128 |
129 | `,
130 | expectedErr: nil,
131 | },
132 | }
133 |
134 | for _, tc := range testCases {
135 | var out bytes.Buffer
136 | err := generate_index(&out, tc.domain, tc.r)
137 | if err != tc.expectedErr {
138 | t.Errorf("Test case %#v got err %#v, want %#v", tc, err, tc.expectedErr)
139 | } else if out.String() != tc.expectedOut {
140 | dmp := diffmatchpatch.New()
141 | diffs := dmp.DiffMain(tc.expectedOut, out.String(), false)
142 | t.Errorf("Test case %q got: \n%s\nAs diff:\n%s", tc.description, out.String(), dmp.DiffPrettyText(diffs))
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/generate_package.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "html/template"
6 | "io"
7 | "strings"
8 | )
9 |
10 | func generate_package(w io.Writer, domain, docsDomain, pkg string, r repository) error {
11 | const html = `
12 |
13 |
14 |
15 | {{.Domain}}/{{.Package}}
16 |
17 |
18 |
25 |
26 |
27 |
28 |
{{.Domain}}/{{.Package}}
29 |
go get {{.Domain}}/{{.Package}}
30 |
import "{{.Domain}}/{{.Package}}"
31 | Home:
{{.HomeURL}}
32 | Source:
{{.Repository.URL}}
33 | {{if .Repository.Subs -}}Sub-packages:
{{end -}}
36 |
37 |
38 | `
39 |
40 | tmpl, err := template.New("").Parse(html)
41 | if err != nil {
42 | return fmt.Errorf("error loading template: %v", err)
43 | }
44 |
45 | var homeURL string
46 | if r.Website.URL != "" {
47 | homeURL = r.Website.URL
48 | } else {
49 | if docsDomain == "" {
50 | docsDomain = "pkg.go.dev"
51 | }
52 | homeURL = fmt.Sprintf("https://%s/%s/%s", docsDomain, domain, pkg)
53 | }
54 |
55 | if strings.HasPrefix(r.URL, "https://github.com") || strings.HasPrefix(r.URL, "https://gitlab.com") {
56 | if r.Type == "" {
57 | r.Type = "git"
58 | }
59 | if r.SourceURLs.Home == "" {
60 | r.SourceURLs.Home = r.URL
61 | }
62 | if r.SourceURLs.Dir == "" {
63 | r.SourceURLs.Dir = r.URL + "/tree/master{/dir}"
64 | }
65 | if r.SourceURLs.File == "" {
66 | r.SourceURLs.File = r.URL + "/blob/master{/dir}/{file}#L{line}"
67 | }
68 | }
69 |
70 | if r.SourceURLs.Home == "" {
71 | r.SourceURLs.Home = "_"
72 | }
73 | if r.SourceURLs.Dir == "" {
74 | r.SourceURLs.Dir = "_"
75 | }
76 | if r.SourceURLs.File == "" {
77 | r.SourceURLs.File = "_"
78 | }
79 |
80 | data := struct {
81 | Domain string
82 | Package string
83 | Repository repository
84 | HomeURL string
85 | }{
86 | Domain: domain,
87 | Package: pkg,
88 | Repository: r,
89 | HomeURL: homeURL,
90 | }
91 |
92 | err = tmpl.ExecuteTemplate(w, "", data)
93 | if err != nil {
94 | return fmt.Errorf("generating template: %v", err)
95 | }
96 |
97 | return nil
98 | }
99 |
--------------------------------------------------------------------------------
/generate_package_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 |
7 | "github.com/sergi/go-diff/diffmatchpatch"
8 | )
9 |
10 | func TestGenerate(t *testing.T) {
11 | testCases := []struct {
12 | description string
13 | domain string
14 | docsDomain string
15 | pkg string
16 | r repository
17 | expectedOut string
18 | expectedErr error
19 | }{
20 | {
21 | description: "simple",
22 | domain: "example.com",
23 | docsDomain: "godoc.org",
24 | pkg: "pkg1",
25 | r: repository{
26 | Prefix: "pkg1",
27 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
28 | Type: "git",
29 | URL: "https://repositoryhost.com/example/go-pkg1",
30 | },
31 | expectedOut: `
32 |
33 |
34 |
35 | example.com/pkg1
36 |
37 |
38 |
45 |
46 |
47 |
48 |
example.com/pkg1
49 |
go get example.com/pkg1
50 |
import "example.com/pkg1"
51 | Home:
https://godoc.org/example.com/pkg1
52 | Source:
https://repositoryhost.com/example/go-pkg1
53 | Sub-packages:
54 |
55 | `,
56 | expectedErr: nil,
57 | },
58 | {
59 | description: "hidden",
60 | domain: "example.com",
61 | docsDomain: "godoc.org",
62 | pkg: "pkg1",
63 | r: repository{
64 | Prefix: "pkg1",
65 | Hidden: true,
66 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
67 | Type: "git",
68 | URL: "https://repositoryhost.com/example/go-pkg1",
69 | },
70 | expectedOut: `
71 |
72 |
73 |
74 | example.com/pkg1
75 |
76 |
77 |
84 |
85 |
86 |
87 |
example.com/pkg1
88 |
go get example.com/pkg1
89 |
import "example.com/pkg1"
90 | Home:
https://godoc.org/example.com/pkg1
91 | Source:
https://repositoryhost.com/example/go-pkg1
92 | Sub-packages:
93 |
94 | `,
95 | expectedErr: nil,
96 | },
97 | {
98 | description: "custom source urls",
99 | domain: "example.com",
100 | docsDomain: "pkg.go.dev",
101 | pkg: "pkg1",
102 | r: repository{
103 | Prefix: "pkg1",
104 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
105 | Type: "git",
106 | URL: "https://repositoryhost.com/example/go-pkg1",
107 | SourceURLs: sourceURLs{
108 | Home: "https://repositoryhost.com/example/go-pkg1/home",
109 | Dir: "https://repositoryhost.com/example/go-pkg1/browser{/dir}",
110 | File: "https://repositoryhost.com/example/go-pkg1/view{/dir}{/file}",
111 | },
112 | Website: website{
113 | URL: "https://www.example.com",
114 | },
115 | },
116 | expectedOut: `
117 |
118 |
119 |
120 | example.com/pkg1
121 |
122 |
123 |
130 |
131 |
132 |
133 |
example.com/pkg1
134 |
go get example.com/pkg1
135 |
import "example.com/pkg1"
136 | Home:
https://www.example.com
137 | Source:
https://repositoryhost.com/example/go-pkg1
138 | Sub-packages:
139 |
140 | `,
141 | expectedErr: nil,
142 | },
143 | {
144 | description: "sub-package",
145 | domain: "example.com",
146 | pkg: "pkg1/subpkg1",
147 | r: repository{
148 | Prefix: "pkg1",
149 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
150 | Type: "git",
151 | URL: "https://repositoryhost.com/example/go-pkg1",
152 | SourceURLs: sourceURLs{
153 | Home: "https://repositoryhost.com/example/go-pkg1/home",
154 | Dir: "https://repositoryhost.com/example/go-pkg1/browser{/dir}",
155 | File: "https://repositoryhost.com/example/go-pkg1/view{/dir}{/file}",
156 | },
157 | Website: website{
158 | URL: "https://www.example.com",
159 | },
160 | },
161 | expectedOut: `
162 |
163 |
164 |
165 | example.com/pkg1/subpkg1
166 |
167 |
168 |
175 |
176 |
177 |
178 |
example.com/pkg1/subpkg1
179 |
go get example.com/pkg1/subpkg1
180 |
import "example.com/pkg1/subpkg1"
181 | Home:
https://www.example.com
182 | Source:
https://repositoryhost.com/example/go-pkg1
183 | Sub-packages:
184 |
185 | `,
186 | expectedErr: nil,
187 | },
188 | {
189 | description: "sub-package hidden",
190 | domain: "example.com",
191 | pkg: "pkg1/subpkg1",
192 | r: repository{
193 | Prefix: "pkg1",
194 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}, {Name: "subpkg3", Hidden: true}},
195 | Type: "git",
196 | URL: "https://repositoryhost.com/example/go-pkg1",
197 | SourceURLs: sourceURLs{
198 | Home: "https://repositoryhost.com/example/go-pkg1/home",
199 | Dir: "https://repositoryhost.com/example/go-pkg1/browser{/dir}",
200 | File: "https://repositoryhost.com/example/go-pkg1/view{/dir}{/file}",
201 | },
202 | Website: website{
203 | URL: "https://www.example.com",
204 | },
205 | },
206 | expectedOut: `
207 |
208 |
209 |
210 | example.com/pkg1/subpkg1
211 |
212 |
213 |
220 |
221 |
222 |
223 |
example.com/pkg1/subpkg1
224 |
go get example.com/pkg1/subpkg1
225 |
import "example.com/pkg1/subpkg1"
226 | Home:
https://www.example.com
227 | Source:
https://repositoryhost.com/example/go-pkg1
228 | Sub-packages:
229 |
230 | `,
231 | expectedErr: nil,
232 | },
233 | {
234 | description: "github defaults",
235 | domain: "example.com",
236 | docsDomain: "pkg.go.dev",
237 | pkg: "pkg1",
238 | r: repository{
239 | Prefix: "pkg1",
240 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
241 | URL: "https://github.com/example/go-pkg1",
242 | },
243 | expectedOut: `
244 |
245 |
246 |
247 | example.com/pkg1
248 |
249 |
250 |
257 |
258 |
259 |
260 |
example.com/pkg1
261 |
go get example.com/pkg1
262 |
import "example.com/pkg1"
263 | Home:
https://pkg.go.dev/example.com/pkg1
264 | Source:
https://github.com/example/go-pkg1
265 | Sub-packages:
266 |
267 | `,
268 | expectedErr: nil,
269 | },
270 | {
271 | description: "sub-package github defaults",
272 | domain: "example.com",
273 | docsDomain: "pkg.go.dev",
274 | pkg: "pkg1/subpkg1",
275 | r: repository{
276 | Prefix: "pkg1",
277 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
278 | URL: "https://github.com/example/go-pkg1",
279 | },
280 | expectedOut: `
281 |
282 |
283 |
284 | example.com/pkg1/subpkg1
285 |
286 |
287 |
294 |
295 |
296 |
297 |
example.com/pkg1/subpkg1
298 |
go get example.com/pkg1/subpkg1
299 |
import "example.com/pkg1/subpkg1"
300 | Home:
https://pkg.go.dev/example.com/pkg1/subpkg1
301 | Source:
https://github.com/example/go-pkg1
302 | Sub-packages:
303 |
304 | `,
305 | expectedErr: nil,
306 | },
307 | {
308 | description: "gitlab defaults",
309 | domain: "example.com",
310 | docsDomain: "",
311 | pkg: "pkg1",
312 | r: repository{
313 | Prefix: "pkg1",
314 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
315 | URL: "https://gitlab.com/example/go-pkg1",
316 | },
317 | expectedOut: `
318 |
319 |
320 |
321 | example.com/pkg1
322 |
323 |
324 |
331 |
332 |
333 |
334 |
example.com/pkg1
335 |
go get example.com/pkg1
336 |
import "example.com/pkg1"
337 | Home:
https://pkg.go.dev/example.com/pkg1
338 | Source:
https://gitlab.com/example/go-pkg1
339 | Sub-packages:
340 |
341 | `,
342 | expectedErr: nil,
343 | },
344 | {
345 | description: "sub-package gitlab defaults",
346 | domain: "example.com",
347 | docsDomain: "pkg.go.dev",
348 | pkg: "pkg1/subpkg1",
349 | r: repository{
350 | Prefix: "pkg1",
351 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
352 | URL: "https://gitlab.com/example/go-pkg1",
353 | },
354 | expectedOut: `
355 |
356 |
357 |
358 | example.com/pkg1/subpkg1
359 |
360 |
361 |
368 |
369 |
370 |
371 |
example.com/pkg1/subpkg1
372 |
go get example.com/pkg1/subpkg1
373 |
import "example.com/pkg1/subpkg1"
374 | Home:
https://pkg.go.dev/example.com/pkg1/subpkg1
375 | Source:
https://gitlab.com/example/go-pkg1
376 | Sub-packages:
377 |
378 | `,
379 | expectedErr: nil,
380 | },
381 | {
382 | description: "github defaults with custom source",
383 | domain: "example.com",
384 | docsDomain: "",
385 | pkg: "pkg1",
386 | r: repository{
387 | Prefix: "pkg1",
388 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
389 | Type: "git",
390 | URL: "https://github.com/example/go-pkg1",
391 | SourceURLs: sourceURLs{
392 | Home: "https://github.com/example/go-pkg1",
393 | Dir: "https://github.com/example/go-pkg1/tree/branch{/dir}",
394 | File: "https://github.com/example/go-pkg1/blob/branch{/dir}/{file}#L{line}",
395 | },
396 | Website: website{
397 | URL: "https://www.example.com",
398 | },
399 | },
400 | expectedOut: `
401 |
402 |
403 |
404 | example.com/pkg1
405 |
406 |
407 |
414 |
415 |
416 |
417 |
example.com/pkg1
418 |
go get example.com/pkg1
419 |
import "example.com/pkg1"
420 | Home:
https://www.example.com
421 | Source:
https://github.com/example/go-pkg1
422 | Sub-packages:
423 |
424 | `,
425 | expectedErr: nil,
426 | },
427 | {
428 | description: "single module deployment that has no 'prefix'",
429 | domain: "example.com",
430 | docsDomain: "",
431 | pkg: "",
432 | r: repository{
433 | Prefix: "",
434 | Subs: []sub{{Name: "subpkg1"}, {Name: "subpkg2"}},
435 | Type: "git",
436 | URL: "https://github.com/example/go-pkg1",
437 | SourceURLs: sourceURLs{
438 | Home: "https://github.com/example/go-pkg1",
439 | Dir: "https://github.com/example/go-pkg1/tree/branch{/dir}",
440 | File: "https://github.com/example/go-pkg1/blob/branch{/dir}/{file}#L{line}",
441 | },
442 | Website: website{
443 | URL: "https://www.example.com",
444 | },
445 | },
446 | expectedOut: `
447 |
448 |
449 |
450 | example.com/
451 |
452 |
453 |
460 |
461 |
462 |
463 |
example.com/
464 |
go get example.com/
465 |
import "example.com/"
466 | Home:
https://www.example.com
467 | Source:
https://github.com/example/go-pkg1
468 | Sub-packages:
469 |
470 | `,
471 | expectedErr: nil,
472 | },
473 | }
474 |
475 | for _, tc := range testCases {
476 | var out bytes.Buffer
477 | err := generate_package(&out, tc.domain, tc.docsDomain, tc.pkg, tc.r)
478 | if err != tc.expectedErr {
479 | t.Errorf("Test case %q got err %#v, want %#v", tc.description, err, tc.expectedErr)
480 | } else if out.String() != tc.expectedOut {
481 | dmp := diffmatchpatch.New()
482 | diffs := dmp.DiffMain(tc.expectedOut, out.String(), false)
483 | t.Errorf("Test case %q got: \n%s\nAs diff:\n%s", tc.description, out.String(), dmp.DiffPrettyText(diffs))
484 | }
485 | }
486 | }
487 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module 4d63.com/vangen
2 |
3 | go 1.21.0
4 |
5 | require github.com/sergi/go-diff v1.0.0
6 |
7 | require (
8 | github.com/davecgh/go-spew v1.1.0 // indirect
9 | github.com/pmezard/go-difflib v1.0.0 // indirect
10 | github.com/stretchr/objx v0.1.0 // indirect
11 | github.com/stretchr/testify v1.3.0 // indirect
12 | )
13 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5 | github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
6 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
7 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
8 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
10 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 | "path/filepath"
8 | )
9 |
10 | var version = ""
11 |
12 | func main() {
13 | err := run()
14 | if err != nil {
15 | fmt.Fprintf(os.Stderr, "%v\n", err)
16 | os.Exit(1)
17 | }
18 | }
19 |
20 | func run() error {
21 | printHelp := flag.Bool("help", false, "print this help list")
22 | printVersion := flag.Bool("version", false, "print program version")
23 | verbose := flag.Bool("verbose", false, "print verbose output when run")
24 | filename := flag.String("config", "vangen.json", "vangen json configuration `filename`")
25 | outputDir := flag.String("out", "vangen/", "output `directory` that static files will be written to")
26 | noOverwrite := flag.Bool("no-overwrite", false, "If an output file already exists, stops with a non-zero return code")
27 | flag.Usage = func() {
28 | fmt.Fprintf(os.Stderr, "Vangen is a tool for generating static HTML for hosting Go repositories at a vanity import path.\n\n")
29 | fmt.Fprintf(os.Stderr, "Usage:\n\n")
30 | fmt.Fprintf(os.Stderr, " vangen [-config=vangen.json] [-out=vangen/]\n\n")
31 | fmt.Fprintf(os.Stderr, "Flags:\n\n")
32 | flag.PrintDefaults()
33 | }
34 | flag.Parse()
35 |
36 | if *printHelp {
37 | flag.Usage()
38 | return nil
39 | }
40 |
41 | if *printVersion {
42 | fmt.Fprintln(os.Stderr, version)
43 | return nil
44 | }
45 |
46 | cf, err := os.Open(*filename)
47 | if err != nil {
48 | return err
49 | }
50 |
51 | c, err := parseConfig(cf)
52 | if err != nil {
53 | return err
54 | }
55 |
56 | err = os.MkdirAll(*outputDir, os.ModePerm)
57 | if err != nil {
58 | return fmt.Errorf("making dir %s: %w", *outputDir, err)
59 | }
60 |
61 | if c.Index {
62 | pathOut := filepath.Join(*outputDir, "index.html")
63 | f, err := os.Create(pathOut)
64 | if err != nil {
65 | return fmt.Errorf("writing file %s: %w", pathOut, err)
66 | }
67 | defer func() {
68 | err := f.Close()
69 | if err != nil {
70 | fmt.Fprintf(os.Stderr, "closing file %s: %v", pathOut, err)
71 | }
72 | }()
73 |
74 | if *verbose {
75 | fmt.Fprintf(os.Stderr, "Writing %s\n", pathOut)
76 | }
77 | err = generate_index(f, c.Domain, c.Repositories)
78 | if err != nil {
79 | return fmt.Errorf("generating index: %w", err)
80 | }
81 |
82 | err = f.Sync()
83 | if err != nil {
84 | return fmt.Errorf("flushing file %s: %w", pathOut, err)
85 | }
86 | }
87 |
88 | for _, r := range c.Repositories {
89 | for _, p := range r.Packages() {
90 | dirOut := filepath.Join(*outputDir, p)
91 | err = os.MkdirAll(dirOut, os.ModePerm)
92 | if err != nil {
93 | return fmt.Errorf("making dir %s: %v", dirOut, err)
94 | }
95 |
96 | pathOut := filepath.Join(dirOut, "index.html")
97 | if *noOverwrite {
98 | if _, err := os.Stat(pathOut); !os.IsNotExist(err) {
99 | if err == nil {
100 | return fmt.Errorf("cannot overwrite output file %s: %w", pathOut, err)
101 | }
102 |
103 | return fmt.Errorf("checking file %s: %w", pathOut, err)
104 | }
105 | }
106 | f, err := os.Create(pathOut)
107 | if err != nil {
108 | return fmt.Errorf("writing file %s: %v", pathOut, err)
109 | }
110 | defer func() {
111 | err := f.Close()
112 | if err != nil {
113 | fmt.Fprintf(os.Stderr, "closing file %s: %v", pathOut, err)
114 | }
115 | }()
116 |
117 | if *verbose {
118 | fmt.Fprintf(os.Stderr, "Writing %s\n", pathOut)
119 | }
120 | err = generate_package(f, c.Domain, c.DocsDomain, p, r)
121 | if err != nil {
122 | return fmt.Errorf("generating package %s: %w", p, err)
123 | }
124 |
125 | err = f.Sync()
126 | if err != nil {
127 | return fmt.Errorf("flushing file %s: %w", pathOut, err)
128 | }
129 | }
130 | }
131 |
132 | return nil
133 | }
134 |
--------------------------------------------------------------------------------