import org.jenkinsci.plugins.pipeline.modeldefinition.Utils def call(String baseImage = 'ubuntu:22.04') { node { stage('Checkout project') { checkout(scm) } stage('Run pre hooks') { hook('pre-symfony-app') } stage('Run in Symfony image') { def symfonyImage = buildDockerImage(baseImage) symfonyImage.inside() { def repo = env.JOB_NAME if (env.BRANCH_NAME ==~ /^PR-.*$/) { repo = env.JOB_NAME - "/${env.JOB_BASE_NAME}" } stage('Install composer dependencies') { sh ''' composer install ''' } parallel([ 'php-security-check': { stage('Check PHP security issues') { catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') { def auditReport = sh(script: 'local-php-security-checker --format=markdown || true', returnStdout: true) if (auditReport.trim() != '') { if (env.CHANGE_ID) { gitea.commentPullRequest(repo, env.CHANGE_ID, auditReport, 0) } else { print auditReport } } if (!auditReport.contains('No packages have known vulnerabilities.')) { throw new Exception('Dependencies check failed !') } } } }, 'php-cs-fixer': { stage('Run PHP-CS-Fixer on modified code') { catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { if ( !fileExists('.php-cs-fixer.dist.php') ) { def phpCsFixerConfig = libraryResource 'com/cadoles/symfony/.php-cs-fixer.dist.php' writeFile file:'.php-cs-fixer.dist.php', text:phpCsFixerConfig } sh ''' CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRTUXB "HEAD~..HEAD" | fgrep ".php" | tr "\n" " ") if ! echo "${CHANGED_FILES}" | grep -qE "^(\\.php-cs-fixer(\\.dist)\\.php?|composer\\.lock)$"; then EXTRA_ARGS=$(printf -- '--path-mode=intersection -- %s' "${CHANGED_FILES}"); else EXTRA_ARGS=''; fi php-cs-fixer fix --config=.php-cs-fixer.dist.php -v --dry-run --using-cache=no --format junit ${EXTRA_ARGS} > php-cs-fixer.xml || true ''' def report = sh(script: 'junit2md php-cs-fixer.xml', returnStdout: true) if (env.CHANGE_ID) { gitea.commentPullRequest(repo, env.CHANGE_ID, report, 1) } else { print report } } } }, 'phpstan': { stage('Run phpstan') { catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') { if ( !fileExists('phpstan.neon') ) { def phpStanConfig = libraryResource 'com/cadoles/symfony/phpstan.neon' writeFile file:'phpstan.neon', text:phpStanConfig } sh ''' phpstan analyze -l 1 --error-format=table src > phpstan.txt || true ''' def report = sh(script: 'cat phpstan.txt', returnStdout: true) report = '## Rapport PHPStan\n\n```\n' + report report = report + '\n```\n' if (env.CHANGE_ID) { gitea.commentPullRequest(repo, env.CHANGE_ID, report, 2) } else { print report } } } } ]) } } stage('Run post hooks') { hook('post-symfony-app') } } } def buildDockerImage(String baseImage) { def imageName = 'cadoles-symfony-ci' dir(".${imageName}") { def dockerfile = libraryResource 'com/cadoles/symfony/Dockerfile' writeFile file:'Dockerfile', text: "FROM ${baseImage}\n\n" + dockerfile def addLetsEncryptCA = libraryResource 'com/cadoles/common/add-letsencrypt-ca.sh' writeFile file:'add-letsencrypt-ca.sh', text:addLetsEncryptCA hook('build-symfony-image') def safeJobName = URLDecoder.decode(env.JOB_NAME).toLowerCase().replace('/', '-').replace(' ', '-') def imageTag = "${safeJobName}-${env.BUILD_ID}" return docker.build("${imageName}:${imageTag}", '.') } } def when(boolean condition, body) { def config = [:] body.resolveStrategy = Closure.OWNER_FIRST body.delegate = config if (condition) { body() } else { Utils.markStageSkippedForConditional(STAGE_NAME) } }