onboard 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZDFmNjkwZDVlMGFmMmI1NjYzY2I5OGZmMTgwNzRiYjkwMjBlNGI3YQ==
4
+ OTgwMzQzNzYzZTdkM2I4Zjg5OWNlYjIyY2VmMmVkZTQ1ODhhYmJkMA==
5
5
  data.tar.gz: !binary |-
6
- NWMyMTdkYTYwNTdmOWM0NDkyZTc3ZGQyMTEwMjJiYzM3MTcyNDE2Ng==
6
+ MDcyODg3MzM0NjAzMDNiY2Q0YTYzYTY3ZmZlNzM4NjkwZDBlMDg2ZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NWRkOTUyMzMxOTJjMGY2MDJmZDQxZDEyMDkxYjJkM2RhNmE0N2ZmM2I3YjE4
10
- YTBlMGM1ODg5ZGRlM2UxZThkODkxNWM0ZWI2MjAxZTUxNzY0MTRjYWJiMjFi
11
- NjYyZmNhYmI4OTQ2NzM3MWRjNzA1NTg4YTFiYWI1MmRiNzhmNjQ=
9
+ NzY4YjJiYjQxZTVkMTA0ZDY0YWNlOTQyMDFmOTFhOWQwMmIxYTYwYWVhODY2
10
+ ZWY5ZTY5Yjg4YmJhOGZhMDVmOWI0ZDQwMTkyYzQ0YmRjMTdiNzVhNjcxYWU2
11
+ ZWJhODJiYmVlN2ViYzUxYTVhZDY3MGNhZjA3Y2JkY2Y1ZTJmZWQ=
12
12
  data.tar.gz: !binary |-
13
- MzQ3NzYwOWNiODMxNzJhNWViZDljODcyOTQyNzk3NWIzOWIzYWIwMzc3ODYw
14
- NjNhZTkxNmFkNTgxMTU3MjQ1OGU3YTcyYmIwNTg2OTc4YjBhYTA2NDg4YWQ2
15
- ZGJhMjcxODhhM2QyY2MyZTc0YjdjMmE3YjExYTYyZTcwZGEzOGM=
13
+ Y2M3NmI2Zjg1MzA2MGM2YThlOGZhZDNkMjlhNmRkZmU0OTMwYjIzZGRhZmJh
14
+ ZjBkMWQ5NThkNTJiMDY5MmJlYmM2MDBmNDhhN2M2MmQ0MjE3NWUyZjM2MDQ0
15
+ ZDkwOTc1OGRlNzg3ODA2ZDNkNjNhMDM4OGFmNDFiY2FjOGU2YWU=
data/bin/onboard CHANGED
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
3
6
  require 'onboard'
4
7
 
5
8
  Onboard::CLI.start(ARGV)
data/lib/onboard/cli.rb CHANGED
@@ -10,7 +10,6 @@ require_relative 'repo'
10
10
 
11
11
  module Onboard
12
12
  class CLI < Thor
13
- # TODO: switch from DOCROOT to CODEBASE to enable more comprehensive searching
14
13
  desc "projects CODEBASE", "add projects to CODEBASE"
15
14
  long_desc <<-LONGDESC
16
15
  `onboard projects` performs multiple tasks when installing contrib
@@ -23,8 +22,6 @@ module Onboard
23
22
  * Adds and commits each project
24
23
 
25
24
  LONGDESC
26
- # TODO: Analyze codebase for project version
27
- # ala - find ./CODEBASE -type f -name '*.info' | xargs -I {} grep -rn '^version = \"' {}
28
25
  option :branch, :aliases => "-b", :desc => "Specify repository branch to update"
29
26
  option :core, :required => true, :aliases => "-c", :type => :numeric, :desc => "Specify Drupal core version"
30
27
  option :path, :required => true, :aliases => "-p", :desc => "Specify project path relative to CODEBASE"
@@ -38,27 +35,32 @@ module Onboard
38
35
  option :yes, :aliases => "-y", :desc => "Assume 'yes' for all prompts"
