Showing posts with label jenkins. Show all posts
Showing posts with label jenkins. Show all posts

Wednesday, September 10, 2014

Clean, Working Jenkins Tests for Polymer Elements


I wound up yesterday thinking that the only way to run Karma tests under Jenkins on my Linode was to upgrade from Debian 6 to Debian 7. So that is what I do.

This is more work than I am inclined to do for a CI server—especially when there are so many good CI services to be purchased. But, then again, it never hurts to keep things up-to-date. The instructions for upgrading Debian 6 to 7 from Linode work quite nicely, thank you very much.

Once I have that installed, making it through the last step of the Jenkins/Karma/Polymer test setup—installing Chrome—is easy enough:
$ sudo dpkg -i ~/Downloads/google-chrome-stable_current_i386.deb
Selecting previously unselected package google-chrome-stable.
(Reading database ... 55887 files and directories currently installed.)
Unpacking google-chrome-stable (from .../google-chrome-stable_current_i386.deb) ...
dpkg: dependency problems prevent configuration of google-chrome-stable:
 google-chrome-stable depends on libappindicator1; however:
  Package libappindicator1 is not installed.
Well, of course I needed one last yak. But, after installing that missing dependency (first correcting the broken Chrome install):
$ sudo apt-get -f install
$ sudo apt-get install libappindicator1
I can now get Chrome installed:
$ sudo dpkg -i ~/Downloads/google-chrome-stable_current_i386.deb
(Reading database ... 57351 files and directories currently installed.)
Preparing to replace google-chrome-stable 37.0.2062.120-1 (using .../google-chrome-stable_current_i386.deb) ...
Unpacking replacement google-chrome-stable ...
Setting up google-chrome-stable (37.0.2062.120-1) ...
Processing triggers for man-db 
Yay!

More importantly, I finally have a passing Jenkins build:



No really, it is passing:



And it is reproduceable as well, which is always a bonus. Actually, that is kind of a must for CI.

I am not quite done, however. My eee-polymer-tests package, which provides reasonable default NPM dependencies for testing Polymer elements (Karma, Jasmine, Bower, etc) also generates skeleton test code. If run with no arguments, as is done with a NPM install from a CI server, eee-polymer-tests code will query the developer for the name of the element being tested. In fact, it is doing so in my Karma run:
> eee-polymer-tests@0.0.1 postinstall /var/lib/jenkins/jobs/Patterns in Polymer/workspace/book/code-js/parent_child/node_modules/eee-polymer-tests
> ./generator.js

What is the name of the Polymer element being tested? 
eee-polymer-tests@0.0.1 node_modules/eee-polymer-tests
├── colors@0.6.2
├── minimist@1.1.0
└── bower@1.3.9
The readline module that is doing this seems to know that this is being run in a non-interactive process—it does not wait for an answer nor does it run any of the code that would generate testing code after an answer.

Still, I would like to save it the trouble (and myself some concern). The way to accomplish this seems to be via the tty module in Node. With it, I can interrogate a file descriptor like STDIN to see if it is associated with a terminal:
var fs = require('fs');
var readline = require('readline');
var tty = require('tty');

if (element) {
  generate();
}
else if (tty.isatty(process.stdin)) {
  queryForElementName();
}
else {
  console.log('Nothing to generate.');
}
In this case, if STDIN is not a TTY, then it is not coming from an interactive session and I do nothing. All that needs to happen is to install the normal Karma, Bower, etc. dependencies.

That works just fine:
> eee-polymer-tests@0.0.1 postinstall /var/lib/jenkins/jobs/Patterns in Polymer/workspace/book/code-js/parent_child/node_modules/eee-polymer-tests
> ./generator.js

Nothing to generate.
eee-polymer-tests@0.0.1 node_modules/eee-polymer-tests
├── colors@0.6.2
├── minimist@1.1.0
└── bower@1.3.9 
I may still have a problem on my hand. Readers of the book that I am testing here, Patterns in Polymer, might want to run the tests themselves. But if they try to npm install like my test code is doing, they will be prompted for an element because they will be running the command in an interactive shell.

Looks like I am going to need a lockfile mechanism of some sort for this. Tomorrow.

Day #179

Tuesday, September 9, 2014

One Yak Too Many?


