onboard 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,93 @@
1
+ # encoding: utf-8
2
+
3
+ require 'thor'
4
+
5
+ require_relative 'confirm'
6
+ require_relative 'project'
7
+
8
+ module Onboard
9
+ class Prepare < Thor
10
+ attr_reader :codebase, :found, :options, :projects
11
+
12
+ no_tasks do
13
+ def initialize(codebase, found, options)
14
+ @codebase = codebase
15
+ @found = found
16
+ @options = options
17
+ @projects = Hash[options['projects'].map { |k, v| [k, v] }]
18
+ end
19
+
20
+ def report
21
+ say('Projects exist at the following locations:', :yellow)
22
+ found.each do |x, y|
23
+ puts ' ' + x
24
+ projects[File.basename(x)] = y
25
+ end
26
+ puts ''
27
+ projects
28
+ end
29
+
30
+ def answer
31
+ if options['no'] == 'no'
32
+ return 'n'
33
+ elsif options['yes'] == 'yes'
34
+ return 'y'
35
+ else
36
+ ''
37
+ end
38
+ end
39
+
40
+ def delete(proj)
41
+ # TODO: check for patches before delete
42
+ return proj unless options[:delete] == 'delete'
43
+ say('Ready to delete existing projects:', :yellow)
44
+ Confirm.new('Proceed?', true).q(answer)
45
+ found.each do |x, _|
46
+ Project.new.clean(x)
47
+ proj[File.basename(x)] = ''
48
+ end
49
+ proj
50
+ end
51
+
52
+ def force(proj)
53
+ return proj if options[:force] == 'force' || proj.empty?
54
+ found.each do |x, _|
55
+ proj.delete(File.basename(x))
56
+ end
57
+ proj
58
+ end
59
+
60
+ def add(proj)
61
+ say('Ready to add the following projects:', :green)
62
+ proj.each do |x, _|
63
+ puts ' ' + "#{codebase}/#{options[:destination]}/#{x}"
64
+ end
65
+ puts ''
66
+ end
67
+
68
+ def confirm(proj)
69
+ add(proj)
70
+ ans = answer
71
+ Confirm.new('Proceed?', true).q(ans)
72
+ end
73
+
74
+ def void
75
+ say('All projects already in codebase.', :yellow)
76
+ exit
77
+ end
78
+
79
+ def do
80
+ if found.empty? == false
81
+ proj = report
82
+ proj = delete(proj)
83
+ else
84
+ proj = projects
85
+ end
86
+ proj = force(proj)
87
+ confirm(proj) unless proj.empty?
88
+ return proj, answer unless proj.empty?
89
+ void
90
+ end
91
+ end
92
+ end
93
+ end
@@ -1,102 +1,89 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'fileutils'
4
- require 'find'
5
- require 'nokogiri'
6
- require 'rubygems/package'
7
4
  require 'thor'
8
- require 'zlib'
9
5
 
10
- require_relative 'confirm'
11
6
  require_relative 'download'
12
- require_relative 'msg'
13
- require_relative 'repo'
7
+ require_relative 'extract'
8
+ require_relative 'release'
9
+ require_relative 'repo_bridge'
10
+ require_relative 'validate'
14
11
 
15
12
  module Onboard
16
-
17
- TAR_LONGLINK = '././@LongLink'
18
- DRUPAL_PRJ_FEED = "http://updates.drupal.org/release-history/"
19
- DRUPAL_DL_LINK = "http://ftp.drupal.org/files/projects/"
20
-
21
13
  class Project < Thor
22
- attr_reader :branch, :codebase, :core, :path, :projects, :vc
14
+ attr_reader :answer, :codebase, :core, :options, :path, :projects, :size
23
15
 
24
16
  no_tasks do
25
- def initialize(args = {})
26
- @branch = args['branch']
27
- @codebase = args['codebase']
28
- @core = args['core']
29
- @path = "#{args['codebase']}/#{args['path']}"
30
- @projects = args['projects']
31
- @vc = args['vc']
32
- @vc_path = args['path']
17
+ def initialize(info, projects, options)
18
+ @answer = info[2]
19
+ @codebase = info[0]
20
+ @core = info[1]
21
+ @options = options
22
+ @path = "#{codebase}/#{options['destination']}"
23
+ @projects = projects
24
+ @size = projects.length
25
+ end
26
+
27
+ def continue?(project, version, latest)
28
+ return true if version.nil?
29
+ clean("#{path}/#{project}")
30
+ repo = build_vc(project)
31
+ check = Validate.new(project, version, core, answer)
32
+ if check.latest?(latest) || check.hacked?(path, repo)
33
+ return false
34
+ else
35
+ return true
36
+ end
33
37
  end
