trout 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Joe Ferris
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,48 @@
1
+ h1. Trout
2
+
3
+ So your common files can swim upstream.
4
+
5
+ h2. Synopsis
6
+
7
+ Trout allows you to maintain a base version of special files (like Gemfile) in
8
+ one repository, and then syncronize just that file with several other
9
+ repositories. This means that you can update your Gemfile in the master
10
+ repository, and then get the latest of all the common gems that you use in each
11
+ project just by running "trout update Gemfile".
12
+
13
+ h2. Installation
14
+
15
+ <pre>
16
+ gem install trout
17
+ </pre>
18
+
19
+ h2. Usage
20
+
21
+ Once you have a master repository set up containing the file you want to
22
+ syncronize, you can check out that file anywhere using trout:
23
+
24
+ <pre>
25
+ trout checkout Gemfile git://github.com/someurl
26
+ </pre>
27
+
28
+ If you update the file in your master repository and want to get the changes,
29
+ you can use the update command:
30
+
31
+ <pre>
32
+ trout update Gemfile
33
+ </pre>
34
+
35
+ If there are any conflicts, trout will help you resolve them by merging the two
36
+ trees using diff3.
37
+
38
+ h2. Why
39
+
40
+ I wrote this primarily to manage Gemfiles between projects, hoping to avoid
41
+ using an "ancestor" git repository that gets dumped into the git history of
42
+ every new project. This also solves the "multiple inheritance" problem of git
43
+ repositories if you want to manage different files from different sources.
44
+
45
+ h2. Author
46
+
47
+ Copyright 2010 Joe Ferris
48
+
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'date'
4
+ require 'rake/gempackagetask'
5
+
6
+ require 'cucumber/rake/task'
7
+
8
+ desc 'Default: run the cucumber features.'
9
+ task :default => :cucumber
10
+
11
+ desc "Clean files generated by rake tasks"
12
+ task :clobber => [:clobber_rdoc, :clobber_rcov]
13
+
14
+ Cucumber::Rake::Task.new(:cucumber) do |t|
15
+ t.fork = true
16
+ t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'progress')]
17
+ end
18
+
19
+ eval("$specification = begin; #{IO.read('trout.gemspec')}; end")
20
+ Rake::GemPackageTask.new($specification) do |package|
21
+ package.need_zip = true
22
+ package.need_tar = true
23
+ end
data/bin/trout ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'trout'
4
+ Trout::CLI.run(ARGV)
5
+
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'aruba'
3
+ require 'fileutils'
4
+
5
+ Before do
6
+ FileUtils.rm_rf("tmp")
7
+ FileUtils.mkdir("tmp")
8
+ end
9
+
10
+ PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
11
+ BIN_PATH = File.join(PROJECT_ROOT, 'bin').freeze
12
+ LIB_PATH = File.join(PROJECT_ROOT, 'lib').freeze
13
+
14
+ ENV['PATH'] = [BIN_PATH, ENV['PATH']].join(':')
15
+ ENV['RUBYLIB'] = LIB_PATH
@@ -0,0 +1,54 @@
1
+ Feature: sync a Gemfile between two repositories
2
+
3
+ @puts @announce
4
+ Scenario: sync a Gemfile
5
+ Given a directory named "upstream_repo"
6
+ And a directory named "child_repo"
7
+ And a file named "upstream_repo/Gemfile" with:
8
+ """
9
+ source "http://rubygems.org"
10
+ gem "rails"
11
+ gem "mysql"
12
+ """
13
+ When I cd to "upstream_repo"
14
+ And I run "git init"
15
+ And I run "git add Gemfile"
16
+ And I run "git commit -m 'Added gemfile'"
17
+ And I cd to "../child_repo"
18
+ And I run "trout checkout Gemfile ../upstream_repo"
19
+ And I run "cat Gemfile"
20
+ Then the output should contain:
21
+ """
22
+ source "http://rubygems.org"
23
+ gem "rails"
24
+ gem "mysql"
25
+ """
26
+ When I cd to "../upstream_repo"
27
+ And I write to "Gemfile" with:
28
+ """
29
+ source "http://rubygems.org"
30
+ gem "rails"
31
+ gem "postgresql"
32
+ """
33
+ When I run "git add Gemfile"
34
+ And I run "git commit -m 'Changed to postgres'"
35
+ And I cd to "../child_repo"
36
+ When I append to "Gemfile" with:
37
+ """
38
+
39
+ gem "redcloth"
40
+ """
41
+ When I run "trout update Gemfile"
42
+ And I run "cat Gemfile"
43
+ Then the output should contain:
44
+ """
45
+ source "http://rubygems.org"
46
+ gem "rails"
47
+ <<<<<<< Gemfile
48
+ gem "mysql"
49
+ gem "redcloth"
50
+ =======
51
+ gem "postgresql"
52
+ >>>>>>> .trout/upstream
53
+ """
54
+
data/lib/trout.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'trout/cli'
2
+
data/lib/trout/cli.rb ADDED
@@ -0,0 +1,36 @@
1
+ require 'trout/managed_file'
2
+
3
+ module Trout
4
+ class CLI
5
+ def self.run(arguments)
6
+ new(arguments).run
7
+ end
8
+
9
+ attr_reader :arguments, :git_url, :filename, :file, :command
10
+
11
+ def initialize(arguments)
12
+ self.arguments = arguments
13
+ self.file = ManagedFile.new(filename)
14
+ end
15
+
16
+ def run
17
+ case command
18
+ when 'checkout'
19
+ self.git_url = arguments[2]
20
+ file.copy_from(git_url)
21
+ when 'update'
22
+ file.update
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def arguments=(arguments)
29
+ @arguments = arguments
30
+ self.command = arguments[0]
31
+ self.filename = arguments[1]
32
+ end
33
+
34
+ attr_writer :git_url, :filename, :file, :command
35
+ end
36
+ end
@@ -0,0 +1,124 @@
1
+ require 'fileutils'
2
+ require 'trout/version_list'
3
+
4
+ module Trout
5
+ class ManagedFile
6
+ attr_reader :filename, :checked_out_url
7
+
8
+ def initialize(filename)
9
+ @filename = filename
10
+ end
11
+
12
+ def copy_from(git_url)
13
+ checkout(git_url)
14
+ copy_to_destination
15
+ write_url_and_version
16
+ ensure
17
+ cleanup
18
+ end
19
+
20
+ def update
21
+ checkout(previous_git_url)
22
+ merge_to_destination
23
+ ensure
24
+ cleanup
25
+ end
26
+
27
+ private
28
+
29
+ def checkout(git_url)
30
+ run_or_fail("git clone #{git_url} #{working('git')}")
31
+ @checked_out_url = git_url
32
+ end
33
+
34
+ def copy_to_destination
35
+ FileUtils.cp(working('git', filename), filename)
36
+ end
37
+
38
+ def merge_to_destination
39
+ upstream = working('upstream')
40
+ at_last_update = working('at_last_update')
41
+ merge = working('merge')
42
+
43
+ FileUtils.cp(working('git', filename), upstream)
44
+
45
+ checkout_last_version
46
+ FileUtils.cp(working('git', filename), at_last_update)
47
+
48
+ enforce_newline(upstream)
49
+ enforce_newline(at_last_update)
50
+ enforce_newline(filename)
51
+
52
+ run("diff3 -mX #{filename} #{at_last_update} #{upstream} > #{merge}")
53
+ FileUtils.mv(merge, filename)
54
+ ensure
55
+ FileUtils.rm_rf(upstream)
56
+ FileUtils.rm_rf(at_last_update)
57
+ end
58
+
59
+ def cleanup
60
+ FileUtils.rm_rf(working('git'))
61
+ @checked_out_url = nil
62
+ end
63
+
64
+ def prepare_working_directory
65
+ FileUtils.mkdir(working_root)
66
+ end
67
+
68
+ def working_root
69
+ '.trout'
70
+ end
71
+
72
+ def write_url_and_version
73
+ version_list.update(filename,
74
+ 'git_url' => checked_out_url,
75
+ 'version' => checked_out_version)
76
+ end
77
+
78
+ def checked_out_version
79
+ git_command("rev-parse HEAD")
80
+ end
81
+
82
+ def checkout_last_version
83
+ git_command("checkout #{previous_git_version}")
84
+ end
85
+
86
+ def git_command(command)
87
+ run_or_fail("git --git-dir=#{working('git/.git')} --work-tree=#{working('git')} #{command}").strip
88
+ end
89
+
90
+ def previous_git_url
91
+ version_list.git_url_for(filename)
92
+ end
93
+
94
+ def previous_git_version
95
+ version_list.version_for(filename)
96
+ end
97
+
98
+ def version_list
99
+ @version_list ||= VersionList.new(working('versions'))
100
+ end
101
+
102
+ def working(*paths)
103
+ File.join(working_root, *paths)
104
+ end
105
+
106
+ def enforce_newline(path)
107
+ if IO.read(path)[-1].chr != "\n"
108
+ File.open(path, "a") { |file| file.puts }
109
+ end
110
+ end
111
+
112
+ def run(command)
113
+ `#{command} 2>&1`
114
+ end
115
+
116
+ def run_or_fail(command)
117
+ output = run(command)
118
+ unless $? == 0
119
+ raise "Command failed with status #{$?}:\n#{command}\n#{output}"
120
+ end
121
+ output
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,44 @@
1
+ require 'yaml'
2
+
3
+ module Trout
4
+ class VersionList
5
+ attr_reader :path, :entries
6
+
7
+ def initialize(path)
8
+ @path = path
9
+ end
10
+
11
+ def git_url_for(filename)
12
+ read
13
+ entries[filename]['git_url']
14
+ end
15
+
16
+ def version_for(filename)
17
+ read
18
+ entries[filename]['version']
19
+ end
20
+
21
+ def update(filename, info)
22
+ read
23
+ entries[filename] ||= {}
24
+ entries[filename].update(info)
25
+ write
26
+ end
27
+
28
+ private
29
+
30
+ def read
31
+ if File.exist?(path)
32
+ @entries = YAML.load(IO.read(path))
33
+ else
34
+ @entries = {}
35
+ end
36
+ end
37
+
38
+ def write
39
+ File.open(path, 'w') do |file|
40
+ file.write(YAML.dump(entries))
41
+ end
42
+ end
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trout
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Joe Ferris
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-10 00:00:00 -04:00
18
+ default_executable: trout
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: cucumber
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: aruba
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ description: |-
49
+ Trout allows you to maintain a base version of special
50
+ files (like Gemfile) in one repository, and then syncronize just that
51
+ file with several other repositories. This means that you can update your
52
+ Gemfile in the master repository, and then get the latest of all the
53
+ common gems that you use in each project just by running "trout update
54
+ Gemfile".
55
+ email: jferris@thoughtbot.com
56
+ executables:
57
+ - trout
58
+ extensions: []
59
+
60
+ extra_rdoc_files: []
61
+
62
+ files:
63
+ - LICENSE
64
+ - Rakefile
65
+ - README.textile
66
+ - lib/trout/cli.rb
67
+ - lib/trout/managed_file.rb
68
+ - lib/trout/version_list.rb
69
+ - lib/trout.rb
70
+ - features/support/env.rb
71
+ - features/sync_gemfile.feature
72
+ - bin/trout
73
+ has_rdoc: true
74
+ homepage: http://github.com/jferris/trout
75
+ licenses: []
76
+
77
+ post_install_message:
78
+ rdoc_options: []
79
+
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ requirements: []
101
+
102
+ rubyforge_project:
103
+ rubygems_version: 1.3.7
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: So your common files can swim upstream.
107
+ test_files:
108
+ - features/support/env.rb
109
+ - features/sync_gemfile.feature