diff --git a/documentation/docs/steps/mtaBuild.md b/documentation/docs/steps/mtaBuild.md index a97321d4c..c5c8f1833 100644 --- a/documentation/docs/steps/mtaBuild.md +++ b/documentation/docs/steps/mtaBuild.md @@ -14,16 +14,19 @@ Executes the SAP MTA Archive Builder to create an mtar archive of the applicatio | `script` | yes | | | | `buildTarget` | yes | `'NEO'` | 'CF', 'NEO', 'XSA' | | `mtaJarLocation` | no | | | +| `applicationName`| no | | | * `script` - The common script environment of the Jenkinsfile running. Typically the reference to the script calling the pipeline step is provided with the `this` parameter, as in `script: this`. This allows the function to access the [`commonPipelineEnvironment`](commonPipelineEnvironment.md) for retrieving, for example, configuration parameters. * `buildTarget` - The target platform to which the mtar can be deployed. * `mtaJarLocation` - The path of the `mta.jar` file. If no parameter is provided, neither at the level of the method call nor via step configuration, the path is retrieved from the Jenkins environment variables using `env.MTA_JAR_LOCATION`. If the Jenkins environment variable is not set it is assumed that `mta.jar` is located in the current working directory. +* `applicationName` - The name of the application which is being built. If the parameter has been provided and no `mta.yaml` exists, the `mta.yaml` will be automatically generated using this parameter and the information (`name` and `version`) from `package.json` before the actual build starts. ## Step configuration The following parameters can also be specified as step parameters using the global configuration file: * `buildTarget` * `mtaJarLocation` +* `applicationName` ## Return value The file name of the resulting archive is returned with this step. The file name is extracted from the key `ID` defined in `mta.yaml`. @@ -43,3 +46,4 @@ dir('/path/to/FioriApp'){ mtarFileName = mtaBuild script:this, buildTarget: 'NEO' } ``` + diff --git a/resources/template_mta.yml b/resources/template_mta.yml new file mode 100644 index 000000000..7d1c7dc22 --- /dev/null +++ b/resources/template_mta.yml @@ -0,0 +1,16 @@ +_schema-version: "2.0.0" +ID: "" +version: + +parameters: + hcp-deployer-version: "1.0.0" + +modules: + - name: "" + type: html5 + path: . + parameters: + version: -${timestamp} + build-parameters: + builder: grunt + build-result: dist diff --git a/src/com/sap/piper/MtaUtils.groovy b/src/com/sap/piper/MtaUtils.groovy new file mode 100644 index 000000000..ea308be18 --- /dev/null +++ b/src/com/sap/piper/MtaUtils.groovy @@ -0,0 +1,40 @@ +package com.sap.piper + +import java.util.Map + +import hudson.AbortException + +class MtaUtils { + + final protected script + + protected MtaUtils(script) { + this.script = script + } + + def generateMtaDescriptorFromPackageJson (String srcPackageJson, String targetMtaDescriptor, String applicationName) throws Exception{ + if (!srcPackageJson) throw new IllegalArgumentException("The parameter 'srcPackageJson' can not be null or empty.") + if (!targetMtaDescriptor) throw new IllegalArgumentException("The parameter 'targetMtaDescriptor' can not be null or empty.") + if (!applicationName) throw new IllegalArgumentException("The parameter 'applicationName' can not be null or empty.") + + if (!script.fileExists(srcPackageJson)) throw new AbortException("'${srcPackageJson}' does not exist.") + + def dataFromJson = script.readJSON file: srcPackageJson + + def mtaData = script.readYaml text: script.libraryResource('template_mta.yml') + + if(!dataFromJson.name) throw new AbortException("'name' not set in the given package.json.") + mtaData['ID'] = dataFromJson.name + + if(!dataFromJson.version) throw new AbortException("'version' not set in the given package.json.") + mtaData['version'] = dataFromJson.version + mtaData['modules'][0]['parameters']['version'] = "${dataFromJson.version}-\${timestamp}" + + mtaData['modules'][0]['name'] = applicationName + + script.writeYaml file: targetMtaDescriptor, data: mtaData + + if (!script.fileExists(targetMtaDescriptor)) throw new AbortException("'${targetMtaDescriptor}' has not been generated.") + } +} + diff --git a/test/groovy/MTABuildTest.groovy b/test/groovy/MTABuildTest.groovy index d585e6424..2016aecde 100644 --- a/test/groovy/MTABuildTest.groovy +++ b/test/groovy/MTABuildTest.groovy @@ -1,23 +1,21 @@ -import hudson.AbortException -import org.yaml.snakeyaml.Yaml -import org.yaml.snakeyaml.parser.ParserException - +import org.junit.Before import org.junit.BeforeClass import org.junit.ClassRule import org.junit.Ignore -import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.ExpectedException import org.junit.rules.RuleChain import org.junit.rules.TemporaryFolder +import org.yaml.snakeyaml.parser.ParserException import com.lesfurets.jenkins.unit.BasePipelineTest +import hudson.AbortException +import util.JenkinsEnvironmentRule import util.JenkinsLoggingRule import util.JenkinsShellCallRule import util.JenkinsStepRule -import util.JenkinsEnvironmentRule import util.Rules public class MtaBuildTest extends BasePipelineTest { @@ -62,6 +60,8 @@ public class MtaBuildTest extends BasePipelineTest { mtaYaml.text = defaultMtaYaml() helper.registerAllowedMethod('pwd', [], { currentDir } ) + + helper.registerAllowedMethod('fileExists', [GString.class], { false }) binding.setVariable('PATH', '/usr/bin') diff --git a/test/groovy/com/sap/piper/MtaUtilsTest.groovy b/test/groovy/com/sap/piper/MtaUtilsTest.groovy new file mode 100644 index 000000000..cc082f16e --- /dev/null +++ b/test/groovy/com/sap/piper/MtaUtilsTest.groovy @@ -0,0 +1,247 @@ +package com.sap.piper + +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException +import org.junit.rules.RuleChain +import org.junit.rules.TemporaryFolder +import org.yaml.snakeyaml.Yaml + +import com.lesfurets.jenkins.unit.BasePipelineTest + +import groovy.json.JsonSlurper +import hudson.AbortException +import util.JenkinsEnvironmentRule +import util.Rules + + +class MtaUtilsTest extends BasePipelineTest { + private static srcPackageJson = 'test/resources/MtaUtils/package.json' + private static mtaTemplate = 'resources/template_mta.yml' + private static data + private static targetMtaDescriptor + private File badJson + private mtaUtils + + @Rule + public JenkinsEnvironmentRule jer = new JenkinsEnvironmentRule(this) + + + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @ClassRule + public static TemporaryFolder tmp = new TemporaryFolder() + + @Rule + public RuleChain ruleChain = Rules + .getCommonRules(this) + .around(jer) + + void prepareObjectInterceptors(object) { + object.metaClass.invokeMethod = helper.getMethodInterceptor() + object.metaClass.static.invokeMethod = helper.getMethodInterceptor() + object.metaClass.methodMissing = helper.getMethodMissingInterceptor() + } + + @Before + void init() { + targetMtaDescriptor = "${tmp.getRoot()}/generated_mta.yml" + def script = new Object() + mtaUtils = new MtaUtils(script) + + prepareObjectInterceptors(script) + + this.helper.registerAllowedMethod('readJSON', [Map], { Map parameters -> + def js = new JsonSlurper() + def reader = new BufferedReader(new FileReader(parameters.file)) + def j = js.parse(reader) + return j + }) + + this.helper.registerAllowedMethod('libraryResource', [Map], { Map parameters -> + new Yaml().load(new File(mtaTemplate).text) + }) + + this.helper.registerAllowedMethod('readYaml', [], { + return new Yaml().load(new FileReader(mtaTemplate)) + }) + + this.helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> + def generatedFile = new File(parameters.file) + generatedFile.text = parameters.data + generatedFile.dump() + data = parameters.data + }) + + + this.helper.registerAllowedMethod('fileExists', [String.class], { true }) + } + + @Test + void testStraightForward(){ + mtaUtils.generateMtaDescriptorFromPackageJson(srcPackageJson, targetMtaDescriptor, 'testAppName') + assert data.ID == 'com.mycompany.northwind' + assert data.version == '1.0.3' + assert data.modules.name[0] == 'testAppName' + assert data.modules.parameters.version[0] == '1.0.3-${timestamp}' + } + + @Test + void testSrcPackageJsonEmpty() { + thrown.expect(IllegalArgumentException) + thrown.expectMessage("The parameter 'srcPackageJson' can not be null or empty.") + mtaUtils.generateMtaDescriptorFromPackageJson('', targetMtaDescriptor, 'testApplicationName') + } + + @Test + void testSrcPackageJsonNull() { + thrown.expect(IllegalArgumentException) + thrown.expectMessage("The parameter 'srcPackageJson' can not be null or empty.") + mtaUtils.generateMtaDescriptorFromPackageJson(null, targetMtaDescriptor, 'testApplicationName') + } + + @Test + void testTargetMtaDescriptorEmpty() { + thrown.expect(IllegalArgumentException) + thrown.expectMessage("The parameter 'targetMtaDescriptor' can not be null or empty.") + mtaUtils.generateMtaDescriptorFromPackageJson(srcPackageJson, '', 'testApplicationName') + } + + @Test + void testTargetMtaDescriptorNull() { + thrown.expect(IllegalArgumentException) + thrown.expectMessage("The parameter 'targetMtaDescriptor' can not be null or empty.") + mtaUtils.generateMtaDescriptorFromPackageJson(srcPackageJson, null, 'testApplicationName') + } + + @Test + void testApplicationNameEmpty() { + thrown.expect(IllegalArgumentException) + thrown.expectMessage("The parameter 'applicationName' can not be null or empty.") + mtaUtils.generateMtaDescriptorFromPackageJson(srcPackageJson, targetMtaDescriptor, '') + } + + @Test + void testApplicationNameNull() { + thrown.expect(IllegalArgumentException) + thrown.expectMessage("The parameter 'applicationName' can not be null or empty.") + mtaUtils.generateMtaDescriptorFromPackageJson(srcPackageJson, targetMtaDescriptor, null) + } + + @Test + void testMissingNameInJson() { + badJson = tmp.newFile('missingName.json') + badJson.text = missingNameInJson() + badJson.dump() + + thrown.expect(AbortException) + thrown.expectMessage("'name' not set in the given package.json.") + + mtaUtils.generateMtaDescriptorFromPackageJson(badJson.absolutePath, targetMtaDescriptor, 'testApplicationName') + } + + @Test + void testEmptyNameInJson() { + badJson = tmp.newFile('emptyName.json') + badJson.text = emptyNameInJson() + badJson.dump() + + thrown.expect(AbortException) + thrown.expectMessage("'name' not set in the given package.json.") + + mtaUtils.generateMtaDescriptorFromPackageJson(badJson.absolutePath, targetMtaDescriptor, 'testApplicationName') + } + + @Test + void testMissingVersionInJson() { + badJson = tmp.newFile('missingVersion.json') + badJson.text = missingVersionInJson() + badJson.dump() + + thrown.expect(AbortException) + thrown.expectMessage("'version' not set in the given package.json.") + + mtaUtils.generateMtaDescriptorFromPackageJson(badJson.absolutePath, targetMtaDescriptor, 'testApplicationName') + } + + @Test + void testEmptyVersionInJson() { + badJson = tmp.newFile('emptyVersion.json') + badJson.text = emptyVersionInJson() + badJson.dump() + + thrown.expect(AbortException) + thrown.expectMessage("'version' not set in the given package.json.") + + mtaUtils.generateMtaDescriptorFromPackageJson(badJson.absolutePath, targetMtaDescriptor, 'testApplicationName') + } + + @Test + void testFileGenerated() { + mtaUtils.generateMtaDescriptorFromPackageJson(srcPackageJson, targetMtaDescriptor, 'testApplicationName') + def file = new File(targetMtaDescriptor) + assert file.exists() + assert file.canRead() + } + + + private missingNameInJson() { + return ''' + { + "version": "1.0.3", + "description": "Webshop application for test purposes", + "private": true, + "devDependencies": { + "grunt": "1.0.1", + "@sap/grunt-sapui5-bestpractice-build": "^1.3.17" + } + } + ''' + } + + private emptyNameInJson() { + return ''' + { + "name": "", + "version": "1.0.3", + "description": "Webshop application for test purposes", + "private": true, + "devDependencies": { + "grunt": "1.0.1", + "@sap/grunt-sapui5-bestpractice-build": "^1.3.17" + } + } + ''' + } + private missingVersionInJson() { + return ''' + { + "name": "com.mycompany.northwind", + "description": "Webshop application for test purposes", + "private": true, + "devDependencies": { + "grunt": "1.0.1", + "@sap/grunt-sapui5-bestpractice-build": "^1.3.17" + } + } + ''' + } + + private emptyVersionInJson() { + return ''' + { + "name": "com.mycompany.northwind", + "version": "", + "description": "Webshop application for test purposes", + "private": true, + "devDependencies": { + "grunt": "1.0.1", + "@sap/grunt-sapui5-bestpractice-build": "^1.3.17" + } + } + ''' + } +} diff --git a/test/resources/MtaUtils/package.json b/test/resources/MtaUtils/package.json new file mode 100644 index 000000000..66126e3b2 --- /dev/null +++ b/test/resources/MtaUtils/package.json @@ -0,0 +1,10 @@ +{ + "name": "com.mycompany.northwind", + "version": "1.0.3", + "description": "Webshop application for test purposes", + "private": true, + "devDependencies": { + "grunt": "1.0.1", + "@sap/grunt-sapui5-bestpractice-build": "^1.3.17" + } +} diff --git a/vars/mtaBuild.groovy b/vars/mtaBuild.groovy index aa1bddd4a..21b656091 100644 --- a/vars/mtaBuild.groovy +++ b/vars/mtaBuild.groovy @@ -1,5 +1,5 @@ -import com.sap.piper.ConfigurationLoader import com.sap.piper.ConfigurationMerger +import com.sap.piper.MtaUtils import groovy.transform.Field @@ -11,12 +11,14 @@ def call(Map parameters = [:]) { Set parameterKeys = [ 'buildTarget', - 'mtaJarLocation' + 'mtaJarLocation', + 'applicationName' ] Set stepConfigurationKeys = [ 'buildTarget', - 'mtaJarLocation' + 'mtaJarLocation', + 'applicationName' ] handlePipelineStepErrors (stepName: stepName, stepParameters: parameters) { @@ -65,8 +67,16 @@ def call(Map parameters = [:]) { } } - def mtaYaml = readYaml file: "${pwd()}/mta.yaml" + def mtaYmlName = "${pwd()}/mta.yaml" + def applicationName = configuration.applicationName + if (!fileExists(mtaYmlName) && applicationName) { + MtaUtils mtaUtils = new MtaUtils(this) + mtaUtils.generateMtaDescriptorFromPackageJson("${pwd()}/package.json", mtaYmlName, applicationName) + } + + def mtaYaml = readYaml file: "${pwd()}/mta.yaml" + //[Q]: Why not yaml.dump()? [A]: This reformats the whole file. sh "sed -ie \"s/\\\${timestamp}/`date +%Y%m%d%H%M%S`/g\" \"${pwd()}/mta.yaml\"" @@ -109,5 +119,4 @@ private getMtaJar(stepName, configuration) { echo "[$stepName] Using MTA JAR from current working directory." return mtaJarLocation -} - +} \ No newline at end of file