PHP
Article

Installing and Securing Jenkins

By Peter Nijssen

PHP Quality Assurance with Jenkins

Earlier this year, I wrote an article about PHP-CI, which you can use as a continuous integration tool for your PHP projects. Within this article I indicated I still liked Jenkins the most as a CI tool. Time to dive into Jenkins and see how we can set this up for our PHP project.

Introduction to Jenkins

The list of things that Jenkins has to offer is huge due to the rich plugin system it has. Basically, Jenkins is just a tool which connects all kinds of different tools and plugins together to create a report for you. For example, it can run PHPUnit and show you the results in a graph over time. It can check your PHP code for errors by running php -l. However, you can also let Jenkins build a project and output a zip file, which you can use to deploy your application. The advantage of this is that you get a complete zip file back to upgrade your production application without having to run tools like Composer or NPM on your production servers.

Jenkins can be used for many different applications. You can use it for web applications written in PHP, but also for software and mobile applications written in Java or any other language. This makes Jenkins a very versatile tool and very interesting for companies handling many different projects.

Because Jenkins has so many options and possibilities, it looks overwhelming for people to start using it. Within this series of articles, we will slowly work our way into Jenkins. We will start with the installation and setup. After that we will continue with analyzing a project. In the end, we will take a close look at numerous other plugins we can use to check the quality of our product.

We will mainly focus on the quality of the PHP code, but in the end we will also take a short look at analyzing our HTML, CSS and JavaScript which work in close harmony with PHP.

Installing Jenkins

You can install Jenkins on any popular operating system. For this article, we are going to install Jenkins on Ubuntu Server 14.04 LTS. If you are using a different operating system, you can check here for the installation instructions per operating system. To install Jenkins on Ubuntu the easiest way would be by running this command.

sudo apt-get install jenkins

However, you are now installing Jenkins which is located in Ubuntu’s repository. Jenkins itself suggests to install directly from the Jenkins repositories. First, we add the key to our system for the repository.

wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -

Now we run the following command to add Jenkins to the sources.list file.

deb http://pkg.jenkins-ci.org/debian binary/

Now we are ready to install Jenkins.

sudo apt-get update
sudo apt-get install jenkins

When finished, Jenkins is successfully installed on your server.

You can now reach Jenkins through your browser by adding port 8080 to the end of the URL. So imagine my server is named ci.myserver.com you can reach Jenkins on ci.myserver.com:8080.

Jenkins.

Securing Jenkins

The first thing you should notice is that Jenkins is completely open. Anyone now going to this URL can do all kinds of different tasks, so the first thing we are going to do is secure it. We start by clicking on Manage Jenkins in the sidebar menu. where we will be greeted by a notification from Jenkins, recommending to secure the CI server.

Jenkins

Click the Setup Security button to continue. On the next page, mark enable security checkbox as checked. The following steps are crucial to follow correctly, because it’s easy to lock yourself out of Jenkins. If this happens, you can follow these steps to gain access again.

The first thing you need to do, is set your security realm. The easiest one is to have Jenkins have his own user database by marking the checkbox in front of Jenkins' own user database. Make sure the allow users to sign up option is also enabled. Now save your configuration by clicking save.

You should now be able to see a sign up link at the top right corner of the page. Click this link and fill in the form to sign up. When done, log in with your newly created account.

Go back to the security settings by clicking Manage Jenkins and then clicking Configure Global Security. You should now uncheck the allow users to sign up checkbox to make sure no new accounts can be created without your permission.

Next we are going to set the authorization. The best options for you are either matrix based security or Project-based Matrix Authorization Strategy. This allows you to set per user which actions they can and cannot perform. If you choose the latter option, you will be able to even set this per project. For now, I am going to pick the matrix based security.

A table with permissions is now displayed. You will only see one table row in which you can define what an anonymous user can do. However, we want to set permissions for our own user account. So fill in your username in the box below and click Add. To make sure you have access to everything, you can check them all.

In the end, this is what it looks like.

Jenkins

Note: Be careful with capitalized letters. Peter and peter are 2 different user accounts, so make sure you fill in the correct username in both cases else you will be locked out of the system.

