coding in the rain

By Andrew Carter

Go Development Environment

Recently, I've started to work with the Go language. One initial point of confusion for me was how to use workspaces. They work a little different than other frameworks and languages.

In simple terms, a workspace sets the context for running the go tool. Go is implemented by a suite of binaries similar to GCC. There is a compiler, linker and other tools for managing specific aspects of the Go workflow. On top of all of this is a tool called go that lets you use much simpler meta tasks like build, install, and test. For the high level tool to operate, it needs to have a context to work within. This includes where to find code and dependencies, where to build and install components, and generally set the state of the world.

Many languages have the concept of a path. Python, Ruby, and Java all operate where you set an environment variable that lets the tools resolve where to find dependencies. Go has a similar concept. There are two primary environment variables that are used by the go tool. GOROOT is typically set to the location where the go binaries and standard libraries are installed. For example, on Mac OS X, this would be /usr/local/go. The second variable is GOPATH. This represents where you would put your code and any code you have pulled in as dependencies.

The GOPATH variable can have as many components as you like. It could be one location or several. It is up to you. This is where workspaces come in. A go workspace is effectively a working area that is composed of the following:

bin/
pkg/
src/

These three directories form the conventions that the go tool will use to find and build things. All paths are relative to the workspace root and component directory.

A more complete example would be like the following:

bin/
  todo
pkg/
  linux_amd64/
    code.google.com/p/goauth2
      oauth.a
    github.com/nf/todo
      task.a
src/
  oauth/
    oauth.go
    oauth_test.go
  github.com/nf/
    todo/
      task/
        task.go
      todo.go
  ...

Go recommends using repository paths as name spacing mechanism. Even if you never check in your code, using this layout makes things easier. The GOPATH is simply a list of all these workspaces you might want to use.

So far so good - you have a way of collecting code and working with it. You have a mechanism for convenience tools to interact with it. You might be tempted to just make ~/Projects/my_gocode and set that to GOPATH and put everything in there. That might be fine but anyone who has spent time working with code that involves external dependencies can tell you, that can get messy fast. Languages like Python and Ruby have helpers like virtualenv or rbenv for creating isolated environments. It seems like a useful idea in Go as well.

Go has a different kind of package manager. It has similarities to PyPi or Rubygems but without a strong notion of versioning. Go strongly encourages using the latest version in master. There is no general versioning support. Whether that is good or not is still being debated. The go tool itself has a get command that will fetch the head of a dependency much like a package manager would. The key thing here is that it will fetch it to the first workspace on the GOPATH. Alternately, you could git checkout any version you wanted directly into src/ to lock to a version or control it more precisely.

With all that in mind, I wanted to give myself a little more flexibility. Conceptually putting everything in one place didn't feel right to me. I would prefer a little more flexibility and isolation. The nice thing is that GOPATH gives you all that power - you just need to set it.

I created a simple workspace manager tool. I found several others (even with the same name) but none did quite what I wanted. I don't want a Go version of virtualenv or rbenv. That seems heavier than what I want. All I need is a more convenient way to switch my workspace context and then use all the standard Go tools.

The tool I created is goenv.

Go workspace manager

Usage:

  goenv command [arguments]

The commands are:

  init     initialize path as workspace (default working directory)
  switch   switch workspace to path (default to working directory)
  add      add path to workspace (default working directory)
  rm       remove path from workspace (default working directory)
  reset    reset to workspace to empty
  list     list all workspace paths
  which    show current Go workspace
  env      environment variables for workspace

goenv manages the workspace by setting the GOPATH environment variable
When setting or adding a workspace, goenv will search up the path
to find the parent with the required GOPATH entries of bin, pkg, and
src

Let's see an example workflow.

You can create a new workspace by using goenv init. This will create a standard Go workspace layout (bin/, pkg/, /src) and add it to the GOPATH. The path to the workspace can be relative or absolute. The command goenv list shows all the active workspaces. The command goenv env is a short version of go env that is just the GOROOT and GOPATH currently set.

% goenv init myapp

% cd myapp

% goenv list
/Users/andrew/Projects/myapp

% goenv env
GOPATH=/Users/andrew/Projects/myapp
GOROOT=/usr/local/go

With the GOPATH set, you can use standard go tool commands like go get. All workspaces added via goenv will have the bin/ path added to the PATH so you can easily run any binaries you build.

% go get code.google.com/p/go.example/hello

% ls -l src/code.google.com/p/go.example/
total 24
-rw-r--r--  1 andrew  staff   1.4K Jun 30 09:36 LICENSE
-rw-r--r--  1 andrew  staff   1.3K Jun 30 09:36 PATENTS
-rw-r--r--  1 andrew  staff   102B Jun 30 09:36 codereview.cfg
drwxr-xr-x  3 andrew  staff   102B Jun 30 09:36 hello/
drwxr-xr-x  4 andrew  staff   136B Jun 30 09:36 newmath/

% ls bin/
hello*

% hello
Hello, world.  Sqrt(2) = 1.414213562373095

Adding a second workspace works like before. If it already exists, go add will amend it. Alternately, if you use go switch, it will clear any currently set workspace and set it to only the new one you added.

% cd ..

% goenv init myapp2

% goenv list
/Users/andrew/Projects/myapp
/Users/andrew/Projects/myapp2

Removing a workspace works like add.

% goenv rm myapp

% goenv list
/Users/andrew/Projects/myapp2

If you are in a working directory, you can find the implied workspace with the goenv which command. This will walk up the path until it finds a go workspace root and either return that path or none if it isn't in a go workspace. This is how the go add and go switch methods determine it implicitly.

