diff --git a/cmd/srpmproc/main.go b/cmd/srpmproc/main.go index b343c11..0132e75 100644 --- a/cmd/srpmproc/main.go +++ b/cmd/srpmproc/main.go @@ -21,23 +21,8 @@ package main import ( - "fmt" "github.com/rocky-linux/srpmproc/pkg/srpmproc" "log" - "os" - "os/user" - "path/filepath" - "strings" - - "github.com/go-git/go-billy/v5" - "github.com/go-git/go-billy/v5/memfs" - "github.com/go-git/go-billy/v5/osfs" - "github.com/go-git/go-git/v5/plumbing/transport/ssh" - "github.com/rocky-linux/srpmproc/pkg/blob" - "github.com/rocky-linux/srpmproc/pkg/blob/file" - "github.com/rocky-linux/srpmproc/pkg/blob/gcs" - "github.com/rocky-linux/srpmproc/pkg/blob/s3" - "github.com/rocky-linux/srpmproc/pkg/data" "github.com/spf13/cobra" ) @@ -62,7 +47,6 @@ var ( noStorageDownload bool noStorageUpload bool manualCommits string - upstreamPrefixHttps string moduleFallbackStream string allowStreamBranches bool ) @@ -73,104 +57,37 @@ var root = &cobra.Command{ } func mn(_ *cobra.Command, _ []string) { - switch version { - case 8: - break - default: - log.Fatalf("unsupported upstream version %d", version) - } - - var importer data.ImportMode - var blobStorage blob.Storage - - if strings.HasPrefix(storageAddr, "gs://") { - blobStorage = gcs.New(strings.Replace(storageAddr, "gs://", "", 1)) - } else if strings.HasPrefix(storageAddr, "s3://") { - blobStorage = s3.New(strings.Replace(storageAddr, "s3://", "", 1)) - } else if strings.HasPrefix(storageAddr, "file://") { - blobStorage = file.New(strings.Replace(storageAddr, "file://", "", 1)) - } else { - log.Fatalf("invalid blob storage") - } - - sourceRpmLocation := "" - if strings.HasPrefix(sourceRpm, "file://") { - sourceRpmLocation = strings.TrimPrefix(sourceRpm, "file://") - importer = &srpmproc.SrpmMode{} - } else { - if moduleMode { - sourceRpmLocation = fmt.Sprintf("%s/%s", modulePrefix, sourceRpm) - } else { - sourceRpmLocation = fmt.Sprintf("%s/%s", rpmPrefix, sourceRpm) - } - importer = &srpmproc.GitMode{} - } - - lastKeyLocation := sshKeyLocation - if lastKeyLocation == "" { - usr, err := user.Current() - if err != nil { - log.Fatalf("could not get user: %v", err) - } - lastKeyLocation = filepath.Join(usr.HomeDir, ".ssh/id_rsa") - } - - var authenticator *ssh.PublicKeys - - var err error - // create ssh key authenticator - authenticator, err = ssh.NewPublicKeysFromFile(sshUser, lastKeyLocation, "") - if err != nil { - log.Fatalf("could not get git authenticator: %v", err) - } - - fsCreator := func(branch string) billy.Filesystem { - return memfs.New() - } - - if tmpFsMode != "" { - log.Printf("using tmpfs dir: %s", tmpFsMode) - fsCreator = func(branch string) billy.Filesystem { - tmpDir := filepath.Join(tmpFsMode, branch) - err := os.MkdirAll(tmpDir, 0755) - if err != nil { - log.Fatalf("could not create tmpfs dir: %v", err) - } - return osfs.New(tmpDir) - } - } - - var manualCs []string - if strings.TrimSpace(manualCommits) != "" { - manualCs = strings.Split(manualCommits, ",") - } - - srpmproc.ProcessRPM(&data.ProcessData{ - Importer: importer, - RpmLocation: sourceRpmLocation, - UpstreamPrefix: upstreamPrefix, - SshKeyLocation: sshKeyLocation, - SshUser: sshUser, + pd, err := srpmproc.NewProcessData(&srpmproc.ProcessDataRequest{ Version: version, - BlobStorage: blobStorage, - GitCommitterName: gitCommitterName, - GitCommitterEmail: gitCommitterEmail, - ModulePrefix: modulePrefix, - ImportBranchPrefix: importBranchPrefix, - BranchPrefix: branchPrefix, - SingleTag: singleTag, - Authenticator: authenticator, - NoDupMode: noDupMode, + StorageAddr: storageAddr, + Package: sourceRpm, ModuleMode: moduleMode, TmpFsMode: tmpFsMode, - NoStorageDownload: noStorageDownload, - NoStorageUpload: noStorageUpload, - ManualCommits: manualCs, - UpstreamPrefixHttps: upstreamPrefixHttps, - ModuleFallbackStream: moduleFallbackStream, + ModulePrefix: modulePrefix, + RpmPrefix: rpmPrefix, + SshKeyLocation: sshKeyLocation, + SshUser: sshUser, + ManualCommits: manualCommits, + UpstreamPrefix: upstreamPrefix, + GitCommitterName: gitCommitterName, + GitCommitterEmail: gitCommitterEmail, + ImportBranchPrefix: importBranchPrefix, + BranchPrefix: branchPrefix, + NoDupMode: noDupMode, AllowStreamBranches: allowStreamBranches, - FsCreator: fsCreator, + ModuleFallbackStream: moduleFallbackStream, + NoStorageUpload: noStorageUpload, + NoStorageDownload: noStorageDownload, + SingleTag: singleTag, }) + if err != nil { + log.Fatal(err) + } + + err = srpmproc.ProcessRPM(pd) + if err != nil { + log.Fatal(err) + } } func main() { @@ -198,7 +115,6 @@ func main() { root.Flags().BoolVar(&noStorageDownload, "no-storage-download", false, "If enabled, blobs are always downloaded from upstream") root.Flags().BoolVar(&noStorageUpload, "no-storage-upload", false, "If enabled, blobs are not uploaded to blob storage") root.Flags().StringVar(&manualCommits, "manual-commits", "", "Comma separated branch and commit list for packages with broken release tags (Format: BRANCH:HASH)") - root.Flags().StringVar(&upstreamPrefixHttps, "upstream-prefix-https", "", "Web version of upstream prefix. Required if module-mode") root.Flags().StringVar(&moduleFallbackStream, "module-fallback-stream", "", "Override fallback stream. Some module packages are published as collections and mostly use the same stream name, some of them deviate from the main stream") root.Flags().BoolVar(&allowStreamBranches, "allow-stream-branches", false, "Allow import from stream branches") diff --git a/pkg/data/import.go b/pkg/data/import.go index 860c27d..9f13778 100644 --- a/pkg/data/import.go +++ b/pkg/data/import.go @@ -21,22 +21,21 @@ package data import ( - "github.com/cavaliercoder/go-rpm" "github.com/go-git/go-git/v5" "hash" ) type ImportMode interface { - RetrieveSource(pd *ProcessData) *ModeData - WriteSource(pd *ProcessData, md *ModeData) - PostProcess(md *ModeData) + RetrieveSource(pd *ProcessData) (*ModeData, error) + WriteSource(pd *ProcessData, md *ModeData) error + PostProcess(md *ModeData) error ImportName(pd *ProcessData, md *ModeData) string } type ModeData struct { + Name string Repo *git.Repository Worktree *git.Worktree - RpmFile *rpm.PackageFile FileWrites map[string][]byte TagBranch string PushBranch string diff --git a/pkg/data/process.go b/pkg/data/process.go index 7e58ea0..36471c3 100644 --- a/pkg/data/process.go +++ b/pkg/data/process.go @@ -26,6 +26,8 @@ import ( "github.com/rocky-linux/srpmproc/pkg/blob" ) +type FsCreatorFunc func(branch string) (billy.Filesystem, error) + type ProcessData struct { RpmLocation string UpstreamPrefix string @@ -48,8 +50,7 @@ type ProcessData struct { NoStorageDownload bool NoStorageUpload bool ManualCommits []string - UpstreamPrefixHttps string ModuleFallbackStream string AllowStreamBranches bool - FsCreator func(branch string) billy.Filesystem + FsCreator FsCreatorFunc } diff --git a/pkg/directives/spec_change.go b/pkg/directives/spec_change.go index d58611f..5489137 100644 --- a/pkg/directives/spec_change.go +++ b/pkg/directives/spec_change.go @@ -234,7 +234,7 @@ func specChange(cfg *srpmprocpb.Cfg, pd *data.ProcessData, md *data.ModeData, _ hasPatch := false version := "" - importName := strings.Replace(pd.Importer.ImportName(pd, md), md.RpmFile.Name(), "1", 1) + importName := strings.Replace(pd.Importer.ImportName(pd, md), md.Name, "1", 1) importNameSplit := strings.SplitN(importName, "-", 2) if len(importNameSplit) == 2 { versionSplit := strings.SplitN(importNameSplit[1], ".el", 2) diff --git a/pkg/misc/regex.go b/pkg/misc/regex.go new file mode 100644 index 0000000..61f1aa7 --- /dev/null +++ b/pkg/misc/regex.go @@ -0,0 +1,14 @@ +package misc + +import ( + "fmt" + "regexp" +) + +func GetTagImportRegex(importBranchPrefix string, allowStreamBranches bool) *regexp.Regexp { + if allowStreamBranches { + return regexp.MustCompile(fmt.Sprintf("refs/tags/(imports/(%s(?:.s|.)|%s(?:|s).+)/(.*))", importBranchPrefix, importBranchPrefix)) + } else { + return regexp.MustCompile(fmt.Sprintf("refs/tags/(imports/(%s.|%s.-.+)/(.*))", importBranchPrefix, importBranchPrefix)) + } +} diff --git a/pkg/srpmproc/git.go b/pkg/modes/git.go similarity index 75% rename from pkg/srpmproc/git.go rename to pkg/modes/git.go index 8fd7153..5e12ddd 100644 --- a/pkg/srpmproc/git.go +++ b/pkg/modes/git.go @@ -18,10 +18,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -package srpmproc +package modes import ( "fmt" + "github.com/rocky-linux/srpmproc/pkg/misc" "io/ioutil" "log" "net/http" @@ -60,15 +61,15 @@ func (p remoteTargetSlice) Swap(i, j int) { type GitMode struct{} -func (g *GitMode) RetrieveSource(pd *data.ProcessData) *data.ModeData { +func (g *GitMode) RetrieveSource(pd *data.ProcessData) (*data.ModeData, error) { repo, err := git.Init(memory.NewStorage(), memfs.New()) if err != nil { - log.Fatalf("could not init git Repo: %v", err) + return nil, fmt.Errorf("could not init git Repo: %v", err) } w, err := repo.Worktree() if err != nil { - log.Fatalf("could not get Worktree: %v", err) + return nil, fmt.Errorf("could not get Worktree: %v", err) } refspec := config.RefSpec("+refs/heads/*:refs/remotes/*") @@ -78,7 +79,7 @@ func (g *GitMode) RetrieveSource(pd *data.ProcessData) *data.ModeData { Fetch: []config.RefSpec{refspec}, }) if err != nil { - log.Fatalf("could not create remote: %v", err) + return nil, fmt.Errorf("could not create remote: %v", err) } err = remote.Fetch(&git.FetchOptions{ @@ -87,7 +88,7 @@ func (g *GitMode) RetrieveSource(pd *data.ProcessData) *data.ModeData { Force: true, }) if err != nil { - log.Fatalf("could not fetch upstream: %v", err) + return nil, fmt.Errorf("could not fetch upstream: %v", err) } var branches remoteTargetSlice @@ -97,8 +98,8 @@ func (g *GitMode) RetrieveSource(pd *data.ProcessData) *data.ModeData { tagAdd := func(tag *object.Tag) error { if strings.HasPrefix(tag.Name, fmt.Sprintf("imports/%s%d", pd.ImportBranchPrefix, pd.Version)) { refSpec := fmt.Sprintf("refs/tags/%s", tag.Name) - if tagImportRegex.MatchString(refSpec) { - match := tagImportRegex.FindStringSubmatch(refSpec) + if misc.GetTagImportRegex(pd.ImportBranchPrefix, pd.AllowStreamBranches).MatchString(refSpec) { + match := misc.GetTagImportRegex(pd.ImportBranchPrefix, pd.AllowStreamBranches).FindStringSubmatch(refSpec) exists := latestTags[match[2]] if exists != nil && exists.when.After(tag.Tagger.When) { @@ -116,13 +117,13 @@ func (g *GitMode) RetrieveSource(pd *data.ProcessData) *data.ModeData { tagIter, err := repo.TagObjects() if err != nil { - log.Fatalf("could not get tag objects: %v", err) + return nil, fmt.Errorf("could not get tag objects: %v", err) } _ = tagIter.ForEach(tagAdd) list, err := remote.List(&git.ListOptions{}) if err != nil { - log.Fatalf("could not list upstream: %v", err) + return nil, fmt.Errorf("could not list upstream: %v", err) } for _, ref := range list { @@ -153,18 +154,18 @@ func (g *GitMode) RetrieveSource(pd *data.ProcessData) *data.ModeData { } return &data.ModeData{ + Name: filepath.Base(pd.RpmLocation), Repo: repo, Worktree: w, - RpmFile: createPackageFile(filepath.Base(pd.RpmLocation)), FileWrites: nil, Branches: sortedBranches, - } + }, nil } -func (g *GitMode) WriteSource(pd *data.ProcessData, md *data.ModeData) { +func (g *GitMode) WriteSource(pd *data.ProcessData, md *data.ModeData) error { remote, err := md.Repo.Remote("upstream") if err != nil { - log.Fatalf("could not get upstream remote: %v", err) + return fmt.Errorf("could not get upstream remote: %v", err) } var refspec config.RefSpec @@ -174,7 +175,7 @@ func (g *GitMode) WriteSource(pd *data.ProcessData, md *data.ModeData) { refspec = config.RefSpec(fmt.Sprintf("+%s:%s", md.TagBranch, md.TagBranch)) branchName = strings.TrimPrefix(md.TagBranch, "refs/heads/") } else { - match := tagImportRegex.FindStringSubmatch(md.TagBranch) + match := misc.GetTagImportRegex(pd.ImportBranchPrefix, pd.AllowStreamBranches).FindStringSubmatch(md.TagBranch) branchName = match[2] refspec = config.RefSpec(fmt.Sprintf("+refs/heads/%s:%s", branchName, md.TagBranch)) } @@ -186,7 +187,7 @@ func (g *GitMode) WriteSource(pd *data.ProcessData, md *data.ModeData) { Force: true, }) if err != nil && err != git.NoErrAlreadyUpToDate { - log.Fatalf("could not fetch upstream: %v", err) + return fmt.Errorf("could not fetch upstream: %v", err) } err = md.Worktree.Checkout(&git.CheckoutOptions{ @@ -194,23 +195,23 @@ func (g *GitMode) WriteSource(pd *data.ProcessData, md *data.ModeData) { Force: true, }) if err != nil { - log.Fatalf("could not checkout source from git: %v", err) + return fmt.Errorf("could not checkout source from git: %v", err) } _, err = md.Worktree.Add(".") if err != nil { - log.Fatalf("could not add Worktree: %v", err) + return fmt.Errorf("could not add Worktree: %v", err) } - metadataFile, err := md.Worktree.Filesystem.Open(fmt.Sprintf(".%s.metadata", md.RpmFile.Name())) + metadataFile, err := md.Worktree.Filesystem.Open(fmt.Sprintf(".%s.metadata", md.Name)) if err != nil { log.Printf("warn: could not open metadata file, so skipping: %v", err) - return + return nil } fileBytes, err := ioutil.ReadAll(metadataFile) if err != nil { - log.Fatalf("could not read metadata file: %v", err) + return fmt.Errorf("could not read metadata file: %v", err) } client := &http.Client{ @@ -239,27 +240,27 @@ func (g *GitMode) WriteSource(pd *data.ProcessData, md *data.ModeData) { body = fromBlobStorage log.Printf("downloading %s from blob storage", hash) } else { - url := fmt.Sprintf("https://git.centos.org/sources/%s/%s/%s", md.RpmFile.Name(), branchName, hash) + url := fmt.Sprintf("https://git.centos.org/sources/%s/%s/%s", md.Name, branchName, hash) log.Printf("downloading %s", url) req, err := http.NewRequest("GET", url, nil) if err != nil { - log.Fatalf("could not create new http request: %v", err) + return fmt.Errorf("could not create new http request: %v", err) } req.Header.Set("Accept-Encoding", "*") resp, err := client.Do(req) if err != nil { - log.Fatalf("could not download dist-git file: %v", err) + return fmt.Errorf("could not download dist-git file: %v", err) } body, err = ioutil.ReadAll(resp.Body) if err != nil { - log.Fatalf("could not read the whole dist-git file: %v", err) + return fmt.Errorf("could not read the whole dist-git file: %v", err) } err = resp.Body.Close() if err != nil { - log.Fatalf("could not close body handle: %v", err) + return fmt.Errorf("could not close body handle: %v", err) } } @@ -268,7 +269,7 @@ func (g *GitMode) WriteSource(pd *data.ProcessData, md *data.ModeData) { f, err := md.Worktree.Filesystem.Create(path) if err != nil { - log.Fatalf("could not open file pointer: %v", err) + return fmt.Errorf("could not open file pointer: %v", err) } hasher := data.CompareHash(body, hash) @@ -283,32 +284,36 @@ func (g *GitMode) WriteSource(pd *data.ProcessData, md *data.ModeData) { _, err = f.Write(body) if err != nil { - log.Fatalf("could not copy dist-git file to in-tree: %v", err) + return fmt.Errorf("could not copy dist-git file to in-tree: %v", err) } _ = f.Close() } + + return nil } -func (g *GitMode) PostProcess(md *data.ModeData) { +func (g *GitMode) PostProcess(md *data.ModeData) error { for _, source := range md.SourcesToIgnore { _, err := md.Worktree.Filesystem.Stat(source.Name) if err == nil { err := md.Worktree.Filesystem.Remove(source.Name) if err != nil { - log.Fatalf("could not remove dist-git file: %v", err) + return fmt.Errorf("could not remove dist-git file: %v", err) } } } _, err := md.Worktree.Add(".") if err != nil { - log.Fatalf("could not add git sources: %v", err) + return fmt.Errorf("could not add git sources: %v", err) } + + return nil } -func (g *GitMode) ImportName(_ *data.ProcessData, md *data.ModeData) string { - if tagImportRegex.MatchString(md.TagBranch) { - match := tagImportRegex.FindStringSubmatch(md.TagBranch) +func (g *GitMode) ImportName(pd *data.ProcessData, md *data.ModeData) string { + if misc.GetTagImportRegex(pd.ImportBranchPrefix, pd.AllowStreamBranches).MatchString(md.TagBranch) { + match := misc.GetTagImportRegex(pd.ImportBranchPrefix, pd.AllowStreamBranches).FindStringSubmatch(md.TagBranch) return match[3] } diff --git a/pkg/srpmproc/gitrpm.go b/pkg/srpmproc/gitrpm.go deleted file mode 100644 index b29d153..0000000 --- a/pkg/srpmproc/gitrpm.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2021 The Srpmproc Authors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package srpmproc - -import ( - "github.com/cavaliercoder/go-rpm" -) - -// TODO: ugly hack, should create an interface -// since GitMode does not parse an RPM file, we just mimick -// the headers of an actual file to reuse RpmFile.Name() -func createPackageFile(name string) *rpm.PackageFile { - return &rpm.PackageFile{ - Lead: rpm.Lead{}, - Headers: []rpm.Header{ - {}, - { - Version: 0, - IndexCount: 1, - Length: 1, - Indexes: []rpm.IndexEntry{ - { - Tag: 1000, - Type: rpm.IndexDataTypeStringArray, - Offset: 0, - ItemCount: 1, - Value: []string{name}, - }, - }, - }, - }, - } -} diff --git a/pkg/srpmproc/mode.go b/pkg/srpmproc/mode.go deleted file mode 100644 index f4a5374..0000000 --- a/pkg/srpmproc/mode.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2021 The Srpmproc Authors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package srpmproc diff --git a/pkg/srpmproc/patch.go b/pkg/srpmproc/patch.go index 6c92965..9d3edc4 100644 --- a/pkg/srpmproc/patch.go +++ b/pkg/srpmproc/patch.go @@ -40,14 +40,14 @@ import ( "google.golang.org/protobuf/encoding/prototext" ) -func cfgPatches(pd *data.ProcessData, md *data.ModeData, patchTree *git.Worktree, pushTree *git.Worktree) { +func cfgPatches(pd *data.ProcessData, md *data.ModeData, patchTree *git.Worktree, pushTree *git.Worktree) error { // check CFG patches _, err := patchTree.Filesystem.Stat("ROCKY/CFG") if err == nil { // iterate through patches infos, err := patchTree.Filesystem.ReadDir("ROCKY/CFG") if err != nil { - log.Fatalf("could not walk patches: %v", err) + return fmt.Errorf("could not walk patches: %v", err) } for _, info := range infos { @@ -60,44 +60,51 @@ func cfgPatches(pd *data.ProcessData, md *data.ModeData, patchTree *git.Worktree filePath := filepath.Join("ROCKY/CFG", info.Name()) directive, err := patchTree.Filesystem.Open(filePath) if err != nil { - log.Fatalf("could not open directive file %s: %v", info.Name(), err) + return fmt.Errorf("could not open directive file %s: %v", info.Name(), err) } directiveBytes, err := ioutil.ReadAll(directive) if err != nil { - log.Fatalf("could not read directive file: %v", err) + return fmt.Errorf("could not read directive file: %v", err) } var cfg srpmprocpb.Cfg err = prototext.Unmarshal(directiveBytes, &cfg) if err != nil { - log.Fatalf("could not unmarshal cfg file: %v", err) + return fmt.Errorf("could not unmarshal cfg file: %v", err) } directives.Apply(&cfg, pd, md, patchTree, pushTree) } } + + return nil } -func applyPatches(pd *data.ProcessData, md *data.ModeData, patchTree *git.Worktree, pushTree *git.Worktree) { +func applyPatches(pd *data.ProcessData, md *data.ModeData, patchTree *git.Worktree, pushTree *git.Worktree) error { // check if patches exist _, err := patchTree.Filesystem.Stat("ROCKY") if err == nil { - cfgPatches(pd, md, patchTree, pushTree) + err := cfgPatches(pd, md, patchTree, pushTree) + if err != nil { + return err + } } + + return nil } -func executePatchesRpm(pd *data.ProcessData, md *data.ModeData) { +func executePatchesRpm(pd *data.ProcessData, md *data.ModeData) error { // fetch patch repository repo, err := git.Init(memory.NewStorage(), memfs.New()) if err != nil { - log.Fatalf("could not create new dist Repo: %v", err) + return fmt.Errorf("could not create new dist Repo: %v", err) } w, err := repo.Worktree() if err != nil { - log.Fatalf("could not get dist Worktree: %v", err) + return fmt.Errorf("could not get dist Worktree: %v", err) } - remoteUrl := fmt.Sprintf("%s/patch/%s.git", pd.UpstreamPrefix, gitlabify(md.RpmFile.Name())) + remoteUrl := fmt.Sprintf("%s/patch/%s.git", pd.UpstreamPrefix, gitlabify(md.Name)) refspec := config.RefSpec(fmt.Sprintf("+refs/heads/*:refs/remotes/origin/*")) _, err = repo.CreateRemote(&config.RemoteConfig{ @@ -106,7 +113,7 @@ func executePatchesRpm(pd *data.ProcessData, md *data.ModeData) { Fetch: []config.RefSpec{refspec}, }) if err != nil { - log.Fatalf("could not create remote: %v", err) + return fmt.Errorf("could not create remote: %v", err) } fetchOptions := &git.FetchOptions{ @@ -124,7 +131,7 @@ func executePatchesRpm(pd *data.ProcessData, md *data.ModeData) { if err != nil { // no patches active log.Println("info: patch repo not found") - return + return nil } else { err = w.Checkout(&git.CheckoutOptions{ Branch: plumbing.NewRemoteReferenceName("origin", "main"), @@ -132,7 +139,10 @@ func executePatchesRpm(pd *data.ProcessData, md *data.ModeData) { }) // common patches found, apply them if err == nil { - applyPatches(pd, md, w, md.Worktree) + err := applyPatches(pd, md, w, md.Worktree) + if err != nil { + return err + } } else { log.Println("info: no common patches found") } @@ -143,17 +153,22 @@ func executePatchesRpm(pd *data.ProcessData, md *data.ModeData) { }) // branch specific patches found, apply them if err == nil { - applyPatches(pd, md, w, md.Worktree) + err := applyPatches(pd, md, w, md.Worktree) + if err != nil { + return err + } } else { log.Println("info: no branch specific patches found") } } + + return nil } -func getTipStream(pd *data.ProcessData, module string, pushBranch string, origPushBranch string, tries int) string { +func getTipStream(pd *data.ProcessData, module string, pushBranch string, origPushBranch string, tries int) (string, error) { repo, err := git.Init(memory.NewStorage(), memfs.New()) if err != nil { - log.Fatalf("could not init git Repo: %v", err) + return "", fmt.Errorf("could not init git Repo: %v", err) } remoteUrl := fmt.Sprintf("%s/rpms/%s.git", pd.UpstreamPrefix, gitlabify(module)) @@ -164,7 +179,7 @@ func getTipStream(pd *data.ProcessData, module string, pushBranch string, origPu Fetch: []config.RefSpec{refspec}, }) if err != nil { - log.Fatalf("could not create remote: %v", err) + return "", fmt.Errorf("could not create remote: %v", err) } list, err := remote.List(&git.ListOptions{ @@ -178,7 +193,7 @@ func getTipStream(pd *data.ProcessData, module string, pushBranch string, origPu return getTipStream(pd, module, pushBranch, origPushBranch, tries+1) } - log.Fatalf("could not get rpm refs. import the rpm before the module: %v", err) + return "", fmt.Errorf("could not get rpm refs. import the rpm before the module: %v", err) } var tipHash string @@ -238,14 +253,14 @@ func getTipStream(pd *data.ProcessData, module string, pushBranch string, origPu log.Fatal("could not find tip hash") } - return strings.TrimSpace(tipHash) + return strings.TrimSpace(tipHash), nil } -func patchModuleYaml(pd *data.ProcessData, md *data.ModeData) { +func patchModuleYaml(pd *data.ProcessData, md *data.ModeData) error { // special case for platform.yaml _, err := md.Worktree.Filesystem.Open("platform.yaml") if err == nil { - return + return nil } mdTxtPath := "SOURCES/modulemd.src.txt" @@ -254,18 +269,18 @@ func patchModuleYaml(pd *data.ProcessData, md *data.ModeData) { mdTxtPath = "SOURCES/modulemd.txt" f, err = md.Worktree.Filesystem.Open(mdTxtPath) if err != nil { - log.Fatalf("could not open modulemd file: %v", err) + return fmt.Errorf("could not open modulemd file: %v", err) } } content, err := ioutil.ReadAll(f) if err != nil { - log.Fatalf("could not read modulemd file: %v", err) + return fmt.Errorf("could not read modulemd file: %v", err) } module, err := modulemd.Parse(content) if err != nil { - log.Fatalf("could not parse modulemd file: %v", err) + return fmt.Errorf("could not parse modulemd file: %v", err) } // Get stream branch from tag @@ -311,23 +326,31 @@ func patchModuleYaml(pd *data.ProcessData, md *data.ModeData) { log.Fatal("could not recognize modulemd ref") } - tipHash = getTipStream(pd, name, pushBranch, md.PushBranch, 0) + tipHash, err = getTipStream(pd, name, pushBranch, md.PushBranch, 0) + if err != nil { + return err + } if tipHash == "0000000000000000000000000000000000000000" { pushBranch = defaultBranch - tipHash = getTipStream(pd, name, pushBranch, md.PushBranch, 0) + tipHash, err = getTipStream(pd, name, pushBranch, md.PushBranch, 0) + if err != nil { + return err + } } rpm.Ref = tipHash } - rootModule := fmt.Sprintf("%s.yaml", md.RpmFile.Name()) + rootModule := fmt.Sprintf("%s.yaml", md.Name) err = module.Marshal(md.Worktree.Filesystem, rootModule) if err != nil { - log.Fatalf("could not marshal root modulemd: %v", err) + return fmt.Errorf("could not marshal root modulemd: %v", err) } _, err = md.Worktree.Add(rootModule) if err != nil { - log.Fatalf("could not add root modulemd: %v", err) + return fmt.Errorf("could not add root modulemd: %v", err) } + + return nil } diff --git a/pkg/srpmproc/process.go b/pkg/srpmproc/process.go index 9e0f433..d3702ef 100644 --- a/pkg/srpmproc/process.go +++ b/pkg/srpmproc/process.go @@ -24,9 +24,17 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/go-git/go-billy/v5" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/rocky-linux/srpmproc/pkg/blob" + "github.com/rocky-linux/srpmproc/pkg/blob/file" + "github.com/rocky-linux/srpmproc/pkg/blob/gcs" + "github.com/rocky-linux/srpmproc/pkg/blob/s3" + "github.com/rocky-linux/srpmproc/pkg/modes" "io/ioutil" "log" "os" + "os/user" "path/filepath" "regexp" "strings" @@ -41,8 +49,44 @@ import ( "github.com/rocky-linux/srpmproc/pkg/data" ) +const ( + RpmPrefixCentOS = "https://git.centos.org/rpms" + ModulePrefixCentOS = "https://git.centos.org/modules" + RpmPrefixRocky = "https://git.rockylinux.org/staging/rpms" + ModulePrefixRocky = "https://git.rockylinux.org/staging/modules" + UpstreamPrefixRocky = "https://git.rockylinux.org/staging" +) + var tagImportRegex *regexp.Regexp +type ProcessDataRequest struct { + // Required + Version int + StorageAddr string + Package string + + // Optional + ModuleMode bool + TmpFsMode string + ModulePrefix string + RpmPrefix string + SshKeyLocation string + SshUser string + ManualCommits string + UpstreamPrefix string + GitCommitterName string + GitCommitterEmail string + ImportBranchPrefix string + BranchPrefix string + FsCreator data.FsCreatorFunc + NoDupMode bool + AllowStreamBranches bool + ModuleFallbackStream string + NoStorageUpload bool + NoStorageDownload bool + SingleTag string +} + func gitlabify(str string) string { if str == "tree" { return "treepkg" @@ -51,6 +95,144 @@ func gitlabify(str string) string { return strings.Replace(str, "+", "plus", -1) } +func NewProcessData(req *ProcessDataRequest) (*data.ProcessData, error) { + switch req.Version { + case 8: + break + default: + return nil, fmt.Errorf("unsupported upstream version %d", req.Version) + } + + // Set defaults + if req.ModulePrefix == "" { + req.ModulePrefix = ModulePrefixCentOS + } + if req.RpmPrefix == "" { + req.RpmPrefix = RpmPrefixCentOS + } + if req.SshUser == "" { + req.SshUser = "git" + } + if req.UpstreamPrefix == "" { + req.UpstreamPrefix = UpstreamPrefixRocky + } + if req.GitCommitterName == "" { + req.GitCommitterName = "rockyautomation" + } + if req.GitCommitterEmail == "" { + req.GitCommitterEmail = "rockyautomation@rockylinux.org" + } + if req.ImportBranchPrefix == "" { + req.ImportBranchPrefix = "c" + } + if req.BranchPrefix == "" { + req.BranchPrefix = "r" + } + + // Validate required + if req.Package == "" { + return nil, fmt.Errorf("package cannot be empty") + } + + var importer data.ImportMode + var blobStorage blob.Storage + + if strings.HasPrefix(req.StorageAddr, "gs://") { + blobStorage = gcs.New(strings.Replace(req.StorageAddr, "gs://", "", 1)) + } else if strings.HasPrefix(req.StorageAddr, "s3://") { + blobStorage = s3.New(strings.Replace(req.StorageAddr, "s3://", "", 1)) + } else if strings.HasPrefix(req.StorageAddr, "file://") { + blobStorage = file.New(strings.Replace(req.StorageAddr, "file://", "", 1)) + } else { + log.Fatalf("invalid blob storage") + } + + sourceRpmLocation := "" + if req.ModuleMode { + sourceRpmLocation = fmt.Sprintf("%s/%s", req.ModulePrefix, req.Package) + } else { + sourceRpmLocation = fmt.Sprintf("%s/%s", req.RpmPrefix, req.Package) + } + importer = &modes.GitMode{} + + lastKeyLocation := req.SshKeyLocation + if lastKeyLocation == "" { + usr, err := user.Current() + if err != nil { + log.Fatalf("could not get user: %v", err) + } + lastKeyLocation = filepath.Join(usr.HomeDir, ".ssh/id_rsa") + } + + var authenticator *ssh.PublicKeys + + var err error + // create ssh key authenticator + authenticator, err = ssh.NewPublicKeysFromFile(req.SshUser, lastKeyLocation, "") + if err != nil { + log.Fatalf("could not get git authenticator: %v", err) + } + + fsCreator := func(branch string) (billy.Filesystem, error) { + return memfs.New(), nil + } + if req.FsCreator != nil { + fsCreator = req.FsCreator + } + + if req.TmpFsMode != "" { + log.Printf("using tmpfs dir: %s", req.TmpFsMode) + fsCreator = func(branch string) (billy.Filesystem, error) { + fs, err := fsCreator(branch) + if err != nil { + return nil, err + } + tmpDir := filepath.Join(req.TmpFsMode, branch) + err = fs.MkdirAll(tmpDir, 0755) + if err != nil { + log.Fatalf("could not create tmpfs dir: %v", err) + } + nFs, err := fs.Chroot(tmpDir) + if err != nil { + return nil, err + } + + return nFs, nil + } + } + + var manualCs []string + if strings.TrimSpace(req.ManualCommits) != "" { + manualCs = strings.Split(req.ManualCommits, ",") + } + + return &data.ProcessData{ + Importer: importer, + RpmLocation: sourceRpmLocation, + UpstreamPrefix: req.UpstreamPrefix, + SshKeyLocation: lastKeyLocation, + SshUser: req.SshUser, + Version: req.Version, + BlobStorage: blobStorage, + GitCommitterName: req.GitCommitterName, + GitCommitterEmail: req.GitCommitterEmail, + ModulePrefix: req.ModulePrefix, + ImportBranchPrefix: req.ImportBranchPrefix, + BranchPrefix: req.BranchPrefix, + SingleTag: req.SingleTag, + Authenticator: authenticator, + NoDupMode: req.NoDupMode, + ModuleMode: req.ModuleMode, + TmpFsMode: req.TmpFsMode, + NoStorageDownload: req.NoStorageDownload, + NoStorageUpload: req.NoStorageUpload, + ManualCommits: manualCs, + ModuleFallbackStream: req.ModuleFallbackStream, + AllowStreamBranches: req.AllowStreamBranches, + FsCreator: fsCreator, + }, nil +} + // ProcessRPM checks the RPM specs and discards any remote files // This functions also sorts files into directories // .spec files goes into -> SPECS @@ -58,14 +240,11 @@ func gitlabify(str string) string { // source files goes into -> SOURCES // all files that are remote goes into .gitignore // all ignored files' hash goes into .{Name}.metadata -func ProcessRPM(pd *data.ProcessData) { - if pd.AllowStreamBranches { - tagImportRegex = regexp.MustCompile(fmt.Sprintf("refs/tags/(imports/(%s(?:.s|.)|%s(?:|s).+)/(.*))", pd.ImportBranchPrefix, pd.ImportBranchPrefix)) - } else { - tagImportRegex = regexp.MustCompile(fmt.Sprintf("refs/tags/(imports/(%s.|%s.-.+)/(.*))", pd.ImportBranchPrefix, pd.ImportBranchPrefix)) +func ProcessRPM(pd *data.ProcessData) error { + md, err := pd.Importer.RetrieveSource(pd) + if err != nil { + return err } - - md := pd.Importer.RetrieveSource(pd) md.BlobCache = map[string][]byte{} remotePrefix := "rpms" @@ -83,9 +262,9 @@ func ProcessRPM(pd *data.ProcessData) { if pd.NoDupMode { repo, err := git.Init(memory.NewStorage(), memfs.New()) if err != nil { - log.Fatalf("could not init git repo: %v", err) + return fmt.Errorf("could not init git repo: %v", err) } - remoteUrl := fmt.Sprintf("%s/%s/%s.git", pd.UpstreamPrefix, remotePrefix, gitlabify(md.RpmFile.Name())) + remoteUrl := fmt.Sprintf("%s/%s/%s.git", pd.UpstreamPrefix, remotePrefix, gitlabify(md.Name)) refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*") remote, err := repo.CreateRemote(&config.RemoteConfig{ @@ -94,7 +273,7 @@ func ProcessRPM(pd *data.ProcessData) { Fetch: []config.RefSpec{refspec}, }) if err != nil { - log.Fatalf("could not create remote: %v", err) + return fmt.Errorf("could not create remote: %v", err) } list, err := remote.List(&git.ListOptions{ @@ -127,7 +306,7 @@ func ProcessRPM(pd *data.ProcessData) { log.Fatalln("invalid manual commit list") } - head := fmt.Sprintf("refs/tags/imports/%s/%s-%s", branchCommit[0], md.RpmFile.Name(), branchCommit[1]) + head := fmt.Sprintf("refs/tags/imports/%s/%s-%s", branchCommit[0], md.Name, branchCommit[1]) md.Branches = append(md.Branches, head) commitPin[head] = branchCommit[1] } @@ -167,15 +346,19 @@ func ProcessRPM(pd *data.ProcessData) { newTag := "imports/" + pd.BranchPrefix + strings.TrimPrefix(match[1], "imports/"+pd.ImportBranchPrefix) newTag = strings.Replace(newTag, "%", "_", -1) - rpmFile := md.RpmFile - // create new Repo for final dist - repo, err := git.Init(memory.NewStorage(), pd.FsCreator(md.PushBranch)) + createdFs, err := pd.FsCreator(md.PushBranch) if err != nil { - log.Fatalf("could not create new dist Repo: %v", err) + return err + } + + // create new Repo for final dist + repo, err := git.Init(memory.NewStorage(), createdFs) + if err != nil { + return fmt.Errorf("could not create new dist Repo: %v", err) } w, err := repo.Worktree() if err != nil { - log.Fatalf("could not get dist Worktree: %v", err) + return fmt.Errorf("could not get dist Worktree: %v", err) } shouldContinue := true @@ -190,7 +373,7 @@ func ProcessRPM(pd *data.ProcessData) { } // create a new remote - remoteUrl := fmt.Sprintf("%s/%s/%s.git", pd.UpstreamPrefix, remotePrefix, gitlabify(rpmFile.Name())) + remoteUrl := fmt.Sprintf("%s/%s/%s.git", pd.UpstreamPrefix, remotePrefix, gitlabify(md.Name)) log.Printf("using remote: %s", remoteUrl) refspec := config.RefSpec(fmt.Sprintf("+refs/heads/%s:refs/remotes/origin/%s", md.PushBranch, md.PushBranch)) log.Printf("using refspec: %s", refspec) @@ -201,7 +384,7 @@ func ProcessRPM(pd *data.ProcessData) { Fetch: []config.RefSpec{refspec}, }) if err != nil { - log.Fatalf("could not create remote: %v", err) + return fmt.Errorf("could not create remote: %v", err) } err = repo.Fetch(&git.FetchOptions{ @@ -221,7 +404,7 @@ func ProcessRPM(pd *data.ProcessData) { if err != nil { h := plumbing.NewSymbolicReference(plumbing.HEAD, refName) if err := repo.Storer.CheckAndSetReference(h, nil); err != nil { - log.Fatalf("could not set reference: %v", err) + return fmt.Errorf("could not set reference: %v", err) } } else { err = w.Checkout(&git.CheckoutOptions{ @@ -230,27 +413,36 @@ func ProcessRPM(pd *data.ProcessData) { Force: true, }) if err != nil { - log.Fatalf("could not checkout: %v", err) + return fmt.Errorf("could not checkout: %v", err) } } - pd.Importer.WriteSource(pd, md) + err = pd.Importer.WriteSource(pd, md) + if err != nil { + return err + } data.CopyFromFs(md.Worktree.Filesystem, w.Filesystem, ".") md.Repo = repo md.Worktree = w if pd.ModuleMode { - patchModuleYaml(pd, md) + err := patchModuleYaml(pd, md) + if err != nil { + return err + } } else { - executePatchesRpm(pd, md) + err := executePatchesRpm(pd, md) + if err != nil { + return err + } } // get ignored files hash and add to .{Name}.metadata - metadataFile := fmt.Sprintf(".%s.metadata", rpmFile.Name()) + metadataFile := fmt.Sprintf(".%s.metadata", md.Name) metadata, err := w.Filesystem.Create(metadataFile) if err != nil { - log.Fatalf("could not create metadata file: %v", err) + return fmt.Errorf("could not create metadata file: %v", err) } for _, source := range md.SourcesToIgnore { sourcePath := source.Name @@ -262,23 +454,23 @@ func ProcessRPM(pd *data.ProcessData) { sourceFile, err := w.Filesystem.Open(sourcePath) if err != nil { - log.Fatalf("could not open ignored source file %s: %v", sourcePath, err) + return fmt.Errorf("could not open ignored source file %s: %v", sourcePath, err) } sourceFileBts, err := ioutil.ReadAll(sourceFile) if err != nil { - log.Fatalf("could not read the whole of ignored source file: %v", err) + return fmt.Errorf("could not read the whole of ignored source file: %v", err) } source.HashFunction.Reset() _, err = source.HashFunction.Write(sourceFileBts) if err != nil { - log.Fatalf("could not write bytes to hash function: %v", err) + return fmt.Errorf("could not write bytes to hash function: %v", err) } checksum := hex.EncodeToString(source.HashFunction.Sum(nil)) checksumLine := fmt.Sprintf("%s %s\n", checksum, sourcePath) _, err = metadata.Write([]byte(checksumLine)) if err != nil { - log.Fatalf("could not write to metadata file: %v", err) + return fmt.Errorf("could not write to metadata file: %v", err) } if data.StrContains(alreadyUploadedBlobs, checksum) { @@ -293,7 +485,7 @@ func ProcessRPM(pd *data.ProcessData) { _, err = w.Add(metadataFile) if err != nil { - log.Fatalf("could not add metadata file: %v", err) + return fmt.Errorf("could not add metadata file: %v", err) } lastFilesToAdd := []string{".gitignore", "SPECS"} @@ -302,7 +494,7 @@ func ProcessRPM(pd *data.ProcessData) { if err == nil { _, err := w.Add(f) if err != nil { - log.Fatalf("could not add %s: %v", f, err) + return fmt.Errorf("could not add %s: %v", f, err) } } } @@ -311,7 +503,10 @@ func ProcessRPM(pd *data.ProcessData) { continue } - pd.Importer.PostProcess(md) + err = pd.Importer.PostProcess(md) + if err != nil { + return err + } // show status status, _ := w.Status() @@ -324,7 +519,7 @@ func ProcessRPM(pd *data.ProcessData) { path := strings.TrimPrefix(trimmed, "D ") _, err := w.Remove(path) if err != nil { - log.Fatalf("could not delete extra file %s: %v", path, err) + return fmt.Errorf("could not delete extra file %s: %v", path, err) } } } @@ -354,12 +549,12 @@ func ProcessRPM(pd *data.ProcessData) { Parents: hashes, }) if err != nil { - log.Fatalf("could not commit object: %v", err) + return fmt.Errorf("could not commit object: %v", err) } obj, err := repo.CommitObject(commit) if err != nil { - log.Fatalf("could not get commit object: %v", err) + return fmt.Errorf("could not get commit object: %v", err) } log.Printf("committed:\n%s", obj.String()) @@ -374,7 +569,7 @@ func ProcessRPM(pd *data.ProcessData) { SignKey: nil, }) if err != nil { - log.Fatalf("could not create tag: %v", err) + return fmt.Errorf("could not create tag: %v", err) } pushRefspecs = append(pushRefspecs, config.RefSpec("HEAD:"+plumbing.NewTagReferenceName(newTag))) @@ -386,15 +581,17 @@ func ProcessRPM(pd *data.ProcessData) { Force: true, }) if err != nil { - log.Fatalf("could not push to remote: %v", err) + return fmt.Errorf("could not push to remote: %v", err) } hashString := obj.Hash.String() latestHashForBranch[md.PushBranch] = hashString } - err := json.NewEncoder(os.Stdout).Encode(latestHashForBranch) + err = json.NewEncoder(os.Stdout).Encode(latestHashForBranch) if err != nil { - log.Fatalf("could not print hashes") + return fmt.Errorf("could not print hashes") } + + return nil } diff --git a/pkg/srpmproc/srpm.go b/pkg/srpmproc/srpm.go deleted file mode 100644 index 9711355..0000000 --- a/pkg/srpmproc/srpm.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) 2021 The Srpmproc Authors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package srpmproc - -import ( - "bytes" - "crypto/sha256" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/cavaliercoder/go-cpio" - "github.com/cavaliercoder/go-rpm" - "github.com/go-git/go-billy/v5/memfs" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/storage/memory" - "github.com/rocky-linux/srpmproc/pkg/data" -) - -type SrpmMode struct{} - -func (s *SrpmMode) RetrieveSource(pd *data.ProcessData) *data.ModeData { - cmd := exec.Command("rpm2cpio", pd.RpmLocation) - cpioBytes, err := cmd.Output() - if err != nil { - log.Fatalf("could not convert to cpio (maybe rpm2cpio is missing): %v", err) - } - - // create in memory git repository - repo, err := git.Init(memory.NewStorage(), memfs.New()) - if err != nil { - log.Fatalf("could not init git Repo: %v", err) - } - - // read the rpm in cpio format - buf := bytes.NewReader(cpioBytes) - r := cpio.NewReader(buf) - fileWrites := map[string][]byte{} - for { - hdr, err := r.Next() - if err == io.EOF { - // end of cpio archive - break - } - if err != nil { - log.Fatalln(err) - } - - bts, err := ioutil.ReadAll(r) - if err != nil { - log.Fatalf("could not copy file to virtual filesystem: %v", err) - } - fileWrites[hdr.Name] = bts - } - - w, err := repo.Worktree() - if err != nil { - log.Fatalf("could not get Worktree: %v", err) - } - - // create structure - err = w.Filesystem.MkdirAll("SPECS", 0755) - if err != nil { - log.Fatalf("could not create SPECS dir in vfs: %v", err) - } - err = w.Filesystem.MkdirAll("SOURCES", 0755) - if err != nil { - log.Fatalf("could not create SOURCES dir in vfs: %v", err) - } - - f, err := os.Open(pd.RpmLocation) - if err != nil { - log.Fatalf("could not open the file again: %v", err) - } - rpmFile, err := rpm.ReadPackageFile(f) - if err != nil { - log.Fatalf("could not read package, invalid?: %v", err) - } - - var sourcesToIgnore []*data.IgnoredSource - for _, source := range rpmFile.Source() { - if strings.Contains(source, ".tar") { - sourcesToIgnore = append(sourcesToIgnore, &data.IgnoredSource{ - Name: source, - HashFunction: sha256.New(), - }) - } - } - - branch := fmt.Sprintf("%s%d", pd.BranchPrefix, pd.Version) - return &data.ModeData{ - Repo: repo, - Worktree: w, - RpmFile: rpmFile, - FileWrites: fileWrites, - Branches: []string{branch}, - SourcesToIgnore: sourcesToIgnore, - } -} - -func (s *SrpmMode) WriteSource(_ *data.ProcessData, md *data.ModeData) { - for fileName, contents := range md.FileWrites { - var newPath string - if filepath.Ext(fileName) == ".spec" { - newPath = filepath.Join("SPECS", fileName) - } else { - newPath = filepath.Join("SOURCES", fileName) - } - - mode := os.FileMode(0666) - for _, file := range md.RpmFile.Files() { - if file.Name() == fileName { - mode = file.Mode() - } - } - - // add the file to the virtual filesystem - // we will move it to correct destination later - f, err := md.Worktree.Filesystem.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode) - if err != nil { - log.Fatalf("could not create file %s: %v", fileName, err) - } - - _, err = f.Write(contents) - if err != nil { - log.Fatalf("could not write to file %s: %v", fileName, err) - } - - _ = f.Close() - - // don't add ignored file to git - if data.IgnoredContains(md.SourcesToIgnore, fileName) { - continue - } - - _, err = md.Worktree.Add(newPath) - if err != nil { - log.Fatalf("could not add source file: %v", err) - } - } - - // add sources to ignore (remote sources) - gitIgnore, err := md.Worktree.Filesystem.Create(".gitignore") - if err != nil { - log.Fatalf("could not create .gitignore: %v", err) - } - for _, ignore := range md.SourcesToIgnore { - line := fmt.Sprintf("SOURCES/%s\n", ignore) - _, err := gitIgnore.Write([]byte(line)) - if err != nil { - log.Fatalf("could not write line to .gitignore: %v", err) - } - } - err = gitIgnore.Close() - if err != nil { - log.Fatalf("could not close .gitignore: %v", err) - } -} - -func (s *SrpmMode) PostProcess(_ *data.ModeData) {} - -func (s *SrpmMode) ImportName(pd *data.ProcessData, _ *data.ModeData) string { - return filepath.Base(pd.RpmLocation) -}