~poldi1405/gomod-recter

14b45f231a76b0bdb474689d2bc3b4b190fd19ce — Moritz Poldrack 2 years ago e3a4ee3 v0.3.0
added special handling of go gets
added source-file links
4 files changed, 130 insertions(+), 14 deletions(-)

M config.go
M internal/data/get-data.go
M internal/data/type.go
M internal/handler/project.go
M config.go => config.go +35 -13
@@ 32,7 32,23 @@ func initConfig() error {
	viper.SetDefault("Network.ListenAddr", "127.0.0.1:25000")
	viper.SetDefault("Proxy.Address", "https://proxy.golang.org/")
	viper.SetDefault("Proxy.IgnoreCert", false)
	viper.SetDefault("Projects", map[string]map[string]interface{}{"example": {"name": "Example Project", "Redirect": false, "Description": "The example project is an example that shows how to add a meaningful description to your project.\n\nIf you think that explaining something with itself is a bad way of explaining a thing, feel free to submit a patch. Repetition hammers the point into your head, which is why I repeat everything I say. Having a long text is a plus because long text demonstrates better what happens if you add long text for a description.", "VCS": "git", "Repo": "https://git.sr.ht/~poldi1405/gomod-recter", "Note": map[string]interface{}{"Show": true, "Text": "This project is currently looking for a new maintainer. To apply, please reach out to me@my-domain.com", "Style": "warning"}}})
	viper.SetDefault("Projects", map[string]map[string]interface{}{
		"example": {
			"name":          "Example Project",
			"Redirect":      false,
			"Description":   "The example project is an example that shows how to add a meaningful description to your project.\n\nIf you think that explaining something with itself is a bad way of explaining a thing, feel free to submit a patch. Repetition hammers the point into your head, which is why I repeat everything I say. Having a long text is a plus because long text demonstrates better what happens if you add long text for a description.",
			"VCS":           "git",
			"Repo":          "https://git.sr.ht/~poldi1405/gomod-recter",
			"License":       "MIT",
			"DefaultBranch": "master",
			"GoSourceFmt":   "",
			"Note": map[string]interface{}{
				"Show":  true,
				"Text":  "This project is currently looking for a new maintainer. To apply, please reach out to me@my-domain.com",
				"Style": "warning",
			},
		},
	})

	glog.Debug("setting up FS watcher")
	viper.OnConfigChange(func(e fsnotify.Event) {


@@ 80,31 96,37 @@ func loadProjects() {
	var wg sync.WaitGroup
	for k := range projectlist {
		wg.Add(1)
		glog.Debugf("setting up project with values: Name:'%s', Desc:'%s',  VCS:'%s', Repo:'%s'", viper.GetString("Projects."+k+".Name"), viper.GetString("Projects."+k+".Description"), viper.GetString("Projects."+k+".VCS"), viper.GetString("Projects."+k+".Repo"))

		redirTo := viper.GetString("Projects." + k + ".RedirectTo")
		if redirTo == "" {
			redirTo = viper.GetString("Projects." + k + ".Repo")
		}

		glog.Debugf("Project data: %+v", viper.Get("Projects."+k))

		proj := &data.Project{
			Key:         k,
			Name:        viper.GetString("Projects." + k + ".Name"),
			Description: viper.GetString("Projects." + k + ".Description"),
			RootPath:    viper.GetString("Domain") + "/" + k,
			VCS:         viper.GetString("Projects." + k + ".VCS"),
			Repo:        viper.GetString("Projects." + k + ".Repo"),
			License:     viper.GetString("Projects." + k + ".License"),
			Redirect:    viper.GetBool("Projects." + k + ".Redirect"),
			RedirectTo:  redirTo,
			Hidden:      viper.GetBool("Projects." + k + ".Hidden"),
			Key:           k,
			Name:          viper.GetString("Projects." + k + ".Name"),
			Description:   viper.GetString("Projects." + k + ".Description"),
			RootPath:      viper.GetString("Domain") + "/" + k,
			VCS:           viper.GetString("Projects." + k + ".VCS"),
			Repo:          viper.GetString("Projects." + k + ".Repo"),
			License:       viper.GetString("Projects." + k + ".License"),
			Redirect:      viper.GetBool("Projects." + k + ".Redirect"),
			RedirectTo:    redirTo,
			Hidden:        viper.GetBool("Projects." + k + ".Hidden"),
			DefaultBranch: viper.GetString("Projects." + k + ".DefaultBranch"),
			GoSourceFmt:   viper.GetString("Projects." + k + ".GoSourceFmt"),
			Note: &data.Note{
				Show:  viper.GetBool("Projects." + k + ".Note.Show"),
				Style: viper.GetString("Projects." + k + ".Note.Style"),
				Text:  viper.GetString("Projects." + k + ".Note.Text"),
			},
			Versions: make([]string, 0),
			Versions:         make([]string, 0),
			LatestCommitTime: time.Time{},
			LatestCommitHash: "",
		}
		glog.Debugf("Parsed project: %+v", proj)
		go func() {
			defer wg.Done()
			proj.GetData()

M internal/data/get-data.go => internal/data/get-data.go +84 -0
@@ 72,3 72,87 @@ func (p *Project) GetData() error {

	return nil
}

func (p *Project) GetGoSource() string {
	// I can't know every format
	if p.GoSourceFmt != "" {
		glog.Debug("custom go-source header defined.")
		return fmt.Sprintf(`<meta name="go-source" content="%s _ %s">`, p.RootPath, p.GoSourceFmt)
	}

	if p.DefaultBranch == "" {
		glog.Debug("no default branch is set. cannot generate go-source header.")
		return ""
	}

	u, err := url.Parse(strings.TrimRight(p.Repo, "/"))
	if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
		glog.Errorf("failed to parse rootpath as URL: %v", err)
		return ""
	}
	glog.Debugf("Host from RootPath: %s", u.Host)
	dir := dirFromUrl(u, p.DefaultBranch)
	file := fileFromUrl(u, p.DefaultBranch)

	if dir == "" || file == "" {
		return ""
	}

	return fmt.Sprintf(`<meta name="go-source" content="%s _ %s %s">`, p.RootPath, dir, file)
}

func dirFromUrl(u *url.URL, DefaultBranch string) string {
	switch u.Host {
	// everything but sourcehut should be considered untested!
	case "git.sr.ht", "github.com":
		return fmt.Sprintf("%s/tree/%s{/dir}", u, DefaultBranch)
	case "gitlab.com":
		return fmt.Sprintf("%s/-/tree/%s{/dir}", u, DefaultBranch)
	case "codeberg.org", "git.disroot.org": // gitea
		return fmt.Sprintf("%s/src/branch/%s{/dir}", u, DefaultBranch)
	case "notabug.org": // gogs
		return fmt.Sprintf("%s/src/%s{/dir}", u, DefaultBranch)
	case "git.code.sf.net": // sourceforge.net, is someone still using that?
		return fmt.Sprintf("https://sourceforge.net%s/ci/%s/tree{/dir}", u.Path, DefaultBranch)
	case "repo.or.cz":
		return fmt.Sprintf("%s/tree/refs/heads/%s:{/dir}", u, DefaultBranch)

		// These are selfhosted instances. You can specify them in the GoSourceFmt key
		//
		// case "cgit":
		//	return "https://[cgit-instance]/[repo]/tree/{/dir}?h=[default-branch]"
		// case "gitea":
		//	return "https://[gitea-instance]/[user]/[repo]/src/branch/[default-branch]{/dir}"
		// case "gogs":
		//	return "https://[gogs-instance]/[user]/[repo]/src/[default-branch]{/dir}"
	}
	return ""
}

func fileFromUrl(u *url.URL, DefaultBranch string) string {
	switch u.Host {
	// everything but sourcehut should be considered untested!
	case "git.sr.ht", "github.com":
		return fmt.Sprintf("%s/tree/%s{/dir}/{file}#L{line}", u, DefaultBranch)
	case "gitlab.com":
		return fmt.Sprintf("%s/-/tree/%s{/dir}/{file}#L{line}", u, DefaultBranch)
	case "codeberg.org", "git.disroot.org": // gitea
		return fmt.Sprintf("%s/src/branch/%s{/dir}/{file}#L{line}", u, DefaultBranch)
	case "notabug.org": // gogs
		return fmt.Sprintf("%s/src/%s{/dir}/{file}#L{line}", u, DefaultBranch)
	case "git.code.sf.net": // sourceforge.net, is someone still using that?
		return fmt.Sprintf("https://sourceforge.net%s/ci/%s/tree{/dir}/{file}#l{line}", u.Path, DefaultBranch)
	case "repo.or.cz":
		return fmt.Sprintf("%s/tree/refs/heads/%s:{/dir}/{file}#l{line}", u, DefaultBranch)

		// These are selfhosted instances. You can specify them in the GoSourceFmt key
		//
		// case "cgit":
		//	return "https://[cgit-instance]/[repo]/tree/{/dir}/{file}?h=[default-branch]#n{line}"
		// case "gitea":
		//	return "https://[gitea-instance]/[user]/[repo]/src/branch/[default-branch]{/dir}/{file}#L{line}"
		// case "gogs":
		//	return "https://[gogs-instance]/[user]/[repo]/src/[default-branch]{/dir}/{file}#L{line}"
	}
	return ""
}

M internal/data/type.go => internal/data/type.go +3 -0
@@ 20,6 20,9 @@ type Project struct {
	RedirectTo string `json:"-"`
	Hidden     bool   `json:"-"`

	DefaultBranch string `json:"-"`
	GoSourceFmt   string `json:"-"`

	Note *Note `json:"-"`

	Versions []string `json:"-"`

M internal/handler/project.go => internal/handler/project.go +8 -1
@@ 2,6 2,7 @@ package handler

import (
	"bytes"
	"fmt"

	"internal/data"



@@ 11,7 12,7 @@ import (
)

func projectHandler(ctx *fasthttp.RequestCtx, project string, remainingPath []byte) {
	glog.Tracef("remainder: %s",remainingPath)
	glog.Tracef("remainder: %s", remainingPath)

	dataset, ok := data.GetProjectList()[project]
	if !ok {


@@ 23,6 24,12 @@ func projectHandler(ctx *fasthttp.RequestCtx, project string, remainingPath []by
	}
	glog.Tracef("Dataset for %s: %v", project, dataset)

	if ctx.URI().QueryArgs().GetBool("go-get") {
		glog.Debug("detected go-get, sending project details")
		ctx.WriteString(fmt.Sprintf(`<html><head><meta name="go-import" content="%s %s %s">%s</head></html>`, dataset.RootPath, dataset.VCS, dataset.Repo, dataset.GetGoSource()))
		return
	}

	if bytes.HasPrefix(remainingPath, []byte("/api")) {
		apiHandler(ctx, &dataset, remainingPath[4:])
		return