github.com/SAP/jenkins-library@v1.362.0/pkg/versioning/docker.go (about)

     1  package versioning
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  // Docker defines an artifact based on a Dockerfile
    13  type Docker struct {
    14  	artifact         Artifact
    15  	content          []byte
    16  	utils            Utils
    17  	options          *Options
    18  	path             string
    19  	versionSource    string
    20  	versioningScheme string
    21  	readFile         func(string) ([]byte, error)
    22  	writeFile        func(string, []byte, os.FileMode) error
    23  }
    24  
    25  func (d *Docker) init() {
    26  	if d.readFile == nil {
    27  		d.readFile = os.ReadFile
    28  	}
    29  
    30  	if d.writeFile == nil {
    31  		d.writeFile = os.WriteFile
    32  	}
    33  }
    34  
    35  func (d *Docker) initDockerfile() {
    36  	if len(d.path) == 0 {
    37  		d.path = "Dockerfile"
    38  	}
    39  }
    40  
    41  // VersioningScheme returns the relevant versioning scheme
    42  func (d *Docker) VersioningScheme() string {
    43  	if len(d.versioningScheme) == 0 {
    44  		return "docker"
    45  	}
    46  	return d.versioningScheme
    47  }
    48  
    49  // GetVersion returns the current version of the artifact
    50  func (d *Docker) GetVersion() (string, error) {
    51  	d.init()
    52  	var err error
    53  
    54  	switch d.versionSource {
    55  	case "FROM":
    56  		var err error
    57  		d.initDockerfile()
    58  		d.content, err = d.readFile(d.path)
    59  		if err != nil {
    60  			return "", errors.Wrapf(err, "failed to read file '%v'", d.path)
    61  		}
    62  		version := d.versionFromBaseImageTag()
    63  		if len(version) == 0 {
    64  			return "", fmt.Errorf("no version information available in FROM statement")
    65  		}
    66  		return version, nil
    67  	case "":
    68  		if len(d.path) == 0 {
    69  			d.path = "VERSION"
    70  		}
    71  		d.versionSource = "custom"
    72  		fallthrough
    73  	case "custom", "dub", "golang", "maven", "mta", "npm", "pip", "sbt":
    74  		if d.options == nil {
    75  			d.options = &Options{}
    76  		}
    77  		d.artifact, err = GetArtifact(d.versionSource, d.path, d.options, d.utils)
    78  		if err != nil {
    79  			return "", err
    80  		}
    81  		return d.artifact.GetVersion()
    82  	default:
    83  		d.initDockerfile()
    84  		d.content, err = d.readFile(d.path)
    85  		if err != nil {
    86  			return "", errors.Wrapf(err, "failed to read file '%v'", d.path)
    87  		}
    88  		version := d.versionFromEnv(d.versionSource)
    89  		if len(version) == 0 {
    90  			return "", fmt.Errorf("no version information available in ENV '%v'", d.versionSource)
    91  		}
    92  		return version, nil
    93  	}
    94  }
    95  
    96  // SetVersion updates the version of the artifact
    97  func (d *Docker) SetVersion(version string) error {
    98  	d.init()
    99  
   100  	dir := ""
   101  
   102  	if d.artifact != nil {
   103  		err := d.artifact.SetVersion(version)
   104  		if err != nil {
   105  			return err
   106  		}
   107  		dir = filepath.Dir(d.path)
   108  	}
   109  
   110  	err := d.writeFile(filepath.Join(dir, "VERSION"), []byte(version), 0700)
   111  	if err != nil {
   112  		return errors.Wrap(err, "failed to write file 'VERSION'")
   113  	}
   114  
   115  	return nil
   116  }
   117  
   118  func (d *Docker) versionFromEnv(env string) string {
   119  	lines := strings.Split(string(d.content), "\n")
   120  	for _, line := range lines {
   121  		if strings.HasPrefix(line, "ENV") && strings.Fields(line)[1] == env {
   122  			return strings.Fields(line)[2]
   123  		}
   124  	}
   125  	return ""
   126  }
   127  
   128  func (d *Docker) versionFromBaseImageTag() string {
   129  	lines := strings.Split(string(d.content), "\n")
   130  	for _, line := range lines {
   131  		if strings.HasPrefix(line, "FROM") {
   132  			imageParts := strings.Split(line, ":")
   133  			partsCount := len(imageParts)
   134  			if partsCount == 1 {
   135  				return ""
   136  			}
   137  			version := imageParts[partsCount-1]
   138  			return strings.TrimSpace(version)
   139  		}
   140  	}
   141  	return ""
   142  }
   143  
   144  // GetCoordinates returns the coordinates
   145  func (d *Docker) GetCoordinates() (Coordinates, error) {
   146  	result := Coordinates{}
   147  
   148  	result.GroupID = ""
   149  	result.ArtifactID, _ = d.GetArtifactID()
   150  
   151  	result.Version = ""
   152  	// cannot properly resolve version unless all options are provided. Can we ensure proper parameterization?
   153  	// result.Version, err = d.GetVersion()
   154  	// if err != nil {
   155  	//	return nil, err
   156  	// }
   157  
   158  	return result, nil
   159  }
   160  
   161  // GetArtifactID returns the current ID of the artifact
   162  func (d *Docker) GetArtifactID() (string, error) {
   163  	d.init()
   164  
   165  	artifactID := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(d.options.DockerImage, "/", "_"), ":", "_"), ".", "_")
   166  	return artifactID, nil
   167  }