You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by bn...@apache.org on 2022/08/25 19:02:20 UTC

[trafficserver-ci] branch main updated: add autest sharding to pr builds

This is an automated email from the ASF dual-hosted git repository.

bnolsen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/trafficserver-ci.git


The following commit(s) were added to refs/heads/main by this push:
     new d78e373  add autest sharding to pr builds
     new 2e218c3  Merge pull request #109 from traeak/pr_autest_shards
d78e373 is described below

commit d78e3736fb5ebfa3531b3de27817169a552167b5
Author: Brian Olsen <bn...@gmail.com>
AuthorDate: Wed Aug 24 14:19:12 2022 +0000

    add autest sharding to pr builds
---
 jenkins/github/autest.pipeline   | 255 ++++++++++++----------
 jenkins/github/toplevel.pipeline | 449 ++++++++++++++++++++-------------------
 2 files changed, 384 insertions(+), 320 deletions(-)

diff --git a/jenkins/github/autest.pipeline b/jenkins/github/autest.pipeline
index 9987b87..f5a5ee0 100644
--- a/jenkins/github/autest.pipeline
+++ b/jenkins/github/autest.pipeline
@@ -1,112 +1,153 @@
 pipeline {
-    agent {
-        docker {
-            image 'ci.trafficserver.apache.org/ats/rockylinux:8'
-            registryUrl 'https://ci.trafficserver.apache.org/'
-            args '--init --cap-add=SYS_PTRACE --network=host -v ${HOME}/ccache:/tmp/ccache:rw'
-            label 'docker'
-        }
-    }
-    environment {
-        CCACHE_DIR = "/tmp/ccache"
-        CCACHE_BASEDIR = "${WORKSPACE}"
-    }    
-    stages {
-        stage('Clone') {
-            steps {
-                dir('src') {
-                    echo "${sha1}"
-                    checkout([$class: 'GitSCM',
-                        branches: [[name: sha1]],
-                        extensions: [
-                            // We have to set an idenity for the merge step because Git requires
-                            // the user.name and user.email to be set to do a merge.
-                            [$class: "UserIdentity",
-                                name: "ATS CI User",
-                                email: "noreply@trafficserver.apache.org"
-                            ],
-                            [$class: "PreBuildMerge",
-                                options: [
-                                    mergeTarget: "${GITHUB_PR_TARGET_BRANCH}",
-                                    fastForwardMode: "NO_FF",
-                                    mergeRemote: "origin",
-                                    mergeStrategy: "DEFAULT"
-                                ]
-                            ],
-                        ],
-                        userRemoteConfigs: [[url: github_url, refspec: '+refs/pull/*:refs/remotes/origin/pr/*']]])
-                    sh 'git show -n 10 --decorate --graph --oneline --no-patch'
-                }
-                echo 'Finished Cloning'
-            }
-        }
-        stage('Build') {
-            steps {
-                echo 'Starting build'
-                dir('src') {
-                    // For Jenkins debugging. We comit the top of README in our debug PRs.
-                    sh('head README')
+	agent {
+		docker {
+			image 'ci.trafficserver.apache.org/ats/rockylinux:8'
+			registryUrl 'https://ci.trafficserver.apache.org/'
+			args '--init --cap-add=SYS_PTRACE --network=host -v ${HOME}/ccache:/tmp/ccache:rw'
+			label 'docker'
+		}
+	}
+	environment {
+		CCACHE_DIR = "/tmp/ccache"
+		CCACHE_BASEDIR = "${WORKSPACE}"
+	}    
+	stages {
+		stage('Initialization') {
+			steps {
+				script {
+					if (env.AUTEST_SHARD) {
+						String[] arr = env.AUTEST_SHARD.split('of')
+						if (2 == arr.length) {
+							shard = arr[0] as int
+							shardcnt = arr[1] as int
+							if (shard < shardcnt) {
+								env.SHARD = shard
+								env.SHARDCNT = shardcnt
+							}
+						}
+					}
 
-                    sh '''#!/bin/bash
-                    
-                       set -x
-                       set -e
-                       source /opt/rh/gcc-toolset-11/enable
-                       sudo update-crypto-policies --set LEGACY
+					if (! env.SHARD || ! env.SHARDCNT) {
+						env.SHARD = 0
+						env.SHARDCNT = 0
+					}
+				}
+			}
+		}
+		stage('Clone') {
+			steps {
+				dir('src') {
+					echo "${sha1}"
+					checkout([$class: 'GitSCM',
+						branches: [[name: sha1]],
+						extensions: [
+							// We have to set an idenity for the merge step because Git requires
+							// the user.name and user.email to be set to do a merge.
+							[$class: "UserIdentity",
+								name: "ATS CI User",
+								email: "noreply@trafficserver.apache.org"
+							],
+							[$class: "PreBuildMerge",
+								options: [
+									mergeTarget: "${GITHUB_PR_TARGET_BRANCH}",
+									fastForwardMode: "NO_FF",
+									mergeRemote: "origin",
+									mergeStrategy: "DEFAULT"
+								]
+							],
+						],
+						userRemoteConfigs: [[url: github_url, refspec: '+refs/pull/*:refs/remotes/origin/pr/*']]])
+					sh 'git show -n 10 --decorate --graph --oneline --no-patch'
+				}
+				echo 'Finished Cloning'
+			}
+		}
+		stage('Build') {
+			steps {
+				echo 'Starting build'
+				dir('src') {
+					// For Jenkins debugging. We comit the top of README in our debug PRs.
+					sh('head README')
 
-                       # We want to pick up the OpenSSL-QUIC version of curl in /opt/bin.
-                       # The HTTP/3 AuTests depend upon this, so update the PATH accordingly.
-                       export PATH=/opt/bin:${PATH}
+					sh '''#!/bin/bash
+					
+						set -x
+						set -e
+						source /opt/rh/gcc-toolset-11/enable
+						sudo update-crypto-policies --set LEGACY
 
-                       # Change permissions so that all files are readable
-                       # (default user umask may change and make these unreadable)
-                       sudo chmod -R o+r .
-                       autoreconf -fiv
-                       ./configure --with-openssl=/opt/openssl-quic --enable-experimental-plugins --enable-example-plugins --prefix=/tmp/ats --enable-werror --enable-debug --enable-wccp --enable-luajit --enable-ccache
-                       make -j4
-                       make install
-                       '''
-                }
-            }
-        }
-        stage('AuTest') {
-            steps {
-                echo 'Starting AuTest'
-                dir('src/tests') {
-                    sh '''
-                       set +e
-                       # We want to pick up the OpenSSL-QUIC version of curl in /opt/bin.
-                       # The HTTP/3 AuTests depend upon this, so update the PATH accordingly.
-                       export PATH=/opt/bin:${PATH}
-                       
-                       export_dir="${WORKSPACE}/output/${GITHUB_PR_NUMBER}"
-                       mkdir -p ${export_dir}
-                       ./autest.sh --ats-bin /tmp/ats/bin/ --sandbox /tmp/sandbox || true
-                       if [ -n "$(ls -A /tmp/sandbox/)" ]; then
-                           cp -rf /tmp/sandbox/ "${export_dir}"
-                           ls "${export_dir}"
-                           sudo chmod -R 777 ${WORKSPACE}
-                           exit 1
-                       else
-                           touch ${export_dir}/No_autest_failures
-                           sudo chmod -R 777 ${WORKSPACE}
-                           exit 0
-                       fi
-                       '''
-                }
-            }
-        }
-    }
-    
-    post { 
-        always {
-            // We exclude socket files because archiveArtifacts doesn't deal well with
-            // their file type.
-            archiveArtifacts artifacts: 'output/**/*', fingerprint: false, allowEmptyArchive: true, excludes: '**/*.sock, **/cache.db'
-            echo "See the build job description for a link to the sandbox."
-        }
-        cleanup { 
-            cleanWs()
-        }
-    }
+						# We want to pick up the OpenSSL-QUIC version of curl in /opt/bin.
+						# The HTTP/3 AuTests depend upon this, so update the PATH accordingly.
+						export PATH=/opt/bin:${PATH}
+
+						# Change permissions so that all files are readable
+						# (default user umask may change and make these unreadable)
+						sudo chmod -R o+r .
+						autoreconf -fiv
+						./configure --with-openssl=/opt/openssl-quic --enable-experimental-plugins --enable-example-plugins --prefix=/tmp/ats --enable-werror --enable-debug --enable-wccp --enable-luajit --enable-ccache
+						make -j4
+						make install
+						'''
+				}
+			}
+		}
+		stage('AuTest') {
+			steps {
+				echo 'Starting AuTest'
+				dir('src/tests') {
+					sh '''#!/bin/bash -x
+						set +e
+						# We want to pick up the OpenSSL-QUIC version of curl in /opt/bin.
+						# The HTTP/3 AuTests depend upon this, so update the PATH accordingly.
+						export PATH=/opt/bin:${PATH}
+						
+						export_dir="${WORKSPACE}/output/${GITHUB_PR_NUMBER}"
+						mkdir -p ${export_dir}
+
+						if [ ${SHARDCNT} -le 0 ]; then
+							./autest.sh --ats-bin /tmp/ats/bin/ --sandbox /tmp/sandbox || true
+						else
+							testsall=( $( find . -iname "*.test.py" | awk -F'/' '{print $NF}' | awk -F'.' '{print $1}' ) )
+							testsall=( $(
+							  for el in  "${testsall[@]}" ; do
+							    echo $el
+							  done | sort) )
+							ntests=${#testsall[@]}
+
+													  shardsize=$((${ntests} / ${SHARDCNT}))
+							[ 0 -ne $((${ntests} % ${shardsize})) ] && shardsize=$((${shardsize} + 1))
+							shardbeg=$((${shardsize} * ${SHARD}))
+							sliced=${testsall[@]:${shardbeg}:${shardsize}}
+							./autest.sh --ats-bin /tmp/ats/bin/ --sandbox /tmp/sandbox -f ${sliced[@]} || true
+
+						fi
+
+						if [ -n "$(ls -A /tmp/sandbox/)" ]; then
+							touch ${export_dir}/Autest_failures
+							cp -rf /tmp/sandbox/ "${export_dir}"
+							ls "${export_dir}"
+							sudo chmod -R 777 ${WORKSPACE}
+							exit 1
+						else
+							touch ${export_dir}/No_autest_failures
+							sudo chmod -R 777 ${WORKSPACE}
+							exit 0
+						fi
+						'''
+				}
+			}
+		}
+	}
+	
+	post { 
+		always {
+			// We exclude socket files because archiveArtifacts doesn't deal well with
+			// their file type.
+			archiveArtifacts artifacts: 'output/**/*', fingerprint: false, allowEmptyArchive: true, excludes: '**/*.sock, **/cache.db'
+			echo "See the build job description for a link to the sandbox."
+		}
+		cleanup { 
+			cleanWs()
+		}
+	}
 }
diff --git a/jenkins/github/toplevel.pipeline b/jenkins/github/toplevel.pipeline
index 626642b..7d3f895 100644
--- a/jenkins/github/toplevel.pipeline
+++ b/jenkins/github/toplevel.pipeline
@@ -1,225 +1,248 @@
 TOP_JOB_DESC = "Builds:\\n"
 
-String buildJob(String ghcontext, String jobName) {
-        setGitHubPullRequestStatus(context: ghcontext, message: 'Building', state: 'PENDING')
-        
-        if (currentBuild.description == null) {
-            currentBuild.description = "Builds:<br>"
-        }
-        currentBuild.displayName = "PR: #${GITHUB_PR_NUMBER} - Build: #${BUILD_NUMBER}"
-        https_github_url = GITHUB_REPO_GIT_URL.replace("git://", "https://")
-        def jobBuild = build job: jobName, propagate: false, parameters: [string(name: 'SHA1', value: GITHUB_PR_HEAD_SHA), string(name: 'GITHUB_URL', value: https_github_url), string(name: 'GITHUB_PR_NUMBER', value: GITHUB_PR_NUMBER), string(name:'GITHUB_PR_TARGET_BRANCH', value: GITHUB_PR_TARGET_BRANCH)]
-        def jobURL = jobBuild.getAbsoluteUrl()
-        currentBuild.description += " ${jobName} - <a href=${jobURL}>${jobURL}</a> <br>"
+String buildJob(String ghcontext, String jobName, String shard='') {
+		setGitHubPullRequestStatus(context: ghcontext, message: 'Building', state: 'PENDING')
+		
+	if (currentBuild.description == null) {
+		currentBuild.description = "Builds:<br>"
+	}
+	currentBuild.displayName = "PR: #${GITHUB_PR_NUMBER} - Build: #${BUILD_NUMBER}"
+	https_github_url = GITHUB_REPO_GIT_URL.replace("git://", "https://")
 
-        def jobResult = jobBuild.getResult()
+	def parms = [
+		string(name: 'SHA1', value: GITHUB_PR_HEAD_SHA),
+		string(name: 'GITHUB_URL', value: https_github_url),
+		string(name: 'GITHUB_PR_NUMBER', value: GITHUB_PR_NUMBER),
+		string(name: 'GITHUB_PR_TARGET_BRANCH', value: GITHUB_PR_TARGET_BRANCH),
+	]
 
-        echo "Build of '${jobName}' returned result: ${jobResult}"
+	def displayname = jobName
+	if (shard != '') {
+		parms << string(name: 'AUTEST_SHARD', value: shard)
+		displayname += ' ' + shard
+	}
 
-        if (jobResult == 'SUCCESS') {
-            setGitHubPullRequestStatus(context: ghcontext, message: "Success - ${jobURL}", state: jobResult)
-        } else {
-            //setGitHubPullRequestStatus(context: ghcontext, message: "Failure - ${jobURL}", state: jobResult, targetUrl: "${jobURL}")
-            setGitHubPullRequestStatus(context: ghcontext, message: "Failure - ${jobURL}", state: jobResult)
-        }
-        return jobResult
+	def jobBuild = build job: jobName, propagate: false, parameters: parms
+	def jobURL = jobBuild.getAbsoluteUrl()
+	currentBuild.description += " ${displayname} - <a href=${jobURL}>${jobURL}</a> <br>"
+
+	def jobResult = jobBuild.getResult()
+
+	echo "Build of '${displayname}' returned result: ${jobResult}"
+
+	if (jobResult == 'SUCCESS') {
+		setGitHubPullRequestStatus(context: ghcontext, message: "Success - ${jobURL}", state: jobResult)
+	} else {
+		setGitHubPullRequestStatus(context: ghcontext, message: "Failure - ${jobURL}", state: jobResult)
+	}
+	return jobResult
 }
 
 pipeline {
-    agent none
-    
-    stages {
-        stage('Quick Checks') {
-            parallel {
-                stage('Clang-Format') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*clang-format.*/ }
-                        }
-                    }
-                    steps {
-                        script {
-                            echo GITHUB_PR_COMMENT_BODY_MATCH
-                            result = buildJob('Clang-Format', 'Github_Builds/clang-format')
-                            if (result == 'FAILURE') {
-                                error('Clang-Format failed')
-                            }
-                        }
-                    }
-                }
-                stage('RAT') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*RAT.*/ }
-                        }
-                    }
-                    steps {
-                        script {
-                            result = buildJob('RAT', 'Github_Builds/rat')
-                            if (result == 'FAILURE') {
-                                error('RAT failed')
-                            }
-                        }
-                    }
-                }
-                stage('Docs') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*docs.*/ }
-                        }
-                    }
-                    steps {
-                        script {
-                            result = buildJob('Docs', 'Github_Builds/docs')
-                            if (result == 'FAILURE') {
-                                error('Docs failed')
-                            }
-                        }
-                    }
-                }                
-            }
-        }
-        
- 
-        stage('Build and Test') {
-            parallel {
-                stage('Ubuntu Build') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*ubuntu.*/ }
-                        }
-                    }
-                    steps {
-                        script {
-                            result = buildJob('Ubuntu', 'Github_Builds/ubuntu')
-                            if (result == 'FAILURE') {
-                                error('Ubuntu build failed')
-                            }
-                        }
-                    }
-                }
-                stage('Fedora Build') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*fedora.*/ }
-                        }
-                    }                    
-                    steps {
-                        script {
-                            result = buildJob('Fedora', 'Github_Builds/fedora')
-                            if (result == 'FAILURE') {
-                                error('Fedora build failed')
-                            }
-                        }
-                    }
-                }
-                stage('Debian Build') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*debian.*/ }
-                        }
-                    }                    
-                    steps {
-                        script {
-                            result = buildJob('Debian', 'Github_Builds/debian')
-                            if (result == 'FAILURE') {
-                                error('Debian build failed')
-                            }
-                            result = 'SUCCESS'
-                        }
-                    }
-                }
+	agent none
+	
+	stages {
+		stage('Quick Checks') {
+			parallel {
+				stage('Clang-Format') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*clang-format.*/ }
+						}
+					}
+					steps {
+						script {
+							echo GITHUB_PR_COMMENT_BODY_MATCH
+							result = buildJob('Clang-Format', 'Github_Builds/clang-format')
+							if (result == 'FAILURE') {
+								error('Clang-Format failed')
+							}
+						}
+					}
+				}
+				stage('RAT') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*RAT.*/ }
+						}
+					}
+					steps {
+						script {
+							result = buildJob('RAT', 'Github_Builds/rat')
+							if (result == 'FAILURE') {
+								error('RAT failed')
+							}
+						}
+					}
+				}
+				stage('Docs') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*docs.*/ }
+						}
+					}
+					steps {
+						script {
+							result = buildJob('Docs', 'Github_Builds/docs')
+							if (result == 'FAILURE') {
+								error('Docs failed')
+							}
+						}
+					}
+				}                
+			}
+		}
+
+		stage('Build and Test') {
+			parallel {
+				stage('Ubuntu Build') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*ubuntu.*/ }
+						}
+					}
+					steps {
+						script {
+							result = buildJob('Ubuntu', 'Github_Builds/ubuntu')
+							if (result == 'FAILURE') {
+								error('Ubuntu build failed')
+							}
+						}
+					}
+				}
+				stage('Fedora Build') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*fedora.*/ }
+						}
+					}                    
+					steps {
+						script {
+							result = buildJob('Fedora', 'Github_Builds/fedora')
+							if (result == 'FAILURE') {
+								error('Fedora build failed')
+							}
+						}
+					}
+				}
+				stage('Debian Build') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*debian.*/ }
+						}
+					}                    
+					steps {
+						script {
+							result = buildJob('Debian', 'Github_Builds/debian')
+							if (result == 'FAILURE') {
+								error('Debian build failed')
+							}
+							result = 'SUCCESS'
+						}
+					}
+				}
 
