mirror of
https://github.com/rocky-linux/srpmproc.git
synced 2024-11-10 15:21:28 +00:00
341 lines
9.1 KiB
Go
341 lines
9.1 KiB
Go
package internal
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"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/config"
|
|
"github.com/go-git/go-git/v5/plumbing"
|
|
"github.com/go-git/go-git/v5/plumbing/object"
|
|
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
|
|
"github.com/go-git/go-git/v5/storage/memory"
|
|
"github.com/mstg/srpmproc/internal/blob"
|
|
"hash"
|
|
"io/ioutil"
|
|
"log"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var tagImportRegex = regexp.MustCompile("refs/tags/(imports/(.*)/(.*))")
|
|
|
|
type ProcessData struct {
|
|
RpmLocation string
|
|
UpstreamPrefix string
|
|
SshKeyLocation string
|
|
SshUser string
|
|
Version int
|
|
GitCommitterName string
|
|
GitCommitterEmail string
|
|
Mode int
|
|
ModulePrefix string
|
|
Authenticator *ssh.PublicKeys
|
|
Importer ImportMode
|
|
BlobStorage blob.Storage
|
|
NoDupMode bool
|
|
ModuleMode bool
|
|
}
|
|
|
|
type ignoredSource struct {
|
|
name string
|
|
hashFunction hash.Hash
|
|
expired bool
|
|
}
|
|
|
|
type modeData struct {
|
|
repo *git.Repository
|
|
worktree *git.Worktree
|
|
rpmFile *rpm.PackageFile
|
|
fileWrites map[string][]byte
|
|
tagBranch string
|
|
pushBranch string
|
|
branches []string
|
|
sourcesToIgnore []*ignoredSource
|
|
}
|
|
|
|
// ProcessRPM checks the RPM specs and discards any remote files
|
|
// This functions also sorts files into directories
|
|
// .spec files goes into -> SPECS
|
|
// metadata files goes to root
|
|
// source files goes into -> SOURCES
|
|
// all files that are remote goes into .gitignore
|
|
// all ignored files' hash goes into .{name}.metadata
|
|
func ProcessRPM(pd *ProcessData) {
|
|
md := pd.Importer.RetrieveSource(pd)
|
|
|
|
remotePrefix := "dist"
|
|
if pd.ModuleMode {
|
|
remotePrefix = "modules"
|
|
}
|
|
|
|
// if no-dup-mode is enabled then skip already imported versions
|
|
var tagIgnoreList []string
|
|
if pd.NoDupMode {
|
|
repo, err := git.Init(memory.NewStorage(), memfs.New())
|
|
if err != nil {
|
|
log.Fatalf("could not init git repo: %v", err)
|
|
}
|
|
remoteUrl := fmt.Sprintf("%s/%s/%s.git", pd.UpstreamPrefix, remotePrefix, md.rpmFile.Name())
|
|
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 {
|
|
log.Println("ignoring no-dup-mode")
|
|
} else {
|
|
for _, ref := range list {
|
|
if !strings.HasPrefix(string(ref.Name()), "refs/tags/imports") {
|
|
continue
|
|
}
|
|
tagIgnoreList = append(tagIgnoreList, string(ref.Name()))
|
|
}
|
|
}
|
|
}
|
|
|
|
sourceRepo := *md.repo
|
|
sourceWorktree := *md.worktree
|
|
|
|
for _, branch := range md.branches {
|
|
md.repo = &sourceRepo
|
|
md.worktree = &sourceWorktree
|
|
md.tagBranch = branch
|
|
for _, source := range md.sourcesToIgnore {
|
|
source.expired = true
|
|
}
|
|
|
|
rpmFile := md.rpmFile
|
|
// create new repo for final dist
|
|
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)
|
|
}
|
|
|
|
var matchString string
|
|
if !tagImportRegex.MatchString(md.tagBranch) {
|
|
if pd.ModuleMode {
|
|
prefix := fmt.Sprintf("refs/heads/c%d", pd.Version)
|
|
if strings.HasPrefix(md.tagBranch, prefix) {
|
|
replace := strings.Replace(md.tagBranch, "refs/heads/", "", 1)
|
|
matchString = fmt.Sprintf("refs/tags/imports/%s/%s", replace, filepath.Base(pd.RpmLocation))
|
|
log.Printf("using match string: %s", matchString)
|
|
}
|
|
}
|
|
if !tagImportRegex.MatchString(matchString) {
|
|
log.Fatal("import tag invalid")
|
|
}
|
|
} else {
|
|
matchString = md.tagBranch
|
|
}
|
|
|
|
match := tagImportRegex.FindStringSubmatch(matchString)
|
|
md.pushBranch = "rocky" + strings.TrimPrefix(match[2], "c")
|
|
newTag := "imports/rocky" + strings.TrimPrefix(match[1], "imports/c")
|
|
|
|
shouldContinue := true
|
|
for _, ignoredTag := range tagIgnoreList {
|
|
if ignoredTag == "refs/tags/"+newTag {
|
|
log.Printf("skipping %s", ignoredTag)
|
|
shouldContinue = false
|
|
}
|
|
}
|
|
if !shouldContinue {
|
|
continue
|
|
}
|
|
|
|
// create a new remote
|
|
remoteUrl := fmt.Sprintf("%s/%s/%s.git", pd.UpstreamPrefix, remotePrefix, rpmFile.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)
|
|
|
|
_, 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 {
|
|
h := plumbing.NewSymbolicReference(plumbing.HEAD, refName)
|
|
if err := repo.Storer.CheckAndSetReference(h, nil); err != nil {
|
|
log.Fatalf("could not set reference: %v", err)
|
|
}
|
|
} else {
|
|
err = w.Checkout(&git.CheckoutOptions{
|
|
Branch: plumbing.NewRemoteReferenceName("origin", md.pushBranch),
|
|
Force: true,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("could not checkout: %v", err)
|
|
}
|
|
}
|
|
|
|
pd.Importer.WriteSource(md)
|
|
|
|
copyFromFs(md.worktree.Filesystem, w.Filesystem, ".")
|
|
md.repo = repo
|
|
md.worktree = w
|
|
|
|
if pd.ModuleMode {
|
|
patchModuleYaml(pd, md)
|
|
} else {
|
|
executePatchesRpm(pd, md)
|
|
}
|
|
|
|
// already uploaded blobs are skipped
|
|
var alreadyUploadedBlobs []string
|
|
// get ignored files hash and add to .{name}.metadata
|
|
metadataFile := fmt.Sprintf(".%s.metadata", rpmFile.Name())
|
|
metadata, err := w.Filesystem.Create(metadataFile)
|
|
if err != nil {
|
|
log.Fatalf("could not create metadata file: %v", err)
|
|
}
|
|
for _, source := range md.sourcesToIgnore {
|
|
if source.expired {
|
|
continue
|
|
}
|
|
|
|
sourcePath := source.name
|
|
sourceFile, err := w.Filesystem.Open(sourcePath)
|
|
if err != nil {
|
|
log.Fatalf("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)
|
|
}
|
|
|
|
source.hashFunction.Reset()
|
|
_, err = source.hashFunction.Write(sourceFileBts)
|
|
if err != nil {
|
|
log.Fatalf("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)
|
|
}
|
|
|
|
path := fmt.Sprintf("%s", checksum)
|
|
if strContains(alreadyUploadedBlobs, path) {
|
|
continue
|
|
}
|
|
pd.BlobStorage.Write(path, sourceFileBts)
|
|
log.Printf("wrote %s to blob storage", path)
|
|
alreadyUploadedBlobs = append(alreadyUploadedBlobs, path)
|
|
}
|
|
|
|
_, err = w.Add(metadataFile)
|
|
if err != nil {
|
|
log.Fatalf("could not add metadata file: %v", err)
|
|
}
|
|
|
|
lastFilesToAdd := []string{".gitignore", "SPECS"}
|
|
for _, f := range lastFilesToAdd {
|
|
_, err := w.Filesystem.Stat(f)
|
|
if err == nil {
|
|
_, err := w.Add(f)
|
|
if err != nil {
|
|
log.Fatalf("could not add %s: %v", f, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
pd.Importer.PostProcess(md)
|
|
|
|
// show status
|
|
status, _ := w.Status()
|
|
log.Printf("successfully processed:\n%s", status)
|
|
|
|
var hashes []plumbing.Hash
|
|
var pushRefspecs []config.RefSpec
|
|
|
|
head, err := repo.Head()
|
|
if err != nil {
|
|
hashes = nil
|
|
pushRefspecs = append(pushRefspecs, "*:*")
|
|
} else {
|
|
log.Printf("tip %s", head.String())
|
|
hashes = append(hashes, head.Hash())
|
|
refOrigin := "refs/heads/" + md.pushBranch
|
|
pushRefspecs = append(pushRefspecs, config.RefSpec(fmt.Sprintf("HEAD:%s", refOrigin)))
|
|
}
|
|
|
|
// we are now finished with the tree and are going to push it to the src repo
|
|
// create import commit
|
|
commit, err := w.Commit("import "+pd.Importer.ImportName(pd, md), &git.CommitOptions{
|
|
Author: &object.Signature{
|
|
Name: pd.GitCommitterName,
|
|
Email: pd.GitCommitterEmail,
|
|
When: time.Now(),
|
|
},
|
|
Parents: hashes,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("could not commit object: %v", err)
|
|
}
|
|
|
|
obj, err := repo.CommitObject(commit)
|
|
if err != nil {
|
|
log.Fatalf("could not get commit object: %v", err)
|
|
}
|
|
|
|
log.Printf("committed:\n%s", obj.String())
|
|
|
|
_, err = repo.CreateTag(newTag, commit, &git.CreateTagOptions{
|
|
Tagger: &object.Signature{
|
|
Name: pd.GitCommitterName,
|
|
Email: pd.GitCommitterEmail,
|
|
When: time.Now(),
|
|
},
|
|
Message: "import " + md.tagBranch + " from " + pd.RpmLocation,
|
|
SignKey: nil,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("could not create tag: %v", err)
|
|
}
|
|
|
|
pushRefspecs = append(pushRefspecs, config.RefSpec(fmt.Sprintf("HEAD:%s", plumbing.NewTagReferenceName(newTag))))
|
|
|
|
err = repo.Push(&git.PushOptions{
|
|
RemoteName: "origin",
|
|
Auth: pd.Authenticator,
|
|
RefSpecs: pushRefspecs,
|
|
Force: true,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("could not push to remote: %v", err)
|
|
}
|
|
}
|
|
}
|