tumbler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .bundle
data/CHANGELOG ADDED
@@ -0,0 +1,10 @@
1
+ == 0.0.1
2
+
3
+ * rollback version (Joshua Hull, 5c14fd9)
4
+
5
+ == 0.0.1
6
+
7
+ * reset version, changelog (Joshua Hull, 09e19dc)
8
+ * Updated changelog (Joshua Hull, 161da98)
9
+ * Bumped version from 0.0.0 to 0.0.1 (Joshua Hull, 654eb14)
10
+
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source :rubygems
2
+
3
+ gem 'callsite'
4
+ gem 'versionomy'
5
+ gem 'bundler'
6
+ gem 'json'
7
+
8
+ group :development do
9
+ gem 'rake'
10
+ gem 'rspec'
11
+ gem 'fakeweb'
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,51 @@
1
+ ---
2
+ dependencies:
3
+ fakeweb:
4
+ group:
5
+ - :development
6
+ version: ">= 0"
7
+ rake:
8
+ group:
9
+ - :development
10
+ version: ">= 0"
11
+ rspec:
12
+ group:
13
+ - :development
14
+ version: ">= 0"
15
+ versionomy:
16
+ group:
17
+ - :default
18
+ version: ">= 0"
19
+ json:
20
+ group:
21
+ - :default
22
+ version: ">= 0"
23
+ bundler:
24
+ group:
25
+ - :default
26
+ version: ">= 0"
27
+ callsite:
28
+ group:
29
+ - :default
30
+ version: ">= 0"
31
+ specs:
32
+ - rake:
33
+ version: 0.8.7
34
+ - blockenspiel:
35
+ version: 0.3.3
36
+ - bundler:
37
+ version: 0.9.26
38
+ - callsite:
39
+ version: 0.0.2
40
+ - fakeweb:
41
+ version: 1.2.8
42
+ - json:
43
+ version: 1.4.3
44
+ - rspec:
45
+ version: 1.3.0
46
+ - versionomy:
47
+ version: 0.4.0
48
+ hash: 607cf6d6ac15a00ffd67cd35247ddf8b124dc5a8
49
+ sources:
50
+ - Rubygems:
51
+ uri: http://gemcutter.org
data/README.rdoc ADDED
@@ -0,0 +1,85 @@
1
+ = Tumbler
2
+
3
+ Let's make gem development fun and not at all repetitive! A set of common tasks which should save you a fair bit of typing and give you more time for doing
4
+ useful things with your life.
5
+
6
+ == The pattern
7
+
8
+ Typically I want to make a gem, host it on github and tag versions. I want to use git as my source control, and use git to create sets of files. As well, I want to use Bundler to specify my dependencies, and then, I want those dependencies to automatically be in my gemspec. As well, I want to be a good citizen, and use gemspec's directly, instead of generating my gemspec from something.
9
+
10
+ == Using Tumbler
11
+
12
+ === For pre-existing projects
13
+
14
+ If you have a pre-existing project, and want to use Tumbler, you have to a do a few things. If you're starting from scratch, you can skip this section.
15
+
16
+ For existing projects, you need to configure it to tell Tumbler to do its work. This is all done from a file at the root of your project called <tt>Tumbler</tt>. A typical <tt>Tumbler</tt> file will look something like this:
17
+
18
+ # Names the gem
19
+ gem_name 'my_awesome_gem'
20
+
21
+ # Use this file to track the version
22
+ use_version do
23
+ filename 'VERSION'
24
+ end
25
+
26
+ # Use this file to hang onto your changelog
27
+ use_changelog do
28
+ filename 'CHANGELOG'
29
+ end
30
+
31
+ Your version number has to only be something parsable by Versionomy. The changelog will automatically be updated when you do releases.
32
+
33
+ As well, in your +Rakefile+, add the following:
34
+
35
+ require 'tumbler'
36
+ Tumbler.use_rake_tasks
37
+
38
+ Alternately, you can have Tumbler do all the work for you. If you install Tumbler to your system gems, you can say:
39
+
40
+ tumbler . -u --name gem_name
41
+
42
+ From inside your project directory, and it will take care of all these details for you.
43
+
44
+ === Generating an application from scratch
45
+
46
+ To generate an application from scratch, run the following:
47
+
48
+ tumbler your_gem_name
49
+
50
+ This will give you a skeleton of an application that you can fill out and make awesome.
51
+
52
+ === Day to day life with Tumbler as your friend
53
+
54
+ Once you've got Tumbler up and running, you'll have access to the following tasks:
55
+
56
+ rake gem:build # Build the gem
57
+ rake gem:install # Install the gem
58
+ rake gem:push # Push the gem
59
+ rake version:major:bump # Bump version from 0.2.12 -> 1.0.0
60
+ rake version:major:release # Bump version from 0.2.12 -> 1.0.0 and push
61
+ rake version:minor:bump # Bump version from 0.2.12 -> 0.3.0
62
+ rake version:minor:release # Bump version from 0.2.12 -> 0.3.0 and push
63
+ rake version:push # Push current version into git
64
+ rake version:tag # Tag current version into git
65
+ rake version:tiny:bump # Bump version from 0.2.12 -> 0.2.13
66
+ rake version:tiny:release # Bump version from 0.2.12 -> 0.2.13 and push
67
+
68
+ Let's take a look a some common workflow:
69
+
70
+ ==== Releasing a new Gem
71
+
72
+ You can either do
73
+
74
+ rake version:(tiny|minor|major):release
75
+
76
+ Which will bump the version, tag it in git, regenerate the changelog from your git commits and push the whole thing to rubygems and your remote or you can do it in two steps:
77
+
78
+ rake version:(tiny|minor|major):bump
79
+ rake version:release
80
+
81
+ This gives you a chance to review the changelog and do anything else you'd like to do before taking the plunge.
82
+
83
+ == What's next?
84
+
85
+ I'm sure there are bugs and other common workflows that I will add/document, but basically, I just wanna type a lot less when I bump a gem, and get things done.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
2
+ require 'tumbler'
3
+ Tumbler.use_rake_tasks
4
+
5
+ require 'spec'
6
+ require 'spec/rake/spectask'
7
+ Spec::Rake::SpecTask.new(:spec) do |t|
8
+ t.spec_opts ||= []
9
+ t.spec_opts << "--options" << "spec/spec.opts"
10
+ t.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
data/Tumbler ADDED
@@ -0,0 +1,9 @@
1
+ gem_name 'tumbler'
2
+
3
+ use_version do
4
+ filename 'VERSION'
5
+ end
6
+
7
+ use_changelog do
8
+ filename 'CHANGELOG'
9
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/tumbler ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/env ruby
2
+
3
+ require 'fileutils'
4
+
5
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
6
+ require 'tumbler'
7
+
8
+ Tumbler::CLI.run(ARGV)
@@ -0,0 +1,62 @@
1
+ require 'tempfile'
2
+ require 'fileutils'
3
+
4
+ class Tumbler
5
+ class Changelog
6
+ include Runner
7
+
8
+ DEFAULT_FILE = 'CHANGELOG'
9
+
10
+ attr_reader :file
11
+
12
+ def initialize(manager, &block)
13
+ @manager = manager
14
+ instance_eval(&block) if block
15
+ end
16
+
17
+ def filename(file)
18
+ @basefile = file
19
+ @file = File.join(@manager.base, file)
20
+ end
21
+
22
+ def write_version_header
23
+ prepend "== #{@manager.version}\n\n"
24
+ end
25
+
26
+ def prepend(data)
27
+ if @manager.noop
28
+ @manager.dry "Prepending #{data} to #{@basefile}"
29
+ else
30
+ Tempfile.open('changelog') do |tmp|
31
+ tmp.puts data
32
+ File.open(@file) do |f|
33
+ f.each do |line|
34
+ tmp << line
35
+ end
36
+ end
37
+ tmp.close
38
+ FileUtils.copy(tmp.path, @file)
39
+ File.unlink(tmp.path)
40
+ end
41
+ end
42
+ end
43
+
44
+ def update
45
+ ensure_existence
46
+ prepend("\n")
47
+ prepend(@manager.latest_changes.inject('') { |changes, change| changes << "* #{change.summary} (#{change.author}, #{change.hash})\n" })
48
+ end
49
+
50
+ def ensure_existence
51
+ File.open(@file, 'w') {|f| f << ''} unless File.exist?(@file)
52
+ end
53
+
54
+ def commit
55
+ sh "git commit #{@basefile} -m'Updated changelog'"
56
+ end
57
+
58
+ def base
59
+ @manager.base
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,69 @@
1
+ require 'optparse'
2
+
3
+ class Tumbler
4
+ class CLI
5
+ def self.run(args)
6
+ CLI.new(args).run
7
+ end
8
+
9
+ def initialize(args)
10
+ @options = {
11
+ :changelog => Changelog::DEFAULT_FILE,
12
+ :version => Version::INITIAL_VERSION
13
+ }
14
+ parser.parse!(args)
15
+ end
16
+
17
+ def parser
18
+ OptionParser.new do |opts|
19
+ opts.banner = "Usage: tumbler name [options]"
20
+
21
+ opts.separator ""
22
+ opts.separator "Options:"
23
+
24
+ opts.on("-cVALUE", "--changelog=VALUE", "Set changelog file") do |v|
25
+ @options[:changelog] = v
26
+ end
27
+
28
+ opts.on("-u", "--update", "Update existing application") do |v|
29
+ @options[:update] = nil
30
+ end
31
+
32
+ opts.on("-nc", "--no-changelog", "Disable changelog") do |v|
33
+ @options[:changelog] = nil
34
+ end
35
+
36
+ opts.on("-vVALUE", "--version=VALUE", "Set version file") do |v|
37
+ @options[:version] = v
38
+ end
39
+
40
+ opts.on("-nv", "--no-value", "Disable version") do |v|
41
+ @options[:version] = nil
42
+ end
43
+
44
+ opts.on("-vNAME", "--name=NAME", "Set gem name") do |v|
45
+ @options[:name] = v
46
+ end
47
+
48
+ opts.on_tail("-h", "--help", "Show this help message.") { puts opts; exit }
49
+ end
50
+ end
51
+
52
+ def run
53
+ puts @options.inspect
54
+ app_name = ARGV.first
55
+
56
+ if app_name.nil?
57
+ raise 'You must supply an application name.'
58
+ elsif @options.key?(:update)
59
+ Tumbler::Updater.new(app_name, @options).update
60
+ elsif File.exist?(app_name)
61
+ raise "There is already a directory named #{app_name}"
62
+ else
63
+ FileUtils.mkdir_p(app_name)
64
+ Tumbler::Generate.app(app_name, app_name, @options).write
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,35 @@
1
+ class Tumbler
2
+ class Gem
3
+ include Runner
4
+
5
+ def initialize(manager)
6
+ @manager = manager
7
+ end
8
+
9
+ def base
10
+ @manager.base
11
+ end
12
+
13
+ def push
14
+ build
15
+ sh("gem push #{built_gem_path}")
16
+ end
17
+
18
+ def built_gem_path
19
+ "#{@manager.name}-#{@manager.version.to_s}.gem"
20
+ end
21
+
22
+ def install
23
+ build
24
+ exec("sudo gem install #{built_gem_path}")
25
+ end
26
+
27
+ def spec_path
28
+ "#{@manager.name}.gemspec"
29
+ end
30
+
31
+ def build
32
+ sh("bundle exec gem build #{spec_path}")
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,4 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'tumbler') unless Kernel.const_defined?(:Tumbler)
2
+ root = File.expand_path(File.dirname(Callsite.parse(caller).find{|c| c.filename =~ /\.gemspec$/}.filename))
3
+ Tumbler.send(:remove_const, :Gemspec) if (Tumbler.const_defined?(:Gemspec))
4
+ Tumbler::Gemspec = Tumbler::GemspecHelper.new(Tumbler.new(root))
@@ -0,0 +1,38 @@
1
+ class Tumbler
2
+ class GemspecHelper
3
+ def initialize(manager)
4
+ @manager = manager
5
+ end
6
+
7
+ def version
8
+ @manager.version.to_s
9
+ end
10
+
11
+ def name
12
+ @manager.name
13
+ end
14
+
15
+ def git_files
16
+ @git_files ||= Dir.chdir(@manager.base) { `git ls-files`.split("\n") }
17
+ end
18
+
19
+ def files(test = nil)
20
+ git_files.select{|f| test.nil? or f.index(test) }
21
+ end
22
+
23
+ def bin_files
24
+ git_files.select{|f| f[/^bin\//] }.map{|f| f[/bin\/(.*)/, 1]}
25
+ end
26
+
27
+ def inject_dependencies(gemspec)
28
+ @manager.bundler.dependencies.each do |dep|
29
+ gemspec.add_runtime_dependency(dep.name, *dep.requirements_list) if dep.groups.include?(:default)
30
+ gemspec.add_development_dependency(dep.name, *dep.requirements_list) if dep.groups.include?(:development)
31
+ end
32
+ end
33
+
34
+ def date
35
+ Time.new.strftime("%Y-%m-%d")
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,137 @@
1
+ require 'rubygems'
2
+ require 'erb'
3
+
4
+ class Tumbler
5
+ class Generate
6
+
7
+ include Runner
8
+
9
+ def self.app(dir, name, opts = {})
10
+ generator = Generate.new(dir, name)
11
+ generator.version = opts[:version] if opts[:version]
12
+ generator.changelog = opts[:changelog] if opts.key?(:changelog)
13
+ generator.development_dependencies << ::Gem::Dependency.new('tumbler')
14
+ if opts[:dependencies]
15
+ Array(opts[:dependencies]).each do |dep|
16
+ generator.dependencies << (dep.is_a?(Array) ? ::Gem::Dependency.new(*dep) : ::Gem::Dependency.new(dep))
17
+ end
18
+ end
19
+ if opts[:development_dependencies]
20
+ Array(opts[:development_dependencies]).each do |dep|
21
+ generator.development_dependencies << (dep.is_a?(Array) ? ::Gem::Dependency.new(*dep) : ::Gem::Dependency.new(dep))
22
+ end
23
+ end
24
+ generator
25
+ end
26
+
27
+ attr_reader :development_dependencies, :dependencies, :base
28
+ attr_accessor :version, :changelog
29
+
30
+ def initialize(dir, name)
31
+ @base = dir
32
+ @name = name
33
+ @dependencies = []
34
+ @development_dependencies = []
35
+ @version = Version::INITIAL_VERSION
36
+ @changelog = Changelog::DEFAULT_FILE
37
+ end
38
+
39
+ def write
40
+ write_gemspec
41
+ write_gemfile
42
+ write_version
43
+ write_changelog
44
+ write_rakefile
45
+ write_tumbler_config
46
+ sh 'git init'
47
+ initial_commit
48
+ end
49
+
50
+ def initial_commit
51
+ sh 'git init'
52
+ sh 'git add .'
53
+ sh 'git commit -a -m"Initial commit"'
54
+ sh "git tag #{@version}"
55
+ end
56
+
57
+ def gemfile_file
58
+ File.join(@base, 'Gemfile')
59
+ end
60
+
61
+ def config_file
62
+ File.join(@base, 'Tumbler')
63
+ end
64
+
65
+ def init_version
66
+ return unless @version
67
+ File.open(@version.file, "w") {|f| f.puts @version.value.to_s} unless File.exist?(@version.file)
68
+ end
69
+
70
+ def init_changelog
71
+ return unless @changelog
72
+ File.open(@changelog.file, "w") {|f| f << ''} unless File.exist?(@changelog.file)
73
+ end
74
+
75
+ def write_version
76
+ File.open(File.join(@base, Version::DEFAULT_FILE), 'w') {|f| f << @version }
77
+ end
78
+
79
+ def write_gemfile
80
+ File.open(gemfile_file, 'w') {|f| f << generate_gemfile }
81
+ end
82
+
83
+ def write_rakefile
84
+ FileUtils.cp(template_path('Rakefile'), @base)
85
+ end
86
+
87
+ def write_gemspec
88
+ File.open(File.join(@base, "#{@name}.gemspec"), 'w') {|f| f << generate_gemspec }
89
+ end
90
+
91
+ def write_changelog
92
+ File.open(File.join(@base, @changelog), 'w') {|f| f << '' } if @changelog
93
+ end
94
+
95
+ def write_tumbler_config
96
+ File.open(config_file, 'w') {|f| f << generate_tumbler_conf}
97
+ end
98
+
99
+ def git_email
100
+ sh('git config user.email').strip rescue 'user.email'
101
+ end
102
+
103
+ def git_name
104
+ sh('git config user.name').strip rescue 'user.name'
105
+ end
106
+
107
+ def github_user
108
+ sh('git config github.user').strip rescue 'github.user'
109
+ end
110
+
111
+ def template_path(path)
112
+ File.join(File.dirname(__FILE__), '..', '..', 'template', path)
113
+ end
114
+
115
+ def generate_tumbler_conf
116
+ template = ERB.new(File.read(template_path('generic.Tumbler.erb')))
117
+ template.result(binding)
118
+ end
119
+
120
+ def generate_gemfile
121
+ template = ERB.new <<-EOF
122
+ source :rubygems
123
+
124
+ <% @dependencies.each do |dep|%>gem <%=dep.name.inspect%>, <%=dep.requirement.to_s.inspect%><%="\n"%><% end %>
125
+ group(:development) do
126
+ <% development_dependencies.each do |dep|%> gem <%=dep.name.inspect%>, <%=dep.requirement.to_s.inspect%><%="\n"%><% end %>
127
+ end
128
+ EOF
129
+ template.result(binding)
130
+ end
131
+
132
+ def generate_gemspec
133
+ template = ERB.new(File.read(template_path('generic.gemspec.erb')))
134
+ template.result(binding)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,81 @@
1
+ class Tumbler
2
+ class Raketasks
3
+
4
+ def self.register(base, name)
5
+ if manager = Tumbler.load(base)
6
+ tasks = Raketasks.new(manager)
7
+ tasks.activate_project_tasks(name)
8
+ else
9
+ raise "This is not properly configured for tumbler probably due to a lack of a Tumbler configuration file."
10
+ end
11
+ end
12
+
13
+ def initialize(manager)
14
+ @manager = manager
15
+ end
16
+
17
+ def activate_project_tasks(protect_namespace)
18
+ tasks = proc do
19
+ namespace :gem do
20
+ desc "Build the gem"
21
+ task :build do
22
+ @manager.gem.build
23
+ end
24
+
25
+ desc "Push the gem"
26
+ task :push do
27
+ @manager.gem.push
28
+ end
29
+
30
+ desc "Install the gem"
31
+ task :install do
32
+ @manager.gem.install
33
+ end
34
+ end
35
+
36
+ namespace :git do
37
+ task :check_clean do
38
+ @manager.clean? or raise("There are files that need to be committed first.")
39
+ end
40
+
41
+ task :check_uptodate do
42
+ @manager.update or raise("Unable to update first.")
43
+ end
44
+ end
45
+
46
+ namespace :version do
47
+
48
+ desc "Tag current version into git"
49
+ task :tag do
50
+ @manager.tag
51
+ end
52
+
53
+ desc "Push current version into git"
54
+ task :push do
55
+ @manager.tag_and_push
56
+ end
57
+
58
+ @manager.version.field_names.each do |field|
59
+ namespace field do
60
+ desc "Bump version from #{@manager.version.to_s} -> #{@manager.version.value.bump(field).to_s}"
61
+ task :bump do
62
+ @manager.bump_and_commit(field)
63
+ end
64
+
65
+ desc "Bump version from #{@manager.version.to_s} -> #{@manager.version.value.bump(field).to_s} and push"
66
+ task :release do
67
+ @manager.bump_and_push(field)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ if protect_namespace
75
+ namespace protect_namespace, &tasks
76
+ else
77
+ tasks.call
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,26 @@
1
+ class Tumbler
2
+ module Runner
3
+
4
+ attr_accessor :noop
5
+
6
+ def sh(cmd)
7
+ output, code = sh_with_code(cmd)
8
+ code == 0 ? output : raise(output)
9
+ end
10
+
11
+ def dry(message)
12
+ puts message
13
+ end
14
+
15
+ def sh_with_code(cmd)
16
+ if noop
17
+ dry("Running `#{cmd}'")
18
+ ['' , 0]
19
+ else
20
+ output = ''
21
+ Dir.chdir(base) { output = `#{cmd}` }
22
+ [output, $?]
23
+ end
24
+ end
25
+ end
26
+ end