github.com/jfrog/jfrog-cli-core@v1.12.1/artifactory/utils/buildutils.go (about)

     1  package utils
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/jfrog/jfrog-cli-core/utils/coreutils"
    16  	"github.com/jfrog/jfrog-client-go/artifactory/buildinfo"
    17  	"github.com/jfrog/jfrog-client-go/auth"
    18  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    19  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    20  	"github.com/jfrog/jfrog-client-go/utils/log"
    21  )
    22  
    23  const BuildInfoDetails = "details"
    24  const BuildTempPath = "jfrog/builds/"
    25  
    26  func GetBuildDir(buildName, buildNumber, projectKey string) (string, error) {
    27  	encodedDirName := base64.StdEncoding.EncodeToString([]byte(buildName + "_" + buildNumber + "_" + projectKey))
    28  	buildsDir := filepath.Join(coreutils.GetCliPersistentTempDirPath(), BuildTempPath, encodedDirName)
    29  	err := os.MkdirAll(buildsDir, 0777)
    30  	if errorutils.CheckError(err) != nil {
    31  		return "", err
    32  	}
    33  	return buildsDir, nil
    34  }
    35  
    36  func CreateBuildProperties(buildName, buildNumber, projectKey string) (string, error) {
    37  	if buildName == "" || buildNumber == "" {
    38  		return "", nil
    39  	}
    40  
    41  	buildGeneralDetails, err := ReadBuildInfoGeneralDetails(buildName, buildNumber, projectKey)
    42  	if err != nil {
    43  		return fmt.Sprintf("build.name=%s;build.number=%s", buildName, buildNumber), err
    44  	}
    45  	timestamp := strconv.FormatInt(buildGeneralDetails.Timestamp.UnixNano()/int64(time.Millisecond), 10)
    46  	return fmt.Sprintf("build.name=%s;build.number=%s;build.timestamp=%s", buildName, buildNumber, timestamp), nil
    47  }
    48  
    49  func getPartialsBuildDir(buildName, buildNumber, projectKey string) (string, error) {
    50  	buildDir, err := GetBuildDir(buildName, buildNumber, projectKey)
    51  	if err != nil {
    52  		return "", err
    53  	}
    54  	buildDir = filepath.Join(buildDir, "partials")
    55  	err = os.MkdirAll(buildDir, 0777)
    56  	if errorutils.CheckError(err) != nil {
    57  		return "", err
    58  	}
    59  	return buildDir, nil
    60  }
    61  
    62  func saveBuildData(action interface{}, buildName, buildNumber, projectKey string) error {
    63  	b, err := json.Marshal(&action)
    64  	if errorutils.CheckError(err) != nil {
    65  		return err
    66  	}
    67  	var content bytes.Buffer
    68  	err = json.Indent(&content, b, "", "  ")
    69  	if errorutils.CheckError(err) != nil {
    70  		return err
    71  	}
    72  	dirPath, err := getPartialsBuildDir(buildName, buildNumber, projectKey)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	log.Debug("Creating temp build file at:", dirPath)
    77  	tempFile, err := os.CreateTemp(dirPath, "temp")
    78  	if err != nil {
    79  		return err
    80  	}
    81  	defer tempFile.Close()
    82  	_, err = tempFile.Write([]byte(content.String()))
    83  	return err
    84  }
    85  
    86  func SaveBuildInfo(buildName, buildNumber, projectKey string, buildInfo *buildinfo.BuildInfo) error {
    87  	b, err := json.Marshal(buildInfo)
    88  	if errorutils.CheckError(err) != nil {
    89  		return err
    90  	}
    91  	var content bytes.Buffer
    92  	err = json.Indent(&content, b, "", "  ")
    93  	if errorutils.CheckError(err) != nil {
    94  		return err
    95  	}
    96  	dirPath, err := GetBuildDir(buildName, buildNumber, projectKey)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	log.Debug("Creating temp build file at: " + dirPath)
   101  	tempFile, err := os.CreateTemp(dirPath, "temp")
   102  	if errorutils.CheckError(err) != nil {
   103  		return err
   104  	}
   105  	defer tempFile.Close()
   106  	_, err = tempFile.Write([]byte(content.String()))
   107  	return errorutils.CheckError(err)
   108  }
   109  
   110  func SaveBuildGeneralDetails(buildName, buildNumber, projectKey string) error {
   111  	partialsBuildDir, err := getPartialsBuildDir(buildName, buildNumber, projectKey)
   112  	if err != nil {
   113  		return err
   114  	}
   115  	log.Debug("Saving build general details at: " + partialsBuildDir)
   116  	detailsFilePath := filepath.Join(partialsBuildDir, BuildInfoDetails)
   117  	var exists bool
   118  	exists, err = fileutils.IsFileExists(detailsFilePath, false)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	if exists {
   123  		return nil
   124  	}
   125  	meta := buildinfo.General{
   126  		Timestamp: time.Now(),
   127  	}
   128  	b, err := json.Marshal(&meta)
   129  	if err != nil {
   130  		return errorutils.CheckError(err)
   131  	}
   132  	var content bytes.Buffer
   133  	err = json.Indent(&content, b, "", "  ")
   134  	if err != nil {
   135  		return errorutils.CheckError(err)
   136  	}
   137  	err = os.WriteFile(detailsFilePath, []byte(content.String()), 0600)
   138  	return errorutils.CheckError(err)
   139  }
   140  
   141  type populatePartialBuildInfo func(partial *buildinfo.Partial)
   142  
   143  func SavePartialBuildInfo(buildName, buildNumber, projectKey string, populatePartialBuildInfoFunc populatePartialBuildInfo) error {
   144  	partialBuildInfo := new(buildinfo.Partial)
   145  	partialBuildInfo.Timestamp = time.Now().UnixNano() / int64(time.Millisecond)
   146  	populatePartialBuildInfoFunc(partialBuildInfo)
   147  	return saveBuildData(partialBuildInfo, buildName, buildNumber, projectKey)
   148  }
   149  
   150  func GetGeneratedBuildsInfo(buildName, buildNumber, projectKey string) ([]*buildinfo.BuildInfo, error) {
   151  	buildDir, err := GetBuildDir(buildName, buildNumber, projectKey)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	buildFiles, err := fileutils.ListFiles(buildDir, false)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	var generatedBuildsInfo []*buildinfo.BuildInfo
   161  	for _, buildFile := range buildFiles {
   162  		dir, err := fileutils.IsDirExists(buildFile, false)
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  		if dir {
   167  			continue
   168  		}
   169  		content, err := fileutils.ReadFile(buildFile)
   170  		if err != nil {
   171  			return nil, err
   172  		}
   173  		buildInfo := new(buildinfo.BuildInfo)
   174  		json.Unmarshal(content, &buildInfo)
   175  		generatedBuildsInfo = append(generatedBuildsInfo, buildInfo)
   176  	}
   177  	return generatedBuildsInfo, nil
   178  }
   179  
   180  func ReadPartialBuildInfoFiles(buildName, buildNumber, projectKey string) (buildinfo.Partials, error) {
   181  	var partials buildinfo.Partials
   182  	partialsBuildDir, err := getPartialsBuildDir(buildName, buildNumber, projectKey)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	buildFiles, err := fileutils.ListFiles(partialsBuildDir, false)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  	for _, buildFile := range buildFiles {
   191  		dir, err := fileutils.IsDirExists(buildFile, false)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  		if dir {
   196  			continue
   197  		}
   198  		if strings.HasSuffix(buildFile, BuildInfoDetails) {
   199  			continue
   200  		}
   201  		content, err := fileutils.ReadFile(buildFile)
   202  		if err != nil {
   203  			return nil, err
   204  		}
   205  		partial := new(buildinfo.Partial)
   206  		json.Unmarshal(content, &partial)
   207  		partials = append(partials, partial)
   208  	}
   209  
   210  	return partials, nil
   211  }
   212  
   213  func ReadBuildInfoGeneralDetails(buildName, buildNumber, projectKey string) (*buildinfo.General, error) {
   214  	partialsBuildDir, err := getPartialsBuildDir(buildName, buildNumber, projectKey)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  	generalDetailsFilePath := filepath.Join(partialsBuildDir, BuildInfoDetails)
   219  	content, err := fileutils.ReadFile(generalDetailsFilePath)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	details := new(buildinfo.General)
   224  	json.Unmarshal(content, &details)
   225  	return details, nil
   226  }
   227  
   228  func RemoveBuildDir(buildName, buildNumber, projectKey string) error {
   229  	tempDirPath, err := GetBuildDir(buildName, buildNumber, projectKey)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	exists, err := fileutils.IsDirExists(tempDirPath, false)
   234  	if err != nil {
   235  		return err
   236  	}
   237  	if exists {
   238  		return errorutils.CheckError(os.RemoveAll(tempDirPath))
   239  	}
   240  	return nil
   241  }
   242  
   243  type BuildInfoConfiguration struct {
   244  	serverDetails auth.ServiceDetails
   245  	DryRun        bool
   246  	EnvInclude    string
   247  	EnvExclude    string
   248  }
   249  
   250  func (config *BuildInfoConfiguration) GetServerDetails() auth.ServiceDetails {
   251  	return config.serverDetails
   252  }
   253  
   254  func (config *BuildInfoConfiguration) SetServerDetails(art auth.ServiceDetails) {
   255  	config.serverDetails = art
   256  }
   257  
   258  func (config *BuildInfoConfiguration) IsDryRun() bool {
   259  	return config.DryRun
   260  }
   261  
   262  type BuildConfiguration struct {
   263  	BuildName   string
   264  	BuildNumber string
   265  	Module      string
   266  	Project     string
   267  }
   268  
   269  func ValidateBuildAndModuleParams(buildConfig *BuildConfiguration) error {
   270  	if (buildConfig.BuildName == "" && buildConfig.BuildNumber != "") || (buildConfig.BuildName != "" && buildConfig.BuildNumber == "") {
   271  		return errors.New("the build-name and build-number options cannot be provided separately")
   272  	}
   273  	if buildConfig.Module != "" && buildConfig.BuildName == "" && buildConfig.BuildNumber == "" {
   274  		return errors.New("the build-name and build-number options are mandatory when the module option is provided")
   275  	}
   276  	return nil
   277  }