trout 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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