assisted_workflow 0.1.0
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.
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +54 -0
- data/LICENSE.txt +22 -0
- data/README.md +77 -0
- data/Rakefile +1 -0
- data/assisted_workflow.gemspec +97 -0
- data/bin/aw +12 -0
- data/lib/assisted_workflow/cli.rb +158 -0
- data/lib/assisted_workflow/config_file.rb +60 -0
- data/lib/assisted_workflow/exceptions.rb +4 -0
- data/lib/assisted_workflow/git.rb +93 -0
- data/lib/assisted_workflow/github.rb +37 -0
- data/lib/assisted_workflow/pivotal.rb +73 -0
- data/lib/assisted_workflow/templates/awconfig.global.tt +7 -0
- data/lib/assisted_workflow/templates/awconfig.local.tt +3 -0
- data/lib/assisted_workflow/templates/commit-msg.tt +36 -0
- data/lib/assisted_workflow/version.rb +3 -0
- data/lib/assisted_workflow.rb +9 -0
- metadata +173 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
assisted_workflow (0.1.0)
|
5
|
+
hashie (~> 2.0.5)
|
6
|
+
octokit (~> 2.0)
|
7
|
+
pivotal-tracker (~> 0.5.12)
|
8
|
+
thor (~> 0.18.1)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
addressable (2.3.5)
|
14
|
+
builder (3.2.2)
|
15
|
+
crack (0.4.1)
|
16
|
+
safe_yaml (~> 0.9.0)
|
17
|
+
faraday (0.8.8)
|
18
|
+
multipart-post (~> 1.2.0)
|
19
|
+
happymapper (0.4.1)
|
20
|
+
libxml-ruby (~> 2.0)
|
21
|
+
hashie (2.0.5)
|
22
|
+
libxml-ruby (2.7.0)
|
23
|
+
mime-types (2.0)
|
24
|
+
mini_portile (0.5.2)
|
25
|
+
multipart-post (1.2.0)
|
26
|
+
nokogiri (1.6.1)
|
27
|
+
mini_portile (~> 0.5.0)
|
28
|
+
nokogiri-happymapper (0.5.8)
|
29
|
+
nokogiri (~> 1.5)
|
30
|
+
octokit (2.7.0)
|
31
|
+
sawyer (~> 0.5.2)
|
32
|
+
pivotal-tracker (0.5.12)
|
33
|
+
builder
|
34
|
+
builder
|
35
|
+
crack
|
36
|
+
happymapper (>= 0.3.2)
|
37
|
+
nokogiri (>= 1.4.3)
|
38
|
+
nokogiri (>= 1.5.5)
|
39
|
+
nokogiri-happymapper (>= 0.5.4)
|
40
|
+
rest-client (~> 1.6.0)
|
41
|
+
rest-client (~> 1.6.0)
|
42
|
+
rest-client (1.6.7)
|
43
|
+
mime-types (>= 1.16)
|
44
|
+
safe_yaml (0.9.7)
|
45
|
+
sawyer (0.5.2)
|
46
|
+
addressable (~> 2.3.5)
|
47
|
+
faraday (~> 0.8, < 0.10)
|
48
|
+
thor (0.18.1)
|
49
|
+
|
50
|
+
PLATFORMS
|
51
|
+
ruby
|
52
|
+
|
53
|
+
DEPENDENCIES
|
54
|
+
assisted_workflow!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Flavio Granero
|
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,77 @@
|
|
1
|
+
# Assisted Workflow (aw)
|
2
|
+
|
3
|
+
AW is a CLI tool to automate software development workflows based on github pull requests.
|
4
|
+
|
5
|
+
* [Github Workflow](http://scottchacon.com/2011/08/31/github-flow.html)
|
6
|
+
|
7
|
+
Here in [Inaka](http://inaka.net) we have the following workflow steps:
|
8
|
+
|
9
|
+
1. Start a pivotal task, moving to a new git branch for the new feature/bug fix
|
10
|
+
2. Commit the changes, pushing to a new remote branch
|
11
|
+
3. Submit a pull-request, allowing other team member to review the code, and merge into master if everything is ok
|
12
|
+
4. Finish the pivotal task, removing both local and remote feature branches
|
13
|
+
5. Deploy master branch.
|
14
|
+
|
15
|
+
For more details, please read more about the [Inaka Workflow](https://github.com/inaka/inaka_corp/wiki/Inaka-Workflow).
|
16
|
+
|
17
|
+
This gem provides a command line tool to automate tasks related with `start`, `submit` and `finish` steps.
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
gem 'assisted_workflow'
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install assisted_workflow
|
32
|
+
|
33
|
+
Or, if you are using homebrew and want to make `aw` system wide available, use brew-gem:
|
34
|
+
|
35
|
+
$ brew install brew-gem
|
36
|
+
$ brew gem assisted_workflow
|
37
|
+
|
38
|
+
## Initial Setup
|
39
|
+
|
40
|
+
TODO: Write initial setup instructions here
|
41
|
+
|
42
|
+
## Usage
|
43
|
+
|
44
|
+
TODO: Write usage instructions here
|
45
|
+
|
46
|
+
##Requirements
|
47
|
+
|
48
|
+
`aw` assumes you're using an 'origin' remote. If you are not,
|
49
|
+
either add an 'origin' remote that points to the GitHub repository you want to submit pull requests.
|
50
|
+
|
51
|
+
##Private repositories
|
52
|
+
|
53
|
+
To submit pull requests for your private repositories you have set up your `aw` config for github
|
54
|
+
|
55
|
+
$ aw config github.token=your_githubtoken123456789 --global
|
56
|
+
|
57
|
+
You must generate your OAuth token for command line use, see how to [generate oauth token](https://help.github.com/articles/creating-an-oauth-token-for-command-line-use).
|
58
|
+
|
59
|
+
## Contributing
|
60
|
+
|
61
|
+
1. Fork it
|
62
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
63
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
64
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
65
|
+
5. Create new Pull Request
|
66
|
+
|
67
|
+
## To-do
|
68
|
+
|
69
|
+
1. test coverage, travis-ci and codeclimate setup.
|
70
|
+
2. add github issues support
|
71
|
+
3. allow individual add-ons tasks like pivotal:start, github:submit, freckle:log
|
72
|
+
|
73
|
+
#### Inspiration
|
74
|
+
|
75
|
+
1. https://github.com/ddollar/foreman
|
76
|
+
2. https://github.com/github/hub
|
77
|
+
3. https://github.com/schacon/git-pulls
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'assisted_workflow/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "assisted_workflow"
|
8
|
+
gem.version = AssistedWorkflow::VERSION
|
9
|
+
gem.authors = ["Flavio Granero"]
|
10
|
+
gem.email = ["maltempe@gmail.com"]
|
11
|
+
gem.summary = %q{AW is a CLI tool to automate software development workflows based on github pull requests}
|
12
|
+
gem.homepage = ""
|
13
|
+
gem.license = "MIT"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.executables = %w( aw )
|
20
|
+
|
21
|
+
gem.add_dependency "thor", "~> 0.18.1"
|
22
|
+
gem.add_dependency "pivotal-tracker", "~> 0.5.12"
|
23
|
+
gem.add_dependency "octokit", "~> 2.0"
|
24
|
+
gem.add_dependency "hashie", "~> 2.0.5"
|
25
|
+
|
26
|
+
gem.description = <<desc
|
27
|
+
`aw` is a command line tool to automate software development workflows based on github pull requests.
|
28
|
+
|
29
|
+
Initial Setup:
|
30
|
+
|
31
|
+
$ aw setup
|
32
|
+
|
33
|
+
Usage:
|
34
|
+
|
35
|
+
$ aw start
|
36
|
+
|
37
|
+
$ aw submit
|
38
|
+
|
39
|
+
$ aw finish
|
40
|
+
|
41
|
+
desc
|
42
|
+
|
43
|
+
gem.post_install_message = <<-message
|
44
|
+
|
45
|
+
`..::/++ooooooooo+/::--`
|
46
|
+
`-:://:---....```......--::///:-.`
|
47
|
+
`..-:::---.``` ```...----::-..`
|
48
|
+
`.:///-````` .-:++::-`
|
49
|
+
`///::` :/+oo:.` -:/+/-
|
50
|
+
..-//. ....` `.-/+/`
|
51
|
+
:+/.` .-://.
|
52
|
+
``/+: `.//-``
|
53
|
+
`::-.` `.---` ``` .-://`
|
54
|
+
`++` `:sso++/+hmy-.` .//-.`
|
55
|
+
`.-//` -++---:/+o/` ` ``:+:
|
56
|
+
.---- .:::-.````` .--::. :+/`
|
57
|
+
-::.. .:::-` `.-++-` :+/.`
|
58
|
+
-/:`` -++-.` `:/::- -/:-.
|
59
|
+
:/:` -oo-.` ``.--.` ``.-. -:-..
|
60
|
+
:/:` -++-.` ``.+ooo+/:+so. -:-..
|
61
|
+
:/:` -++:-` `.+o+++++oso. -:-..
|
62
|
+
-/:.` .::/+- `//:--//++/` -/:..
|
63
|
+
.---- `.+s/.` `-:. :+/.`
|
64
|
+
`.//` -:+ss:-. ``.:/oso++-` :+:
|
65
|
+
`//.`` .-:+++///++++///++++/:--`` ``:/:
|
66
|
+
.-::- ``--:::+ss+/:::----.`` `----.
|
67
|
+
`/+/` ../s+-.``` -//.``
|
68
|
+
.--++- .::.`.:/:-. .-+ys.
|
69
|
+
``:hds++:::/:` .++-` .:/oo+//::-
|
70
|
+
``+oo////+yds+/-.-//. .--/++++/:-``:/:``
|
71
|
+
`.oso--://sho+////+/-` ```...::/++++/::. -::--
|
72
|
+
``.--:/:--:+/.`.:/shyss+:-..``````````.---::/+oso++/..-:://. .++.`
|
73
|
+
`:/:``:++++/-- -:/++++ooooooooooooooooo++:-.`` `-:/+/` --::-
|
74
|
+
`--`` `sdy/:. -:::-. ````````` -:/++-` `/o/`
|
75
|
+
`-. .-+o/`` .--//. .:///:.` -::..`
|
76
|
+
``...` `:///- `.-++- `.:++::- .--:-.
|
77
|
+
.......:ss-`` `++:.` `.-+o/:- ./+-
|
78
|
+
`.::.`.:/.`` `.:o+.`` ``./++o+-`` `//-``
|
79
|
+
`+o. `-:+++//:::/++++:-.` -----
|
80
|
+
`++` ``--------::---.` `.-::
|
81
|
+
`//. `.....`` ``-++
|
82
|
+
`.//-...----------------..............----------------------------..---/oo
|
83
|
+
|
84
|
+
|
85
|
+
AW, Thanks!
|
86
|
+
===========
|
87
|
+
|
88
|
+
Use the provided `aw` command-line tool to start a task creating a feature branch, submit a pull request with the changes and finish a task, keeping your repository clean. For more details, search for Inaka Workflow description.
|
89
|
+
|
90
|
+
Cheers,
|
91
|
+
Flavio
|
92
|
+
|
93
|
+
--------------------------------------------------------------------------------
|
94
|
+
|
95
|
+
message
|
96
|
+
|
97
|
+
end
|
data/bin/aw
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
# resolve bin path, ignoring symlinks
|
5
|
+
require "pathname"
|
6
|
+
bin_file = Pathname.new(__FILE__).realpath
|
7
|
+
|
8
|
+
$:.unshift File.expand_path("../../lib", bin_file)
|
9
|
+
|
10
|
+
require "assisted_workflow/cli"
|
11
|
+
|
12
|
+
AssistedWorkflow::CLI.start
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require "assisted_workflow"
|
2
|
+
require "yaml"
|
3
|
+
require "thor"
|
4
|
+
|
5
|
+
class AssistedWorkflow::CLI < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
GLOBAL_CONFIG = File.expand_path(".awconfig", ENV["HOME"])
|
8
|
+
LOCAL_CONFIG = ".awconfig"
|
9
|
+
source_root(File.expand_path(File.join(__FILE__, "..", "templates")))
|
10
|
+
|
11
|
+
map ["-v", "--version"] => :version
|
12
|
+
|
13
|
+
desc "setup", "Setup initial configuration in current project directory"
|
14
|
+
def setup
|
15
|
+
copy_file "awconfig.global.tt", GLOBAL_CONFIG
|
16
|
+
copy_file "awconfig.local.tt", LOCAL_CONFIG
|
17
|
+
if File.exists?(".git")
|
18
|
+
copy_file "commit-msg.tt", ".git/hooks/commit-msg"
|
19
|
+
else
|
20
|
+
raise AssistedWorkflow::Error, ".git folder not found"
|
21
|
+
end
|
22
|
+
say "set your own configuration editing the .awconfig files or running:", :green
|
23
|
+
say "\t$ aw config pivotal.fullname='Flavio Granero' --global"
|
24
|
+
say "\t$ aw config pivotal.token=MYPIVOTALTOKEN --global"
|
25
|
+
say "\t$ aw config github.token=MYGITHUBOAUTHTOKEN --global"
|
26
|
+
say "\t$ aw config pivotal.project_id=00001"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "start [STORY_ID]", "Start the pivotal story and create a new branch to receive the changes"
|
30
|
+
def start(story_id=nil)
|
31
|
+
check_awfile!
|
32
|
+
story = pivotal.find_story(story_id)
|
33
|
+
if story.nil?
|
34
|
+
stories = pivotal.pending_stories
|
35
|
+
print_title "pending stories"
|
36
|
+
print_table(pivotal.display_values(stories))
|
37
|
+
say "start a story using:", :green
|
38
|
+
say "\t$ aw start [STORY_ID]"
|
39
|
+
else
|
40
|
+
say "creating the feature branch"
|
41
|
+
git.create_story_branch(story)
|
42
|
+
say "starting story: #{story.name}"
|
43
|
+
pivotal.start_story(story)
|
44
|
+
say "after commiting your changes, submit a pull request using:", :green
|
45
|
+
say "\t$ aw submit"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "submit", "Submits the current story creating a new pull request"
|
50
|
+
def submit
|
51
|
+
check_awfile!
|
52
|
+
story_id = git.current_story_id
|
53
|
+
say "loading story info"
|
54
|
+
story = pivotal.find_story(story_id)
|
55
|
+
if story
|
56
|
+
say "preparing local branch"
|
57
|
+
git.rebase_and_push
|
58
|
+
say "submiting the new pull request"
|
59
|
+
pr = github.create_pull_request(git.repository, git.current_branch, story)
|
60
|
+
say "finishing the story"
|
61
|
+
pivotal.finish_story(story)
|
62
|
+
say "new pull request: #{pr._links.html.href}", :yellow
|
63
|
+
say "after pull request approval, remove the feature branch using:", :green
|
64
|
+
say "\t$aw finish"
|
65
|
+
else
|
66
|
+
raise AssistedWorkflow::Error, "story not found, make sure a feature branch in active"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "finish", "Check if the changes are merged into master, removing the current feature branch"
|
71
|
+
def finish
|
72
|
+
check_awfile!
|
73
|
+
story_id = git.current_story_id
|
74
|
+
if story_id.to_i > 0
|
75
|
+
if git.is_merged?
|
76
|
+
say "removing local and remote feature branches"
|
77
|
+
git.remove_branch
|
78
|
+
say "well done! check your next stories using:", :green
|
79
|
+
say "\t$ aw start"
|
80
|
+
else
|
81
|
+
say "this branch is not merged into master yet", :yellow
|
82
|
+
end
|
83
|
+
else
|
84
|
+
raise AssistedWorkflow::Error, "story not found, make sure a feature branch in active"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "version", "Display assisted_workflow gem version"
|
89
|
+
def version
|
90
|
+
say AssistedWorkflow::VERSION
|
91
|
+
end
|
92
|
+
|
93
|
+
desc "config group.key=value", "Set configuration keys in local config file"
|
94
|
+
method_option :global, :type => :boolean, :aliases => "-g", :desc => "Set configuration key in global configuration file (for all projects)"
|
95
|
+
def config(*args)
|
96
|
+
if args.empty?
|
97
|
+
print_table configuration.to_hash
|
98
|
+
else
|
99
|
+
config_file.parse(args).save!
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
no_tasks do
|
104
|
+
def pivotal
|
105
|
+
@pivotal ||= AssistedWorkflow::Pivotal.new(configuration[:pivotal])
|
106
|
+
end
|
107
|
+
|
108
|
+
def git
|
109
|
+
@git ||= AssistedWorkflow::Git.new
|
110
|
+
end
|
111
|
+
|
112
|
+
def github
|
113
|
+
@github ||= AssistedWorkflow::Github.new(configuration[:github])
|
114
|
+
end
|
115
|
+
|
116
|
+
def config_file
|
117
|
+
@config_file ||= AssistedWorkflow::ConfigFile.new(awfile)
|
118
|
+
end
|
119
|
+
|
120
|
+
# loads all configuration, merging global and local values
|
121
|
+
def configuration
|
122
|
+
@configuration ||= begin
|
123
|
+
AssistedWorkflow::ConfigFile.new(GLOBAL_CONFIG).merge_file(LOCAL_CONFIG)
|
124
|
+
rescue TypeError
|
125
|
+
raise AssistedWorkflow::Error, "Error on loading .awconfig files. Please check the content format."
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class << self
|
131
|
+
def start(given_args=ARGV, config={})
|
132
|
+
super
|
133
|
+
rescue AssistedWorkflow::Error => e
|
134
|
+
config[:shell].say e.message, :red
|
135
|
+
exit(1)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
private ######################################################################
|
140
|
+
|
141
|
+
def print_title(title)
|
142
|
+
say "-" * title.length, :green
|
143
|
+
say title.upcase, :green
|
144
|
+
say "-" * title.length, :green
|
145
|
+
end
|
146
|
+
|
147
|
+
def check_awfile!
|
148
|
+
raise AssistedWorkflow::Error, "#{awfile} does not exist.\nmake sure you run `$ aw setup` in your project folder." unless File.exist?(awfile)
|
149
|
+
end
|
150
|
+
|
151
|
+
def awfile
|
152
|
+
case
|
153
|
+
when options[:awfile] then options[:awfile]
|
154
|
+
when options[:global] then GLOBAL_CONFIG
|
155
|
+
else LOCAL_CONFIG
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "hashie"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
module AssistedWorkflow
|
5
|
+
# special hash class to allow configuration management
|
6
|
+
class ConfigHash < Hash
|
7
|
+
include Hashie::Extensions::MergeInitializer
|
8
|
+
include Hashie::Extensions::IndifferentAccess
|
9
|
+
include Hashie::Extensions::DeepMerge
|
10
|
+
end
|
11
|
+
|
12
|
+
# class providing methods to load, manage and save configuration files
|
13
|
+
class ConfigFile
|
14
|
+
# parse a command line arguments into configuration keys
|
15
|
+
# Example:
|
16
|
+
# pivotal.token=mypivotaltoken
|
17
|
+
# => {:pivotal => {:token => "mypivotaltoken"}}
|
18
|
+
def parse(args)
|
19
|
+
Array(args).each do |values|
|
20
|
+
keys, value = values.split("=")
|
21
|
+
keys.split(".").reverse.each do |k|
|
22
|
+
value = {k => value}
|
23
|
+
end
|
24
|
+
@hash.deep_merge!(value)
|
25
|
+
end
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# dumps the configuration values to a file in yaml format
|
30
|
+
def save!
|
31
|
+
content = @hash.to_yaml
|
32
|
+
content.gsub! " !ruby/hash:AssistedWorkflow::ConfigHash", ""
|
33
|
+
File.open(@awfile, 'w'){ |f| f.write(content) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](key)
|
37
|
+
@hash[key]
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_hash
|
41
|
+
@hash.dup
|
42
|
+
end
|
43
|
+
|
44
|
+
# merges other config file into the current one
|
45
|
+
def merge_file(file)
|
46
|
+
other_config = ConfigFile.new(file)
|
47
|
+
@hash.deep_merge!(other_config.to_hash)
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(awfile)
|
52
|
+
@awfile = awfile
|
53
|
+
@hash = if File.exists?(@awfile)
|
54
|
+
ConfigHash.new(::YAML::load_file(@awfile) || {})
|
55
|
+
else
|
56
|
+
ConfigHash.new
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require "assisted_workflow/exceptions"
|
2
|
+
|
3
|
+
module AssistedWorkflow
|
4
|
+
|
5
|
+
class GitError < Error; end
|
6
|
+
|
7
|
+
class Git
|
8
|
+
|
9
|
+
DESCRIPTION_LIMIT = 30
|
10
|
+
|
11
|
+
# creates a new git branch based on story attributes
|
12
|
+
# the branch name format is:
|
13
|
+
# => story_onwer_username.story_id.story_name
|
14
|
+
|
15
|
+
def create_story_branch(story)
|
16
|
+
branch = branch_name(story)
|
17
|
+
git "checkout -b #{branch}"
|
18
|
+
# git "push --set-upstream origin #{branch}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# run all the git steps required for a clean pull request
|
22
|
+
def rebase_and_push
|
23
|
+
check_everything_commited!
|
24
|
+
branch = current_branch
|
25
|
+
git "checkout master"
|
26
|
+
git "pull --rebase"
|
27
|
+
git "checkout #{branch}"
|
28
|
+
git "rebase master"
|
29
|
+
git "push -u -f origin #{branch}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# returns the current story id based on branch name
|
33
|
+
def current_story_id
|
34
|
+
current_branch.split(".")[1]
|
35
|
+
end
|
36
|
+
|
37
|
+
# returns the current local branch name
|
38
|
+
def current_branch
|
39
|
+
git("rev-parse --abbrev-ref HEAD", :silent => true)
|
40
|
+
end
|
41
|
+
|
42
|
+
# returns the repository name assigned to origin following the format:
|
43
|
+
# owner/project
|
44
|
+
def repository
|
45
|
+
url = git("config --get remote.origin.url", :error => "cannot find 'origin' remote repository url")
|
46
|
+
url.gsub("git@github.com:", "").gsub("https://github.com/", "").gsub(/\.git$/, "").chomp
|
47
|
+
end
|
48
|
+
|
49
|
+
# check if current branch is merged into master
|
50
|
+
def is_merged?
|
51
|
+
check_everything_commited!
|
52
|
+
branch = current_branch
|
53
|
+
git "checkout master"
|
54
|
+
git "pull --rebase"
|
55
|
+
merged = git("branch --merged").include?(branch)
|
56
|
+
git "checkout #{branch}"
|
57
|
+
merged
|
58
|
+
end
|
59
|
+
|
60
|
+
# removes current branch and his remote version
|
61
|
+
def remove_branch
|
62
|
+
branch = current_branch
|
63
|
+
git "push origin :#{branch}"
|
64
|
+
git "checkout master"
|
65
|
+
git "branch -D #{branch}"
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def git(command, options = {})
|
71
|
+
puts "git #{command}" unless options[:silent] == true
|
72
|
+
result = %x{git #{command}}.chomp
|
73
|
+
unless $? == 0
|
74
|
+
msg = ["git command error", options[:error]].compact.join(": ")
|
75
|
+
raise GitError, msg
|
76
|
+
end
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
def branch_name(story)
|
81
|
+
description = story.name.to_s.downcase.gsub(/\W/, "_").slice(0, DESCRIPTION_LIMIT)
|
82
|
+
[story.other_id, story.id, description].join(".").downcase
|
83
|
+
end
|
84
|
+
|
85
|
+
def not_commited_changes
|
86
|
+
git("status --porcelain", :silent => true).split("\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
def check_everything_commited!
|
90
|
+
raise AssistedWorkflow::Error, "git: there are not commited changes" unless not_commited_changes.empty?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "assisted_workflow/exceptions"
|
2
|
+
require "octokit"
|
3
|
+
|
4
|
+
module AssistedWorkflow
|
5
|
+
|
6
|
+
class Github
|
7
|
+
def initialize(options)
|
8
|
+
validate_options!(options)
|
9
|
+
@client = Octokit::Client.new(:access_token => options["token"])
|
10
|
+
end
|
11
|
+
|
12
|
+
# creates a pull request using current branch changes
|
13
|
+
def create_pull_request(repo, branch, story)
|
14
|
+
base = "master"
|
15
|
+
title = "[##{story.id}] #{story.name}"
|
16
|
+
pull_request = @client.create_pull_request(repo, base, branch, title, story.description)
|
17
|
+
if pull_request.nil?
|
18
|
+
raise AssistedWorkflow::Error, "error on submiting the pull request"
|
19
|
+
else
|
20
|
+
pull_request
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def validate_options!(options)
|
27
|
+
if options.nil? || options.empty?
|
28
|
+
raise AssistedWorkflow::Error, "github missing configuration"
|
29
|
+
end
|
30
|
+
required_keys = %w(token)
|
31
|
+
missing_keys = required_keys - options.keys
|
32
|
+
if missing_keys.size > 0
|
33
|
+
raise AssistedWorkflow::Error, "github missing configuration: #{missing_keys.inspect}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "assisted_workflow/exceptions"
|
2
|
+
require 'pivotal_tracker'
|
3
|
+
|
4
|
+
# wrapper class to pivotal api client
|
5
|
+
module AssistedWorkflow
|
6
|
+
class Pivotal
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
validate_options!(options)
|
10
|
+
|
11
|
+
PivotalTracker::Client.token = options["token"]
|
12
|
+
begin
|
13
|
+
@project = PivotalTracker::Project.find(options["project_id"])
|
14
|
+
rescue
|
15
|
+
raise AssistedWorkflow::Error, "pivotal project #{options["project_id"]} not found."
|
16
|
+
end
|
17
|
+
@fullname = options["fullname"]
|
18
|
+
@username = options["username"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_story(story_id)
|
22
|
+
if story_id.to_i > 0
|
23
|
+
story = @project.stories.find(story_id)
|
24
|
+
story.other_id = @username || @fullname
|
25
|
+
story.other_id = story.other_id.to_s.downcase.split.join
|
26
|
+
story
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def start_story(story)
|
31
|
+
story.update(:current_state => "started") if story
|
32
|
+
end
|
33
|
+
|
34
|
+
def finish_story(story)
|
35
|
+
story.update(:current_state => finished_state(story)) if story
|
36
|
+
end
|
37
|
+
|
38
|
+
def pending_stories
|
39
|
+
@project.stories.all(:state => "unstarted", :owned_by => @username, :limit => 5)
|
40
|
+
end
|
41
|
+
|
42
|
+
def display_values(stories)
|
43
|
+
stories.map do |story|
|
44
|
+
[story.id, story.name]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def valid?
|
49
|
+
@project.present?
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def validate_options!(options)
|
55
|
+
if options.nil? || options.empty?
|
56
|
+
raise AssistedWorkflow::Error, "pivotal missing configuration"
|
57
|
+
end
|
58
|
+
required_keys = %w(fullname token project_id)
|
59
|
+
missing_keys = required_keys - options.keys
|
60
|
+
if missing_keys.size > 0
|
61
|
+
raise AssistedWorkflow::Error, "pivotal missing configuration: #{missing_keys.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def finished_state(story)
|
66
|
+
if story.story_type == "chore"
|
67
|
+
"accepted"
|
68
|
+
else
|
69
|
+
"finished"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
# This hook script will automatically add the Pivotal Tracker ID
|
4
|
+
# to the beginning of the git commit message (in the format of
|
5
|
+
# [#12345678]) if it is not already there.
|
6
|
+
#
|
7
|
+
# The script pulls the ID from the beginning of the branch name. If
|
8
|
+
# the branch name does not start with the Pivotal Tracker ID, then
|
9
|
+
# this script will do nothing.
|
10
|
+
#
|
11
|
+
# Specifying the Pivotal Tracker ID in the git commit message allows
|
12
|
+
# Pivotal to list all commits associated with a story when the
|
13
|
+
# story is viewed.
|
14
|
+
#
|
15
|
+
# To enable this script, copy it into your repo's .git/hooks directory.
|
16
|
+
#
|
17
|
+
|
18
|
+
COMMIT_MSG_FILE=$1
|
19
|
+
CURRENT_BRANCH=`git rev-parse --abbrev-ref HEAD`
|
20
|
+
TRACKER_ID=`echo $CURRENT_BRANCH | awk -F\. '{print $2}'`
|
21
|
+
|
22
|
+
# Make sure the branch name starts with what looks like a tracker ID
|
23
|
+
if [ "$TRACKER_ID" != "" ]; then
|
24
|
+
|
25
|
+
# If we could not find the tracker ID in the commit message in the
|
26
|
+
# proper format, then add it.
|
27
|
+
grep -q "\[#$TRACKER_ID\]" $COMMIT_MSG_FILE
|
28
|
+
if [ $? -eq 1 ]; then
|
29
|
+
sed "1s/^/[#$TRACKER_ID] /" $COMMIT_MSG_FILE > /tmp/tracker_git_commit_msg
|
30
|
+
mv /tmp/tracker_git_commit_msg $COMMIT_MSG_FILE
|
31
|
+
fi
|
32
|
+
fi
|
33
|
+
|
34
|
+
# Explicitly exit 0 to make sure we don't accidentally abort the commit
|
35
|
+
exit 0
|
36
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "assisted_workflow/version"
|
2
|
+
require "assisted_workflow/exceptions"
|
3
|
+
|
4
|
+
module AssistedWorkflow
|
5
|
+
autoload :Pivotal, "assisted_workflow/pivotal"
|
6
|
+
autoload :Git, "assisted_workflow/git"
|
7
|
+
autoload :Github, "assisted_workflow/github"
|
8
|
+
autoload :ConfigFile, "assisted_workflow/config_file"
|
9
|
+
end
|
metadata
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: assisted_workflow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Flavio Granero
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-01-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.18.1
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.18.1
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: pivotal-tracker
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.5.12
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.5.12
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: octokit
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: hashie
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.0.5
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.0.5
|
78
|
+
description: ! " `aw` is a command line tool to automate software development workflows
|
79
|
+
based on github pull requests.\n \n Initial Setup:\n \n $ aw setup\n\n Usage:\n\n
|
80
|
+
\ $ aw start\n \n $ aw submit\n \n $ aw finish\n \n"
|
81
|
+
email:
|
82
|
+
- maltempe@gmail.com
|
83
|
+
executables:
|
84
|
+
- aw
|
85
|
+
extensions: []
|
86
|
+
extra_rdoc_files: []
|
87
|
+
files:
|
88
|
+
- .gitignore
|
89
|
+
- Gemfile
|
90
|
+
- Gemfile.lock
|
91
|
+
- LICENSE.txt
|
92
|
+
- README.md
|
93
|
+
- Rakefile
|
94
|
+
- assisted_workflow.gemspec
|
95
|
+
- bin/aw
|
96
|
+
- lib/assisted_workflow.rb
|
97
|
+
- lib/assisted_workflow/cli.rb
|
98
|
+
- lib/assisted_workflow/config_file.rb
|
99
|
+
- lib/assisted_workflow/exceptions.rb
|
100
|
+
- lib/assisted_workflow/git.rb
|
101
|
+
- lib/assisted_workflow/github.rb
|
102
|
+
- lib/assisted_workflow/pivotal.rb
|
103
|
+
- lib/assisted_workflow/templates/awconfig.global.tt
|
104
|
+
- lib/assisted_workflow/templates/awconfig.local.tt
|
105
|
+
- lib/assisted_workflow/templates/commit-msg.tt
|
106
|
+
- lib/assisted_workflow/version.rb
|
107
|
+
homepage: ''
|
108
|
+
licenses:
|
109
|
+
- MIT
|
110
|
+
post_install_message: ! "\n `..::/++ooooooooo+/::--` \n
|
111
|
+
\ `-:://:---....```......--::///:-.` \n
|
112
|
+
\ `..-:::---.``` ```...----::-..` \n
|
113
|
+
\ `.:///-````` .-:++::-` \n
|
114
|
+
\ `///::` :/+oo:.` -:/+/- \n
|
115
|
+
\ ..-//. ....` `.-/+/` \n
|
116
|
+
\ :+/.` .-://. \n
|
117
|
+
\ ``/+: `.//-`` \n
|
118
|
+
\ `::-.` `.---` ``` .-://` \n
|
119
|
+
\ `++` `:sso++/+hmy-.` .//-.` \n
|
120
|
+
\ `.-//` -++---:/+o/` ` ``:+: \n
|
121
|
+
\ .---- .:::-.````` .--::. :+/` \n
|
122
|
+
\ -::.. .:::-` `.-++-` :+/.` \n
|
123
|
+
\ -/:`` -++-.` `:/::- -/:-. \n
|
124
|
+
\ :/:` -oo-.` ``.--.` ``.-. -:-.. \n
|
125
|
+
\ :/:` -++-.` ``.+ooo+/:+so. -:-.. \n
|
126
|
+
\ :/:` -++:-` `.+o+++++oso. -:-.. \n
|
127
|
+
\ -/:.` .::/+- `//:--//++/` -/:.. \n
|
128
|
+
\ .---- `.+s/.` `-:. :+/.` \n
|
129
|
+
\ `.//` -:+ss:-. ``.:/oso++-` :+: \n
|
130
|
+
\ `//.`` .-:+++///++++///++++/:--`` ``:/: \n
|
131
|
+
\ .-::- ``--:::+ss+/:::----.`` `----. \n
|
132
|
+
\ `/+/` ../s+-.``` -//.`` \n
|
133
|
+
\ .--++- .::.`.:/:-. .-+ys. \n
|
134
|
+
\ ``:hds++:::/:` .++-` .:/oo+//::- \n
|
135
|
+
\ ``+oo////+yds+/-.-//. .--/++++/:-``:/:`` \n
|
136
|
+
\ `.oso--://sho+////+/-` ```...::/++++/::. -::-- \n
|
137
|
+
\ ``.--:/:--:+/.`.:/shyss+:-..``````````.---::/+oso++/..-:://. .++.` \n
|
138
|
+
\ `:/:``:++++/-- -:/++++ooooooooooooooooo++:-.`` `-:/+/` --::- \n
|
139
|
+
\ `--`` `sdy/:. -:::-. ````````` -:/++-` `/o/` \n
|
140
|
+
\ `-. .-+o/`` .--//. .:///:.` -::..` \n``...`
|
141
|
+
`:///- `.-++- `.:++::- .--:-. \n.......:ss-``
|
142
|
+
\ `++:.` `.-+o/:- ./+- \n `.::.`.:/.``
|
143
|
+
\ `.:o+.`` ``./++o+-`` `//-``\n `+o.
|
144
|
+
\ `-:+++//:::/++++:-.` -----\n `++`
|
145
|
+
\ ``--------::---.` `.-::\n `//.
|
146
|
+
\ `.....`` ``-++\n `.//-...----------------..............----------------------------..---/oo\n\n\n
|
147
|
+
\ AW, Thanks!\n ===========\n\n Use the provided `aw` command-line
|
148
|
+
tool to start a task creating a feature branch, submit a pull request with the changes
|
149
|
+
and finish a task, keeping your repository clean. For more details, search for Inaka
|
150
|
+
Workflow description.\n\n Cheers,\n Flavio\n\n--------------------------------------------------------------------------------\n\n"
|
151
|
+
rdoc_options: []
|
152
|
+
require_paths:
|
153
|
+
- lib
|
154
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
155
|
+
none: false
|
156
|
+
requirements:
|
157
|
+
- - ! '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
requirements: []
|
167
|
+
rubyforge_project:
|
168
|
+
rubygems_version: 1.8.24
|
169
|
+
signing_key:
|
170
|
+
specification_version: 3
|
171
|
+
summary: AW is a CLI tool to automate software development workflows based on github
|
172
|
+
pull requests
|
173
|
+
test_files: []
|