f1b8d88d6b
* Improving mattermost auth implementation * Making mattermost-auth based on shared database access * Reverting unneeded changes in the config.json file * Fixing tiny problems * Removing the need of using the mattermost session token * Fixing some bugs and allowing to not-bind the server to any port * Small fix to correctly get the templates * Adding the mattermost-plugin code inside focalboard repo * Adding a not working code part of the cluster websocket communication * Updating the mattermost version * Adding the cluster messages for the websockets * Updating to the new node version * Making it compatible with S3 * Addressing some tiny problems * Fixing server tests * Adds support for MySQL migrations and initialization Co-authored-by: Miguel de la Cruz <miguel@mcrx.me>
176 lines
4.6 KiB
Go
176 lines
4.6 KiB
Go
package plan
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/mattermost/mattermost-plugin-starter-template/build/sync/plan/git"
|
|
)
|
|
|
|
// CheckFail is a custom error type used to indicate a
|
|
// check that did not pass (but did not fail due to external
|
|
// causes.
|
|
// Use `IsCheckFail` to check if an error is a check failure.
|
|
type CheckFail string
|
|
|
|
func (e CheckFail) Error() string {
|
|
return string(e)
|
|
}
|
|
|
|
// CheckFailf creates an error with the specified message string.
|
|
// The error will pass the IsCheckFail filter.
|
|
func CheckFailf(msg string, args ...interface{}) CheckFail {
|
|
if len(args) > 0 {
|
|
msg = fmt.Sprintf(msg, args...)
|
|
}
|
|
return CheckFail(msg)
|
|
}
|
|
|
|
// IsCheckFail determines if an error is a check fail error.
|
|
func IsCheckFail(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
_, ok := err.(CheckFail)
|
|
return ok
|
|
}
|
|
|
|
// RepoIsCleanChecker checks whether the git repository is clean.
|
|
type RepoIsCleanChecker struct {
|
|
Params struct {
|
|
Repo RepoID
|
|
}
|
|
}
|
|
|
|
// Check implements the Checker interface.
|
|
// The path parameter is ignored because this checker checks the state of a repository.
|
|
func (r RepoIsCleanChecker) Check(_ string, ctx Setup) error {
|
|
ctx.Logf("checking if repository %q is clean", r.Params.Repo)
|
|
rc := ctx.GetRepo(r.Params.Repo)
|
|
repo := rc.Git
|
|
worktree, err := repo.Worktree()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get worktree: %v", err)
|
|
}
|
|
status, err := worktree.Status()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get worktree status: %v", err)
|
|
}
|
|
if !status.IsClean() {
|
|
return CheckFailf("%q repository is not clean", r.Params.Repo)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PathExistsChecker checks whether the fle or directory with the
|
|
// path exists. If it does not, an error is returned.
|
|
type PathExistsChecker struct {
|
|
Params struct {
|
|
Repo RepoID
|
|
}
|
|
}
|
|
|
|
// Check implements the Checker interface.
|
|
func (r PathExistsChecker) Check(path string, ctx Setup) error {
|
|
repo := r.Params.Repo
|
|
if repo == "" {
|
|
repo = TargetRepo
|
|
}
|
|
ctx.Logf("checking if path %q exists in repo %q", path, repo)
|
|
absPath := ctx.PathInRepo(repo, path)
|
|
_, err := os.Stat(absPath)
|
|
if os.IsNotExist(err) {
|
|
return CheckFailf("path %q does not exist", path)
|
|
} else if err != nil {
|
|
return fmt.Errorf("failed to stat path %q: %v", absPath, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FileUnalteredChecker checks whether the file in Repo is
|
|
// an unaltered version of that same file in ReferenceRepo.
|
|
//
|
|
// Its purpose is to check that a file has not been changed after forking a repository.
|
|
// It could be an old unaltered version, so the git history of the file is traversed
|
|
// until a matching version is found.
|
|
//
|
|
// If the repositories in the parameters are not specified,
|
|
// reference will default to the source repository and repo - to the target.
|
|
type FileUnalteredChecker struct {
|
|
Params struct {
|
|
SourceRepo RepoID `json:"compared-to"`
|
|
TargetRepo RepoID `json:"in"`
|
|
}
|
|
}
|
|
|
|
// Check implements the Checker interface.
|
|
func (f FileUnalteredChecker) Check(path string, setup Setup) error {
|
|
setup.Logf("checking if file %q has not been altered", path)
|
|
repo := f.Params.TargetRepo
|
|
if repo == "" {
|
|
repo = TargetRepo
|
|
}
|
|
source := f.Params.SourceRepo
|
|
if source == "" {
|
|
source = SourceRepo
|
|
}
|
|
trgPath := setup.PathInRepo(repo, path)
|
|
srcPath := setup.PathInRepo(source, path)
|
|
|
|
fileHashes, err := git.FileHistory(path, setup.GetRepo(source).Git)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var srcDeleted bool
|
|
srcInfo, err := os.Stat(srcPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
srcDeleted = true
|
|
} else {
|
|
return fmt.Errorf("failed to get stat for %q: %v", trgPath, err)
|
|
}
|
|
} else if srcInfo.IsDir() {
|
|
return fmt.Errorf("%q is a directory in source repository", path)
|
|
}
|
|
|
|
trgInfo, err := os.Stat(trgPath)
|
|
if os.IsNotExist(err) {
|
|
if srcDeleted {
|
|
// File has been deleted in target and source repositories.
|
|
// Consider it unaltered.
|
|
return nil
|
|
}
|
|
// Check if the file was ever in git history.
|
|
_, err := git.FileHistory(path, setup.GetRepo(repo).Git)
|
|
if errors.Is(err, git.ErrNotFound) {
|
|
// This is a new file being introduced to the target repo.
|
|
// Consider it unaltered.
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
return CheckFailf("file %q has been deleted", trgPath)
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get stat for %q: %v", trgPath, err)
|
|
}
|
|
if trgInfo.IsDir() {
|
|
return fmt.Errorf("%q is a directory", trgPath)
|
|
}
|
|
|
|
currentHash, err := git.GetFileHash(trgPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sort.Strings(fileHashes)
|
|
idx := sort.SearchStrings(fileHashes, currentHash)
|
|
if idx < len(fileHashes) && fileHashes[idx] == currentHash {
|
|
return nil
|
|
}
|
|
return CheckFailf("file %q has been altered", trgPath)
|
|
}
|