github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/git/submodule.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package git
     7  
     8  import (
     9  	"fmt"
    10  	"net"
    11  	"net/url"
    12  	"path"
    13  	"regexp"
    14  	"strings"
    15  )
    16  
    17  var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`)
    18  
    19  // SubModule submodule is a reference on git repository
    20  type SubModule struct {
    21  	Name string
    22  	URL  string
    23  }
    24  
    25  // SubModuleFile represents a file with submodule type.
    26  type SubModuleFile struct {
    27  	*Commit
    28  
    29  	refURL string
    30  	refID  string
    31  }
    32  
    33  // NewSubModuleFile create a new submodule file
    34  func NewSubModuleFile(c *Commit, refURL, refID string) *SubModuleFile {
    35  	return &SubModuleFile{
    36  		Commit: c,
    37  		refURL: refURL,
    38  		refID:  refID,
    39  	}
    40  }
    41  
    42  func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
    43  	if refURL == "" {
    44  		return ""
    45  	}
    46  
    47  	refURI := strings.TrimSuffix(refURL, ".git")
    48  
    49  	prefixURL, _ := url.Parse(urlPrefix)
    50  	urlPrefixHostname, _, err := net.SplitHostPort(prefixURL.Host)
    51  	if err != nil {
    52  		urlPrefixHostname = prefixURL.Host
    53  	}
    54  
    55  	urlPrefix = strings.TrimSuffix(urlPrefix, "/")
    56  
    57  	// FIXME: Need to consider branch - which will require changes in modules/git/commit.go:GetSubModules
    58  	// Relative url prefix check (according to git submodule documentation)
    59  	if strings.HasPrefix(refURI, "./") || strings.HasPrefix(refURI, "../") {
    60  		return urlPrefix + path.Clean(path.Join("/", repoFullName, refURI))
    61  	}
    62  
    63  	if !strings.Contains(refURI, "://") {
    64  		// scp style syntax which contains *no* port number after the : (and is not parsed by net/url)
    65  		// ex: git@try.gitea.io:go-gitea/gitea
    66  		match := scpSyntax.FindAllStringSubmatch(refURI, -1)
    67  		if len(match) > 0 {
    68  
    69  			m := match[0]
    70  			refHostname := m[2]
    71  			pth := m[3]
    72  
    73  			if !strings.HasPrefix(pth, "/") {
    74  				pth = "/" + pth
    75  			}
    76  
    77  			if urlPrefixHostname == refHostname || refHostname == sshDomain {
    78  				return urlPrefix + path.Clean(path.Join("/", pth))
    79  			}
    80  			return "http://" + refHostname + pth
    81  		}
    82  	}
    83  
    84  	ref, err := url.Parse(refURI)
    85  	if err != nil {
    86  		return ""
    87  	}
    88  
    89  	refHostname, _, err := net.SplitHostPort(ref.Host)
    90  	if err != nil {
    91  		refHostname = ref.Host
    92  	}
    93  
    94  	supportedSchemes := []string{"http", "https", "git", "ssh", "git+ssh"}
    95  
    96  	for _, scheme := range supportedSchemes {
    97  		if ref.Scheme == scheme {
    98  			if ref.Scheme == "http" || ref.Scheme == "https" {
    99  				if len(ref.User.Username()) > 0 {
   100  					return ref.Scheme + "://" + fmt.Sprintf("%v", ref.User) + "@" + ref.Host + ref.Path
   101  				}
   102  				return ref.Scheme + "://" + ref.Host + ref.Path
   103  			} else if urlPrefixHostname == refHostname || refHostname == sshDomain {
   104  				return urlPrefix + path.Clean(path.Join("/", ref.Path))
   105  			} else {
   106  				return "http://" + refHostname + ref.Path
   107  			}
   108  		}
   109  	}
   110  
   111  	return ""
   112  }
   113  
   114  // RefURL guesses and returns reference URL.
   115  func (sf *SubModuleFile) RefURL(urlPrefix, repoFullName, sshDomain string) string {
   116  	return getRefURL(sf.refURL, urlPrefix, repoFullName, sshDomain)
   117  }
   118  
   119  // RefID returns reference ID.
   120  func (sf *SubModuleFile) RefID() string {
   121  	return sf.refID
   122  }