A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go
at refactor 103 lines 2.9 kB view raw
1package readme 2 3import ( 4 "fmt" 5 "net/url" 6 "strings" 7) 8 9// Platform represents a supported Git hosting platform 10type Platform string 11 12const ( 13 PlatformGitHub Platform = "github" 14 PlatformGitLab Platform = "gitlab" 15 PlatformTangled Platform = "tangled" 16) 17 18// ParseSourceURL extracts platform, user, and repo from a source repository URL. 19// Returns ok=false if the URL is not a recognized pattern. 20func ParseSourceURL(sourceURL string) (platform Platform, user, repo string, ok bool) { 21 if sourceURL == "" { 22 return "", "", "", false 23 } 24 25 parsed, err := url.Parse(sourceURL) 26 if err != nil { 27 return "", "", "", false 28 } 29 30 // Normalize: remove trailing slash and .git suffix 31 path := strings.TrimSuffix(parsed.Path, "/") 32 path = strings.TrimSuffix(path, ".git") 33 path = strings.TrimPrefix(path, "/") 34 35 if path == "" { 36 return "", "", "", false 37 } 38 39 host := strings.ToLower(parsed.Host) 40 41 switch { 42 case host == "github.com": 43 // GitHub: github.com/{user}/{repo} 44 parts := strings.SplitN(path, "/", 3) 45 if len(parts) < 2 || parts[0] == "" || parts[1] == "" { 46 return "", "", "", false 47 } 48 return PlatformGitHub, parts[0], parts[1], true 49 50 case host == "gitlab.com": 51 // GitLab: gitlab.com/{user}/{repo} or gitlab.com/{group}/{subgroup}/{repo} 52 // For nested groups, user = everything except last part, repo = last part 53 lastSlash := strings.LastIndex(path, "/") 54 if lastSlash == -1 || lastSlash == 0 { 55 return "", "", "", false 56 } 57 user = path[:lastSlash] 58 repo = path[lastSlash+1:] 59 if user == "" || repo == "" { 60 return "", "", "", false 61 } 62 return PlatformGitLab, user, repo, true 63 64 case host == "tangled.org" || host == "tangled.sh": 65 // Tangled: tangled.org/{user}/{repo} or tangled.sh/@{user}/{repo} (legacy) 66 // Strip leading @ from user if present 67 path = strings.TrimPrefix(path, "@") 68 parts := strings.SplitN(path, "/", 3) 69 if len(parts) < 2 || parts[0] == "" || parts[1] == "" { 70 return "", "", "", false 71 } 72 return PlatformTangled, parts[0], parts[1], true 73 74 default: 75 return "", "", "", false 76 } 77} 78 79// DeriveReadmeURL converts a source repository URL to a raw README URL. 80// Returns empty string if platform is not supported. 81func DeriveReadmeURL(sourceURL, branch string) string { 82 platform, user, repo, ok := ParseSourceURL(sourceURL) 83 if !ok { 84 return "" 85 } 86 87 switch platform { 88 case PlatformGitHub: 89 // https://raw.githubusercontent.com/{user}/{repo}/refs/heads/{branch}/README.md 90 return fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/refs/heads/%s/README.md", user, repo, branch) 91 92 case PlatformGitLab: 93 // https://gitlab.com/{user}/{repo}/-/raw/{branch}/README.md 94 return fmt.Sprintf("https://gitlab.com/%s/%s/-/raw/%s/README.md", user, repo, branch) 95 96 case PlatformTangled: 97 // https://tangled.org/{user}/{repo}/raw/{branch}/README.md 98 return fmt.Sprintf("https://tangled.org/%s/%s/raw/%s/README.md", user, repo, branch) 99 100 default: 101 return "" 102 } 103}