Add godoc, fix commit hash resolution for submodules
- Add godoc tool (now a separate submodule at cmd/godoc/v0.1.0-deprecated) - Fix update tool to handle godoc's special tag format - All tools now use commit hashes instead of version tags
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -26,6 +27,11 @@ type ToolsFile struct {
|
||||
type ModuleInfo struct {
|
||||
Version string `json:"Version"`
|
||||
Time string `json:"Time"`
|
||||
Origin struct {
|
||||
VCS string `json:"VCS"`
|
||||
URL string `json:"URL"`
|
||||
Hash string `json:"Hash"`
|
||||
} `json:"Origin"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -100,7 +106,7 @@ func main() {
|
||||
output = append(output, '\n')
|
||||
|
||||
absPath, _ := filepath.Abs(*outputPath)
|
||||
if err := os.WriteFile(*outputPath, output, 0644); err != nil {
|
||||
if err := os.WriteFile(*outputPath, output, 0o644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error writing %s: %v\n", *outputPath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -114,26 +120,81 @@ func main() {
|
||||
}
|
||||
|
||||
// getModulePath extracts the module path from a package path
|
||||
// e.g., "golang.org/x/tools/cmd/goimports" -> "golang.org/x/tools"
|
||||
func getModulePath(pkg string) string {
|
||||
// Handle special cases where cmd is in a subpath
|
||||
if strings.Contains(pkg, "/cmd/") {
|
||||
parts := strings.Split(pkg, "/cmd/")
|
||||
return parts[0]
|
||||
}
|
||||
// For packages like "github.com/cweill/gotests/gotests"
|
||||
// we need the parent module
|
||||
if strings.Contains(pkg, "/gojson") && strings.Contains(pkg, "ChimeraCoder") {
|
||||
return "github.com/ChimeraCoder/gojson"
|
||||
}
|
||||
if strings.HasSuffix(pkg, "/gotests") {
|
||||
return strings.TrimSuffix(pkg, "/gotests")
|
||||
}
|
||||
// gopls is a submodule of golang.org/x/tools
|
||||
if strings.HasSuffix(pkg, "/gopls") {
|
||||
return pkg
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
|
||||
// getRepoPath extracts the git repository path from a module path
|
||||
func getRepoPath(modPath string) string {
|
||||
// golang.org/x/tools/gopls -> golang.org/x/tools
|
||||
if strings.HasPrefix(modPath, "golang.org/x/") {
|
||||
parts := strings.Split(modPath, "/")
|
||||
if len(parts) >= 3 {
|
||||
return strings.Join(parts[:3], "/")
|
||||
}
|
||||
}
|
||||
return modPath
|
||||
}
|
||||
|
||||
// getGitURL returns the git URL for a module path
|
||||
func getGitURL(modPath string) string {
|
||||
// Get the repo path first
|
||||
repoPath := getRepoPath(modPath)
|
||||
|
||||
// Handle vanity imports
|
||||
switch {
|
||||
case strings.HasPrefix(repoPath, "golang.org/x/"):
|
||||
name := strings.TrimPrefix(repoPath, "golang.org/x/")
|
||||
return "https://github.com/golang/" + name + ".git"
|
||||
case strings.HasPrefix(repoPath, "honnef.co/go/tools"):
|
||||
return "https://github.com/dominikh/go-tools.git"
|
||||
case strings.HasPrefix(repoPath, "mvdan.cc/"):
|
||||
name := strings.TrimPrefix(repoPath, "mvdan.cc/")
|
||||
return "https://github.com/mvdan/" + name + ".git"
|
||||
default:
|
||||
// Assume github.com/user/repo format
|
||||
parts := strings.Split(repoPath, "/")
|
||||
if len(parts) >= 3 {
|
||||
return "https://" + strings.Join(parts[:3], "/") + ".git"
|
||||
}
|
||||
return "https://" + repoPath + ".git"
|
||||
}
|
||||
}
|
||||
|
||||
// getTagPattern returns the git tag pattern for a module
|
||||
func getTagPattern(pkg, version string) string {
|
||||
// gopls has tags like "gopls/v0.21.0"
|
||||
if strings.Contains(pkg, "/gopls") {
|
||||
return "gopls/" + version
|
||||
}
|
||||
// godoc has tags like "cmd/godoc/v0.1.0-deprecated"
|
||||
if strings.HasSuffix(pkg, "/cmd/godoc") {
|
||||
return "cmd/godoc/" + version
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
// pseudoVersionRe matches pseudo-versions like v0.0.0-20250907133731-34b10582faa4
|
||||
var pseudoVersionRe = regexp.MustCompile(`^v\d+\.\d+\.\d+-\d{14}-([a-f0-9]{12})$`)
|
||||
|
||||
func fetchLatestVersion(tool Tool) (Tool, error) {
|
||||
modPath := getModulePath(tool.Package)
|
||||
|
||||
// First get the latest version info
|
||||
cmd := exec.Command("go", "list", "-m", "-json", modPath+"@latest")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
@@ -153,14 +214,80 @@ func fetchLatestVersion(tool Tool) (Tool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it's already a pseudo-version with commit hash
|
||||
if matches := pseudoVersionRe.FindStringSubmatch(info.Version); matches != nil {
|
||||
// Extract the 12-char hash from pseudo-version, we need the full hash
|
||||
shortHash := matches[1]
|
||||
gitURL := getGitURL(modPath)
|
||||
fullHash, err := resolveShortHash(gitURL, shortHash)
|
||||
if err != nil {
|
||||
return Tool{}, fmt.Errorf("resolve hash failed: %w", err)
|
||||
}
|
||||
return Tool{
|
||||
Name: tool.Name,
|
||||
Package: tool.Package,
|
||||
Version: fullHash,
|
||||
Date: date,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// It's a tagged version, resolve to commit hash
|
||||
gitURL := getGitURL(modPath)
|
||||
tagPattern := getTagPattern(tool.Package, info.Version)
|
||||
|
||||
hash, err := resolveTagToHash(gitURL, tagPattern)
|
||||
if err != nil {
|
||||
return Tool{}, fmt.Errorf("resolve tag failed: %w", err)
|
||||
}
|
||||
|
||||
return Tool{
|
||||
Name: tool.Name,
|
||||
Package: tool.Package,
|
||||
Version: info.Version,
|
||||
Version: hash,
|
||||
Date: date,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func resolveTagToHash(gitURL, tag string) (string, error) {
|
||||
cmd := exec.Command("git", "ls-remote", "--tags", gitURL, tag)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("git ls-remote failed: %w", err)
|
||||
}
|
||||
|
||||
lines := strings.TrimSpace(string(output))
|
||||
if lines == "" {
|
||||
return "", fmt.Errorf("tag %s not found", tag)
|
||||
}
|
||||
|
||||
// Format: <hash>\t<ref>
|
||||
parts := strings.Fields(lines)
|
||||
if len(parts) < 1 {
|
||||
return "", fmt.Errorf("unexpected git ls-remote output")
|
||||
}
|
||||
|
||||
return parts[0], nil
|
||||
}
|
||||
|
||||
func resolveShortHash(gitURL, shortHash string) (string, error) {
|
||||
// Use git ls-remote with the short hash to find full hash
|
||||
cmd := exec.Command("git", "ls-remote", gitURL)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("git ls-remote failed: %w", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 1 && strings.HasPrefix(parts[0], shortHash) {
|
||||
return parts[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("hash %s not found", shortHash)
|
||||
}
|
||||
|
||||
type result struct {
|
||||
idx int
|
||||
tool Tool
|
||||
|
||||
Reference in New Issue
Block a user