34
38
 
35
- def feed(project)
36
- "#{DRUPAL_PRJ_FEED}#{project}/#{core}"
39
+ def reset(project)
40
+ clean("#{path}/#{project}")
41
+ repo = build_vc(project)
42
+ RepoBridge.new(repo).co
37
43
  end
38
44
 
39
- def hacked?(project, existing)
40
- self.clean("#{path}/#{project}")
41
- link = self.build_link(project, existing)
45
+ def deploy(project, version)
46
+ clean("#{path}/#{project}")
47
+ link = Download.new.build_link(project, version)
42
48
  Download.new.fetch(link)
43
- self.extract(Download.new.path(link)) if self.verify(project, link, existing)
44
- repo = self.build_vc(project)
45
- changes = Repo.new(repo).st(true)
46
- if changes.empty? == false
47
- return !Confirm.new("Proceed?").yes?
48
- else
49
- return false
50
- end
49
+ # TODO: retry download after failed download verification
50
+ Extract.new(Download.new.path(link), link, path).x if Validate.new(project, version, core, answer).verify(link)
51
51
  end
52
52
 
53
- def latest?(latest, existing, project)
54
- if Gem::Dependency.new('', "~> #{latest}").match?('', "#{existing}")
55
- say("#{project} is already at the latest version (#{latest}).", :yellow)
56
- return true
53
+ def push(changes)
54
+ return false unless changes
55
+ repo = build_vc
56
+ RepoBridge.new(repo).push
57
+ end
58
+
59
+ def update(project, changes, count)
60
+ if options['vc']
61
+ repo = build_vc(project)
62
+ update = RepoBridge.new(repo).up
63
+ changes = changes ? changes : update
64
+ push(changes) if count == 0
65
+ return changes
57
66
  else
58
- return false
67
+ say("#{project} added to codebase but changes are not yet under version control.", :yellow)
59
68
  end
60
69
  end
61
70
 
62
- def build_link(project, version)
63
- DRUPAL_DL_LINK + "#{project}-#{version}.tar.gz"
71
+ def delegate(project, version, changes, count)
72
+ latest, _md5 = Release.new(project, core).choose
73
+ if continue?(project, version, latest)
74
+ deploy(project, latest)
75
+ return update(project, changes, count)
76
+ else
77
+ reset(project)
78
+ end
64
79
  end
65
80
 
66
81
  def dl
67
- changes = []
82
+ changes = false
83
+ count = size
68
84
  projects.each do |x, y|
69
- md5, version = self.release(x)
70
- if y.empty? == false
71
- if !self.hacked?(x, y) && !self.latest?(version, y, x)
72
- proceed = true
73
- else
74
- proceed = false
75
- end
76
- else
77
- proceed = true
78
- end
79
- if proceed
80
- self.clean("#{path}/#{x}")
81
- link = self.build_link(x, version)
82
- Download.new.fetch(link)
83
- # TODO: retry download after failed download verification
84
- self.extract(Download.new.path(link)) if self.verify(x, link)
85
- if vc == true
86
- repo = self.build_vc(x)
87
- changes += self.vc_up(repo)
88
- else
89
- say("#{x} added to codebase but changes are not yet under version control.", :yellow)
90
- end
91
- else
92
- self.clean("#{path}/#{x}")
93
- repo = self.build_vc(x)
94
- Repo.new(repo).co
95
- end
96
- end
97
- if changes.empty? == false
98
- repo = self.build_vc()
99
- self.vc_push(repo)
85
+ count -= 1
86
+ changes = delegate(x, y, changes, count)
100
87
  end
101
88
  end
102
89
 
@@ -104,106 +91,13 @@ module Onboard
104
91
  repo = {}
105
92
  repo['codebase'] = codebase
106
93
  repo['path'] = @vc_path
107
- repo['branch'] = branch
108
94
  repo['project'] = x