Yesterday I was able to get a Jenkins server up and running on a Linode, properly secured, and checking out Dart and JavaScript code for testing. Although the test script ran, I do not yet have a successful test—mostly due to lack of additional dependencies.

I am still building this on a Debian 6 LTS Linode host. I continue working through the error messages as they occur on the server. First up:
scripts/test.sh: line 14: xvfb-run: command not found
That is easy enough:
$ sudo apt-get install xvfb
I was happy to discover xvfb-run the other day as it makes testing life easier. Since I had never used it, I thought it might be an new addition to the xvfb package, but it is even present on this old LTS system.

Next, I install Bower and Karma globally:
$ npm install -g bower karma-cli
I might be able to do away with the global bower install, but really there is no harm in having it globally. It seems like a reasonable system-wide dependency to have available on a test box.

Now if I run the Jenkins build, I get:
...
[TEST] book/code-js/angular (Bower updated).
/home/cstrom/local/node-v0.10.31/lib/node_modules/karma/node_modules/di/lib/injector.js:9
      throw error('No provider for "' + name + '"!');
            ^
Error: No provider for "framework:jasmine"! (Resolving: framework:jasmine)
...
This one is a little tricky. The solution is obvious enough—I need to install the karma-jasmine NPM package. But how should this get installed?

I think the answer is that my test script should be responsible for this. The karma-jasmine plugin is technically a dependency of each individual project chapter. The testing script is already working through each project chapter, installing bower components and running Karma:
for X in book/code-js/*
do
    # Change the current working directory to a chapter's code directory:
    cd $X

    # Update bower
    bower update --quiet
    echo "(Bower updated)."

    # Run the Karma tests
    xvfb-run karma start --single-run --log-level warn
    # ...
done
On my dev box, I manually ensure that the NPM dependencies (like karma plugins) are installed with a command line npm install. I need some way to automate this on my test server. Since I am already bower installing, I might as well npm install first.

I think.

I am honestly unsure about that so, for now, I add a sed script before the test runner in Jenkins' Execute Shell entry:
#!/bin/bash -l

export PATH=/home/cstrom/local/node-v0.10.31/bin:$PATH

sed -i 's/bower update --quiet/npm install; bower update --quiet/' scripts/test.sh

scripts/test.sh
The sed script modifies the all-chapters test runner, adding npm intall before the bower install. If this works, I will ruminate on it and possibly edit the test.sh global test runner. Of course, that does not work...

After all that, I finally reach:
ERROR [launcher]:  Cannot start Chrome
 Can not find the binary google-chrome
 Please set env variable CHROME_BIN
This seems easy enough. I grab the latest stable Chrome:
$ wget https://dl.google.com/linux/direct/google-chrome-stable_current_i386.deb
And install it:
sudo dpkg -i google-chrome-stable_current_i386.deb                                                                   ~/Downloads
Selecting previously deselected package google-chrome-stable.
(Reading database ... 40540 files and directories currently installed.)
Unpacking google-chrome-stable (from google-chrome-stable_current_i386.deb) ...
dpkg: dependency problems prevent configuration of google-chrome-stable:
 google-chrome-stable depends on gconf-service; however:
  Package gconf-service is not installed.
 google-chrome-stable depends on libgconf-2-4 (>= 2.31.1); however:
  Package libgconf-2-4 is not installed.
 google-chrome-stable depends on libgdk-pixbuf2.0-0 (>= 2.22.0); however:
  Package libgdk-pixbuf2.0-0 is not installed.
 google-chrome-stable depends on libglib2.0-0 (>= 2.28.0); however:
  Version of libglib2.0-0 on system is 2.24.2-1.
...
And here, I may be stuck.

I do not know if it is going to be possible to install Chrome on Debian 6 because of these errors. I suppose I might try Firefox instead, but that seems more like a nice-to-have whereas Chrome is a requirement to fully test Polymer. It looks like I may need to upgrade to Debian 7. Which may be one yak too many.


Day #178

Monday, September 8, 2014

Jenkins for Testing Dart and JavaScript Polymer


It is entirely possible that I am too cheap. It is also possible that there is nothing out there that does exactly what I need it to do. So tonight, I set out to see how easy it is to roll my own.

What I need is a continuous integration server for my books. I need a solution for all of my books, but if I can find something for Patterns in Polymer which is written in both Dart and JavaScript, then I will likely have everything that I need.

Some of the key features that I need:

  • Needs to update Dart when new releases are available
  • Needs to update specific libraries (e.g. Polymer.dart and Polymer) when new releases are available
  • Needs to run tests when code is updated or new code is added
  • Needs to run dart analyzer (ignoring code intentionally broken for demonstration purposes)

I do not necessarily need to be told immediately if the code in my books has broken. I like to write as much as possible then go back and fix words and code as needed. I will typically run the tests specific to the current chapter locally, so it is not imperative that I get quick feedback (daily will be fine).

I happen to have been messing around with Jenkins a bit recently. I also happen to have a Linode that is not doing too much. Perhaps these two can be a match made in JavaScript & Dart heaven?

I have Debian 6 installed on my Linode, which is old, but still in LTS. Hopefully that will be sufficient. I start by following the instructions for adding Jenkins packages to a Debian system. This boils down to: add the alternate package server's key to my Linode, add the package source to the list of sources, update, and sudo apt-get install jenkins.

I do not care for the server being publicly available:
$ sudo netstat -nlp  | grep 8080
tcp6       0      0 :::8080                 :::*                    LISTEN      29489/java
I am very happy with it binding to localhost and then using SSH port forward to access it as needed. Never expose services that you do not need, after all.

The /etc/default/jenkins file configures Jenkins on Debian. It does not include the listen address by default, just the port:
# port for HTTP connector (default 8080; disable with -1)
HTTP_PORT=8080

JENKINS_ARGS="--webroot=/var/cache/jenkins/war --httpPort=$HTTP_PORT --ajp13Port=$AJP_PORT"
So I update the file to include that information:
# address to which to bind the HTTP connector
HTTP_HOST=127.0.0.1

# port for HTTP connector (default 8080; disable with -1)
HTTP_PORT=8080

JENKINS_ARGS="--webroot=/var/cache/jenkins/war --httpListenAddress=$HTTP_HOST --httpPort=$HTTP_PORT --ajp13Port=$AJP_PORT"
After a sudo /etc/init.d/jenkins restart I am happier:
$ sudo netstat -nlp  | grep 8080
tcp6       0      0 127.0.0.1:8080          :::*                    LISTEN      29709/java
I disconnect my SSH session, and reconect, port forwarding 8080 from my local machine to 8080 on localhost of my Linode:
$ ssh -L 8080:localhost:8080 linode
With that, I have a Jenkins server that only I can access:



I start by removing plugins that I am never going to need (mostly the myriad Java ones) from Manage Jenkins → Manage Plugins → Installed:



Then I install the GitHub plugin from the Available tab so that I can grab my private repositories:




Configuring SSH Hosts for GitHub

Once that is complete, I create an ssh key for Jenkins to use:
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/var/lib/jenkins/.ssh/id_rsa): /var/lib/jenkins/.ssh/patterns_in_polymer_id_rsa
Your identification has been saved in /var/lib/jenkins/.ssh/patterns_in_polymer_id_rsa.
Your public key has been saved in /var/lib/jenkins/.ssh/patterns_in_polymer_id_rsa.pub.

Next, I create an ssh alias that will use this ssh key:
$ pwd
/var/lib/jenkins
$ cat .ssh/config 
Host patterns_in_polymer_github
  Hostname github.com
  IdentityFile /var/lib/jenkins/.ssh/patterns_in_polymer_id_rsa
  PreferredAuthentications publickey
The benefit of this is that I can create as many keys as I like here without getting key-already-in-use messages from Github when I try to add another project to my Linode. I add the deploy key to my private book repo:



With that, I can access the repository using that SSH alias:
$ git ls-remote -h git@patterns_in_polymer_github:eee-c/polymer-patterns HEAD
The authenticity of host 'github.com (192.30.252.130)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,192.30.252.130' (RSA) to the list of known hosts.
It is important to run that step from the command-line otherwise Jenkins, unable to prompt for a yes/no answer, will fail to connect.

I am now ready to create the Jenkins project:



To that project, I add the SSH-aliased Github URL:



I configure the project to run daily at 0430:



And last, but not least, I define the tests to run:



And that works. Well, it checks out the code and executes the test script. I still need a bit more setup on my Linode (Xvfb-run, a recent Node.js, Dart, Chrome, etc). But hopefully the hard part is out of the way. I will find out just how hard the remaining configuration is tomorrow.


Day #177