From e4902bda71071f0a36ac488a6a32b3cb820ad521 Mon Sep 17 00:00:00 2001 From: Mustafa Gezen Date: Sun, 20 Dec 2020 11:40:27 +0100 Subject: [PATCH] add directive support --- gen.go | 2 + go.mod | 2 + internal/directives/directives.go | 21 +++++ internal/directives/replace.go | 54 +++++++++++ internal/patch.go | 143 ++++++++++++++++++++++++++---- internal/process.go | 2 +- proto/cfg.proto | 24 +++++ 7 files changed, 230 insertions(+), 18 deletions(-) create mode 100644 gen.go create mode 100644 internal/directives/directives.go create mode 100644 internal/directives/replace.go create mode 100644 proto/cfg.proto diff --git a/gen.go b/gen.go new file mode 100644 index 0000000..39980b7 --- /dev/null +++ b/gen.go @@ -0,0 +1,2 @@ +//go:generate protoc -Iproto --go_opt=paths=source_relative --go_out=pb proto/cfg.proto +package srpmproc diff --git a/go.mod b/go.mod index 51f3388..b94af20 100644 --- a/go.mod +++ b/go.mod @@ -10,5 +10,7 @@ require ( github.com/cavaliercoder/go-rpm v0.0.0-20200122174316-8cb9fd9c31a8 github.com/go-git/go-billy/v5 v5.0.0 github.com/go-git/go-git/v5 v5.2.0 + github.com/golang/protobuf v1.4.2 github.com/spf13/cobra v1.1.1 + google.golang.org/protobuf v1.25.0 ) diff --git a/internal/directives/directives.go b/internal/directives/directives.go new file mode 100644 index 0000000..03b2971 --- /dev/null +++ b/internal/directives/directives.go @@ -0,0 +1,21 @@ +package directives + +import ( + "github.com/go-git/go-git/v5" + srpmprocpb "github.com/mstg/srpmproc/pb" + "path/filepath" + "strings" +) + +func checkAddPrefix(file string) string { + if strings.HasPrefix(file, "SOURCES/") || + strings.HasPrefix(file, "SPECS/") { + return file + } + + return filepath.Join("SOURCES", file) +} + +func Apply(cfg *srpmprocpb.Cfg, patchTree *git.Worktree, pushTree *git.Worktree) { + replace(cfg, patchTree, pushTree) +} diff --git a/internal/directives/replace.go b/internal/directives/replace.go new file mode 100644 index 0000000..d3cd470 --- /dev/null +++ b/internal/directives/replace.go @@ -0,0 +1,54 @@ +package directives + +import ( + "github.com/go-git/go-git/v5" + srpmprocpb "github.com/mstg/srpmproc/pb" + "io/ioutil" + "log" + "os" +) + +func replace(cfg *srpmprocpb.Cfg, patchTree *git.Worktree, pushTree *git.Worktree) { + for _, replace := range cfg.Replace { + filePath := checkAddPrefix(replace.File) + stat, err := pushTree.Filesystem.Stat(filePath) + if replace.File == "" || err != nil { + log.Fatalf("file to replace is invalid") + } + + err = pushTree.Filesystem.Remove(filePath) + if err != nil { + log.Fatalf("could not remove old file: %v", err) + } + + f, err := pushTree.Filesystem.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, stat.Mode()) + if err != nil { + log.Fatalf("could not open replacement file: %v", err) + } + + switch replacing := replace.Replacing.(type) { + case *srpmprocpb.Replace_WithFile: + fPatch, err := patchTree.Filesystem.OpenFile(replacing.WithFile, os.O_RDONLY, 0644) + if err != nil { + log.Fatalf("could not open replacing file: %v", err) + } + + replacingBytes, err := ioutil.ReadAll(fPatch) + if err != nil { + log.Fatalf("could not read replacing file: %v", err) + } + + _, err = f.Write(replacingBytes) + if err != nil { + log.Fatalf("could not write replacing file: %v", err) + } + break + case *srpmprocpb.Replace_WithInline: + _, err := f.Write([]byte(replacing.WithInline)) + if err != nil { + log.Fatalf("could not write inline replacement: %v", err) + } + break + } + } +} diff --git a/internal/patch.go b/internal/patch.go index 8330e52..674ac6c 100644 --- a/internal/patch.go +++ b/internal/patch.go @@ -2,19 +2,28 @@ package internal import ( "bytes" + "fmt" "github.com/bluekeyes/go-gitdiff/gitdiff" + "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/storage/memory" + "github.com/mstg/srpmproc/internal/directives" + srpmprocpb "github.com/mstg/srpmproc/pb" + "google.golang.org/protobuf/encoding/prototext" + "io/ioutil" "log" "path/filepath" "strings" ) -func srpmPatches(w *git.Worktree) { +func srpmPatches(patchTree *git.Worktree, pushTree *git.Worktree) { // check SRPM patches - _, err := w.Filesystem.Stat("ROCKY/SRPM") + _, err := patchTree.Filesystem.Stat("ROCKY/SRPM") if err == nil { // iterate through patches - infos, err := w.Filesystem.ReadDir("ROCKY/SRPM") + infos, err := patchTree.Filesystem.ReadDir("ROCKY/SRPM") if err != nil { log.Fatalf("could not walk patches: %v", err) } @@ -28,7 +37,7 @@ func srpmPatches(w *git.Worktree) { log.Printf("applying %s", info.Name()) filePath := filepath.Join("ROCKY/SRPM", info.Name()) - patch, err := w.Filesystem.Open(filePath) + patch, err := patchTree.Filesystem.Open(filePath) if err != nil { log.Fatalf("could not open patch file %s: %v", info.Name(), err) } @@ -44,7 +53,7 @@ func srpmPatches(w *git.Worktree) { } var output bytes.Buffer if !patchedFile.IsDelete && !patchedFile.IsNew { - patchSubjectFile, err := w.Filesystem.Open(srcPath) + patchSubjectFile, err := pushTree.Filesystem.Open(srcPath) if err != nil { log.Fatalf("could not open patch subject: %v", err) } @@ -56,11 +65,11 @@ func srpmPatches(w *git.Worktree) { } oldName := filepath.Join("SOURCES", patchedFile.OldName) - _ = w.Filesystem.Remove(oldName) - _ = w.Filesystem.Remove(srcPath) + _ = pushTree.Filesystem.Remove(oldName) + _ = pushTree.Filesystem.Remove(srcPath) if patchedFile.IsNew { - newFile, err := w.Filesystem.Create(srcPath) + newFile, err := pushTree.Filesystem.Create(srcPath) if err != nil { log.Fatalf("could not create new file: %v", err) } @@ -72,13 +81,13 @@ func srpmPatches(w *git.Worktree) { if err != nil { log.Fatalf("could not write post-patch file: %v", err) } - _, err = w.Add(srcPath) + _, err = pushTree.Add(srcPath) if err != nil { log.Fatalf("could not add file %s to git: %v", srcPath, err) } log.Printf("git add %s", srcPath) } else if !patchedFile.IsDelete { - newFile, err := w.Filesystem.Create(srcPath) + newFile, err := pushTree.Filesystem.Create(srcPath) if err != nil { log.Fatalf("could not create post-patch file: %v", err) } @@ -86,13 +95,13 @@ func srpmPatches(w *git.Worktree) { if err != nil { log.Fatalf("could not write post-patch file: %v", err) } - _, err = w.Add(srcPath) + _, err = pushTree.Add(srcPath) if err != nil { log.Fatalf("could not add file %s to git: %v", srcPath, err) } log.Printf("git add %s", srcPath) } else { - _, err = w.Remove(oldName) + _, err = pushTree.Remove(oldName) if err != nil { log.Fatalf("could not remove file %s to git: %v", oldName, err) } @@ -100,7 +109,7 @@ func srpmPatches(w *git.Worktree) { } } - _, err = w.Add(filePath) + _, err = pushTree.Add(filePath) if err != nil { log.Fatalf("could not add file %s to git: %v", filePath, err) } @@ -109,10 +118,110 @@ func srpmPatches(w *git.Worktree) { } } -func executePatches(w *git.Worktree) { - // check if patches exist - _, err := w.Filesystem.Stat("ROCKY") +func cfgPatches(patchTree *git.Worktree, pushTree *git.Worktree) { + // check CFG patches + _, err := patchTree.Filesystem.Stat("ROCKY/CFG") if err == nil { - srpmPatches(w) + // iterate through patches + infos, err := patchTree.Filesystem.ReadDir("ROCKY/CFG") + if err != nil { + log.Fatalf("could not walk patches: %v", err) + } + + for _, info := range infos { + // can only process .cfg files + if !strings.HasSuffix(info.Name(), ".cfg") { + continue + } + + log.Printf("applying directive %s", info.Name()) + 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) + } + directiveBytes, err := ioutil.ReadAll(directive) + if err != nil { + log.Fatalf("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) + } + + directives.Apply(&cfg, patchTree, pushTree) + } + } +} + +func applyPatches(patchTree *git.Worktree, pushTree *git.Worktree) { + // check if patches exist + _, err := patchTree.Filesystem.Stat("ROCKY") + if err == nil { + srpmPatches(patchTree, pushTree) + cfgPatches(patchTree, pushTree) + } +} + +func executePatches(pd *ProcessData, md *modeData) { + // fetch patch repository + repo, err := git.Init(memory.NewStorage(), memfs.New()) + if err != nil { + log.Fatalf("could not create new dist repo: %v", err) + } + w, err := repo.Worktree() + if err != nil { + log.Fatalf("could not get dist worktree: %v", err) + } + + remoteUrl := fmt.Sprintf("%s/patch/%s.git", pd.UpstreamPrefix, md.rpmFile.Name()) + refspec := config.RefSpec(fmt.Sprintf("+refs/heads/*:refs/remotes/origin/*")) + + _, err = repo.CreateRemote(&config.RemoteConfig{ + Name: "origin", + URLs: []string{remoteUrl}, + Fetch: []config.RefSpec{refspec}, + }) + if err != nil { + log.Fatalf("could not create remote: %v", err) + } + + err = repo.Fetch(&git.FetchOptions{ + RemoteName: "origin", + RefSpecs: []config.RefSpec{refspec}, + Auth: pd.Authenticator, + }) + + refName := plumbing.NewBranchReferenceName(md.pushBranch) + log.Printf("set reference to ref: %s", refName) + + if err != nil { + // no patches active + log.Println("info: patch repo not found") + return + } else { + err = w.Checkout(&git.CheckoutOptions{ + Branch: plumbing.NewRemoteReferenceName("origin", "master"), + Force: true, + }) + // common patches found, apply them + if err == nil { + applyPatches(w, md.worktree) + } else { + log.Println("info: no common patches found") + } + + err = w.Checkout(&git.CheckoutOptions{ + Branch: plumbing.NewRemoteReferenceName("origin", md.pushBranch), + Force: true, + }) + // branch specific patches found, apply them + if err == nil { + applyPatches(w, md.worktree) + } else { + log.Println("info: no branch specific patches found") + } } } diff --git a/internal/process.go b/internal/process.go index b184c79..068ec1e 100644 --- a/internal/process.go +++ b/internal/process.go @@ -135,7 +135,7 @@ func ProcessRPM(pd *ProcessData) { md.repo = repo md.worktree = w - executePatches(w) + executePatches(pd, md) // get ignored files hash and add to .{name}.metadata metadataFile := fmt.Sprintf(".%s.metadata", rpmFile.Name()) diff --git a/proto/cfg.proto b/proto/cfg.proto new file mode 100644 index 0000000..69d334c --- /dev/null +++ b/proto/cfg.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +option go_package = "github.com/mstg/srpmproc/pb;srpmprocpb"; + +package srpmproc; + +// Replace directive replaces literal files with other files. +// Replacing content can either be inline or in the same patch-tree. +message Replace { + // required - replaced file + string file = 1; + + oneof replacing { + // replace with in-tree file + string with_file = 2; + + // replace with inline content + string with_inline = 3; + } +} + +message Cfg { + repeated Replace replace = 1; +}