109
- return repo
110
- end
111
-
112
- def vc_co(args)
113
- Repo.new(args).co
114
- end
115
-
116
- def vc_up(args)
117
- g = Repo.new(args)
118
- info = g.info
119
- changes = []
120
- msg = "Committing #{args['project']} on #{info['current_branch']} branch..."
121
- Msg.new(msg).format
122
- changes = g.commit("#{args['path']}/#{args['project']}")
123
- if changes.empty? == false
124
- say(" [done]", :green)
125
- else
126
- puts "\nNo changes to commit for #{args['project']}"
127
- end
128
- return changes
129
- end
130
-
131
- def vc_push(args)
132
- g = Repo.new(args)
133
- info = g.info
134
- msg = "Pushing all changes to #{info['remotes'][0]}..."
135
- Msg.new(msg).format
136
- g.push
137
- say(" [done]", :green)
138
- end
139
-
140
- def verify(x, file, version='')
141
- md5, version = self.release(x, version)
142
- if md5 == Digest::MD5.file(Download.new.path(file)).hexdigest
143
- return true
144
- else
145
- say("Verification failed for #{x} download!", :red)
146
- exit
147
- end
148
- end
149
-
150
- def release(project, version='')
151
- feed = self.feed(project)
152
- Download.new.fetch(feed)
153
- xml = File.open(Download.new.path(feed))
154
- doc = Nokogiri::XML(xml)
155
- releases = {}
156
- if version.empty? == false
157
- doc.xpath('//releases//release').each do |item|
158
- if item.at_xpath('version').content == version
159
- releases[item.at_xpath('mdhash').content] = item.at_xpath('version').content
160
- end
161
- end
162
- else
163
- doc.xpath('//releases//release').each do |item|
164
- if !item.at_xpath('version_extra')
165
- releases[item.at_xpath('mdhash').content] = item.at_xpath('version').content
166
- end
167
- end
168
- end
169
- if releases.nil?
170
- doc.xpath('//releases//release').each do |item|
171
- releases[item.at_xpath('mdhash').content] = item.at_xpath('version').content
172
- end
173
- end
174
- return releases.first
95
+ repo
175
96
  end
176
97
 
177
98
  def clean(arg)
178
99
  FileUtils.rm_r arg if File.directory?(arg)
179
100
  end
180
-
181
- def extract(archive)
182
- Gem::Package::TarReader.new( Zlib::GzipReader.open archive ) do |tar|
183
- dst = nil
184
- tar.each do |entry|
185
- if entry.full_name == TAR_LONGLINK
186
- dst = File.join path, entry.read.strip
187
- next
188
- end
189
- dst ||= File.join path, entry.full_name
190
- if entry.directory?
191
- FileUtils.rm_rf dst unless File.directory? dst
192
- FileUtils.mkdir_p dst, :mode => entry.header.mode, :verbose => false
193
- elsif entry.file?
194
- FileUtils.rm_rf dst unless File.file? dst
195
- File.open dst, "wb" do |f|
196
- f.print entry.read
197
- end
198
- FileUtils.chmod entry.header.mode, dst, :verbose => false
199
- elsif entry.header.typeflag == '2' #Symlink!
200
- File.symlink entry.header.linkname, dst
201
- end
202
- dst = nil
203
- end
204
- end
205
- end
206
101
  end
207
102
  end
208
103
  end
209
-
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ require 'nokogiri'
4
+
5
+ require_relative 'download'
6
+
7
+ module Onboard
8
+ DRUPAL_PRJ_FEED = 'http://updates.drupal.org/release-history/'
9
+
10
+ class Release
11
+ attr_reader :core, :doc, :feed, :project
12
+
13
+ def initialize(project, core)
14
+ @core = core
15
+ @project = project
16
+ @doc = build_doc
17
+ end
18
+
19
+ def build_doc
20
+ feed = "#{DRUPAL_PRJ_FEED}#{project}/#{core}"
21
+ Download.new.fetch(feed)
22
+ xml = File.open(Download.new.path(feed))
23
+ Nokogiri::XML(xml)
24
+ end
25
+
26
+ def specify(version, releases)
27
+ if releases['stable'][version].nil? == false
28
+ return version, releases['stable'][version]
29
+ elsif releases['extra'][version].nil? == false
30
+ return version, releases['extra'][version]
31
+ end
32
+ end
33
+
34
+ def choose(version = '')
35
+ releases = releases_get
36
+ if version.empty? == false
37
+ return specify(version, releases)
38
+ elsif releases['stable'].empty? == false
39
+ return releases['stable'].first
40
+ elsif releases['extra'].empty? == false
41
+ return releases['extra'].first
42
+ end
43
+ end
44
+
45
+ def releases_get
46
+ releases = { 'stable' => {}, 'extra' => {} }
47
+ doc.xpath('//releases//release').each do |item|
48
+ md5 = item.at_xpath('mdhash').content
49
+ version = item.at_xpath('version').content
50
+ status = item.at_xpath('version_extra').nil? ? 'stable' : 'extra'
51
+ releases[status][version] = md5
52
+ end
53
+ releases
54
+ end
55
+ end
56
+ end
data/lib/onboard/repo.rb CHANGED
@@ -5,82 +5,70 @@ require 'git'
5
5
  require 'pathname'
