Archive for category Technology

JIRA and Campfire Integration

I have been working on migrating my company’s Pivotal Tracker to JIRA. We love most of the features of JIRA, but we were really missing the nice updates to our Campfire room to tell us what is going on with our issues.

Sure, we could use an RSS feed plugin for Campy, but that is not real-time enough.

I didn’t want to write a huge plugin just to send messages to Campfire (it seems like such a simple thing to do!). I was poking around for solutions, and came across this plugin. It’s a plugin that runs scripts written in the Groovy dynamic language for Java. One nice thing about this plugin is that it lets you execute Python (Jython) or Ruby (Jruby). The only problem is that you can’t write a listener using Python, so I ended up having to learn Groovy.

Here’s what I came up with. It requires you to install the HTTPBuilder libraries for Groovy, and this script will require some customization to fit your environment. Right now it only supports sending messages when new issues are created and when a new comment is added to an existing issue, but other issue events would be easy enough to add – this is meant as an example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import com.atlassian.jira.event.issue.AbstractIssueEventListener
import com.atlassian.jira.event.issue.IssueEvent
import com.atlassian.jira.ComponentManager
import org.apache.log4j.Category
import groovyx.net.http.RESTClient
import static groovyx.net.http.ContentType.JSON
import static com.atlassian.jira.event.type.EventType.*
 
class CampfireListener extends AbstractIssueEventListener {
 
    @Override
    void workflowEvent(IssueEvent event) {
        def Category log = Category.getInstance("com.onresolve.jira.groovy.PostFunction")
        def campfire = new RESTClient('https://CAMPFIREID.campfirenow.com/room/ROOMID/')
        def issueBaseUrl = "http://yourjirahost:8080/path-to-jira/browse/"
        campfire.auth.basic 'CAMPFIRE AUTH TOKEN', 'X' 
        switch (event.getEventTypeId()) {
            case ISSUE_COMMENTED_ID:
                def resp = campfire.post( path: 'speak.json',
                                      body: [ message: [ type: "TextMessage", body:
                                          String.format("%s added a comment to %s (%s%s):",
                                              event.getUser().getDisplayName(),
                                              event.issue.getKey(),
                                              issueBaseUrl,
                                              event.issue.getKey())] ],
                                      requestContentType: JSON)
                resp = campfire.post( path: 'speak.json',
                                      body: [ message: [ type: "PasteMessage", body:
                                          String.format("%s", event.getComment().getBody()) ] ],
                                      requestContentType: JSON)
                break
            case ISSUE_CREATED_ID:
                def resp = campfire.post( path: 'speak.json',
                        body: [ message: [ type: "TextMessage", body:
                            String.format('%s created a new issue: "%s" (%s%s):',
                                event.getUser().getDisplayName(),
                                event.issue.getSummary(),
                                issueBaseUrl,
                                event.issue.getKey()) ] ],
                        requestContentType: JSON)
                resp = campfire.post( path: 'speak.json',
                      body: [ message: [ type: "PasteMessage", body:
                          String.format("%s", event.getIssue().getDescription()) ] ],
                      requestContentType: JSON)
                break
        }
    }
}

, ,

No Comments

Problems with IPv4/6 hosts in OSX Lion

If you’re like me, you end up doing a lot of testing in virtual machines. I run several VMs that do various different tasks for me so that I don’t have to sully my Mac with packages I rarely need, etc. So what I do is create a VM and give it a local hostname defined in my /etc/hosts file.

Recently, after my upgrade to OSX Lion, I started running into an issue where it would take over 5 seconds to connect to any of my VMs using any protocol (SSH, HTTP, etc). After several hours of troubleshooting, and several Wireshark sessions later, the truth of the matter was plain:

OSX is now doing IPv6 by default with no way that I have found to disable it yet, and so that means that if you have hosts that you want to reference with locally defined hostnames, you are going to have to create an IPv6 entry for them as well.

Here is a great site that I found that helps with the conversion: SubnetOnline.com

I am sure that there are better ways to do this, but I just wanted to share this quick little hack that I found with you. If you have a better way, please let me know!

,

No Comments

Re-enabling Key Repeat in OSX Lion

I have recently upgraded to OSX Lion, and I have to say that I love everything about it. Except for one thing. In many apps, the key repeat has been disabled in favor of the new press-and-hold popup for getting alternative characters.

This is fine for most apps, but for apps like PyCharm where I use vi key maps, it becomes very, very frustrating.

I came across this little tip to re-enable the key repeat, and my life is measurably better (first world problems, I know…).

Run the following in your Terminal.app:

1
defaults write -g ApplePressAndHoldEnabled -bool false

Then reboot, and you should be good to go!

, , ,

12 Comments

Campy: the Python Campfire Bot

We use a Campfire at Needle for our day-to-day communication between our tech team members and other business members. We really like Campfire’s ability to store files, conference call and keep transcripts even when you are not logged in. We get GitHub commit messages, Pivotal Tracker story updates and more scattered in our conversations.

These one-way updates are very nice, but I thought it would be nice to be able to have a bot that sat in the channel and could take commands from anybody and do more complex tasks for us without us ever having to leave Campfire. To solve this problem, I wrote Campy, a pure Python Campfire bot with an extensible plugin system.

The first plugin I wrote as a proof of concept is a Google image search plugin. You tell the bot some search parameters that you would like it to search for, and you get back an image inline in your chat transcript. Don’t worry, safe search is on by default so you don’t get any NSFW images back.

The second plugin I wrote was the all-important Pivotal Tracker plugin. This plugin lets you do the following:

  • pt story create “Title” “Description” bug|feature|chore|release — Create a story
  • pt getmine started|finished|delivered|accepted|rejected|unstarted|unscheduled — Get a list of all stories that belong to you
  • pt start #story_id — Start a particular story.
  • pt start|tell next bug|feature|chore [mine] — Start or tell the next story of a given type. Optionally supply ‘mine’ at the end of the message to only work on your own stories.

The plugins system is very extensible, so you can create a plugin to do pretty much anything!

You can get Campy here: https://github.com/bbelchak/campy

No Comments

Unit Testing Django with a NoSQL Backend

In my previous post about unit testing for django, I laid the groundwork for how to unit test any django application. One nice feature that django includes with its test framework is the test database syncing. Even better is if you are using South to do database migrations – it will run the migrations in  your test environment for you.

However, what if you are using a NoSQL database backend like MongoDB, Cassandra, CouchDB or something similar and you aren’t using the Django ORM? How do you handle setting up and tearing down the database environments?

The good news is that Python’s unittest framework makes this easy. You can override the setUp() and tearDown() on each TestCase that you build. Here is a snippet to get you started:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import pymongo
from django.test import TestCase
 
# It would be best to define this in a utility class somewhere
def get_db(db_name=None):
    """ GetDB - simple function to wrap getting a database 
    connection from the connection pool.
    """
    return pymongo.Connection(
            host=settings.MONGO_HOST,
            port=settings.MONGO_PORT)[getattr(
                settings, "MONGO_DBNAME_PREFIX", "") + 
                (db_name or settings.MONGO_DBNAME)]
 
class MyTestCase(TestCase):
    fixtures = ['test_data.json']
    def setUp(self):
        self.db = get_db('test')
        self.db.create_collection('mytestcollection')
 
    def test_doc_published(self):
        # Set up a document to save
        doc = dict(text="test",
                      user_id=1)
        self.db.mytestcollection.save(doc)
        self.assertEqual(self.db.mytestcollection.find_one(
            {'user_id':1})['text'], 'test')
 
    def tearDown(self):
        self.db.drop_collection('mytestcollection')

What this does is setUp() a collection in the mytestcollection collection, runs the my_doc_published test and then tears down the test database environment by dropping the mytestcollection collection.

Things to remember for setUp() and tearDown():

  • setUp() is called before every test method in your TestCase class.
  • tearDown() is called after every test method in your TestCase class.
  • tearDown() is called even if your test methods fail or error out.

And there you have it! Django makes testing even non-ORM datasources a snap, if you know how to wire it up.
UPDATE: Some would say that database fixtures and setting up/tearing down database environments as part of your unit tests is not “unit testing”. This is not entirely accurate, because in order to do unit tests that rely on backend data, you must instantiate and tear down pristine database environments.

, , ,

No Comments

Unit Testing Your Django Application

Unit testing is a very important part of any software project. It helps you know that the new code you are deploying works, and isn’t going to blow up in your face. It also helps you feel good about changing large chunks of code without destroying everything you’ve done for the last 3 years.

Unit testing with django is as simple as pie. The documentation is very good, and you can learn a lot about more advanced testing methods from the python documentation. In this blog post, I aim to show a quick way to get up and running with testing your django application.

First, if you are just starting out, make sure you put a high emphasis on testing your application, otherwise you are going to end up with a bunch of code that has never been tested and you will find yourself writing code for weeks just to get partial coverage on the code you’ve already written. Starting off on the right foot is a much better approach, and you will find life much more enjoyable.

Let’s get started…
Read the rest of this entry »

, ,

No Comments

Code Completion (IntelliSense) in VIM

VIM has been my editor of choice for at least 15 years. I love how fast I can edit files, perform menial tasks, and wreak general havoc on any code project I am working on at any given moment. One of the things that I have missed about VIM from an IDE perspective has been code completion (a.k.a. “IntelliSense”). I have spent a lot of time on websites and man pages trying to figure out syntax and function names for several types of languages, and just recently discovered a long-included feature of VIM called omni completion, or Omnicomplete.

Since my life is mostly centered around django these days, I will discuss how I’ve benefited from omnicomplete and how I’ve set it up in my own environment.

First, since django is a web development framework, I want to make sure that I can get omnicompletion for HTML, Python, JavaScript and CSS. Omnicompletion works for almost any programming language that VIM has syntax highlighting support for, and these languages are no exception.

Read the rest of this entry »

, ,

No Comments

Django database migrations with South

I have been using django for web development for almost a year now, and I just recently started using South to do database migrations. To be fair, most of the work that I have been doing with databases has centered around MongoDB and schema-less document stores instead of a traditional RDBMS. Since Django does not come with any database migration tools, my standard approach was to make sure that my models are completely thought out before running the manage.py syncdb command. The lack of a good database migration tool was one of the things that originally had turned me off to django.

Enter South. South lets you manage your database in a way very similar to how Ruby on Rails works.

Read the rest of this entry »

, , ,

No Comments

Removing old ssh fingerprints from your known_hosts the quick and easy way

Ever have this problem? You just rebuilt a machine, and when you go to SSH into it, you get the following message:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!

Many people just go edit their ~/.ssh/known_hosts file and carry on. But there is a faster/better way!

OpenSSH comes with a command called ssh-keygen that allows you to generate and manage all your keys, including your ssh fingerprints.

Simple usage for this would be:

1
ssh-keygen -R HOSTNAME

, ,

No Comments

Automatic MongoDB Backups to S3

One of the big problems with hosting your own database solution is that you have to do backups for it on a regular basis. Not only do you need to do backups for it, but you need to also keep backups offsite. Luckily, Amazon S3 allows a cheap and easy solution for your offsite backups. Read the rest of this entry »

, ,

No Comments

Switch to our mobile site