forked from misaka00251/go2spec
d19eadb416
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
142 lines
4.0 KiB
Go
142 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"html"
|
|
"path"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
func getRepoURLForGopkg(gopkg string) (string, error) {
|
|
info, err := getPkgsiteInfo(context.TODO(), gopkg)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
repoURL := strings.TrimSpace(info.Module.RepoURL)
|
|
if repoURL == "" {
|
|
return "", fmt.Errorf("pkgsite module %q has no repository URL", info.Module.Path)
|
|
}
|
|
return gitCloneURLFromRepoURL(repoURL), nil
|
|
}
|
|
|
|
var (
|
|
htmlTagRegexp = regexp.MustCompile(`<[^>]*>`)
|
|
markdownImageRegex = regexp.MustCompile(`!\[[^\]]*\]\([^)]+\)`)
|
|
markdownLinkRegex = regexp.MustCompile(`\[([^\]]+)\]\([^)]+\)`)
|
|
packagePrefixRegex = regexp.MustCompile(`^Package\s+\S+\s+`)
|
|
)
|
|
|
|
// cleanSummaryCandidate turns a godoc synopsis or README line into an
|
|
// RPM-style Summary by stripping markup, keeping the first sentence, dropping
|
|
// the leading "Package foo" convention, and capitalizing the result.
|
|
func cleanSummaryCandidate(summary string) string {
|
|
summary = html.UnescapeString(strings.TrimSpace(summary))
|
|
summary = markdownImageRegex.ReplaceAllString(summary, "")
|
|
summary = markdownLinkRegex.ReplaceAllString(summary, "$1")
|
|
summary = htmlTagRegexp.ReplaceAllString(summary, " ")
|
|
summary = strings.ReplaceAll(summary, "`", "")
|
|
summary = strings.Join(strings.Fields(summary), " ")
|
|
summary = strings.Trim(summary, " \t\n\r#*-_")
|
|
if end := strings.Index(summary, ". "); end >= 0 {
|
|
summary = summary[:end]
|
|
}
|
|
summary = packagePrefixRegex.ReplaceAllString(summary, "")
|
|
if summary != "" && summary[0] >= 'a' && summary[0] <= 'z' {
|
|
summary = string(summary[0]-('a'-'A')) + summary[1:]
|
|
}
|
|
if summary == "" ||
|
|
strings.HasPrefix(summary, "[!") ||
|
|
strings.HasPrefix(summary, "![") ||
|
|
strings.HasPrefix(summary, "<!--") ||
|
|
strings.ContainsAny(summary, "<>") {
|
|
return ""
|
|
}
|
|
return strings.TrimSuffix(summary, ".")
|
|
}
|
|
|
|
// summaryFromReadme returns the first prose line of a README, skipping code
|
|
// fences, headings, HTML comments, badges, admonitions, and images.
|
|
func summaryFromReadme(markdown string) string {
|
|
inFence := false
|
|
lines := strings.Split(markdown, "\n")
|
|
for i, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
if strings.HasPrefix(line, "```") {
|
|
inFence = !inFence
|
|
continue
|
|
}
|
|
if i+1 < len(lines) {
|
|
next := strings.TrimSpace(lines[i+1])
|
|
if next != "" && strings.Trim(next, "=-") == "" {
|
|
continue
|
|
}
|
|
}
|
|
if inFence ||
|
|
line == "" ||
|
|
strings.HasPrefix(line, "#") ||
|
|
strings.HasPrefix(line, "[!") ||
|
|
strings.HasPrefix(line, "![") ||
|
|
strings.HasPrefix(line, "<!--") ||
|
|
strings.HasPrefix(line, "<p align=") ||
|
|
strings.HasPrefix(line, "---") ||
|
|
strings.Trim(line, "=-") == "" {
|
|
continue
|
|
}
|
|
if summary := cleanSummaryCandidate(line); summary != "" {
|
|
return summary
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func unusableSummary(summary, gopkg string, info *pkgsiteInfo) bool {
|
|
if summary == "" {
|
|
return true
|
|
}
|
|
base := path.Base(gopkg)
|
|
moduleBase := path.Base(info.Module.Path)
|
|
return strings.EqualFold(summary, base) ||
|
|
strings.EqualFold(summary, moduleBase) ||
|
|
strings.EqualFold(summary, info.Package.Name)
|
|
}
|
|
|
|
func getLicenseForGopkg(gopkg string) (string, error) {
|
|
info, err := getPkgsiteInfo(context.TODO(), gopkg)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
licenses := info.Module.Licenses
|
|
if len(licenses) == 0 {
|
|
licenses = info.Package.Licenses
|
|
}
|
|
|
|
return pkgsiteLicenseExpression(licenses), nil
|
|
}
|
|
|
|
// getDescriptionForGopkg gets the package synopsis from pkg.go.dev,
|
|
// intended for the summary in the RPM spec.
|
|
func getDescriptionForGopkg(gopkg string) (string, error) {
|
|
info, err := getPkgsiteInfo(context.TODO(), gopkg)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
description := cleanSummaryCandidate(info.Package.Synopsis)
|
|
if unusableSummary(description, gopkg, info) {
|
|
description = ""
|
|
}
|
|
if description == "" && info.Module.Readme != nil {
|
|
description = summaryFromReadme(info.Module.Readme.Contents)
|
|
if unusableSummary(description, gopkg, info) {
|
|
description = ""
|
|
}
|
|
}
|
|
if description == "" {
|
|
return "", fmt.Errorf("no usable synopsis for %q", gopkg)
|
|
}
|
|
return description, nil
|
|
}
|