mirror of
https://github.com/hashicorp/terraform.git
synced 2026-03-21 18:10:30 -04:00
137 lines
3.2 KiB
Go
137 lines
3.2 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package moduleaddrs
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// detectGit translates Git SSH URLs into normal-shaped URLs.
|
|
func detectGit(src string) (string, bool, error) {
|
|
if len(src) == 0 {
|
|
return "", false, nil
|
|
}
|
|
|
|
u, err := detectSSH(src)
|
|
if err != nil {
|
|
return "", true, err
|
|
}
|
|
if u == nil {
|
|
return "", false, nil
|
|
}
|
|
|
|
// We require the username to be "git" to assume that this is a Git URL
|
|
if u.User.Username() != "git" {
|
|
return "", false, nil
|
|
}
|
|
|
|
return "git::" + u.String(), true, nil
|
|
}
|
|
|
|
// detectGitHub detects shorthand schemeless references to github.com and
|
|
// translates them into git HTTP source addresses.
|
|
func detectGitHub(src string) (string, bool, error) {
|
|
if len(src) == 0 {
|
|
return "", false, nil
|
|
}
|
|
|
|
if strings.HasPrefix(src, "github.com/") {
|
|
src, rawQuery, _ := strings.Cut(src, "?")
|
|
|
|
parts := strings.Split(src, "/")
|
|
if len(parts) < 3 {
|
|
return "", false, fmt.Errorf(
|
|
"GitHub URLs should be github.com/username/repo")
|
|
}
|
|
|
|
urlStr := fmt.Sprintf("https://%s", strings.Join(parts[:3], "/"))
|
|
url, err := url.Parse(urlStr)
|
|
if err != nil {
|
|
return "", true, fmt.Errorf("error parsing GitHub URL: %s", err)
|
|
}
|
|
url.RawQuery = rawQuery
|
|
|
|
if !strings.HasSuffix(url.Path, ".git") {
|
|
url.Path += ".git"
|
|
}
|
|
|
|
if len(parts) > 3 {
|
|
url.Path += "//" + strings.Join(parts[3:], "/")
|
|
}
|
|
|
|
return "git::" + url.String(), true, nil
|
|
}
|
|
|
|
return "", false, nil
|
|
}
|
|
|
|
// detectBitBucket detects shorthand schemeless references to bitbucket.org and
|
|
// translates them into git HTTP source addresses.
|
|
func detectBitBucket(src string) (string, bool, error) {
|
|
if len(src) == 0 {
|
|
return "", false, nil
|
|
}
|
|
|
|
if strings.HasPrefix(src, "bitbucket.org/") {
|
|
u, err := url.Parse("https://" + src)
|
|
if err != nil {
|
|
return "", true, fmt.Errorf("error parsing BitBucket URL: %s", err)
|
|
}
|
|
|
|
// NOTE: A long, long time ago bitbucket.org repositories could
|
|
// potentially be either Git or Mercurial repositories and we would've
|
|
// needed to make an API call here to know which to generate.
|
|
//
|
|
// Thankfully BitBucket now only supports Git, and so we can just
|
|
// assume all bitbucket.org strings are trying to refer to Git
|
|
// repositories.
|
|
|
|
if !strings.HasSuffix(u.Path, ".git") {
|
|
u.Path += ".git"
|
|
}
|
|
|
|
return "git::" + u.String(), true, nil
|
|
}
|
|
|
|
return "", false, nil
|
|
}
|
|
|
|
// sshPattern matches SCP-like SSH patterns (user@host:path)
|
|
var sshPattern = regexp.MustCompile("^(?:([^@]+)@)?([^:]+):/?(.+)$")
|
|
|
|
// detectSSH determines if the src string matches an SSH-like URL and
|
|
// converts it into a net.URL. This returns nil if the string doesn't match
|
|
// the SSH pattern.
|
|
func detectSSH(src string) (*url.URL, error) {
|
|
matched := sshPattern.FindStringSubmatch(src)
|
|
if matched == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
user := matched[1]
|
|
host := matched[2]
|
|
path := matched[3]
|
|
qidx := strings.Index(path, "?")
|
|
if qidx == -1 {
|
|
qidx = len(path)
|
|
}
|
|
|
|
var u url.URL
|
|
u.Scheme = "ssh"
|
|
u.User = url.User(user)
|
|
u.Host = host
|
|
u.Path = path[0:qidx]
|
|
if qidx < len(path) {
|
|
q, err := url.ParseQuery(path[qidx+1:])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error parsing Git SSH URL: %s", err)
|
|
}
|
|
u.RawQuery = q.Encode()
|
|
}
|
|
|
|
return &u, nil
|
|
}
|