1ba991379Sopenharmony_ci/* 2ba991379Sopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd. 3ba991379Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4ba991379Sopenharmony_ci * you may not use this file except in compliance with the License. 5ba991379Sopenharmony_ci * You may obtain a copy of the License at 6ba991379Sopenharmony_ci * 7ba991379Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8ba991379Sopenharmony_ci * 9ba991379Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10ba991379Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11ba991379Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12ba991379Sopenharmony_ci * See the License for the specific language governing permissions and 13ba991379Sopenharmony_ci * limitations under the License. 14ba991379Sopenharmony_ci */ 15ba991379Sopenharmony_ci 16ba991379Sopenharmony_cipackage gitee_common 17ba991379Sopenharmony_ci 18ba991379Sopenharmony_ciimport ( 19ba991379Sopenharmony_ci "bufio" 20ba991379Sopenharmony_ci "bytes" 21ba991379Sopenharmony_ci "encoding/xml" 22ba991379Sopenharmony_ci "fmt" 23ba991379Sopenharmony_ci "fotff/vcs" 24ba991379Sopenharmony_ci "fotff/vcs/gitee" 25ba991379Sopenharmony_ci "github.com/huandu/go-clone" 26ba991379Sopenharmony_ci "github.com/sirupsen/logrus" 27ba991379Sopenharmony_ci "os" 28ba991379Sopenharmony_ci "path/filepath" 29ba991379Sopenharmony_ci "regexp" 30ba991379Sopenharmony_ci "sort" 31ba991379Sopenharmony_ci "strconv" 32ba991379Sopenharmony_ci "strings" 33ba991379Sopenharmony_ci "sync" 34ba991379Sopenharmony_ci "time" 35ba991379Sopenharmony_ci) 36ba991379Sopenharmony_ci 37ba991379Sopenharmony_citype IssueInfo struct { 38ba991379Sopenharmony_ci visited bool 39ba991379Sopenharmony_ci RelatedIssues []string 40ba991379Sopenharmony_ci MRs []*gitee.Commit 41ba991379Sopenharmony_ci StructCTime string 42ba991379Sopenharmony_ci StructureUpdates []*vcs.ProjectUpdate 43ba991379Sopenharmony_ci} 44ba991379Sopenharmony_ci 45ba991379Sopenharmony_citype Step struct { 46ba991379Sopenharmony_ci IssueURLs []string 47ba991379Sopenharmony_ci MRs []*gitee.Commit 48ba991379Sopenharmony_ci StructCTime string 49ba991379Sopenharmony_ci StructureUpdates []*vcs.ProjectUpdate 50ba991379Sopenharmony_ci} 51ba991379Sopenharmony_ci 52ba991379Sopenharmony_cifunc (m *Manager) stepsFromGitee(from, to string) (pkgs []string, err error) { 53ba991379Sopenharmony_ci updates, err := m.getRepoUpdates(from, to) 54ba991379Sopenharmony_ci if err != nil { 55ba991379Sopenharmony_ci return nil, err 56ba991379Sopenharmony_ci } 57ba991379Sopenharmony_ci startTime, err := parseTime(from) 58ba991379Sopenharmony_ci if err != nil { 59ba991379Sopenharmony_ci return nil, err 60ba991379Sopenharmony_ci } 61ba991379Sopenharmony_ci endTime, err := parseTime(to) 62ba991379Sopenharmony_ci if err != nil { 63ba991379Sopenharmony_ci return nil, err 64ba991379Sopenharmony_ci } 65ba991379Sopenharmony_ci logrus.Infof("find %d repo updates from %s to %s", len(updates), from, to) 66ba991379Sopenharmony_ci steps, err := getAllStepsFromGitee(startTime, endTime, m.Branch, m.ManifestBranch, updates) 67ba991379Sopenharmony_ci if err != nil { 68ba991379Sopenharmony_ci return nil, err 69ba991379Sopenharmony_ci } 70ba991379Sopenharmony_ci logrus.Infof("find total %d steps from %s to %s", len(steps), from, to) 71ba991379Sopenharmony_ci baseManifest, err := vcs.ParseManifestFile(filepath.Join(m.Workspace, from, "manifest_tag.xml")) 72ba991379Sopenharmony_ci if err != nil { 73ba991379Sopenharmony_ci return nil, err 74ba991379Sopenharmony_ci } 75ba991379Sopenharmony_ci for _, step := range steps { 76ba991379Sopenharmony_ci var newPkg string 77ba991379Sopenharmony_ci if newPkg, baseManifest, err = m.genStepPackage(baseManifest, step); err != nil { 78ba991379Sopenharmony_ci return nil, err 79ba991379Sopenharmony_ci } 80ba991379Sopenharmony_ci pkgs = append(pkgs, newPkg) 81ba991379Sopenharmony_ci } 82ba991379Sopenharmony_ci return pkgs, nil 83ba991379Sopenharmony_ci} 84ba991379Sopenharmony_ci 85ba991379Sopenharmony_cifunc (m *Manager) getRepoUpdates(from, to string) (updates []vcs.ProjectUpdate, err error) { 86ba991379Sopenharmony_ci m1, err := vcs.ParseManifestFile(filepath.Join(m.Workspace, from, "manifest_tag.xml")) 87ba991379Sopenharmony_ci if err != nil { 88ba991379Sopenharmony_ci return nil, err 89ba991379Sopenharmony_ci } 90ba991379Sopenharmony_ci m2, err := vcs.ParseManifestFile(filepath.Join(m.Workspace, to, "manifest_tag.xml")) 91ba991379Sopenharmony_ci if err != nil { 92ba991379Sopenharmony_ci return nil, err 93ba991379Sopenharmony_ci } 94ba991379Sopenharmony_ci return vcs.GetRepoUpdates(m1, m2) 95ba991379Sopenharmony_ci} 96ba991379Sopenharmony_ci 97ba991379Sopenharmony_cifunc getAllStepsFromGitee(startTime, endTime time.Time, branch string, manifestBranch string, updates []vcs.ProjectUpdate) (ret []Step, err error) { 98ba991379Sopenharmony_ci allMRs, err := getAllMRs(startTime, endTime, branch, manifestBranch, updates) 99ba991379Sopenharmony_ci if err != nil { 100ba991379Sopenharmony_ci return nil, err 101ba991379Sopenharmony_ci } 102ba991379Sopenharmony_ci issueInfos, err := combineMRsToIssue(allMRs, branch) 103ba991379Sopenharmony_ci if err != nil { 104ba991379Sopenharmony_ci return nil, err 105ba991379Sopenharmony_ci } 106ba991379Sopenharmony_ci return combineIssuesToStep(issueInfos) 107ba991379Sopenharmony_ci} 108ba991379Sopenharmony_ci 109ba991379Sopenharmony_cifunc getAllMRs(startTime, endTime time.Time, branch string, manifestBranch string, updates []vcs.ProjectUpdate) (allMRs []*gitee.Commit, err error) { 110ba991379Sopenharmony_ci var once sync.Once 111ba991379Sopenharmony_ci for _, update := range updates { 112ba991379Sopenharmony_ci var prs []*gitee.Commit 113ba991379Sopenharmony_ci if update.P1.StructureDiff(update.P2) { 114ba991379Sopenharmony_ci once.Do(func() { 115ba991379Sopenharmony_ci prs, err = gitee.GetBetweenTimeMRs("openharmony", "manifest", manifestBranch, startTime, endTime) 116ba991379Sopenharmony_ci }) 117ba991379Sopenharmony_ci if update.P1 != nil { 118ba991379Sopenharmony_ci var p1 []*gitee.Commit 119ba991379Sopenharmony_ci p1, err = gitee.GetBetweenTimeMRs("openharmony", update.P1.Name, branch, startTime, endTime) 120ba991379Sopenharmony_ci prs = append(prs, p1...) 121ba991379Sopenharmony_ci } 122ba991379Sopenharmony_ci if update.P2 != nil { 123ba991379Sopenharmony_ci var p2 []*gitee.Commit 124ba991379Sopenharmony_ci p2, err = gitee.GetBetweenTimeMRs("openharmony", update.P2.Name, branch, startTime, endTime) 125ba991379Sopenharmony_ci prs = append(prs, p2...) 126ba991379Sopenharmony_ci } 127ba991379Sopenharmony_ci } else { 128ba991379Sopenharmony_ci prs, err = gitee.GetBetweenMRs(gitee.CompareParam{ 129ba991379Sopenharmony_ci Head: update.P2.Revision, 130ba991379Sopenharmony_ci Base: update.P1.Revision, 131ba991379Sopenharmony_ci Owner: "openharmony", 132ba991379Sopenharmony_ci Repo: update.P2.Name, 133ba991379Sopenharmony_ci }) 134ba991379Sopenharmony_ci } 135ba991379Sopenharmony_ci if err != nil { 136ba991379Sopenharmony_ci return nil, err 137ba991379Sopenharmony_ci } 138ba991379Sopenharmony_ci allMRs = append(allMRs, prs...) 139ba991379Sopenharmony_ci } 140ba991379Sopenharmony_ci logrus.Infof("find total %d merge request commits of all repo updates", len(allMRs)) 141ba991379Sopenharmony_ci return 142ba991379Sopenharmony_ci} 143ba991379Sopenharmony_ci 144ba991379Sopenharmony_cifunc combineMRsToIssue(allMRs []*gitee.Commit, branch string) (map[string]*IssueInfo, error) { 145ba991379Sopenharmony_ci ret := make(map[string]*IssueInfo) 146ba991379Sopenharmony_ci for _, mr := range allMRs { 147ba991379Sopenharmony_ci num, err := strconv.Atoi(strings.Trim(regexp.MustCompile(`!\d+ `).FindString(mr.Commit.Message), "! ")) 148ba991379Sopenharmony_ci if err != nil { 149ba991379Sopenharmony_ci return nil, fmt.Errorf("parse MR message for %s fail: %s", mr.URL, err) 150ba991379Sopenharmony_ci } 151ba991379Sopenharmony_ci issues, err := gitee.GetMRIssueURL(mr.Owner, mr.Repo, num) 152ba991379Sopenharmony_ci if err != nil { 153ba991379Sopenharmony_ci return nil, err 154ba991379Sopenharmony_ci } 155ba991379Sopenharmony_ci if len(issues) == 0 { 156ba991379Sopenharmony_ci issues = []string{mr.URL} 157ba991379Sopenharmony_ci } 158ba991379Sopenharmony_ci var scs []*vcs.ProjectUpdate 159ba991379Sopenharmony_ci var scTime string 160ba991379Sopenharmony_ci if mr.Owner == "openharmony" && mr.Repo == "manifest" { 161ba991379Sopenharmony_ci if scTime, scs, err = parseStructureUpdates(mr, branch); err != nil { 162ba991379Sopenharmony_ci return nil, err 163ba991379Sopenharmony_ci } 164ba991379Sopenharmony_ci } 165ba991379Sopenharmony_ci for i, issue := range issues { 166ba991379Sopenharmony_ci if _, ok := ret[issue]; !ok { 167ba991379Sopenharmony_ci ret[issue] = &IssueInfo{ 168ba991379Sopenharmony_ci MRs: []*gitee.Commit{mr}, 169ba991379Sopenharmony_ci RelatedIssues: append(issues[:i], issues[i+1:]...), 170ba991379Sopenharmony_ci StructCTime: scTime, 171ba991379Sopenharmony_ci StructureUpdates: scs, 172ba991379Sopenharmony_ci } 173ba991379Sopenharmony_ci } else { 174ba991379Sopenharmony_ci ret[issue] = &IssueInfo{ 175ba991379Sopenharmony_ci MRs: append(ret[issue].MRs, mr), 176ba991379Sopenharmony_ci RelatedIssues: append(ret[issue].RelatedIssues, append(issues[:i], issues[i+1:]...)...), 177ba991379Sopenharmony_ci StructCTime: scTime, 178ba991379Sopenharmony_ci StructureUpdates: append(ret[issue].StructureUpdates, scs...), 179ba991379Sopenharmony_ci } 180ba991379Sopenharmony_ci } 181ba991379Sopenharmony_ci } 182ba991379Sopenharmony_ci } 183ba991379Sopenharmony_ci logrus.Infof("find total %d issues of all repo updates", len(ret)) 184ba991379Sopenharmony_ci return ret, nil 185ba991379Sopenharmony_ci} 186ba991379Sopenharmony_ci 187ba991379Sopenharmony_cifunc combineOtherRelatedIssue(parent, self *IssueInfo, all map[string]*IssueInfo) { 188ba991379Sopenharmony_ci if self.visited { 189ba991379Sopenharmony_ci return 190ba991379Sopenharmony_ci } 191ba991379Sopenharmony_ci self.visited = true 192ba991379Sopenharmony_ci for _, other := range self.RelatedIssues { 193ba991379Sopenharmony_ci if son, ok := all[other]; ok { 194ba991379Sopenharmony_ci combineOtherRelatedIssue(self, son, all) 195ba991379Sopenharmony_ci delete(all, other) 196ba991379Sopenharmony_ci } 197ba991379Sopenharmony_ci } 198ba991379Sopenharmony_ci parent.RelatedIssues = deDupIssues(append(parent.RelatedIssues, self.RelatedIssues...)) 199ba991379Sopenharmony_ci parent.MRs = deDupMRs(append(parent.MRs, self.MRs...)) 200ba991379Sopenharmony_ci parent.StructureUpdates = deDupProjectUpdates(append(parent.StructureUpdates, self.StructureUpdates...)) 201ba991379Sopenharmony_ci if len(parent.StructCTime) != 0 && parent.StructCTime < self.StructCTime { 202ba991379Sopenharmony_ci parent.StructCTime = self.StructCTime 203ba991379Sopenharmony_ci } 204ba991379Sopenharmony_ci} 205ba991379Sopenharmony_ci 206ba991379Sopenharmony_cifunc deDupProjectUpdates(us []*vcs.ProjectUpdate) (retMRs []*vcs.ProjectUpdate) { 207ba991379Sopenharmony_ci dupIndexes := make([]bool, len(us)) 208ba991379Sopenharmony_ci for i := range us { 209ba991379Sopenharmony_ci for j := i + 1; j < len(us); j++ { 210ba991379Sopenharmony_ci if us[j].P1 == us[i].P1 && us[j].P2 == us[i].P2 { 211ba991379Sopenharmony_ci dupIndexes[j] = true 212ba991379Sopenharmony_ci } 213ba991379Sopenharmony_ci } 214ba991379Sopenharmony_ci } 215ba991379Sopenharmony_ci for i, dup := range dupIndexes { 216ba991379Sopenharmony_ci if dup { 217ba991379Sopenharmony_ci continue 218ba991379Sopenharmony_ci } 219ba991379Sopenharmony_ci retMRs = append(retMRs, us[i]) 220ba991379Sopenharmony_ci } 221ba991379Sopenharmony_ci return 222ba991379Sopenharmony_ci} 223ba991379Sopenharmony_ci 224ba991379Sopenharmony_cifunc deDupMRs(mrs []*gitee.Commit) (retMRs []*gitee.Commit) { 225ba991379Sopenharmony_ci tmp := make(map[string]*gitee.Commit) 226ba991379Sopenharmony_ci for _, m := range mrs { 227ba991379Sopenharmony_ci tmp[m.SHA] = m 228ba991379Sopenharmony_ci } 229ba991379Sopenharmony_ci for _, m := range tmp { 230ba991379Sopenharmony_ci retMRs = append(retMRs, m) 231ba991379Sopenharmony_ci } 232ba991379Sopenharmony_ci return 233ba991379Sopenharmony_ci} 234ba991379Sopenharmony_ci 235ba991379Sopenharmony_cifunc deDupIssues(issues []string) (retIssues []string) { 236ba991379Sopenharmony_ci tmp := make(map[string]string) 237ba991379Sopenharmony_ci for _, i := range issues { 238ba991379Sopenharmony_ci tmp[i] = i 239ba991379Sopenharmony_ci } 240ba991379Sopenharmony_ci for _, i := range tmp { 241ba991379Sopenharmony_ci retIssues = append(retIssues, i) 242ba991379Sopenharmony_ci } 243ba991379Sopenharmony_ci return 244ba991379Sopenharmony_ci} 245ba991379Sopenharmony_ci 246ba991379Sopenharmony_ci// parseStructureUpdates get changed XMLs and parse it to recognize repo structure changes. 247ba991379Sopenharmony_ci// Since we do not care which revision a repo was, P1 is not welly handled, just assign it not nil for performance. 248ba991379Sopenharmony_cifunc parseStructureUpdates(commit *gitee.Commit, branch string) (string, []*vcs.ProjectUpdate, error) { 249ba991379Sopenharmony_ci tmp := make(map[string]vcs.ProjectUpdate) 250ba991379Sopenharmony_ci if len(commit.Files) == 0 { 251ba991379Sopenharmony_ci // commit that queried from MR req does not contain file details, should fetch again 252ba991379Sopenharmony_ci var err error 253ba991379Sopenharmony_ci if commit, err = gitee.GetCommit(commit.Owner, commit.Repo, commit.SHA); err != nil { 254ba991379Sopenharmony_ci return "", nil, err 255ba991379Sopenharmony_ci } 256ba991379Sopenharmony_ci } 257ba991379Sopenharmony_ci for _, f := range commit.Files { 258ba991379Sopenharmony_ci if filepath.Ext(f.Filename) != ".xml" { 259ba991379Sopenharmony_ci continue 260ba991379Sopenharmony_ci } 261ba991379Sopenharmony_ci if err := parseFilePatch(f.Patch, tmp); err != nil { 262ba991379Sopenharmony_ci return "", nil, err 263ba991379Sopenharmony_ci } 264ba991379Sopenharmony_ci } 265ba991379Sopenharmony_ci var ret []*vcs.ProjectUpdate 266ba991379Sopenharmony_ci for _, pu := range tmp { 267ba991379Sopenharmony_ci projectUpdateCopy := pu 268ba991379Sopenharmony_ci ret = append(ret, &projectUpdateCopy) 269ba991379Sopenharmony_ci } 270ba991379Sopenharmony_ci for _, pu := range ret { 271ba991379Sopenharmony_ci if pu.P1 == nil && pu.P2 != nil { 272ba991379Sopenharmony_ci lastCommit, err := gitee.GetLatestMRBefore("openharmony", pu.P2.Name, branch, commit.Commit.Committer.Date) 273ba991379Sopenharmony_ci if err != nil { 274ba991379Sopenharmony_ci return "", nil, err 275ba991379Sopenharmony_ci } 276ba991379Sopenharmony_ci pu.P2.Revision = lastCommit.SHA 277ba991379Sopenharmony_ci } 278ba991379Sopenharmony_ci } 279ba991379Sopenharmony_ci return commit.Commit.Committer.Date, ret, nil 280ba991379Sopenharmony_ci} 281ba991379Sopenharmony_ci 282ba991379Sopenharmony_cifunc parseFilePatch(str string, m map[string]vcs.ProjectUpdate) error { 283ba991379Sopenharmony_ci sc := bufio.NewScanner(bytes.NewBuffer([]byte(str))) 284ba991379Sopenharmony_ci for sc.Scan() { 285ba991379Sopenharmony_ci line := sc.Text() 286ba991379Sopenharmony_ci var p vcs.Project 287ba991379Sopenharmony_ci if strings.HasPrefix(line, "-") { 288ba991379Sopenharmony_ci if err := xml.Unmarshal([]byte(line[1:]), &p); err == nil { 289ba991379Sopenharmony_ci m[p.Name] = vcs.ProjectUpdate{P1: &p, P2: m[p.Name].P2} 290ba991379Sopenharmony_ci } 291ba991379Sopenharmony_ci } else if strings.HasPrefix(line, "+") { 292ba991379Sopenharmony_ci if err := xml.Unmarshal([]byte(line[1:]), &p); err == nil { 293ba991379Sopenharmony_ci m[p.Name] = vcs.ProjectUpdate{P1: m[p.Name].P1, P2: &p} 294ba991379Sopenharmony_ci } 295ba991379Sopenharmony_ci } 296ba991379Sopenharmony_ci } 297ba991379Sopenharmony_ci return nil 298ba991379Sopenharmony_ci} 299ba991379Sopenharmony_ci 300ba991379Sopenharmony_cifunc combineIssuesToStep(issueInfos map[string]*IssueInfo) (ret []Step, err error) { 301ba991379Sopenharmony_ci for _, info := range issueInfos { 302ba991379Sopenharmony_ci combineOtherRelatedIssue(info, info, issueInfos) 303ba991379Sopenharmony_ci } 304ba991379Sopenharmony_ci for issue, infos := range issueInfos { 305ba991379Sopenharmony_ci sort.Slice(infos.MRs, func(i, j int) bool { 306ba991379Sopenharmony_ci // move the latest MR to the first place, use its merged_time to represent the update time of the issue 307ba991379Sopenharmony_ci return infos.MRs[i].Commit.Committer.Date > infos.MRs[j].Commit.Committer.Date 308ba991379Sopenharmony_ci }) 309ba991379Sopenharmony_ci ret = append(ret, Step{ 310ba991379Sopenharmony_ci IssueURLs: append(infos.RelatedIssues, issue), 311ba991379Sopenharmony_ci MRs: infos.MRs, 312ba991379Sopenharmony_ci StructCTime: infos.StructCTime, 313ba991379Sopenharmony_ci StructureUpdates: infos.StructureUpdates}) 314ba991379Sopenharmony_ci } 315ba991379Sopenharmony_ci sort.Slice(ret, func(i, j int) bool { 316ba991379Sopenharmony_ci ti, tj := ret[i].MRs[0].Commit.Committer.Date, ret[j].MRs[0].Commit.Committer.Date 317ba991379Sopenharmony_ci if len(ret[i].StructCTime) != 0 { 318ba991379Sopenharmony_ci ti = ret[i].StructCTime 319ba991379Sopenharmony_ci } 320ba991379Sopenharmony_ci if len(ret[j].StructCTime) != 0 { 321ba991379Sopenharmony_ci ti = ret[j].StructCTime 322ba991379Sopenharmony_ci } 323ba991379Sopenharmony_ci return ti < tj 324ba991379Sopenharmony_ci }) 325ba991379Sopenharmony_ci logrus.Infof("find total %d steps of all issues", len(ret)) 326ba991379Sopenharmony_ci return 327ba991379Sopenharmony_ci} 328ba991379Sopenharmony_ci 329ba991379Sopenharmony_cifunc parseTime(pkg string) (time.Time, error) { 330ba991379Sopenharmony_ci t, err := time.ParseInLocation(`20060102_150405`, regexp.MustCompile(`\d{8}_\d{6}`).FindString(pkg), time.Local) 331ba991379Sopenharmony_ci if err != nil { 332ba991379Sopenharmony_ci return time.ParseInLocation(`20060102150405`, regexp.MustCompile(`\d{14}`).FindString(pkg), time.Local) 333ba991379Sopenharmony_ci } 334ba991379Sopenharmony_ci return t, nil 335ba991379Sopenharmony_ci} 336ba991379Sopenharmony_ci 337ba991379Sopenharmony_cifunc (m *Manager) genStepPackage(base *vcs.Manifest, step Step) (newPkg string, newManifest *vcs.Manifest, err error) { 338ba991379Sopenharmony_ci defer func() { 339ba991379Sopenharmony_ci logrus.Infof("package dir %s for step %v generated", newPkg, step.IssueURLs) 340ba991379Sopenharmony_ci }() 341ba991379Sopenharmony_ci newManifest = clone.Clone(base).(*vcs.Manifest) 342ba991379Sopenharmony_ci for _, u := range step.StructureUpdates { 343ba991379Sopenharmony_ci if u.P2 != nil { 344ba991379Sopenharmony_ci newManifest.UpdateManifestProject(u.P2.Name, u.P2.Path, u.P2.Remote, u.P2.Revision, true) 345ba991379Sopenharmony_ci } else if u.P1 != nil { 346ba991379Sopenharmony_ci newManifest.RemoveManifestProject(u.P1.Name) 347ba991379Sopenharmony_ci } 348ba991379Sopenharmony_ci } 349ba991379Sopenharmony_ci for _, mr := range step.MRs { 350ba991379Sopenharmony_ci newManifest.UpdateManifestProject(mr.Repo, "", "", mr.SHA, false) 351ba991379Sopenharmony_ci } 352ba991379Sopenharmony_ci md5sum, err := newManifest.Standardize() 353ba991379Sopenharmony_ci if err != nil { 354ba991379Sopenharmony_ci return "", nil, err 355ba991379Sopenharmony_ci } 356ba991379Sopenharmony_ci if err := os.MkdirAll(filepath.Join(m.Workspace, md5sum), 0750); err != nil { 357ba991379Sopenharmony_ci return "", nil, err 358ba991379Sopenharmony_ci } 359ba991379Sopenharmony_ci if err := os.WriteFile(filepath.Join(m.Workspace, md5sum, "__last_issue__"), []byte(fmt.Sprintf("%v", step.IssueURLs)), 0640); err != nil { 360ba991379Sopenharmony_ci return "", nil, err 361ba991379Sopenharmony_ci } 362ba991379Sopenharmony_ci err = newManifest.WriteFile(filepath.Join(m.Workspace, md5sum, "manifest_tag.xml")) 363ba991379Sopenharmony_ci if err != nil { 364ba991379Sopenharmony_ci return "", nil, err 365ba991379Sopenharmony_ci } 366ba991379Sopenharmony_ci return md5sum, newManifest, nil 367ba991379Sopenharmony_ci} 368