Docker: Add dockerfile for Linux build environment.
- Dockerfile with getDependencies.sh
- Pipeline stages for Docker lint, build and promote
diff --git a/Jenkinsfile b/Jenkinsfile
index c0890b5..87cf291 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,48 +1,38 @@
 @Library("cmsis")
 
 DOCKERINFO = [
-    'linux_staging': [
+    'staging': [
         'registryUrl': 'mcu--docker-staging.eu-west-1.artifactory.aws.arm.com',
         'registryCredentialsId': 'artifactory',
         'k8sPullSecret': 'artifactory-mcu-docker-staging',
         'namespace': 'mcu--docker-staging',
-        'image': 'cmsis_fusa/linux',
+        'image': 'cmsis/linux',
         'label': "${JENKINS_ENV}-${JOB_BASE_NAME}-${BUILD_NUMBER}"
     ],
-    'linux_production': [
+    'production': [
         'registryUrl': 'mcu--docker.eu-west-1.artifactory.aws.arm.com',
         'registryCredentialsId': 'artifactory',
         'namespace': 'mcu--docker',
         'k8sPullSecret': 'artifactory-mcu-docker',
-        'image': 'cmsis_fusa/linux',
-        'label': 'aws'
-    ],
-    'windows_staging': [
-        'registryUrl': 'mcu--docker-staging.eu-west-1.artifactory.aws.arm.com',
-        'registryCredentialsId': 'artifactory',
-        'namespace': 'mcu--docker-staging',
-        'image': 'cmsis_fusa/windows',
-        'label': "${JENKINS_ENV}-${JOB_BASE_NAME}-${BUILD_NUMBER}"
-    ],
-    'windows_production': [
-        'registryUrl': 'mcu--docker.eu-west-1.artifactory.aws.arm.com',
-        'registryCredentialsId': 'artifactory',
-        'namespace': 'mcu--docker',
-        'image': 'cmsis_fusa/windows',
-        'label': 'aws'
+        'image': 'cmsis/linux',
+        'label': 'latest'
     ]
 ]
 
-dockerinfo_linux = DOCKERINFO['linux_production']
-dockerinfo_windows = DOCKERINFO['windows_production']
+dockerinfo = DOCKERINFO['production']
 
 isPrecommit = (JOB_BASE_NAME == 'pre_commit')
+isPostcommit = (JOB_BASE_NAME == 'post_commit')
 isNightly = (JOB_BASE_NAME == 'nightly')
 
 patternGlobal = [
     '^Jenkinsfile'
 ]
 
+patternDocker = [
+    '^docker/.*'
+]
+
 patternCoreM = [
     '^CMSIS/Core/Include/.*',
     '^Device/ARM/ARMCM.*'
@@ -63,8 +53,22 @@
         'adevices': ['CA7', 'CA9neon'],
         'devices' : [],
         'configs' : [
+            'AC5': ['low', 'tiny'],
             'AC6': ['low', 'tiny'],
-            'AC6LTM': ['low', 'tiny']
+            'AC6LTM': ['low', 'tiny'],
+            'GCC': ['low', 'tiny']
+        ]
+    ],
+    'post_commit': [
+        'devices' : ['CM0', 'CM0plus', 'CM3', 'CM4', 'CM4FP', 'CM7', 'CM7SP', 'CM7DP',
+             'CM23', 'CM23S', 'CM23NS', 'CM33', 'CM33S', 'CM33NS',
+             'CM35P', 'CM35PS', 'CM35PNS',
+             'CA5', 'CA5neon', 'CA7', 'CA7neon', 'CA9', 'CA9neon'],
+        'configs' : [
+            'AC5': ['low', 'tiny'],
+            'AC6': ['low', 'tiny'],
+            'AC6LTM': ['low', 'tiny'],
+            'GCC': ['low', 'tiny']
         ]
     ],
     'nightly':[
@@ -73,8 +77,10 @@
                      'CM35P', 'CM35PS', 'CM35PNS',
                      'CA5', 'CA5neon', 'CA7', 'CA7neon', 'CA9', 'CA9neon'],
         'configs' : [
+            'AC5': ['low', 'mid', 'high', 'size', 'tiny'],
             'AC6': ['low', 'mid', 'high', 'size', 'tiny'],
-            'AC6LTM': ['low', 'mid', 'high', 'size', 'tiny']
+            'AC6LTM': ['low', 'mid', 'high', 'size', 'tiny'],
+            'GCC': ['low', 'mid', 'high', 'size', 'tiny']
         ]
     ]
 ]
@@ -94,18 +100,26 @@
 }
 
 FORCE_BUILD = false
