abtion-scripts 1.1.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.
@@ -0,0 +1,195 @@
1
+ require 'json'
2
+ require 'tempfile'
3
+
4
+ module AbtionScripts
5
+ class Pivotal
6
+ attr_reader :token
7
+
8
+ def initialize(token)
9
+ @token = token
10
+ end
11
+
12
+ def request(url, method: "GET", body: nil)
13
+ cmd = %Q{curl --silent -X #{method} -H "X-TrackerToken: #{token}"}
14
+ cmd += %Q{ -H "Content-Type: application/json"}
15
+
16
+ if body
17
+ cmd += %Q{ -d '#{body}'}
18
+ end
19
+
20
+ cmd += %Q{ "#{url}"}
21
+
22
+ result = `#{cmd}`
23
+
24
+ JSON.parse(result.strip)
25
+ end
26
+ end
27
+
28
+ class Begin < Base
29
+ def self.description
30
+ "Starts your Pivotal Tracker story and opens a new PR on Github"
31
+ end
32
+
33
+ def self.help
34
+ <<-EOF
35
+ abtion begin #{colorize(:light_blue, "[story id] [branch name]")}
36
+
37
+ Example: $ #{colorize(:light_blue, 'abtion begin "#133717234"')}
38
+
39
+ This command will start your Pivotal Tracker story for you, open a pull
40
+ request on Github, and copy over the Pivotal Tracker story description to
41
+ the Github pull request description. As well, any tasks in your
42
+ Pivotal Tracker story will automatically become [x] checkbox tasks on the
43
+ Github PR.
44
+
45
+ * All branch names will be auto-converted to
46
+ kebab-case, lowercase
47
+
48
+ * Passing story id/branch name as arguments are optional - if
49
+ they are missing, you'll be prompted
50
+ EOF
51
+ end
52
+
53
+ def run
54
+ story_id, branch_name = argv
55
+
56
+ if !command?("hub")
57
+ abort <<-EOF
58
+ You need to install `hub` before you can use this program.
59
+
60
+ brew install hub
61
+ EOF
62
+ end
63
+
64
+ pivotal = Pivotal.new(git_config("user.pivotalApiToken"))
65
+ config_abort_if_blank!("user.pivotalApiToken", pivotal.token)
66
+
67
+ project_id = git_config("user.pivotalProjectId")
68
+ config_abort_if_blank!("user.pivotalProjectId", project_id)
69
+
70
+ story_id ||= prompt("Please paste the Pivotal story ID here")
71
+ story_id = story_id.gsub(/[^\d]/, '')
72
+
73
+ story_url = "https://www.pivotaltracker.com/services/v5"\
74
+ "/projects/#{project_id}/stories/#{story_id}"
75
+
76
+ story = pivotal.request(story_url)
77
+
78
+ default_branch_name = normalized_branch_name(story['name'])
79
+
80
+ branch_name ||= prompt("Please enter a branch name (#{default_branch_name})")
81
+
82
+ if branch_name.strip == ""
83
+ branch_name = default_branch_name
84
+ else
85
+ branch_name = normalized_branch_name(branch_name)
86
+ end
87
+
88
+ # Start the story
89
+ pivotal.request(story_url, method: "PUT", body: '{ "current_state":"started" }')
90
+
91
+ silent "git checkout master",
92
+ "git fetch origin",
93
+ "git reset --hard origin/master"
94
+
95
+ puts "==> Checking out #{branch_name}"
96
+
97
+ silent "git checkout -b #{branch_name}",
98
+ 'git commit --allow-empty -m "Initial commit for story #' + story_id + '"',
99
+ "git push origin #{branch_name}",
100
+ "git branch --set-upstream #{branch_name} origin/#{branch_name}"
101
+
102
+ tasks = pivotal.request(
103
+ "https://www.pivotaltracker.com/services/v5"\
104
+ "/projects/#{project_id}/stories/#{story_id}/tasks"
105
+ )
106
+
107
+ story_description = story['description']
108
+
109
+ if story_description.nil?
110
+ story_description = "_No story description given in Pivotal_"
111
+ end
112
+
113
+ description = <<-EOF
114
+ #{story['name']}
115
+
116
+ #{story['url']}
117
+
118
+ # Description
119
+
120
+ #{story_description}
121
+ EOF
122
+
123
+ description.strip!
124
+
125
+ unless tasks.empty?
126
+ description += "\n\n## TODO\n\n"
127
+
128
+ tasks.each do |task|
129
+ description += "- [ ] #{task['description']}\n"
130
+ end
131
+
132
+ description.strip!
133
+ end
134
+
135
+ puts "==> Opening pull request on GitHub"
136
+
137
+ tempfile = Tempfile.new('begin_pull_request')
138
+
139
+ begin
140
+ tempfile.write(description)
141
+ tempfile.close
142
+
143
+ labels = ['WIP', story['story_type']].join(',')
144
+
145
+ url = `hub pull-request -F #{tempfile.path} -l "#{labels}" -a "" -o`
146
+
147
+ # Copy it to your clipboard
148
+ system("echo #{url} | pbcopy")
149
+ puts url
150
+ ensure
151
+ tempfile.unlink
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def silent(*cmds)
158
+ cmds.each { |cmd| system("#{cmd} >/dev/null 2>&1") }
159
+ end
160
+
161
+ def command?(name)
162
+ `which #{name}`
163
+ $?.success?
164
+ end
165
+
166
+ def git_config(key)
167
+ `git config --local --get #{key}`.strip
168
+ end
169
+
170
+ def config_abort_if_blank!(key, value)
171
+ if value.strip == ""
172
+ abort <<-EOF
173
+ You need to set the #{key} value in your git config!
174
+
175
+ git config --local --add #{key} [value]
176
+ EOF
177
+ end
178
+ end
179
+
180
+ def prompt(msg)
181
+ print "#{msg} > "
182
+ value = STDIN.gets.strip
183
+ puts
184
+ value
185
+ end
186
+
187
+ def normalized_branch_name(branch_name)
188
+ branch_name
189
+ .gsub(/[^\w\s-]/, '')
190
+ .gsub(/\s+/, '-')
191
+ .downcase
192
+ .gsub(/-*$/, '') # trailing dashes
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,39 @@
1
+ module AbtionScripts::Colorize
2
+ extend self
3
+
4
+ def self.included(base)
5
+ colorize = self
6
+
7
+ base.class_eval do
8
+ extend colorize
9
+ end
10
+ end
11
+
12
+ COLOR_CODES = {
13
+ black: 30,
14
+ blue: 34,
15
+ brown: 33,
16
+ cyan: 36,
17
+ dark_gray: 90,
18
+ green: 32,
19
+ light_blue: 94,
20
+ light_cyan: 96,
21
+ light_gray: 37,
22
+ light_green: 92,
23
+ light_purple: 95,
24
+ light_red: 91,
25
+ light_yellow: 93,
26
+ purple: 35,
27
+ red: 31,
28
+ white: 97,
29
+ yellow: 33,
30
+
31
+ command: 96,
32
+ error: 91,
33
+ info: 93,
34
+ }
35
+
36
+ def colorize(color, string)
37
+ "\e[#{COLOR_CODES[color]}m#{string}\e[0m"
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ class Diff < AbtionScripts::Base
2
+ def self.name
3
+ "diff"
4
+ end
5
+
6
+ DAY = 86400
7
+
8
+ def run
9
+ yesterday = (Time.now - DAY).strftime("%Y-%m-%d")
10
+
11
+ last_sha = `git log --after "#{yesterday} 00:00" --before "#{yesterday} 23:59" --format="format:%H"`
12
+ .strip
13
+ .split("\n")
14
+ .last
15
+
16
+ start_sha = `git log "#{last_sha}^" --format="format:%H"`
17
+ .strip
18
+ .split("\n")
19
+ .first
20
+
21
+ repo = `git remote -v | grep github | awk '{ print $2 }' | head -n 1`.strip
22
+ repo.gsub!(/\.git/, "")
23
+ repo.gsub!(/git@github.com:/, "")
24
+
25
+ github_repo = repo.split("/")[-2..-1].join("/")
26
+
27
+ system("open https://github.com/#{github_repo}/compare/#{start_sha}...master")
28
+ end
29
+ end
@@ -0,0 +1,200 @@
1
+ class AbtionScripts::Doctor < AbtionScripts::Base
2
+ class Check
3
+ include AbtionScripts::Colorize
4
+
5
+ attr_reader :name, :command, :remedy, :problems
6
+
7
+ def initialize(name:, command:, remedy:)
8
+ @name = name
9
+ @command = command
10
+ @remedy = remedy
11
+ @problems = []
12
+ end
13
+
14
+ def run!
15
+ print "Checking: #{name}... "
16
+
17
+ success = if command.respond_to?(:call)
18
+ command.call
19
+ else
20
+ system "#{command} > /dev/null 2>&1"
21
+ end
22
+
23
+ if success
24
+ puts 'OK'
25
+ else
26
+ print colorize(:error, 'F')
27
+ fix = remedy.respond_to?(:join) ? remedy.join(" ") : remedy
28
+ puts "\n To fix: #{fix}\n\n"
29
+
30
+ problems << name
31
+ end
32
+ end
33
+ end
34
+
35
+ def self.description
36
+ "Checks the health of your development environment"
37
+ end
38
+
39
+ def initialize(*args)
40
+ super
41
+ @checks = []
42
+ end
43
+
44
+ def run
45
+ case argv.first
46
+ when "list"
47
+ list_default_checks
48
+ else
49
+ run_doctor
50
+ end
51
+ end
52
+
53
+ def self.help
54
+ "doctor - helps you diagnose any setup issues with this application\n"
55
+ end
56
+
57
+ def self.help_subcommands
58
+ {
59
+ "abtion doctor" => "runs health checks and gives a report",
60
+ "abtion doctor list" => "prints a list of default checks you can use when overriding doctor checks in your app"
61
+ }
62
+ end
63
+
64
+ def run_doctor
65
+ run_checks
66
+ report
67
+ end
68
+
69
+ def list_default_checks
70
+ puts "These default checks are available for use in your overrides:"
71
+ puts
72
+
73
+ default_checks.each do |name|
74
+ puts " - #{colorize(:light_blue, name)}"
75
+ end
76
+
77
+ puts
78
+ end
79
+
80
+ def check(**options)
81
+ check = Check.new(options)
82
+ @checks << check
83
+
84
+ check.run!
85
+ end
86
+
87
+ private
88
+
89
+ def run_checks
90
+ run_default_checks
91
+ end
92
+
93
+ def run_default_checks
94
+ default_checks.each do |check|
95
+ send(check)
96
+ end
97
+ end
98
+
99
+ def default_checks
100
+ %i[
101
+ check_envrc_file_exists
102
+ check_env_file_exists
103
+ check_direnv_installed
104
+ check_gemfile_dependencies
105
+ check_postgres_launchctl
106
+ check_postgres_running
107
+ check_postgres_role
108
+ check_db_exists
109
+ check_db_migrated
110
+ check_phantomjs_installed
111
+ ]
112
+ end
113
+
114
+ def check_postgres_launchctl
115
+ check \
116
+ name: "postgres launchctl script is linked",
117
+ command: "ls -1 ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist",
118
+ remedy: command("ln -sfv /usr/local/opt/postgresql/*.plist ~/Library/LaunchAgents")
119
+ end
120
+
121
+ def check_postgres_running
122
+ check \
123
+ name: "postgres is running",
124
+ command: "psql -l",
125
+ remedy: command("launchctl load ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist")
126
+ end
127
+
128
+ def check_postgres_role
129
+ check \
130
+ name: "postgres role exists",
131
+ command: "psql -U postgres -l",
132
+ remedy: command("createuser --superuser postgres")
133
+ end
134
+
135
+ def check_gemfile_dependencies
136
+ check \
137
+ name: "Gemfile dependencies are up to date",
138
+ command: "bundle check",
139
+ remedy: command("bundle")
140
+ end
141
+
142
+ def check_db_exists
143
+ check \
144
+ name: "Development database exists",
145
+ command: "source .envrc && rails runner -e development 'ActiveRecord::Base.connection'",
146
+ remedy: command("rake db:setup")
147
+
148
+ check \
149
+ name: "Test database exists",
150
+ command: "source .envrc && rails runner -e test 'ActiveRecord::Base.connection'",
151
+ remedy: command("rake db:setup")
152
+ end
153
+
154
+ def check_db_migrated
155
+ check \
156
+ name: "DB is migrated",
157
+ command: "source .envrc && rails runner 'ActiveRecord::Migration.check_pending!'",
158
+ remedy: command("rake db:migrate db:test:prepare")
159
+ end
160
+
161
+ def check_direnv_installed
162
+ check \
163
+ name: "direnv installed",
164
+ command: "which direnv",
165
+ remedy: command("brew install direnv")
166
+ end
167
+
168
+ def check_phantomjs_installed
169
+ check \
170
+ name: "PhantomJS installed",
171
+ command: "which phantomjs",
172
+ remedy: command("brew install phantomjs")
173
+ end
174
+
175
+ def check_envrc_file_exists
176
+ check \
177
+ name: ".envrc file exists",
178
+ command: "stat .envrc",
179
+ remedy: command("cp .envrc.sample .envrc")
180
+ end
181
+
182
+ def check_env_file_exists
183
+ check \
184
+ name: ".env file exists (for 'heroku local')",
185
+ command: "stat .env",
186
+ remedy: command("ln -s .envrc .env")
187
+ end
188
+
189
+ def problems
190
+ @checks.map(&:problems).flatten
191
+ end
192
+
193
+ def report
194
+ exit problems.size
195
+ end
196
+
197
+ def command(s)
198
+ "run #{colorize :command, s}"
199
+ end
200
+ end