ext 0.0.5
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/MIT_LICENSE.txt +22 -0
- data/README +108 -0
- data/Rakefile +64 -0
- data/bin/ext +5 -0
- data/bin/ext.bat +6 -0
- data/lib/ext/string.rb +9 -0
- data/lib/ext/symbol.rb +7 -0
- data/lib/externals/command.rb +35 -0
- data/lib/externals/configuration/configuration.rb +159 -0
- data/lib/externals/ext.rb +557 -0
- data/lib/externals/project.rb +110 -0
- data/lib/externals/project_types/rails.rb +28 -0
- data/lib/externals/scms/git_project.rb +114 -0
- data/lib/externals/scms/svn_project.rb +98 -0
- data/lib/externals/test_case.rb +97 -0
- data/test/test_checkout_git.rb +35 -0
- data/test/test_checkout_with_subprojects_git.rb +77 -0
- data/test/test_checkout_with_subprojects_svn.rb +153 -0
- data/test/test_init_git.rb +47 -0
- data/test/test_rails_detection.rb +26 -0
- data/test/test_string_extensions.rb +15 -0
- data/test/test_touch_emptydirs.rb +52 -0
- metadata +72 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
module Externals
|
2
|
+
# this regular expression will match a quoted string
|
3
|
+
# it will allow for " to be escaped with "" within the string
|
4
|
+
quoted = '(?:"(?:(?:[^"]*(?:"")?)*)")'
|
5
|
+
|
6
|
+
# this regular expression will match strings of text that are not quoted. They must appear at the start of a line or after a ,
|
7
|
+
# it will also match empty strings like ,,
|
8
|
+
unquoted = '(?:[^"\\s][^\\s$]*)'
|
9
|
+
|
10
|
+
column = "(#{quoted}|#{unquoted})"
|
11
|
+
PROJECT_LINE_REGEX = Regexp.new("^\\s*#{column}(?:\\s+#{column})?\\s*$")
|
12
|
+
|
13
|
+
class Project
|
14
|
+
attr_accessor :branch, :repository, :path
|
15
|
+
attr_writer :is_main, :name
|
16
|
+
|
17
|
+
def name
|
18
|
+
@name ||= (extract_name(repository) || File.basename(path))
|
19
|
+
end
|
20
|
+
|
21
|
+
def main?
|
22
|
+
@is_main
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.scm
|
26
|
+
raise "subclass responsibility"
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def scm
|
31
|
+
self.class.scm
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def initialize row_string, is_main = false
|
36
|
+
raise "Abstract class" if self.class == Project
|
37
|
+
|
38
|
+
#It's the main project
|
39
|
+
self.is_main = is_main || row_string == "."
|
40
|
+
|
41
|
+
if row_string =~ PROJECT_LINE_REGEX
|
42
|
+
repbranch = trim_quotes($1)
|
43
|
+
self.path = trim_quotes($2)
|
44
|
+
|
45
|
+
if repbranch =~ /^(.*):(\w+)$/
|
46
|
+
self.repository = $1
|
47
|
+
self.branch = $2
|
48
|
+
else
|
49
|
+
self.repository = repbranch
|
50
|
+
end
|
51
|
+
else
|
52
|
+
raise "poorly formatted .externals entry: #{row_string}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
[:co, :ex].each do |method_name|
|
57
|
+
define_method method_name do |args|
|
58
|
+
raise "subclass responsibility"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def update_ignore path
|
63
|
+
if !ignore_contains?(path)
|
64
|
+
append_ignore path
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def checkout *args
|
69
|
+
co(*args)
|
70
|
+
end
|
71
|
+
|
72
|
+
def export *args
|
73
|
+
ex(*args)
|
74
|
+
end
|
75
|
+
|
76
|
+
def extract_name repository
|
77
|
+
if repository =~ /\/([\w_-]+)(?:\.git)?$/
|
78
|
+
$1
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def path
|
83
|
+
@path || default_path(self)
|
84
|
+
end
|
85
|
+
|
86
|
+
def parent_path
|
87
|
+
File.dirname path
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.project_line? line
|
91
|
+
#Make sure it's not a comment
|
92
|
+
return false if line =~ /^\s*#/
|
93
|
+
|
94
|
+
line =~ PROJECT_LINE_REGEX
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
protected
|
100
|
+
def trim_quotes value
|
101
|
+
if value
|
102
|
+
if [value[0].chr, value[-1].chr] == ['"', '"']
|
103
|
+
value[1..-2]
|
104
|
+
else
|
105
|
+
value
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Externals
|
2
|
+
module RailsProjectType
|
3
|
+
def self.install
|
4
|
+
Externals::Project.send(:include, Externals::RailsProjectType::Project)
|
5
|
+
end
|
6
|
+
|
7
|
+
module Project
|
8
|
+
def default_path row
|
9
|
+
if row.repository =~ /\/([\w_-]*)(?:.git)?$/
|
10
|
+
($1 == 'rails') ? File.join("vendor","rails") : File.join("vendor","plugins", $1)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
class RailsDetector
|
19
|
+
def self.detected?
|
20
|
+
boot_path = File.join('config','boot.rb')
|
21
|
+
if File.exists? boot_path
|
22
|
+
open(boot_path) do |f|
|
23
|
+
f.read =~ /^\s*module\s+Rails/
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Externals
|
2
|
+
class GitProject < Project
|
3
|
+
def co *args
|
4
|
+
puts "path is #{path} repository is #{repository}"
|
5
|
+
if path != '.'
|
6
|
+
(rmdircmd = "rmdir #{path}")
|
7
|
+
`#{rmdircmd}` if File.exists?(path)
|
8
|
+
end
|
9
|
+
|
10
|
+
dest = path
|
11
|
+
dest = '' if dest == '.'
|
12
|
+
dest = "\"#{dest}\"" if dest && !dest.empty?
|
13
|
+
|
14
|
+
puts(gitclonecmd = "git clone \"#{repository}\" #{dest}")
|
15
|
+
puts `#{gitclonecmd}`
|
16
|
+
if branch
|
17
|
+
puts `cd #{path}; git checkout --track -b #{branch} origin/#{branch}`
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def ex *args
|
22
|
+
if path != '.'
|
23
|
+
(rmdircmd = "rmdir #{path}")
|
24
|
+
`#{rmdircmd}` if File.exists? path
|
25
|
+
end
|
26
|
+
|
27
|
+
dest = path
|
28
|
+
|
29
|
+
dest = '' if dest == '.'
|
30
|
+
|
31
|
+
dest = "\"#{dest}\"" if dest && !dest.empty?
|
32
|
+
|
33
|
+
puts(gitclonecmd = "git clone --depth 1 \"#{repository}\" #{dest}")
|
34
|
+
|
35
|
+
puts `#{gitclonecmd}`
|
36
|
+
if branch
|
37
|
+
puts `cd #{path}; git checkout --track -b #{branch} origin/#{branch}`
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def up *args
|
42
|
+
puts "updating #{path}:"
|
43
|
+
Dir.chdir path do
|
44
|
+
puts `git pull`
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def st *args
|
49
|
+
puts "\nstatus for #{path}:"
|
50
|
+
Dir.chdir path do
|
51
|
+
puts `git status`
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.scm_path? path
|
56
|
+
path =~ /^git:/ || path =~ /.git$/
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.fill_in_opts opts, main_options, sub_options
|
60
|
+
opts.on("--git", "-g", "same as '--scm git' Uses git to checkout/export the main project",
|
61
|
+
Integer) {sub_options[:scm] = main_options[:scm] = 'git'}
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def self.scm
|
66
|
+
"git"
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.detected?
|
70
|
+
File.exists? ".git"
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.add_all
|
74
|
+
puts `git add .`
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
protected
|
79
|
+
def ignore_contains? path
|
80
|
+
text = ignore_text
|
81
|
+
text.split(/\n/).detect {|r| r.strip == path.strip}
|
82
|
+
end
|
83
|
+
|
84
|
+
def ignore_text
|
85
|
+
return '' unless File.exists? '.gitignore'
|
86
|
+
retval = ''
|
87
|
+
open('.gitignore') do |f|
|
88
|
+
retval = f.read
|
89
|
+
end
|
90
|
+
retval
|
91
|
+
end
|
92
|
+
|
93
|
+
def append_ignore path
|
94
|
+
rows = ignore_text || ''
|
95
|
+
return if rows.index path.strip
|
96
|
+
|
97
|
+
rows = rows.split(/\n/)
|
98
|
+
rows << path.strip
|
99
|
+
|
100
|
+
rows.delete_if {|row| row =~ /^\s*$/}
|
101
|
+
|
102
|
+
|
103
|
+
open('.gitignore', 'w') do |f|
|
104
|
+
f.write "#{rows.compact.join("\n")}\n"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def extract_name s
|
109
|
+
if s =~ /\/([\w_-]+)(?:\.git)?$/
|
110
|
+
$1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Externals
|
2
|
+
class SvnProject < Project
|
3
|
+
def co *args
|
4
|
+
(rmdircmd = "rmdir #{path}")
|
5
|
+
|
6
|
+
`#{rmdircmd}` if File.exists? path
|
7
|
+
puts(svncocmd = "svn co #{repository} #{path}")
|
8
|
+
puts `#{svncocmd}`
|
9
|
+
end
|
10
|
+
|
11
|
+
def ex *args
|
12
|
+
(rmdircmd = "rmdir #{path}")
|
13
|
+
|
14
|
+
`#{rmdircmd}` if File.exists? path
|
15
|
+
puts(svncocmd = "svn export #{repository} #{path}")
|
16
|
+
puts `#{svncocmd}`
|
17
|
+
end
|
18
|
+
|
19
|
+
def up *args
|
20
|
+
puts "updating #{path}:"
|
21
|
+
Dir.chdir path do
|
22
|
+
puts `svn up .`
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def st *args
|
27
|
+
puts "\nstatus for #{path}:"
|
28
|
+
Dir.chdir path do
|
29
|
+
puts `svn status`
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.scm_path? path
|
34
|
+
return true if path =~ /^svn(\+ssh)?:/
|
35
|
+
if path =~ /^https?:\/\/([\w+\-_]+)\.(?:[\w+\-_]+\.)*[\w\-_]+(?:\/|$)/
|
36
|
+
return true if $1.downcase == "svn"
|
37
|
+
|
38
|
+
if path =~ /^https?:\/\/(?:[\w_\-]+\.)*[\w\-_]+\/(\w+)\//
|
39
|
+
return true if $1.downcase == "svn"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.fill_in_opts opts, main_options, sub_options
|
47
|
+
opts.on("--svn", "--subversion","-s", "same as '--scm svn' Uses subversion to checkout/export the main project",
|
48
|
+
Integer) {sub_options[:scm] = main_options[:scm] = 'svn'}
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.scm
|
52
|
+
"svn"
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.detected?
|
56
|
+
File.exists? ".svn"
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.add_all
|
60
|
+
status = `svn st`
|
61
|
+
|
62
|
+
status.split("\n").grep(/^\?/).each do |to_add|
|
63
|
+
puts `svn add #{to_add.gsub(/^\?\s*/,"")}`
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
protected
|
69
|
+
def ignore_contains? path
|
70
|
+
ignore_text(path) =~ Regexp.new("^\\s*#{File.basename(path)}\\s*$")
|
71
|
+
end
|
72
|
+
|
73
|
+
def append_ignore path
|
74
|
+
parent = File.dirname(path)
|
75
|
+
child = File.basename(path)
|
76
|
+
|
77
|
+
rows = ignore_text(path).split(/\n/)
|
78
|
+
|
79
|
+
return if rows.detect {|row| row.strip == child.strip}
|
80
|
+
|
81
|
+
rows << child.strip
|
82
|
+
|
83
|
+
rows.delete_if {|row| row =~ /^\s*$/}
|
84
|
+
|
85
|
+
Dir.chdir(parent) do
|
86
|
+
puts `svn propset svn:ignore "#{rows.compact.join("\n")}\n" .`
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def ignore_text(path)
|
91
|
+
ignore_text = ''
|
92
|
+
Dir.chdir File.dirname(path) do
|
93
|
+
puts(ignore_text = `svn propget svn:ignore`)
|
94
|
+
end
|
95
|
+
ignore_text
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
module Externals
|
4
|
+
class TestCase < Test::Unit::TestCase
|
5
|
+
protected
|
6
|
+
|
7
|
+
def initialize_test_git_repository
|
8
|
+
scm = 'git'
|
9
|
+
Dir.chdir(File.join(File.dirname(__FILE__), '..', '..', 'test')) do
|
10
|
+
`mkdir repositories` unless File.exists? 'repositories'
|
11
|
+
Dir.chdir 'repositories' do
|
12
|
+
`mkdir #{scm}repo`
|
13
|
+
Dir.chdir("#{scm}repo") do
|
14
|
+
`git init`
|
15
|
+
open 'readme.txt', 'w' do |f|
|
16
|
+
f.write "readme.txt Line 1
|
17
|
+
Line 2
|
18
|
+
Line 3"
|
19
|
+
end
|
20
|
+
|
21
|
+
`git add .`
|
22
|
+
`git commit -m "added readme.txt"`
|
23
|
+
|
24
|
+
open 'readme.txt', 'a' do |f|
|
25
|
+
f.write "line 4"
|
26
|
+
end
|
27
|
+
|
28
|
+
`git add .`
|
29
|
+
`git commit -m "added a line to readme.txt"`
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_test_svn_repository
|
36
|
+
scm = 'svn'
|
37
|
+
Dir.chdir(File.join(File.dirname(__FILE__), '..', '..', 'test')) do
|
38
|
+
`mkdir repositories` unless File.exists? 'repositories'
|
39
|
+
Dir.chdir 'repositories' do
|
40
|
+
puts `svnadmin create #{scm}repo`
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def destroy_test_repository scm
|
46
|
+
puts(rmcmd = "rm -rf #{repository_dir(scm)}")
|
47
|
+
puts `#{rmcmd}`
|
48
|
+
end
|
49
|
+
|
50
|
+
def repository_dir scm = nil
|
51
|
+
if scm.nil?
|
52
|
+
File.join(File.dirname(__FILE__), '..', '..', 'test', 'repositories')
|
53
|
+
else
|
54
|
+
File.expand_path(File.join(repository_dir, "#{scm}repo"))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def create_rails_application
|
60
|
+
Dir.mkdir applications_dir unless File.exists?(applications_dir)
|
61
|
+
Dir.chdir applications_dir do
|
62
|
+
if windows?
|
63
|
+
puts `ruby C:\\ruby\\bin\\rails rails_app`
|
64
|
+
else
|
65
|
+
puts `rails rails_app`
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def windows?
|
71
|
+
ENV['OS'] =~ /^Win/i
|
72
|
+
end
|
73
|
+
|
74
|
+
def destroy_rails_application
|
75
|
+
Dir.chdir applications_dir do
|
76
|
+
`rm -rf rails_app`
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def root_dir
|
81
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
82
|
+
end
|
83
|
+
|
84
|
+
def applications_dir
|
85
|
+
File.join(root_dir, 'test', 'applications')
|
86
|
+
end
|
87
|
+
|
88
|
+
def rails_application_dir
|
89
|
+
File.join(applications_dir, 'rails_app')
|
90
|
+
end
|
91
|
+
|
92
|
+
public
|
93
|
+
def test_true
|
94
|
+
assert true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|