When saving, you will automatically get logged out. Note that you don’t see anything anymore except a login form. After logging in, you should have access to everything like before. If you get a permission error, you made a mistake and you either have to change the settings or you got yourself locked out of Jenkins. In the latter case, see the link above.

Preparing Jenkins

So far, we have been busy installing and securing Jenkins. Now it’s time to configure Jenkins so we can start building and analyzing our PHP projects. Sebastian Bergmann created an excellent website showing you how to set up Jenkins for PHP. We are going to do the same steps as documented, but in a slightly different order.

Installing plugins

First we need to install several plugins into Jenkins. Plugins are small extensions to Jenkins which can perform tasks for you like analyzing a report and converting it to a graph. We will need the following list of plugins to get started.

  • checkstyle
  • cloverphp
  • crap4j
  • dry
  • htmlpublisher
  • jdepend
  • plot
  • pmd
  • violations
  • xunit

We are going to install these plugins through the interface. If you feel more experienced or comfortable with the command line, you can check out this documentation to see how to install them via the command line. Also, that page will give you a short explanation about what every plugin will do.

Within Jenkins, go to Manage Jenkins and then to Manage Plugins. Click the tab named available. Here you will see a complete list of all available plugins. With the filter at the top right corner, you can quickly search for every plugin. Make sure you install the complete list of the plugins mentioned above by checking the checkboxes.

Jenkins

Check the checkbox in front of Restart Jenkins when installation is complete and no jobs are running to restart Jenkins when everything is done. After the restart, all the required plugins have been installed correctly.

Creating a template

When Jenkins is set up, we normally start by creating a project, sometimes called a job. A project is typically one PHP project you got. Within a project, you will have several builds. You can configure Jenkins to analyze (build) your code at certain moments. This can be scheduled intervals or on pull requests from contributors, for example. So a project contains several builds over time, each being a separate analysis of your code. Thanks to the plugins we installed, you will be able to see the results of those builds in nice graphs. Within these graphs you can quickly spot if a certain build increased or decreased the quality of your project. We can also see where we need to improve our code according to all of our tools.

We could now create a new project. However, we also would need to configure this project. For example, we could configure that each time PHPUnit is run, a code coverage page should be created so we can see which code is tested and which code is untested. There’s a lot of tests we would need to configure on a new project, and this would likely be time consuming. Luckily, Sebastian Bergmann also created a template which we’re going to use.

We need to open up the command line to be able to install this template. The easiest way would be to download the CLI tool Jenkins provided. The problem however is the fact that we secured our Jenkins installation earlier. This would mean we have to open up security back to anonymous or connect public/private key to our account.

In this case, we are going for an alternative method of installing the template. First, log in as user Jenkins on the command line and go to the home directory.

sudo su jenkins
cd ~

Within the home directory, go into the jobs directory and create a new directory named php-template.

cd jobs
mkdir php-template

Now we are going to enter this directory and add the configuration file.

cd php-template
wget https://raw.github.com/sebastianbergmann/php-jenkins-template/master/config.xml

Now, we need Jenkins to reload its configuration. You can do this by going back to the web interface, click Manage Jenkins and click on Reload Configuration from Disk. Jenkins will now reload its configuration. When done, you should see the php-template project on the main overview page.

Jenkins

Jenkins is now ready. We can create new projects based on this template.

Conclusion

