1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00
sap-jenkins-library/cmd/mtaBuild.go
Kevin Hudemann ceb3dd0a04
Refactor pkg/npm and npmExecuteScripts (#1684)
This change refactors the npm pkg and npmExecuteScripts implementations
to be reusable for future steps, e.g., npmExecuteLint.

In addition, it fixes few small bugs related to unit test execution on
Windows and the fileUtils mocking implementation.

Co-authored-by: Daniel Kurzynski <daniel.kurzynski@sap.com>
Co-authored-by: Stephan Aßmus <stephan.assmus@sap.com>
2020-06-18 17:30:17 +02:00

406 lines
9.2 KiB
Go

package cmd
import (
"bytes"
"encoding/json"
"fmt"
"github.com/SAP/jenkins-library/pkg/npm"
"os"
"path"
"strings"
"text/template"
"time"
"github.com/SAP/jenkins-library/pkg/command"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/maven"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/ghodss/yaml"
)
const templateMtaYml = `_schema-version: "3.1"
ID: "{{.ID}}"
version: {{.Version}}
parameters:
hcp-deployer-version: "1.1.0"
modules:
- name: {{.ApplicationName}}
type: html5
path: .
parameters:
version: {{.Version}}-${timestamp}
name: {{.ApplicationName}}
build-parameters:
builder: grunt
build-result: dist`
// for mocking
var getSettingsFile = maven.GetSettingsFile
// MTABuildTarget ...
type MTABuildTarget int
const (
// NEO ...
NEO MTABuildTarget = iota
// CF ...
CF MTABuildTarget = iota
//XSA ...
XSA MTABuildTarget = iota
)
// ValueOfBuildTarget ...
func ValueOfBuildTarget(str string) (MTABuildTarget, error) {
switch str {
case "NEO":
return NEO, nil
case "CF":
return CF, nil
case "XSA":
return XSA, nil
default:
return -1, fmt.Errorf("Unknown BuildTarget/Platform: '%s'", str)
}
}
// String ...
func (m MTABuildTarget) String() string {
return [...]string{
"NEO",
"CF",
"XSA",
}[m]
}
func mtaBuild(config mtaBuildOptions,
telemetryData *telemetry.CustomData,
commonPipelineEnvironment *mtaBuildCommonPipelineEnvironment) {
log.Entry().Debugf("Launching mta build")
files := piperutils.Files{}
httpClient := piperhttp.Client{}
e := command.Command{}
npmExecutorOptions := npm.ExecutorOptions{DefaultNpmRegistry: config.DefaultNpmRegistry, SapNpmRegistry: config.SapNpmRegistry, ExecRunner: &e}
npmExecutor := npm.NewExecutor(npmExecutorOptions)
err := runMtaBuild(config, commonPipelineEnvironment, &e, &files, &httpClient, npmExecutor)
if err != nil {
log.Entry().
WithError(err).
Fatal("failed to execute mta build")
}
}
func runMtaBuild(config mtaBuildOptions,
commonPipelineEnvironment *mtaBuildCommonPipelineEnvironment,
e execRunner,
p piperutils.FileUtils,
httpClient piperhttp.Downloader,
npmExecutor npm.Executor) error {
e.Stdout(log.Writer()) // not sure if using the logging framework here is a suitable approach. We handover already log formatted
e.Stderr(log.Writer()) // entries to a logging framework again. But this is considered to be some kind of project standard.
var err error
err = handleSettingsFiles(config, p, httpClient)
if err != nil {
return err
}
err = npmExecutor.SetNpmRegistries()
mtaYamlFile := "mta.yaml"
mtaYamlFileExists, err := p.FileExists(mtaYamlFile)
if err != nil {
return err
}
if !mtaYamlFileExists {
if err = createMtaYamlFile(mtaYamlFile, config.ApplicationName, p); err != nil {
return err
}
} else {
log.Entry().Infof("\"%s\" file found in project sources", mtaYamlFile)
}
if err = setTimeStamp(mtaYamlFile, p); err != nil {
return err
}
mtarName, err := getMtarName(config, mtaYamlFile, p)
if err != nil {
return err
}
var call []string
switch config.MtaBuildTool {
case "classic":
mtaJar := getMarJarName(config)
buildTarget, err := ValueOfBuildTarget(config.BuildTarget)
if err != nil {
return err
}
call = append(call, "java", "-jar", mtaJar, "--mtar", mtarName, fmt.Sprintf("--build-target=%s", buildTarget), "build")
if len(config.Extensions) != 0 {
call = append(call, fmt.Sprintf("--extension=%s", config.Extensions))
}
case "cloudMbt":
platform, err := ValueOfBuildTarget(config.Platform)
if err != nil {
return err
}
call = append(call, "mbt", "build", "--mtar", mtarName, "--platform", platform.String())
if len(config.Extensions) != 0 {
call = append(call, fmt.Sprintf("--extensions=%s", config.Extensions))
}
call = append(call, "--target", "./")
default:
return fmt.Errorf("Unknown mta build tool: \"%s\"", config.MtaBuildTool)
}
if err = addNpmBinToPath(e); err != nil {
return err
}
log.Entry().Infof("Executing mta build call: \"%s\"", strings.Join(call, " "))
if err := e.RunExecutable(call[0], call[1:]...); err != nil {
return err
}
commonPipelineEnvironment.mtarFilePath = mtarName
err = installMavenArtifacts(e, config)
return err
}
func installMavenArtifacts(e execRunner, config mtaBuildOptions) error {
pomXMLExists, err := piperutils.FileExists("pom.xml")
if err != nil {
return err
}
if pomXMLExists {
err = maven.InstallMavenArtifacts(e, maven.EvaluateOptions{M2Path: config.M2Path})
if err != nil {
return err
}
}
return nil
}
func getMarJarName(config mtaBuildOptions) string {
mtaJar := "mta.jar"
if len(config.MtaJarLocation) > 0 {
mtaJar = config.MtaJarLocation
}
return mtaJar
}
func addNpmBinToPath(e execRunner) error {
dir, _ := os.Getwd()
newPath := path.Join(dir, "node_modules", ".bin")
oldPath := os.Getenv("PATH")
if len(oldPath) > 0 {
newPath = newPath + ":" + oldPath
}
e.SetEnv([]string{"PATH=" + newPath})
return nil
}
func getMtarName(config mtaBuildOptions, mtaYamlFile string, p piperutils.FileUtils) (string, error) {
mtarName := config.MtarName
if len(mtarName) == 0 {
log.Entry().Debugf("mtar name not provided via config. Extracting from file \"%s\"", mtaYamlFile)
mtaID, err := getMtaID(mtaYamlFile, p)
if err != nil {
return "", err
}
if len(mtaID) == 0 {
return "", fmt.Errorf("Invalid mtar ID. Was empty")
}
log.Entry().Debugf("mtar name extracted from file \"%s\": \"%s\"", mtaYamlFile, mtaID)
mtarName = mtaID + ".mtar"
}
return mtarName, nil
}
func setTimeStamp(mtaYamlFile string, p piperutils.FileUtils) error {
mtaYaml, err := p.FileRead(mtaYamlFile)
if err != nil {
return err
}
mtaYamlStr := string(mtaYaml)
timestampVar := "${timestamp}"
if strings.Contains(mtaYamlStr, timestampVar) {
if err := p.FileWrite(mtaYamlFile, []byte(strings.ReplaceAll(mtaYamlStr, timestampVar, getTimestamp())), 0644); err != nil {
return err
}
log.Entry().Infof("Timestamp replaced in \"%s\"", mtaYamlFile)
} else {
log.Entry().Infof("No timestamp contained in \"%s\". File has not been modified.", mtaYamlFile)
}
return nil
}
func getTimestamp() string {
t := time.Now()
return fmt.Sprintf("%d%02d%02d%02d%02d%02d\n", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
}
func createMtaYamlFile(mtaYamlFile, applicationName string, p piperutils.FileUtils) error {
log.Entry().Debugf("mta yaml file not found in project sources.")
if len(applicationName) == 0 {
return fmt.Errorf("'%[1]s' not found in project sources and 'applicationName' not provided as parameter - cannot generate '%[1]s' file", mtaYamlFile)
}
packageFileExists, err := p.FileExists("package.json")
if !packageFileExists {
return fmt.Errorf("package.json file does not exist")
}
var result map[string]interface{}
pContent, err := p.FileRead("package.json")
if err != nil {
return err
}
json.Unmarshal(pContent, &result)
version, ok := result["version"].(string)
if !ok {
return fmt.Errorf("Version not found in \"package.json\" (or wrong type)")
}
name, ok := result["name"].(string)
if !ok {
return fmt.Errorf("Name not found in \"package.json\" (or wrong type)")
}
mtaConfig, err := generateMta(name, applicationName, version)
if err != nil {
return err
}
p.FileWrite(mtaYamlFile, []byte(mtaConfig), 0644)
log.Entry().Infof("\"%s\" created.", mtaYamlFile)
return nil
}
func handleSettingsFiles(config mtaBuildOptions,
p piperutils.FileUtils,
httpClient piperhttp.Downloader) error {
if len(config.ProjectSettingsFile) > 0 {
if err := getSettingsFile(maven.ProjectSettingsFile, config.ProjectSettingsFile, p, httpClient); err != nil {
return err
}
} else {
log.Entry().Debugf("Project settings file not provided via configuration.")
}
if len(config.GlobalSettingsFile) > 0 {
if err := getSettingsFile(maven.GlobalSettingsFile, config.GlobalSettingsFile, p, httpClient); err != nil {
return err
}
} else {
log.Entry().Debugf("Global settings file not provided via configuration.")
}
return nil
}
func generateMta(id, applicationName, version string) (string, error) {
if len(id) == 0 {
return "", fmt.Errorf("Generating mta file: ID not provided")
}
if len(applicationName) == 0 {
return "", fmt.Errorf("Generating mta file: ApplicationName not provided")
}
if len(version) == 0 {
return "", fmt.Errorf("Generating mta file: Version not provided")
}
tmpl, e := template.New("mta.yaml").Parse(templateMtaYml)
if e != nil {
return "", e
}
type properties struct {
ID string
ApplicationName string
Version string
}
props := properties{ID: id, ApplicationName: applicationName, Version: version}
var script bytes.Buffer
tmpl.Execute(&script, props)
return script.String(), nil
}
func getMtaID(mtaYamlFile string, fileUtils piperutils.FileUtils) (string, error) {
var result map[string]interface{}
p, err := fileUtils.FileRead(mtaYamlFile)
if err != nil {
return "", err
}
err = yaml.Unmarshal(p, &result)
if err != nil {
return "", err
}
id, ok := result["ID"].(string)
if !ok || len(id) == 0 {
fmt.Errorf("Id not found in mta yaml file (or wrong type)")
}
return id, nil
}