octopolo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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