39
36
  def projects(codebase)
40
37
  core = "#{options[:core]}.x"
41
- projects = []
38
+ projects = {}
42
39
  if options[:modules].nil? == false
43
- options[:modules].each { |x| projects.push x }
40
+ options[:modules].each { |x| projects[x] = '' }
44
41
  elsif options[:themes].nil? == false
45
- options[:themes].each { |x| projects.push x }
42
+ options[:themes].each { |x| projects[x] = '' }
46
43
  end
47
44
  path = "#{options[:path]}"
48
45
  found = Finder.new(projects, codebase).locate
49
- if found.any?
46
+ if found.empty? == false
50
47
  say("Projects exist at the following locations:", :yellow)
51
- found.each { |x| puts " " + x }
48
+ found.each do |x, y|
49
+ puts " " + x
50
+ projects[File.basename(x)] = y
51
+ end
52
52
  puts ""
53
53
  end
54
54
  if options[:force] != 'force'
55
- found.each do |x|
56
- projects.delete(File.basename(x))
55
+ if found.empty? == false
56
+ found.each do |x, y|
57
+ projects.delete(File.basename(x))
58
+ end
57
59
  end
58
60
  end
59
61
  if projects.empty? == false
60
62
  say("Ready to add the following projects:", :green)
61
- projects.each do |x|
63
+ projects.each do |x, y|
62
64
  puts " " + "#{codebase}/#{path}/#{x}"
63
65
  end
64
66
  puts ""
@@ -68,53 +70,22 @@ module Onboard
68
70
  say("Script was exited.")
69
71
  exit
70
72
  end
71
- projects.each do |x|
72
- prm = {}
73
- prm['path'] = "#{codebase}/#{path}/#{x}"
74
- Project.new(prm).rm
75
- # TODO: replace 'open().read' with custom caching solution
76
- pdl = {}
77
- pdl['project'] = x
78
- pdl['core'] = core
79
- dl = Project.new(pdl).dl
80
- feed_md5, archive = dl
81
- # TODO: replace 'open().read' with custom caching solution
82
- open(archive).read
83
- uri = URI.parse(archive)
84
- targz = "/tmp/open-uri-503" + [ @path, uri.host, Digest::SHA1.hexdigest(archive) ].join('/')
85
- md5 = Digest::MD5.file(targz).hexdigest
86
- # TODO: retry download after failed download verification
87
- if md5 == feed_md5
88
- pex = {}
89
- pex['dest'] = "#{codebase}/#{path}"
90
- pex['path'] = targz
91
- Project.new(pex).extract
92
- else
93
- say("Verification failed for #{x} archive!", :red)
94
- exit
95
- end
96
- end
73
+ prj = {}
74
+ prj['codebase'] = codebase
75
+ prj['path'] = "#{codebase}/#{path}"
76
+ prj['projects'] = projects
77
+ prj['core'] = core
78
+ Project.new(prj).dl
97
79
  if options[:vc] == true
98
- require_relative 'msg'
99
80
  branch = options[:branch].nil? ? '' : options[:branch]
100
81
  repo = {}
101
82
  repo['branch'] = branch
102
83
  repo['codebase'] = codebase
103
- repo_info = Repo.new(repo).info
104
- projects.each do |x|
105
- acmsg = "Committing #{x} on #{repo_info['current_branch']} branch..."
106
- say(Msg.new(acmsg).format)
107
- repo['path'] = "#{path}/#{x}"
108
- Repo.new(repo).update
109
- say(" [done]", :green)
110
- # TODO: error handling and conditional messaging for failures
111
- end
112
- pmsg = "Pushing all changes to #{repo_info['remotes']}..."
113
- say(Msg.new(pmsg).format)
114
- Repo.new(repo).push
115
- say(" [done]", :green)
84
+ repo['projects'] = projects
85
+ repo['path'] = path
86
+ Repo.new(repo).update
116
87
  else
117
- projects.each do |x|
88
+ projects.each do |x, y|
118
89
  say("#{x} added to codebase but changes are not yet tracked in version control.", :yellow)
