Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Tools
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Ian Zabel

Level up your development workflow with GitHub & Pivotal Tracker

Ian Zabel
Saturday, November 3, 2012

I’ve been working with my client, Unpakt, for a while now. One of their core values is making people’s lives easier. They’re specifically focused on making it easy for people moving to a new home or office to find a mover, compare prices and book their move online.

As a development team, we’ve taken that same core value of making things easier and applied it to our software development & deployment workflow. Over time, we’ve progressively improved our process. We’re now at the point where we’re happy with it, and I wanted to share what we’ve been up to.

The Typical Flow

Let’s assume I’m using GitHub and Pivotal Tracker and I’ve just completed a story. What’s next? Push my changes up to GitHub, click Finish on my story, wait for the build to go green, deploy to staging. Then, click Deliver on all the stories that have just been deployed. Other stories were most likely ready to be delivered as well, so I’ll check that they were actually included in the deployment, and then Deliver them in Tracker.

The typical flow goes like this:

  1. Commit the changes to my story
  2. Push to GitHub
  3. Click Finish on story in tracker
  4. Wait for green build
  5. Deploy to staging
  6. Click Deliver in Tracker

That’s not really that bad. I’m lazy though. If something can be automated, I want it to be automated. For instance, the deployment process should be automatically handled by my Continuous Integration server.

If we take this to the next step and automate our build and deployments as described in Robbie Clutton’s Building Confidence post, we can reduce that flow to this:

  1. Commit the changes to my story
  2. Push to GitHub
  3. Click Finish on story in tracker
  4. Wait for green build
  5. Click Deliver in Tracker

Let’s assume we’ve done that. That’s a nice improvement, as deploys will happen automatically whenever there’s a green build. That in itself is amazing.

But, what if we could reduce the steps in my everyday flow to this?

  1. Commit the changes to my story
  2. Push to GitHub

I want that!

A Streamlined Flow

Here’s the basic idea: tie the commit message to the Tracker Story ID. This enables all kinds of functionality that will get us down to that two step process.

GitHub & Pivotal Tracker Integration

GitHub supports integration with Pivotal Tracker. Dan Podsedly blogged about this back in 2010. You just tag your commits with Tracker Story IDs, and GitHub automatically comments on your story with the commit message. And, most importantly, it will click Finish on the story for you.

First, configure the Pivotal Tracker service hook on GitHub.

Once this is enabled, you can start tagging your commits to use the hook. The syntax for your commit message is pretty simple. Add the following at the end of your commit message:

