mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-01-30 05:59:39 +02:00
Add new step for Dockerfile linting (#723)
* Add new step for Dockerfile linting * Add documentation template file * Remove newlines * Remove internal URL * Rephrase comment * Ammend stash * Fix test * move dockerImage to general * use explicit curl options * small changes * small changes * skip GIT blame * First comments * Also add remark to URL parameter * Second set of comments * Fix return code handling * Switch type to set * Revert unrelated changes * Avoid modification of config * add quality gate defaults * Update hadolintExecute.groovy * fix code climate issue
This commit is contained in:
parent
f171f88b13
commit
111080cbfe
17
documentation/docs/steps/hadolintExecute.md
Normal file
17
documentation/docs/steps/hadolintExecute.md
Normal file
@ -0,0 +1,17 @@
|
||||
# ${docGenStepName}
|
||||
|
||||
## ${docGenDescription}
|
||||
|
||||
## ${docGenParameters}
|
||||
|
||||
## ${docGenConfiguration}
|
||||
|
||||
## Exceptions
|
||||
|
||||
None
|
||||
|
||||
## Examples
|
||||
|
||||
```groovy
|
||||
hadolintExecute script: this
|
||||
```
|
@ -251,6 +251,19 @@ steps:
|
||||
languageRunner: 'ruby'
|
||||
runCommand: 'bundle install && bundle exec gauge run'
|
||||
testOptions: 'specs'
|
||||
hadolintExecute:
|
||||
configurationFile: '.hadolint.yaml'
|
||||
configurationUrl: ''
|
||||
dockerFile: './Dockerfile'
|
||||
dockerImage: 'hadolint/hadolint:latest-debian'
|
||||
qualityGates:
|
||||
- threshold: 1
|
||||
type: 'TOTAL_ERROR'
|
||||
unstable: false
|
||||
reportFile: 'hadolint.xml'
|
||||
reportName: 'HaDoLint'
|
||||
stashContent:
|
||||
- 'buildDescriptor'
|
||||
handlePipelineStepErrors:
|
||||
echoDetails: true
|
||||
failOnError: true
|
||||
@ -408,7 +421,7 @@ steps:
|
||||
noDefaultExludes: []
|
||||
pipelineStashFilesBeforeBuild:
|
||||
stashIncludes:
|
||||
buildDescriptor: '**/pom.xml, **/.mvn/**, **/assembly.xml, **/.swagger-codegen-ignore, **/package.json, **/requirements.txt, **/setup.py, **/mta*.y*ml, **/.npmrc, Dockerfile, **/VERSION, **/version.txt, **/Gopkg.*, **/build.sbt, **/sbtDescriptor.json, **/project/*'
|
||||
buildDescriptor: '**/pom.xml, **/.mvn/**, **/assembly.xml, **/.swagger-codegen-ignore, **/package.json, **/requirements.txt, **/setup.py, **/mta*.y*ml, **/.npmrc, Dockerfile, .hadolint.yaml, **/VERSION, **/version.txt, **/Gopkg.*, **/build.sbt, **/sbtDescriptor.json, **/project/*'
|
||||
deployDescriptor: '**/manifest*.y*ml, **/*.mtaext.y*ml, **/*.mtaext, **/xs-app.json, helm/**, *.y*ml'
|
||||
git: '.git/**'
|
||||
opa5: '**/*.*'
|
||||
|
67
test/groovy/HadolintExecuteTest.groovy
Normal file
67
test/groovy/HadolintExecuteTest.groovy
Normal file
@ -0,0 +1,67 @@
|
||||
import hudson.AbortException
|
||||
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.ExpectedException
|
||||
import org.junit.rules.RuleChain
|
||||
import util.BasePiperTest
|
||||
import util.JenkinsDockerExecuteRule
|
||||
import util.JenkinsLoggingRule
|
||||
import util.JenkinsReadYamlRule
|
||||
import util.JenkinsShellCallRule
|
||||
import util.JenkinsStepRule
|
||||
import util.Rules
|
||||
|
||||
import static org.junit.Assert.assertThat
|
||||
import static org.hamcrest.Matchers.*
|
||||
|
||||
class HadolintExecuteTest extends BasePiperTest {
|
||||
|
||||
private ExpectedException thrown = new ExpectedException().none()
|
||||
private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this)
|
||||
private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this)
|
||||
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
|
||||
private JenkinsReadYamlRule yamlRule = new JenkinsReadYamlRule(this)
|
||||
private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this)
|
||||
|
||||
@Rule
|
||||
public RuleChain ruleChain = Rules
|
||||
.getCommonRules(this)
|
||||
.around(thrown)
|
||||
.around(yamlRule)
|
||||
.around(dockerExecuteRule)
|
||||
.around(shellRule)
|
||||
.around(stepRule)
|
||||
.around(loggingRule)
|
||||
|
||||
@Before
|
||||
void init() {
|
||||
helper.registerAllowedMethod 'stash', [String, String], { name, includes -> assertThat(name, is('hadolintConfiguration')); assertThat(includes, is('.hadolint.yaml')) }
|
||||
helper.registerAllowedMethod 'fileExists', [String], { s -> s == './Dockerfile' }
|
||||
helper.registerAllowedMethod 'checkStyle', [Map], { m -> assertThat(m.pattern, is('hadolint.xml')); return 'checkstyle' }
|
||||
helper.registerAllowedMethod 'recordIssues', [Map], { m -> assertThat(m.tools, hasItem('checkstyle')) }
|
||||
helper.registerAllowedMethod 'archiveArtifacts', [String], { String p -> assertThat('hadolint.xml', is(p)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHadolintExecute() {
|
||||
stepRule.step.hadolintExecute(script: nullScript, juStabUtils: utils, dockerImage: 'hadolint/hadolint:latest-debian', configurationUrl: 'https://github.wdf.sap.corp/raw/SGS/Hadolint-Dockerfile/master/.hadolint.yaml')
|
||||
assertThat(dockerExecuteRule.dockerParams.dockerImage, is('hadolint/hadolint:latest-debian'))
|
||||
assertThat(loggingRule.log, containsString("Unstash content: buildDescriptor"))
|
||||
assertThat(shellRule.shell,
|
||||
hasItems(
|
||||
"curl --fail --location --output .hadolint.yaml https://github.wdf.sap.corp/raw/SGS/Hadolint-Dockerfile/master/.hadolint.yaml",
|
||||
"hadolint ./Dockerfile --config .hadolint.yaml --format checkstyle > hadolint.xml"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoDockerfile() {
|
||||
helper.registerAllowedMethod 'fileExists', [String], { false }
|
||||
thrown.expect AbortException
|
||||
thrown.expectMessage '[hadolintExecute] Dockerfile \'./Dockerfile\' is not found.'
|
||||
stepRule.step.hadolintExecute(script: nullScript, juStabUtils: utils, dockerImage: 'hadolint/hadolint:latest-debian')
|
||||
}
|
||||
}
|
104
vars/hadolintExecute.groovy
Normal file
104
vars/hadolintExecute.groovy
Normal file
@ -0,0 +1,104 @@
|
||||
import static com.sap.piper.Prerequisites.checkScript
|
||||
import com.sap.piper.GenerateDocumentation
|
||||
import com.sap.piper.ConfigurationHelper
|
||||
import com.sap.piper.Utils
|
||||
import groovy.transform.Field
|
||||
|
||||
@Field def STEP_NAME = getClass().getName()
|
||||
@Field Set GENERAL_CONFIG_KEYS = [
|
||||
/**
|
||||
* Dockerfile to be used for the assessment.
|
||||
*/
|
||||
'dockerFile',
|
||||
/**
|
||||
* Name of the docker image that should be used, in which node should be installed and configured. Default value is 'hadolint/hadolint:latest-debian'.
|
||||
*/
|
||||
'dockerImage'
|
||||
]
|
||||
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
|
||||
/**
|
||||
* Name of the configuration file used locally within the step. If a file with this name is detected as part of your repo downloading the central configuration via `configurationUrl` will be skipped. If you change the file's name make sure your stashing configuration also reflects this.
|
||||
*/
|
||||
'configurationFile',
|
||||
/**
|
||||
* URL pointing to the .hadolint.yaml exclude configuration to be used for linting. Also have a look at `configurationFile` which could avoid central configuration download in case the file is part of your repository.
|
||||
*/
|
||||
'configurationUrl',
|
||||
/**
|
||||
* Docker options to be set when starting the container.
|
||||
*/
|
||||
'dockerOptions',
|
||||
/**
|
||||
* Quality Gates to fail the build, see [warnings-ng plugin documentation](https://github.com/jenkinsci/warnings-plugin/blob/master/doc/Documentation.md#quality-gate-configuration).
|
||||
*/
|
||||
'qualityGates',
|
||||
/**
|
||||
* Name of the result file used locally within the step.
|
||||
*/
|
||||
'reportFile'
|
||||
])
|
||||
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
|
||||
/**
|
||||
* Executes the Haskell Dockerfile Linter which is a smarter Dockerfile linter that helps you build [best practice](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) Docker images.
|
||||
* The linter is parsing the Dockerfile into an abstract syntax tree (AST) and performs rules on top of the AST.
|
||||
*/
|
||||
@GenerateDocumentation
|
||||
void call(Map parameters = [:]) {
|
||||
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
|
||||
final script = checkScript(this, parameters) ?: this
|
||||
final utils = parameters.juStabUtils ?: new Utils()
|
||||
|
||||
// load default & individual configuration
|
||||
Map configuration = ConfigurationHelper.newInstance(this)
|
||||
.loadStepDefaults()
|
||||
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
|
||||
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
|
||||
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
|
||||
.mixin(parameters, PARAMETER_KEYS)
|
||||
.use()
|
||||
|
||||
new Utils().pushToSWA([
|
||||
step: STEP_NAME,
|
||||
stepParamKey1: 'scriptMissing',
|
||||
stepParam1: parameters?.script == null
|
||||
], configuration)
|
||||
|
||||
def existingStashes = utils.unstashAll(configuration.stashContent)
|
||||
|
||||
if (!fileExists(configuration.dockerFile)) {
|
||||
error "[${STEP_NAME}] Dockerfile '${configuration.dockerFile}' is not found."
|
||||
}
|
||||
|
||||
if(!fileExists(configuration.configurationFile) && configuration.configurationUrl) {
|
||||
sh "curl --fail --location --output ${configuration.configurationFile} ${configuration.configurationUrl}"
|
||||
if(existingStashes) {
|
||||
def stashName = 'hadolintConfiguration'
|
||||
stash name: stashName, includes: configuration.configurationFile
|
||||
existingStashes += stashName
|
||||
}
|
||||
}
|
||||
|
||||
def options = [
|
||||
"--config ${configuration.configurationFile}",
|
||||
"--format checkstyle > ${configuration.reportFile}"
|
||||
]
|
||||
|
||||
dockerExecute(
|
||||
script: script,
|
||||
dockerImage: configuration.dockerImage,
|
||||
dockerOptions: configuration.dockerOptions,
|
||||
stashContent: existingStashes
|
||||
) {
|
||||
// HaDoLint status code is ignore, results will be handled by recordIssues / archiveArtifacts
|
||||
def ignore = sh returnStatus: true, script: "hadolint ${configuration.dockerFile} ${options.join(' ')}"
|
||||
|
||||
archiveArtifacts configuration.reportFile
|
||||
recordIssues(
|
||||
tools: [checkStyle(name: configuration.reportName, pattern: configuration.reportFile)],
|
||||
qualityGates: configuration.qualityGates,
|
||||
enabledForFailure: true,
|
||||
blameDisabled: true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user