livebuzz-gitx 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +31 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +6 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +8 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +84 -0
  11. data/Rakefile +5 -0
  12. data/bin/git-buildtag +6 -0
  13. data/bin/git-cleanup +7 -0
  14. data/bin/git-integrate +6 -0
  15. data/bin/git-nuke +6 -0
  16. data/bin/git-release +6 -0
  17. data/bin/git-review +6 -0
  18. data/bin/git-reviewrequest +8 -0
  19. data/bin/git-share +6 -0
  20. data/bin/git-start +6 -0
  21. data/bin/git-track +6 -0
  22. data/bin/git-update +6 -0
  23. data/lib/livebuzz/gitx/cli/base_command.rb +58 -0
  24. data/lib/livebuzz/gitx/cli/buildtag_command.rb +41 -0
  25. data/lib/livebuzz/gitx/cli/cleanup_command.rb +47 -0
  26. data/lib/livebuzz/gitx/cli/integrate_command.rb +95 -0
  27. data/lib/livebuzz/gitx/cli/nuke_command.rb +64 -0
  28. data/lib/livebuzz/gitx/cli/release_command.rb +41 -0
  29. data/lib/livebuzz/gitx/cli/review_command.rb +101 -0
  30. data/lib/livebuzz/gitx/cli/share_command.rb +17 -0
  31. data/lib/livebuzz/gitx/cli/start_command.rb +39 -0
  32. data/lib/livebuzz/gitx/cli/track_command.rb +16 -0
  33. data/lib/livebuzz/gitx/cli/update_command.rb +37 -0
  34. data/lib/livebuzz/gitx/configuration.rb +47 -0
  35. data/lib/livebuzz/gitx/extensions/string.rb +12 -0
  36. data/lib/livebuzz/gitx/extensions/thor.rb +39 -0
  37. data/lib/livebuzz/gitx/github.rb +177 -0
  38. data/lib/livebuzz/gitx/version.rb +5 -0
  39. data/lib/livebuzz/gitx.rb +10 -0
  40. data/livebuzz-gitx.gemspec +37 -0
  41. data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_failure_status.yml +135 -0
  42. data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_success_status.yml +149 -0
  43. data/spec/fixtures/vcr_cassettes/pull_request_does_not_exist.yml +133 -0
  44. data/spec/livebuzz/gitx/cli/base_command_spec.rb +41 -0
  45. data/spec/livebuzz/gitx/cli/buildtag_command_spec.rb +70 -0
  46. data/spec/livebuzz/gitx/cli/cleanup_command_spec.rb +37 -0
  47. data/spec/livebuzz/gitx/cli/integrate_command_spec.rb +272 -0
  48. data/spec/livebuzz/gitx/cli/nuke_command_spec.rb +165 -0
  49. data/spec/livebuzz/gitx/cli/release_command_spec.rb +148 -0
  50. data/spec/livebuzz/gitx/cli/review_command_spec.rb +307 -0
  51. data/spec/livebuzz/gitx/cli/share_command_spec.rb +32 -0
  52. data/spec/livebuzz/gitx/cli/start_command_spec.rb +96 -0
  53. data/spec/livebuzz/gitx/cli/track_command_spec.rb +31 -0
  54. data/spec/livebuzz/gitx/cli/update_command_spec.rb +79 -0
  55. data/spec/spec_helper.rb +86 -0
  56. data/spec/support/global_config.rb +24 -0
  57. data/spec/support/home_env.rb +11 -0
  58. data/spec/support/matchers/meet_expectations_matcher.rb +7 -0
  59. data/spec/support/pry.rb +1 -0
  60. data/spec/support/stub_execution.rb +14 -0
  61. data/spec/support/timecop.rb +9 -0
  62. data/spec/support/vcr.rb +6 -0
  63. data/spec/support/webmock.rb +1 -0
  64. metadata +350 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0616b6dbae3062ab6f77cd11e45b9d093347109e
