octopolo 0.0.1

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.
Files changed (108) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +21 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +3 -0
  7. data/Guardfile +5 -0
  8. data/MIT-LICENSE +20 -0
  9. data/README.markdown +55 -0
  10. data/Rakefile +38 -0
  11. data/bash_completion.sh +13 -0
  12. data/bin/octopolo +21 -0
  13. data/bin/op +21 -0
  14. data/lib/octopolo.rb +15 -0
  15. data/lib/octopolo/changelog.rb +27 -0
  16. data/lib/octopolo/cli.rb +210 -0
  17. data/lib/octopolo/commands/accept_pull.rb +8 -0
  18. data/lib/octopolo/commands/compare_release.rb +9 -0
  19. data/lib/octopolo/commands/deployable.rb +8 -0
  20. data/lib/octopolo/commands/github_auth.rb +5 -0
  21. data/lib/octopolo/commands/new_branch.rb +9 -0
  22. data/lib/octopolo/commands/new_deployable.rb +8 -0
  23. data/lib/octopolo/commands/new_staging.rb +8 -0
  24. data/lib/octopolo/commands/octopolo_setup.rb +5 -0
  25. data/lib/octopolo/commands/pivotal_auth.rb +5 -0
  26. data/lib/octopolo/commands/pull_request.rb +13 -0
  27. data/lib/octopolo/commands/signoff.rb +10 -0
  28. data/lib/octopolo/commands/stage_up.rb +8 -0
  29. data/lib/octopolo/commands/stale_branches.rb +11 -0
  30. data/lib/octopolo/commands/sync_branch.rb +11 -0
  31. data/lib/octopolo/commands/tag_release.rb +13 -0
  32. data/lib/octopolo/config.rb +146 -0
  33. data/lib/octopolo/convenience_wrappers.rb +46 -0
  34. data/lib/octopolo/dated_branch_creator.rb +81 -0
  35. data/lib/octopolo/git.rb +262 -0
  36. data/lib/octopolo/github.rb +95 -0
  37. data/lib/octopolo/github/commit.rb +45 -0
  38. data/lib/octopolo/github/pull_request.rb +126 -0
  39. data/lib/octopolo/github/pull_request_creator.rb +127 -0
  40. data/lib/octopolo/github/user.rb +40 -0
  41. data/lib/octopolo/jira/story_commenter.rb +26 -0
  42. data/lib/octopolo/pivotal.rb +44 -0
  43. data/lib/octopolo/pivotal/story_commenter.rb +19 -0
  44. data/lib/octopolo/pull_request_merger.rb +99 -0
  45. data/lib/octopolo/renderer.rb +37 -0
  46. data/lib/octopolo/reports.rb +18 -0
  47. data/lib/octopolo/scripts.rb +23 -0
  48. data/lib/octopolo/scripts/accept_pull.rb +67 -0
  49. data/lib/octopolo/scripts/compare_release.rb +52 -0
  50. data/lib/octopolo/scripts/deployable.rb +27 -0
  51. data/lib/octopolo/scripts/github_auth.rb +87 -0
  52. data/lib/octopolo/scripts/new_branch.rb +34 -0
  53. data/lib/octopolo/scripts/new_deployable.rb +14 -0
  54. data/lib/octopolo/scripts/new_staging.rb +15 -0
  55. data/lib/octopolo/scripts/octopolo_setup.rb +55 -0
  56. data/lib/octopolo/scripts/pivotal_auth.rb +44 -0
  57. data/lib/octopolo/scripts/pull_request.rb +127 -0
  58. data/lib/octopolo/scripts/signoff.rb +85 -0
  59. data/lib/octopolo/scripts/stage_up.rb +26 -0
  60. data/lib/octopolo/scripts/stale_branches.rb +54 -0
  61. data/lib/octopolo/scripts/sync_branch.rb +37 -0
  62. data/lib/octopolo/scripts/tag_release.rb +70 -0
  63. data/lib/octopolo/templates/pull_request_body.erb +24 -0
  64. data/lib/octopolo/user_config.rb +112 -0
  65. data/lib/octopolo/version.rb +3 -0
  66. data/lib/octopolo/week.rb +130 -0
  67. data/octopolo.gemspec +31 -0
  68. data/spec/.DS_Store +0 -0
  69. data/spec/octopolo/cli_spec.rb +310 -0
  70. data/spec/octopolo/config_spec.rb +344 -0
  71. data/spec/octopolo/convenience_wrappers_spec.rb +80 -0
  72. data/spec/octopolo/dated_branch_creator_spec.rb +143 -0
  73. data/spec/octopolo/git_spec.rb +419 -0
  74. data/spec/octopolo/github/commit_spec.rb +59 -0
  75. data/spec/octopolo/github/pull_request_creator_spec.rb +174 -0
  76. data/spec/octopolo/github/pull_request_spec.rb +291 -0
  77. data/spec/octopolo/github/user_spec.rb +65 -0
  78. data/spec/octopolo/github_spec.rb +169 -0
  79. data/spec/octopolo/jira/stor_commenter_spec.rb +30 -0
  80. data/spec/octopolo/pivotal/story_commenter_spec.rb +34 -0
  81. data/spec/octopolo/pivotal_spec.rb +61 -0
  82. data/spec/octopolo/pull_request_merger_spec.rb +144 -0
  83. data/spec/octopolo/renderer_spec.rb +35 -0
  84. data/spec/octopolo/scripts/accept_pull_spec.rb +76 -0
  85. data/spec/octopolo/scripts/compare_release_spec.rb +115 -0
  86. data/spec/octopolo/scripts/deployable_spec.rb +52 -0
  87. data/spec/octopolo/scripts/github_auth_spec.rb +156 -0
  88. data/spec/octopolo/scripts/new_branch_spec.rb +41 -0
  89. data/spec/octopolo/scripts/new_deployable_spec.rb +18 -0
  90. data/spec/octopolo/scripts/new_staging_spec.rb +18 -0
  91. data/spec/octopolo/scripts/octopolo_setup_spec.rb +120 -0
  92. data/spec/octopolo/scripts/pivotal_auth_spec.rb +77 -0
  93. data/spec/octopolo/scripts/pull_request_spec.rb +217 -0
  94. data/spec/octopolo/scripts/signoff_spec.rb +139 -0
  95. data/spec/octopolo/scripts/stage_up_spec.rb +52 -0
  96. data/spec/octopolo/scripts/stale_branches_spec.rb +81 -0
  97. data/spec/octopolo/scripts/sync_branch_spec.rb +57 -0
  98. data/spec/octopolo/scripts/tag_release_spec.rb +108 -0
  99. data/spec/octopolo/user_config_spec.rb +167 -0
  100. data/spec/octopolo_spec.rb +7 -0
  101. data/spec/spec_helper.rb +29 -0
  102. data/spec/support/engine_yard.cache +0 -0
  103. data/spec/support/sample_octopolo.yml +2 -0
  104. data/spec/support/sample_user.yml +2 -0
  105. data/templates/lib.erb +23 -0
  106. data/templates/script.erb +7 -0
  107. data/templates/spec.erb +29 -0
  108. metadata +344 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Y2E4MmFlMWM0MTc2YTdkODhhMDc2ODcwNTU0OGY1MzBlZmM4N2ViYQ==
5
+ data.tar.gz: !binary |-
6
+ YjMwZWMyNmRjMWUxYmU1ZWJlMGU4MzExYjFiNzI3ZTdhZTAzYTgwMQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YzBhYzM4NjM1YzYyOWEzMWJlNmVjZWM3Nzk2NGE4MTU5ZTI5MjBiY2Y5NTk1
10
+ MjZlY2ZlOWVmZjMyZmVkZWQzMzkxNDBjMjMwZmU4NTU4MTQxNTUxYjdkZTFh
11
+ NzBhOTE3MWE3Yjk3YTVhMjgzNTI4NWY3MWMzODNhZWY0MmRkNjk=
12
+ data.tar.gz: !binary |-
13
+ NjZkMTYyNGU1ZjNkMGRmZTk4YmExMmIyM2M3NzNkMGIxY2FkYjZkMDgwNTAy
14
+ NWIxMDE4ZWFjOGU5OGEyODIzOWFhMWE1Njg5NzAzODQ0OGU5MDQzY2RlMmQ4
15
+ YmU1ZGM4YzJlYmRlNjA0MmNlYTNiMjdhYmE1M2YzZGNkMTE1ZWI=
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ .automation.yml
2
+ .octopolo.yml
3
+ *.gem
4
+ *.rbc
5
+ .bundle
6
+ .config
7
+ coverage
8
+ InstalledFiles
9
+ lib/bundler/man
10
+ Gemfile.lock
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
17
+
18
+ # YARD artifacts
19
+ .yardoc
20
+ _yardoc
21
+ doc/
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ octopolo
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p448
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :rspec, cmd: 'bundle exec rspec', all_on_start: true, all_after_pass: true do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Sport Ngin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,55 @@
1
+ Octopolo
2
+ ========
3
+
4
+ [![Build Status][build_status_image]][build_status]
5
+
6
+ A set of Github workflow scripts.
7
+
8
+
9
+ ### GitHub Octopolo
10
+
11
+ #### GitHub Setup
12
+
13
+ Interactively set up your local machine for GitHub octopolo, including
14
+ configuring your user-level setting and setting up a GitHub API token for our
15
+ scripts to use.
16
+
17
+ octopolo-setup
18
+
19
+
20
+ #### Create New Branch
21
+
22
+ Create a new branch from the master branch and push it out to GitHub.
23
+
24
+ new-branch bug-123-something
25
+
26
+ #### Create Pull Request for Current Branch
27
+
28
+ Create a pull-request against the project's deploy branch, associating with the
29
+ given GitHub issue, if one is provided.
30
+
31
+ pull-request
32
+
33
+ #### Deploy Current Branch to Staging
34
+
35
+ From within a bugfix branch, merge to the current staging branch.
36
+
37
+ stage-up
38
+
39
+ #### Merge Release Into Your Branch
40
+
41
+ From within a bugfix branch, merge the latest released code (or, optionally,
42
+ another named branch) into your current branch.
43
+
44
+ sync-branch
45
+ sync-branch some-other-branch
46
+
47
+ #### Review Changes In Releases
48
+
49
+ Select from recent release tags and generate a link to the GitHub compare view.
50
+
51
+ compare-releases
52
+
53
+
54
+ [build_status]: https://travis-ci.org/sportngin/octopolo
55
+ [build_status_image]: https://travis-ci.org/sportngin/octopolo.svg?branch=master
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ # TODO dogfood this rake task and wrap it into a script that is tested and whatnot
5
+ desc "Create new octopolo script"
6
+ task :new, [:scriptname] do |t, args|
7
+ require "erb"
8
+ scriptname = args[:scriptname]
9
+
10
+ if scriptname.nil? or scriptname.empty?
11
+ puts "Must provide a script name, e.g., rake new[do_something]"
12
+ else
13
+ # turn foo-bar into FooBar
14
+ class_file_name = scriptname.gsub(/[-]/, "_")
15
+ class_name = scriptname.split(/[_-]/).map(&:capitalize).join
16
+
17
+ puts "Creating a new script named #{scriptname} with a class named #{class_name}"
18
+
19
+ files = {
20
+ "templates/script.erb" => "bin/#{scriptname}",
21
+ "templates/lib.erb" => "lib/octopolo/scripts/#{class_file_name}.rb",
22
+ "templates/spec.erb" => "spec/octopolo/scripts/#{class_file_name}_spec.rb",
23
+ }
24
+
25
+ files.each do |template_path, output_path|
26
+ base_path = File.dirname(__FILE__)
27
+ template_file = File.expand_path(template_path, base_path)
28
+ output_file = File.expand_path(output_path, base_path)
29
+ perms = output_path.include?("bin/") ? 0755 : 0666
30
+
31
+ puts "writing to #{output_path}"
32
+ File.open(output_file, "w+", perms) do |f|
33
+ f.write(ERB.new(File.read(template_file)).result(binding))
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ _op() {
2
+ cur="${COMP_WORDS[COMP_CWORD]}"
3
+
4
+ if [[ -z $OCTOPOLO_0_0_1_COMMANDS ]]; then
5
+ OCTOPOLO_0_0_1_COMMANDS=`op help -c`
6
+ fi
7
+
8
+ COMPREPLY=($(compgen -W "${OCTOPOLO_0_0_1_COMMANDS}" -- ${cur}))
9
+ return 0
10
+ }
11
+
12
+ complete -F _op op
13
+ complete -F _op octopolo
data/bin/octopolo ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'gli'
4
+ require_relative '../lib/octopolo'
5
+
6
+ include GLI::App
7
+
8
+ program_desc 'GitHub workflow scripts. From the Ngineers as SportNgin.'
9
+ version Octopolo::VERSION
10
+
11
+ wrap_help_text :verbatim
12
+
13
+ program_long_desc """
14
+ DOCUMENTATION
15
+ For documentation and help in setting up your configuration files,
16
+ see Octopolo's GitHub repo: https://github.com/sportngin/octopolo
17
+ """
18
+
19
+ commands_from File.expand_path(File.dirname(__FILE__) + '/../lib/octopolo/commands')
20
+
21
+ exit run(ARGV)
data/bin/op ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'gli'
4
+ require_relative '../lib/octopolo'
5
+
6
+ include GLI::App
7
+
8
+ program_desc 'GitHub workflow scripts. From the Ngineers as SportNgin.'
9
+ version Octopolo::VERSION
10
+
11
+ wrap_help_text :verbatim
12
+
13
+ program_long_desc """
14
+ DOCUMENTATION
15
+ For documentation and help in setting up your configuration files,
16
+ see Octopolo's GitHub repo: https://github.com/sportngin/octopolo
17
+ """
18
+
19
+ commands_from File.expand_path(File.dirname(__FILE__) + '/../lib/octopolo/commands')
20
+
21
+ exit run(ARGV)
data/lib/octopolo.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "readline"
2
+ require "octokit"
3
+ require "hashie"
4
+ require_relative "octopolo/cli"
5
+ require_relative "octopolo/config"
6
+ require_relative "octopolo/version"
7
+ require_relative "octopolo/convenience_wrappers"
8
+
9
+ module Octopolo
10
+
11
+ def self.config
12
+ @config ||= Octopolo::Config.parse
13
+ end
14
+
15
+ end
@@ -0,0 +1,27 @@
1
+ require 'fileutils'
2
+
3
+ module Octopolo
4
+ class Changelog
5
+ attr_reader :filename
6
+
7
+ def initialize(filename="CHANGELOG.markdown")
8
+ @filename = filename
9
+ end
10
+
11
+ def readlines
12
+ File.readlines(@filename)
13
+ end
14
+
15
+ def open
16
+ FileUtils.touch(@filename) unless File.exists?(@filename)
17
+ File.copy_stream(@filename,'old_changelog')
18
+ File.open('old_changelog', 'r') do |old_changelog|
19
+ File.open(@filename, 'w') do |changelog|
20
+ yield changelog
21
+ old_changelog.each_line { |line| changelog.puts line }
22
+ end
23
+ end
24
+ File.delete('old_changelog')
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,210 @@
1
+ require "open3"
2
+ require "highline"
3
+
4
+ module Octopolo
5
+ # Public: Class to perform cli-related tasks, like performing commands.
6
+ class CLI
7
+ # Public: Perform the given shell command.
8
+ #
9
+ # command - A String containing the command to perform.
10
+ # say_command - A Boolean determining whether to display the performed command to the screen. (default: true)
11
+ #
12
+ # Examples
13
+ #
14
+ # CLI.perform "git pull", false
15
+ # git pull
16
+ # Already up-to-date.
17
+ # # => "Already up-to-date."
18
+ #
19
+ # CLI.perform "git pull", false
20
+ # # => "Already up-to-date."
21
+ #
22
+ # Returns the output of the command as a String.
23
+ def self.perform(command, say_command = true)
24
+ # display the command
25
+ say command if say_command
26
+ # and then perform it
27
+ if Open3.respond_to?(:capture3)
28
+ output, error, status = Open3.capture3(command)
29
+ else
30
+ # Only necessary as long as we use 1.8.7, which doesn't have Open3.capture3
31
+ output = `#{command}`
32
+ end
33
+
34
+ # speak the output
35
+ say output if say_command
36
+ # return the output of the command
37
+ output
38
+ rescue => e
39
+ say "Unable to perform '#{command}': #{e.message}"
40
+ end
41
+
42
+ # Public: Perform the command, but do not print out the command
43
+ #
44
+ # command - A String containing the command to perform.
45
+ #
46
+ # Examples
47
+ #
48
+ # CLI.perform_quietly "git pull"
49
+ # Already up-to-date.
50
+ # # => "Already up-to-date."
51
+ def self.perform_quietly command
52
+ perform command, false
53
+ end
54
+
55
+ # Public: Replace the current process with the given shell command.
56
+ #
57
+ # command - A String containing the command to perform.
58
+ #
59
+ # Returns nothing and exits the current Ruby process.
60
+ def self.perform_and_exit(command)
61
+ say command
62
+ # Kernel#exec replaces the ruby process with the new bash process
63
+ # executing the command. This is useful for us mainly for things like
64
+ # calling `ssh` which will be interactive or `hub` which will open a text
65
+ # editor. Those commands don't play well with Kernel#` or Open3.capture3.
66
+ exec command
67
+ end
68
+
69
+ # Public: Display the given message.
70
+ #
71
+ # message - A String containig the message to display.
72
+ #
73
+ # Examples
74
+ #
75
+ # CLI.say "About to do something awesome"
76
+ #
77
+ # CLI.say "This may take a moment..."
78
+ #
79
+ # Returns nothing.
80
+ def self.say(message)
81
+ unless message.nil? || message.empty?
82
+ puts message
83
+ end
84
+ end
85
+
86
+ # Public: Display a blank line
87
+ def self.spacer_line
88
+ say " "
89
+ end
90
+
91
+ # Public: Perform a set of commands in the given directory.
92
+ #
93
+ # Yields nothing.
94
+ #
95
+ # dir - A String indicating the path to perform the commands in.
96
+ #
97
+ # Examples
98
+ #
99
+ # CLI.perform_in "~" do
100
+ # CLI.perform "rm -Rf"
101
+ # end
102
+ #
103
+ # CLI.perform_in "/tmp" do
104
+ # CLI.perform "ls"
105
+ # end
106
+ #
107
+ # Returns nothing.
108
+ def self.perform_in(dir)
109
+ say "Performing in #{dir}:"
110
+ Dir.chdir(dir) do
111
+ yield
112
+ end
113
+ end
114
+
115
+ def self.ask(question, choices, skip_asking = false)
116
+ return choices.first if choices.size == 1
117
+
118
+ unless skip_asking
119
+ say question
120
+ choices.each_with_index do |choice, i|
121
+ say "#{i+1}) #{choice}"
122
+ end
123
+ end
124
+
125
+ selection = nil
126
+ while not choices.include?(selection)
127
+ selection = prompt
128
+ break if choices.include?(selection)
129
+ # if entering a 1-based index value
130
+ selection_index = selection.to_i - 1
131
+ selection = choices[selection_index] if selection_index >= 0
132
+ break if choices.include?(selection)
133
+ say "Not a valid choice."
134
+ end
135
+
136
+ selection
137
+ end
138
+
139
+ # Public: Ask a yes or no question
140
+ #
141
+ # question - The question to display to the user in the prompt
142
+ #
143
+ # Returns a Boolean
144
+ def self.ask_boolean(question)
145
+ answer = prompt("#{question} (y/n)")
146
+ # basically accept anything that starts with Y as a yes answer
147
+ answer =~ /^y/i
148
+ end
149
+
150
+ def self.prompt prompt_text="> "
151
+ highline.ask prompt_text do |conf|
152
+ conf.readline = true
153
+ end.to_s
154
+ end
155
+
156
+ # Public: Prompt user for multiple lines of input
157
+ #
158
+ # prompt_text - The text to display before the prompt
159
+ #
160
+ # Example:
161
+ #
162
+ # # Accept multiple lines of text, with the prompt "QA Plan:"
163
+ # plan = CLI.prompt_multiline "QA Plan:"
164
+ #
165
+ # Returns a String containing the value the user entered
166
+ def self.prompt_multiline prompt_text
167
+ highline.ask(prompt_text) do |conf|
168
+ # accept text until the first blank line (instead of stopping at the
169
+ # first newline), to allow multiple lines of input
170
+ conf.gather = ""
171
+ conf.readline = true
172
+ end
173
+ end
174
+
175
+ # Public: Prompt user for input, but do not display what they type
176
+ #
177
+ # prompt_text - The text to display before the prompt; e.g., "Password: "
178
+ #
179
+ # Returns a String containing the value the user entered
180
+ def self.prompt_secret prompt_text
181
+ highline.ask(prompt_text) do |conf|
182
+ # do not display the text input
183
+ conf.echo = false
184
+ conf.readline = true
185
+ end
186
+ end
187
+
188
+ def self.copy_to_clipboard(input)
189
+ say "Putting '#{input}' on the clipboard."
190
+ # have to do this all roundy-abouty by passing to /bin/bash, because by default, ruby performs commands with /bin/sh, which doesn't respect the -n flag on echo
191
+ # http://stackoverflow.com/questions/5059039/ruby-execute-shell-command-echo-with-n-option
192
+ perform "/bin/bash -c 'echo -n #{input}' | pbcopy", false
193
+ end
194
+
195
+ # Public: Open the given path with Mac OS X's built-in `open` command
196
+ def self.open path
197
+ perform_and_exit "open '#{path}'"
198
+ end
199
+
200
+ # Public: Instantiate an instance of HighLine
201
+ #
202
+ # This is likely a temporary method until we replace a lot of CLI's guts
203
+ # with HighLine equivalents.
204
+ #
205
+ # Returns an instance of HighLine
206
+ def self.highline
207
+ HighLine.new
208
+ end
209
+ end
210
+ end