Jenkinsfile 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. // Helper function for database operations
  2. def executeDbQuery(String query, String description = null) {
  3. echo "SQL query: ${query} ### ${description}"
  4. sh """mysql -u ${DEPLOY_DB_USER} --password=${DEPLOY_DB_PWD} -D ${DEPLOY_DB_NAME} -h ${DEPLOY_DB_HOST} -e "${query}" """
  5. }
  6. // Helper function for database operations
  7. def executeDbMultiQuery(String queries, String description = null) {
  8. echo "SQL query: ${queries} ### ${description}"
  9. sh """mysql -u ${DEPLOY_DB_USER} --password=${DEPLOY_DB_PWD} -D ${DEPLOY_DB_NAME} -h ${DEPLOY_DB_HOST} << 'EOF'
  10. ${queries}
  11. EOF"""
  12. }
  13. pipeline {
  14. agent any
  15. options {
  16. buildDiscarder(logRotator(numToKeepStr: '10'))
  17. timeout(time: 60, unit: 'MINUTES')
  18. timestamps()
  19. }
  20. environment {
  21. // Node.js configuration
  22. PATH = "/var/lib/jenkins/.nvm/versions/node/v20.19.3/bin:$PATH"
  23. // Binary paths
  24. PHP = "/usr/bin/php8.3"
  25. GIT = "git"
  26. CURL = "curl"
  27. PHING = "/usr/bin/phing-3.1.0.phar"
  28. DOXYGEN = "/usr/bin/doxygen"
  29. COMPOSER_PHAR = "/usr/bin/composer.phar"
  30. // Modules to install
  31. MODULES = "bx_posts,bx_timeline,bx_contact,bx_developer,bx_antispam,bx_convos,bx_organizations,bx_notifications,bx_ocean,bx_ether,bx_accounts,bx_albums,bx_facebook,bx_tricon,bx_oauth,bx_chat_plus,bx_datafox,bx_payment,bx_market,bx_groups,bx_intercom,bx_googlecon,bx_linkedin,bx_twitter,bx_mailchimp,bx_forum,bx_acl,bx_events,bx_files,bx_glossary,bx_googletagman,bx_photos,bx_polls,bx_profiler,bx_quoteofday,bx_smtp,bx_shopify,bx_snipcart,bx_se_migration,bx_spaces,bx_stripe_connect,bx_unacon,bx_videos,bx_charts,bx_dolcon,bx_fontawesome,bx_channels,bx_attendant,bx_massmailer,bx_directory,bx_plyr,bx_ads,bx_decorous,bx_lucid,bx_credits,bx_wiki,bx_tasks,bx_courses,bx_classes,bx_artificer,bx_donations,bx_feedback,bx_help_tours,bx_stream,bx_attendant,bx_messenger,bx_analytics,bx_reputation,bx_ru"
  32. // Test install parameters
  33. INSTALL_DB_NAME = "mainsite_virtual"
  34. INSTALL_DB_USER = "mainsite_una"
  35. INSTALL_DB_PWD = "*****"
  36. // Package parameters
  37. PACKAGE_PUBLISH_DIR = "/var/www/vhosts/mainsite/html/builds"
  38. PACKAGE_PUBLISH_LATEST_FILE = "${PACKAGE_PUBLISH_DIR}/latest-file.txt"
  39. PACKAGE_PUBLISH_LATEST_DATE = "${PACKAGE_PUBLISH_DIR}/latest-date.txt"
  40. PACKAGE_PUBLISH_LATEST_BUILD = "${PACKAGE_PUBLISH_DIR}/latest-build.txt"
  41. // Deploy parameters
  42. DEPLOY_PATH = "/var/www/vhosts/mainsite/html/test"
  43. DEPLOY_HTTP_HOST = "ci.una.io"
  44. DEPLOY_HTTP_PATH = "/test/"
  45. DEPLOY_DOC_ROOT = "/var/www/vhosts/mainsite/html"
  46. DEPLOY_DB_HOST = "127.0.0.1"
  47. DEPLOY_DB_NAME = "mainsite_una"
  48. DEPLOY_DB_USER = "mainsite_una"
  49. DEPLOY_DB_PWD = "*****"
  50. // Documentation parameters
  51. DOCS_OUTPUT = "/var/www/vhosts/mainsite/html/docs"
  52. // Environment flags
  53. UNA_HTTPS = "1"
  54. // Date stamp for package
  55. DSTAMP = sh(script: 'date +"%Y%m%d"', returnStdout: true).trim()
  56. ZIPFILE = "UNA-${DSTAMP}-${BUILD_NUMBER}.zip"
  57. // Demo users
  58. DEMO_USERS_PASS = "*****"
  59. DEMO_USERS_SALT = "*****"
  60. DEMO_S3_BUCKET = "una-ci"
  61. DEMO_S3_ENDPOINT = "us-east-1.linodeobjects.com"
  62. DEMO_S3_KEY = "*****"
  63. DEMO_S3_SECRET = "*****"
  64. DEMO_GOOGLE_MAPS_API_KEY = "*****"
  65. DEMO_MESSENGER_URL = "https://una.io:5443"
  66. DEMO_API_OAUTH_ID = "*****"
  67. DEMO_API_OAUTH_SECRET = "*****"
  68. DEMO_API_KEY = "*****"
  69. DEMO_SOCKETS_URL = "https://ci.una.io"
  70. DEMO_SOCKETS_APP_ID = "neo-ci"
  71. DEMO_SOCKETS_KEY = "neo-ci"
  72. DEMO_SOCKETS_SECRET = "*****"
  73. TEST_USER_ADMIN_PWD = "*****"
  74. TEST_USER_REGULAR_PWD = "*****"
  75. TEST_USER_TOM_PWD = "*****"
  76. }
  77. stages {
  78. stage('Prepare & worspace clean up') {
  79. steps {
  80. script {
  81. echo "Cleaning workspace before proceeding"
  82. // Clean up workspace
  83. cleanWs(
  84. cleanWhenAborted: true,
  85. cleanWhenFailure: true,
  86. cleanWhenNotBuilt: true,
  87. cleanWhenSuccess: true,
  88. cleanWhenUnstable: true,
  89. deleteDirs: true,
  90. disableDeferredWipeout: true
  91. )
  92. echo "NodeJS version:"
  93. sh 'node -v'
  94. echo "Starting UNA CI/CD Pipeline"
  95. echo "Build Number: ${BUILD_NUMBER}"
  96. echo "Package File: ${ZIPFILE}"
  97. }
  98. }
  99. }
  100. stage('Clone UNA repository') {
  101. steps {
  102. git branch: 'master',
  103. url: 'https://github.com/unacms/UNA/',
  104. credentialsId: '0043feeb-9df7-4494-9b2b-d6c8a1b8ca08'
  105. }
  106. }
  107. stage('Clone additional UNA modules') {
  108. parallel {
  109. stage('Clone: Media Manager') {
  110. steps {
  111. dir('modules/boonex/media') {
  112. git branch: 'master',
  113. url: 'git@github.com:unacms/media-manager.git',
  114. credentialsId: '0043feeb-9df7-4494-9b2b-d6c8a1b8ca08'
  115. }
  116. }
  117. }
  118. stage('Clone: Messenger') {
  119. steps {
  120. dir('modules/boonex/messenger') {
  121. git branch: 'master',
  122. url: 'git@github.com:unacms/jot-client-una.git',
  123. credentialsId: '0043feeb-9df7-4494-9b2b-d6c8a1b8ca08'
  124. }
  125. }
  126. }
  127. }
  128. }
  129. stage('Install dependencies') {
  130. steps {
  131. script {
  132. echo "Cleaning some files"
  133. sh '${PHP} ${PHING} clean_install'
  134. echo "Install PHP and JS dependences"
  135. sh '${PHP} ${PHING} -verbose -Dphp=${PHP} -Dcomposer=${COMPOSER_PHAR} prepare'
  136. }
  137. }
  138. }
  139. stage('Test installation') {
  140. steps {
  141. script {
  142. echo "Setting up permissions"
  143. sh '${PHP} ${PHING} change_permissions'
  144. echo "Performing installation for testing"
  145. sh '''
  146. ${PHP} ${PHING} -verbose -debug \
  147. -Dphp=${PHP} \
  148. -Ddb_name=${INSTALL_DB_NAME} \
  149. -Ddb_user=${INSTALL_DB_USER} \
  150. -Ddb_password=${INSTALL_DB_PWD} \
  151. -Dmodules="${MODULES}" \
  152. install
  153. '''
  154. }
  155. }
  156. }
  157. stage('Unit tests') {
  158. steps {
  159. script {
  160. echo "Running PHP Unit Tests"
  161. def status = sh(
  162. script: "${PHP} tests/vendor/bin/phpunit -c tests/phpunit.xml",
  163. returnStatus: true
  164. )
  165. echo "PHPUnit exit code: ${status}"
  166. }
  167. }
  168. post {
  169. always {
  170. // Publish test results if they exist
  171. archiveArtifacts artifacts: 'logs/junit.xml', allowEmptyArchive: true
  172. junit checksName: 'Unit-Tests', testResults: 'logs/junit.xml'
  173. }
  174. }
  175. }
  176. stage('Code Quality Analysis') {
  177. steps {
  178. script {
  179. echo "Running PHP Copy Paste Detector"
  180. sh '''
  181. find . -type d -name updates -prune -o \
  182. -type d -name plugins -prune -o \
  183. -type d -name plugins_public -prune -o \
  184. -type d -name cache -prune -o \
  185. -type d -name cache_public -prune -o \
  186. -type d -name storage -prune -o \
  187. -type d -name logs -prune -o \
  188. -type d -name tmp -prune -o \
  189. -type d -name samples -prune -o \
  190. -type d -path './tests/vendor' -prune -o \
  191. -type d -path './upgrade' -prune -o \
  192. -type d -path './modules/boonex/membership_pricing' -prune -o \
  193. -type f -name '*.php' ! -name 'header.inc.php' ! -name 'BxTemplStudioFormsField.php' ! -name 'config.php' \
  194. -print | xargs -r ${PHP} -d memory_limit=512M /usr/bin/phpcpd.phar --min-lines 6 --log-pmd logs/phpcpd.xml || true
  195. '''
  196. echo "Checking log files:"
  197. sh 'ls -la logs/'
  198. }
  199. }
  200. post {
  201. always {
  202. // Archive code quality reports
  203. // archiveArtifacts artifacts: 'logs/phpcpd.xml', allowEmptyArchive: true, followSymlinks: false
  204. recordIssues(
  205. sourceCodeRetention: 'LAST_BUILD',
  206. tools: [cpd(pattern: 'logs/phpcpd.xml')]
  207. )
  208. }
  209. }
  210. }
  211. stage('Package Build') {
  212. steps {
  213. script {
  214. echo "Creating package: ${ZIPFILE}"
  215. sh '${PHP} ${PHING} -Dpackage_file=${ZIPFILE} package'
  216. sh 'cp "packages/${ZIPFILE}" ${PACKAGE_PUBLISH_DIR}'
  217. // Update latest build info
  218. sh 'echo ${ZIPFILE} > ${PACKAGE_PUBLISH_LATEST_FILE}'
  219. sh 'date +"%Y.%m.%d" > ${PACKAGE_PUBLISH_LATEST_DATE}'
  220. sh 'echo ${BUILD_NUMBER} > ${PACKAGE_PUBLISH_LATEST_BUILD}'
  221. }
  222. }
  223. post {
  224. success {
  225. archiveArtifacts artifacts: "packages/${ZIPFILE}", fingerprint: true
  226. }
  227. }
  228. }
  229. stage('Deployment') {
  230. steps {
  231. script {
  232. echo "Cleaning up test site"
  233. sh '"${DEPLOY_PATH}/../../cleanup_test_site.sh"'
  234. echo "Deploying to test environment"
  235. sh '''
  236. ${PHP} ${PHING} \
  237. -Dphp=${PHP} \
  238. -Ddeploy_path=${DEPLOY_PATH} \
  239. -Dhttp_host=${DEPLOY_HTTP_HOST} \
  240. -Dhttp_path=${DEPLOY_HTTP_PATH} \
  241. -Ddoc_root=${DEPLOY_DOC_ROOT} \
  242. -Ddb_name=${DEPLOY_DB_NAME} \
  243. -Ddb_host=${DEPLOY_DB_HOST} \
  244. -Ddb_user=${DEPLOY_DB_USER} \
  245. -Ddb_password=${DEPLOY_DB_PWD} \
  246. -Dmodules="${MODULES}" \
  247. deploy
  248. '''
  249. }
  250. }
  251. }
  252. stage('Database Configuration') {
  253. steps {
  254. script {
  255. echo "Configuring database for UI tests"
  256. // Maps API key
  257. executeDbQuery(
  258. "UPDATE sys_options SET value = '${DEMO_GOOGLE_MAPS_API_KEY}' WHERE name = 'sys_maps_api_key'",
  259. "Setting Google Maps API key"
  260. )
  261. // Messenger server
  262. executeDbQuery(
  263. "UPDATE sys_options SET value = '${DEMO_MESSENGER_URL}' WHERE name = 'bx_messenger_server_url'",
  264. "Configuring messenger server URL"
  265. )
  266. // Create test users
  267. echo "Creating test users"
  268. def users = ['user', 'lily', 'eva', 'linda', 'emma', 'karen', 'ella', 'viky', 'mila', 'eric', 'luck', 'jack', 'rob', 'dave', 'tom', 'andy', 'ivan']
  269. users.each { user ->
  270. executeDbMultiQuery("""
  271. SET sql_mode = '';
  272. INSERT INTO sys_accounts (name, email, email_confirmed, receive_updates, receive_news, password, salt, role, added) VALUES
  273. ('${user}', '${user}@example.com', 1, 0, 0, SHA1(CONCAT(MD5('${DEMO_USERS_PASS}'), '${DEMO_USERS_SALT}')), '${DEMO_USERS_SALT}', 1, UNIX_TIMESTAMP());
  274. SET @iAccountId = LAST_INSERT_ID();
  275. INSERT INTO sys_profiles (account_id, type, content_id, status) VALUES
  276. (@iAccountId, 'system', @iAccountId, 'active');
  277. """, "Creating user: ${user}")
  278. }
  279. // Storage configuration
  280. echo "Configuring S3 storage"
  281. executeDbQuery(
  282. "UPDATE sys_options SET value = '${DEMO_S3_KEY}' WHERE name = 'sys_storage_s3_access_key'",
  283. "Setting S3 access key"
  284. )
  285. executeDbQuery(
  286. "UPDATE sys_options SET value = '${DEMO_S3_SECRET}' WHERE name = 'sys_storage_s3_secret_key'",
  287. "Setting S3 secret key"
  288. )
  289. executeDbQuery(
  290. "UPDATE sys_options SET value = '${DEMO_S3_BUCKET}' WHERE name = 'sys_storage_s3_bucket'",
  291. "Setting S3 bucket name"
  292. )
  293. executeDbQuery(
  294. "UPDATE sys_options SET value = '${DEMO_S3_ENDPOINT}' WHERE name = 'sys_storage_s3_endpoint'",
  295. "Setting S3 endpoint"
  296. )
  297. executeDbQuery(
  298. "UPDATE sys_objects_storage SET engine = 'S3'",
  299. "Switching storage engine to S3"
  300. )
  301. // SEO URI rewrites
  302. executeDbQuery(
  303. "INSERT INTO sys_seo_uri_rewrites SET uri_orig = 'view-persons-profile', uri_rewrite = 'u'",
  304. "Adding person profile URL rewrite"
  305. )
  306. executeDbQuery(
  307. "INSERT INTO sys_seo_uri_rewrites SET uri_orig = 'view-organization-profile', uri_rewrite = 'o'",
  308. "Adding organization profile URL rewrite"
  309. )
  310. // Enable subgroups
  311. executeDbQuery(
  312. "UPDATE sys_objects_privacy SET spaces = 'bx_events,bx_spaces,bx_groups' WHERE object = 'bx_groups_allow_view_to'",
  313. "Enabling subgroups functionality"
  314. )
  315. }
  316. }
  317. }
  318. stage('API Configuration') {
  319. parallel {
  320. stage('Old API Setup') {
  321. steps {
  322. script {
  323. echo "Configuring old OAuth API"
  324. executeDbMultiQuery("""
  325. INSERT INTO bx_oauth_clients (title, client_id, client_secret, redirect_uri, grant_types, scope, parent_id, user_id) VALUES
  326. ('nextjs', '${DEMO_API_OAUTH_ID}', '${DEMO_API_OAUTH_SECRET}', 'http://app.una.io:3000/', NULL, 'service', 0, 1);
  327. SET @iClientId = LAST_INSERT_ID();
  328. INSERT INTO bx_oauth_allowed_origins (client_id, url) VALUES
  329. (@iClientId, 'https://ci.una.io:3000');
  330. """, "Setting up OAuth client and allowed origins")
  331. }
  332. }
  333. }
  334. stage('New API Setup') {
  335. steps {
  336. script {
  337. echo "Configuring new API system"
  338. executeDbMultiQuery("""
  339. INSERT INTO sys_api_keys (title, `key`, `order`) VALUES
  340. ('NextJS', '${DEMO_API_KEY}', 1);
  341. INSERT INTO sys_api_origins (url, `order`) VALUES
  342. ('neo://app', 1),
  343. ('http://localhost:3000', 2),
  344. ('https://neo.vercel.app/', 4),
  345. ('https://neo.so/', 5),
  346. ('http://hihi.com', 6),
  347. ('https://hihi.com', 7),
  348. ('https://neo-ci.una.io', 8);
  349. UPDATE sys_options SET value = 'on' WHERE name = 'sys_api_enable';
  350. UPDATE sys_options SET value = 'on' WHERE name = 'sys_api_access_by_origin';
  351. UPDATE sys_options SET value = 'on' WHERE name = 'sys_api_access_by_key';
  352. UPDATE sys_options SET value = 'on' WHERE name = 'sys_session_auth';
  353. """, "Setting up new API keys, origins and enabling API features")
  354. }
  355. }
  356. }
  357. stage('Sockets Server Setup') {
  358. steps {
  359. script {
  360. echo "Configuring sockets server"
  361. executeDbMultiQuery("""
  362. UPDATE sys_options SET value = 'sys_sockets_soketi' WHERE name = 'sys_sockets_type';
  363. UPDATE sys_options SET value = '${DEMO_SOCKETS_URL}' WHERE name = 'sys_sockets_url';
  364. UPDATE sys_options SET value = '${DEMO_SOCKETS_APP_ID}' WHERE name = 'sys_sockets_app_id';
  365. UPDATE sys_options SET value = '${DEMO_SOCKETS_KEY}' WHERE name = 'sys_sockets_key';
  366. UPDATE sys_options SET value = '${DEMO_SOCKETS_SECRET}' WHERE name = 'sys_sockets_secret';
  367. """, "Configuring Soketi sockets server settings")
  368. }
  369. }
  370. }
  371. }
  372. }
  373. stage('UI Tests') {
  374. steps {
  375. timeout(time: 6, unit: 'MINUTES') {
  376. script {
  377. echo "Creating cache directories"
  378. sh '''
  379. mkdir -p -m 0777 "${DEPLOY_PATH}/cache/CSS"
  380. mkdir -p -m 0777 "${DEPLOY_PATH}/cache/HTML"
  381. mkdir -p -m 0777 "${DEPLOY_PATH}/cache/URI"
  382. '''
  383. dir('una-ui-test3') {
  384. echo "Clone repository with tests"
  385. git branch: 'main',
  386. url: 'git@github.com:unacms/una-ui-test3.git',
  387. credentialsId: '0043feeb-9df7-4494-9b2b-d6c8a1b8ca08'
  388. // Create environment file
  389. sh '''
  390. cat > .env.local << EOF
  391. baseUrl=https://${DEPLOY_HTTP_HOST}${DEPLOY_HTTP_PATH}
  392. userAdminPwd=${TEST_USER_ADMIN_PWD}
  393. userRegularPwd=${TEST_USER_REGULAR_PWD}
  394. userTomPwd=${TEST_USER_TOM_PWD}
  395. EOF
  396. '''
  397. echo "Install dependences for UI tests"
  398. sh 'npm install'
  399. sh 'npx playwright install'
  400. echo "Running Playwright UI Tests"
  401. sh 'npm run test'
  402. }
  403. }
  404. }
  405. }
  406. post {
  407. always {
  408. // Archive test results
  409. archiveArtifacts artifacts: 'una-ui-test3/results/results.xml', allowEmptyArchive: true
  410. junit checksName: 'UI-Tests', testResults: 'una-ui-test3/results/results.xml'
  411. }
  412. }
  413. }
  414. // end
  415. }
  416. post {
  417. /*
  418. always {
  419. xunit checksName: 'UI-Tests', thresholds: [
  420. failed(failureNewThreshold: '0', failureThreshold: '0', unstableNewThreshold: '0', unstableThreshold: '0')
  421. ],
  422. tools: [
  423. JUnit(excludesPattern: '', pattern: 'una-ui-test3/results/results.xml', stopProcessingIfError: true)
  424. ]
  425. }
  426. */
  427. success {
  428. echo 'UNA CI/CD Pipeline completed successfully!'
  429. }
  430. failure {
  431. echo 'UNA CI/CD Pipeline failed!'
  432. script {
  433. emailext(
  434. subject: "Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
  435. body: "Build failed!\nCheck console output at ${env.BUILD_URL}\n\nCommit author: ${env.CHANGE_AUTHOR ?: 'unknown'}",
  436. to: "${env.CHANGE_AUTHOR_EMAIL}"
  437. )
  438. slackSend(
  439. botUser: true,
  440. channel: '#unacms',
  441. color: '#ff4444',
  442. iconEmoji: '👮',
  443. message: "Build failed!\nCheck console output at ${env.BUILD_URL}\n\nCommit author: ${env.CHANGE_AUTHOR ?: 'unknown'}",
  444. notifyCommitters: true,
  445. tokenCredentialId: 'slack-jenkins-bot-token'
  446. )
  447. }
  448. }
  449. unstable {
  450. echo 'UNA CI/CD Pipeline completed with test failures!'
  451. }
  452. }
  453. }