123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- package git
- import (
- "errors"
- "io/ioutil"
- "os/exec"
- "strconv"
- "strings"
- "github.com/ditcraft/client/helpers"
- )
- // GetRepository will return the name of the current repostiry
- // minus the "https://github.com/" part
- func GetRepository() (string, error) {
- // Retrieving the current repository url from git
- cmdName := "git"
- cmdArgs := []string{"config", "--get", "remote.origin.url"}
- cmdOut, err := exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return "", errors.New("There was an error running git config - are you in a repository folder?")
- }
- cmdOutString := string(cmdOut)[0 : len(string(cmdOut))-1]
- // Removing unecessary stuff from the url
- cmdOutString = sanitizeURL(cmdOutString)
- configErr := checkGitSetup()
- if configErr != nil {
- helpers.PrintLine("You don't have a user.email and/or user.name set in your git config. dit won't work if this is not fixed!", 2)
- helpers.PrintLine("Please run 'git config user.name <GITHUB_USERNAME_HERE>' and 'git config user.email <GITHUB_EMAIL_HERE>'", 2)
- return "", errors.New("git is not configured correctly")
- }
- return cmdOutString, nil
- }
- // Clone wraps the git clone functionality and initialized the ditContract afterwards
- func Clone(_repository string) (string, error) {
- // Only GitHub repositores are supported right now
- // if !strings.Contains(_repository, "github.com") {
- // return "", errors.New("Currently only github repositories are supported")
- // }
- // Calling git clone
- cmdArgs := []string{"clone", _repository, "--progress"}
- cmdOut, err := exec.Command("git", cmdArgs...).CombinedOutput()
- if strings.Contains(string(cmdOut), "Repository not found") {
- return "", errors.New("Repository not found")
- }
- if strings.Contains(string(cmdOut), "unable to update url base from redirection") {
- return "", errors.New("Repository URL invalid or not found")
- }
- if strings.Contains(string(cmdOut), "already exists and is not an empty directory.") {
- return "", errors.New("Destination path already exists")
- }
- if strings.Contains(string(cmdOut), "Invalid username or password") {
- helpers.PrintLine("Wrong username or password", 2)
- helpers.PrintLine("Note: If you have 2FA activated for GitHub, please go to: ", 1)
- helpers.PrintLine("https://github.blog/2013-09-03-two-factor-authentication/#how-does-it-work-for-command-line-git", 1)
- return "", errors.New("")
- }
- if err != nil {
- return "", errors.New("There was an error running git clone")
- }
- // Removing unecessary stuff from the url
- _repository = sanitizeURL(_repository)
- configErr := checkGitSetup()
- if configErr != nil {
- helpers.PrintLine("You don't have a user.email and/or user.name set in your git config. dit won't work if this is not fixed!", 1)
- helpers.PrintLine("Please run 'git config user.name <GITHUB_USERNAME_HERE>' and 'git config user.email <GITHUB_EMAIL_HERE>'", 1)
- }
- return _repository, nil
- }
- func checkGitSetup() error {
- // Verifying whether a git user email is set correctly
- cmdName := "git"
- cmdArgs := []string{"config", "user.email"}
- cmdOut, err := exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if len(string(cmdOut)) <= 0 || err != nil {
- return errors.New("There is no git user.email set")
- }
- // Verifying whether a git user name is set correctly
- cmdName = "git"
- cmdArgs = []string{"config", "user.name"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if len(string(cmdOut)) <= 0 || err != nil {
- return errors.New("There is no git user.name set")
- }
- // Verifying whether the git password is being cached
- // If nothing is set, we will cache it for now
- cmdName = "git"
- cmdArgs = []string{"config", "credential.helper"}
- cmdOut, _ = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if len(string(cmdOut)) <= 0 {
- cmdName = "git"
- cmdArgs = []string{"config", "credential.helper", "cache"}
- _, _ = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- }
- return nil
- }
- // Validate will return an error when the connection to
- // the git provider fails (validated through a git fetch)
- func Validate() error {
- repoName, err := GetRepository()
- if err != nil {
- return err
- }
- repoName = strings.SplitN(repoName, "/", 2)[1]
- // Verifying whether a master branch (or any branch) exists
- cmdName := "git"
- cmdArgs := []string{"branch"}
- cmdOutBranch, err := exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error running git branch")
- }
- // Verifying whether the connection works correctly
- cmdName = "git"
- cmdArgs = []string{"fetch", "-v"}
- cmdOutFetch, err := exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if (!strings.Contains(string(cmdOutFetch), repoName) && len(cmdOutBranch) > 0) || err != nil {
- return errors.New("The connection to the git provider failed - please ensure that the repository is configured correctly")
- }
- return nil
- }
- // CheckForChanges will return an error if there are no new files to commit
- func CheckForChanges() error {
- // Verifying whether files have been added
- cmdName := "git"
- cmdArgs := []string{"ls-files", "--others", "--modified", "--exclude-standard"}
- cmdOut, err := exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error running the git ls-files command")
- }
- // If there are no files added, nothing can be committed
- if len(cmdOut) == 0 {
- return errors.New("There are no changed files to add for a commit")
- }
- return nil
- }
- // Commit will push the changes onto a new proposal branch
- func Commit(_proposalID int, _commitMessage string) error {
- // Name of the branch that will be used
- branchName := "dit_proposal_" + strconv.Itoa(_proposalID)
- // Verifying whether a master branch (or any branch) exists
- cmdName := "git"
- cmdArgs := []string{"branch"}
- cmdOut, err := exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error running git branch")
- }
- // If not: an empty readme will be pushed to master in order to
- // prevent the dit_proposal branch to be the main branch through the first commit
- if len(cmdOut) == 0 {
- repositoryName, err := GetRepository()
- if err != nil {
- return err
- }
- // Create a README.md file
- err = ioutil.WriteFile("README.md", []byte("# "+repositoryName), 0644)
- if err != nil {
- return errors.New("Failed to write README.md file")
- }
- // Add it for a commit
- cmdName = "git"
- cmdArgs = []string{"add", "README.md"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error adding the README.md")
- }
- // Commit it
- cmdName = "git"
- cmdArgs = []string{"commit", "-m", "Initial push to master"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error commiting the initial README")
- }
- // Push it
- cmdName = "git"
- cmdArgs = []string{"push", "-v"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if strings.Contains(string(cmdOut), "Invalid username or password") {
- helpers.PrintLine("Wrong username or password", 2)
- helpers.PrintLine("Note: If you have 2FA activated for GitHub, please go to: ", 2)
- helpers.PrintLine("https://github.blog/2013-09-03-two-factor-authentication/#how-does-it-work-for-command-line-git", 2)
- }
- if err != nil {
- return errors.New("There was an error pushing the initial commit")
- }
- }
- // Switch to the dit_proposal branch
- cmdName = "git"
- cmdArgs = []string{"checkout", "-b", branchName}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error running git checkout")
- }
- // Verify whether we are on the dit_proposal branch
- cmdName = "git"
- cmdArgs = []string{"status"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil || !strings.Contains(string(cmdOut), "On branch "+branchName) {
- return errors.New("There was an error running git status")
- }
- // Adding all files that can be added
- cmdName = "git"
- cmdArgs = []string{"add", "."}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error running the git add command")
- }
- // Verifying whether the files have been added
- cmdName = "git"
- cmdArgs = []string{"diff", "--cached", "--name-only"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error running the git diff command")
- }
- // If there are no files added, nothing can be committed
- if len(cmdOut) == 0 {
- return errors.New("There are no changed files to add for a commit")
- }
- // Commit the changes with the provided commit message
- cmdName = "git"
- cmdArgs = []string{"commit", "-m", _commitMessage, "-v"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil || !strings.Contains(string(cmdOut), _commitMessage) {
- return errors.New("There was an error running git commit")
- }
- // Push these changes to the new branch
- cmdName = "git"
- cmdArgs = []string{"push", "--set-upstream", "origin", branchName, "-v"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if strings.Contains(string(cmdOut), "Invalid username or password") {
- helpers.PrintLine("Wrong username or password", 2)
- helpers.PrintLine("Note: If you have 2FA activated for GitHub, please go to: ", 2)
- helpers.PrintLine("https://github.blog/2013-09-03-two-factor-authentication/#how-does-it-work-for-command-line-git", 2)
- }
- if err != nil || !strings.Contains(string(cmdOut), "set up to track remote branch") {
- return errors.New("There was an error running git push")
- }
- // Switch to the master branch
- cmdName = "git"
- cmdArgs = []string{"checkout", "master"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error running the last git checkout")
- }
- // Verify whether we are on the master branch again
- cmdName = "git"
- cmdArgs = []string{"status"}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil || !strings.Contains(string(cmdOut), "On branch master") {
- return errors.New("There was an error running the last git status")
- }
- return nil
- }
- // Merge will merge a dit_proposal into the master after a successfull vote
- func Merge(_proposalID string) error {
- // Name of the branch that will be used
- branchName := "dit_proposal_" + _proposalID
- // Switching to the master branch
- cmdName := "git"
- cmdArgs := []string{"checkout", "master"}
- _, err := exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error running git checkout master")
- }
- // Merging the dit_proposal branch into the master
- cmdName = "git"
- cmdArgs = []string{"merge", branchName, "-v"}
- _, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil {
- return errors.New("There was an error running git merge")
- }
- // Pushing the merged master branch
- cmdName = "git"
- cmdArgs = []string{"push", "-v"}
- cmdOut, err := exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if strings.Contains(string(cmdOut), "Invalid username or password") {
- helpers.PrintLine("Wrong username or password", 2)
- helpers.PrintLine("Note: If you have 2FA activated for GitHub, please go to: ", 2)
- helpers.PrintLine("https://github.blog/2013-09-03-two-factor-authentication/#how-does-it-work-for-command-line-git", 2)
- }
- if err != nil {
- return errors.New("There was an error running git push")
- }
- // Deleting the now obsolete dit_proposal branch
- err = DeleteBranch(_proposalID)
- if err != nil {
- return errors.New("There was an error deleting the branch after a successful merge")
- }
- return nil
- }
- // DeleteBranch will delete a dit_proposal branch after it has been merged or after an unsuccessful vote
- func DeleteBranch(_proposalID string) error {
- // Name of the branch that will be used
- branchName := "dit_proposal_" + _proposalID
- // Remove the branch on the remote side
- cmdName := "git"
- cmdArgs := []string{"push", "--delete", "origin", branchName}
- cmdOut, err := exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if strings.Contains(string(cmdOut), "Invalid username or password") {
- helpers.PrintLine("Wrong username or password", 2)
- helpers.PrintLine("Note: If you have 2FA activated for GitHub, please go to: ", 2)
- helpers.PrintLine("https://github.blog/2013-09-03-two-factor-authentication/#how-does-it-work-for-command-line-git", 2)
- }
- if err != nil || !strings.Contains(string(cmdOut), "deleted") {
- return errors.New("There was an error deleting the branch remotely")
- }
- // Remove the branch on the local side
- cmdName = "git"
- cmdArgs = []string{"branch", "-D", branchName}
- cmdOut, err = exec.Command(cmdName, cmdArgs...).CombinedOutput()
- if err != nil || !strings.Contains(string(cmdOut), "Deleted") {
- return errors.New("There was an error deleting the branch locally")
- }
- return nil
- }
- func sanitizeURL(_url string) string {
- _url = strings.Replace(_url, "http://", "", -1)
- _url = strings.Replace(_url, "https://", "", -1)
- _url = strings.Replace(_url, "git@", "", -1)
- _url = strings.Replace(_url, "github.com:", "github.com/", -1)
- _url = strings.Replace(_url, "gitlab.com:", "gitlab.com/", -1)
- _url = strings.Replace(_url, "bitbucket.org:", "bitbucket.com/", -1)
- _url = strings.Replace(_url, ".git", "", -1)
- return _url
- }
|