6
6
  require 'thor'
7
7
 
8
+ require_relative 'patch'
9
+
8
10
  module Onboard
9
11
  class Repo < Thor
10
- attr_reader :g, :path, :branch, :codebase, :project
12
+ attr_reader :g, :path, :codebase, :project
11
13
 
12
14
  no_tasks do
13
15
  def initialize(repo)
14
16
  @codebase = repo['codebase']
15
- @g = self.prepare(repo)
17
+ @g = prepare(repo)
16
18
  @path = repo['path']
17
- @branch = repo['branch']
18
19
  @project = repo['project']
19
20
  end
20
21
 
21
- def st(patch = false)
22
- changed = []
23
- deleted = []
24
- untracked = []
25
-
26
- if patch
27
- patches_dir = "/tmp/onboard/patches"
28
- unless File.directory?(patches_dir)
29
- FileUtils.mkdir_p(patches_dir)
30
- end
31
- patch_file = File.open( "#{patches_dir}/#{Time.now.to_i}_#{project}.patch","w" )
32
- end
33
-
34
- # TODO: figure out why g.status.changed.keys.each is returning
35
- # unchanged files
36
- g.status.changed.keys.each { |file| changed.push(file.to_s) if !g.diff('HEAD', file).patch.empty? }
37
- g.status.deleted.keys.each { |file| deleted.push(file.to_s) }
38
- g.status.untracked.keys.each { |file| untracked.push(file.to_s) }
39
- all = changed + deleted + untracked
40
-
41
- if changed.empty? == false
42
- say('CHANGED FILES:', :yellow)
43
- changed.each do |x|
44
- puts g.diff('HEAD', x).patch
45
- if patch
46
- patch_file << g.diff('HEAD', x).patch
47
- end
48
- end
49
- puts ''
22
+ def changed(list, patch_file, patch)
23
+ return false if list.empty?
24
+ say('CHANGED FILES:', :yellow)
25
+ list.each do |x|
26
+ puts g.diff('HEAD', x).patch
27
+ patch_file << g.diff('HEAD', x).patch if patch
50
28
  end
29
+ end
51
30
 
52
- if deleted.empty? == false
53
- say('DELETED FILES:', :yellow)
54
- deleted.each do |x|
55
- say(x, :red)
56
- if patch
57
- patch_file << g.diff('--', x).patch
58
- end
59
- end
60
- puts ''
31
+ def deleted(list, patch_file, patch)
32
+ return false if list.empty?
33
+ say('DELETED FILES:', :yellow)
34
+ list.each do |x|
35
+ say(x, :red)
36
+ patch_file << g.diff('--', x).patch if patch
61
37
  end
38
+ end
62
39
 
63
- if untracked.empty? == false
64
- say('UNTRACKED FILES:', :yellow)
65
- untracked.each do |x|
66
- say(x, :red)
67
- if patch
68
- g.add(x)
69
- patch_file << g.diff('HEAD', x).patch
70
- end
40
+ def untracked(list, patch_file, patch)
41
+ return false if list.empty?
42
+ say('UNTRACKED FILES:', :yellow)
43
+ list.each do |x|
44
+ say(x, :red)
45
+ if patch
46
+ g.add(x)
47
+ patch_file << g.diff('HEAD', x).patch
71
48
  end
72
- puts ''
73
49
  end
50
+ end
74
51
 
