manifestly 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e99d41748251cf3299fda9c0d9a5175377b9b4dd
4
+ data.tar.gz: 9bd8f1e0fc81ef7f0ca0ab23d0a1a4fd3f67c4e9
5
+ SHA512:
6
+ metadata.gz: cbb2d0025da752056c50b00ab2b0a3eb3c9d39d5db16f1d442bf4423ce21fffd2a403fd063e6d099e25c201101f5971b147794ce2bbe0c73f16b497952e511dc
7
+ data.tar.gz: 27fe5f795c060428d1a0af2d791c35dd4d9f7fbdbd3deeb4d245a76b0fccd3c36d00307b5fca4ef3dd099470d019590aab0ebcfa2f72c4f7e7f2cfc9d78a10c9
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .DS_Store
11
+ *.manifest
12
+ *.*~
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1 @@
1
+ manifestly
@@ -0,0 +1 @@
1
+ 2.2.3
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in manifestly.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 JP Slavinsky
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ # Manifestly
2
+
3
+ Manifestly helps you manage complicated deployments involving multiple sites and git repositories.
4
+
5
+ ## Installation
6
+
7
+ Manifestly is run as a standalone executable. Install with:
8
+
9
+ $ gem install manifestly
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install manifestly
18
+
19
+ ## Usage
20
+
21
+ Manifestly has built in help:
22
+
23
+ $ manifestly help
24
+
25
+ ### create
26
+
27
+ To create a new manifest, run
28
+
29
+ $ manifestly create
30
+
31
+ Both `create` and `apply` take a `--search-paths` option to specify where the repositories of interest can be found. The default search path is the current directory.
32
+
33
+ `create` will then show you a blank manifest and give you options to add or remove repositories, or to choose the manifest commit for an existing repository. When repositories are added, their latest commit is listed. All commits seen during manifest creation are local commits only -- this tool does not look up remote commits.
34
+
35
+ ### push
36
+
37
+ To push a manifest file you have created, call:
38
+
39
+ $ manifestly push --local=my_file.manifest --mfrepo=myorg/myrepo --remote=foo
40
+
41
+ This will take your local file and push it as the latest commit on top of the `foo` file at github.com/myorg/myrepo.
42
+
43
+ ### pull
44
+
45
+ To pull a manifest file... instructions TBD, see built-in help.
46
+
47
+ ### apply
48
+
49
+ To apply a manifest file to your local repositories... instructions TBD, see built-in help.
50
+
51
+ $ manifestly apply --file=20150919-210854-2d47.manifest
52
+
53
+ ### list
54
+
55
+ To list available manifest files... instructions TBD, see built-in help
56
+
57
+ ## Development
58
+
59
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
60
+
61
+ To run manifestly like an end user eventually will, run:
62
+
63
+ ```
64
+ exe/manifestly <OPTIONS AND ARGS HERE>
65
+ ```
66
+
67
+ Remember to pass the `--search-paths` option to tell manifestly where to find your repositories relative to where you are running the gem from.
68
+
69
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
70
+
71
+ ## Contributing
72
+
73
+ 1. Fork it ( https://github.com/[my-github-username]/manifestly/fork )
74
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
75
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
76
+ 4. Push to the branch (`git push origin my-new-feature`)
77
+ 5. Create a new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "manifestly"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
6
+
7
+ begin
8
+ require 'manifestly'
9
+ rescue LoadError => e
10
+ warn "Could not load 'manifestly'; #{e.message}"
11
+ exit -1
12
+ end
13
+
14
+ Manifestly::CLI.start
@@ -0,0 +1,8 @@
1
+ require_relative "manifestly/version"
2
+ require_relative 'manifestly/utilities'
3
+ require_relative 'manifestly/repository'
4
+ require_relative 'manifestly/manifest'
5
+ require_relative 'manifestly/ui'
6
+ require_relative 'manifestly/cli'
7
+
8
+ module Manifestly; end
@@ -0,0 +1,289 @@
1
+ require "thor"
2
+ require 'rainbow'
3
+ require 'command_line_reporter'
4
+ require 'git'
5
+ require 'securerandom'
6
+
7
+ # TODO make ruby 1.9 compatible
8
+
9
+ module Manifestly
10
+ class CLI < Thor
11
+ package_name "manifestly"
12
+
13
+ include CommandLineReporter
14
+ include Manifestly::Ui
15
+
16
+ def self.search_paths_option
17
+ method_option :search_paths,
18
+ :desc => "A list of paths where git repositories can be found",
19
+ :type => :array,
20
+ :required => false,
21
+ :default => '.'
22
+ end
23
+
24
+ desc "create", "Create a new manifest"
25
+ search_paths_option
26
+ method_option :based_on,
27
+ :desc => "A manifest file to use as a starting point",
28
+ :type => :string,
29
+ :required => false
30
+ def create
31
+ manifest = if options[:based_on]
32
+ Manifest.read(options[:based_on], available_repositories)
33
+ else
34
+ Manifest.new
35
+ end
36
+
37
+ present_manifest_menu(manifest)
38
+ end
39
+
40
+ desc "apply", "Sets the manifest's repository's current states to the commits listed in the manifest"
41
+ search_paths_option
42
+ method_option :file,
43
+ :desc => "The manifest file to apply",
44
+ :type => :string,
45
+ :required => true
46
+ def apply
47
+ manifest = Manifest.read(options[:file], available_repositories)
48
+ manifest.items.each(&:checkout_commit!)
49
+ end
50
+
51
+ desc "push", "Pushes a local manifest file to a manifest repository"
52
+ method_option :local,
53
+ :desc => 'The local manifest file to push',
54
+ :type => :string,
55
+ :required => true
56
+ method_option :mfrepo,
57
+ :desc => "The repository to push to (full URL or 'organization/reponame')",
58
+ :type => :string,
59
+ :required => true
60
+ method_option :remote,
61
+ :desc => "The name of the remote file",
62
+ :type => :string,
63
+ :required => true
64
+ def push
65
+ say("Not yet implemented.")
66
+ end
67
+
68
+ desc "pull", "Downloads a manifest file from a manifest repository"
69
+ method_option :sha,
70
+ :desc => "The commit SHA of the manifest on the remote repository",
71
+ :type => :string,
72
+ :required => true
73
+ method_option :mfrepo,
74
+ :desc => "The manifest repository to pull from (full URL or 'organization/reponame')",
75
+ :type => :string,
76
+ :required => true
77
+ method_option :save_as,
78
+ :desc => "The name to use for the downloaded file (defaults to '<SHA>.manifest')",
79
+ :type => :string,
80
+ :required => false
81
+ def pull
82
+ say("Not yet implemented.")
83
+ end
84
+
85
+ desc "list", "Lists manifests from a manifest repository"
86
+ method_option :mfrepo,
87
+ :desc => "The manifest repository to read from (full URL or 'organization/reponame')",
88
+ :type => :string,
89
+ :required => true
90
+ method_option :remote,
91
+ :desc => "The name of the manifest to read from",
92
+ :type => :string,
93
+ :required => true
94
+ def list
95
+ say("Not yet implemented.")
96
+ end
97
+
98
+ protected
99
+
100
+ def present_manifest_menu(manifest)
101
+ while true
102
+ print_manifest(manifest)
103
+
104
+ action, args = ask_and_split(
105
+ '(a)dd or (r)emove repository; (c)hoose commit; (w)rite manifest; (q)uit:'
106
+ ) || next
107
+
108
+ case action
109
+ when 'a'
110
+ add_repositories(manifest)
111
+ when 'r'
112
+ indices = convert_args_to_indices(args) || next
113
+ manifest.remove_repositories_by_index(indices)
114
+ when 'c'
115
+ indices = convert_args_to_indices(args, true) || next
116
+ present_commit_menu(manifest[indices.first])
117
+ when 'w'
118
+ default_filename = Time.now.strftime("%Y%m%d-%H%M%S") + "-#{::SecureRandom.hex(2)}.manifest"
119
+ filename = ask("Enter desired manifest filename (ENTER for '#{default_filename}'):")
120
+ filename = default_filename if filename.blank?
121
+
122
+ manifest.write(filename)
123
+ break
124
+ when 'q'
125
+ break if yes?('Are you sure you want to quit? (y or yes):')
126
+ end
127
+ end
128
+ end
129
+
130
+ def present_commit_menu(manifest_item)
131
+ while true
132
+ print_commit_shas(manifest_item)
133
+
134
+ action, args = ask_and_split(
135
+ '(n)ext or (p)revious page; (c)hoose index; (m)anual SHA entry; (t)oggle PRs only; (r)eturn:'
136
+ ) || next
137
+
138
+ case action
139
+ when 'n'
140
+ manifest_item.repository.next_page_of_commits
141
+ when 'p'
142
+ manifest_item.repository.prev_page_of_commits
143
+ when 'c'
144
+ indices = convert_args_to_indices(args, true) || next
145
+ manifest_item.set_commit_by_index(indices.first)
146
+ break
147
+ when 'm'
148
+ sha = args.first
149
+ begin
150
+ manifest_item.set_commit_by_sha(sha)
151
+ break
152
+ rescue CommitNotPresent
153
+ say('That SHA is invalid')
154
+ next
155
+ end
156
+ when 't'
157
+ manifest_item.repository.toggle_prs_only
158
+ when 'r'
159
+ break
160
+ end
161
+ end
162
+ end
163
+
164
+ def ask_and_split(message)
165
+ answer = ask(message).downcase.split
166
+
167
+ if answer.empty?
168
+ say('No response provided, please try again.')
169
+ return false
170
+ end
171
+
172
+ [answer.shift, answer]
173
+ end
174
+
175
+ def convert_args_to_indices(args, select_one=false)
176
+ if args.empty?
177
+ say('You must specify index(es) of manifest items after the action, e.g. "r 2 7".')
178
+ false
179
+ elsif select_one && args.size > 1
180
+ say('You can only specify one manifest index for this action.')
181
+ false
182
+ elsif args.any? {|si| !si.is_i?}
183
+ say('All specified indices must be integers.')
184
+ false
185
+ else
186
+ args.collect{|index| index.to_i}
187
+ end
188
+ end
189
+
190
+ def add_repositories(manifest)
191
+ puts "\n"
192
+ selected_repositories = select(
193
+ repository_choices,
194
+ hide_shortcuts: true,
195
+ choice_name: "repository",
196
+ question: "\nChoose which repositories you want in the manifest (e.g. '0 2 5'):"
197
+ )
198
+
199
+ selected_repositories.each do |repository|
200
+ manifest.add_repository(repository)
201
+ end
202
+ end
203
+
204
+ def print_manifest(manifest)
205
+ puts "\n"
206
+ puts "Current Manifest:\n"
207
+ puts "\n"
208
+ table :border => false do
209
+ row :header => true do
210
+ column "#", width: 4
211
+ column "Repository", width: 36
212
+ column "Branch", width: 30
213
+ column "SHA", width: 10
214
+ end
215
+
216
+ if manifest.empty?
217
+ row do
218
+ column ""
219
+ column "----- EMPTY -----"
220
+ end
221
+ else
222
+ manifest.items.each_with_index do |item, index|
223
+ row do
224
+ column "(#{index})", align: 'right', width: 4
225
+ column "#{item.repository_name}", width: 40
226
+ column "#{item.repository.current_branch_name}"
227
+ column "#{item.commit.sha[0..9]}", width: 10
228
+ end
229
+ end
230
+ end
231
+ end
232
+ puts "\n"
233
+ end
234
+
235
+ def print_commit_shas(manifest_item)
236
+ puts "\n"
237
+ puts "Commit SHAs for #{manifest_item.repository_name}:\n"
238
+ puts "\n"
239
+ table :border => true do
240
+ row :header => true do
241
+ column "#", width: 4
242
+ column "SHA", width: 10
243
+ column "Message", width: 54
244
+ column "Date", width: 25
245
+ end
246
+
247
+ manifest_item.repository.commits.each_with_index do |commit, index|
248
+ row do
249
+ column "#{index}", align: 'right'
250
+ column "#{commit.sha[0..9]}"
251
+ column "#{summarize_commit_message(commit)}"
252
+ column "#{commit.date}"
253
+ end
254
+ end
255
+ end
256
+ puts "\n"
257
+ end
258
+
259
+ def summarize_commit_message(commit)
260
+ commit.message
261
+ .gsub(/Merge pull request (#\w+)( from [\w-]+\/[\w-]+)/, 'PR \1')
262
+ .gsub("\n",' ')
263
+ .gsub(/\s+/, ' ')[0..79]
264
+ end
265
+
266
+ def repository_choices
267
+ available_repositories.collect{|repo| {display: repo.github_name || repo.working_dir, value: repo}}
268
+ end
269
+
270
+ def available_repositories
271
+ @available_repositories ||= (repository_search_paths.flat_map do |path|
272
+ directories_under(path).collect{|dir| Manifestly::Repository.load(dir)}
273
+ end).compact
274
+ end
275
+
276
+ def directories_under(path)
277
+ entries = Dir.entries(path)
278
+ entries.reject!{ |dir| dir =='.' || dir == '..' }
279
+
280
+ full_entry_paths = entries.collect{|entry| File.join(path, entry)}
281
+ full_entry_paths.reject{ |path| !File.directory?(path) }
282
+ end
283
+
284
+ def repository_search_paths
285
+ [options[:search_paths]].flatten
286
+ end
287
+
288
+ end
289
+ end
@@ -0,0 +1,56 @@
1
+ require_relative './manifest_item'
2
+
3
+ module Manifestly
4
+ class Manifest
5
+
6
+ def initialize()
7
+ @items = []
8
+ end
9
+
10
+ def add_repository(repository)
11
+ @items.push(Manifestly::ManifestItem.new(repository))
12
+ end
13
+
14
+ def add_item(manifest_item)
15
+ @items.push(manifest_item)
16
+ end
17
+
18
+ def remove_repositories_by_index(indices)
19
+ indices.each{|index| @items[index] = nil}
20
+ @items.compact!
21
+ nil
22
+ end
23
+
24
+ def [](index)
25
+ @items[index]
26
+ end
27
+
28
+ def self.read(filename, repositories)
29
+ manifest = Manifest.new
30
+
31
+ File.open(filename, 'r') do |file|
32
+ file.each_line do |line|
33
+ item = ManifestItem.from_file_string(line, repositories)
34
+ manifest.add_item(item)
35
+ end
36
+ end
37
+
38
+ manifest
39
+ end
40
+
41
+ def write(filename)
42
+ File.open(filename, 'w') do |file|
43
+ @items.each do |item|
44
+ file.write(item.to_file_string + "\n")
45
+ end
46
+ end
47
+ end
48
+
49
+ attr_reader :items
50
+
51
+ def empty?
52
+ items.empty?
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,48 @@
1
+ module Manifestly
2
+ class ManifestItem
3
+
4
+ class InvalidManifestItem < StandardError; end
5
+ class RepositoryNotFound < StandardError; end
6
+
7
+ def initialize(repository)
8
+ @repository = repository
9
+ @commit = repository.commits.first
10
+ end
11
+
12
+ def repository_name
13
+ @repository.github_name
14
+ end
15
+
16
+ def set_commit_by_index(index)
17
+ @commit = @repository.commits[index]
18
+ end
19
+
20
+ def set_commit_by_sha(sha)
21
+ @commit = @repository.find_commit(sha)
22
+ end
23
+
24
+ def checkout_commit!
25
+ @repository.checkout_commit(@commit.sha)
26
+ end
27
+
28
+ def to_file_string
29
+ "#{repository_name} @ #{commit.sha}"
30
+ end
31
+
32
+ def self.from_file_string(string, repositories)
33
+ repo_name, sha = string.split('@').collect(&:strip)
34
+ raise(InvalidManifestItem, string) if repo_name.blank? || sha.blank?
35
+
36
+ repository = repositories.select{|repo| repo.github_name == repo_name}.first
37
+ raise(RepositoryNotFound, repo_name) if repository.nil?
38
+
39
+ item = ManifestItem.new(repository)
40
+ item.set_commit_by_sha(sha)
41
+ item
42
+ end
43
+
44
+ attr_accessor :commit
45
+ attr_reader :repository
46
+
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ module Manifestly
2
+ class ManifestRepository
3
+
4
+ def self.get(url_or_name)
5
+ # Checkout locally in tmp dir if not yet avail
6
+ # then wrap with Repository and instantiate
7
+ end
8
+
9
+ protected
10
+
11
+ def initialize()
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,87 @@
1
+ class Manifestly::Repository
2
+
3
+ class CommitNotPresent < StandardError; end
4
+
5
+ # Returns an object if can load a git repository at the specified path,
6
+ # otherwise nil
7
+ def self.load(path)
8
+ repository = new(path)
9
+ repository.is_git_repository? ? repository : nil
10
+ end
11
+
12
+ def is_git_repository?
13
+ begin
14
+ git
15
+ rescue ArgumentError
16
+ false
17
+ end
18
+ end
19
+
20
+ def git
21
+ Git.open(@path)
22
+ end
23
+
24
+ COMMITS_PER_PAGE = 15
25
+
26
+ def commits
27
+ log = git.log(COMMITS_PER_PAGE)
28
+ log = log.grep("Merge pull request") if @prs_only
29
+ log.skip(@commit_page * COMMITS_PER_PAGE)
30
+ end
31
+
32
+ def next_page_of_commits
33
+ @commit_page += 1
34
+ end
35
+
36
+ def prev_page_of_commits
37
+ @commit_page -= 1
38
+ end
39
+
40
+ def reset_page_of_commits
41
+ @commit_page = 0
42
+ end
43
+
44
+ # returns the commit matching the provided sha or raises
45
+ def find_commit(sha)
46
+ begin
47
+ git.gcommit(sha).tap(&:sha)
48
+ rescue Git::GitExecuteError => e
49
+ raise CommitNotPresent, "SHA not found: #{sha}"
50
+ end
51
+ end
52
+
53
+ def checkout_commit(sha)
54
+ git.checkout(sha)
55
+ end
56
+
57
+ def current_branch_name
58
+ git.lib.branch_current
59
+ end
60
+
61
+ def toggle_prs_only
62
+ @commit_page = 0
63
+ @prs_only = !@prs_only
64
+ end
65
+
66
+ def origin
67
+ @origin ||= git.remotes.select{|remote| remote.name == 'origin'}.first
68
+ end
69
+
70
+ def github_name
71
+ return nil if origin.nil?
72
+ origin.url[/github.com.(.*).git/,1]
73
+ end
74
+
75
+ def working_dir
76
+ git.dir
77
+ end
78
+
79
+ protected
80
+
81
+ def initialize(path)
82
+ @path = path
83
+ @commit_page = 0
84
+ @prs_only = false
85
+ end
86
+
87
+ end
@@ -0,0 +1,73 @@
1
+ module Manifestly
2
+ module Ui
3
+
4
+ def Ui.included(base)
5
+
6
+ def select(choices, options={})
7
+
8
+ options[:hide_choices] ||= false
9
+ options[:hide_shortcuts] ||= false
10
+ options[:select_one] ||= false
11
+ options[:choice_name] ||= 'item'
12
+ options[:inputs] ||= []
13
+ options[:question] ||= "Please choose an item:" # make this dependent on choice_name and select_one
14
+ options[:hide_all_choice] ||= false
15
+
16
+ if !options[:hide_all_choice]
17
+ choices.push(display: 'All', shortcut: 'all', value: choices.collect{|c| c[:value]})
18
+ end
19
+
20
+ selections = []
21
+
22
+ if !options[:inputs].empty?
23
+ selections = (choices.select do |choice|
24
+ options[:inputs].any? do |input|
25
+ input.downcase.starts_with?(choice[:shortcut].downcase)
26
+ end
27
+ end).collect{|choice| choice[:value]}
28
+
29
+ say Rainbow("The input '#{options[:inputs].join(' ')}' did not match any choices.").red if selections.empty?
30
+ else
31
+ if !options[:hide_choices]
32
+ table :border => false do
33
+ row :header => true do
34
+ column "", width: 4
35
+ column options[:choice_name].capitalize, width: 40
36
+ column "Shortcut", width:10 unless options[:hide_shortcuts]
37
+ end
38
+ choices.each_with_index do |choice, index|
39
+ row do
40
+ column "(#{index})", align: 'right', width: 4
41
+ column "#{choice[:display]}", width: 40
42
+ column "#{choice[:shortcut] || 'n/a'}", width: 10 unless options[:hide_shortcuts]
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ selected_indices = ask(options[:question]).split(" ")
49
+
50
+ if selected_indices.length != 1 && options[:select_one]
51
+ say Rainbow("Please choose only one #{options[:choice_name]}! (or CTRL + C to exit)").red
52
+ options[:hide_choices] = true
53
+ selections = select(choices, question, options)
54
+ elsif selected_indices.any? {|si| !si.is_i?}
55
+ say Rainbow("Please enter a number or numbers separated by spaces! (or CTRL + C to exit)").red
56
+ options[:hide_choices] = true
57
+ selections = select(choices, question, options)
58
+ elsif selected_indices.empty?
59
+ say Rainbow("Please choose at least one #{options[:choice_name]}! (or CTRL + C to exit)").red
60
+ selections = select(choices, question, options)
61
+ else
62
+ selections = selected_indices.collect{|si| choices[si.to_i][:value]}.flatten
63
+ end
64
+ end
65
+
66
+ return selections.flatten
67
+ end
68
+ end
69
+
70
+
71
+
72
+ end
73
+ end
@@ -0,0 +1,50 @@
1
+ class Hash
2
+ def get_deep(*fields)
3
+ fields.inject(self) {|acc,e| acc[e] if acc}
4
+ end
5
+
6
+ def except!(*keys)
7
+ keys.each { |key| delete(key) }
8
+ self
9
+ end
10
+
11
+ def except(*keys)
12
+ dup.except!(*keys)
13
+ end
14
+ end
15
+
16
+ class String
17
+ def is_i?
18
+ !!(self =~ /\A[-+]?[0-9]+\z/)
19
+ end
20
+
21
+ BLANK_RE = /\A[[:space:]]*\z/
22
+
23
+ def blank?
24
+ BLANK_RE === self
25
+ end
26
+
27
+ def starts_with?(prefix)
28
+ prefix.respond_to?(:to_str) && self[0, prefix.length] == prefix
29
+ end
30
+
31
+ def wrap(width=78)
32
+ gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
33
+ end
34
+ end
35
+
36
+ class NilClass
37
+ def blank?
38
+ true
39
+ end
40
+ end
41
+
42
+ class Display
43
+ def self.tut(message)
44
+ Rainbow(message).bright.blue
45
+ end
46
+
47
+ def self.prompt(message)
48
+ " #{tut('> ' + message)}"
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module Manifestly
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'manifestly/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "manifestly"
8
+ spec.version = Manifestly::VERSION
9
+ spec.authors = ["JP Slavinsky"]
10
+ spec.email = ["jps@kindlinglabs.com"]
11
+
12
+ spec.summary = %q{Manage multi-site deploy manifests}
13
+ spec.homepage = "https://github.com/openstax/manifestly"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'thor'
22
+ spec.add_dependency 'rainbow'
23
+ spec.add_dependency 'command_line_reporter'
24
+ spec.add_dependency 'git'
25
+
26
+ spec.add_development_dependency 'byebug'
27
+ spec.add_development_dependency "bundler", "~> 1.9"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: manifestly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - JP Slavinsky
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rainbow
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: command_line_reporter
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: git
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.9'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.9'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '10.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '10.0'
111
+ description:
112
+ email:
113
+ - jps@kindlinglabs.com
114
+ executables:
115
+ - manifestly
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - ".rspec"
121
+ - ".ruby-gemset"
122
+ - ".ruby-version"
123
+ - ".travis.yml"
124
+ - Gemfile
125
+ - LICENSE.txt
126
+ - README.md
127
+ - Rakefile
128
+ - bin/console
129
+ - bin/setup
130
+ - exe/manifestly
131
+ - lib/manifestly.rb
132
+ - lib/manifestly/cli.rb
133
+ - lib/manifestly/manifest.rb
134
+ - lib/manifestly/manifest_item.rb
135
+ - lib/manifestly/manifest_repository.rb
136
+ - lib/manifestly/repository.rb
137
+ - lib/manifestly/ui.rb
138
+ - lib/manifestly/utilities.rb
139
+ - lib/manifestly/version.rb
140
+ - manifestly.gemspec
141
+ homepage: https://github.com/openstax/manifestly
142
+ licenses:
143
+ - MIT
144
+ metadata: {}
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ requirements: []
160
+ rubyforge_project:
161
+ rubygems_version: 2.4.8
162
+ signing_key:
163
+ specification_version: 4
164
+ summary: Manage multi-site deploy manifests
165
+ test_files: []