119
90
  end
120
91
  end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'net/http'
4
+
5
+ module Onboard
6
+ class Download
7
+ def initialize(cache_dir='/tmp')
8
+ @cache_dir = cache_dir
9
+ end
10
+
11
+ def path(url)
12
+ File.join("", @cache_dir, Digest::MD5.hexdigest(url))
13
+ end
14
+
15
+ def fetch(url, max_age=1800)
16
+ file_path = self.path(url)
17
+ if File.exists? file_path
18
+ return File.new(file_path).read if Time.now-File.mtime(file_path)<max_age
19
+ end
20
+ File.open(file_path, "w") do |data|
21
+ data << Net::HTTP.get_response(URI.parse(url)).body
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/onboard/find.rb CHANGED
@@ -12,20 +12,38 @@ module Onboard
12
12
  end
13
13
 
14
14
  def locate
15
- found = []
15
+ found = {}
16
16
  Find.find(haystack) do |e|
17
17
  if File.directory?(e)
18
- if needle.include?(File.basename(e))
19
- Find.find(e) do |f|
20
- if File.extname(f) == '.info'
21
- found.push e
18
+ if needle.has_key?(File.basename(e))
19
+ Dir.entries(e).select do |f|
20
+ file = "#{e}/#{f}"
21
+ if File.file?(file)
22
+ if self.info_ext?(file)
23
+ if self.version(file).empty? == false
24
+ found[e] = self.version(file)
25
+ end
26
+ end
22
27
  end
23
28
  end
24
29
  end
25
30
  end
26
31
  end
32
+ return found
33
+ end
34
+
35
+ def info_ext?(file)
36
+ File.extname(file) == '.info'
37
+ end
27
38
 
28
- return found.uniq
39
+ def version(file)
40
+ File.open(file) do |g|
41
+ g.each_line do |line|
42
+ if line =~ /version/
43
+ return line.scan(/.*?"(.*?)".*$/)[0].nil? ? '' : line.scan(/.*?"(.*?)".*$/)[0]
44
+ end
45
+ end
46
+ end
29
47
  end
30
48
  end
31
49
  end
data/lib/onboard/msg.rb CHANGED
@@ -14,9 +14,9 @@ module Onboard
14
14
  end
15
15
 
16
16
  def format
17
- width = Screen.new().width
17
+ height, width = Screen.new().size
18
18
  spaces = " " * (width - msg.length - 8)
19
- return msg + spaces
19
+ say(msg + spaces)
20
20
  end
21
21
  end
22
22
  end
@@ -1,65 +1,125 @@
1
1
  # encoding: utf-8
2
+
2
3
  require 'fileutils'
3
4
  require 'find'
4
5
  require 'nokogiri'
5
6
  require 'rubygems/package'
7
+ require 'thor'
6
8
  require 'zlib'
7
9
 
10
+ require_relative 'confirm'
11
+ require_relative 'download'
12
+ require_relative 'repo'
13
+
8
14
  module Onboard
9
15
 
10
16
  TAR_LONGLINK = '././@LongLink'
17
+ DRUPAL_PRJ_FEED = "http://updates.drupal.org/release-history/"
18
+ DRUPAL_DL_LINK = "http://ftp.drupal.org/files/projects/"
11
19
 
12
- class Project
13
- attr_reader :feed, :path, :dest
20
+ class Project < Thor
21
+ attr_reader :core, :path, :projects, :codebase
14
22
 
15
- def initialize(args = {})
16
- @feed = "http://updates.drupal.org/release-history/#{args['project']}/#{args['core']}"
17
- @path = args['path']
18
- @dest = args['dest']
19
- end
23
+ no_tasks do
24
+ def initialize(args = {})
25
+ @core = args['core']
26
+ @path = args['path']
27
+ @projects = args['projects']
28
+ @codebase = args['codebase']
29
+ end
30
+
31
+ def feed(project)
32
+ "#{DRUPAL_PRJ_FEED}#{project}/#{core}"
33
+ end
20
34
 