75
- if patch
76
- patch_file.close
77
- Dir.foreach(patches_dir) do |item|
78
- file = "#{patches_dir}/#{item}"
79
- FileUtils.rm_r file if File.zero?(file)
80
- end
81
- end
52
+ def repo_status
53
+ all = {}
54
+ all['changed'] = []
55
+ all['deleted'] = []
56
+ all['untracked'] = []
57
+ g.status.changed.keys.each { |file| all['changed'].push(file.to_s) unless g.diff('HEAD', file).patch.empty? }
58
+ g.status.deleted.keys.each { |file| all['deleted'].push(file.to_s) }
59
+ g.status.untracked.keys.each { |file| all['untracked'].push(file.to_s) }
60
+ all
61
+ end
82
62
 
83
- return all
63
+ def st(patch = false)
64
+ patch_file = Patch.new.open(project) if patch
65
+ # TODO: figure out why g.status.changed.keys.each is returning unchanged files
66
+ all = repo_status
67
+ changed(all['changed'], patch_file, patch)
68
+ deleted(all['deleted'], patch_file, patch)
69
+ untracked(all['untracked'], patch_file, patch)
70
+ Patch.new.close(patch_file)
71
+ all.empty? ? false : true
84
72
  end
85
73
 
86
74
  def co
@@ -91,7 +79,7 @@ module Onboard
91
79
  repo = {}
92
80
  repo['current_branch'] = g.current_branch
93
81
  repo['remotes'] = g.remotes
94
- return repo
82
+ repo
95
83
  end
96
84
 
97
85
  def prepare(args)
@@ -102,16 +90,16 @@ module Onboard
102
90
  project = File.basename(path)
103
91
 
104
92
  changes = []
105
- g.status.changed.keys.each { |x| changes.push x if !g.diff('HEAD', x).patch.empty? }
93
+ g.status.changed.keys.each { |x| changes.push x unless g.diff('HEAD', x).patch.empty? }
106
94
  g.status.deleted.keys.each { |x| changes.push x }
107
95
  g.status.untracked.keys.each { |x| changes.push x }
108
96
 
109
97
  if changes.empty? == false
110
- g.add(codebase, :all=>true)
98
+ g.add(codebase, :all => true)
111
99
  g.commit("Add #{project}")
112
100
  end
113
101
 
114
- return changes
102
+ changes
115
103
  end
116
104
 
117
105
  def push
@@ -120,4 +108,3 @@ module Onboard
120
108
  end
121
109
  end
122
110
  end
123
-
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+
3
+ require 'thor'
4
+
5
+ require_relative 'msg'
6
+ require_relative 'repo'
7
+
8
+ module Onboard
9
+ class RepoBridge < Thor
10
+ attr_reader :args, :g, :info
11
+
12
+ no_tasks do
13
+ def initialize(args = {})
14
+ @args = args
15
+ @g = Repo.new(args)
16
+ @info = g.info
17
+ end
18
+
19
+ def co
20
+ g.co
21
+ end
22
+
23
+ def up
24
+ msg = "Committing #{args['project']} on #{info['current_branch']} branch..."
25
+ Msg.new(msg).format
26
+ changes = g.commit("#{args['path']}/#{args['project']}")
27
+ if changes.empty? == false
28
+ say(' [done]', :green)
29
+ else
30
+ puts "\nNo changes to commit for #{args['project']}"
31
+ end
32
+ changes
33
+ end
34
+
35
+ def push
36
+ msg = "Pushing all changes to #{info['remotes'][0]}..."
37
+ Msg.new(msg).format
38
+ g.push
39
+ say(' [done]', :green)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -4,12 +4,10 @@ require 'io/console'
4
4
 
5
5
  module Onboard
6
6
  class Screen
7
-
8
7
  def size
9
8
  IO.console.winsize
10
9
  rescue LoadError
11
- [Integer(`tput li`), Integer(`tput co`)]
10
+ [Integer(`tput li`), Integer(`tput co`)]
12
11
  end
13
12
  end
14
13
  end
15
-
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'git'
4
+
5
+ module Onboard
6
+ class Source
7
+ attr_reader :source
8
+
9
+ no_tasks do
10
+ def initialize(source)
11
+ @source = source
12
+ end
13
+
14
+ def get
15
+ end
16
+ end
17
+ end
18
+ end