% cd myapp

% goenv which
/Users/andrew/Projects/myapp

% cd src/code.google.com/p/go.example/hello

% goenv switch

% goenv list
/Users/andrew/Projects/myapp

Also you can work with multiple paths.

% cd ~/Projects/myapp

% goenv reset

% goenv list
Go workspace not set

% goenv add myapp myapp2

% goenv list
/Users/andrew/Projects/myapp
/Users/andrew/Projects/myapp2

I'm finding this is about all I've needed for managing workspaces. It is convenient to set the context and use the built in tools. Additionally, it is easily transferrable to a Make file in that you simply would need to set the GOPATH and the rest would fall out. Like a lot of Go, the simplicity is a little misleading at first but it is elegant when you wrap your head around it.

For BBEdit users, I'm working on a Go.bbpackage that includes some similar ideas for running go commands on the current document. Since BBEdit will run each in a new shell context, the workspace is always dynamically found and used. I'm not sure yet if or how I want to bubble up some of the more complicated methods.

Can WWDC Be Fixed?

Apple announced WWDC 2012 this morning. The event will be held in San Francisco June 11 to 15. The tickets went on sale around 5:45 AM Pacific time. Fortunately for me, I was in Austin, TX and eating breakfast when the word got out (7:45 AM Central time). Since I haven't gone for a few years, I jumped on my chance and got my pass. The conference sold out in about 2 hours.

Apple did make some good changes this year. They are no longer allowing resale of registrations. Also, they are capping each organization to 5 passes. These are both very good moves. However, it still doesn't solve the simple supply/demand problem. More people want to go than can get tickets. Today, the west coast paid the bigger price since many people weren't even awake during the entire time tickets were on sale.

Clearly, something else should be used to allocate tickets. The following are some alternative methods to allocate passes.

Lottery

This is probably the simplest option. Allow people to pre-register for a ticket lottery. Apply the same rules as sales (no transfers and no more than 5 per organization). At a specific time, run the lottery and allocate the 5000 or so tickets. Give lottery winners 24 hours to redeem them. Any remaining tickets could either go up for general sale or further lottery.

The problem here is that you may see more developers than really want to go enter the lottery. It is the most democratized form of allocation. There is no priority being given. There probably would be pressure to have VIP passes that circumvent the lottery. It could be hard to keep out some high profile companies.

One Pass per Active Developer Account

In the old days, you could buy an ADC developer account. In exchange, you would receive special ADC mailings 4 times a year, a significant hardware discount, and a WWDC pass. If there are less than 5000 organizations, give every active account the opportunity to buy one pass. Put any remaining passes up for general sale or lottery.

Priority Allocation

Another scheme could be to favor first year developers. If you are in the first year of your developer account, you get a pass. The problem with this is that you could see individuals creating new organizations every year to get a pass.

Round Robin

If you went last year, you can't go this year. If the pool isn't big enough (i.e. less than 10,000 potential attendees), you could go to a system where you can attend 2 out of 3 years. The idea would be to sideline enough potential attendees to control access.

Local User Groups

The final solution I can think of is to organize user group events for the week of WWDC. You could do live streaming of sessions, spread Apple evangelists to key regions to host in person events, and other outreach tactics. The idea here would be to make it more of an active global event and rely less on the primary event.

Of all the solutions, I think one pass per active account is the best solution. Since they are offering 5 per organization, it seems more fair to allow all organizations to have one pass and then offer what is left to others in an organization. I think they can probably combat the problem of people creating accounts just for WWDC by charging for the pass upfront as part of the subscription. You would need to be pretty serious about going to pay for the ticket up front. This would at least allow organizations that know they will send at least one person to lock down their spot.

As many WWDC veterans will tell you, a huge amount of the value is the networking with other Mac and iOS developers. That alone is incentive to attend. Hopefully, Apple can find some sort of equitable solution to make sure as many people as possible can actively participate in WWDC.

RailsConf 2012 Day 3

Today was the final day of RailsConf. The last day offered a couple great talks. Yehuda Katz talked about The Next Five Years for Rails. Honestly, Yehuda's talk should have been the day 3 keynote. It was probably the most honest and informative talk of the entire conference. Yehuda did a great job calling out what it is about Rails that attracted all of us to the platform in the first place. I agree with him that the next big thing should be making Rails just as good for JSON API services as it is for HTML. It's a mixed world more than ever. Building web services whose clients are primarily mobile devices only is increasing.

Nick Quaranto had a great tour of Basecamp Next: Code Spelunking. It was actually nice to see that 37signals has some of the same kinds of tradeoffs in their software as everyone else. One of the big takeaways is to always be pragmatic about things like changing data stores. There were numerous things I want to investigate including all the cool JavaScript console tricks, using GitHub for API documentation (see 37signals BCX API), and the strong parameters gem.

Jared Ning had a nice overview of minitest. If you hadn't seen it before, it was a good tour of what minitest is about and how it draws from both Test::Unit and RSpec. I'm already in the minitest camp so there wasn't a lot new for me. I liked the explanation Jared used to talk about mocking and stubbing. I agree with his advice - use mocks and stubs sparingly and only after you test against the real thing.

Overall, it was another great RailsConf. As a developer that switches among multiple platforms and languages, it's always great to dive into nothing but my favorite language Ruby for a few days. Rails feels to me like it is continuing to mature. It's moving a little slower now but that's ok. There is still lots opinions among the faithful and that's healthy. As always, I look forward to apply what I've learned to our own projects.

Archive