21
- def dl
22
- doc = Nokogiri::XML(open(@feed).read)
23
- releases = {}
24
- doc.xpath('//releases//release').each do |item|
25
- if !item.at_xpath('version_extra')
26
- releases[item.at_xpath('mdhash').content] = item.at_xpath('download_link').content
35
+ def hacked?(project, existing)
36
+ self.clean("#{path}/#{project}")
37
+ link = DRUPAL_DL_LINK + project + "-" + existing + ".tar.gz"
38
+ Download.new.fetch(link)
39
+ self.extract(Download.new.path(link)) if self.verify(project, link, existing)
40
+ st = {}
41
+ st['codebase'] = codebase
42
+ changes = Repo.new(st).st
43
+ if changes.empty? == false
44
+ Confirm.new("Proceed?").yes?
27
45
  end
28
46
  end
29
- if releases.nil?
30
- doc.xpath('//releases//release').each do |item|
31
- releases[item.at_xpath('mdhash').content] = item.at_xpath('download_link').content
47
+
48
+ def dl
49
+ projects.each do |x, y|
50
+ self.hacked?(x, y[0]) if y.empty? == false
51
+ self.clean("#{path}/#{x}")
52
+ md5, link = self.release(x)
53
+ Download.new.fetch(link)
54
+ self.extract(Download.new.path(link)) if self.verify(x, link)
55
+ # TODO: retry download after failed download verification
32
56
  end
33
57
  end
34
- return releases.first
35
- end
36
58
 
37
- def rm
38
- FileUtils.rm_r path if File.directory?(path)
39
- end
59
+ def verify(x, file, version='')
60
+ md5, link = self.release(x, version)
61
+ if md5 == Digest::MD5.file(Download.new.path(file)).hexdigest
62
+ return true
63
+ else
64
+ say("Verification failed for #{project} download!", :red)
65
+ exit
66
+ end
67
+ end
40
68
 
41
- def extract
42
- Gem::Package::TarReader.new( Zlib::GzipReader.open path ) do |tar|
43
- dst = nil
44
- tar.each do |entry|
45
- if entry.full_name == TAR_LONGLINK
46
- dst = File.join dest, entry.read.strip
47
- next
69
+ def release(project, version='')
70
+ feed = self.feed(project)
71
+ Download.new.fetch(feed)
72
+ xml = File.open(Download.new.path(feed))
73
+ doc = Nokogiri::XML(xml)
74
+ releases = {}
75
+ if version.empty? == false
76
+ doc.xpath('//releases//release').each do |item|
77
+ if item.at_xpath('version').content == version
78
+ releases[item.at_xpath('mdhash').content] = item.at_xpath('download_link').content
79
+ end
48
80
  end
49
- dst ||= File.join dest, entry.full_name
50
- if entry.directory?
51
- FileUtils.rm_rf dst unless File.directory? dst
52
- FileUtils.mkdir_p dst, :mode => entry.header.mode, :verbose => false
53
- elsif entry.file?
54
- FileUtils.rm_rf dst unless File.file? dst
55
- File.open dst, "wb" do |f|
56
- f.print entry.read
81
+ else
82
+ doc.xpath('//releases//release').each do |item|
83
+ if !item.at_xpath('version_extra')
84
+ releases[item.at_xpath('mdhash').content] = item.at_xpath('download_link').content
57
85
  end
58
- FileUtils.chmod entry.header.mode, dst, :verbose => false
59
- elsif entry.header.typeflag == '2' #Symlink!
60
- File.symlink entry.header.linkname, dst
61
86
  end
87
+ end
88
+ if releases.nil?
89
+ doc.xpath('//releases//release').each do |item|
90
+ releases[item.at_xpath('mdhash').content] = item.at_xpath('download_link').content
91
+ end
92
+ end
93
+ return releases.first
94
+ end
95
+
96
+ def clean(arg)
97
+ FileUtils.rm_r arg if File.directory?(arg)
98
+ end
99
+
100
+ def extract(archive)
101
+ Gem::Package::TarReader.new( Zlib::GzipReader.open archive ) do |tar|
62
102
  dst = nil
