RSS
 

Posts Tagged ‘CI’

CakePHP 3.6 is coming

14 Mar

The first betas have landed already. Make your plugins and apps 3.6-ready now.
It is actually rather easy – better to be proactive now and have a small migration path than a big fallout and some surprises in a few weeks or months.

Prepare your Application

First read the 3.6 migration guide to get a grasp of the upcoming changes.
Deprecations ignored (easy ones, more later), there are not too many changes – especially no heavy ones. All in all this should be a very smooth upgrade from previous 3.x versions.

Now for the actual upgrade we have a few important issues to talk about:

Regressions

3.6 might introduce accidental regressions or issues that your app can not deal with. Those should be either fixed or outlined in the documentation and migration guide.
You can spend a few minutes here to find out early on how compatible your app is to a new minor version. Execute

composer require cakephp/cakephp:"dev-3.next as 3.5.14"

if you want to check the latest 3.6 version faked as 3.5 (due to the constraints). You can of course try 3.6 here as aliases, as well (as long as no constraints locks it down to ~3.5.x).

Note: For some plugins there might be already a 3.next branch (or even tags), as well.

Report or PR any issues you find and help to make the 3.6 release as smooth and BC as possible.

Migration path

Always keep up with minors in the framework. So at this point I expect everyone to be already running on CakePHP 3.5 for a while.

You can already have fully removed any deprecation of 3.4 or 3.5, which will further make the migration path smaller and easier to review.
Since 3.6 will trigger actual deprecation notices, this is really advised to do early on.
Once you actually switch to 3.6 you can actually silence the deprecations for the first run to verify functional compatibility by setting the error level in your app.php configs:

    'errorLevel' => E_ALL & ~E_USER_DEPRECATED,

Read the 3.6 migration guide for any functional upgrade that might be necessary, those should be applied first.

Once you established functional BC, you can start replacing all deprecations and afterwards again all tests should pass.

Tip: Try not use the above silence as a production default if possible. Instead try to remove those asap. But if not all plugins are updated yet, or do you don’t have the time to fix all of them right now, it is acceptable to just keep the deprecations silenced for the time being.

Migration tooling

Until now there was the CakePHP Upgrade tool which really helped a lot for the migration.
And it still does for a small subset, e.g. upgrade skeleton syncs the skeleton files from the latest cakephp/app repo into your app.
So it might not be completely dead just yet. If you are even below 3.5, definitely check it out.

But regex can only do so much. For more complex issues and especially all the deprecations there are now more modern and less error prone solutions available.
There is a very useful PHP tool that can help with deprecation replacement called rector.
It introspects the code and can very reliably identify and replace code pieces.

I tried it and using

composer require --dev bamarni/composer-bin-plugin
composer bin rector require --dev rector/rector
// lets dry-run for src/ directory
vendor/bin/rector process src/ --level cakephp36 --dry-run

it will show you what it would change as diff. Without --dry-run it will then actually run the upgrade. Make sure you committed all files or made a backup.

Tip: If it takes too long, just run it on each subfolder, e.g. src/Model, …

You can take a look at its CakePHP config. If there is anything missing, you can PR (pull request) your additions.

Plugins/Ecosystem

If you encounter issues with plugins or alike, open a ticket or better yet a pull request to get these things fixed ASAP.
This should ideally also be done pro-actively before you actually start upgrading your code-base, so that there is no time-bottleneck.

In case there really is something stuck, you can make a fork and use a tmp branch of master on your side for the upgrade.
Your composer.json would then point to dev-master of your repository fork and contain a repositories link so that composer knows to look there:

  "repositories": [
    {
      "type": "git",
      "url": "git@github.com:your-name/plugin-name.git"
    }
  ],

Once there is a tag with the fixed code in that original repository, you can remove and use the tagged release again.

Prepare your Plugins

This actually brings us the next point – if you are maintainer of a plugin yourself.

Having so many deprecations throughout 3.x it becomes more and more difficult to keep track of when methods were added and since what version you can use them, or not use others anymore.
With 3.6 this is more important than ever since this now adds deprecation warnings emitted for deprecated usage.
So make sure that if you start supporting 3.6 for your plugins, that your composer.json constraint towards cakephp/cakephp reads ^3.6. Users need to know if they can pull your version without the application breaking.

