onboard 0.2.3 → 0.3.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.
@@ -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