Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 9cf095eb1cbcd409602543fd48fe5362ec6e9487 @zilkey committed Mar 28, 2009
@@ -0,0 +1,8 @@
+---
+:author: Jeff Dean
+:summary: Helps you automatically create tags for each stage in a multi-stage deploment and deploy from the latest tag from the previous environment
+:name: autotagger
+:dependencies:
+- capistrano
+:homepage: http://github.com/zilkey/git_tagger/tree/master
+:version: 0.0.1
@@ -0,0 +1,4 @@
+pkg/*
+rdoc/*
+*.gem
+.DS_Store
@@ -0,0 +1,20 @@
+Copyright (c) 2009 [Jeff Dean]
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,79 @@
+# AutoTagger
+
+AutoTagger allows you to create a date-stamped tag for each stage of your deployment, and deploy from the last tag from the previous environment.
+
+Let's say you have the following workflow:
+
+ * Run all test on a Continuous Integration (CI) server
+ * Deploy to a staging server
+ * Deploy to a production server
+
+You can use the `autotag` command to tag releases on your CI box, then use the capistrano tasks to auto-tag each release.
+
+## Capistrano Integration
+
+Example deploy file:
+
+ require 'release_tagger'
+
+ # The :stages variable is required
+ set :stages, [:ci, :staging, :production]
+
+ # The :working_directory variable is optional, and defaults to Dir.pwd
+ # :working_directory can be an absolute or relative path
+ set :working_directory, "../../"
+
+ task :production do
+ # In each of your environments that need auto-branch setting, you need to set :current_stage
+ set :current_stage, :production
+ end
+
+ task :staging do
+ # If you do not set current_stage, it will not auto-set your branch
+ # set :current_stage, :staging
+ end
+
+ # You need to add the before/ater callbacks yourself
+ before "deploy:update_code", "release_tagger:set_branch"
+ after "deploy:update_code", "release_tagger:create_tag"
+
+Assume you have the following tags in your git repository:
+
+ * ci/01
+ * staging/01
+ * production/01
+
+The deployments would look like this:
+
+ cap staging deploy # => ci/01
+ cap production deploy # => staging/01
+
+You can override with with the -Shead and -Stag options
+
+ cap staging deploy -Shead=true # => master
+ cap staging deploy -Stag=staging/01 # => staging/01
+
+## The autotag executable
+
+ autotag -h
+ autotag demo
+ autotag demo .
+ autotag demo /Users/me/foo
+
+## Known Issues
+
+ * DOES NOT work with capistrano ext multi-stage
+ * It will accept invalid tag names (if you specify a tag name with a space, it will blow up when you try to create the tag)
+
+## Things that might be useful
+
+ * Make it possible to define a different remote other than "origin"
+ * Make it possible to define a different default branch other than "master"
+ * Make it work with either cap-ext multistage or single-file deploy.rb files
+ * Make it possible to provide your own tag naming convention (like the PaperClip string interpolation), instead of relying on <prefix>/<timestamp>
+
+## Links
+
+ * http://codeintensity.blogspot.com/2008/06/changelogs-and-deployment-notification.html
+
+Copyright (c) 2009 [Jeff Dean], released under the MIT license
@@ -0,0 +1,45 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{auto_tagger}
+ s.version = "0.0.1"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Jeff Dean"]
+ s.date = %q{2009-03-28}
+ s.default_executable = %q{autotag}
+ s.executables = ["autotag"]
+
+ files = []
+
+ ['lib', 'recipes', 'bin', 'spec'].each do |dir|
+ files += Dir.glob(File.join(File.dirname(__FILE__), dir, "**", "*"))
+ end
+
+ ['MIT-LICENSE','README.md'].each do |file|
+ files << File.join(File.dirname(__FILE__), file)
+ end
+
+ s.files = files
+
+ puts s.files
+
+ s.email = "jeff at zilkey dot com"
+ s.homepage = %q{http://github.com/zilkey/git_tagger/tree/master}
+ s.require_paths = ["lib", "recipes"]
+ s.rubygems_version = %q{1.3.1}
+ s.summary = %q{Helps you automatically create tags for each stage in a multi-stage deploment and deploy from the latest tag from the previous environment}
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 2
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q<capistrano>, [">= 2.5.3"])
+ else
+ s.add_dependency(%q<capistrano>, [">= 2.5.3"])
+ end
+ else
+ s.add_dependency(%q<capistrano>, [">= 2.5.3"])
+ end
+end
@@ -0,0 +1,20 @@
+#!/usr/bin/env ruby
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "auto_tagger"))
+
+def usage
+ puts
+ puts "USAGE: #{File.basename($0)} [-h] [<stage> <repository>]"
+ puts
+ puts ' where: -h displays this help message'
+ puts ' stage sets the tag prefix'
+ puts ' repository sets the repository to act on'
+ puts
+ exit 1
+end
+
+if ARGV[0] && ARGV[0] == "-h"
+ usage
+elsif ARGV[0]
+ AutoTagger.new(ARGV[0], ARGV[1]).create_tag
+ exit 1
+end
@@ -0,0 +1,9 @@
+[
+ 'commander',
+ 'repository',
+ 'tag',
+ 'auto_tagger',
+ 'capistrano_helper'
+].each do |file|
+ require File.expand_path(File.join(File.dirname(__FILE__), "auto_tagger", file))
+end
@@ -0,0 +1,26 @@
+class AutoTagger
+
+ class EnvironmentCannotBeBlankError < StandardError; end
+
+ attr_reader :stage, :repository, :working_directory
+
+ def initialize(stage, path = nil)
+ raise EnvironmentCannotBeBlankError if stage.to_s.strip == ""
+ @working_directory = File.expand_path(path ||= Dir.pwd)
+ @repository = Repository.new(@working_directory)
+ @stage = stage
+ end
+
+ def create_tag
+ repository.tags.fetch
+ new_tag = repository.tags.create(stage)
+ repository.tags.push
+ new_tag
+ end
+
+ def latest_tag
+ repository.tags.fetch
+ repository.tags.latest_from(stage)
+ end
+
+end
@@ -0,0 +1,38 @@
+class CapistranoHelper
+
+ class NoStagesSpecifiedError < StandardError
+ def message
+ "You must set the :stages variable to an array, like set :stages, [:ci, :demo]"
+ end
+ end
+
+ attr_reader :variables, :stages, :current_stage, :working_directory
+
+ def initialize(variables)
+ raise NoStagesSpecifiedError unless variables[:stages]
+ @variables = variables
+ @stages = variables[:stages]
+ @current_stage = variables[:current_stage]
+ @working_directory = variables[:working_directory] || Dir.pwd
+ end
+
+ def previous_stage
+ if current_stage
+ index = stages.index(current_stage) - 1
+ stages[index] if index > -1
+ end
+ end
+
+ def branch
+ if variables.has_key?(:head)
+ variables[:branch]
+ elsif variables.has_key?(:tag)
+ variables[:tag]
+ elsif previous_stage && (latest = AutoTagger.new(previous_stage, working_directory).latest_tag)
+ latest
+ else
+ variables[:branch]
+ end
+ end
+
+end
@@ -0,0 +1,16 @@
+class Commander
+ class << self
+ def execute(path, cmd)
+ `#{command_in_context(path, cmd)}`
+ end
+
+ def execute!(path, cmd)
+ system command_in_context(path, cmd)
+ end
+
+ def command_in_context(path, cmd)
+ "cd #{path} && #{cmd}"
+ end
+ end
+end
+
@@ -0,0 +1,38 @@
+class Repository
+
+ class NoPathProvidedError < StandardError; end
+ class NoSuchPathError < StandardError; end
+ class InvalidGitRepositoryError < StandardError; end
+ class GitCommandFailedError < StandardError; end
+
+ attr_reader :path
+
+ def initialize(path)
+ if path.to_s.strip == ""
+ raise NoPathProvidedError
+ elsif ! File.exists?(path)
+ raise NoSuchPathError
+ elsif ! File.exists?(File.join(path, ".git"))
+ raise InvalidGitRepositoryError
+ else
+ @path = path
+ end
+ end
+
+ def ==(other)
+ other.is_a?(Repository) && other.path == path
+ end
+
+ def tags
+ @tags ||= Tag.new(self)
+ end
+
+ def run(cmd)
+ Commander.execute(path, cmd)
+ end
+
+ def run!(cmd)
+ Commander.execute!(path, cmd) || raise(GitCommandFailedError)
+ end
+
+end
@@ -0,0 +1,38 @@
+class Tag
+
+ attr_reader :repository
+
+ def initialize(repository)
+ @repository = repository
+ end
+
+ def find_all
+ repository.run("git tag").split("\n")
+ end
+
+ def fetch
+ repository.run! "git fetch origin --tags"
+ end
+
+ def latest_from(stage)
+ find_all.select{|tag| tag =~ /^#{stage}/}.sort.last
+ end
+
+ def push
+ repository.run! "git push origin --tags"
+ end
+
+ def create(stage)
+ # git tag -a -m 'Successful continuous integration build on #{timestamp}' #{tag_name}"
+ tag_name = name_for(stage)
+ repository.run! "git tag #{tag_name}"
+ tag_name
+ end
+
+ private
+
+ def name_for(stage)
+ "%s/%s" % [stage, Time.now.utc.strftime('%Y%m%d%H%M%S')]
+ end
+
+end
@@ -0,0 +1,26 @@
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "auto_tagger"))
+
+Capistrano::Configuration.instance(:must_exist).load do
+ namespace :release_tagger do
+ desc %Q{
+ Sets the branch to the latest tag from the previous stage.
+ Use -Shead=true to set the branch to master, -Stag=<tag> to specify the tag explicitly.
+ }
+ task :set_branch do
+ branch_name = CapistranoHelper.new(variables).branch
+ set :branch, branch_name
+ logger.info "setting branch to #{branch_name.inspect}"
+ end
+
+ desc %Q{Creates a tag using the current_stage variable}
+ task :create_tag do
+ if variables[:current_stage]
+ tag_name = AutoTagger.new(variables[:current_stage], variables[:working_directory]).create_tag
+ logger.info "created and pushed tag #{tag_name}"
+ else
+ logger.info "AUTO TAGGER WARNING: skipping auto-creation of tag. Please specify :current_stage to enable auto-creation of tags (like set :current_stage, :ci)."
+ end
+ end
+ end
+end
+
Oops, something went wrong.

0 comments on commit 9cf095e

Please sign in to comment.