First of all: Make sure you support 7.2 in your Travis test matrix. There are quite some changes hidden in count() usage that can easily break your code or at least emit warnings.

Secondly, make sure you got also older PHP and CakePHP versions properly regression tested.
I started to actually add the following trick into my plugins:

matrix:
  include:
    ...
    - php: 5.6
      env: PREFER_LOWEST=1

Then exchange the first before_script line composer install with

before_script:
  - if [[ $PREFER_LOWEST != 1 ]]; then composer install --prefer-source --no-interaction ; fi
  - if [[ $PREFER_LOWEST == 1 ]]; then composer update --prefer-dist --no-interaction --prefer-lowest --prefer-stable ; fi

This will make sure, composer uses the lowest possible versions everywhere.
You will now spot any wrong usage of methods right in your tests in Travis – if those are covered by tests, that it.
That’s why I think it is more valuable that the tests execute the code than having good asserts. Sure, the latter is also needed in some cases. But the mere fact that the code executes fine means already a lot, from syntax issues (based on the PHP changes even in minor versions) and interface issues to other more runtime issues.

A full working version can be seen e.g. in TinyAuth .travis.yml.

Try to support 3.5 and 3.6

It is usually best to not always jump right to the latest minor as minimum requirement. This makes it harder for folks on the previous minor to get patches and bugfixes.
Thus, with the new deprecation warnings, you might have to do the following if you use any deprecated functionality because of this:

Add <ini name="error_reporting" value="16383"/> into your phpunit.xml.dist file (see here for example).

Similar things can be required for tooling like PHPStan. The linked IdeHelper plugins also shows a solution for this using a shim.php file that contains

error_reporting(E_ALL & ~E_USER_DEPRECATED);

Icing on the cake

You want to go even a step further and get alerted early on if your plugin or app breaks with new minors or patches?

Most of the time I encountered CI fails and BC issues months after when I applied a new change to a plugin and suddenly tests that should have passed didn’t pass anymore.
In this case you can see that the core way of building a query has been changed and now has less () brackets in 3.5+. Notified about this I can just make the tests conditional on the CakePHP version and all is good again.

It is rather easy to enable a cronjob in CI (e.g. Travis) that runs nightly or weekly and alerts you of any failures of new releases.
This is an example of how to enable for Open Source plugins in Travis:

 
1 Comment

Posted in CakePHP

 

Continuous Integration with Jenkins

04 Mar

CI with Jenkins and GitHub is especially interesting for private repositories, as CI with Travis is mainly for free GitHub repos (unless you have Travis Pro, of course).
But since Jenkins is OpenSource and free, it might make sense to set up this on your server and run CI directly in-house.
It integrates nicely with GitHub. So the following tutorial will focus on that example.

So for the beginning we have our example app in GitHub, e.g. a CakePHP app (optionally including composer dependencies).

The main goals are:

  • Continuous test results on each push (especially to master).
  • Automatic test results for each PR (very important when working in teams) – linked inside the PR just as Travis would.
  • Coverage being analysed to see where the weak points can be (classes with < 50% test case coverage).

Set Up Jenkins

This is probably the most difficult part of all.

We first set up Jenkins.

wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | apt-key add -
nano /etc/apt/sources.list
------------------------------
deb http://pkg.jenkins-ci.org/debian binary/
------------------------------
apt-get update
apt-get install jenkins

Check if Jenkins runs properly

http://hostname.domain.local:8080

Install plugins and all their dependencies

Let’s then include the plugins we need to integrate GitHub: "GitHub Pull Request Builder" and "GitHub plugin".
The first is especially interesting as it makes it possible to have Jenkins "build/pass" statuses directly in each PR (similar to Travis).

Access Data -> Global Access Data -> Add Access Data

Create access data for the repository you want to access. Provide username and password or keyfiles.

