Commit 3eeb4201 authored by Raphael Defosseux's avatar Raphael Defosseux

CI: adding CLANG-FORMAT check stage

 -- copied .clang-format file from the MAGMA project
 -- Create a container and install dedicated clang-format version
 -- Running new format check script
 -- Report in HTML number and files not respecting forma

Also added a step to verify if the sanity check stage is indeed OK.
Signed-off-by: default avatarRaphael Defosseux <raphael.defosseux@openairinterface.org>
parent de1221c4
......@@ -178,6 +178,45 @@ pipeline {
}
}
}
// Running CLANG-FORMATTING check in parallel to gain time
stage ('Code Formatting Checker') {
steps {
script {
myShCmd('docker run --name ci-cn-clang-formatter -d ubuntu:bionic /bin/bash -c "sleep infinity"', new_host_flag, new_host_user, new_host)
myShCmd('docker exec -it ci-cn-clang-formatter /bin/bash -c "apt-get update && apt-get upgrade --yes" > archives/clang_format_install.log', new_host_flag, new_host_user, new_host)
myShCmd('docker exec -it ci-cn-clang-formatter /bin/bash -c "apt-get install --yes git tree bzip2" >> archives/clang_format_install.log', new_host_flag, new_host_user, new_host)
myShCmd('docker cp ./openair-spgwu.tar.bz2 ci-cn-clang-formatter:/home', new_host_flag, new_host_user, new_host)
myShCmd('docker exec -it ci-cn-clang-formatter /bin/bash -c "cd /home && tar -xjf openair-spgwu.tar.bz2"', new_host_flag, new_host_user, new_host)
myShCmd('docker exec -it ci-cn-clang-formatter /bin/bash -c "rm -f /home/openair-spgwu.tar.bz2"', new_host_flag, new_host_user, new_host)
// We install a dedicated version (installed on our CI server).
myShCmd('docker cp /opt/clang-format/9.0.0/bin/clang-format ci-cn-clang-formatter:/usr/local/bin', new_host_flag, new_host_user, new_host)
if (env.ghprbPullId != null) {
myShCmd('docker exec -it ci-cn-clang-formatter /bin/bash -c "cd /home && ./ci-scripts/checkCodingFormattingRules.sh --src-branch ' + env.ghprbSourceBranch +' --target-branch ' + env.ghprbTargetBranch + '"', new_host_flag, new_host_user, new_host)
} else {
myShCmd('docker exec -it ci-cn-clang-formatter /bin/bash -c "cd /home && ./ci-scripts/checkCodingFormattingRules.sh"', new_host_flag, new_host_user, new_host)
}
}
}
post {
always {
script {
myShCmd('docker cp ci-cn-clang-formatter:/home/src/oai_rules_result.txt src', new_host_flag, new_host_user, new_host)
// May not have been generated
try {
myShCmd('docker cp ci-cn-clang-formatter:/home/src/oai_rules_result_list.txt src', new_host_flag, new_host_user, new_host)
} catch (Exception e) {
echo "Failed to copy src/oai_rules_result_list.txt! It may not have been generated. That's OK!"
}
copyFrom2ndServer('archives/clang_format*.*', 'archives', new_host_flag, new_host_user, new_host)
copyFrom2ndServer('src/oai_rules*.*', 'src', new_host_flag, new_host_user, new_host)
// no need to keep the clang-formatter container
myShCmd('docker rm -f ci-cn-clang-formatter', new_host_flag, new_host_user, new_host)
}
}
}
}
}
}
stage('Deploy Full EPC') {
......@@ -268,20 +307,23 @@ pipeline {
sh "sleep 20"
myShCmd('docker exec -it ci-oai-spgwc /bin/bash -c "killall oai_spgwc"', new_host_flag, new_host_user, new_host)
myShCmd('docker exec -it ci-oai-spgwu /bin/bash -c "killall oai_spgwu"', new_host_flag, new_host_user, new_host)
}
}
post {
always {
sh "sleep 2"
myShCmd('docker cp ci-oai-spgwc:/openair-spgwc/spgwc_check_run.log archives', new_host_flag, new_host_user, new_host)
myShCmd('docker cp ci-oai-spgwu:/openair-spgwu-tiny/spgwu_check_run.log archives', new_host_flag, new_host_user, new_host)
copyFrom2ndServer('archives/*_check_run.log', 'archives', new_host_flag, new_host_user, new_host)
// add a check python script
sh "python3 ./ci-scripts/verifySanityCheckDeployment.py --job_name=${JOB_NAME} --job_id=${BUILD_ID}"
}
}
post {
success {
sh "echo 'OAI-SPGW-C CHECK START/STOP: OK' > archives/spgwc_cnf_check_start.log"
sh "echo 'OAI-SPGW-U CHECK START/STOP: OK' > archives/spgwu_cnf_check_start.log"
}
unsuccessful {
// If anything wrong occurs, we still try to copy
myShCmd('docker cp ci-oai-spgwc:/openair-spgwc/spgwc_check_run.log archives', new_host_flag, new_host_user, new_host)
myShCmd('docker cp ci-oai-spgwu:/openair-spgwu-tiny/spgwu_check_run.log archives', new_host_flag, new_host_user, new_host)
copyFrom2ndServer('archives/*_check_run.log', 'archives', new_host_flag, new_host_user, new_host)
sh "echo 'OAI-SPGW-C CHECK START/STOP: KO' > archives/spgwc_cnf_check_start.log"
sh "echo 'OAI-SPGW-U CHECK START/STOP: KO' > archives/spgwu_cnf_check_start.log"
}
......
#!/bin/bash
#/*
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
# * contributor license agreements. See the NOTICE file distributed with
# * this work for additional information regarding copyright ownership.
# * The OpenAirInterface Software Alliance licenses this file to You under
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
# * except in compliance with the License.
# * You may obtain a copy of the License at
# *
# * http://www.openairinterface.org/?page_id=698
# *
# * Unless required by applicable law or agreed to in writing, software
# * distributed under the License is distributed on an "AS IS" BASIS,
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# * See the License for the specific language governing permissions and
# * limitations under the License.
# *-------------------------------------------------------------------------------
# * For more information about the OpenAirInterface (OAI) Software Alliance:
# * contact@openairinterface.org
# */
function usage {
echo "OAI Coding / Formatting Guideline Check script"
echo " Original Author: Raphael Defosseux"
echo ""
echo " Requirement: clang-format / git shall be installed"
echo ""
echo " By default (no options) the complete repository will be checked"
echo " In case of merge/pull request, provided source and target branch,"
echo " the script will check only the modified files"
echo ""
echo "Usage:"
echo "------"
echo " checkCodingFormattingRules.sh [OPTIONS]"
echo ""
echo "Options:"
echo "--------"
echo " --src-branch #### OR -sb ####"
echo " Specify the source branch of the merge request."
echo ""
echo " --target-branch #### OR -tb ####"
echo " Specify the target branch of the merge request (usually develop)."
echo ""
echo " --help OR -h"
echo " Print this help message."
echo ""
}
if [ $# -ne 4 ] && [ $# -ne 1 ] && [ $# -ne 0 ]
then
echo "Syntax Error: not the correct number of arguments"
echo ""
usage
exit 1
fi
cd src
if [ $# -eq 0 ]
then
echo " ---- Checking the whole repository ----"
echo ""
if [ -f oai_rules_result.txt ]
then
rm -f oai_rules_result.txt
fi
if [ -f oai_rules_result_list.txt ]
then
rm -f oai_rules_result_list.txt
fi
EXTENSION_LIST=("h" "hpp" "c" "cpp")
NB_TO_FORMAT=0
NB_TOTAL=0
for EXTENSION in ${EXTENSION_LIST[@]}
do
echo "Checking for all files with .${EXTENSION} extension"
FILE_LIST=`tree -n --noreport -i -f -P *.${EXTENSION} | sed -e 's#^\./##' | grep -v test | grep "\.${EXTENSION}"`
for FILE_TO_CHECK in ${FILE_LIST[@]}
do
TO_FORMAT=`clang-format -output-replacements-xml ${FILE_TO_CHECK} 2>&1 | grep -v replacements | grep -c replacement`
NB_TOTAL=$((NB_TOTAL + 1))
if [ $TO_FORMAT -ne 0 ]
then
NB_TO_FORMAT=$((NB_TO_FORMAT + 1))
# In case of full repo, being silent
#echo "src/$FILE_TO_CHECK"
echo "src/$FILE_TO_CHECK" >> ./oai_rules_result_list.txt
fi
done
done
echo "Nb Files that do NOT follow OAI rules: $NB_TO_FORMAT over $NB_TOTAL checked!"
echo "NB_FILES_FAILING_CHECK=$NB_TO_FORMAT" > ./oai_rules_result.txt
echo "NB_FILES_CHECKED=$NB_TOTAL" >> ./oai_rules_result.txt
exit 0
fi
checker=0
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-h|--help)
shift
usage
exit 0
;;
-sb|--src-branch)
SOURCE_BRANCH="$2"
let "checker|=0x1"
shift
shift
;;
-tb|--target-branch)
TARGET_BRANCH="$2"
let "checker|=0x2"
shift
shift
;;
*)
echo "Syntax Error: unknown option: $key"
echo ""
usage
exit 1
esac
done
if [ $checker -ne 3 ]
then
echo "Source Branch is : $SOURCE_BRANCH"
echo "Target Branch is : $TARGET_BRANCH"
echo ""
echo "Syntax Error: missing option"
echo ""
usage
exit 1
fi
# Merge request scenario
MERGE_COMMMIT=`git log -n1 --pretty=format:%H`
if [ -f .git/refs/remotes/origin/$TARGET_BRANCH ]
then
TARGET_INIT_COMMIT=`cat .git/refs/remotes/origin/$TARGET_BRANCH`
else
TARGET_INIT_COMMIT=`git log -n1 --pretty=format:%H origin/$TARGET_BRANCH`
fi
echo " ---- Checking the modified files by the merge request ----"
echo ""
echo "Source Branch is : $SOURCE_BRANCH"
echo "Target Branch is : $TARGET_BRANCH"
echo "Merged Commit is : $MERGE_COMMMIT"
echo "Target Init is : $TARGET_INIT_COMMIT"
echo ""
echo " ----------------------------------------------------------"
echo ""
# Retrieve the list of modified files since the latest develop commit
MODIFIED_FILES=`git log $TARGET_INIT_COMMIT..$MERGE_COMMMIT --oneline --name-status | egrep "^M|^A" | sed -e "s@^M\t*@@" -e "s@^A\t*@@" | sort | uniq | grep -v test`
NB_TO_FORMAT=0
NB_TOTAL=0
if [ -f oai_rules_result.txt ]
then
rm -f oai_rules_result.txt
fi
if [ -f oai_rules_result_list.txt ]
then
rm -f oai_rules_result_list.txt
fi
for FULLFILE in $MODIFIED_FILES
do
filename=$(basename -- "$FULLFILE")
EXT="${filename##*.}"
if [ $EXT = "c" ] || [ $EXT = "h" ] || [ $EXT = "cpp" ] || [ $EXT = "hpp" ]
then
SRC_FILE=`echo $FULLFILE | sed -e "s#src/##"`
TO_FORMAT=`clang-format -output-replacements-xml ${SRC_FILE} 2>&1 | grep -v replacements | grep -c replacement`
NB_TOTAL=$((NB_TOTAL + 1))
if [ $TO_FORMAT -ne 0 ]
then
NB_TO_FORMAT=$((NB_TO_FORMAT + 1))
echo $FULLFILE
echo $FULLFILE >> ./oai_rules_result_list.txt
fi
fi
done
echo ""
echo " ----------------------------------------------------------"
echo "Nb Files that do NOT follow OAI rules: $NB_TO_FORMAT over $NB_TOTAL checked!"
echo "NB_FILES_FAILING_CHECK=$NB_TO_FORMAT" > ./oai_rules_result.txt
echo "NB_FILES_CHECKED=$NB_TOTAL" >> ./oai_rules_result.txt
exit 0
......@@ -44,6 +44,8 @@ class HtmlReport():
self.file = open(cwd + '/test_results_oai_spgwu.html', 'w')
self.generateHeader()
self.coding_formatting_log()
self.analyze_sca_log()
self.buildSummaryHeader()
......@@ -163,6 +165,49 @@ class HtmlReport():
self.file.write('</div></body>\n')
self.file.write('</html>\n')
def coding_formatting_log(self):
cwd = os.getcwd()
self.file.write(' <h2>OAI Coding / Formatting Guidelines Check</h2>\n')
if os.path.isfile(cwd + '/src/oai_rules_result.txt'):
cmd = 'grep NB_FILES_FAILING_CHECK ' + cwd + '/src/oai_rules_result.txt | sed -e "s#NB_FILES_FAILING_CHECK=##"'
nb_fail = subprocess.check_output(cmd, shell=True, universal_newlines=True)
cmd = 'grep NB_FILES_CHECKED ' + cwd + '/src/oai_rules_result.txt | sed -e "s#NB_FILES_CHECKED=##"'
nb_total = subprocess.check_output(cmd, shell=True, universal_newlines=True)
if int(nb_fail.strip()) == 0:
self.file.write(' <div class="alert alert-success">\n')
if self.git_pull_request:
self.file.write(' <strong>All modified files in Pull-Request follow OAI rules. <span class="glyphicon glyphicon-ok-circle"></span> -> (' + nb_total.strip() + ' were checked)</strong>\n')
else:
self.file.write(' <strong>All files in repository follow OAI rules. <span class="glyphicon glyphicon-ok-circle"></span> -> (' + nb_total.strip() + ' were checked)</strong>\n')
self.file.write(' </div>\n')
else:
self.file.write(' <div class="alert alert-warning">\n')
if self.git_pull_request:
self.file.write(' <strong>' + nb_fail.strip() + ' modified files in Pull-Request DO NOT follow OAI rules. <span class="glyphicon glyphicon-warning-sign"></span> -> (' + nb_total.strip() + ' were checked)</strong>\n')
else:
self.file.write(' <strong>' + nb_fail.strip() + ' files in repository DO NOT follow OAI rules. <span class="glyphicon glyphicon-warning-sign"></span> -> (' + nb_total.strip() + ' were checked)</strong>\n')
self.file.write(' </div>\n')
if os.path.isfile(cwd + '/src/oai_rules_result_list.txt'):
self.file.write(' <button data-toggle="collapse" data-target="#oai-formatting-details">More details on formatting check</button>\n')
self.file.write(' <div id="oai-formatting-details" class="collapse">\n')
self.file.write(' <p>Please apply the following command to this(ese) file(s): </p>\n')
self.file.write(' <p style="margin-left: 30px"><strong><code>cd src && clang-format -i filename(s)</code></strong></p>\n')
self.file.write(' <table class="table-bordered" width = "60%" align = "center" border = 1>\n')
self.file.write(' <tr><th bgcolor = "lightcyan" >Filename</th></tr>\n')
with open(cwd + '/src/oai_rules_result_list.txt', 'r') as filelist:
for line in filelist:
self.file.write(' <tr><td>' + line.strip() + '</td></tr>\n')
filelist.close()
self.file.write(' </table>\n')
self.file.write(' </div>\n')
else:
self.file.write(' <div class="alert alert-danger">\n')
self.file.write(' <strong>Was NOT performed (with CLANG-FORMAT tool). <span class="glyphicon glyphicon-ban-circle"></span></strong>\n')
self.file.write(' </div>\n')
self.file.write(' <br>\n')
def analyze_sca_log(self):
cwd = os.getcwd()
if os.path.isfile(cwd + '/archives/cppcheck_build.log'):
......
#/*
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
# * contributor license agreements. See the NOTICE file distributed with
# * this work for additional information regarding copyright ownership.
# * The OpenAirInterface Software Alliance licenses this file to You under
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
# * except in compliance with the License.
# * You may obtain a copy of the License at
# *
# * http://www.openairinterface.org/?page_id=698
# *
# * Unless required by applicable law or agreed to in writing, software
# * distributed under the License is distributed on an "AS IS" BASIS,
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# * See the License for the specific language governing permissions and
# * limitations under the License.
# *-------------------------------------------------------------------------------
# * For more information about the OpenAirInterface (OAI) Software Alliance:
# * contact@openairinterface.org
# */
#---------------------------------------------------------------------
import os
import re
import sys
import subprocess
class verifySanityCheckDeployment():
def __init__(self):
self.job_name = ''
def checkLogs(self):
spgwc_status = self.analyze_check_run_log('SPGW-C')
spgwu_status = self.analyze_check_run_log('SPGW-U')
if not spgwc_status:
print ('SPGW-C did not deploy properly')
if not spgwu_status:
print ('SPGW-U did not deploy properly')
if not spgwc_status or not spgwu_status:
sys.exit('Sanity Check Deployment went wrong')
else:
print ('Sanity Check Deployment is OK')
def analyze_check_run_log(self, nfType):
logFileName = nfType.lower().replace('-','') + '_check_run.log'
cwd = os.getcwd()
status = False
if os.path.isfile(cwd + '/archives/' + logFileName):
nb_pfcp_hb_proc = 0
nb_sx_hb_resp = 0
nb_sx_hb_req = 0
with open(cwd + '/archives/' + logFileName, 'r') as logfile:
for line in logfile:
result = re.search('PFCP HEARTBEAT PROCEDURE', line)
if result is not None:
nb_pfcp_hb_proc += 1
result = re.search('SX HEARTBEAT RESPONSE', line)
if result is not None:
nb_sx_hb_resp += 1
result = re.search('SX HEARTBEAT REQUEST', line)
if result is not None:
nb_sx_hb_req += 1
logfile.close()
if nfType == 'SPGW-C':
if nb_pfcp_hb_proc > 0:
status = True
if nfType == 'SPGW-U':
if nb_pfcp_hb_proc > 0 and nb_sx_hb_resp > 0 and nb_sx_hb_req > 0:
status = True
return status
def Usage():
print('----------------------------------------------------------------------------------------------------------------------')
print('verifySanityCheckDeployment.py')
print(' Verify the Sanity Check Deployment in the pipeline.')
print('----------------------------------------------------------------------------------------------------------------------')
print('Usage: python3 verifySanityCheckDeployment.py [options]')
print(' --help Show this help.')
print('---------------------------------------------------------------------------------------------- Mandatory Options -----')
print(' --job_name=[Jenkins Job name]')
print(' --job_id=[Jenkins Job Build ID]')
#--------------------------------------------------------------------------------------------------------
#
# Start of main
#
#--------------------------------------------------------------------------------------------------------
argvs = sys.argv
argc = len(argvs)
vscd = verifySanityCheckDeployment()
while len(argvs) > 1:
myArgv = argvs.pop(1)
if re.match('^\-\-help$', myArgv, re.IGNORECASE):
Usage()
sys.exit(0)
elif re.match('^\-\-job_name=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match('^\-\-job_name=(.+)$', myArgv, re.IGNORECASE)
vscd.job_name = matchReg.group(1)
elif re.match('^\-\-job_id=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match('^\-\-job_id=(.+)$', myArgv, re.IGNORECASE)
vscd.job_id = matchReg.group(1)
else:
sys.exit('Invalid Parameter: ' + myArgv)
if vscd.job_name == '' or vscd.job_id == '':
sys.exit('Missing Parameter in job description')
vscd.checkLogs()
#
# Copyright (c) 2015, EURECOM (www.eurecom.fr)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of the FreeBSD Project.
#
# see https://clang.llvm.org/docs/ClangFormatStyleOptions.html for explanation
# of style options
BasedOnStyle: Google
Language: Cpp
IndentWidth: 2
ColumnLimit: 80
IncludeBlocks: Preserve
SortIncludes: false
# alignment
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
DerivePointerAlignment: false
PointerAlignment: Left
# function style
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortFunctionsOnASingleLine: Inline
AlwaysBreakAfterReturnType: None
IndentWrappedFunctionNames: false
# template style
AlwaysBreakTemplateDeclarations: Yes
# preprocessor style
IndentPPDirectives: None
# block style
AllowShortBlocksOnASingleLine: false
KeepEmptyLinesAtTheStartOfBlocks: false
# break style
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: false
BreakStringLiterals: true
CompactNamespaces: false
ContinuationIndentWidth: 4
MaxEmptyLinesToKeep: 1
ReflowComments: true
# spacing style
UseTab: Never
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
# class style
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
# case statements
IndentCaseLabels: true
# cpp
Cpp11BracedListStyle: true
FixNamespaceComments: true
NamespaceIndentation: None
SortUsingDeclarations: true
# todo
# AlwaysBreakBeforeMultilineStrings: bool
# PenaltyBreakAssignment (unsigned)
# PenaltyBreakBeforeFirstCallParameter (unsigned)
# PenaltyBreakComment (unsigned)
# PenaltyBreakFirstLessLess (unsigned)
# PenaltyBreakString (unsigned)
# PenaltyBreakTemplateDeclaration (unsigned)
# PenaltyExcessCharacter (unsigned)
# PenaltyReturnTypeOnItsOwnLine (unsigned)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment