mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package unittest
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/hanzoai/git/modules/setting"
|
|
"github.com/hanzoai/git/modules/util"
|
|
)
|
|
|
|
// SyncFile synchronizes the two files. This is skipped if both files
|
|
// exist and the size, modtime, and mode match.
|
|
func SyncFile(srcPath, destPath string) error {
|
|
dest, err := os.Stat(destPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return util.CopyFile(srcPath, destPath)
|
|
}
|
|
return err
|
|
}
|
|
|
|
src, err := os.Stat(srcPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if src.Size() == dest.Size() &&
|
|
src.ModTime().Equal(dest.ModTime()) &&
|
|
src.Mode() == dest.Mode() {
|
|
return nil
|
|
}
|
|
|
|
return util.CopyFile(srcPath, destPath)
|
|
}
|
|
|
|
// SyncDirs synchronizes files recursively from source to target directory.
|
|
// It returns error when error occurs in underlying functions.
|
|
func SyncDirs(srcPath, destPath string) error {
|
|
destPath = filepath.Clean(destPath)
|
|
destPathAbs, err := filepath.Abs(destPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
devDataPathAbs, err := filepath.Abs(filepath.Join(setting.GetGitTestSourceRoot(), "data"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if strings.HasPrefix(destPathAbs+string(filepath.Separator), devDataPathAbs+string(filepath.Separator)) {
|
|
return errors.New("destination path should not be inside the data directory, otherwise your data for dev mode will be removed")
|
|
}
|
|
|
|
err = os.MkdirAll(destPath, os.ModePerm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// the keep file is used to keep the directory in a git repository, it doesn't need to be synced
|
|
// and go-git doesn't work with the ".keep" file (it would report errors like "ref is empty")
|
|
const keepFile = ".keep"
|
|
|
|
// find and delete all untracked files
|
|
destFiles, err := util.ListDirRecursively(destPath, &util.ListDirOptions{IncludeDir: true})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, destFile := range destFiles {
|
|
destFilePath := filepath.Join(destPath, destFile)
|
|
shouldRemove := filepath.Base(destFilePath) == keepFile
|
|
if _, err = os.Stat(filepath.Join(srcPath, destFile)); err != nil {
|
|
if os.IsNotExist(err) {
|
|
shouldRemove = true
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
// if src file does not exist, remove dest file
|
|
if shouldRemove {
|
|
if err = os.RemoveAll(destFilePath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// sync src files to dest
|
|
srcFiles, err := util.ListDirRecursively(srcPath, &util.ListDirOptions{IncludeDir: true})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, srcFile := range srcFiles {
|
|
destFilePath := filepath.Join(destPath, srcFile)
|
|
// util.ListDirRecursively appends a slash to the directory name
|
|
if strings.HasSuffix(srcFile, "/") {
|
|
err = os.MkdirAll(destFilePath, os.ModePerm)
|
|
} else if filepath.Base(destFilePath) != keepFile {
|
|
err = SyncFile(filepath.Join(srcPath, srcFile), destFilePath)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|