+DOCKER_BUILD = true
 CORE_VALIDATION = true
 COMMIT = null
 VERSION = null
 
 pipeline {
+    agent { label 'master' }
     options {
         timestamps()
         timeout(time: 1, unit: 'HOURS')
         ansiColor('xterm')
         skipDefaultCheckout()
     }
-    agent { label 'master' }
+    environment {
+        CI_ACCOUNT          = credentials('grasci')
+        ARTIFACTORY         = credentials('artifactory')
+        USER                = "${CI_ACCOUNT_USR}"
+        PASS                = "${CI_ACCOUNT_PSW}"
+        ARTIFACTORY_API_KEY = "${ARTIFACTORY_PSW}"
+    }
     stages {
         stage('Checkout') {
             steps {
@@ -115,45 +129,156 @@
                     VERSION = (sh(returnStdout: true, script: 'git describe --always')).trim()
                     echo "VERSION: '${VERSION}'"
                 }
+
+                dir('docker') {
+                    stash name: 'dockerfile', includes: '**'
+                }
             }
         }
 
         stage('Analyse') {
             when {
-                expression { return isPrecommit }
+                expression { return isPrecommit || isPostcommit }
                 beforeOptions true
             }
             steps {
                 script {
                     def fileset = changeset
                     def hasGlobal = fileSetMatches(fileset, patternGlobal)
+                    def hasDocker = fileSetMatches(fileset, patternDocker)
                     def hasCoreM = fileSetMatches(fileset, patternCoreM)
                     def hasCoreA = fileSetMatches(fileset, patternCoreA)
                     def hasCoreValidation = fileSetMatches(fileset, patternCoreValidation)
 
 echo """Change analysis:
 - hasGlobal = ${hasGlobal}
+- hasDocker = ${hasDocker}
 - hasCoreM = ${hasCoreM}
 - hasCoreA = ${hasCoreA}
 - hasCoreValidation = ${hasCoreValidation}
 """
 
-                    if (hasGlobal || hasCoreM || hasCoreValidation) {
-                        CONFIGURATION['devices'] += CONFIGURATION['mdevices']
-                    }
-                    if (hasGlobal || hasCoreA || hasCoreValidation) {
-                        CONFIGURATION['devices'] += CONFIGURATION['adevices']
+                    if (isPrecommit) {
+                        if (hasGlobal || hasDocker || hasCoreM || hasCoreValidation) {
+                            CONFIGURATION['devices'] += CONFIGURATION['mdevices']
+                        }
+                        if (hasGlobal || hasDocker || hasCoreA || hasCoreValidation) {
+                            CONFIGURATION['devices'] += CONFIGURATION['adevices']
+                        }
                     }
 
-                    CORE_VALIDATION &= hasGlobal || hasCoreM || hasCoreA || hasCoreValidation
-                    
+                    DOCKER_BUILD &= hasDocker
+                    CORE_VALIDATION &= hasGlobal || hasDocker || hasCoreM || hasCoreA || hasCoreValidation
+
 echo """Stage schedule:
+- DOCKER_BUILD = ${DOCKER_BUILD}
 - CORE_VALIDATION = ${CORE_VALIDATION}
 """
                 }
             }
         }
 
+        stage('Docker Lint') {
+            when {
+                expression { return DOCKER_BUILD }
+                beforeOptions true
+            }
+            agent {
+                kubernetes {
+                    defaultContainer 'hadolint'
+                    slaveConnectTimeout 600
+                    yaml """\
+                        apiVersion: v1
+                        kind: Pod
+                        securityContext:
+                          runAsUser: 1000
+                          runAsGroup: 1000
+                        spec:
+                          imagePullSecrets:
+                            - name: artifactory-mcu-docker
+                          securityContext:
+                            runAsUser: 1000
+                            runAsGroup: 1000
+                          containers:
+                            - name: hadolint
+                              image: mcu--docker.eu-west-1.artifactory.aws.arm.com/hadolint/hadolint:v1.19.0-alpine
+                              alwaysPullImage: true
+                              imagePullPolicy: Always
+                              command:
+                                - sleep
+                              args:
+                                - infinity
+                              resources:
+                                requests:
+                                  cpu: 2
+                                  memory: 2Gi
+                        """.stripIndent()
+                }
+            }
+            steps {
+                dir('docker') {
+                    unstash 'dockerfile'
+
+                    sh 'hadolint --format json dockerfile | tee hadolint.log'
+
+                    recordIssues tools: [hadoLint(id: 'hadolint', pattern: 'hadolint.log')],
+                                 qualityGates: [[threshold: 1, type: 'DELTA', unstable: true]],
+                                 referenceJobName: 'nightly', ignoreQualityGate: true
+                }
+            }
+        }
+
+        stage('Docker Build') {
+            when {
+                expression { return (isPrecommit || isPostcommit) && DOCKER_BUILD }
+                beforeOptions true
+            }
+            agent {
+                kubernetes {
+                    defaultContainer 'docker-dind'
+                    slaveConnectTimeout 600
+                    yaml """\
+                        apiVersion: v1
+                        kind: Pod
+                        spec:
+                          imagePullSecrets:
+                            - name: artifactory-mcu-docker
+                          containers:
+                            - name: docker-dind
+                              image: docker:dind
+                              securityContext:
+                                privileged: true
+                              volumeMounts:
+                                - name: dind-storage
+                                  mountPath: /var/lib/docker
+                          volumes:
+                            - name: dind-storage
+                              emptyDir: {}
+                        """.stripIndent()
+                }
+            }
+            steps {
+                sh('apk add bash curl git')
+                script {
+                    dir('docker') {
+                        unstash 'dockerfile'
+                    
+                        dockerinfo = DOCKERINFO['staging']
+                        withCredentials([sshUserPrivateKey(credentialsId: 'grasci_with_pk',
+                                keyFileVariable: 'grasciPk',
+                                passphraseVariable: '',
+                                usernameVariable: 'grasciUsername')]) {
+                            sh("GIT_SSH_COMMAND='ssh -i $grasciPk -o StrictHostKeyChecking=no' ./getDependencies.sh")
+                        }
+                        docker.withRegistry("https://${dockerinfo['registryUrl']}", dockerinfo['registryCredentialsId']) {
+                            def image = docker.build("${dockerinfo['registryUrl']}/${dockerinfo['image']}:${dockerinfo['label']}", "--build-arg DOCKER_REGISTRY=${dockerinfo['registryUrl']} .")
+                            image.push()
+                        }
+                    }
+                }
+            }
+        }
+
         stage('CoreValidation') {
             when {
                 expression { return CORE_VALIDATION }
@@ -184,13 +309,13 @@
                                     kind: Pod
                                     spec:
                                       imagePullSecrets:
-                                        - name: ${dockerinfo_linux['k8sPullSecret']}
+                                        - name: ${dockerinfo['k8sPullSecret']}
                                       securityContext:
                                         runAsUser: 1000
                                         runAsGroup: 1000
                                       containers:
                                         - name: cmsis
-                                          image: ${dockerinfo_linux['registryUrl']}/${dockerinfo_linux['image']}:${dockerinfo_linux['label']}
+                                          image: ${dockerinfo['registryUrl']}/${dockerinfo['image']}:${dockerinfo['label']}
                                           alwaysPullImage: true
                                           imagePullPolicy: Always
                                           command:
@@ -243,5 +368,54 @@
 
             }
         }
+
+        stage('Docker Promote') {
+            when {
+                expression { return isPostcommit && DOCKER_BUILD }
+                beforeOptions true
+            }
+            agent {
+                kubernetes {
+                    defaultContainer 'docker-dind'
+                    slaveConnectTimeout 600
+                    yaml """\
+                        apiVersion: v1
+                        kind: Pod
+                        spec:
+                          imagePullSecrets:
+                            - name: artifactory-mcu-docker
+                          containers:
+                            - name: docker-dind
+                              image: docker:dind
+                              securityContext:
+                                privileged: true
+                              volumeMounts:
+                                - name: dind-storage
+                                  mountPath: /var/lib/docker
+                          volumes:
+                            - name: dind-storage
+                              emptyDir: {}
+                        """.stripIndent()
+                }
+            }
+            steps {
+                script {
+                    String postCommitTag = "${dockerinfo['registryUrl']}/${dockerinfo['image']}:${dockerinfo['label']}"
+                    String prodCommitTag = "${DOCKERINFO['production']['registryUrl']}/${DOCKERINFO['production']['image']}:${DOCKERINFO['production']['label']}"
+
+                    // Pull & retag Docker Staging Container to Production
+                    docker.withRegistry("https://${dockerinfo['registryUrl']}", dockerinfo['registryCredentialsId']) {
+                        def image = docker.image("$postCommitTag")
+                        image.pull()
+                        sh "docker tag $postCommitTag $prodCommitTag"
+                    }
+                    // Push to Docker Production
+                    docker.withRegistry("https://${DOCKERINFO['production']['registryUrl']}", DOCKERINFO['production']['registryCredentialsId']) {
+                        def image = docker.image("$prodCommitTag")
+                        image.push()
+                    }
+                }
+            }
+        }
     }
 }