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
Posted by Jason Meridth in continous integration, git, integrity, ruby on Thursday, May 28, 2009
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
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:
so that the file can be run
chmod a+x .git/hooks/post-receive
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
Posted by Jason Meridth in continous integration, firefox, hudson on Wednesday, March 4, 2009
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
Posted by Jason Meridth in git, permissions, sharedrepository on Thursday, February 26, 2009
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/
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
Posted by Jason Meridth in design patterns, ruby on Monday, February 16, 2009
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.
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
Posted by Jason Meridth in ruby, selenium, selenium remote control, slicehost, test::unit, testing on Wednesday, February 11, 2009
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.
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.
sudo gem install selenium-client
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:
after you add that, re-source your bash file.
# commands
alias selsrvr="java -jar ~/code/seleniumrc/selenium-server-1.0-beta-2/selenium-server.jar"
For me on my Mac
For me on my Ubuntu box:
. ~/.bash_profile
. ~/.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.
7. Start the selenium server:
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
8. run the test:
selsrvr
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.
ruby ~/code/seleniumrc/test.rb
This is my 3rd time using selenium and I like what I see.
More to come.
Hudson and Nginx
Posted by Jason Meridth in apache, continous integration, htpasswd, hudson, nginx on Monday, February 9, 2009
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:
I only allow access to my server via 80 (web), 443 (ssl) and my ssh port (I'm not telling).
# Deny HTTP requests to port 8080 externally but allow internally
-A INPUT -i eth0 -p tcp -m tcp --dport 8080 -j REJECT
This works for now, but I'm experiencing another weird issue. I follow the Nginx way of using HTTP basic authentication:
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?
location / {
auth_basic "Restricted";
auth_basic_user_file conf/htpasswd;
}
I'll update this post once I figure it out.
Making a father proud
Posted by Jason Meridth in books, design patterns, pair programming, ruby, test driven
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. :)

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
Posted by Jason Meridth in continous integration, linux, nginx, ruby on Wednesday, February 4, 2009
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
Posted by Jason Meridth in agile, pair programming, xp on Thursday, January 29, 2009
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 clothThat 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 testand then ran:
script/aboutto show the info about the projects (Rails 2.2.2, etc). After that we typed:
script/serverand 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...
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
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.