Within this article we installed, secured and prepared Jenkins to start analyzing our PHP project. In the next part, we will add our project, prepare it, and of course analyze it.

  • SixKnight

    Part 4 of 1? ;)

    • http://www.bitfalls.com/ Bruno Skvorc

      Sorry about that. Happens sometimes. Fixed.

  • jerrac

    Thanks for posting this. I’ve been looking into setting up a CI server, Jenkins or Gitlab CI, so a series of articles on how to do so is definitely nice. The articles I’ve found by Google searching haven’t been very helpful. Especially nice to see it be about php apps.

    • https://www.peternijssen.nl/ Peter Nijssen

      I felt the same. So hopefully I can help you out with these articles. Don’t hesitate to ask questions in the upcoming articles :)

      Regarding Gitlab CI. It’s “nothing” compared with Jenkins ;) I am currently using in Gitlab CI in a company environment to do quick checks like if everything is according to our coding standards so the developer has quick feedback. I use Jenkins for the heavy lifting by creating reports, runnin unit tests, etc.

      • jerrac

        The next few articles might answer these questions, but they’re where I go stuck. So..

        When running tests, you want to run them on a system as close to production as possible. So, do you need Jenkins installed on the test server, as well as the main CI server?

        For deployment to work, do you need Jenkins installed on your production server?

        When researching Gitlab CI, it sounded like I needed a “Runner” on every server. And the Jenkins documentation didn’t clarify anything for me…

        • https://www.peternijssen.nl/ Peter Nijssen

          Gitlab CI has runners indeed. They are just “running” your builds every time. If you have lots of builds which takes ages, you can decide to get more runners. Jenkins can do the same. It can also run multiple builds at once.

          Next to that Jenkins supports master/slaves. This means you can set 1 server as master and multiple servers as slaves. Again, this is something you might want to do when you are running lots of builds. You can then split the load between servers instead of doing it all at one server.

          I personally only run Jenkins on a dedicated CI server. This server closely resembles a real production server and is there to give me feedback on unit tests and the quality of code.

          I think it’s also a matter of how what you want your Jenkins server to do. In my case, I use it as a tool to analyze the code and give me feedback on unit tests. That’s why, each time a developer creates a merge/pull request, Jenkins is kicked off. This helps me in reviewing the code. When I accept the code, it goes on to a test server which then is checked by some colleagues of mine. When so far everything looks fine, it can go on to the production servers.

          A whole different strategy (and one I want to move on to) is that you use Jenkins to generate a zip file, which can then be used to move to test servers and/or production servers. In that case, you use Jenkins at one side as perhaps a quality check and on the other hand as a tool to build and prepare a new release.

          If you are mostly interested about the quality of the code, this series will guide you through it. If you are more interested about the whole deployment path, you have to wait I am afraid. I haven’t started yet on such articles.

          Regarding code quality, feel free to also check out some other quality assurance tools I wrote about;

          http://www.sitepoint.com/check-codes-quality-sensiolabs-insight/
          http://www.sitepoint.com/continuous-integration-php-ci/

  • Eric

    On ubuntu add jenkins to source.list using

    sh -c ‘echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list’
    instead of
    deb http://pkg.jenkins-ci.org/debian binary/

  • JenkinsNoob

    Excellent tutorial! Keep doing the good job! Cheers!

  • Pilar Romero

    Hi Peter,

    I don’t have clear the installation about Jenkins, I have a server with Ubuntu, there are other web applications made in php, the installation of the Jenkins is the same with sudo apt-get install jenkins or I have to do other configuration.
    My applications web is settings with virtual-host

    Thanks for your help

  • Ed Sherwin Nonog

    Hi Sir Peter, php-template doesn’t work on my ubuntu test instance, I encountered different issue in using the php-template. Any suggestion on this. Thanks for the help. Started by an SCM change
    Building in workspace /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace
    Updating http://202.90.158.20/svn/erp/asti/branches/ui_1.2_sys_setup at revision ‘2015-10-21T13:55:07.647 +0800’
    U protected/modules/references/controllers/BaseRefTiTopicController.php
    U protected/modules/references/views/baseRefTiTopic/_grid.php
    At revision 18322
    [workspace] $ ant “-Dselenium=Connection failed”
    Buildfile: /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build.xml

    clean:
    [delete] Deleting directory /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/api
    [delete] Deleting directory /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/coverage
    [delete] Deleting directory /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/logs
    [delete] Deleting directory /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/pdepend
    [delete] Deleting directory /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/phpdox

    prepare:
    [mkdir] Created dir: /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/api
    [mkdir] Created dir: /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/coverage
    [mkdir] Created dir: /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/logs
    [mkdir] Created dir: /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/pdepend
    [mkdir] Created dir: /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/phpdox

    lint:

    BUILD FAILED
    /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build.xml:87: /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/src does not exist.

    Total time: 1 second
    Build step ‘Invoke Ant’ marked build as failure
    [WARNINGS] Parsing warnings in console log with parser PHP Runtime
    [CHECKSTYLE] Collecting checkstyle analysis files…
    [CHECKSTYLE] Finding all files that match the pattern checkstyle.xml
    [CHECKSTYLE] Parsing 1 file in /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace
    [CHECKSTYLE] Parsing of file /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/checkstyle.xml failed due to an exception:

    org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 1; Premature end of file.
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1239)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:648)
    at org.apache.commons.digester3.Digester.parse(Digester.java:1642)
    at org.apache.commons.digester3.Digester.parse(Digester.java:1745)
    at hudson.plugins.checkstyle.parser.CheckStyleParser.parse(CheckStyleParser.java:67)
    at hudson.plugins.analysis.core.AbstractAnnotationParser.parse(AbstractAnnotationParser.java:54)
    at hudson.plugins.analysis.core.FilesParser.parseFile(FilesParser.java:324)
    at hudson.plugins.analysis.core.FilesParser.parseFiles(FilesParser.java:282)
    at hudson.plugins.analysis.core.FilesParser.parserCollectionOfFiles(FilesParser.java:233)
    at hudson.plugins.analysis.core.FilesParser.invoke(FilesParser.java:202)
    at hudson.plugins.analysis.core.FilesParser.invoke(FilesParser.java:32)
    at hudson.FilePath.act(FilePath.java:991)
    at hudson.FilePath.act(FilePath.java:969)
    at hudson.plugins.checkstyle.CheckStylePublisher.perform(CheckStylePublisher.java:83)
    at hudson.plugins.analysis.core.HealthAwarePublisher.perform(HealthAwarePublisher.java:68)
    at hudson.plugins.analysis.core.HealthAwareRecorder.perform(HealthAwareRecorder.java:259)
    at hudson.tasks.BuildStepCompatibilityLayer.perform(BuildStepCompatibilityLayer.java:75)
    at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
    at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:779)
    at hudson.model.AbstractBuild$AbstractBuildExecution.performAllBuildSteps(AbstractBuild.java:726)
    at hudson.model.Build$BuildExecution.post2(Build.java:185)
    at hudson.model.AbstractBuild$AbstractBuildExecution.post(AbstractBuild.java:671)
    at hudson.model.Run.execute(Run.java:1766)
    at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
    at hudson.model.ResourceController.execute(ResourceController.java:98)
    at hudson.model.Executor.run(Executor.java:408)

    [PMD] Collecting PMD analysis files…
    [PMD] Finding all files that match the pattern pmd.xml
    [PMD] Parsing 1 file in /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace
    [PMD] Parsing of file /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/pmd.xml failed due to an exception:

    org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 1; Premature end of file.
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1239)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:648)
    at org.apache.commons.digester3.Digester.parse(Digester.java:1642)
    at org.apache.commons.digester3.Digester.parse(Digester.java:1701)
    at hudson.plugins.pmd.parser.PmdParser.parse(PmdParser.java:70)
    at hudson.plugins.analysis.core.AbstractAnnotationParser.parse(AbstractAnnotationParser.java:54)
    at hudson.plugins.analysis.core.FilesParser.parseFile(FilesParser.java:324)
    at hudson.plugins.analysis.core.FilesParser.parseFiles(FilesParser.java:282)
    at hudson.plugins.analysis.core.FilesParser.parserCollectionOfFiles(FilesParser.java:233)
    at hudson.plugins.analysis.core.FilesParser.invoke(FilesParser.java:202)
    at hudson.plugins.analysis.core.FilesParser.invoke(FilesParser.java:32)
    at hudson.FilePath.act(FilePath.java:991)
    at hudson.FilePath.act(FilePath.java:969)
    at hudson.plugins.pmd.PmdPublisher.perform(PmdPublisher.java:81)
    at hudson.plugins.analysis.core.HealthAwarePublisher.perform(HealthAwarePublisher.java:68)
    at hudson.plugins.analysis.core.HealthAwareRecorder.perform(HealthAwareRecorder.java:259)
    at hudson.tasks.BuildStepCompatibilityLayer.perform(BuildStepCompatibilityLayer.java:75)
    at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
    at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:779)
    at hudson.model.AbstractBuild$AbstractBuildExecution.performAllBuildSteps(AbstractBuild.java:726)
    at hudson.model.Build$BuildExecution.post2(Build.java:185)
    at hudson.model.AbstractBuild$AbstractBuildExecution.post(AbstractBuild.java:671)
    at hudson.model.Run.execute(Run.java:1766)
    at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
    at hudson.model.ResourceController.execute(ResourceController.java:98)
    at hudson.model.Executor.run(Executor.java:408)

    [DRY] Collecting duplicate code analysis files…
    [DRY] Finding all files that match the pattern pmd-cpd.xml
    [DRY] Parsing 1 file in /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace
    [DRY] Parsing of file /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/pmd-cpd.xml failed due to an exception:

    java.io.IOException: No parser found for duplicated code results file /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/pmd-cpd.xml
    at hudson.plugins.dry.parser.DuplicationParserRegistry.parse(DuplicationParserRegistry.java:103)
    at hudson.plugins.analysis.core.FilesParser.parseFile(FilesParser.java:324)
    at hudson.plugins.analysis.core.FilesParser.parseFiles(FilesParser.java:282)
    at hudson.plugins.analysis.core.FilesParser.parserCollectionOfFiles(FilesParser.java:233)
    at hudson.plugins.analysis.core.FilesParser.invoke(FilesParser.java:202)
    at hudson.plugins.analysis.core.FilesParser.invoke(FilesParser.java:32)
    at hudson.FilePath.act(FilePath.java:991)
    at hudson.FilePath.act(FilePath.java:969)
    at hudson.plugins.dry.DryPublisher.perform(DryPublisher.java:126)
    at hudson.plugins.analysis.core.HealthAwarePublisher.perform(HealthAwarePublisher.java:68)
    at hudson.plugins.analysis.core.HealthAwareRecorder.perform(HealthAwareRecorder.java:259)
    at hudson.tasks.BuildStepCompatibilityLayer.perform(BuildStepCompatibilityLayer.java:75)
    at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
    at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:779)
    at hudson.model.AbstractBuild$AbstractBuildExecution.performAllBuildSteps(AbstractBuild.java:726)
    at hudson.model.Build$BuildExecution.post2(Build.java:185)
    at hudson.model.AbstractBuild$AbstractBuildExecution.post(AbstractBuild.java:671)
    at hudson.model.Run.execute(Run.java:1766)
    at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
    at hudson.model.ResourceController.execute(ResourceController.java:98)
    at hudson.model.Executor.run(Executor.java:408)

    Recording plot data
    [htmlpublisher] Archiving HTML reports…
    [htmlpublisher] Archiving at BUILD level /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/api to /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/builds/15/htmlreports/API_Documentation
    ERROR: Directory ‘/var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/build/api’ exists but failed copying to ‘/var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/builds/15/htmlreports/API_Documentation’.
    Publishing Selenium report…
    ERROR: Publisher ‘Publish Selenium Html Report’ failed: Missing directory target
    Publishing Selenium report…
    [xUnit] [INFO] – Starting to record.
    [xUnit] [INFO] – Processing PHPUnit-3.x (default)
    [xUnit] [INFO] – [PHPUnit-3.x (default)] – 1 test report file(s) were found with the pattern ‘junit.xml’ relative to ‘/var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace’ for the testing framework ‘PHPUnit-3.x (default)’.
    [xUnit] [ERROR] – Test reports were found but not all of them are new. Did all the tests run?
    * /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/junit.xml is 1 day 20 hr old

    [xUnit] [INFO] – Failing BUILD.
    [xUnit] [INFO] – There are errors when processing test results.
    [xUnit] [INFO] – Skipping tests recording.
    [xUnit] [INFO] – Stop build.
    [JDepend] JDepend plugin is ready
    [JDepend] Couldn’t generate JDepend file at ‘jdepend.xml’java.io.FileNotFoundException: /var/lib/jenkins/jobs/asti-1.2_ui_sys_setup_enhancement/workspace/jdepend.xml (No such file or directory)
    [BFA] Scanning build for known causes…
    [BFA] No failure causes found
    [BFA] Done. 0s
    Warning: this build has no associated authentication, so build permissions may be lacking, and downstream projects which cannot even be seen by an anonymous user will be silently skipped
    Finished: FAILURE

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in PHP, once a week, for free.