-                stage('Rocky Build') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*rocky.*/ }
-                        }
-                    }                    
-                    steps {
-                        script {
-                            result = buildJob('Rocky', 'Github_Builds/rocky')
-                            if (result == 'FAILURE') {
-                                error('Rocky build failed')
-                            }
-                        }
-                    }
-                }
-                
-                stage('OSX Build') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*osx.*/ }
-                        }
-                    }                    
-                    steps {
-                        script {
-                            result = buildJob('OSX', 'Github_Builds/osx')
-                            if (result == 'FAILURE') {
-                                error('OSX build failed')
-                            }
-                        }
-                    }
-                }
+				stage('Rocky Build') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*rocky.*/ }
+						}
+					}                    
+					steps {
+						script {
+							result = buildJob('Rocky', 'Github_Builds/rocky')
+							if (result == 'FAILURE') {
+								error('Rocky build failed')
+							}
+						}
+					}
+				}
+				
+				stage('OSX Build') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*osx.*/ }
+						}
+					}                    
+					steps {
+						script {
+							result = buildJob('OSX', 'Github_Builds/osx')
+							if (result == 'FAILURE') {
+								error('OSX build failed')
+							}
+						}
+					}
+				}
 
-                stage('FreeBSD Build') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*freebsd.*/ }
-                        }
-                    }                    
-                    steps {
-                        script {
-                            result = buildJob('FreeBSD', 'Github_Builds/freebsd')
-                            if (result == 'FAILURE') {
-                                error('FreeBSD build failed')
-                            }
-                        }
-                   }
-                }                
+				stage('FreeBSD Build') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*freebsd.*/ }
+						}
+					}                    
+					steps {
+						script {
+							result = buildJob('FreeBSD', 'Github_Builds/freebsd')
+							if (result == 'FAILURE') {
+								error('FreeBSD build failed')
+							}
+						}
+					}
+				}
 
