autowow 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 10f7a14f0576ced3352895a30cc324a4bc230ab8
4
+ data.tar.gz: 2965fb8eac0567e25d45ace1225deb8e4920f90b
5
+ SHA512:
6
+ metadata.gz: 2f6f8ca60672d34f473877980e17f8b503462ef27a2ff1860a31703f1bff69cd12ef2fa6ede088b59b54c78687c4815eff43835c11c14e8c8b83dbd41de5c092
7
+ data.tar.gz: 5b900454d685e75c10299da0836945ac5dd67a47a54db11e784df2c85808b16d41f752d1cee794ba25315043383c8239408dbf066d065a72ca108c96468c5c89
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
13
+
14
+ .idea
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.15.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in autowow.gemspec
6
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,21 @@
1
+ guard 'bundler' do
2
+ watch('Gemfile')
3
+ watch(%r{^(.+)\.gemspec$})
4
+ end
5
+
6
+ guard 'rspec', cmd: "bundle exec spring rspec #{ENV['FOCUS']}", all_after_pass: ENV['FOCUS'].nil? do
7
+ watch(%r{^spec/.+_spec\.rb$})
8
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
9
+ watch('spec/spec_helper.rb') { 'spec' }
10
+
11
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
12
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
13
+ watch(%r{^app/models/(.+)\.rb$}) { |m| "spec/builders/#{m[1]}_builder_spec.rb" }
14
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
15
+ watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
16
+ watch(%r{^spec/factories/(.+)\.rb$}) { 'spec' }
17
+ watch('config/routes.rb') { 'spec/routing' }
18
+ watch('app/controllers/application_controller.rb') { 'spec/controllers' }
19
+
20
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
21
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Csaba Apagyi
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.
data/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # Autowow
2
+
3
+ #### Set of commands to AUTOmate Way Of Working
4
+
5
+ | Branch | Status |
6
+ | ------ | ------ |
7
+ | Release | [![Build Status](https://travis-ci.org/thisismydesign/autowow.svg?branch=release)](https://travis-ci.org/thisismydesign/autowow) [![Coverage Status](https://coveralls.io/repos/github/thisismydesign/autowow/badge.svg?branch=release)](https://coveralls.io/github/thisismydesign/autowow?branch=release) [![Gem Version](https://badge.fury.io/rb/autowow.svg)](https://badge.fury.io/rb/autowow) [![Total Downloads](http://ruby-gem-downloads-badge.herokuapp.com/autowow?type=total)](https://rubygems.org/gems/autowow) |
8
+ | Development | [![Build Status](https://travis-ci.org/thisismydesign/autowow.svg?branch=master)](https://travis-ci.org/thisismydesign/autowow) [![Coverage Status](https://coveralls.io/repos/github/thisismydesign/autowow/badge.svg?branch=master)](https://coveralls.io/github/thisismydesign/autowow?branch=master) |
9
+
10
+ ## Usage
11
+
12
+ Install from source as `rake install`.
13
+
14
+ Run `autowow` or `aw` to see available commands.
15
+
16
+ *Disclaimer: Use it on your own responsibility. Some commands may be tailored to my use case but as always pull requests and issues are welcomed.*
17
+
18
+ ## Commands
19
+
20
+ Commands in general
21
+ * start by outputting the status before execution
22
+ * end by outputting the status after execution
23
+ * are safe
24
+ * only touch files via other commands (e.g. `git`)
25
+ * do not cause conflicted state
26
+ * hard check for prerequisites
27
+ * store and restore uncommitted changes
28
+ * output executed commands that cause any change
29
+ * execute in current directory
30
+
31
+ ### VCS
32
+
33
+ Commands related to version control systems.
34
+ Currently only Git and the GitHub API are supported.
35
+
36
+ #### Branch merged
37
+
38
+ * Switches to master and pulls your merged changes
39
+ * Removes local working branch
40
+
41
+ Prerequisites: not on master
42
+
43
+ #### Update projects
44
+
45
+ * Updates local repositories
46
+ * Updates remote forks
47
+ * Searches for repositories on paths: `.`, `./*/`
48
+
49
+ Prerequisites: no uncommitted changes on master
50
+
51
+ #### Clear branches
52
+
53
+ * Removes branches without not pushed changes
54
+ * Keeps current and master branches
55
+
56
+ #### Add upstream
57
+
58
+ * Adds parent repository as remote 'upstream'
59
+
60
+ Prerequisites: doesn't have remote called 'upstream'
61
+
62
+ #### Hi
63
+
64
+ Day starter routine
65
+
66
+ * Updates projects (runs 'Update projects')
67
+ * Shows latest and deprecated repos
68
+
69
+ #### Hi!
70
+
71
+ Day starter routine for a new start
72
+
73
+ * Runs 'Add upstream' and 'Clear branches' for all projects
74
+ * Runs 'Hi'
75
+
76
+ #### Open
77
+
78
+ * Opens project in browser
79
+
80
+ ### Gem
81
+
82
+ #### Gem release
83
+
84
+ * Rebases `release` branch to master
85
+ * Releases gem via `rake release`
86
+
87
+ Prerequisites: on master
88
+
89
+ ## Feedback
90
+
91
+ Any feedback is much appreciated.
92
+
93
+ I can only tailor this project to fit use-cases I know about - which are usually my own ones. If you find that this might be the right direction to solve your problem too but you find that it's suboptimal or lacks features don't hesitate to contact me.
94
+
95
+ Please let me know if you make use of this project so that I can prioritize further efforts.
96
+
97
+ ## Conventions
98
+
99
+ This gem is developed using the following conventions:
100
+ - [Bundler's guide for developing a gem](http://bundler.io/v1.14/guides/creating_gem.html)
101
+ - [Better Specs](http://www.betterspecs.org/)
102
+ - [Semantic versioning](http://semver.org/)
103
+ - [RubyGems' guide on gem naming](http://guides.rubygems.org/name-your-gem/)
104
+ - [RFC memo about key words used to Indicate Requirement Levels](https://tools.ietf.org/html/rfc2119)
105
+ - [Bundler improvements](https://github.com/thisismydesign/bundler-improvements)
106
+ - [Minimal dependencies](http://www.mikeperham.com/2016/02/09/kill-your-dependencies/)
107
+
108
+ ## Contributing
109
+
110
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/your_gem.
111
+
112
+ ## License
113
+
114
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+
7
+ desc "Check if source can be required locally"
8
+ task :require do
9
+ sh "ruby -e \"require '#{File.dirname __FILE__}/lib/autowow'\""
10
+ end
11
+
12
+ task :default => [:require, :spec]
data/autowow.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "autowow/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "autowow"
8
+ spec.version = Autowow::VERSION
9
+ spec.authors = ["thisismydesign"]
10
+ spec.email = ["thisismydesign@users.noreply.github.com"]
11
+
12
+ spec.summary = %q{Set of commands to AUTOmate Way Of Working}
13
+ spec.homepage = "https://github.com/thisismydesign/autowow"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "bin"
20
+ spec.executables << 'autowow'
21
+ spec.executables << 'aw'
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_dependency "easy_logging", ">= 0.3.0"
25
+ spec.add_dependency "thor"
26
+ spec.add_dependency "colorize"
27
+ # Fixed version because we refine it :(
28
+ spec.add_dependency "time_difference", "= 0.5.0"
29
+ spec.add_dependency "launchy"
30
+
31
+ spec.add_development_dependency "bundler", "~> 1.15"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "rspec", "~> 3.0"
34
+ spec.add_development_dependency "guard"
35
+ spec.add_development_dependency "guard-bundler"
36
+ spec.add_development_dependency "guard-rspec"
37
+ spec.add_development_dependency "coveralls"
38
+ end
data/bin/autowow ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'autowow'
4
+
5
+ Autowow::CLI.start
data/bin/aw ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'autowow'
4
+
5
+ Autowow::CLI.start
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "autowow"
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(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/autowow.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'easy_logging'
2
+
3
+ require_relative 'autowow/log_formatter'
4
+
5
+ EasyLogging.level = Logger::DEBUG
6
+ EasyLogging.formatter = proc do |severity, datetime, progname, msg|
7
+ Autowow::LogFormatter.beautify(severity, msg)
8
+ end
9
+
10
+ require_relative 'autowow/cli'
11
+
12
+ module Autowow; end
@@ -0,0 +1,55 @@
1
+ require 'thor'
2
+
3
+ require_relative 'vcs'
4
+ require_relative 'gem'
5
+
6
+ module Autowow
7
+ class CLI < Thor
8
+
9
+ map %w[bm] => :branch_merged
10
+ map %w[grls] => :gem_release
11
+ map %w[up] => :update_projects
12
+ map %w[cb] => :clear_branches
13
+ map %w[au] => :add_upstream
14
+
15
+ desc "branch_merged", "clean working branch and return to master"
16
+ def branch_merged
17
+ Autowow::Vcs.branch_merged
18
+ end
19
+
20
+ desc "gem_release", "release gem and return to master"
21
+ def gem_release
22
+ Autowow::Gem.gem_release
23
+ end
24
+
25
+ desc "update_projects", "updates idle projects"
26
+ def update_projects
27
+ Autowow::Vcs.update_projects
28
+ end
29
+
30
+ desc "clear_branches", "removes unused branches"
31
+ def clear_branches
32
+ Autowow::Vcs.clear_branches
33
+ end
34
+
35
+ desc "add_upstream", "adds upstream branch if available"
36
+ def add_upstream
37
+ Autowow::Vcs.add_upstream
38
+ end
39
+
40
+ desc "hi", "day starter routine"
41
+ def hi
42
+ Autowow::Vcs.hi
43
+ end
44
+
45
+ desc "hi!", "day starter routine for a new start"
46
+ def hi!
47
+ Autowow::Vcs.hi!
48
+ end
49
+
50
+ desc "open", "opens project URL in browser"
51
+ def open
52
+ Autowow::Vcs.open
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,60 @@
1
+ require 'open3'
2
+
3
+ module Autowow
4
+ class Command
5
+
6
+ include EasyLogging
7
+
8
+ def self.run(*args)
9
+ Command.new(*args).explain.chronic_execute
10
+ end
11
+
12
+ def self.run_dry(*args)
13
+ Command.new(*args).execute
14
+ end
15
+
16
+ def self.popen3_reader(*args)
17
+ args.each do |arg|
18
+ reader = <<-EOF
19
+ def #{arg}
20
+ @#{arg} = @#{arg}.read.rstrip unless @#{arg}.is_a?(String)
21
+ return @#{arg}
22
+ end
23
+ EOF
24
+ class_eval(reader)
25
+ end
26
+ end
27
+
28
+ popen3_reader :stdin, :stdout, :stderr
29
+ attr_reader :wait_thr
30
+
31
+ def initialize(*args)
32
+ @cmd = args
33
+ end
34
+
35
+ def explain
36
+ logger.debug(@cmd.join(' ')) unless @cmd.empty?
37
+ self
38
+ end
39
+
40
+ def execute
41
+ @stdin, @stdout, @stderr, @wait_thr = Open3.popen3(*@cmd)
42
+ self
43
+ end
44
+
45
+ def chronic_execute
46
+ @stdin, @stdout, @stderr, @wait_thr = Open3.popen3(*@cmd)
47
+ logger.error(stderr) unless stderr.empty?
48
+ self
49
+ end
50
+
51
+ def output_matches?(matcher)
52
+ stdout.match(matcher)
53
+ end
54
+
55
+ def output_does_not_match?(matcher)
56
+ !output_matches?(matcher)
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ module StringDecorator
2
+ class ::String
3
+ def reverse_chomp(str)
4
+ reverse.chomp(str.reverse).reverse
5
+ end
6
+ end
7
+ end
data/lib/autowow/fs.rb ADDED
@@ -0,0 +1,41 @@
1
+ require_relative 'time_difference'
2
+
3
+ module Autowow
4
+ class Fs
5
+ using RefinedTimeDifference
6
+
7
+ def self.ls_dirs
8
+ Dir.glob(File.expand_path('./*/'))
9
+ end
10
+
11
+ def self.latest(files)
12
+ files.sort_by{ |f| File.mtime(f) }.reverse!.first
13
+ end
14
+
15
+ def self.older_than(files, quantity, unit)
16
+ files.select do |dir|
17
+ TimeDifference.between(File.mtime(dir), Time.now).public_send("in_#{unit}") > quantity
18
+ end
19
+ end
20
+
21
+ def self.for_dirs(dirs)
22
+ dirs.each do |working_dir|
23
+ # TODO: add handling of directories via extra param to popen3
24
+ # https://stackoverflow.com/a/10148084/2771889
25
+ Dir.chdir(working_dir) do
26
+ yield working_dir
27
+ end
28
+ end
29
+ end
30
+
31
+ def self.in_place_or_subdirs(in_place)
32
+ if in_place
33
+ yield
34
+ else
35
+ for_dirs(ls_dirs) do
36
+ yield
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'vcs'
2
+ require_relative 'command'
3
+
4
+ module Autowow
5
+ class Gem
6
+ include EasyLogging
7
+
8
+ def self.gem_release
9
+ start_status = Vcs.status
10
+ logger.info(start_status)
11
+ working_branch = Vcs.current_branch
12
+ logger.error("Not on master.") and return unless working_branch.eql?('master')
13
+ Vcs.push
14
+
15
+ Vcs.on_branch('release') do
16
+ Vcs.pull
17
+ Vcs.rebase(working_branch)
18
+ release
19
+ end
20
+
21
+ logger.info(Vcs.status)
22
+ end
23
+
24
+ def self.release
25
+ Command.run('rake', 'release')
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ require 'colorize'
2
+
3
+ module Autowow
4
+ class LogFormatter
5
+ def self.beautify(severity, msg)
6
+ log_msg = msg.end_with?($/) ? msg : "#{msg}#{$/}"
7
+ log_msg = " $ #{log_msg}" if severity.eql?('DEBUG')
8
+ color(severity, log_msg)
9
+ end
10
+
11
+ def self.color(severity, msg)
12
+ case severity
13
+ when 'DEBUG'
14
+ msg.yellow
15
+ when 'WARN'
16
+ msg.light_red
17
+ when 'ERROR'
18
+ msg.red
19
+ else
20
+ msg
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ require 'time_difference'
2
+
3
+ module RefinedTimeDifference
4
+ refine(TimeDifference) do
5
+ def humanize_higher_than(limit)
6
+ limit_index = TimeDifference::TIME_COMPONENTS.index(limit)
7
+
8
+ diff_parts = []
9
+ in_general.each_with_index do |array, index|
10
+ part, quantity = array
11
+ next if quantity <= 0 or limit_index < index
12
+ part = part.to_s.humanize
13
+
14
+ if quantity <= 1
15
+ part = part.singularize
16
+ end
17
+
18
+ diff_parts << "#{quantity} #{part}"
19
+ end
20
+
21
+ last_part = (diff_parts.pop or '')
22
+ if diff_parts.empty?
23
+ return last_part
24
+ else
25
+ return [diff_parts.join(', '), last_part].join(' and ')
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,281 @@
1
+ require 'uri'
2
+ require 'net/https'
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'launchy'
6
+
7
+ require_relative 'command'
8
+ require_relative 'decorators/string_decorator'
9
+ require_relative 'fs'
10
+ require_relative 'time_difference'
11
+
12
+ module Autowow
13
+ class Vcs
14
+ include EasyLogging
15
+ include StringDecorator
16
+
17
+ using RefinedTimeDifference
18
+
19
+ def self.branch_merged
20
+ start_status = status
21
+ logger.info(start_status)
22
+ working_branch = current_branch
23
+ logger.error("Nothing to do.") and return if working_branch.eql?('master')
24
+
25
+ keep_changes do
26
+ checkout('master')
27
+ pull
28
+ end
29
+ branch_force_delete(working_branch)
30
+
31
+ logger.info(status)
32
+ end
33
+
34
+ def self.update_projects
35
+ Fs.in_place_or_subdirs(is_git?(status_dry)) do
36
+ update_project
37
+ end
38
+ end
39
+
40
+ def self.update_project
41
+ start_status = status_dry
42
+ return unless is_git?(start_status)
43
+ logger.info("Updating #{File.expand_path('.')} ...")
44
+ logger.warn("Skipped: uncommitted changes on master.") and return if uncommitted_changes?(start_status) and current_branch.eql?('master')
45
+
46
+ on_branch('master') do
47
+ has_upstream?(remotes.stdout) ? pull_upstream : pull
48
+ end
49
+ end
50
+
51
+ def self.clear_branches
52
+ logger.info(branch.stdout)
53
+ working_branch = current_branch
54
+ master_branch = 'master'
55
+
56
+ (branches - [master_branch, working_branch]).each do |branch|
57
+ branch_force_delete(branch) if branch_pushed(branch)
58
+ end
59
+
60
+ logger.info(branch.stdout)
61
+ end
62
+
63
+ def self.add_upstream
64
+ start_status = status_dry
65
+ logger.error("Not a git repository.") and return unless is_git?(start_status)
66
+ remote_list = remotes.stdout
67
+ logger.warn("Already has upstream.") and return if has_upstream?(remote_list)
68
+ logger.info(remote_list)
69
+
70
+ url = URI.parse(origin_push_url(remote_list))
71
+ host = "api.#{url.host}"
72
+ path = "/repos#{url.path}"
73
+ request = Net::HTTP.new(host, url.port)
74
+ request.verify_mode = OpenSSL::SSL::VERIFY_NONE
75
+ request.use_ssl = url.scheme == 'https'
76
+ response = request.get(path)
77
+ logger.error("Github API (#{url.scheme}://#{host}#{path}) could not be reached: #{response.body}") and return unless response.kind_of?(Net::HTTPSuccess)
78
+ parsed_response = JSON.parse(response.body)
79
+ logger.warn('Not a fork.') and return unless parsed_response['fork']
80
+ parent_url = parsed_response.dig('parent', 'html_url')
81
+ add_remote('upstream', parent_url) unless parent_url.to_s.empty?
82
+
83
+ logger.info(remotes.stdout)
84
+ end
85
+
86
+ def self.hi
87
+ latest_project_info = get_latest_project_info
88
+ logger.info("\nHang on, updating your local projects and remote forks...\n\n")
89
+ git_projects.each do |project|
90
+ Dir.chdir(project) do
91
+ logger.info("\nGetting #{project} in shape...")
92
+ yield if block_given?
93
+ update_project
94
+ end
95
+ end
96
+ logger.info("\nGood morning!\n\n")
97
+ logger.info(latest_project_info)
98
+ check_projects_older_than(1, :months)
99
+ end
100
+
101
+ def self.hi!
102
+ hi do
103
+ logger.info('Removing unused branches...')
104
+ clear_branches
105
+ logger.info('Adding upstream...')
106
+ add_upstream
107
+ end
108
+ end
109
+
110
+ def self.open
111
+ Launchy.open(origin_push_url(remotes.stdout))
112
+ end
113
+
114
+ def self.get_latest_project_info
115
+ latest = latest_repo
116
+ time_diff = TimeDifference.between(File.mtime(latest), Time.now).humanize_higher_than(:days).downcase
117
+ time_diff_text = time_diff.empty? ? 'recently' : "#{time_diff} ago"
118
+ "It looks like you were working on #{File.basename(latest)} #{time_diff_text}.\n\n"
119
+ end
120
+
121
+ def self.latest_repo
122
+ Fs.latest(git_projects)
123
+ end
124
+
125
+ def self.check_projects_older_than(quantity, unit)
126
+ old_projects = Fs.older_than(git_projects, quantity, unit)
127
+ deprecated_projects = old_projects.reject do |project|
128
+ Dir.chdir(project) { branches.reject{ |branch| branch_pushed(branch) }.any? }
129
+ end
130
+
131
+ logger.info("The following projects have not been touched for more than #{quantity} #{unit} and all changes have been pushed, maybe consider removing them?") unless deprecated_projects.empty?
132
+ deprecated_projects.each do |project|
133
+ time_diff = TimeDifference.between(File.mtime(project), Time.now).humanize_higher_than(:weeks).downcase
134
+ logger.info(" #{File.basename(project)} (#{time_diff})")
135
+ end
136
+ end
137
+
138
+ def self.stash
139
+ Command.run('git', 'stash').output_does_not_match?(%r{No local changes to save})
140
+ end
141
+
142
+ def self.current_branch
143
+ Command.run_dry('git', 'symbolic-ref', '--short', 'HEAD').stdout
144
+ end
145
+
146
+ def self.status
147
+ status = Command.run('git', 'status')
148
+ status.stdout + status.stderr
149
+ end
150
+
151
+ def self.status_dry
152
+ status = Command.run_dry('git', 'status')
153
+ status.stdout + status.stderr
154
+ end
155
+
156
+ def self.checkout(existing_branch)
157
+ Command.run('git', 'checkout', existing_branch)
158
+ end
159
+
160
+ def self.create(branch)
161
+ Command.run('git', 'checkout', '-b', branch)
162
+ Command.run('git', 'push', '--set-upstream', 'origin', branch)
163
+ end
164
+
165
+ def self.pull
166
+ Command.run('git', 'pull')
167
+ end
168
+
169
+ def self.pull_upstream
170
+ Command.run('git', 'fetch', 'upstream')
171
+ Command.run('git', 'merge', 'upstream/master')
172
+ Command.run('git', 'push', 'origin', 'master')
173
+ end
174
+
175
+ def self.branch
176
+ Command.run('git', 'branch')
177
+ end
178
+
179
+ def self.stash_pop
180
+ Command.run('git', 'stash', 'pop')
181
+ end
182
+
183
+ def self.branch_force_delete(branch)
184
+ Command.run('git', 'branch', '-D', branch)
185
+ end
186
+
187
+ def self.remotes
188
+ Command.run_dry('git', 'remote', '-v')
189
+ end
190
+
191
+ def self.has_upstream?(remotes)
192
+ remotes.include?('upstream')
193
+ end
194
+
195
+ def self.uncommitted_changes?(start_status)
196
+ !(start_status.include?('nothing to commit, working tree clean') or start_status.include?('nothing added to commit but untracked files present'))
197
+ end
198
+
199
+ def self.is_git?(start_status)
200
+ !start_status.include?('Not a git repository')
201
+ end
202
+
203
+ def self.origin_push_url(remotes)
204
+ # Order is important: first try to match "url" in "#{url}.git" as non-dot_git matchers would include ".git" in the match
205
+ origin_push_url_ssl_dot_git(remotes) or
206
+ origin_push_url_ssl(remotes)or
207
+ origin_push_url_https_dot_git(remotes) or
208
+ origin_push_url_https(remotes)
209
+ end
210
+
211
+ def self.origin_push_url_https(remotes)
212
+ remotes[%r{(?<=origin(\s))http(s?)://[a-zA-Z\-_./]*(?=(\s)\(push\))}]
213
+ end
214
+
215
+ def self.origin_push_url_https_dot_git(remotes)
216
+ remotes[%r{(?<=origin(\s))http(s?)://[a-zA-Z\-_./]*(?=(\.)git(\s)\(push\))}]
217
+ end
218
+
219
+ def self.origin_push_url_ssl_dot_git(remotes)
220
+ url = remotes[%r{(?<=origin(\s)git@)[a-zA-Z\-_./:]*(?=(\.)git(\s)\(push\))}]
221
+ "https://#{url.gsub(':', '/')}" if url
222
+ end
223
+
224
+ def self.origin_push_url_ssl(remotes)
225
+ url = remotes[%r{(?<=origin(\s)git@)[a-zA-Z\-_./:]*(?=(\s)\(push\))}]
226
+ "https://#{url.gsub(':', '/')}" if url
227
+ end
228
+
229
+ def self.add_remote(name, url)
230
+ Command.run('git', 'remote', 'add', name, url)
231
+ end
232
+
233
+ def self.on_branch(branch)
234
+ keep_changes do
235
+ working_branch = current_branch
236
+ switch_needed = !working_branch.eql?(branch)
237
+ if switch_needed
238
+ result = checkout(branch)
239
+ create(branch) if result.stderr.eql?("error: pathspec '#{branch}' did not match any file(s) known to git.")
240
+ end
241
+
242
+ yield
243
+
244
+ checkout(working_branch) if switch_needed
245
+ end
246
+ end
247
+
248
+ def self.keep_changes
249
+ status = status_dry
250
+ pop_stash = uncommitted_changes?(status)
251
+ stash if pop_stash
252
+ yield
253
+ stash_pop if pop_stash
254
+ end
255
+
256
+ def self.git_projects
257
+ Fs.ls_dirs.select do |dir|
258
+ Dir.chdir(dir) do
259
+ is_git?(status_dry)
260
+ end
261
+ end
262
+ end
263
+
264
+ def self.branch_pushed(branch)
265
+ Command.run_dry('git', 'log', branch, '--not', '--remotes').stdout.empty?
266
+ end
267
+
268
+ def self.branches
269
+ branches = Command.run_dry('git', 'for-each-ref', "--format='%(refname)'", 'refs/heads/').stdout
270
+ branches.each_line.map { |line| line.strip[%r{(?<='refs/heads/)(.*)(?=')}] }
271
+ end
272
+
273
+ def self.push
274
+ Command.run('git', 'push')
275
+ end
276
+
277
+ def self.rebase(branch)
278
+ Command.run('git', 'rebase', branch)
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,3 @@
1
+ module Autowow
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,237 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: autowow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - thisismydesign
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-09-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: easy_logging
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.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.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
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: colorize
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: time_difference
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.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.5.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: launchy
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
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.15'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.15'
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
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: guard-bundler
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: guard-rspec
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: coveralls
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ description:
182
+ email:
183
+ - thisismydesign@users.noreply.github.com
184
+ executables:
185
+ - autowow
186
+ - aw
187
+ extensions: []
188
+ extra_rdoc_files: []
189
+ files:
190
+ - ".gitignore"
191
+ - ".rspec"
192
+ - ".travis.yml"
193
+ - Gemfile
194
+ - Guardfile
195
+ - LICENSE.txt
196
+ - README.md
197
+ - Rakefile
198
+ - autowow.gemspec
199
+ - bin/autowow
200
+ - bin/aw
201
+ - bin/console
202
+ - bin/setup
203
+ - lib/autowow.rb
204
+ - lib/autowow/cli.rb
205
+ - lib/autowow/command.rb
206
+ - lib/autowow/decorators/string_decorator.rb
207
+ - lib/autowow/fs.rb
208
+ - lib/autowow/gem.rb
209
+ - lib/autowow/log_formatter.rb
210
+ - lib/autowow/time_difference.rb
211
+ - lib/autowow/vcs.rb
212
+ - lib/autowow/version.rb
213
+ homepage: https://github.com/thisismydesign/autowow
214
+ licenses:
215
+ - MIT
216
+ metadata: {}
217
+ post_install_message:
218
+ rdoc_options: []
219
+ require_paths:
220
+ - lib
221
+ required_ruby_version: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - ">="
224
+ - !ruby/object:Gem::Version
225
+ version: '0'
226
+ required_rubygems_version: !ruby/object:Gem::Requirement
227
+ requirements:
228
+ - - ">="
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ requirements: []
232
+ rubyforge_project:
233
+ rubygems_version: 2.6.11
234
+ signing_key:
235
+ specification_version: 4
236
+ summary: Set of commands to AUTOmate Way Of Working
237
+ test_files: []