Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save prondubuisi/ecce396ff463bf9321273667a7c7149c to your computer and use it in GitHub Desktop.
Save prondubuisi/ecce396ff463bf9321273667a7c7149c to your computer and use it in GitHub Desktop.
Quick and Dirty Intro to Civi CI

Quick and Dirty Intro to Civi CI

Background

I find it easiest to consider the stack in three layers:

  • Host Environment or System Platform: Loosely, this is the collection of MySQL, Apache, PHP, NodeJS, and so on. These system-services and language-runtimes are provided in many distribution channels (e.g. Debian, Ubuntu, CentOS, RHEL, nix, MAMP, XAMPP, Bitnami, Docker, etc).

  • Buildkit or Toolchain: Loosely, this is the collection of composer, drush, cv, wp-cli, bower, civibuild, phpunit, and so on. These are mostly CLI tools written in PHP, NodeJS, or bash. They are usually not provided by an OS distributor (or, if they are, it can be hard to get the right versions), and they don't represent standalone network services.

  • Build or Site: Loosely, this is the collection of civicrm-core.git along with a CMS (Drupal, WordPress, etc) and related modules/extensions/integrations. It is a web-app. When setting up a site with Civi (et al), this is the main artifact. (For example, dmaster is the typical name for a build with Drupal 7 and the master version of Civi.)

Now-a-days, many teams in our industry are focused on a single design for their deployments; they will standardize the stack and define a single artifact or project which encompasses all these layers (e.g. one Dockerfile or one docker-compose.yml). However, Civi can be deployed in a range of environments. This has always been part of its appeal: that you can install it on top of an existing webserver running Drupal/Joomla/WordPress/etc. Each of those existing webservers has its own host-environment. Moreover, each developer who works with one of those existing webservers is likely to have a laptop with its own host-environment. As an ecosystem, we don't have the same degree of standardization that you'd find on a typical team.

The three tier analogy is not perfect, but it should help you get started by illustrating the design/scope of civicrm-buildkit.git: you can start with just about any host-environment (Debian, MAMP, et al), setup buildkit, and then produce a representative range of example sites (plain D7; D7+Civi master; D7+Civi 5.13; plain WP; WP+Civi master; etc).

Quick start on a test server

The official CI servers include a few host environments based on bknix, e.g.

  • bknix-min: This has the minimum versions of MySQL/PHP/NodeJS/etc supported by Civi.
  • bknix-max: This has the maxiumum versions of MySQL/PHP/NodeJS/etc supported by Civi.
  • bknix-dfl: This has middle-of-the-road versions of MySQL/PHP/NodeJS/etc.

Additionally, we have a copy of buildkit for each host-environment.

Let's do an example: we'll use the minimum host-environment to create a test site (Drupal 7 with Civi 5.13) and run all the test suites.

Connect to a test server. Login as Jenkins.

$ ssh myuser@35.246.100.181
myuser@test-gsoc19:~$ sudo -iu jenkins bash
jenkins@test-gsoc19:~$

At this point, the command line does not have access to PHP, MySQL, etc.

jenkins@test-gsoc19:~$ php --version
bash: php: command not found

jenkins@test-gsoc19:~$ node --version
bash: node: command not found

jenkins@test-gsoc19:~$ mysql --version
bash: mysql: command not found

We need to specify that we want to work with bknix-min:

jenkins@test-gsoc19$ eval $( use-bknix min )
[bknix-min:~]

Now, we have access all the minimum versions. Observe that the host-environment lives at /nix/var/nix/profiles/bknix-min.

[bknix-min:~] which php
/nix/var/nix/profiles/bknix-min/bin/php
[bknix-min:~] php --version
PHP 5.6.38 (cli) (built: Sep 11 2018 23:19:50)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
    with Xdebug v2.3.1, Copyright (c) 2002-2015, by Derick Rethans

[bknix-min:~] which node
/nix/var/nix/profiles/bknix-min/bin/node

[bknix-min:~] node --version
v8.11.3

[bknix-min:~] which mysql
/nix/var/nix/profiles/bknix-min/bin/mysql

[bknix-min:~] mysql --version
mysql  Ver 14.14 Distrib 5.5.58, for Linux (x86_64) using readline 5.1

Additionally, this server also has buildkit. Observe that this lives in /home/jenkins/bknix-min/civicrm-buildkit:

[bknix-min:~] which cv
/home/jenkins/bknix-min/civicrm-buildkit/bin/cv

[bknix-min:~] cv --version
cv version v0.2.15

[bknix-min:~] which drush
/home/jenkins/bknix-min/civicrm-buildkit/bin/drush

[bknix-min:~] drush --version
 Drush Version   :  8.1.18

Create a site with Drupal 7 and CiviCRM 5.13. We'll use civibuild to setup the site:

[bknix-min:~] civibuild create mytest --type drupal-clean --civi-ver 5.13
[[Download mytest (type 'drupal-clean' in '/home/jenkins/bknix-min/build/mytest')]]
[[Update caches]]

...(skip a lot of text)...

[[Save CMS DB (mytestcms_umnfa) to file (/home/jenkins/bknix-min/civicrm-buildkit/app/snapshot/mytest/cms.sql.gz)]]
[[Save Civi DB (mytestcivi_ukibu) to file (/home/jenkins/bknix-min/civicrm-buildkit/app/snapshot/mytest/civi.sql.gz)]]
[[Setup MySQL for Test]]
[[Restore "/home/jenkins/bknix-min/build/mytest" DB (test) from file (/home/jenkins/bknix-min/civicrm-buildkit/app/snapshot/mytest/civi.sql.gz)]]
[[Show site summary (mytest/default)]]
 - CMS_ROOT: /home/jenkins/bknix-min/build/mytest
 - CMS_URL: http://mytest.35.246.100.181.nip.io:8002
 - CMS_DB_DSN: mysql://mytestcms_umnfa:knLQPCs76YhIqlhI@127.0.0.1:3308/mytestcms_umnfa?new_link=true
 - CIVI_DB_DSN: mysql://mytestcivi_ukibu:d1wHlS1fnmFJLyW7@127.0.0.1:3308/mytestcivi_ukibu?new_link=true
 - TEST_DB_DSN: mysql://mytesttest_304ck:rno6MPYARGlEUrxp@127.0.0.1:3308/mytesttest_304ck?new_link=true
 - ADMIN_USER: admin
 - ADMIN_PASS: wfJLUdcwvEIi
 - DEMO_USER: demo
 - DEMO_PASS: demo

Now, run the test suites. As a developer, it's usually better to call a specific tool (like phpunit5); but for CI purposes, we often use the wrapper script civi-test-run, as in:

[bknix-min:~] civi-test-run -b mytest -j /tmp/mytest-results all

The results will be recorded in JUnit XML format under /tmp/mytest-results.

Jenkins?

https://test.civicrm.org/ is the Jenkins master. Most of the important/relevant test jobs are based on some combination of civibuild and civi-test-run. For example:

  • CiviCRM-Core-PR: Uses civibuild to create a site with a proposed patch/PR, and then it calls civi-test-run.
  • CiviCRM-Core-Matrix: Uses civibuild to create a matrix of test sites (with different versions of Civi and different host-environments), and it calls civi-test-run for each.

These jobs should be fairly thin. For example, consider this snapshot of the CiviCRM-Core-PR job:

  • It doesn't call composer or cv or drush or wget -- it calls civibuild.
  • It doesn't call phpcs or jshint -- it calls civilint
  • It doesn't call phpunit5 or karma -- it calls civi-test-run.

This is no accident; it's an adaptation. Jenkins is a brilliant task-runner, but the stock Jenkinx 1.x experience encourages you to write jobs in a way which is difficult to test/reproduce/modify (i.e. all the configuration is done by clicking around the web UI). This makes it harder to copy/share/test revisions. Instead, we move most of the important logic+policy out Jenkins and into civicrm-buildkit.git. This way, one can clone the repo and patch/test the build scripts locally.

For more depth...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment