Hudson service files

This post is strictly for my reference. These scripts are dependent on an existing user named hudson with a home directory of /home/hudson and the hudson.war file being located at /usr/share/hudson/hudson.war.

/etc/init.d/hudson


#! /bin/bash
#
# hudson Start/Stop the Hudson Continuous Integration server.
#
# chkconfig: 345 91 10
# description: Hudson is a Continuous Integration server. \
# It monitors a source code repository and triggers builds \
# when it detects any changes. See https://hudson.dev.java.net/ \
# for more details.
# processname: hudson
# pidfile: /var/run/hudson.pid


# Source function library.
#. /etc/rc.d/init.d/functions

# Get config.
#. /etc/sysconfig/network

# Check that networking is up.
#[ "${NETWORKING}" = "no" ] && exit 0

startup=/usr/local/bin/start-hudson.sh
shutdown=/usr/local/bin/stop-hudson.sh
export JAVA_HOME=/usr/java/jdk1.6.0
HUDSON_USER=hudson

start(){
echo -n $"Starting Hudson service: "
su - $HUDSON_USER -c $startup
RETVAL=$?
echo
}

stop(){
echo -n $"Stopping Hudson service: "
su - $HUDSON_USER -c $shutdown
RETVAL=$?
echo
}

status(){
numproc=`ps -ef | grep hudson.war | grep -v "grep hudson.war" | wc -l`
if [ $numproc -gt 0 ]; then
echo "Hudson is running..."
else
echo "Hudson is stopped..."
fi
}

restart(){
stop
start
}


# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
exit 1
esac

exit 0


/usr/local/bin/start-hudson.sh

#!/bin/bash
HUDSON_WAR=/usr/share/hudson/hudson.war
HUDSON_LOG=/home/hudson/hudson.log
java -jar $HUDSON_WAR > $HUDSON_LOG Z>&1 &


/usr/local/bin/stop-hudson.sh

#!/bin/bash
kill -9 `ps -ef | grep hudson.war | grep -v grep | awk '{print $2}'`

My Walks

I started walking on June 1, 2009 to start taking my weight loss seriously. I walked on June 1 and found an iPhone application, via my friend Joshua Lockwood (thanks Josh) called RunKeeper. When I walk it tracks my location and I can upload the walks to their website. Here are the walks I've made: (I will update this post everytime I walk)

June 18, 2009

June 15, 2009

June 10, 2009-June 14, 2009 == 5 days of excuses and laziness

June 9, 2008

June 8, 2009

June 7, 2009

June 6, 2009

June 5, 2009

June 4, 2009

June 3, 2009

June 2, 2009

Git Post-receive hook with integrity post call

Using the following code:




#!/usr/bin/env ruby

require 'rubygems'
require 'net/http'
require 'net/https'
require 'json'

# EDIT POST_RECEIVE_URL
POST_RECEIVE_URL = 'https://my.domain.com/application_name/push'

old_head, new_head, ref = STDIN.gets.split

#puts "old_head: #{old_head}"
#puts "new_head: #{new_head}"
#puts "ref: #{ref}"

revision_text = `git-rev-list --pretty=format:'Author: %an <%ae>%nDate: %cd%n%s%n' #{new_head} ^#{old_head}`
revisions = []
revision_text.split(/\n\n/).each { |commit|
s = commit.split(/\n/)

s[0] =~ /commit (\w+)/
sha1 = $1
s[1] =~ /Author: (.*) <(.+?)>/
author_name, author_email = $1, $2
s[2] =~ /Date: +(.+?) -0/
timestamp = $1
message = s[3].strip
revisions << {
'id' => sha1,
'author' => {
'email' => author_email,
'name' => author_name,
},
'message' => message,
'timestamp' => timestamp,
}
}

if revisions.empty?
exit 0
end

payload = {
'payload' => {
"ref" => ref,
"commits" => revisions,
}.to_json
}

if pid = fork
Process.detach pid
else
uri = URI.parse(POST_RECEIVE_URL)
post_req = Net::HTTP::Post.new(uri.path)
post_req.basic_auth '53cr3t', 'p@55w0rd'
post_req.set_form_data(payload, ';')

req = Net::HTTP.new(uri.host, uri.port)
req.use_ssl = true
req.verify_mode = OpenSSL::SSL::VERIFY_NONE
req.start {|http| http.request(post_req)}
end

puts "Running Integrity CI build for application_name application"


This git post-receive hook script queries the latest commit, gets the revision changes, and then passes that payload (via json) to a net/https POST call. I setup our Integrity CI server with a self-signed certificate and HTTP basic authentication.

The Net::HTTP::Post.new instantiation group handles populating the basic authentication information and putting the payload into the form data of the request. The Net::HTTP.new group handles telling the request that it will be via SSL (443) and to not verify the SSL certificate (since I'm using a self-signed certificate; otherwise, I'd get a warning and kill my automation process)

The other important part is the "if pid = fork". The ruby fork command allows code to happen in the background, in the case the POST call, and therefore make the call synchronous. Otherwise, when the developers commit their code to the repository, they will have to wait for the build to finish before their prompt will be returned to them. fork will return zero (0) if it fails to create the background process.

I'm still checking whether the zombie process warning in the rdoc (for the fork command) is a problem. I'll update this if it is.

The last line is notification for the user so they know what's going on. Always good to keep the user in the loop. :)

Nerds

This is from one of our pairing sessions. I drink Monster.


And this is from this past weekend at the Alt.NET conference:


Should make someone laugh.

Adding a post-receive git hook to fire off Hudson CI server

UPDATE: guard clause throws LocalJumpError. Changed to simple if block

Context: I needed our hudson CI builds to auto-fire when a developer pushes their code changes to our canonical/upstream git repositories.

In your .git/hooks/post-receive file place the file code (FULL CODE):


#!/usr/bin/env ruby
#
while (input = STDIN.read) != ''
rev_old, rev_new, ref = input.split(" ")
if ref == "refs/heads/master"

url="http://yourhudsondomain.com/job/job_name_here/build?delay=0sec"

puts "Run Hudson build for job_name_here application"
`wget #{url} > /dev/null 2>&1`
end
end

EXPLAINING EACH LINE:

I avoid writing bash scripts in default bash if I can. This is for a Ruby on Rails app, so I decided to write it in Ruby.

#!/usr/bin/env ruby


The while loop checks to make sure there are values in STDIN (by default they are rev_old, rev_new, and ref).

#
while (input = STDIN.read) != ''
end


I put an if statement block:

if ref == "refs/heads/master"
end

to make sure I only fire the build if it is the master branch.

Set the URL for the app you want to build:

url="http://yourhudsondomain.com/job/job_name_here/build?delay=0sec"


Add a little text to notify the user what is going on (always a good idea):

puts "Run Hudson build for job_name_here application"


And finally all the url with wget and feeding any response to the black hole that is /dev/null:

`wget #{url} > /dev/null 2>&1`


IMPORTANT:
Don't forget to run:

chmod a+x .git/hooks/post-receive
so that the file can be run

Some Gotchas:
1. If you are going to a url that has http basic authentication like my actual script does, you can pass the username and password like you normally would in an HTTP call:

url="http://username:password@yourhudsondomain.com/job/job_name_here/build?delay=0sec"


2. If you are doing an https call with a self-signed certificate, wget will complain. You can add the --no-check-certificate argument to wget to bypass certificate validation against local certificate caches:

`wget --no-check-certificate #{url} > /dev/null 2>&1`

Firefox Hudson Build Monitor add-on

MOZILLA: READ THIS!

To Mozilla,
You should upgrad ethe Hudson Build Monitor Plugin so that users don't have to create add-on accounts just to download. Cliffano's customer support and product are top of the line.

Explanation:

I have started using Hudson as my Continous Integraton server at work and still setting it up for custom interaction with Git. I have used CruiseControl.NET and TeamCity extensively in the past and both have tray tools. Well, I found HudsonTracker, but it wouldn't work for me because I'm using Basic HTTP Auth with my Hudson instance.

I then found the Firefox Hudson Build Monitor plugin, written by Cliffano Subagio (firefox developer account and blog), but again, same problem. I decided to issue a ticket with the Firefox plugin.

Within a short time (couple of days), Cliffano responded with a new version to test the HTTP Authentication against.

I uninstalled the old version just to make sure of no conflict and then I right-clicked the xpi file and said to open with Firefox. It installed without issue and showed the butler in the firefox status bar:


You can right-click him and get the preferences menu:




There are 5 tabs:
1.Feeds - where you put your RSS feed URL to your hudson dashboard


2. Display - designate some UI elements (# of builds, what to show on status bar when collapsed, color indicators [Hudson uses blue for successful builds for some reason. Can choose green here for us Red/Green guys/gals], etc)


3. Notification - designate how you will be notified of certain events


4. Network - username and password for HTTP Authentication


5. Misc - designate how to open page (new tab, new firefox window) and enable debugging


NOTE: Whenever I added my RSS feed from my Hudson dashboard to the tool (I'm using Firefox on a Mac) the URL would not stay. Cliffano had a solution for this:
1. Go to your Hudson dashboard

2. Right click your RSS feed of choice (I prefer "for all" which is https://hudson.mydomain.com/rssAll):


3. Choose "Add Link To Hudson Build Monitor"


4. Name the URL:


And VOILA! it stayed in my preferences. Most users won't have to go this route, but instead of typing out the url and just a name, it is easier.

Thank you Cliffano. Hopefully Mozilla will upgrade the plugin and stop requiring people to create accounts to download it.

Cliffano has stated that a 1.0 version will be coming soon. Enjoy!

Git and sharedrepository

Today I pushed some code changes to our canonical repository. Well, at least I tried. I couldn't because the user and group that were associated to that change had been changed by the prior user's push. It was the user change that was the problem, it was the group change.

I found out that we needed to add "sharedrepository=true" to the /srv/git//.git/config file so that the group assigned to the repo's objects (/srv/git//.git/objects) doesn't get changed to the local user's.

All the developers are apart of the same group on the git server; the same group initially assinged to the objects. With the "sharedrepository=true" setting, that won't change when anyone from that group pushes to the canonically repository.

Gitosis/Git error

UPDATE (FIX): I had named my remote origin with git://hostname:repo_name.git, when it should have been git@hostname:repo_name.git -- Helping agilejoe setup his gitosis, made me realize what I'd done wrong. Thanks for letting me try again Joe.

I get the following error on a couple of my gitosis repos. I've started from scratch om the gitosis server, recreated the local git repo, and many other things. No dice.

localhost:localRepoFolder user$ git push origin master:refs/heads/master
[0: ::1]: errno=Connection refused
[0: 127.0.0.1]: errno=Connection refused
fatal: unable to connect a socket (Connection refused)

I'm stumped but will update this post once a fix is found. My id_rsa.pub file that I used with creation of the gitosis stuff on the server is fine, because like I said, I can edit the gitosis-admin repo and other repos just fine locally. It's only a select few (3 out of 8) that it bombs on.

I"ll post my gitosis.conf file soon.

I'm on an Ubuntu 8.10 slice. I've done both debian package (aptitude) and "git clone->sudo python setup.py install" install mechanisms

Example instructions to install gitosis

Design Patterns in Ruby by Russ Olsen



After nudging by Joe Ocampo and Scott Bellware, I finally sat down at finished "Design Patterns in Ruby" by Russ Olsen.

The format of most of the chapters made the book an interesting read:
1. A introduction to why you might need the pattern
2. A static language developer's approach with the Ruby language
3. A seasoned Ruby developer's approach to the design pattern
4. Using and Abusing
5. in the wild
6. Wrapping up

Some of the items that I learned [LosTechies is not a cult contrary to some of the examples you read below; some of the examples below are using LosTechies nomenclature but closely resemble what the author had in the book]

If any of the stuff below intrigues you: GO BUY THE BOOK. You won't regret it. Even if you are trying to understand patterns in another language. Russ Olsen does an excellent job explaining the INTENT of the patterns.

FUN
When teaching the reader about "Truth, Lies, and nil", the author even pokes fun at himself:



BOOLEAN
In Ruby, zero, being neither false nor nil, evaluates to true in Boolean expression.
will print out: Zero is true!


ARRAYS
Points for matrix reference in array examples



REGULAR EXPRESSIONS
The flow of the language when creating regular expressions:



ARBITRARY PARAMETERS
Any author that uses DC comic character to explain arbitrary numbers of arguments, is a winner in my mind:


DUCK TYPING AND UNIT TESTS
He mentions duck typing and the fact that "Unit Tests Are Not Optional" is a section heading when teaching the Template Method Pattern.


PROCS AND BLOCKS



STRATEGY PATTERN
using proc-based formatters to create a ruby-based strategy pattern

You could create any type of formatter you want in a proc instead creating new classes.


OBSERVER PATTERN
Modules exist that encapsulate things that some of us static developers might already be used to:


ITERATOR PATTERN
Internal Iterators versus External Iterators: (had never heard it put this way)
External iterator - client drives the iteration...you won't call next until you are good and ready for the next element
Internal iterator - the aggregate relentlessly pushes the code block to accept item after item.


COMMAND PATTERN
The Command pattern translates very smoothly into code blocks. Here is a PabloForPresidentButton class reworked to use code blocks:
The author does not diminish the needs for classes. For straightforward actions, use a Proc object. For complex object or objects that will carry around a lot of state, create a command class.


ADAPTER PATTERN
Instead of adhering to some interface and trying to create your adapter, why not just extend the original class.

Before any of you Open-Closed people attack, please re-read the definition of OCP - Open for extension, closed for modification. Doesn't this adhere to that? :)


PROXY PATTERN
There are three tyes of Proxies: The Protection Proxy, Remote Proxy, Virtual Proxy
These are mentioned in the Gang of Four book. He introduces a Ruby-esqe way to approach proxies: the method_missing Method


Will output:
delegating deposit method to subject.
delegating withdraw method to subject.
delegating balance method to subject.
account balance is now: 75


DECORATOR PATTERN



SINGLETON PATTERN
The author admits the career of the singleton has been checkered, but still shows that you can use it in the Ruby world. An example he gives us to allow testing of singleton implementation code is to put the implementation code in a base class and have the child be the singleton:



FACTORY/ABSTRACT FACTORY PATTERN
I never had it straight, exactly, what the difference between these patterns were (yes, besides name). I never bothered to look. According to the author, Factory returns back a single object while Abstract Factory is "an object dedicated to creating a compatible set of objects". According to GoF book (which I have open in front of me), Abstract Factory "provides an interface for creating families of related or dependent objects without specifying their concreate classes". Which one do you think would have turned the light bulb off in your head? :)

The other item was using "Convention Over Configuration" to generate abstract factories:
Notice the correct classes (reader/writer) are generated dynamically with the help of the const_get Ruby method.


BUILDER PATTERN
Magic methods: "very easy to implement using the method_missing technique...you simply catch all unexpected method calls with method_missing and parse the method name to see if it matches the pattern of your magic method name".



Last 3 Chapters are the meat of the book:
INTERPRETER PATTERN (place where Bellware told me to start)
DOMAIN SPECIFIC LANGUAGE (DSL)
CONVENTION OVER CONFIGURATION

Those stay obscure so you can go read it. I think the book is worth a visit on Safari books if you have an account or worth the purchase for the bathroom reading.

I enjoyed it. Thanks Scott and Joe

Gitosis and a non-standard SSH port

UPDATE: Looks like the scie.nti.st post also has the SSH non-standard information

I recently installed gitosis, a free way to host your own git repositories, and I am using a non-standard port (not 22). I tried to clone the gitosis-admin repository and it timed-out. Learned via this post that I needed to create a config file under my .ssh folder in my home directory that listed the Host and Port.

Example ~/.ssh/config:


Host git.mydomain.com
Port 30000


When I do this:

git clone git-user@git.mydomain.com:gitosis-admin.git


it will then use my non-standard port to clone the gitosis repository.

I hadn't done this before. Good to know.

This post by scie.nti.st is the best HowTo on installing gitosis I could find.

Selenium-Client and Fun

So I've had the pleasure of working with selenium-client, the official Ruby client API for Selenium Remote Control (bare bone client driver).

Assumptions:
1. You have Ruby 1.8+ installed
2. You have rubygems installed

I'm going to use the Slicehost Manage website to drive with my selenium tests. No babble, just instructions.

1.


sudo gem install selenium-client
2. download the selenium remote control (version 1.0 beta at the time of this entry). Remember where you install this because you will be reference the selenium-server.jar inside it.

3. update your .bashrc (usually on Ubuntu) or .bash_profile (what I have on my Mac) with a alias to start the selenium server (for me I have unzipped the download into a ~/code/seleniumrc folder:

# commands
alias selsrvr="java -jar ~/code/seleniumrc/selenium-server-1.0-beta-2/selenium-server.jar"
after you add that, re-source your bash file.
For me on my Mac

. ~/.bash_profile
For me on my Ubuntu box:

. ~/.bashrc

4. Create a test file, test.rb somewhere (for me ~/code/seleniumrc/test.rb):

5. Write a test that:
1. Logs in
2. Creates a slice
3. Captures the new root password
4. Captures the new IP Address
5. SSHs onto the new slice
6. Adds a file, and lists out the content of ~ (aka /home/root)
7. Deletes the slice
8. Logs out

6.

require "test/unit"
require "rubygems"
gem "selenium-client", ">=1.2.9"
require "selenium/client"
require "net/ssh"

class NewTest < Test::Unit::TestCase
attr_reader :browser

def setup
@slice_name = "slice3"
@browser = Selenium::Client::Driver.new "localhost", 4444, "*firefox", "https://manage.slicehost.com/", 10000
browser.start_new_browser_session
end

def teardown
browser.close_current_browser_session
end

def test_should_be_able_to_ssh_onto_new_slice_and_add_new_file
login
begin
create_slice(@slice_name)
root_password = @browser.get_text("id=flash").sub(/You will receive an email upon completion. The new root password will be: /, '')
ip_address = @browser \
.get_text("xpath=/html/body/div[@id='container']/div[@id='main']/div[1]/div[@id='slice_action']/div[@id='show_slice']/table/tbody/tr[4]/td[2]") \
.scan(/\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}/)
assert !180.times{ break if (@browser.is_text_present("active") rescue false); sleep 1 }
Net::SSH.start(ip_address[0].to_s, 'root', :password => root_password) do |ssh|
ssh.exec("rm selenium_test_file.txt")
ssh.exec("touch selenium_test_file.txt")

stdout = ""
ssh.exec!("ls -lah ~") do |channel, stream, data|
stdout << data if stream == :stdout
end
puts stdout
end
go_to_slice(@slice_name)
ensure
assert !180.times{ break if (@browser.is_text_present("active") rescue false); sleep 1 }
delete_slice(@slice_name)
end
logout
end

def test_should_be_able_to_delete_an_existing_slice
login
go_to_slice(@slice_name)
delete_slice(@slice_name)
logout
end


def login
@browser.open "/login"
assert_equal "Slicehost Login", browser.title
@browser.type "email", "youemail@domain.com"
@browser.type "password", "yourpassword"
@browser.click "commit"
browser_wait
end

def create_slice(slice_name)
@browser.click "link=Add a Slice"
browser_wait
@browser.type "name", slice_name
@browser.click "commit"
browser_wait
end

def go_to_slice(slice_name)
@browser.click "link=My Slices"
browser_wait
@browser.click "link=#{slice_name}"
browser_wait
end

def delete_slice(slice_name)
@browser.click "link=Delete"
@browser.click "link=Delete #{slice_name}"
assert /^Permanently delete this Slice and all of its data[\s\S] There is no turning back!$/ =~ @browser.get_confirmation
end

def logout
@browser.click "link=Logout"
browser_wait
end

def browser_wait
@browser.wait_for_page_to_load "30000"
end
end
7. Start the selenium server:

selsrvr
8. run the test:

ruby ~/code/seleniumrc/test.rb
You should see the firefox browser open up in two windows and see the automation in process. Once the slice is created, I've setup a timer at 180 seconds (3 min) to wait for the slice to be created and set to a status of active. Like #5 above states, once complete the test captures the root password and ip address off the web UI and uses them to SSH into the new slice.

This is my 3rd time using selenium and I like what I see.

More to come.

Hudson and Nginx

UPDATE: I left HTTP Auth in place via Nginx and removed security from my Hudson instance. This works for me because I don't care about Hudson security. I'm just trying to prevent external access to the dashboard.

I've been having fun trying to figure out how to block a user's ability of going to http://mydomain:8080 and bypassing my 80 to 443 redirect and htpasswd HTTP basic authentication. I even went as far as removing Nginx and installing Apache.
According to the Hudson site, we can use Apache to do this. Either I'm not smart enough or web pages like this reminded me why I hate the Java Enterprise stack as much as I do. Talk about installation obfuscation.

My problem exists because the Tomcat instance running the hudson war file uses Apache JServe Protocol (AJP) and will allow direct access on whatever port you used (default 8080). This is even if you proxy_pass to 8080 from 443 (SSL) or 80 (Web).

Through testing and research it seems that Nginx does not have a solution for this. I can't allow access to my Hudson instance from external users. I have a self-signed certificate and a .htpasswd file to protect the instance, but is all useless is someone could just type in http://mydomain.com:8080. I want that redirected to https://mydomain.com which will then proxy internally to http://127.0.0.1:8080.

I didn't want to fight this, so I just used iptables (linux firewall) to block external requests on port 8080 but allow internal requests to 8080. Here is the entry in my iptables file:


# Deny HTTP requests to port 8080 externally but allow internally
-A INPUT -i eth0 -p tcp -m tcp --dport 8080 -j REJECT
I only allow access to my server via 80 (web), 443 (ssl) and my ssh port (I'm not telling).

This works for now, but I'm experiencing another weird issue. I follow the Nginx way of using HTTP basic authentication:

location / {
auth_basic "Restricted";
auth_basic_user_file conf/htpasswd;
}
If the user listed in the htpasswd file is also a registered user on the Hudson instance (through the Hudson UI) then the HTTP basic authentication will work. If they are in the htpasswd file but not registered with Hudson, it fails with a HTTP 401 (Unauthorized). Why would the HTTP basic authentication be "tunneled" through to Hudson?

I'll update this post once I figure it out.

Making a father proud

My wife, Christina, took these the other day. My 6 month old was eager to start learning software development best practices. Test Driven by Koskela and Design Patterns in Ruby by Olsen. He took one look at the Test Driven book and realized he would be a Ruby guy. :)





I know he's just going off colors, but it would be awesome to pair program with one/both of my boys someday. Kind of like Uncle Bob and his son Micah


Remove program on Ubuntu (command line interface - CLI)

I use the command line interface (CLI) more that I use the Gnome/KDE/etc. GUI interfaces. Therefore, I use apt-get to handle my package installations on my Ubuntu boxes/slices.

The 3 most common commands I use with apt-get are:
(replace the word packagename below in each command with the specific package name)

To search for the name of the package to use with the install command:


sudo apt-cache search packagename

To install a package and all dependencies:

sudo apt-get install packagename

To uninstall packages (use --purge argument to ensure no orphaned files/folders):

sudo apt-get --purge remove packagename

If you ever want to see what you already have installed:

sudo dpkg -l

If you already know a part of the name of the package, to see if it's installed:

sudo dpkg -l | grep packagename

When nginx proxy_pass doesn't work, it isn't nginx, check your iptables

I spent the last week getting very frustrated with nginx and it wasn't the problem. I've been trying to setup cruisecontrolrb, a Continuous Integrtion (CI) tool written in Ruby. (I've already gotten Hudson working, but wanted to compare). It's a very minimalist approach to CI.

I had the vhost file for my domain setup as so:


upstream cruise {
server 127.0.0.1:3333;
}

server {
listen 80;
server_name cc.mydomain.com;

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-FORWARDED_PROTO https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect false;

access_log /home/myuser/website/cruisecontrolrb/log/access.log;
error_log /home/myuser/website/cruisecontrolrb/log/error.log;

location / {
root /var/www/apps/qb/current/public/;
if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}
if (!-f $request_filename) {
proxy_pass http://cruise;
expires -1;
break;
}
}
}


For more information on what this all means go here. More or less what it says is that if you visit http://cc.mydomain.com it will redirect you internally (via proxy) to http://127.0.0.1:3333 (the default port for cruisecontrolrb) and the user won't know any better.

Unfortunately the only ports I was allowing inbound to my server were my SSH port, 80 (web), and 443 (SSL). Had the epiphany today when speaking with our Linux system Master Jedi. I was trying to get on a staging server and the SSH port wasn't 22 (the default). He told me the port and said he had to remember to close 22 access via iptables. Then my forehead slap commenced.

Slicehost Articles

If you are Linux newbie or are still unsure about how to setup your Linux box/slice/server (sorry Windows guys), take a serious look at the Slicehost Articles. Other articles include setup of Apache, Nginx, Postfix, MySQL, Ruby on Rails, Capistrano, SSH, Self-signed certificates, Django, etc.

For all the Only Windows guys who read this, these articles are a great way to get your feet wet with Linux. Can't hurt your pocket book. It's free. Ubuntu is my favorite.

I've tweeted about this a number of times.

These articles are phenomenal.

Pair Programming

I recently found this post, Pair Programming all the time, by Jay Fields and loved it. I've felt the same way about pair programming.

"I define all the time (in terms of pairing) as when I'm writing code that I'm going to commit."
That is perfect. Common sense but stated explicitly. I worked in an Agile shop for 2 1/2 years and the environment was setup to highlight pair programming. Pictures and little explanation here (Thanks Joe). We even marked tasks in the stories as low (L) or high (H) to dictate whether a pair was necessary (this was decided during our modeling week buy the two developers who tasked the story, but always up for discussion during the iteration). It worked out pretty well.

I understand and have heard all the reasons to not pair program. Sometimes it works and sometimes it doesn't. I've personally experienced the benefits. You learn to work with different personalities and that can only benefit you in your professional career. And, the obvious reason, is immediate code review. But, as my friend Scott C. Reynolds says (more or less):
Not everyone is cut from the same cloth
That is true and that is life. I hope this helps someone understand that not all pair programming enthusiasts are zealots. I know it's a fine line though.

Law of Demeter

I've posted on the Law of Demeter before and so have many others. It pretty much means don't allow external objects to reach into your aggregate without going through the aggregate root.

I was reading a good website/slide show on mocking in Ruby and the next to last slide had the following:

  • You can play with yourself (please be adult about this one)
  • You can play with your own toys
  • You can play with toys that were given to you
  • And you can play with toys you’ve made yourself

Source: C2 Wiki – Law of Demeter

Good to understand.

Coffee shop, friend, and Ruby on Rails

I had a very good evening with a good friend talking about Linux and Ruby on Rails. My friend hadn't been in the Linux realm for a bit and just wanted a quick refresher. We installed gnome-do because I'm a QuickSilver user on my Mac and the fact that you have to type "Alt+F2" then "gnome-terminal" to bring up a terminal in gnome is frustrating. Of course, I just press "Ctrl+Alt+F2" which takes me to tty2 and gets rid of the Gnome GUI. :) Gnome-do is a lot like Spotlight or QuickSilver on a Mac, or the start bar textbox in Vista.

I don't claim to be a Linux guru by any means, but due to my current position, I've been able to work on my Linux padawan skills. Antony Messerli has been my jedi master. I also pointed my friend to the Slicehost Articles and a few other links. While working with him I realized just how much I've learned at my new position.

In all honestly, all that I did from memory was available in the first 2 setup Slicehost articles for Ubuntu 8.20 (Intrepid) (Article 1 & 2) Kudos to PickledOnion (the author).

My friend already had most of the items installed necessary to get rails running locally. Ruby, rubygems, rake, rails. We ran created a test rails app in his code directory:

rails test
and then ran:
script/about 
to show the info about the projects (Rails 2.2.2, etc). After that we typed:
script/server
and Webrick started up. We then opened http://localhost:3000 and he saw the default Rails index.html file saying "Welcome to Rails, blah, blah".

My friend was impressed at how fast we got the app running. Now to our purpose of meeting. We went to the substruct project google code page. My friend is setting up a shopping card for a client and wanted to try out Rails. He found Substruct and wanted to set it up.



Following the instructions on the site proved to be fruitful and we had a substruct project up and running in under 30 minutes. Awesome. The project had built in support for PayPal and ability for admins to change UI through web interface, no editing of erb files. FTW.

This also prompted me to create another slice on my Slicehost account and setup Phusion Passenger (mod_rails). I'm still setting it up completely but may just migrate my Nginx server over to mod_rails. It was a very simple setup.

More to come...

My favorite XKCD comic



Explanation: Wikipedia entry on Sudo

Current Status

I'm working with a Ruby on Rails project now (RoR from now on) and absolutely loving it. I'm on nothing but Linux boxes, working with Ruby, and have been put in charge of strengthening our testing skills. I kind of laugh at this because I am having to learn Ruby, Rails, Nginx, Mongrels, etc and then figure out how to test RoR code.

I still touch up a C# API for another project at the company I work for; I'm not completely disconnected from the .NET world.

I went down the Test::Unit path first, due to the fact that it comes with Rails. I was quickly reminded why I went down the Behavior Driven Development path in C#. Test::Unit is a lot of doing Test Driven Development in NUnit (or other xUnit) fashion. You prefix your tests with "test_" and go from there. It works. I can't deny that in either the .NET world or the Rails world. I just prefer to focus on the behavior because it highlights that I will only make specifications or examples pass because they are serving a function my user has requested.

I then went down the, ever popular, RSpec path. A few more gems installed and another TextMate bundle to gain some RSpec macros. I liked it but it felt like too much. I can't explain it. I have friends who swear by RSpec and more power to them. Going down this route also exposed Zentest/autotest to me. For testing specs I'd use autospec from the same tool. Once I figured out how to sync Growl (I'm on a Macbook Pro), the notification tool on a Mac, with autospec, I was in heaven. So I'd run autospec, it would run all my specs and then spit up a notification with a green window for passing specs or red for failure with the count of failed tests. Autospec would then just wait for a file to change and then run the specs in the file and again notify me. It was like my own local CI notification.

I then found out about Shoulda. Shoulda is a gem/plugin that gives you the same context/should notation as RSpec but still utilizing the Test::Unit portion of Rails (again Test::Unit comes with Rails by default). I had some fun getting it working but with some help on the Google Group (http://groups.google.com/group/shoula), I was able to get it working pretty fast. And instead of running autospec, I'd run autotest again with a few changes to my ~/.autotest file to get my local notifications.

I will be posting more posts going into detail about all of these paragraphs and also about my continuing adventures in the Ruby on Rails and Linux world.

Things I will be posting about:

  • Nginx
  • Apache with Phusion Passenger
  • Linux OS (commands, basic setup, security, etc)
  • Ruby on Rails
  • Ruby
  • Python
  • Django
  • TextMate (code editor on Mac)
  • CloudFiles (Rackspace Cloud storage)
  • Hudson and CruiseControl.rb (Continuous Integration software that works with Ruby)

Visual SVN Server

Due to the new policies by Assembla.com (not allowing free private source repositories), I've had to finally grab another server and stand-up Visual SVN Server.  It's free, VERY easy to manage and worked like a charm once installed.  Kudos to the Visual SVN team.

Yes, this is against my better nature as a fan of Linux and "free" software, but I had heard good things about Visual SVN Server and wanted to see for myself.

Creating an older rails project with multiple (newer) versions installed

I have been studying rspec the last week or two and have been using Rail 2.2.2 to do my exploring. I was finally ready to jump into some legacy code (rails 1.2.3) and couldn't even create a spec folder on the directory tree because the version of rails was older:


ruby script/generate rspec


Cannot find gem
Install the missing gem with 'gem install -v=1.2.3 rails', or
change environment.rb to define RAILS_GEM_VERSION with your desired version.

After looking at the rails file in /usr/bin, I noticed the regex was looking for something that matched the following regex string:

/^_(.*)_$/

If my recollection holds, that means I'm looking for any character set that begins and ends with underscores (i.e., _1.x.x_). So I tried the following:

rails _1.2.3_

and it succeeded. I found out this applies to most gems and not rails. So if you expecting a system to use a specific gem and you look at the executable script (i.e., /usr/bin/spec) and see the regex, try out specifying it manually (i.e., spec _1.1.12_ )

Hope this helps someone else. Continuing my Ruby journey.

Twitterfeed.com killed the my Google Reader

image

We recently created a TwitterFeed.com account for LosTechies.  What this service does is query your RSS feed and whenever a new post is created, it will tweet the blog entry information.  I'm back on twitter and read it religiously.  The "real-time" information is priceless.  Being notified about posts is awesome.  Heck, it even notifies me when one of the other LosTechies members posts.

This is a call out to all bloggers that use twitter.  Create a TwitterFeed.com account and you no longer have to announce your posts manually and it will help all your followers read your ideas that exceed 140 characters.

Ubuntu 8.10 - Intrepid Ibex coming soon

Intrepid Ibex is coming out soon.

The upgrades listed here

Beta download here