-                stage('Clang-Analyzer') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*clang-analyzer.*/ }
-                        }
-                    }                    
-                    steps {
-                        script {
-                            result = buildJob('Clang-Analyzer', 'Github_Builds/clang-analyzer')
-                            if (result == 'FAILURE') {
-                                error('Clang-Analyzer failed')
-                            }
-                        }
-                    }
-                }
-                stage('AuTest') {
-                    when {
-                        anyOf {
-                            environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
-                            expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*autest.*/ }
-                        }
-                    }
-                    steps {
-                        script {
-                            result = buildJob('AuTest', 'Github_Builds/autest')
-                            if (result == 'FAILURE') {
-                                error('AuTest failed')
-                            }
-                        }
-                    }
-                }
-            } // parallel for "Build and Test"
-        } // End Stage("Build and Test")
-    } // End Stages
+				stage('Clang-Analyzer') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*clang-analyzer.*/ }
+						}
+					}                    
+					steps {
+						script {
+							result = buildJob('Clang-Analyzer', 'Github_Builds/clang-analyzer')
+							if (result == 'FAILURE') {
+								error('Clang-Analyzer failed')
+							}
+						}
+					}
+				}
+				stage('AuTest') {
+					when {
+						anyOf {
+							environment name: 'GITHUB_PR_COMMENT_BODY_MATCH', value: ''
+							expression { GITHUB_PR_COMMENT_BODY_MATCH ==~ /.*autest.*/ }
+						}
+					}
+					steps {
+						script {
+							String jobpath = 'Github_Builds/autest'
+							if (env.AUTEST_SHARDS) {
+								def nshards = env.AUTEST_SHARDS as int
+								def jobs = [:]
+								for (index = 0 ; index < nshards ; index++) {
+									String shard = index + "of" + env.AUTEST_SHARDS
+									jobs[shard] = { buildJob('AuTest ' + shard, jobpath, shard) }
+								}
+								parallel jobs
+							} else {
+								result = buildJob('AuTest', jobpath)
+								if (result == 'FAILURE') {
+									error('AuTest failed')
+								}
+							}
+						}
+					}
+				}
+			} // parallel for "Build and Test"
+		} // End Stage("Build and Test")
+	} // End Stages
 }