/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package vcs import ( "crypto/md5" "encoding/xml" "fmt" "github.com/sirupsen/logrus" "os" "sort" ) type Manifest struct { XMLName xml.Name `xml:"manifest"` Remote Remote `xml:"remote"` Default Default `xml:"default"` Projects []Project `xml:"project"` } type Remote struct { Name string `xml:"name,attr"` Fetch string `xml:"fetch,attr"` Review string `xml:"review,attr"` } type Default struct { Remote string `xml:"remote,attr"` Revision string `xml:"revision,attr"` SyncJ string `xml:"sync-j,attr"` } type Project struct { XMLName xml.Name `xml:"project"` Name string `xml:"name,attr"` Path string `xml:"path,attr,omitempty"` Revision string `xml:"revision,attr"` Remote string `xml:"remote,attr,omitempty"` CloneDepth string `xml:"clone-depth,attr,omitempty"` LinkFile []LinkFile `xml:"linkfile,omitempty"` } type LinkFile struct { Src string `xml:"src,attr"` Dest string `xml:"dest,attr"` } type ProjectUpdate struct { P1, P2 *Project } func (p *Project) String() string { if p == nil { return "" } return fmt.Sprintf("<%s>", p.Name) } func (p *Project) StructureDiff(p2 *Project) bool { if p == nil && p2 != nil || p != nil && p2 == nil { return true } if p == nil && p2 == nil { return false } return p.Name != p2.Name || p.Path != p2.Path || p.Remote != p2.Remote } func (p *Project) Equals(p2 *Project) bool { return p.Name == p2.Name && p.Path == p2.Path && p.Remote == p2.Remote && p.Revision == p2.Revision } func ParseManifestFile(file string) (*Manifest, error) { data, err := os.ReadFile(file) if err != nil { return nil, err } var m Manifest err = xml.Unmarshal(data, &m) return &m, err } func (m *Manifest) WriteFile(filePath string) error { data, err := xml.MarshalIndent(m, "", " ") if err != nil { return err } data = append([]byte(xml.Header), data...) return os.WriteFile(filePath, data, 0640) } func GetRepoUpdates(m1, m2 *Manifest) (updates []ProjectUpdate, err error) { if _, err := m1.Standardize(); err != nil { return nil, err } if _, err := m2.Standardize(); err != nil { return nil, err } var j int for i := 0; i < len(m1.Projects); { if m2.Projects[j].Name == m1.Projects[i].Name { if !m1.Projects[i].Equals(&m2.Projects[j]) { logrus.Infof("%v changes", &m1.Projects[i]) updates = append(updates, ProjectUpdate{ P1: &m1.Projects[i], P2: &m2.Projects[j], }) } i++ j++ } else if m2.Projects[j].Name > m1.Projects[i].Name { logrus.Infof("%v removed", &m1.Projects[i]) updates = append(updates, ProjectUpdate{ P1: &m1.Projects[i], P2: nil, }) i++ } else { // m2.Projects[j].Name < m1.Projects[i].Name logrus.Infof("%v added", &m2.Projects[j]) updates = append(updates, ProjectUpdate{ P1: nil, P2: &m2.Projects[j], }) j++ } } return } func (m *Manifest) UpdateManifestProject(name, path, remote, revision string, add bool) { if name == "manifest" { return } for i, p := range m.Projects { if p.Name == name { if path != "" { m.Projects[i].Path = path } if remote != "" { m.Projects[i].Remote = remote } if revision != "" { m.Projects[i].Revision = revision } return } } if add { m.Projects = append(m.Projects, Project{Name: name, Path: path, Revision: revision, Remote: remote}) } } func (m *Manifest) RemoveManifestProject(name string) { for i, p := range m.Projects { if p.Name == name { m.Projects = append(m.Projects[:i], m.Projects[i:]...) return } } } func (m *Manifest) Standardize() (string, error) { sort.Slice(m.Projects, func(i, j int) bool { return m.Projects[i].Name < m.Projects[j].Name }) data, err := xml.MarshalIndent(m, "", " ") if err != nil { return "", err } data = append([]byte(xml.Header), data...) sumByte := md5.Sum(data) return fmt.Sprintf("%X", sumByte), nil }