4
+ data.tar.gz: 363feec0115bce1bc18baa4998c8be06e0556bcd
5
+ SHA512:
6
+ metadata.gz: 7d08cf9017d8b2a87190849657421a81bd86b423f31e492da77381456c17fc949e10c79715334ccbb54e2dabd11ca18960794135c633bad0650e7f7001294d8f
7
+ data.tar.gz: fcbbf1f9a923aa23e21c8b1c0691116fd3b39edd77e6d4636a8d79e2efaa9c8315900b70c3689f529c419b9cb9564ab04e300986739dd37c5e24dd68298d2ecc
data/.gitignore ADDED
@@ -0,0 +1,31 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## GIT
17
+ *.orig
18
+
19
+ ## PROJECT::GENERAL
20
+ coverage
21
+ rdoc
22
+ pkg
23
+
24
+ ## bundler
25
+ Gemfile.lock
26
+
27
+ ## RSpec temp files
28
+ spec/tmp
29
+
30
+ ## RubyMine
31
+ /.idea/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ livebuzz-gitx
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.2
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - '2.1.2'
4
+ before_install:
5
+ - git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
6
+ - git fetch
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in your gemspec file
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec, cmd: 'bundle exec rspec', failed_mode: :keep do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ryan Sonnek
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # livebuzz-gitx
2
+
3
+ [![Build Status](https://travis-ci.org/livebuzz/livebuzz-gitx.png?branch=master)](https://travis-ci.org/livebuzz/livebuzz-gitx)
4
+ [![Code Coverage](https://coveralls.io/repos/livebuzz/livebuzz-gitx/badge.png)](https://coveralls.io/r/livebuzz/livebuzz-gitx)
5
+ [![Code Climate](https://codeclimate.com/github/livebuzz/livebuzz-gitx.png)](https://codeclimate.com/github/livebuzz/livebuzz-gitx)
6
+
7
+ Useful Git eXtensions for Development workflow at LiveBuzz.
8
+
9
+ Forked from [TheGarage-GitX](https://github.com/thegarage/thegarage-gitx), which is inspired by the [socialcast-git-extensions gem](https://github.com/socialcast/socialcast-git-extensions)
10
+
11
+ # Git Extensions for Workflow
12
+
13
+ ### Options
14
+ * `--trace` or `-v` = verbose output for debugging commands
15
+ * `--pretend` or `-p` = dry run commands and do not actually invoke operations
16
+
17
+ ## git start <new_branch_name (optional)>
18
+
19
+ update local repository with latest upstream changes and create a new feature branch
20
+
21
+ ## git update
22
+
23
+ update the local feature branch with latest remote changes plus upstream released changes.
24
+
25
+ ## git integrate <aggregate_branch_name (optional, default: staging)>
26
+
27
+ integrate the current feature branch into an aggregate branch (ex: prototype, staging)
28
+
29
+ ## git review
30
+
31
+ create a pull request on github for peer review of the current branch. This command is re-runnable
32
+ in order to re-assign pull requests.
33
+
34
+ options:
35
+ * `--assign` or `-a` = assign pull request to github user
36
+ * `--open` or `-o` = open pull request in default web browser.
37
+ * `--bump` or `-b` = bump an existing pull request by posting a comment to re-review new changes
38
+ * `--approve` = approve/signoff on pull request (with optional feedback)
39
+ * `--reject` = reject pull request (with details)
40
+
41
+ NOTE: the `--bump` option will also update the pull request commit status to mark the branch as 'pending peer review'.
42
+ This setting is cleared when a reviewer approves or rejects the pull request.
43
+
44
+ ## git release
45
+
46
+ release the current feature branch to master. This operation will perform the following:
47
+
48
+ * pull in latest code from remote branch
49
+ * merge in latest code from master branch
50
+ * prompt user to confirm they actually want to perform the release
51
+ * merge current branch into master
52
+ * (optional) cleanup merged branches from remote server
53
+
54
+ options:
55
+ * `--cleanup` = automatically cleanup merged branches after release complete
56
+
57
+ # Extra Utility Git Extensions
58
+
59
+ ## git cleanup
60
+
61
+ delete released branches after they have been merged into master.
62
+
63
+ ## git nuke <aggregate_branch_name>
64
+
65
+ reset an aggregate branch (ex: prototype, staging) back to a known good state.
66
+
67
+ ## git buildtag
68
+
69
+ create a build tag for the current Travis-CI build and push it back to origin
70
+
71
+
72
+ ## Note on Patches/Pull Requests
73
+
74
+ * Fork the project.
75
+ * Make your feature addition or bug fix.
76
+ * Add tests for it. This is important so I don't break it in a
77
+ future version unintentionally.
78
+ * Commit, do not mess with rakefile, version, or history.
79
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
80
+ * Send me a pull request. Bonus points for topic branches.
81
+
82
+ ## Copyright
83
+
84
+ Copyright (c) 2015 LiveBuzz Ltd. (c) 2013 The Garage, Inc. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new('spec')
5
+ task :default => :spec
data/bin/git-buildtag ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/buildtag_command'
5
+ args = ARGV.dup.unshift('buildtag')
6
+ LiveBuzz::Gitx::Cli::BuildtagCommand.start(args)
data/bin/git-cleanup ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/cleanup_command'
5
+ args = ARGV.dup.unshift('cleanup')
6
+ LiveBuzz::Gitx::Cli::CleanupCommand.start(args)
7
+
data/bin/git-integrate ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/integrate_command'
5
+ args = ARGV.dup.unshift('integrate')
6
+ LiveBuzz::Gitx::Cli::IntegrateCommand.start(args)
data/bin/git-nuke ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/nuke_command'
5
+ args = ARGV.dup.unshift('nuke')
6
+ LiveBuzz::Gitx::Cli::NukeCommand.start(args)
data/bin/git-release ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/release_command'
5
+ args = ARGV.dup.unshift('release')
6
+ LiveBuzz::Gitx::Cli::ReleaseCommand.start(args)
data/bin/git-review ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/review_command'
5
+ args = ARGV.dup.unshift('review')
6
+ LiveBuzz::Gitx::Cli::ReviewCommand.start(args)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ puts 'WARNING: git reviewrequest has been deprecated. use `git review` instead'
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
6
+ require 'livebuzz/gitx/cli/review_command'
7
+ args = ARGV.dup.unshift('review')
8
+ LiveBuzz::Gitx::Cli::ReviewCommand.start(args)
data/bin/git-share ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/share_command'
5
+ args = ARGV.dup.unshift('share')
6
+ LiveBuzz::Gitx::Cli::ShareCommand.start(args)
data/bin/git-start ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/start_command'
5
+ args = ARGV.dup.unshift('start')
6
+ LiveBuzz::Gitx::Cli::StartCommand.start(args)
data/bin/git-track ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/track_command'
5
+ args = ARGV.dup.unshift('track')
6
+ LiveBuzz::Gitx::Cli::TrackCommand.start(args)
data/bin/git-update ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'livebuzz/gitx/cli/update_command'
5
+ args = ARGV.dup.unshift('update')
6
+ LiveBuzz::Gitx::Cli::UpdateCommand.start(args)
@@ -0,0 +1,58 @@
1
+ require 'thor'
2
+ require 'pathname'
3
+ require 'rugged'
4
+ require 'livebuzz/gitx'
5
+
6
+ module LiveBuzz
7
+ module Gitx
8
+ module Cli
9
+ class BaseCommand < Thor
10
+ include Thor::Actions
11
+
12
+ class MergeError < Thor::Error; end
13
+
14
+ add_runtime_options!
15
+
16
+ method_option :trace, :type => :boolean, :aliases => '-v'
17
+ def initialize(*args)
18
+ super(*args)
19
+ end
20
+
21
+ private
22
+
23
+ def repo
24
+ @repo ||= begin
25
+ path = Dir.pwd
26
+ Rugged::Repository.discover(path)
27
+ end
28
+ end
29
+
30
+ def checkout_branch(branch_name)
31
+ run_cmd "git checkout #{branch_name}"
32
+ end
33
+
34
+ # lookup the current branch of the repo
35
+ def current_branch
36
+ repo.branches.find(&:head?)
37
+ end
38
+
39
+ def assert_aggregate_branch!(target_branch)
40
+ fail "Invalid aggregate branch: #{target_branch} must be one of supported aggregate branches #{config.aggregate_branches}" unless config.aggregate_branch?(target_branch)
41
+ end
42
+
43
+ def assert_not_protected_branch!(branch, action)
44
+ raise "Cannot #{action} reserved branch" if config.reserved_branch?(branch) || config.aggregate_branch?(branch)
45
+ end
46
+
47
+ # helper to invoke other CLI commands
48
+ def execute_command(command_class, method, args = [])
49
+ command_class.new.send(method, *args)
50
+ end
51
+
52
+ def config
53
+ @configuration ||= LiveBuzz::Gitx::Configuration.new(repo.workdir)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,41 @@
1
+ require 'thor'
2
+ require 'livebuzz/gitx'
3
+ require 'livebuzz/gitx/cli/base_command'
4
+
5
+ module LiveBuzz
6
+ module Gitx
7
+ module Cli
8
+ class BuildtagCommand < BaseCommand
9
+
10
+ desc 'buildtag', 'create a tag for the current build and push it back to origin (supports Travis CI and Codeship)'
11
+ def buildtag
12
+ fail "Unknown branch. Environment variables TRAVIS_BRANCH or CI_BRANCH are required" unless branch_name
13
+ fail "Branch must be one of the supported taggable branches: #{config.taggable_branches}" unless config.taggable_branch?(branch_name)
14
+
15
+ label = "buildtag generated by build #{build_number}"
16
+ create_build_tag(branch_name, label)
17
+ end
18
+
19
+ private
20
+
21
+ # pull the current branch name from environment variables
22
+ # supports Travis CI or Codeship variables
23
+ # see https://www.codeship.io/documentation/continuous-integration/set-environment-variables/
24
+ def branch_name
25
+ ENV['TRAVIS_BRANCH'] || ENV['CI_BRANCH']
26
+ end
27
+
28
+ def build_number
29
+ ENV['TRAVIS_BUILD_NUMBER'] || ENV['CI_BUILD_NUMBER']
30
+ end
31
+
32
+ def create_build_tag(branch, label)
33
+ timestamp = Time.now.utc.strftime '%Y-%m-%d-%H-%M-%S'
34
+ git_tag = "build-#{branch}-#{timestamp}"
35
+ run_cmd "git tag #{git_tag} -a -m '#{label}'"
36
+ run_cmd "git push origin #{git_tag}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,47 @@
1
+ require 'thor'
2
+ require 'livebuzz/gitx'
3
+ require 'livebuzz/gitx/cli/base_command'
4
+
5
+ module LiveBuzz
6
+ module Gitx
7
+ module Cli
8
+ class CleanupCommand < BaseCommand
9
+ desc 'cleanup', 'Cleanup branches that have been merged into master from the repo'
10
+ def cleanup
11
+ checkout_branch LiveBuzz::Gitx::BASE_BRANCH
12
+ run_cmd "git pull"
13
+ run_cmd 'git remote prune origin'
14
+
15
+ say "Deleting local and remote branches that have been merged into "
16
+ say LiveBuzz::Gitx::BASE_BRANCH, :green
17
+ merged_branches(remote: true).each do |branch|
18
+ run_cmd "git push origin --delete #{branch}"
19
+ end
20
+ merged_branches(remote: false).each do |branch|
21
+ run_cmd "git branch -d #{branch}"
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ # @return list of branches that have been merged
28
+ def merged_branches(options = {})
29
+ args = []
30
+ args << '-r' if options[:remote]
31
+ args << "--merged"
32
+ output = run_cmd("git branch #{args.join(' ')}").split("\n")
33
+ branches = output.map do |branch|
34
+ branch = branch.gsub(/\*/, '').strip.split(' ').first
35
+ branch = branch.split('/').last if options[:remote]
36
+ branch
37
+ end
38
+ branches.uniq!
39
+ branches -= config.reserved_branches
40
+ branches.reject! { |b| config.aggregate_branch?(b) }
41
+
42
+ branches
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,95 @@
1
+ require 'thor'
2
+ require 'livebuzz/gitx'
3
+ require 'livebuzz/gitx/cli/base_command'
4
+ require 'livebuzz/gitx/cli/update_command'
5
+ require 'livebuzz/gitx/github'
6
+
7
+ module LiveBuzz
8
+ module Gitx
9
+ module Cli
10
+ class IntegrateCommand < BaseCommand
11
+ include LiveBuzz::Gitx::Github
12
+ desc 'integrate', 'integrate the current branch into one of the aggregate development branches (default = staging)'
13
+ method_option :resume, :type => :string, :aliases => '-r', :desc => 'resume merging of feature-branch'
14
+ def integrate(integration_branch = 'staging')
15
+ assert_aggregate_branch!(integration_branch)
16
+
17
+ branch = feature_branch_name
18
+ print_message(branch, integration_branch)
19
+
20
+ begin
21
+ execute_command(UpdateCommand, :update)
22
+ rescue
23
+ fail MergeError, "Merge Conflict Occurred. Please Merge Conflict Occurred. Please fix merge conflict and rerun the integrate command"
24
+ end
25
+
26
+ integrate_branch(branch, integration_branch) unless options[:resume]
27
+ checkout_branch branch
28
+
29
+ create_integrate_comment(branch) unless config.reserved_branch?(branch)
30
+ end
31
+
32
+ private
33
+
34
+ def print_message(branch, integration_branch)
35
+ message = options[:resume] ? 'Resuming integration of' : 'Integrating'
36
+ say "#{message} "
37
+ say "#{branch} ", :green
38
+ say "into "
39
+ say integration_branch, :green
40
+ end
41
+
42
+ def integrate_branch(branch, integration_branch)
43
+ fetch_remote_branch(integration_branch)
44
+ begin
45
+ run_cmd "git merge #{branch}"
46
+ rescue
47
+ fail MergeError, "Merge Conflict Occurred. Please fix merge conflict and rerun command with --resume #{branch} flag"
48
+ end
49
+ run_cmd "git push origin HEAD"
50
+ end
51
+
52
+ def feature_branch_name
53
+ @feature_branch ||= begin
54
+ feature_branch = options[:resume] || current_branch.name
55
+ until local_branch_exists?(feature_branch)
56
+ feature_branch = ask("#{feature_branch} does not exist. Please select one of the available local branches: #{local_branches}")
57
+ end
58
+ feature_branch
59
+ end
60
+ end
61
+
62
+ # nuke local branch and pull fresh version from remote repo
63
+ def fetch_remote_branch(target_branch)
64
+ create_remote_branch(target_branch) unless remote_branch_exists?(target_branch)
65
+ run_cmd "git fetch origin"
66
+ run_cmd "git branch -D #{target_branch}", :allow_failure => true
67
+ checkout_branch target_branch
68
+ end
69
+
70
+ def local_branch_exists?(branch)
71
+ local_branches.include?(branch)
72
+ end
73
+
74
+ def local_branches
75
+ @local_branches ||= repo.branches.each_name(:local)
76
+ end
77
+
78
+ def remote_branch_exists?(target_branch)
79
+ repo.branches.each_name(:remote).include?("origin/#{target_branch}")
80
+ end
81
+
82
+ def create_remote_branch(target_branch)
83
+ repo.create_branch(target_branch, LiveBuzz::Gitx::BASE_BRANCH)
84
+ run_cmd "git push origin #{target_branch}:#{target_branch}"
85
+ end
86
+
87
+ def create_integrate_comment(branch)
88
+ pull_request = find_or_create_pull_request(branch)
89
+ comment = '[gitx] integrated into staging :twisted_rightwards_arrows:'
90
+ github_client.add_comment(github_slug, pull_request.number, comment)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,64 @@
1
+ require 'thor'
2
+ require 'livebuzz/gitx'
3
+ require 'livebuzz/gitx/cli/base_command'
4
+
5
+ module LiveBuzz
6
+ module Gitx
7
+ module Cli
8
+ class NukeCommand < BaseCommand
9
+ desc 'nuke', 'nuke the specified aggregate branch and reset it to a known good state'
10
+ method_option :destination, :type => :string, :aliases => '-d', :desc => 'destination branch to reset to'
11
+ def nuke(bad_branch)
12
+ good_branch = options[:destination] || ask("What branch do you want to reset #{bad_branch} to? (default: #{bad_branch})")
13
+ good_branch = bad_branch if good_branch.length == 0
14
+
15
+ last_known_good_tag = current_build_tag(good_branch)
16
+ return unless yes?("Reset #{bad_branch} to #{last_known_good_tag}? (y/n)", :green)
17
+ assert_aggregate_branch!(bad_branch)
18
+ return if migrations_need_to_be_reverted?(bad_branch, last_known_good_tag)
19
+
20
+ say "Resetting "
21
+ say "#{bad_branch} ", :green
22
+ say "branch to "
23
+ say last_known_good_tag, :green
24
+
25
+ checkout_branch LiveBuzz::Gitx::BASE_BRANCH
26
+ run_cmd "git branch -D #{bad_branch}", :allow_failure => true
27
+ run_cmd "git push origin --delete #{bad_branch}", :allow_failure => true
28
+ run_cmd "git checkout -b #{bad_branch} #{last_known_good_tag}"
29
+ run_cmd "git push origin #{bad_branch}"
30
+ run_cmd "git branch --set-upstream-to origin/#{bad_branch}"
31
+ checkout_branch LiveBuzz::Gitx::BASE_BRANCH
32
+ end
33
+
34
+ private
35
+
36
+ def migrations_need_to_be_reverted?(bad_branch, last_known_good_tag)
37
+ return false unless File.exist?('db/migrate')
38
+ outdated_migrations = run_cmd("git diff #{last_known_good_tag}...#{bad_branch} --name-only db/migrate").split
39
+ return false if outdated_migrations.empty?
40
+
41
+ say "#{bad_branch} contains migrations that may need to be reverted. Ensure any reversable migrations are reverted on affected databases before nuking.", :red
42
+ say 'Example commands to revert outdated migrations:'
43
+ outdated_migrations.reverse.each do |migration|
44
+ version = File.basename(migration).split('_').first
45
+ say "rake db:migrate:down VERSION=#{version}"
46
+ end
47
+ !yes?("Are you sure you want to nuke #{bad_branch}? (y/n) ", :green)
48
+ end
49
+
50
+ def current_build_tag(branch)
51
+ last_build_tag = build_tags_for_branch(branch).last
52
+ raise "No known good tag found for branch: #{branch}. Verify tag exists via `git tag -l 'build-#{branch}-*'`" unless last_build_tag
53
+ last_build_tag
54
+ end
55
+
56
+ def build_tags_for_branch(branch)
57
+ run_cmd "git fetch --tags"
58
+ build_tags = run_cmd("git tag -l 'build-#{branch}-*'").split
59
+ build_tags.sort
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,41 @@
1
+ require 'thor'
2
+ require 'livebuzz/gitx'
3
+ require 'livebuzz/gitx/cli/base_command'
4
+ require 'livebuzz/gitx/cli/update_command'
5
+ require 'livebuzz/gitx/cli/integrate_command'
6
+ require 'livebuzz/gitx/cli/cleanup_command'
7
+ require 'livebuzz/gitx/github'
8
+
9
+ module LiveBuzz
10
+ module Gitx
11
+ module Cli
12
+ class ReleaseCommand < BaseCommand
13
+ include LiveBuzz::Gitx::Github
14
+
15
+ desc 'release', 'release the current branch to production'
16
+ method_option :cleanup, :type => :boolean, :desc => 'cleanup merged branches after release'
17
+ def release
18
+ return unless yes?("Release #{current_branch.name} to production? (y/n)", :green)
19
+
20
+ branch = current_branch.name
21
+ assert_not_protected_branch!(branch, 'release')
22
+ execute_command(UpdateCommand, :update)
23
+
24
+ find_or_create_pull_request(branch)
25
+ status = branch_status(branch)
26
+ if status != 'success'
27
+ return unless yes?("Branch status is currently: #{status}. Proceed with release? (y/n)", :red)
28
+ end
29
+
30
+ checkout_branch LiveBuzz::Gitx::BASE_BRANCH
31
+ run_cmd "git pull origin #{LiveBuzz::Gitx::BASE_BRANCH}"
32
+ run_cmd "git merge --no-ff #{branch}"
33
+ run_cmd "git push origin HEAD"
34
+
35
+ execute_command(IntegrateCommand, :integrate, 'staging')
36
+ execute_command(CleanupCommand, :cleanup) if options[:cleanup]
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end