Add git save implementation
This commit is contained in:
parent
fd486bbe09
commit
6f55ab1d38
|
@ -0,0 +1,224 @@
|
|||
// Copyright 2019 Sorint.lab
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gitsave
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/sorintlab/agola/internal/util"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
gitIndexFile = "index"
|
||||
gitRefsPrefix = "refs/gitsave"
|
||||
)
|
||||
|
||||
func copyFile(src, dest string) error {
|
||||
srcf, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcf.Close()
|
||||
|
||||
destf, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destf.Close()
|
||||
|
||||
_, err = io.Copy(destf, srcf)
|
||||
return err
|
||||
}
|
||||
|
||||
func fileExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return false, err
|
||||
}
|
||||
return !os.IsNotExist(err), nil
|
||||
}
|
||||
|
||||
// GitDir returns the git dir relative to the working dir
|
||||
func GitDir() (string, error) {
|
||||
git := &util.Git{}
|
||||
lines, err := git.OutputLines(context.Background(), nil, "rev-parse", "--git-dir")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(lines) != 1 {
|
||||
return "", errors.Errorf("received %d lines, expected one line", len(lines))
|
||||
}
|
||||
return lines[0], err
|
||||
}
|
||||
|
||||
func currentGitBranch() (string, error) {
|
||||
git := &util.Git{}
|
||||
lines, err := git.OutputLines(context.Background(), nil, "symbolic-ref", "--short", "HEAD")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(lines) != 1 {
|
||||
return "", errors.Errorf("received %d lines, expected one line", len(lines))
|
||||
}
|
||||
return lines[0], err
|
||||
}
|
||||
|
||||
// gitDir returns the git dir relative to the working dir
|
||||
func gitWriteTree(indexPath string) (string, error) {
|
||||
git := &util.Git{Env: []string{"GIT_INDEX_FILE=" + indexPath}}
|
||||
lines, err := git.OutputLines(context.Background(), nil, "write-tree")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(lines) != 1 {
|
||||
return "", errors.Errorf("received %d lines, expected one line", len(lines))
|
||||
}
|
||||
return lines[0], err
|
||||
}
|
||||
|
||||
func gitCommitTree(message, treeSHA string) (string, error) {
|
||||
git := &util.Git{}
|
||||
lines, err := git.OutputLines(context.Background(), nil, "commit-tree", "-m", message, treeSHA)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(lines) != 1 {
|
||||
return "", errors.Errorf("received %d lines, expected one line", len(lines))
|
||||
}
|
||||
return lines[0], err
|
||||
}
|
||||
|
||||
func gitUpdateRef(message, ref, commitSHA string) error {
|
||||
git := &util.Git{}
|
||||
_, err := git.Output(context.Background(), nil, "update-ref", "-m", message, ref, commitSHA)
|
||||
return err
|
||||
}
|
||||
|
||||
func gitAddUntrackedFiles(indexPath string) error {
|
||||
git := &util.Git{Env: []string{"GIT_INDEX_FILE=" + indexPath}}
|
||||
_, err := git.Output(context.Background(), nil, "add", ".")
|
||||
return err
|
||||
}
|
||||
|
||||
func gitAddIgnoredFiles(indexPath string) error {
|
||||
git := &util.Git{Env: []string{"GIT_INDEX_FILE=" + indexPath}}
|
||||
_, err := git.Output(context.Background(), nil, "add", "-f", "-A", ".")
|
||||
return err
|
||||
}
|
||||
|
||||
func GitAddRemote(configPath, name, url string) error {
|
||||
git := &util.Git{}
|
||||
_, err := git.Output(context.Background(), nil, "remote", "add", name, url)
|
||||
return err
|
||||
}
|
||||
|
||||
func GitPush(configPath, remote, branch string) error {
|
||||
git := &util.Git{}
|
||||
_, err := git.Output(context.Background(), nil, "push", remote, branch, "-f")
|
||||
return err
|
||||
}
|
||||
|
||||
type GitSaveConfig struct {
|
||||
AddUntracked bool
|
||||
AddIgnored bool
|
||||
}
|
||||
|
||||
type GitSave struct {
|
||||
log *zap.SugaredLogger
|
||||
conf *GitSaveConfig
|
||||
}
|
||||
|
||||
func NewGitSave(logger *zap.Logger, conf *GitSaveConfig) *GitSave {
|
||||
return &GitSave{log: logger.Sugar(), conf: conf}
|
||||
}
|
||||
|
||||
// Save adds files to the provided index, creates a tree and a commit pointing to
|
||||
// that tree, finally it creates a branch poiting to that commit
|
||||
// Save will use the current worktree index if available to speed the index generation
|
||||
func (s *GitSave) Save(tmpIndexPath, message, branchName string) error {
|
||||
gitdir, err := GitDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
indexPath := filepath.Join(gitdir, gitIndexFile)
|
||||
|
||||
curBranch, err := currentGitBranch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
indexExists, err := fileExists(indexPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if indexExists {
|
||||
// copy current git index to a temporary index
|
||||
if err := copyFile(indexPath, tmpIndexPath); err != nil {
|
||||
return err
|
||||
}
|
||||
s.log.Infof("created temporary index: %s", tmpIndexPath)
|
||||
// read the current branch tree information into the index
|
||||
git := &util.Git{Env: []string{"GIT_INDEX_FILE=" + tmpIndexPath}}
|
||||
_, err = git.Output(context.Background(), nil, "read-tree", curBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
s.log.Infof("index %s does not exist", indexPath)
|
||||
}
|
||||
|
||||
if s.conf.AddUntracked {
|
||||
s.log.Infof("adding untracked files")
|
||||
if err := gitAddUntrackedFiles(tmpIndexPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if s.conf.AddIgnored {
|
||||
s.log.Infof("adding ignored files")
|
||||
if err := gitAddIgnoredFiles(tmpIndexPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.log.Infof("writing tree file")
|
||||
treeSHA, err := gitWriteTree(tmpIndexPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.log.Infof("tree: %s", treeSHA)
|
||||
|
||||
s.log.Infof("committing tree")
|
||||
commitSHA, err := gitCommitTree("git-save", treeSHA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.log.Infof("commit: %s", commitSHA)
|
||||
|
||||
s.log.Infof("updating ref")
|
||||
if err = gitUpdateRef("git-save", filepath.Join(gitRefsPrefix, branchName), commitSHA); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue