github.com/devtron-labs/ci-runner@v0.0.0-20240518055909-b2672f3349d7/helper/GitManager.go (about)

     1  /*
     2   *  Copyright 2020 Devtron Labs
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package helper
    19  
    20  import (
    21  	"context"
    22  	"github.com/devtron-labs/ci-runner/util"
    23  	"log"
    24  	"os"
    25  	"path/filepath"
    26  )
    27  
    28  type GitOptions struct {
    29  	UserName      string   `json:"userName"`
    30  	Password      string   `json:"password"`
    31  	SshPrivateKey string   `json:"sshPrivateKey"`
    32  	AccessToken   string   `json:"accessToken"`
    33  	AuthMode      AuthMode `json:"authMode"`
    34  }
    35  
    36  type WebhookData struct {
    37  	Id              int               `json:"id"`
    38  	EventActionType string            `json:"eventActionType"`
    39  	Data            map[string]string `json:"data"`
    40  }
    41  
    42  type GitContext struct {
    43  	context.Context // Embedding original Go context
    44  	Auth            *BasicAuth
    45  }
    46  type BasicAuth struct {
    47  	Username, Password string
    48  }
    49  
    50  type AuthMode string
    51  
    52  const (
    53  	AUTH_MODE_USERNAME_PASSWORD AuthMode = "USERNAME_PASSWORD"
    54  	AUTH_MODE_SSH               AuthMode = "SSH"
    55  	AUTH_MODE_ACCESS_TOKEN      AuthMode = "ACCESS_TOKEN"
    56  	AUTH_MODE_ANONYMOUS         AuthMode = "ANONYMOUS"
    57  )
    58  
    59  type SourceType string
    60  
    61  const (
    62  	SOURCE_TYPE_BRANCH_FIXED SourceType = "SOURCE_TYPE_BRANCH_FIXED"
    63  	SOURCE_TYPE_WEBHOOK      SourceType = "WEBHOOK"
    64  )
    65  
    66  const (
    67  	WEBHOOK_SELECTOR_TARGET_CHECKOUT_NAME        string = "target checkout"
    68  	WEBHOOK_SELECTOR_SOURCE_CHECKOUT_NAME        string = "source checkout"
    69  	WEBHOOK_SELECTOR_TARGET_CHECKOUT_BRANCH_NAME string = "target branch name"
    70  
    71  	WEBHOOK_EVENT_MERGED_ACTION_TYPE     string = "merged"
    72  	WEBHOOK_EVENT_NON_MERGED_ACTION_TYPE string = "non-merged"
    73  )
    74  
    75  type GitManager struct {
    76  	gitCliManager GitCliManager
    77  }
    78  
    79  func NewGitManagerImpl(gitCliManager GitCliManager) *GitManager {
    80  	return &GitManager{
    81  		gitCliManager: gitCliManager,
    82  	}
    83  }
    84  
    85  func (impl *GitManager) CloneAndCheckout(ciProjectDetails []CiProjectDetails) error {
    86  	for index, prj := range ciProjectDetails {
    87  		// git clone
    88  
    89  		log.Println("-----> git " + prj.CloningMode + " cloning " + prj.GitRepository)
    90  
    91  		if prj.CheckoutPath != "./" {
    92  			if _, err := os.Stat(prj.CheckoutPath); os.IsNotExist(err) {
    93  				_ = os.Mkdir(prj.CheckoutPath, os.ModeDir)
    94  			}
    95  		}
    96  		var cErr error
    97  		var auth *BasicAuth
    98  		authMode := prj.GitOptions.AuthMode
    99  		switch authMode {
   100  		case AUTH_MODE_USERNAME_PASSWORD:
   101  			auth = &BasicAuth{Password: prj.GitOptions.Password, Username: prj.GitOptions.UserName}
   102  		case AUTH_MODE_ACCESS_TOKEN:
   103  			auth = &BasicAuth{Password: prj.GitOptions.AccessToken, Username: prj.GitOptions.UserName}
   104  		default:
   105  			auth = &BasicAuth{}
   106  		}
   107  
   108  		gitContext := GitContext{
   109  			Auth: auth,
   110  		}
   111  		// create ssh private key on disk
   112  		if authMode == AUTH_MODE_SSH {
   113  			cErr = util.CreateSshPrivateKeyOnDisk(index, prj.GitOptions.SshPrivateKey)
   114  			cErr = util.CreateSshPrivateKeyOnDisk(index, prj.GitOptions.SshPrivateKey)
   115  			if cErr != nil {
   116  				log.Fatal("could not create ssh private key on disk ", " err ", cErr)
   117  			}
   118  		}
   119  
   120  		_, msgMsg, cErr := impl.gitCliManager.Clone(gitContext, prj)
   121  		if cErr != nil {
   122  			log.Fatal("could not clone repo ", " err ", cErr, "msgMsg", msgMsg)
   123  		}
   124  
   125  		// checkout code
   126  		if prj.SourceType == SOURCE_TYPE_BRANCH_FIXED {
   127  			// checkout incoming commit hash or branch name
   128  			checkoutSource := ""
   129  			if len(prj.CommitHash) > 0 {
   130  				checkoutSource = prj.CommitHash
   131  			} else {
   132  				if len(prj.SourceValue) == 0 {
   133  					prj.SourceValue = "main"
   134  				}
   135  				checkoutSource = prj.SourceValue
   136  			}
   137  			log.Println("checkout commit in branch fix : ", checkoutSource)
   138  			msgMsg, cErr = impl.gitCliManager.GitCheckout(gitContext, prj.CheckoutPath, checkoutSource, authMode, prj.FetchSubmodules, prj.GitRepository)
   139  			if cErr != nil {
   140  				log.Fatal("could not checkout hash ", " err ", cErr, "msgMsg", msgMsg)
   141  			}
   142  
   143  		} else if prj.SourceType == SOURCE_TYPE_WEBHOOK {
   144  
   145  			webhookData := prj.WebhookData
   146  			webhookDataData := webhookData.Data
   147  
   148  			targetCheckout := webhookDataData[WEBHOOK_SELECTOR_TARGET_CHECKOUT_NAME]
   149  			if len(targetCheckout) == 0 {
   150  				log.Fatal("could not get target checkout from request data")
   151  			}
   152  
   153  			log.Println("checkout commit in webhook : ", targetCheckout)
   154  
   155  			// checkout target hash
   156  			msgMsg, cErr = impl.gitCliManager.GitCheckout(gitContext, prj.CheckoutPath, targetCheckout, authMode, prj.FetchSubmodules, prj.GitRepository)
   157  			if cErr != nil {
   158  				log.Fatal("could not checkout  ", "targetCheckout ", targetCheckout, " err ", cErr, " msgMsg", msgMsg)
   159  				return cErr
   160  			}
   161  
   162  			// merge source if action type is merged
   163  			if webhookData.EventActionType == WEBHOOK_EVENT_MERGED_ACTION_TYPE {
   164  				sourceCheckout := webhookDataData[WEBHOOK_SELECTOR_SOURCE_CHECKOUT_NAME]
   165  
   166  				// throw error if source checkout is empty
   167  				if len(sourceCheckout) == 0 {
   168  					log.Fatal("sourceCheckout is empty")
   169  				}
   170  
   171  				log.Println("merge commit in webhook : ", sourceCheckout)
   172  
   173  				// merge source
   174  				_, msgMsg, cErr = impl.gitCliManager.Merge(filepath.Join(util.WORKINGDIR, prj.CheckoutPath), sourceCheckout)
   175  				if cErr != nil {
   176  					log.Fatal("could not merge ", "sourceCheckout ", sourceCheckout, " err ", cErr, " msgMsg", msgMsg)
   177  					return cErr
   178  				}
   179  
   180  			}
   181  
   182  		}
   183  
   184  	}
   185  	return nil
   186  }