103
+ tar.each do |entry|
104
+ if entry.full_name == TAR_LONGLINK
105
+ dst = File.join path, entry.read.strip
106
+ next
107
+ end
108
+ dst ||= File.join path, entry.full_name
109
+ if entry.directory?
110
+ FileUtils.rm_rf dst unless File.directory? dst
111
+ FileUtils.mkdir_p dst, :mode => entry.header.mode, :verbose => false
112
+ elsif entry.file?
113
+ FileUtils.rm_rf dst unless File.file? dst
114
+ File.open dst, "wb" do |f|
115
+ f.print entry.read
116
+ end
117
+ FileUtils.chmod entry.header.mode, dst, :verbose => false
118
+ elsif entry.header.typeflag == '2' #Symlink!
119
+ File.symlink entry.header.linkname, dst
120
+ end
121
+ dst = nil
122
+ end
63
123
  end
64
124
  end
65
125
  end
data/lib/onboard/repo.rb CHANGED
@@ -1,52 +1,109 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # TODO: Watch for updates to rugged that enable git push
4
3
  require 'git'
5
4
  require 'pathname'
5
+ require 'thor'
6
+
7
+ require_relative 'msg'
6
8
 
7
9
  module Onboard
8
- class Repo
9
- attr_reader :g, :path, :branch
10
-
11
- def initialize(repo)
12
- @codebase = repo['codebase']
13
- @g = self.prepare(repo)
14
- @path = repo['path']
15
- @branch = repo['branch']
16
- end
10
+ class Repo < Thor
11
+ attr_reader :g, :path, :branch, :codebase, :projects
17
12
 
18
- def info
19
- repo = {}
20
- repo['current_branch'] = g.current_branch
21
- repo['remotes'] = g.remotes
22
- return repo
23
- end
13
+ no_tasks do
14
+ def initialize(repo)
15
+ @codebase = repo['codebase']
16
+ @g = self.prepare(repo)
17
+ @path = repo['path']
18
+ @branch = repo['branch']
19
+ @projects = repo['projects']
20
+ end
24
21
 
25
- def prepare(args)
26
- repo = Git.open((Pathname.new(args['codebase'])).to_s)
27
- selection = args['branch'].empty? ? repo.current_branch : args['branch']
28
- repo.branch(selection).checkout
29
- return repo
30
- end
22
+ def st
23
+ changed = []
24
+ deleted = []
25
+ untracked = []
26
+
27
+ # TODO: figure out why g.status.changed.keys.each is returning
28
+ # unchanged files
29
+ g.status.changed.keys.each { |file| changed.push(file.to_s) if !g.diff('HEAD', file).patch.empty? }
30
+ g.status.deleted.keys.each { |file| deleted.push(file.to_s) }
31
+ g.status.untracked.keys.each { |file| untracked.push(file.to_s) }
32
+ all = changed + deleted + untracked
31
33
 
32
- def update
33
- project = File.basename(path)
34
+ if changed.empty? == false
35
+ say('CHANGED FILES:', :yellow)
36
+ changed.each { |z| puts g.diff('HEAD', z).patch }
37
+ puts ''
38
+ end
34
39
 
35
- changes = []
36
- g.status.changed.keys.each { |x| changes.push x }
37
- g.status.deleted.keys.each { |x| changes.push x }
38
- g.status.untracked.keys.each { |x| changes.push x }
40
+ if deleted.empty? == false
41
+ say('DELETED FILES:', :yellow)
42
+ deleted.each { |x| say(x, :red) }
43
+ puts ''
44
+ end
39
45
 
40
- if changes.nil? == false
41
- g.add(path, :all=>true)
42
- g.commit("Add #{project}")
43
- else
44
- puts "No changes to commit for #{project}"
46
+ if untracked.empty? == false
47
+ say('UNTRACKED FILES:', :yellow)
48
+ untracked.each { |y| say(y, :red) }
49
+ puts ''
50
+ end
51
+
52
+ return all
45
53
  end