Manage Jenkins -> Configure System

  • Add your repositories under "Git"
  • Set "Manually manage hook URLs" under "GitHub Web Hook"
  • Generate token (https://github.com/settings/applications) with Github user which has access to the
    private repository and put the token under "GitHub pull requests builder"

Add Job

  1. Select "Free Style" project and provide a jenkins working space name
  2. Put your repository url (e.g. https://github.com/Name/repo) to "GitHub-Project" input field
  3. Select Git under "Source-Code-Management"
    Put your repository url again and select the access data from step 5 of this tutorial.
    Click on advanced settings and set "+refs/pull/:refs/remotes/origin/pr/" for Refspec and "origin" for name.
    Set also the branch specifier to "${sha1}"
  4. Select "Build when a change is pushed to GitHub" and "GitHub pull requests builder" under "Build trigger"
    Add persons or organizations to "GitHub pull requests builder" which are whitelisted for trigger an PR build request
  5. Add shell execution script for your test cases under build proceed (see below)
  6. Add "Set build status on GitHub commit" as post build action.

My shell execution script for the CakePHP apps looks like this:

cd /var/lib/jenkins/jobs/projectname/workspace/app
cp /var/www/projectname/app/Config/database.php Config/
mkdir -p tmp && chmod -R 777 tmp && rm -R tmp/*
cp /var/www/projectname/app/composer.phar ./ && php composer.phar update
Console/cake test app AllApp --stderr

It first navigates into the freshly pulled repo code and into the APP dir.
Then it copies over the database.php from the current staging website (could also be some other place where you store them).
It then creates tmp dirs and sets the correct permissions.
The composer.phar file is also copied over and executed to pull all composer dependencies.
At finally the AllApp (group) tests are executed – with –stderr in order for the session to work properly.
One can then also apply -q for silent (=none) output.

Setup GitHub hooks

To trigger the build process on Jenkins, we need to configure service hook on GitHub. In order to achieve this, navigate to GitHub repository settings and configure Jenkins Hook URL for GitHub plugin. The URL format http://<jenkins-username>:<jenkins-password>@<Elastic-IP-Address>:8080/github-webhook/

Running tests

Run first test manually

Click on "Build with Parameters" and hit "Build" and you will see the first manually triggered build in progress.
If all goes well the icon should be blue (pass) – not red (fail).
In case you need to see what is going on, check the "Console Output" of a specific build.

Check automated tests

Commit to master and see if a build is triggered. Also open a PR and check if there is a badge displayed with the current build status.

Sugar

Badge

To have a small "build status: passing" badge in our Readme, we can install the plugin "Embeddable Build Status".
It will create a new menu entry from where you can select your badges including ready-to-use Markdown syntax if needed.

And if it is a private repo you might also want to htaccess the Jenkins web interface or protect it some other way from the public.

Code Coverage

You can install the plugin "Clover PHP Plugin".
Create a folder "coverage" in your workspace.

Add this to the above command:

--log-junit ../coverage/unitreport.xml --coverage-html ../coverage --coverage-clover ../coverage/coverage.xml

And set up a "Publish Clover PHP Coverage Report" Post Build Action with the absolute path to this coverage.xml file in the workspace.
In my case just coverage/coverage.xml as it can be relative to the workspace, as well.
If you also want to publish the HTML coverage report (which is pretty neat), you can simply put coverage in there then.

Make sure you got Xdebug up and running (apt-get install php5-xdebug). It is a direct dependency here.
Note that using code coverage reports will slow the whole process down by "x3" at least. Usually that shouldn’t really make a difference, though.

In most cases you want to exclude few dirs. In case you are already using a phpunit.xml file which resides in your APP dir, you can easily add

<phpunit>
	<filter>
		<whitelist>
			<directory suffix=".php">*/Controller</directory>
			...
		</whitelist>
		<blacklist>
			...
		</blacklist>
	</filter>
</phpunit>

You can also just create this file and also copy it over to the jenkins workspace.

Memory Limit

If you have a project of medium to large size you will soon see the memory usage for the test coverage of 50%+ way above 200MB.
With more coverage and the project getter bigger, or some coverage report generated you will run out of memory with 256MB (which one would think should suffice).
Raise your limit to at least 512 for the PHPUnit testing. You can use the phpunit.xml file:

<phpunit>
	<php>
		<ini name="memory_limit" value="512M" />
	</php>
	...
</phpunit>

Cleanup

Use the plugin "Discard Old Build plugin" to keep your diskspace reasonable. Without it (especially with CodeCoverage) the disk space
will soon exceed several GB. We usually keep the last 40 builds.

More

This article goes more into detail about the installation of Jenkins and also how to use additional tools like CodeSniffer, MessDetector and CO using a build file and Ant. But I didn’t check that out yet.
There is also jenkins-php.org if you want to go all in 🙂

 
No Comments

Posted in Testing