[(Finishes|Fixes|Delivers) #TRACKER_STORY_ID]

So, if you just fixed a CSS bug for story #35192613:

When you push to GitHub, the post-receive hook will then call back to Tracker and put a comment on the story with a link to the commit on GitHub:

At this point, we’ve eliminated step 3 from above. Now our workflow looks like this:

  1. Commit the changes to my story
  2. Push to GitHub
  3. Wait for green build
  4. Click Deliver in Tracker

Honestly, that’s a pretty nice workflow. If I’m diligent about tagging each commit with the Tracker Story ID, GitHub will handle Finishing the story for me. It’ll also add a comment to the story, so others can take a look at the commit related to the story. Technical PMs especially appreciate this, since their curiousity about what changed is easily satisfied by just clicking on the comment in the story.

But I want more. I want to automate steps 3 and 4.

Deliver Stories Automatically

Pivotal Tracker has an API hook to deliver all finished stories. However, it’s not what we’re looking for. If our CI server calls that API hook after a green build, it’s possible for other stories to have been finished in the meantime that will not be included in the deploy. So, calling this API endpoint will just cause false positives.

A side-effect of tagging all your commits with Tracker Story IDs is that the git log now contains a nice little list of stories that have been finished. This makes it trivial to automate delivery of only the stories that have been deployed.

Unpakt was keen to share the script that makes this happen. They’ve thrown it up as a gist:

#!/usr/bin/env ruby

# gem 'pivotal-tracker'
require 'pivotal-tracker'

TRACKER_TOKEN = "..."
TRACKER_PROJECT_ID = "..."

PivotalTracker::Client.token = TRACKER_TOKEN
PivotalTracker::Client.use_ssl = true

unpakt_project = PivotalTracker::Project.find(TRACKER_PROJECT_ID)
stories = unpakt_project.stories.all(:state => "finished", :story_type => ['bug', 'feature'])

staging_deploy_tag = `git tag | grep staging | tail -n1`

stories.each do | story |
  puts "Searching for #{story.id} in local git repo."
  search_result = `git log --grep #{story.id} #{staging_deploy_tag}`
  if search_result.length > 0
    puts "Found #{story.id}, marking as delivered."
    story.notes.create(:text => "Delivered by staging deploy script.")
    story.update({"current_state" => "delivered"})
  else
    puts "Coult not find #{story.id} in git repo."
  end
end

Using the pivotal-tracker gem, the script finds all Finished stories in your Tracker project, and looks for a commit in the git log with that Story ID. If it finds one, it marks the story as Delivered.

It also adds a note to the story:

Our New Development Workflow

So, here’s what our development workflow now looks like:

  1. Commit the changes to my story (including the Tracker Story ID in the commit message)
  2. Push to GitHub

GitHub will mark my story as finished, and add a comment. CI will then run. If there’s a green build, it will deploy to staging. The deploy script will then run the deliver_stories script shown above, and all stories that have been deployed will be marked as delivered.

This is really, really nice. Having this much of the process automated for me just makes my life easier. It also gives our PM much quicker feedback about what’s happening. It’s very easy to forget to click Finish on a story, or deliver stories that weren’t deployed, or forget to click deliver on stories that were. This workflow eliminates these concerns.

Caveat

For this to work properly, the script that deploys to staging must checkout the same SHA as the green build from CI. If the git log shows commits from after the green build, some stories may be marked as Delivered when they haven’t actually made it through a green build and been deployed.

Caution

This workflow is not for every team. For example, it may be that your team functions better with developers verifying that each story is actually available and working on the staging environment before clicking Deliver in Tracker. No Product Manager wants to try to accept a story only to find that it’s not actually available on the staging environment. It may also be that on your project “Deployed” is not the same as “Delivered”.

You should only consider adopting a flow like this if your project is of a nature where deploys are easy, everyone on the team is playing along, and you can agree that “Deployed” and “Delivered” are one and the same.

The workflow described in this post has been working perfectly for months at Unpakt. We initially ran into the problem described above in the Caveat section, because our CI server running our deploy script while HEAD was checked out instead of the green build’s SHA. We haven’t had any stories accidentally marked as Delivered since we resolved that with TeamCity’s Snapshot Dependency feature.

Unpakt is hiring!

Unpakt is a small team with solid financial backing, led by a highly successful entrepreneur. They are creating an intense but fun and respectful culture and are looking for talented people to join their team. Check out their job listings!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Deploy strategies for HerokuSan

Ken Mayer
Monday, May 14, 2012

Deploy Strategies

If you look at the network graphs of heroku_san on github, you’ll see a number of branches where the only change is the deletion of the following line from the deploy task:

stage.migrate

If more than a few people are willing to take the effort to fork a gem just so they can delete 1 line, something smells. The reason is that these forkers were using something other than Rails+ActiveRecord+SQL in their project. Some were using Sinatra, others were using Rails, but with CouchDB.

The raison d’être for the heroku_san gem is to make Heroku deploys dirt simple. So, if people are making whole forks to customize the deploy task, we should make it less painful.

Enter strategies

Strategies are an object oriented programming pattern for creating pluggable execution control. Now, there is a new class of objects that inherit from HerokuSan::Deploy::Base. These objects control how deploys are executed for you. The Rails strategy, HerokuSan::Deploy::Rails does exactly what HerokuSan has always done:

  • push to git@heroku.com
  • call rake db:migrate
  • restart

On the other hand, the Sinatra strategy, HerokuSan::Deploy::Sinatra does nothing more than the base strategy:

  • push to git@heroku.com

You can create your own strategies and then configure HerokuSan to use it instead of its default:

Rails 3 projects

Amend your Rakefile:

require 'heroku_san'

class MyStrategy < HerokuSan::Deploy::Base
  def deploy
    super
    # call my own code to do something unique
  end
end

HerokuSan.project = HerokuSan::Project.new(Rails.root.join("config","heroku.yml"), :deploy => MyStrategy)

Sinatra (and other Rack based apps)

Amend your Rakefile

require 'heroku_san'

class MyStrategy < HerokuSan::Deploy::Base
  def deploy
    super
    # call my own code to do something unique
  end
end

config_file = File.join(File.expand_path(File.dirname(__FILE__)), 'config', 'heroku.yml')
HerokuSan.project = HerokuSan::Project.new(config_file, :deploy => MyStrategy)

load "heroku_san/tasks.rb"
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

From customer requirements to releasable gem

Ken Mayer
Sunday, May 13, 2012

One of the many pleasures of working at Pivotal Labs is that we are encouraged to release some of our work as open source. Often during the course of our engagements, we write code that might have wide-spread use. Due to the nature of our contracts, we can not unilaterally release such code. Those rights belong to the client. And rightly so. So, it is an even greater pleasure when one of our clients believes in “giving back” to the community, as well.

One such example is this modest gem, attribute_access_controllable which allows you to set read-only access at the attribute level, on a per-instance basis. For example, let’s say that you have a model Person with an attribute birthday, which, for security purposes, cannot be changed once this attribute is set (except, perhaps, by an administrator with extraordinary privileges). Any future attempts to change this attribute will result in a validation error.

e.g.

> alice = Person.new(:birthday => '12/12/12')
=> #<Person id: nil, attr1: nil, created_at: nil, updated_at: nil, read_only_attributes: nil, birthday: "0012-12-12">
> alice.attr_read_only(:birthday)
=> #<Set: {"birthday"}>
> alice.save!
=> true
> alice.birthday = "2012-12-12"
=> "2012-12-12"
> alice.save!
ActiveRecord::RecordInvalid: Validation failed: Birthday is invalid, Birthday is read_only
> alice.save!(:skip_read_only => true)
=> true

Setting this up is trivial, thanks to a Rails generator which does most of the heavy lifting for you.

rails generate attribute_access Person

After that, you need only know about one new method added to your class:

#attr_read_only(*attributes) # Marks attributes as read-only

There are a few others, but this one, plus the new functionality added to #save and #save! will get you quite far.

And if that’s all that you were looking for when you stumbled across this article, then there’s no need to read any further. Go install the gem and have fun (and may your tests be green when you expect them to be).

From customer requirements to releasable gem

On the other hand, if you are interested in how we got from the original customer story to a releasable open sourced gem, read on. The source code for the module is a mere 34 lines long. It implements 2 new methods, a validator and (gently) overrides #save and #save!. Being good Test Driven Developers, we wrote our specs first, and since we wanted this behavior to be included in several models, we wrote our specs as a shared behavior as well. The spec clocks in at 44 lines, slightly longer than our implementation. All in all, tiny. The whole commit was less than 100 lines of code.

AttributeAccessControllable
  it should behave like it has AttributeAccessControllable
    #attr_read_only(:attribute, ...) marks an attribute as read-only
    #read_only_attribute?(:attribute) returns true when marked read-only
    #read_only_attribute?(:attribute) returns false when not marked read-only (or not marked at all)
    #save! raises error when :attribute is read-only
    #save!(:context => :skip_read_only) is okay
    #save is invalid when :attribute is read-only
    #save(:context => :skip_read_only) is okay

In order to get to something “releasable” we needed a few more things, which we put on our To-Do list:

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file

The list got longer as we fleshed out both the documentation and the integration tests, as you’ll see in a moment, but first, let’s talk about

Getting the legal issues resolved

Pivotal’s open sourcing policy is straightforward and simple to execute; We don’t touch it. We write code for our clients, it’s their code to do with as they please. My particular client liked the work we did for them and thought it would make a great open source gem. The Director of Engineering signed off on the idea and I paired with him to create the github repository during a lunch break. The first commit was tiny, just a basic directory structure and the existing code. I don’t think the tests passed because they lacked a proper RSpec infrastructure.

Creating the gem

bundler gem DIRECTORY

is your best friend. It set up the layout for us, including an MIT License and a gem specification. It had a boilerplate README, too.

Writing the documentation for the code you wished you had

Next, we wrote a draft of the README file which documented what we knew: You needed a migration to create a column called :read_only_attributes and you needed to include the module into the class. Then we started thinking about the pain points of using our code as is. Wouldn’t it be nice if we could create the migration automatically? Rails generators do that sort of thing, how hard could it be? (Famous last words…) It became clear that we needed to test drive out some new features of the gem that supported the actual module.

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file
  4. Integration test

I am not a big cucumber fan, but…

Really, I’m not. I used to write Cucumber features all the time, but nowadays, I use a combination of RSpec and Capybara to get most of my day-to-day integration testing done. There is, however, one sweet spot for Cucumber that I’m finding more and more useful; A very high-level document that describes essential features in a way that a reader will say, “Ahhh, so that is how it is supposed to work!” Here’s a copy of the spec I wrote:

Feature: Read only attributes

Scenario: In a simple rails application
  Given a new rails application
  And I generate a new migration for the class "Person"
  And I generate an attribute access migration for the class "Person"
  And I have a test that exercises read-only
  When I run `rake spec`
  Then the output should contain "7 examples, 0 failures"

You probably won’t find any web-steps out there to handle these lines. I use Aruba to handle the dirty work of executing shell commands in a safe sandbox-y way. The step definition file hides most of the ugliness. Even so, most readers could figure out what to do, by hand, for each step.

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file
  4. Integration test
  5. Generator

Big generators

This gem was my first attempt at writing a generator, so it was awkward. I still don’t understand Thor properly. Fortunately, I happened upon Ammeter, which helped me write out test specs for the generator. If you’ve got good specs, then you can sometimes stumble along until you learn enough to get it right. Alex Rothenberg’s original blog post about the gem was quite informative, as were the test cases from the Devise gem.

I have to admit; constructing the generator was more complex than the original module! There are more “moving parts;” templates, usage files, specs, in addition to the generator itself. So there is a certain amount of overhead that might overwhelm the original content. On the other hand, I learned quite a bit, and the gem is far more useful.

require "spec_helper"
require 'generators/attribute_access/attribute_access_generator'

describe AttributeAccessGenerator do
  before do
    prepare_destination
    Rails::Generators.options[:rails][:orm] = :active_record
  end

  describe "the migration" do
    before { run_generator %w(Person) }
    subject { migration_file('db/migrate/create_people.rb') }
    it { should exist }
    it { should be_a_migration }
    it { should contain 'class CreatePeople < ActiveRecord::Migration' }
    it { should contain 'create_table :people do |t|'}
    it { should contain 't.text :read_only_attributes'}
  end

  describe "the class" do
    before { run_generator %w(Person) }
    subject { file('app/models/person.rb') }
    it { should exist }
    it { should contain 'include AttributeAccessControllable' }
  end

Some interesting things to note; you must require the generator, since it is not pulled in by default. The subject of each suite is a file, not the class AttributeAccessGenerator. The migration_file helper prepends the TIMESTAMP onto the migration file for you. If you need to set up more things for your test, destination_root is a helper with a path to the temporary directory. It remains after the tests have run, which makes it useful when debugging.

Here’s something else that I did not know, but it might help new generator writers; the order in which you define your methods in the generator class is significant. I don’t know how this is done, but each “method” in the generator class is executed in turn. This is important for my generator; the model class definition must exist before I inject the new content that mixes in the module, so I had to write the generate_model method before the inject_attribute_access_content method. I was scratching my head over that one for quite awhile.

require "rails/generators/active_record"

class AttributeAccessGenerator < ActiveRecord::Generators::Base
  source_root File.expand_path('../templates', __FILE__)

  def create_migration_file
    if (behavior == :invoke && model_exists?)
      migration_template "migration.rb", "db/migrate/add_read_only_attributes_to_#{table_name}"
    else
      migration_template "migration_create.rb", "db/migrate/create_#{table_name}"
    end
  end

  def generate_model
    invoke "active_record:model", [name], :migration => false unless model_exists? && behavior == :invoke
  end

  def inject_attribute_access_content
    class_path = class_name.to_s.split('::')

    indent_depth = class_path.size
    content = "  " * indent_depth + 'include AttributeAccessControllable' + "n"

    inject_into_class(model_path, class_path.last, content)
  end

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file
  4. Integration test
  5. Generator
  6. Shareable tests

Yo, I hear you like tests in your tests

Lastly, we want to share the testing love. The gem consumer should not have to write tests to drive out the same feature that we have already tested. That would not be very DRY. So, in order to make our shared behavior, er, um, shareable, we moved it into lib with a few wrappers, namely, the spec_support.rb file, which you can include in your own spec files to test drive adding the module to your own classes.

Which is where And I have a test that exercises read-only comes in. You can see this in the steps.rb file:

require 'spec_helper'
require 'attribute_access_controllable/spec_support'

describe Person do
  it_should_behave_like "it has AttributeAccessControllable", :attr1
end

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file
  4. Integration test
  5. Generator
  6. Shareable tests

Don’t be afraid to release v1.0.0

I am a strong believer in semantic versioning. I simply can not understand why some core ruby tools are still living in version zero land, even after years and years of development and use. So, after a couple of internal commits, we released v1.0.0 of the gem, and less than a day later released v1.1.0 and then v1.1.1! (You probably shouldn’t use anything less than v1.1.1)

An interesting mix

In summary, we used a lot of tools and techniques to go from a simple commit to a shareable gem:

  • Rails generators
  • Cucumber
  • Aruba
  • Ammeter
  • RSpec shared behaviors
  • Integration tests
  • Generator tests
  • Module tests

I encourage everyone to release as much of their work as possible because it raises the state of the art for us all. There are limits, of course, but that still affords lots of wiggle room. Small gems like attribute_access_controllable won’t change the world, but they ease the pain of staying DRY and we all get to learn a little something.

Thanks

To Social Chorus for choosing to open source this code. And to Pivotal Labs for encouraging a better way to do software engineering.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Dry DevOps with heroku_san

Ken Mayer
Sunday, March 25, 2012

Quiz time!

  1. How many times (each day) have you typed this at your console?
    git push heroku master and then forgotten to run
    heroku run rake db:migrate --app yellow-snow-3141 or
    heroku ps:restart
  2. Does your script support a multi-stage environments?
  3. Do you remember how to get to the application’s console process?
  4. Is your application’s configuration consistent across all stages?
  5. Are you deploy scripts tested?

Yeah, me too.

Enter heroku_san, the 85% solution, which allows me to do this:

$ rake staging deploy

And this is what happens: Maintenance mode is turned on; the latest green build is deployed to my staging server; all pending migrations are run, the app restarted, and then maintenance mode disabled. All while I go get a fresh cup of joe.

The value for us is that we don’t repeat ourselves, that our app will deploy, and we are confident that extending the toolkit is easy with a clear API and good test coverage. That’s a big win when you have dozens of apps deploying all day long.

Installation is pretty simple; add gem 'heroku_san' to your Gemfile (you are using Bundler, aren’t you?). This gem is only used locally, so you can add it to your group :development, :test block. Then create and edit a configuration file; config/heroku.yml[1]. The README file has more details and there’s a wiki on github with handy tips on how to extend the basic functionality in common ways.

So, why?

The gem has been around since 2010 and supported by a loyal core of maintainers. Version 2.1.1 brings changes large and small.

The biggest change since v1.3 is the creation of 2 new classes: Project and Stage, and a test suite to drive out the features. The task library is now almost entirely simple method calls, so there’s a lot of confidence that they will run correctly. Just to make sure, however, I’ve written an integration test, remote.feature which exercises the whole stack by creating an app on Heroku and run it through its paces. It’s worth a read.

There are some other new features worth highlighting:

  • Heroku stack support (aspen, bamboo and cedar)
  • Direct use of the Heroku::Client (instead of calls out to the shell)
  • tags — By adding an (optional) tag configuration value to your heroku.yml file, heroku_san will deploy only the most recent revision with that tag to your stage. This is great for creating a multi-stage environment, and when combined with auto_tagger and a continuous integration server (CI), you get a lot of bang for a little bit. See the wiki for more complete instructions.

tl;dr

Why write your own deploy scripts, again. Use heroku_san (or another gem like kumade)


Notes

[1]: I am working hard to remove the Rails dependencies.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Skype’s automatic gain control driving you INSANE?

Ken Mayer
Friday, July 15, 2011

If you’re working in an environment with a noisy background, Skype’s über-friendly automatic gain control can drive you crazy (open up sound preferences and watch your input levels change like magic)

Here’s a fix (that still works in Skype 5):

http://jon.smajda.com/2011/04/19/disable-automatic-gain-control-in-skype

tl;dr?

Open up ~/Library/Application Support/Skype/shared.xml in a text editor, find the <VoiceEng> section (w-a-a-a-a-y at the bottom), and add <AGC>0</AGC>. Like so:

<VoiceENG>
  <AGC>0</AGC>
  ... [One or more <MicVolume>'s. Leave them alone.]
</VoiceEng>

P.S. If you did read the article, <EC> is the Echo cancellation mode. I like to leave it on, but YMMV, especially if you’re wearing headphones and have no need for it.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Dropbox + git = Designer Luv

Ken Mayer
Tuesday, April 5, 2011

One of the thornier problems in our workflow is knowing when assets are delivered from the designer and keeping them in sync with our application as they change. We used to use e-mail, Skype or sticky notes. The trouble is that the designer’s file naming and directory structure were never quite the same as the application’s /public/images directory, so direct comparisons were impossible and we ended with a lot bookkeeping to make sure that we didn’t lose any changes. Our solution is to clone the project’s git repository into a folder inside a shared Dropbox folder.

Dropbox is cheap, if not free, available on all platforms, and synchronizes easily across most network connections. git is, well, awesome version control. We already had a Dropbox account set up for the project, and all team members had accepted invitations to join the share. We then made a git clone of our application source code in the Dropbox folder;

cd ~/Dropbox/MyProject
git clone https://github.com/myproject/myapp git-repository

We took a minute or 2 to show the designer how to find the images directory, and our basic rationale behind naming and directory structure. That became her “new” assets folder. We did not show the designer how to use git, nor do we need to. As far as she is concerned, it is just another folder. /public/images is even a familiar place for designers.

Here’s where the fun starts: We add the Dropbox/git-repository as a remote to our main project repository.

cd ~/myapp
git remote add dropbox file:///Users/me/Dropbox/MyProject/git-repository

Now, as far as our main project is concerned, the Dropbox folder is another remote, just like github.

During our daily workflow, whenever we need a new asset, or to look for a changed one, we cd over to the Dropbox folder with the git repository, change into the /public/images directory and look for what we need. We then use git add to stage and commit only those assets that we need. Back in our main project, we then

git pull dropbox master

from the Dropbox remote and voila! New images! Later, if the designer makes changes, we can simply run git status to see what’s changed, re-run git add, commit and pull. The only really gotcha is training our fingers not to type git commit -am '...'

We have found this to be a very fast, lightweight, way of integrating new assets into the project and keeping track of old ones. The designer is no longer worried about getting out of sync with our project, nor are we worried about missing design changes. As a bonus, there’s a history in the git logs, so we can review changes and even revert if necessary. All this without the designer ever learning a single git command, nor us worrying about our project directory getting accidentally clobbered.

[This is actually a very old trick that I've been using since the late 90's, but back then it was using CVS and NFS mounted shares!]

There are other benefits, too. Sometimes we’ll make minor fixes to the images delivered to us. By adding a remote to the Dropbox git repos, we can pull from our application master and then the designer gets our fixes.

cd ~/Dropbox/git-repository
git remote add myapp file:///Users/me/my app

Remember, it’s just a git repos! You can alway revert or reset to previous versions. In the most terrible worst case, you delete the whole Dropbox directory and start “over,” that is, at the HEAD of the repository master on github (or whereever).

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (783)
  • rails (117)
  • testing (90)
  • ruby (86)
  • ruby on rails (71)
  • jobs (62)
  • javascript (59)
  • techtalk (44)
  • ironblogger (42)
  • rspec (39)
  • bloggerdome (34)
  • productivity (34)
  • activerecord (30)
  • rubymine (30)
  • git (29)
  • gogaruco (29)
  • nyc (27)
  • design (24)
  • mobile (23)
  • pivotal tracker (22)
  • process (21)
  • cucumber (21)
  • jasmine (19)
  • ios (18)
  • tracker ecosystem (17)
  • webos (17)
  • objective-c (17)
  • fun (16)
  • android (16)
  • palm (16)
  • ci (16)
  • "soft" ware (16)
  • bdd (15)
  • tdd (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • css (14)
  • gem (13)
  • mouse-free development (12)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • api (12)
  • keyboard (11)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
Subscribe to workflow Feed
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Tools
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >