github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/vcs/svn.go (about) 1 package vcs 2 3 import ( 4 "encoding/xml" 5 "os" 6 "strings" 7 8 "github.com/fossas/fossa-cli/errors" 9 "github.com/fossas/fossa-cli/exec" 10 ) 11 12 // SubversionRepository implements the System interface. 13 type SubversionRepository struct { 14 dir string 15 cmd string 16 info svnInfo 17 } 18 19 // NewSubversionRepository uses the Subversion repository's metadata at dir to identify the codebase. 20 func NewSubversionRepository(dir string) (*SubversionRepository, error) { 21 cmd, _, err := exec.Which("--version", os.Getenv("SVN_BINARY"), "svn") 22 if err != nil { 23 return nil, errors.Wrap(err, "could not find svn binary") 24 } 25 stdout, _, err := exec.Run(exec.Cmd{ 26 Name: cmd, 27 Argv: []string{"info", "--xml"}, 28 Dir: dir, 29 }) 30 if err != nil { 31 return nil, errors.Wrapf(err, "could not run `%s info`", cmd) 32 } 33 34 repo := SubversionRepository{ 35 dir: dir, 36 cmd: cmd, 37 } 38 39 if err = repo.info.unmarshalXML([]byte(stdout)); err != nil { 40 return nil, err 41 } 42 return &repo, nil 43 } 44 45 func (s *SubversionRepository) Project() string { return s.info.Entry.URL } 46 47 func (s *SubversionRepository) Head() Revision { 48 return Revision{ 49 Branch: svnBranchFromInfo(&s.info), 50 RevisionID: s.info.Entry.Revision, 51 } 52 } 53 54 // svnBranchFromInfo extracts the name of the branch from the info provided. 55 func svnBranchFromInfo(info *svnInfo) string { 56 relativeURL := strings.TrimPrefix(info.Entry.RelativeURL, "^") 57 58 // trimmed has just what follows the path of the URL locating the project. 59 trimmed := strings.TrimPrefix( 60 strings.TrimPrefix(info.Entry.URL, info.Entry.Repository.Root), 61 relativeURL, 62 ) 63 64 // Branches are typically identified by being under this directory. 65 branches := "/branches/" 66 67 if strings.HasPrefix(trimmed, branches) { 68 // This is an ordinary branch. 69 trimmed = strings.TrimPrefix(trimmed, branches) 70 } else { 71 trimmed = strings.TrimPrefix(trimmed, "/") 72 } 73 74 if trimmed != "" { 75 return trimmed 76 } 77 return "trunk" 78 } 79 80 // The svnInfo type represents the result of running `svn info --xml`. 81 type svnInfo struct { 82 Entry infoEntry `xml:"entry"` 83 } 84 85 type infoEntry struct { 86 Path string `xml:"path,attr"` 87 88 // Revision is the latest revision ID, a numeric string. 89 Revision string `xml:"revision,attr"` 90 Kind string `xml:"kind,attr"` 91 92 // URL is the remote location from which the repo can be downloaded. 93 URL string `xml:"url"` 94 RelativeURL string `xml:"relative-url"` 95 Repository struct { 96 Root string `xml:"root"` 97 Uuid string `xml:"uuid"` 98 } `xml:"repository"` 99 WcInfo struct { 100 WcrootAbspath string `xml:"wcroot-abspath"` 101 Schedule string `xml:"schedule"` 102 Depth string `xml:"depth"` 103 } `xml:"wc-info"` 104 } 105 106 func (s *svnInfo) unmarshalXML(data []byte) error { 107 return xml.Unmarshal(data, s) 108 }