46
- end
47
54
 
48
- def push
49
- g.push
55
+ def info
56
+ repo = {}
57
+ repo['current_branch'] = g.current_branch
58
+ repo['remotes'] = g.remotes
59
+ return repo
60
+ end
61
+
62
+ def prepare(args)
63
+ Git.open((Pathname.new(args['codebase'])).to_s)
64
+ end
65
+
66
+ def update
67
+ info = self.info
68
+ changes = []
69
+ projects.each do |x, y|
70
+ msg = "Committing #{x} on #{info['current_branch']} branch..."
71
+ Msg.new(msg).format
72
+ changes = self.commit("#{path}/#{x}")
73
+ if changes.empty? == false
74
+ say(" [done]", :green)
75
+ else
76
+ puts "\nNo changes to commit for #{x}"
77
+ end
78
+ end
79
+ if changes.empty? == false
80
+ msg = "Pushing all changes to #{info['remotes'][0]}..."
81
+ Msg.new(msg).format
82
+ self.push
83
+ say(" [done]", :green)
84
+ end
85
+ end
86
+
87
+ def commit(path)
88
+ project = File.basename(path)
89
+
90
+ changes = []
91
+ g.status.changed.keys.each { |x| changes.push x if !g.diff('HEAD', x).patch.empty? }
92
+ g.status.deleted.keys.each { |x| changes.push x }
93
+ g.status.untracked.keys.each { |x| changes.push x }
94
+
95
+ if changes.empty? == false
96
+ g.add(path, :all=>true)
97
+ g.commit("Add #{project}")
98
+ end
99
+
100
+ return changes
101
+ end
102
+
103
+ def push
104
+ g.push
105
+ end
50
106
  end
51
107
  end
52
108
  end
109
+
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'curses'
3
+ require 'io/console'
4
4
 
5
5
  module Onboard
6
6
  class Screen
7
- Curses.init_screen()
8
7
 
9
- def width
10
- return Curses.cols
8
+ def size
9
+ IO.console.winsize
10
+ rescue LoadError
11
+ [Integer(`tput li`), Integer(`tput co`)]
11
12
  end
12
13
  end
13
14
  end
15
+
@@ -1,3 +1,3 @@
1
1
  module Onboard
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
data/onboard.gemspec CHANGED
@@ -17,7 +17,6 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
  spec.add_dependency "nokogiri", "~> 1.6"
20
- spec.add_dependency "open-uri-cached", [">= 0.0.5", "< 2"]
21
20
  spec.add_dependency "git", "~> 1.2"
22
21
  spec.add_dependency "thor", [">= 0.19.1", "< 2"]
23
22
  spec.add_development_dependency "bundler", "~> 1.6"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathaniel Hoag
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-24 00:00:00.000000000 Z
11
+ date: 2014-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -24,26 +24,6 @@ dependencies:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.6'
27
- - !ruby/object:Gem::Dependency
28
- name: open-uri-cached
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ! '>='
32
- - !ruby/object:Gem::Version
33
- version: 0.0.5
34
- - - <
35
- - !ruby/object:Gem::Version
36
- version: '2'
37
- type: :runtime
38
- prerelease: false
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ! '>='
42
- - !ruby/object:Gem::Version
43
- version: 0.0.5
44
- - - <
45
- - !ruby/object:Gem::Version
46
- version: '2'
47
27
  - !ruby/object:Gem::Dependency
48
28
  name: git
49
29
  requirement: !ruby/object:Gem::Requirement
@@ -123,6 +103,7 @@ files:
123
103
  - lib/onboard.rb
124
104
  - lib/onboard/cli.rb
125
105
  - lib/onboard/confirm.rb
106
+ - lib/onboard/download.rb
126
107
  - lib/onboard/find.rb
127
108
  - lib/onboard/msg.rb
128
109
  - lib/onboard/project.rb