Fix `MOVE` action for Commit API (#1037)

pull/3484/head
Johannes Batzill 2024-02-10 01:10:20 +00:00 committed by Harness
parent 9d8bf25692
commit 40e5b84ba1
2 changed files with 51 additions and 60 deletions

View File

@ -15,11 +15,9 @@
package git
import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"path"
"strings"
"time"
@ -35,7 +33,6 @@ import (
)
const (
filePrefix = "file://"
defaultFilePermission = "100644" // 0o644 default file permission
)
@ -318,8 +315,7 @@ func (s *Service) prepareTreeEmptyRepo(
return errors.InvalidArgument("invalid path")
}
reader := bytes.NewReader(action.Payload)
if err := createFile(ctx, shared, nil, filePath, defaultFilePermission, reader); err != nil {
if err := createFile(ctx, shared, nil, filePath, defaultFilePermission, action.Payload); err != nil {
return errors.Internal(err, "failed to create file '%s'", action.Path)
}
}
@ -389,15 +385,13 @@ func (s *Service) processAction(
return errors.InvalidArgument("path cannot be empty")
}
reader := bytes.NewReader(action.Payload)
switch action.Action {
case CreateAction:
err = createFile(ctx, shared, commit, filePath, defaultFilePermission, reader)
err = createFile(ctx, shared, commit, filePath, defaultFilePermission, action.Payload)
case UpdateAction:
err = updateFile(ctx, shared, commit, filePath, action.SHA, defaultFilePermission, reader)
err = updateFile(ctx, shared, commit, filePath, action.SHA, defaultFilePermission, action.Payload)
case MoveAction:
err = moveFile(ctx, shared, commit, filePath, defaultFilePermission, reader)
err = moveFile(ctx, shared, commit, filePath, action.SHA, defaultFilePermission, action.Payload)
case DeleteAction:
err = deleteFile(ctx, shared, filePath)
}
@ -406,7 +400,7 @@ func (s *Service) processAction(
}
func createFile(ctx context.Context, repo SharedRepo, commit *git.Commit,
filePath, mode string, reader io.Reader) error {
filePath, mode string, payload []byte) error {
// only check path availability if a source commit is available (empty repo won't have such a commit)
if commit != nil {
if err := checkPathAvailability(commit, filePath, true); err != nil {
@ -414,7 +408,7 @@ func createFile(ctx context.Context, repo SharedRepo, commit *git.Commit,
}
}
hash, err := repo.WriteGitObject(ctx, reader)
hash, err := repo.WriteGitObject(ctx, bytes.NewReader(payload))
if err != nil {
return fmt.Errorf("createFile: error hashing object: %w", err)
}
@ -427,7 +421,7 @@ func createFile(ctx context.Context, repo SharedRepo, commit *git.Commit,
}
func updateFile(ctx context.Context, repo SharedRepo, commit *git.Commit, filePath, sha,
mode string, reader io.Reader) error {
mode string, payload []byte) error {
// get file mode from existing file (default unless executable)
entry, err := getFileEntry(commit, sha, filePath)
if err != nil {
@ -437,7 +431,7 @@ func updateFile(ctx context.Context, repo SharedRepo, commit *git.Commit, filePa
mode = "100755"
}
hash, err := repo.WriteGitObject(ctx, reader)
hash, err := repo.WriteGitObject(ctx, bytes.NewReader(payload))
if err != nil {
return fmt.Errorf("updateFile: error hashing object: %w", err)
}
@ -449,38 +443,42 @@ func updateFile(ctx context.Context, repo SharedRepo, commit *git.Commit, filePa
}
func moveFile(ctx context.Context, repo SharedRepo, commit *git.Commit,
filePath, mode string, reader io.Reader) error {
buffer := &bytes.Buffer{}
newPath, err := parsePayload(reader, buffer)
filePath, sha, mode string, payload []byte) error {
newPath, newContent, err := parseMovePayload(payload)
if err != nil {
return err
}
if buffer.Len() == 0 && newPath != "" {
err = repo.ShowFile(ctx, filePath, commit.ID.String(), buffer)
// ensure file exists and matches SHA
entry, err := getFileEntry(commit, sha, filePath)
if err != nil {
return fmt.Errorf("moveFile: failed lookup for path '%s': %w", newPath, err)
}
return err
}
// ensure new path is available
if err = checkPathAvailability(commit, newPath, false); err != nil {
return err
}
filesInIndex, err := repo.LsFiles(ctx, filePath)
if err != nil {
return fmt.Errorf("moveFile: listing files error: %w", err)
}
if !slices.Contains(filesInIndex, filePath) {
return errors.NotFound("path %s not found", filePath)
}
hash, err := repo.WriteGitObject(ctx, buffer)
var fileHash string
var fileMode string
if newContent != nil {
hash, err := repo.WriteGitObject(ctx, bytes.NewReader(newContent))
if err != nil {
return fmt.Errorf("moveFile: error hashing object: %w", err)
}
if err = repo.AddObjectToIndex(ctx, mode, hash, newPath); err != nil {
fileHash = hash
fileMode = mode
if entry.IsExecutable() {
fileMode = "100755"
}
} else {
fileHash = entry.ID.String()
fileMode = entry.Mode().String()
}
if err = repo.AddObjectToIndex(ctx, fileMode, fileHash, newPath); err != nil {
return fmt.Errorf("moveFile: add object error: %w", err)
}
@ -519,7 +517,7 @@ func getFileEntry(
}
// If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error
if sha == "" || sha != entry.ID.String() {
if sha != "" && sha != entry.ID.String() {
return nil, errors.InvalidArgument("sha does not match for path %s [given: %s, expected: %s]",
path, sha, entry.ID.String())
}
@ -564,29 +562,22 @@ func checkPathAvailability(commit *git.Commit, filePath string, isNewFile bool)
return nil
}
func parsePayload(payload io.Reader, content io.Writer) (string, error) {
newPath := ""
reader := bufio.NewReader(payload)
// check for filePrefix
prefixBytes := make([]byte, len(filePrefix))
if _, err := reader.Read(prefixBytes); err != nil {
if errors.Is(err, io.EOF) {
return "", nil
}
return "", err
}
// check if payload starts with filePrefix constant
if bytes.Equal(prefixBytes, []byte(filePrefix)) {
filename, _ := reader.ReadString('\n') // no err handling because next statement will check filename
newPath = files.CleanUploadFileName(filename)
if newPath == "" {
return "", types.ErrInvalidPath
}
func parseMovePayload(payload []byte) (string, []byte, error) {
var newContent []byte
var newPath string
filePathEnd := bytes.IndexByte(payload, 0)
if filePathEnd < 0 {
newPath = string(payload)
newContent = nil
} else {
if _, err := content.Write(prefixBytes); err != nil {
return "", err
newPath = string(payload[:filePathEnd])
newContent = payload[filePathEnd+1:]
}
newPath = files.CleanUploadFileName(newPath)
if newPath == "" {
return "", nil, types.ErrInvalidPath
}
_, err := io.Copy(content, reader)
return newPath, err
return newPath, newContent, nil
}

View File

@ -107,7 +107,7 @@ export function useCommitModal({
{
action: commitAction,
path: oldResourcePath || resourcePath,
payload: `${oldResourcePath ? `file://${resourcePath}\n` : ''}${payload}`,
payload: `${oldResourcePath ? `${resourcePath}\0` : ''}${payload}`,
sha
// encoding: 'base64',
// payload: window.btoa(payload || '')
@ -153,7 +153,7 @@ export function useCommitModal({
{
action: commitAction,
path: oldResourcePath || resourcePath,
payload: `${oldResourcePath ? `file://${resourcePath}\n` : ''}${payload}`,
payload: `${oldResourcePath ? `${resourcePath}\0` : ''}${payload}`,
sha
}
],