TheOddLinguist
TheOddLinguist

Reputation: 677

Tagging release before deploying with Capistrano

I'm looking to build a nice little Capistrano recipe for deploying sites version controlled in Git.

In addition to some other things I'm working on adding, my first task is to tag the current release with the current date...and when that tag already exists (e.g. multiple releases in a day), append a letter.

I've written some working code, and I've tested it in my production.rb (using multistage in capistrano-ext)...but I have to think I could have written this better. For one, there's some huge repetition in the actual checking for existence of a tag. However, no matter what order I move things around, this is the only configuration that produces results.

Any ideas? Thanks in advance.

before 'deploy' do
  # Tag name is build_YYYYMMDD
  tag_name = "build_#{Time.now.strftime('%Y%m%d')}"
  check_tag = `git tag -l #{tag_name}`
  # If the tag exists, being appending letter suffix
  if not check_tag.empty?
    suffix = 'a'
    check_tag = `git tag -l #{tag_name}#{suffix}`
    while not check_tag.empty? do
      suffix.next!
      check_tag = `git tag -l #{tag_name}#{suffix}`
    end
    tag_name = "#{tag_name}#{suffix}"
  end
  # Tag with computed tag name
  p "Tagging #{tag_name}" # TODO How to output via Capistrano?
  system "git tag #{tag_name}"
  # Push tags to origin remote
  p "Pushing tag to origin" # TODO How to output via Capistrano?
  system "git push origin master --tags"
end

Upvotes: 13

Views: 5524

Answers (5)

lucas
lucas

Reputation: 1151

This updated version of the accepted answer worked for me:

desc 'Tag the deployed revision'
task :push_deploy_tag do
  env = fetch(:rails_env)
  timestamp = fetch(:release_timestamp)
  user = `git config --get user.name`.chomp
  email = `git config --get user.email`.chomp

  puts `git tag #{env}-#{timestamp} #{fetch :current_revision} -m "Deployed by #{user} <#{email}>"`
  puts `git push --tags origin`
end

Upvotes: 0

P&#229;l
P&#229;l

Reputation: 988

Thanks to @dunedain289 for a great answer. I took it a step further to try replicate heroku releases for capistrano. I needed a nice tool to know what was already deployed on the servers, and to make a diff with my current local branch:

  1. first, use @dunedain289's code, slightly modified to work for me

    task :push_deploy_tag do
       user = `git config --get user.name`.chomp
       email = `git config --get user.email`.chomp
       stage = "production" unless stage # hack, stage undefined for me
       puts `git tag #{stage}_#{release_name} -m "Deployed by #{user} <#{email}>"`
       puts `git push --tags origin`
     end
    
  2. Add a task to filter the release tags and diff the last

    task :diff do
      stage = "production" unless stage
      last_stage_tag = `git tag -l #{stage}* | tail -1`
      system("git diff #{last_stage_tag}", out: $stdout, err: :out)
    end
    
    #this preserves coloring if you have a .gitconfig with 
    [color "diff"]
      meta = yellow bold
      frag = magenta bold
      old = red bold
      new = green bold
    
  3. execute

    $ cap diff
    # awesome colored diff of production and local
    

Upvotes: 3

tmarkiewicz
tmarkiewicz

Reputation: 515

Regarding your question in the code about how to output the text via Capistrano. Just change p to puts and it displays fine.

Upvotes: 1

Adam Dymitruk
Adam Dymitruk

Reputation: 129584

Just add the short hash of the commit you are building from.

git log -1 --format=%h

Upvotes: 3

dunedain289
dunedain289

Reputation: 2388

I've done something similar with Capistrano. The easiest thing to do is tag using the timestamp name that Capistrano used during the deploy - that's YYYYMMDDHHMMSS, so it's really hard to get duplicates.

Example:

task :push_deploy_tag do
  user = `git config --get user.name`.chomp
  email = `git config --get user.email`.chomp
  puts `git tag #{stage}_#{release_name} #{current_revision} -m "Deployed by #{user} <#{email}>"`
  puts `git push --tags origin`
end

Upvotes: 24

Related Questions