munkey 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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