munkey 0.4.0

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/README.rdoc ADDED
@@ -0,0 +1,28 @@
1
+ = Munkey
2
+
3
+ Uses git to mirror an ftp server and track local changes. These can then be
4
+ pushed back up to the ftp server or additional changes pulled down from the
5
+ ftp server.
6
+
7
+ Solves my problem of working on a website locally but needing to interact
8
+ with other developers editing directly the server via ftp.
9
+
10
+ This is not very efficient yet since it downloads all the (non-.gitignore'd)
11
+ files each time.
12
+
13
+ Note: Depends on net-ftp-list gem so be sure to install that as well
14
+
15
+ == Usage
16
+
17
+ munkey clone [--ignore=ignoresfile] ftp://usr:pwd@host/dir [dst]
18
+
19
+ munkey pull [--no-merge] [--full-download] - pulls in new changes from ftp
20
+
21
+ munkey push - pushes changes back into ftp server. Important to have pulled
22
+ recent changes from ftp since munkey will overwrite locally
23
+ modified files.
24
+
25
+ == Caveats
26
+
27
+ * Gitignore support doesn't understand inverse (!...) lines -- patches welcome
28
+ * Probably doesn't work on Windows (or any non-unix platform)
data/bin/munkey ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'munkey'
4
+
5
+ class MunkeyBin
6
+ def initialize(args)
7
+ @options = {}
8
+ @options[:verbose] = args.delete('--quiet') ? false : true
9
+ end
10
+
11
+ def clone(args)
12
+ ignores = read_ignores(args.shift) if /^--ignore=/ =~ args.first
13
+ src = args.shift
14
+ dst = args.shift
15
+ dst ||= join_to_pwd(src.split('/').last) if src
16
+ dst = join_to_pwd(dst) unless dst.slice(0, 1) == '/'
17
+ show_help unless src && dst
18
+ Munkey::clone src, dst, @options.merge(:ignores => ignores)
19
+ end
20
+
21
+ def pull(args)
22
+ merge = args.delete('--no-merge') ? false : true
23
+ quick = args.delete('--full-download') ? false : true
24
+ Munkey.new(Dir.pwd, @options).pull(:merge => merge, :quick => quick)
25
+ end
26
+
27
+ def push(args)
28
+ dryrun = args.delete('--dry-run') ? true : false
29
+ Munkey.new(Dir.pwd, @options).push(dryrun)
30
+ end
31
+
32
+ def show_help
33
+ puts "Usage: munkey <command> [options] [args]"
34
+ puts
35
+ puts "munkey clone [--quiet] [--ignore=ignoresfile] ftp://usr:pwd@host/dir [dst]"
36
+ puts "\t- Clone from FTP source"
37
+ puts "munkey pull [--quiet] [--no-merge] [--full-download]"
38
+ puts "\t- Pull in changes from FTP source"
39
+ puts "munkey push [--quiet] [--dry-run]"
40
+ puts "\t- Push your changes back to FTP source"
41
+ exit
42
+ end
43
+
44
+ protected
45
+ def join_to_pwd(path)
46
+ /^\// =~ path ? path : File.join(Dir.pwd, path)
47
+ end
48
+
49
+ def read_ignores(arg)
50
+ ignore_file = arg.gsub(/^--ignore=/, '')
51
+ File.read join_to_pwd(ignore_file)
52
+ end
53
+ end
54
+
55
+ munkey = MunkeyBin.new(ARGV)
56
+ if !ARGV.empty? and munkey.respond_to?(cmd = ARGV.shift)
57
+ begin
58
+ munkey.send cmd, ARGV
59
+ rescue NoMethodError
60
+ puts "WARNING: Invalid FTP Source"
61
+ end
62
+ else
63
+ munkey.show_help
64
+ end
@@ -0,0 +1,48 @@
1
+ class GitignoreParser
2
+ DEFAULT_IGNORES = ".git\n"
3
+
4
+ class << self
5
+ def parse(gitpath)
6
+ new(gitpath)
7
+ end
8
+ end
9
+
10
+ def initialize(gitpath)
11
+ @gitpath = gitpath
12
+
13
+ ignore_file = File.join(@gitpath, '.gitignore')
14
+ gitignore = DEFAULT_IGNORES
15
+ gitignore += File.read(ignore_file) if File.exist?(ignore_file)
16
+
17
+ @globs = []
18
+ rx = gitignore.split("\n").map do |i|
19
+ i.strip!
20
+ if i == '' or i.slice(0,1) == '#'
21
+ nil
22
+ elsif not i.include?('*')
23
+ if i.slice(-1,1) == '/'
24
+ i
25
+ else
26
+ "^#{i}|\/#{i}"
27
+ end
28
+ else
29
+ @globs << i
30
+ nil
31
+ end
32
+ end.compact.join("|")
33
+ @regex = (rx == '' ? nil : Regexp.new(rx))
34
+ end
35
+
36
+ def ignore?(path)
37
+ raise NotAbsolutePathError unless path.slice(0, 1) == '/'
38
+ relpath = path.gsub %r{^#{Regexp.escape(@gitpath)}\/}, ''
39
+ @regex =~ relpath || @globs.any? {|g| File.fnmatch(g, relpath) }
40
+ end
41
+
42
+ end
43
+
44
+ class NotAbsolutePathError < StandardError
45
+ def initialize
46
+ super("Supplied path is not an absolute path")
47
+ end
48
+ end
data/lib/munkey.rb ADDED
@@ -0,0 +1,179 @@
1
+ require 'rubygems'
2
+ require 'uri'
3
+ require 'ftp_sync'
4
+ require 'yaml'
5
+ require 'gitignore_parser'
6
+ require 'fileutils'
7
+
8
+ class Munkey
9
+ DEFAULT_BRANCH = 'munkey'
10
+
11
+ class << self
12
+ def clone(ftpsrc, repo_path, options = {})
13
+ src = URI::parse(ftpsrc)
14
+ raise InvalidSource unless src.is_a?(URI::FTP)
15
+
16
+ repo = create_repo(repo_path, options)
17
+ repo.save_ftp_details(src)
18
+ repo.pull_ftp_files
19
+ repo.commit_changes
20
+ repo.create_branch
21
+ repo
22
+ end
23
+
24
+ private
25
+ def create_repo(repo_path, options = {})
26
+ return false unless system("git init #{repo_path}")
27
+ if options[:ignores]
28
+ File.open(File.join(repo_path, '.gitignore'), 'w') do |f|
29
+ f.write options[:ignores]
30
+ end
31
+ system("cd #{repo_path} && git add .gitignore")
32
+ end
33
+ new(repo_path, options)
34
+ end
35
+ end
36
+
37
+ def initialize(gitpath, options)
38
+ @gitpath = gitpath
39
+ munkey_file = File.join(gitpath, '.git', 'munkey.yml')
40
+ @ftpdetails = YAML.load_file(munkey_file) if File.exist?(munkey_file)
41
+ @verbose = options[:verbose] || false
42
+ end
43
+
44
+ def pull(options = {})
45
+ default_options = { :merge => true, :quick => false }
46
+ default_options.merge! options
47
+
48
+ tmp_repo = clone_to_tmp
49
+ pull_ftp_files(tmp_repo, default_options[:quick])
50
+ commit_changes(tmp_repo)
51
+ push_into_base_repo(tmp_repo)
52
+ FileUtils.rm_rf(tmp_repo)
53
+ merge_foreign_changes if default_options[:merge]
54
+ end
55
+
56
+ def push(dryrun = false)
57
+ changes = files_changed_between_branches
58
+
59
+ list_ftp_changes(changes) && return if dryrun
60
+
61
+ update_ftp_server(changes)
62
+ tmp_repo = clone_to_tmp
63
+ merge_pushed_changes(tmp_repo)
64
+ push_into_base_repo(tmp_repo)
65
+ FileUtils.rm_rf(tmp_repo)
66
+ end
67
+
68
+ def save_ftp_details(ftp_uri)
69
+ @ftpdetails = { :host => ftp_uri.host, :path => "/#{ftp_uri.path}", :user => ftp_uri.user, :password => ftp_uri.password }
70
+ File.open File.join(@gitpath, '.git', 'munkey.yml'), 'w' do |f|
71
+ f.write @ftpdetails.to_yaml
72
+ end
73
+ end
74
+
75
+ def pull_ftp_files(dst = nil, quick = false)
76
+ if quick
77
+ since = last_pull_date
78
+ end
79
+
80
+ dst ||= @gitpath
81
+ gitignore = GitignoreParser.parse(dst)
82
+ ftp = FtpSync.new(@ftpdetails[:host], @ftpdetails[:user], @ftpdetails[:password], :ignore => gitignore, :verbose => @verbose)
83
+ ftp.pull_dir(dst, @ftpdetails[:path], { :delete => true, :since => (since || nil) }) do |p|
84
+ Dir.chdir(dst) do
85
+ relpath = p.gsub %r{^#{Regexp.escape(dst)}\/}, ''
86
+ system("git rm -r#{git_quiet} '#{relpath}'")
87
+ end
88
+ end
89
+ end
90
+
91
+ def commit_changes(dst = nil)
92
+ Dir.chdir(dst || @gitpath) do
93
+ system("git add .") && system("git commit -m 'Pull from ftp://#{@ftpdetails[:host]}#{@ftpdetails[:path]} at #{Time.now.to_s}'")
94
+ end
95
+ end
96
+
97
+ def create_branch(branch_name = DEFAULT_BRANCH)
98
+ Dir.chdir(@gitpath) do
99
+ system("git branch #{branch_name}")
100
+ end
101
+ end
102
+
103
+ def clone_to_tmp(branch = DEFAULT_BRANCH)
104
+ tmp_repo = File.join ENV['TMPDIR'], create_tmpname
105
+ system("git clone#{git_quiet} -b #{branch} #{@gitpath} #{tmp_repo}")
106
+ tmp_repo
107
+ end
108
+
109
+ def push_into_base_repo(tmp_repo, branch = DEFAULT_BRANCH)
110
+ Dir.chdir(tmp_repo) do
111
+ system("git push#{git_quiet} origin #{branch}:#{branch}")
112
+ end
113
+ end
114
+
115
+ def merge_foreign_changes(branch = DEFAULT_BRANCH)
116
+ Dir.chdir(@gitpath) do
117
+ system("git merge #{branch}")
118
+ end
119
+ end
120
+
121
+ def merge_pushed_changes(tmp_repo)
122
+ Dir.chdir(tmp_repo) do
123
+ system("git pull#{git_quiet} origin master")
124
+ end
125
+ end
126
+
127
+ def files_changed_between_branches(branch = DEFAULT_BRANCH)
128
+ changes = { :changed => [], :removed => [] }
129
+ Dir.chdir(@gitpath) do
130
+ `git diff --name-status #{branch} master`.strip.split("\n").each do |f|
131
+ status, name = f.split(/\s+/, 2)
132
+ if status == "D"
133
+ changes[:removed] << name
134
+ else
135
+ changes[:changed] << name unless name == '.gitignore'
136
+ end
137
+ end
138
+ end
139
+
140
+ changes
141
+ end
142
+
143
+ def update_ftp_server(changes)
144
+ ftp = FtpSync.new(@ftpdetails[:host], @ftpdetails[:user], @ftpdetails[:password], :verbose => @verbose)
145
+
146
+ unless changes[:changed].size == 0
147
+ ftp.push_files @gitpath, @ftpdetails[:path], changes[:changed]
148
+ end
149
+
150
+ unless changes[:removed].size == 0
151
+ ftp.remove_files @ftpdetails[:path], changes[:removed]
152
+ end
153
+ end
154
+
155
+ def list_ftp_changes(changes)
156
+ changes[:changed].each {|f| puts "WILL UPLOAD #{f}" }
157
+ changes[:removed].each {|f| puts "WILL REMOVE #{f}" }
158
+ end
159
+
160
+ def last_pull_date(branch = DEFAULT_BRANCH)
161
+ Dir.chdir(@gitpath) do
162
+ commits = `git log --format=oneline #{branch}`.strip.split("\n")
163
+ commits.find {|c| c =~ /^[a-f\d]{40} Pull from ftp:\/\/.* at ([\w +:]+)$/ } ? Time.parse($1) : nil
164
+ end
165
+ end
166
+
167
+ private
168
+
169
+ def git_quiet
170
+ @verbose ? '' : ' -q'
171
+ end
172
+
173
+ def create_tmpname
174
+ tmpname = ''
175
+ char_list = ("a".."z").to_a + ("0".."9").to_a
176
+ 1.upto(20) { |i| tmpname << char_list[rand(char_list.size)] }
177
+ return tmpname
178
+ end
179
+ end
@@ -0,0 +1,116 @@
1
+ test_path = File.expand_path(File.dirname(__FILE__))
2
+ lib_path = File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
3
+
4
+ $:.unshift test_path unless $:.include?(test_path)
5
+ $:.unshift lib_path unless $:.include?(lib_path)
6
+
7
+ require 'test/unit'
8
+ require 'gitignore_parser'
9
+ require 'tmpdir'
10
+
11
+ class GitignoreTest < Test::Unit::TestCase
12
+
13
+ def setup
14
+ @gitdir = File.join Dir.tmpdir, create_tmpname
15
+ FileUtils.mkdir_p @gitdir
16
+ end
17
+
18
+ def teardown
19
+ FileUtils.rm_rf @gitdir
20
+ end
21
+
22
+ def test_skips_blank_lines_in_gitignore
23
+ create_git_ignore "\nfoo.txt\n\n"
24
+ assert GitignoreParser::parse(@gitdir).ignore?(File.join(@gitdir, 'foo.txt'))
25
+ end
26
+
27
+ def test_skips_commented_lines_in_gitignore
28
+ create_git_ignore "foo.txt\n#bar.txt\n"
29
+ gitignore = GitignoreParser::parse(@gitdir)
30
+ assert gitignore.ignore?(File.join(@gitdir, 'foo.txt'))
31
+ assert !gitignore.ignore?(File.join(@gitdir, 'bar.txt'))
32
+ end
33
+
34
+ def test_filename_ignore
35
+ create_git_ignore 'foo.txt'
36
+ gitignore = GitignoreParser::parse(@gitdir)
37
+ assert gitignore.ignore?(File.join(@gitdir, 'foo.txt'))
38
+ assert gitignore.ignore?(File.join(@gitdir, 'foo/foo.txt'))
39
+ assert gitignore.ignore?(File.join(@gitdir, 'foo/bar/foo.txt'))
40
+ assert !gitignore.ignore?(File.join(@gitdir, 'bar.txt'))
41
+ assert !gitignore.ignore?(File.join(@gitdir, 'foo/bar.txt'))
42
+ assert !gitignore.ignore?(File.join(@gitdir, 'barfoo.txt'))
43
+ end
44
+
45
+ def test_simple_ignore
46
+ create_git_ignore "*.txt\n"
47
+ gitignore = GitignoreParser::parse(@gitdir)
48
+ assert gitignore.ignore?(File.join(@gitdir, 'foo.txt'))
49
+ assert gitignore.ignore?(File.join(@gitdir, 'nested/foo.txt'))
50
+ assert !gitignore.ignore?(File.join(@gitdir, 'foo.jpg'))
51
+ assert !gitignore.ignore?(File.join(@gitdir, 'foo_txt'))
52
+ assert !gitignore.ignore?(File.join(@gitdir, 'foo.txt.jpg'))
53
+ end
54
+
55
+ def test_combined_ignore
56
+ create_git_ignore "*.txt\n*.jpg\n"
57
+ gitignore = GitignoreParser::parse(@gitdir)
58
+ assert gitignore.ignore?(File.join(@gitdir, "foo.txt"))
59
+ assert gitignore.ignore?(File.join(@gitdir, "foo.jpg"))
60
+ assert gitignore.ignore?(File.join(@gitdir, "foo.jpg.txt"))
61
+ assert !gitignore.ignore?(File.join(@gitdir, "foo.doc"))
62
+ end
63
+
64
+ def test_path_only_ignore
65
+ create_git_ignore "doc/"
66
+ gitignore = GitignoreParser::parse(@gitdir)
67
+ assert gitignore.ignore?(File.join(@gitdir, "doc/foo.txt"))
68
+ assert gitignore.ignore?(File.join(@gitdir, "src/doc/foo.txt"))
69
+ assert !gitignore.ignore?(File.join(@gitdir, "src/foo.txt"))
70
+ assert !gitignore.ignore?(File.join(@gitdir, "doc"))
71
+ end
72
+
73
+ def test_simple_glob_ignore
74
+ create_git_ignore "doc/*.txt"
75
+ gitignore = GitignoreParser::parse(@gitdir)
76
+ assert gitignore.ignore?(File.join(@gitdir, "doc/foo.txt"))
77
+ assert !gitignore.ignore?(File.join(@gitdir, "src/doc/foo.txt"))
78
+ assert !gitignore.ignore?(File.join(@gitdir, "src/foo.txt"))
79
+ end
80
+
81
+ def test_path_only_glob_ignore
82
+ create_git_ignore "doc/*"
83
+ gitignore = GitignoreParser::parse(@gitdir)
84
+ assert gitignore.ignore?(File.join(@gitdir, "doc/html/index.html"))
85
+ assert gitignore.ignore?(File.join(@gitdir, "doc/html"))
86
+ assert gitignore.ignore?(File.join(@gitdir, "doc/pdf"))
87
+ assert gitignore.ignore?(File.join(@gitdir, "doc/pdf/index.pdf"))
88
+ assert gitignore.ignore?(File.join(@gitdir, "doc/index.html"))
89
+ assert !gitignore.ignore?(File.join(@gitdir, "src/index.html"))
90
+ assert !gitignore.ignore?(File.join(@gitdir, "src/doc/index.html"))
91
+ end
92
+
93
+ def test_path_and_file_glob
94
+ create_git_ignore "foo/**/*.txt"
95
+ gitignore = GitignoreParser::parse(@gitdir)
96
+ assert gitignore.ignore?(File.join(@gitdir, "foo/bar/hello.txt"))
97
+ assert !gitignore.ignore?(File.join(@gitdir, "foo/bar/hello.html"))
98
+ assert !gitignore.ignore?(File.join(@gitdir, "foo/bar"))
99
+ end
100
+
101
+ protected
102
+
103
+ def create_tmpname
104
+ tmpname = ''
105
+ char_list = ("a".."z").to_a + ("0".."9").to_a
106
+ 1.upto(20) { |i| tmpname << char_list[rand(char_list.size)] }
107
+ return tmpname
108
+ end
109
+
110
+ def create_git_ignore(ignore_content)
111
+ File.open File.join(@gitdir, '.gitignore'), 'w' do |f|
112
+ f.write ignore_content
113
+ end
114
+ end
115
+
116
+ end
@@ -0,0 +1,286 @@
1
+ test_path = File.expand_path(File.dirname(__FILE__))
2
+ lib_path = File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
3
+
4
+ $:.unshift test_path unless $:.include?(test_path)
5
+ $:.unshift lib_path unless $:.include?(lib_path)
6
+
7
+ require 'test/unit'
8
+ require 'tmpdir'
9
+ require 'net/ftp'
10
+ require 'munkey'
11
+ require 'fileutils'
12
+
13
+ class MunkeyTest < Test::Unit::TestCase
14
+
15
+ def setup
16
+ Net::FTP.create_ftp_src
17
+ Net::FTP.listing_overrides = {}
18
+ @gitdir = File.join Dir.tmpdir, create_tmpname
19
+ end
20
+
21
+ def teardown
22
+ FileUtils.rm_rf @gitdir
23
+ FileUtils.rm_rf Net::FTP.ftp_src
24
+ FileUtils.rm_rf Net::FTP.ftp_dst if File.exist?(Net::FTP.ftp_dst)
25
+ end
26
+
27
+ def test_clone_creates_supplied_target_dir
28
+ Munkey.clone('ftp://user:pass@test.server/', @gitdir)
29
+ assert File.exist?(@gitdir)
30
+ end
31
+
32
+ def test_clone_initialises_git_in_target_dir
33
+ Munkey.clone('ftp://user:pass@test.server/', @gitdir)
34
+ assert File.exist?(File.join(@gitdir, '.git'))
35
+ end
36
+
37
+ def test_clone_creates_git_ignore_with_supplied_ignores
38
+ Munkey.clone('ftp://user:pass@test.server/', @gitdir, :ignores => '*.txt')
39
+ assert File.exist?(File.join(@gitdir, '.gitignore'))
40
+ assert_equal "*.txt", File.read(File.join(@gitdir, '.gitignore'))
41
+ end
42
+
43
+ def test_clone_saves_ftp_details
44
+ Munkey.clone('ftp://user:pass@test.server/', @gitdir)
45
+ assert File.exist?(File.join(@gitdir, '.git', 'munkey.yml'))
46
+ ftp_details = YAML.load(File.read(File.join(@gitdir, '.git', 'munkey.yml')))
47
+ assert_equal 'test.server', ftp_details[:host]
48
+ assert_equal 'user', ftp_details[:user]
49
+ assert_equal 'pass', ftp_details[:password]
50
+ assert_equal '/', ftp_details[:path]
51
+ end
52
+
53
+ def test_clone_pulls_in_files_from_ftp
54
+ Munkey.clone('ftp://user:pass@test.server/', @gitdir)
55
+ assert File.exist?(File.join(@gitdir, 'README'))
56
+ assert File.exist?(File.join(@gitdir, 'dirA'))
57
+ assert File.exist?(File.join(@gitdir, 'dirA', 'fileAA'))
58
+ end
59
+
60
+ def test_clone_adds_files_to_git
61
+ Munkey.clone('ftp://user:pass@test.server/', @gitdir)
62
+ Dir.chdir(@gitdir) do
63
+ status = `git status`
64
+ assert_no_match /untracked files present/, status
65
+ end
66
+ end
67
+
68
+ def test_clone_creates_a_munkey_branch
69
+ Munkey.clone('ftp://user:pass@test.server/', @gitdir)
70
+ Dir.chdir(@gitdir) do
71
+ branches = `git branch`.split("\n").map {|b| b.gsub(/^\*/, '').strip }
72
+ assert branches.include?('master')
73
+ assert branches.include?('munkey')
74
+ end
75
+ end
76
+
77
+ def test_pull_adds_new_files
78
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
79
+ FileUtils.touch File.join(Net::FTP.ftp_src, 'missing')
80
+ assert !File.exist?(File.join(@gitdir, 'missing'))
81
+ munkey.pull
82
+ assert File.exist?(File.join(@gitdir, 'missing'))
83
+ end
84
+
85
+ def test_pull_removes_missing_files
86
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
87
+ File.unlink File.join(Net::FTP.ftp_src, 'README')
88
+ assert File.exist?(File.join(@gitdir, 'README'))
89
+ munkey.pull
90
+ assert !File.exist?(File.join(@gitdir, 'README'))
91
+ end
92
+
93
+ def test_pull_doesnt_change_locally_removed_files
94
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
95
+ readme = File.join(@gitdir, 'README')
96
+ File.unlink(readme)
97
+ assert !File.exist?(readme)
98
+ munkey.pull
99
+ assert !File.exist?(readme)
100
+ end
101
+
102
+ def test_pull_adds_a_commit
103
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
104
+ FileUtils.touch File.join(Net::FTP.ftp_src, 'missing')
105
+ munkey.pull
106
+ Dir.chdir(@gitdir) do
107
+ commits = `git log --format=oneline`.strip.split("\n")
108
+ assert_equal 2, commits.size
109
+ end
110
+ end
111
+
112
+ def test_pull_with_no_merge_commits_to_munkey_branch_but_not_master
113
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
114
+ Dir.chdir(@gitdir) do
115
+ munkey_commits = `git log --format=oneline munkey`.strip.split("\n").size
116
+ master_commits = `git log --format=oneline master`.strip.split("\n").size
117
+ FileUtils.touch File.join(Net::FTP.ftp_src, 'missing')
118
+ munkey.pull(:merge => false)
119
+ assert_equal munkey_commits + 1, `git log --format=oneline munkey`.strip.split("\n").size
120
+ assert_equal master_commits, `git log --format=oneline master`.strip.split("\n").size
121
+ end
122
+ end
123
+
124
+ def test_quick_pull_includes_changed_files
125
+ Net::FTP.listing_overrides['/'] = ["-rw-r--r-- 1 root other 0 #{(Time.now + 90).strftime('%b %d %H:%M')} README"]
126
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
127
+ File.open(File.join(Net::FTP.ftp_src, 'README'), 'w') {|f| f.write 'quicktest' }
128
+ Dir.chdir(@gitdir) do
129
+ munkey.pull(:quick => true)
130
+ assert_equal 2, `git log --format=oneline munkey`.strip.split("\n").size
131
+ end
132
+ end
133
+
134
+ def test_quick_pull_excludes_unmodified_files
135
+ Net::FTP.listing_overrides['/'] = ["-rw-r--r-- 1 root other 0 #{(Time.now - 90).strftime('%b %d %H:%M')} README"]
136
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
137
+ File.open(File.join(Net::FTP.ftp_src, 'README'), 'w') {|f| f.write 'quicktest' }
138
+ Dir.chdir(@gitdir) do
139
+ munkey.pull(:quick => true)
140
+ assert_equal 1, `git log --format=oneline munkey`.strip.split("\n").size
141
+ end
142
+ end
143
+
144
+ def test_push_includes_newly_added_files
145
+ Net::FTP.create_ftp_dst
146
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
147
+ add_file_to_git 'newfile'
148
+ munkey.push
149
+ assert File.exist?(File.join(Net::FTP.ftp_dst, 'newfile'))
150
+ end
151
+
152
+ def test_push_includes_files_from_multiple_commits
153
+ Net::FTP.create_ftp_dst
154
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
155
+ add_file_to_git('newfile')
156
+ add_file_to_git('secondfile')
157
+ munkey.push
158
+ assert File.exist?(File.join(Net::FTP.ftp_dst, 'newfile'))
159
+ assert File.exist?(File.join(Net::FTP.ftp_dst, 'secondfile'))
160
+ end
161
+
162
+ def test_push_excludes_remote_added_files
163
+ Net::FTP.create_ftp_dst
164
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
165
+ FileUtils.touch File.join(Net::FTP.ftp_src, 'another')
166
+ munkey.pull
167
+ munkey.push
168
+ assert !File.exist?(File.join(Net::FTP.ftp_dst, 'another'))
169
+ end
170
+
171
+ def test_push_excludes_existing_files
172
+ Net::FTP.create_ftp_dst
173
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
174
+ add_file_to_git 'newfile'
175
+ munkey.push
176
+ assert File.exist?(File.join(Net::FTP.ftp_dst, 'newfile'))
177
+ assert !File.exist?(File.join(Net::FTP.ftp_dst, 'README'))
178
+ end
179
+
180
+ def test_push_excludes_gitignore
181
+ Net::FTP.create_ftp_dst
182
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
183
+ add_file_to_git '.gitignore'
184
+ munkey.push
185
+ assert !File.exist?(File.join(Net::FTP.ftp_dst, '.gitignore'))
186
+ end
187
+
188
+ def test_push_includes_files_changed_on_both_local_and_remote
189
+ Net::FTP.create_ftp_dst
190
+ File.open(File.join(Net::FTP.ftp_src, 'README'), 'w') do |f|
191
+ f.write "line 1\nline 2\nline 3\nline 4\n"
192
+ end
193
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
194
+ File.open(File.join(Net::FTP.ftp_src, 'README'), 'w') do |f|
195
+ f.write "line 1\nline 2\nline 3\nline 4\nline 5\n"
196
+ end
197
+ File.open(File.join(@gitdir, 'README'), 'w') do |f|
198
+ f.write "line 1\nline two\nline 3\nline 4\n"
199
+ end
200
+ munkey.pull
201
+ munkey.push
202
+ assert File.exist?(File.join(Net::FTP.ftp_dst, 'README'))
203
+ end
204
+
205
+ def test_push_removes_locally_removed_files
206
+ FileUtils.cp_r Net::FTP.ftp_src, Net::FTP.ftp_dst
207
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
208
+ Dir.chdir(@gitdir) do
209
+ system("git rm README && git commit -m 'removed README'")
210
+ end
211
+ assert File.exist?(File.join(Net::FTP.ftp_dst, 'README'))
212
+ munkey.push
213
+ assert !File.exist?(File.join(Net::FTP.ftp_dst, 'README'))
214
+ end
215
+
216
+ def test_push_excludes_remotely_removed_files
217
+ FileUtils.cp_r Net::FTP.ftp_src, Net::FTP.ftp_dst
218
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
219
+ File.unlink File.join(Net::FTP.ftp_src, 'README')
220
+ munkey.pull
221
+ munkey.push
222
+ assert File.exist?(File.join(Net::FTP.ftp_dst, 'README'))
223
+ end
224
+
225
+ def test_push_excludes_files_with_same_change_local_and_remote
226
+ Net::FTP.create_ftp_dst
227
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
228
+ File.open(File.join(Net::FTP.ftp_src, 'README'), 'w') {|f| f.write 'munkey' }
229
+ Dir.chdir(@gitdir) do
230
+ File.open('README', 'w') {|f| f.write 'munkey' }
231
+ system("git add . && git commit -m 'add README CHANGES'")
232
+ end
233
+ munkey.pull
234
+ munkey.push
235
+ assert !File.exist?(File.join(Net::FTP.ftp_dst, 'README'))
236
+ end
237
+
238
+ def test_push_adds_a_commit_to_the_munkey_branch
239
+ Net::FTP.create_ftp_dst
240
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
241
+ add_file_to_git('newfile')
242
+ add_file_to_git('secondfile')
243
+ munkey.push
244
+ Dir.chdir(@gitdir) do
245
+ commits = `git log --format=oneline munkey`.strip.split("\n")
246
+ assert_equal 3, commits.size
247
+ end
248
+ end
249
+
250
+ def test_dryrun_push_doesnt_create_commit_in_munkey_branch
251
+ Net::FTP.create_ftp_dst
252
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
253
+ last_commit = File.read File.join(@gitdir, '.git', 'refs', 'heads', 'munkey')
254
+ add_file_to_git 'newfile'
255
+ munkey.push(true)
256
+ assert_equal last_commit, File.read(File.join(@gitdir, '.git', 'refs', 'heads', 'munkey'))
257
+ end
258
+
259
+ def test_dryrun_push_doesnt_upload_files
260
+ Net::FTP.create_ftp_dst
261
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
262
+ add_file_to_git 'newfile'
263
+ munkey.push(true)
264
+ assert !File.exist?(File.join(Net::FTP.ftp_dst, 'newfile'))
265
+ end
266
+
267
+ def test_last_pull_date
268
+ munkey = Munkey.clone('ftp://user:pass@test.server/', @gitdir)
269
+ assert_equal Time.now.to_s, munkey.last_pull_date.to_s
270
+ end
271
+
272
+ protected
273
+ def create_tmpname
274
+ tmpname = ''
275
+ char_list = ("a".."z").to_a + ("0".."9").to_a
276
+ 1.upto(20) { |i| tmpname << char_list[rand(char_list.size)] }
277
+ return tmpname
278
+ end
279
+
280
+ def add_file_to_git(filename, content = nil)
281
+ Dir.chdir(@gitdir) do
282
+ File.open(filename, 'a') {|f| f.write content }
283
+ system("git add . && git commit -m 'Added file #{filename}'")
284
+ end
285
+ end
286
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: munkey
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
+ platform: ruby
12
+ authors:
13
+ - jebw
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-12 00:00:00 +01:00
19
+ default_executable: munkey
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: ftp_sync
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 0
32
+ - 4
33
+ - 1
34
+ version: 0.4.1
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: Tool for using git to push and pull from ftp servers
38
+ email: jeb@jdwilkins.co.uk
39
+ executables:
40
+ - munkey
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - README.rdoc
45
+ files:
46
+ - lib/gitignore_parser.rb
47
+ - lib/munkey.rb
48
+ - bin/munkey
49
+ - README.rdoc
50
+ - test/gitignore_test.rb
51
+ - test/munkey_test.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/jebw/munkey
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --line-numbers
59
+ - --title
60
+ - Tool for using git to push and pull from ftp servers
61
+ - --main
62
+ - README.rdoc
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.3.7
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Tool for using git to push and pull from ftp servers
90
+ test_files:
91
+ - test/gitignore_test.rb
92
+ - test/munkey_test.rb