srpmproc/internal/patch.go

334 lines
10 KiB
Go
Raw Normal View History

2021-04-06 19:39:02 +00:00
// 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 internal
import (
2020-12-20 10:40:27 +00:00
"fmt"
2021-04-15 04:41:12 +00:00
"io/ioutil"
"log"
"path/filepath"
"strings"
"time"
2020-12-20 10:40:27 +00:00
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
2020-12-20 10:40:27 +00:00
"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"
2021-04-15 04:41:12 +00:00
"github.com/rocky-linux/srpmproc/internal/data"
"github.com/rocky-linux/srpmproc/internal/directives"
"github.com/rocky-linux/srpmproc/modulemd"
srpmprocpb "github.com/rocky-linux/srpmproc/pb"
2020-12-20 10:40:27 +00:00
"google.golang.org/protobuf/encoding/prototext"
)
func cfgPatches(pd *data.ProcessData, md *data.ModeData, patchTree *git.Worktree, pushTree *git.Worktree) {
2020-12-20 10:40:27 +00:00
// 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)
}
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, pd, md, patchTree, pushTree)
2020-12-20 10:40:27 +00:00
}
}
}
func applyPatches(pd *data.ProcessData, md *data.ModeData, patchTree *git.Worktree, pushTree *git.Worktree) {
// check if patches exist
2020-12-20 10:40:27 +00:00
_, err := patchTree.Filesystem.Stat("ROCKY")
if err == nil {
cfgPatches(pd, md, patchTree, pushTree)
2020-12-20 10:40:27 +00:00
}
}
func executePatchesRpm(pd *data.ProcessData, md *data.ModeData) {
2020-12-20 10:40:27 +00:00
// fetch patch repository
repo, err := git.Init(memory.NewStorage(), memfs.New())
if err != nil {
log.Fatalf("could not create new dist Repo: %v", err)
2020-12-20 10:40:27 +00:00
}
w, err := repo.Worktree()
if err != nil {
log.Fatalf("could not get dist Worktree: %v", err)
2020-12-20 10:40:27 +00:00
}
2021-03-17 14:54:16 +00:00
remoteUrl := fmt.Sprintf("%s/patch/%s.git", pd.UpstreamPrefix, gitlabify(md.RpmFile.Name()))
2020-12-20 10:40:27 +00:00
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)
}
2021-03-18 17:44:02 +00:00
fetchOptions := &git.FetchOptions{
2020-12-20 10:40:27 +00:00
RemoteName: "origin",
RefSpecs: []config.RefSpec{refspec},
2021-03-18 17:44:02 +00:00
}
if !strings.HasPrefix(pd.UpstreamPrefix, "http") {
fetchOptions.Auth = pd.Authenticator
}
err = repo.Fetch(fetchOptions)
2020-12-20 10:40:27 +00:00
refName := plumbing.NewBranchReferenceName(md.PushBranch)
2020-12-20 10:40:27 +00:00
log.Printf("set reference to ref: %s", refName)
if err != nil {
// no patches active
2021-03-18 17:44:02 +00:00
log.Println("info: patch repo not found")
2020-12-20 10:40:27 +00:00
return
} else {
err = w.Checkout(&git.CheckoutOptions{
Branch: plumbing.NewRemoteReferenceName("origin", "main"),
2020-12-20 10:40:27 +00:00
Force: true,
})
// common patches found, apply them
if err == nil {
applyPatches(pd, md, w, md.Worktree)
2020-12-20 10:40:27 +00:00
} else {
log.Println("info: no common patches found")
}
err = w.Checkout(&git.CheckoutOptions{
Branch: plumbing.NewRemoteReferenceName("origin", md.PushBranch),
2020-12-20 10:40:27 +00:00
Force: true,
})
// branch specific patches found, apply them
if err == nil {
applyPatches(pd, md, w, md.Worktree)
2020-12-20 10:40:27 +00:00
} else {
log.Println("info: no branch specific patches found")
}
}
}
2020-12-22 16:28:04 +00:00
2021-04-06 19:35:46 +00:00
func getTipStream(pd *data.ProcessData, module string, pushBranch string, origPushBranch string, tries int) string {
2020-12-22 16:28:04 +00:00
repo, err := git.Init(memory.NewStorage(), memfs.New())
if err != nil {
log.Fatalf("could not init git Repo: %v", err)
2020-12-22 16:28:04 +00:00
}
2021-03-17 14:54:16 +00:00
remoteUrl := fmt.Sprintf("%s/rpms/%s.git", pd.UpstreamPrefix, gitlabify(module))
2020-12-22 16:28:04 +00:00
refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
remote, 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)
}
list, err := remote.List(&git.ListOptions{
Auth: pd.Authenticator,
})
if err != nil {
2021-04-06 19:35:46 +00:00
log.Printf("could not import module: %s", module)
if tries < 3 {
log.Printf("could not get rpm refs. will retry in 3s. %v", err)
time.Sleep(3 * time.Second)
return getTipStream(pd, module, pushBranch, origPushBranch, tries+1)
}
2020-12-22 16:28:04 +00:00
log.Fatalf("could not get rpm refs. import the rpm before the module: %v", err)
}
var tipHash string
for _, ref := range list {
2021-04-28 08:38:53 +00:00
if strings.Contains(ref.Name().String(), "-bootstrap") {
continue
}
2020-12-22 16:28:04 +00:00
prefix := fmt.Sprintf("refs/heads/%s", pushBranch)
2021-03-17 14:54:16 +00:00
branchVersion := strings.Split(pushBranch, "-")
if len(branchVersion) == 3 {
version := branchVersion[2]
// incompatible pattern: v1,v2 etc.
// example can be found in refs/tags/imports/c8-stream-1.1/subversion-1.10-8030020200519083055.9ce6d490
if strings.HasPrefix(version, "v") {
prefix = strings.Replace(prefix, version, strings.TrimPrefix(version, "v"), 1)
}
}
if strings.HasPrefix(ref.Name().String(), prefix) {
tipHash = ref.Hash().String()
}
}
2021-04-06 19:35:46 +00:00
if tipHash == "" {
for _, ref := range list {
if strings.Contains(ref.Name().String(), "-bootstrap") {
continue
}
2021-04-06 19:35:46 +00:00
prefix := fmt.Sprintf("refs/heads/%s", origPushBranch)
2021-03-17 14:54:16 +00:00
2021-04-06 19:35:46 +00:00
if strings.HasPrefix(ref.Name().String(), prefix) {
tipHash = ref.Hash().String()
}
}
}
if tipHash == "" {
for _, ref := range list {
if strings.Contains(ref.Name().String(), "-bootstrap") {
continue
}
2021-04-06 19:35:46 +00:00
if !strings.Contains(ref.Name().String(), "stream") {
tipHash = ref.Hash().String()
}
2020-12-22 16:28:04 +00:00
}
}
if tipHash == "" {
2021-04-06 19:35:46 +00:00
for _, ref := range list {
log.Println(pushBranch, ref.Name())
}
2020-12-22 16:28:04 +00:00
log.Fatal("could not find tip hash")
}
2021-04-23 09:17:41 +00:00
return strings.TrimSpace(tipHash)
2020-12-22 16:28:04 +00:00
}
func patchModuleYaml(pd *data.ProcessData, md *data.ModeData) {
2020-12-22 16:28:04 +00:00
// special case for platform.yaml
_, err := md.Worktree.Filesystem.Open("platform.yaml")
2020-12-22 16:28:04 +00:00
if err == nil {
return
}
mdTxtPath := "SOURCES/modulemd.src.txt"
f, err := md.Worktree.Filesystem.Open(mdTxtPath)
2020-12-22 16:28:04 +00:00
if err != nil {
2021-04-26 10:17:40 +00:00
mdTxtPath = "SOURCES/modulemd.txt"
f, err = md.Worktree.Filesystem.Open(mdTxtPath)
if err != nil {
log.Fatalf("could not open modulemd file: %v", err)
}
2020-12-22 16:28:04 +00:00
}
content, err := ioutil.ReadAll(f)
if err != nil {
log.Fatalf("could not read modulemd file: %v", err)
}
module, err := modulemd.Parse(content)
if err != nil {
log.Fatalf("could not parse modulemd file: %v", err)
}
// Get stream branch from tag
match := tagImportRegex.FindStringSubmatch(md.TagBranch)
streamBranch := strings.Split(match[2], "-")
// Force stream to be the same as stream name in branch
module.Data.Stream = streamBranch[len(streamBranch)-1]
2021-04-06 19:35:46 +00:00
log.Println("This module contains the following rpms:")
for name := range module.Data.Components.Rpms {
log.Printf("\t- %s", name)
}
2021-04-25 10:58:13 +00:00
defaultBranch := md.PushBranch
if pd.ModuleFallbackStream != "" {
defaultBranch = fmt.Sprintf("%s%d-stream-%s", pd.BranchPrefix, pd.Version, pd.ModuleFallbackStream)
}
2021-01-01 15:20:39 +00:00
for name, rpm := range module.Data.Components.Rpms {
var tipHash string
var pushBranch string
2021-04-25 10:58:13 +00:00
split := strings.Split(rpm.Ref, "-")
// TODO: maybe point to correct release tag? but refer to latest for now,
// we're bootstrapping a new distro for latest RHEL8 anyways. So earlier
// versions are not that important
2021-04-23 09:17:41 +00:00
if strings.HasPrefix(rpm.Ref, "stream-rhel-rhel-") {
2021-04-25 10:58:13 +00:00
pushBranch = defaultBranch
2021-04-23 09:17:41 +00:00
} else if strings.HasPrefix(rpm.Ref, "stream-rhel-") {
repString := fmt.Sprintf("%s%ss-", pd.BranchPrefix, string(split[4][0]))
newString := fmt.Sprintf("%s%s-", pd.BranchPrefix, string(split[4][0]))
pushBranch = strings.Replace(md.PushBranch, repString, newString, 1)
} else if strings.HasPrefix(rpm.Ref, "stream-") && len(split) == 2 {
2021-04-25 10:58:13 +00:00
pushBranch = defaultBranch
} else if strings.HasPrefix(rpm.Ref, "stream-") && len(split) == 3 {
// example: ant
pushBranch = fmt.Sprintf("%s%d-stream-%s", pd.BranchPrefix, pd.Version, split[2])
} else if strings.HasPrefix(rpm.Ref, "stream-") {
pushBranch = fmt.Sprintf("%s%s-stream-%s", pd.BranchPrefix, string(split[3][0]), split[1])
} else if strings.HasPrefix(rpm.Ref, "rhel-") {
2021-04-25 10:58:13 +00:00
pushBranch = defaultBranch
2021-01-01 15:20:39 +00:00
} else {
log.Fatal("could not recognize modulemd ref")
2021-01-01 15:20:39 +00:00
}
2020-12-22 16:28:04 +00:00
2021-04-06 19:35:46 +00:00
tipHash = getTipStream(pd, name, pushBranch, md.PushBranch, 0)
2021-04-25 10:58:13 +00:00
if tipHash == "0000000000000000000000000000000000000000" {
pushBranch = defaultBranch
tipHash = getTipStream(pd, name, pushBranch, md.PushBranch, 0)
}
2021-01-01 15:20:39 +00:00
rpm.Ref = tipHash
2020-12-22 16:28:04 +00:00
}
rootModule := fmt.Sprintf("%s.yaml", md.RpmFile.Name())
err = module.Marshal(md.Worktree.Filesystem, rootModule)
2020-12-22 16:28:04 +00:00
if err != nil {
log.Fatalf("could not marshal root modulemd: %v", err)
}
_, err = md.Worktree.Add(rootModule)
2020-12-22 16:28:04 +00:00
if err != nil {
log.Fatalf("could not add root modulemd: %v", err)
}
}