1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16package vcs 17 18import ( 19 "crypto/md5" 20 "encoding/xml" 21 "fmt" 22 "github.com/sirupsen/logrus" 23 "os" 24 "sort" 25) 26 27type Manifest struct { 28 XMLName xml.Name `xml:"manifest"` 29 Remote Remote `xml:"remote"` 30 Default Default `xml:"default"` 31 Projects []Project `xml:"project"` 32} 33 34type Remote struct { 35 Name string `xml:"name,attr"` 36 Fetch string `xml:"fetch,attr"` 37 Review string `xml:"review,attr"` 38} 39 40type Default struct { 41 Remote string `xml:"remote,attr"` 42 Revision string `xml:"revision,attr"` 43 SyncJ string `xml:"sync-j,attr"` 44} 45 46type Project struct { 47 XMLName xml.Name `xml:"project"` 48 Name string `xml:"name,attr"` 49 Path string `xml:"path,attr,omitempty"` 50 Revision string `xml:"revision,attr"` 51 Remote string `xml:"remote,attr,omitempty"` 52 CloneDepth string `xml:"clone-depth,attr,omitempty"` 53 LinkFile []LinkFile `xml:"linkfile,omitempty"` 54} 55 56type LinkFile struct { 57 Src string `xml:"src,attr"` 58 Dest string `xml:"dest,attr"` 59} 60 61type ProjectUpdate struct { 62 P1, P2 *Project 63} 64 65func (p *Project) String() string { 66 if p == nil { 67 return "<nil>" 68 } 69 return fmt.Sprintf("<%s>", p.Name) 70} 71 72func (p *Project) StructureDiff(p2 *Project) bool { 73 if p == nil && p2 != nil || p != nil && p2 == nil { 74 return true 75 } 76 if p == nil && p2 == nil { 77 return false 78 } 79 return p.Name != p2.Name || p.Path != p2.Path || p.Remote != p2.Remote 80} 81 82func (p *Project) Equals(p2 *Project) bool { 83 return p.Name == p2.Name && p.Path == p2.Path && p.Remote == p2.Remote && p.Revision == p2.Revision 84} 85 86func ParseManifestFile(file string) (*Manifest, error) { 87 data, err := os.ReadFile(file) 88 if err != nil { 89 return nil, err 90 } 91 var m Manifest 92 err = xml.Unmarshal(data, &m) 93 return &m, err 94} 95 96func (m *Manifest) WriteFile(filePath string) error { 97 data, err := xml.MarshalIndent(m, "", " ") 98 if err != nil { 99 return err 100 } 101 data = append([]byte(xml.Header), data...) 102 return os.WriteFile(filePath, data, 0640) 103} 104 105func GetRepoUpdates(m1, m2 *Manifest) (updates []ProjectUpdate, err error) { 106 if _, err := m1.Standardize(); err != nil { 107 return nil, err 108 } 109 if _, err := m2.Standardize(); err != nil { 110 return nil, err 111 } 112 var j int 113 for i := 0; i < len(m1.Projects); { 114 if m2.Projects[j].Name == m1.Projects[i].Name { 115 if !m1.Projects[i].Equals(&m2.Projects[j]) { 116 logrus.Infof("%v changes", &m1.Projects[i]) 117 updates = append(updates, ProjectUpdate{ 118 P1: &m1.Projects[i], 119 P2: &m2.Projects[j], 120 }) 121 } 122 i++ 123 j++ 124 } else if m2.Projects[j].Name > m1.Projects[i].Name { 125 logrus.Infof("%v removed", &m1.Projects[i]) 126 updates = append(updates, ProjectUpdate{ 127 P1: &m1.Projects[i], 128 P2: nil, 129 }) 130 i++ 131 } else { // m2.Projects[j].Name < m1.Projects[i].Name 132 logrus.Infof("%v added", &m2.Projects[j]) 133 updates = append(updates, ProjectUpdate{ 134 P1: nil, 135 P2: &m2.Projects[j], 136 }) 137 j++ 138 } 139 } 140 return 141} 142 143func (m *Manifest) UpdateManifestProject(name, path, remote, revision string, add bool) { 144 if name == "manifest" { 145 return 146 } 147 for i, p := range m.Projects { 148 if p.Name == name { 149 if path != "" { 150 m.Projects[i].Path = path 151 } 152 if remote != "" { 153 m.Projects[i].Remote = remote 154 } 155 if revision != "" { 156 m.Projects[i].Revision = revision 157 } 158 return 159 } 160 } 161 if add { 162 m.Projects = append(m.Projects, Project{Name: name, Path: path, Revision: revision, Remote: remote}) 163 } 164} 165 166func (m *Manifest) RemoveManifestProject(name string) { 167 for i, p := range m.Projects { 168 if p.Name == name { 169 m.Projects = append(m.Projects[:i], m.Projects[i:]...) 170 return 171 } 172 } 173} 174 175func (m *Manifest) Standardize() (string, error) { 176 sort.Slice(m.Projects, func(i, j int) bool { 177 return m.Projects[i].Name < m.Projects[j].Name 178 }) 179 data, err := xml.MarshalIndent(m, "", " ") 180 if err != nil { 181 return "", err 182 } 183 data = append([]byte(xml.Header), data...) 184 sumByte := md5.Sum(data) 185 return fmt.Sprintf("%X", sumByte), nil 186} 187