v1gittools 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +157 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/doc_assets/in_progress.png +0 -0
- data/doc_assets/none.png +0 -0
- data/doc_assets/qa.png +0 -0
- data/exec/v1git +52 -0
- data/lib/v1gittools.rb +12 -0
- data/lib/v1gittools/base_tool.rb +25 -0
- data/lib/v1gittools/change_log_tool.rb +49 -0
- data/lib/v1gittools/config.rb +241 -0
- data/lib/v1gittools/deep_hash_with_indifferent_access.rb +41 -0
- data/lib/v1gittools/develop_story_tool.rb +28 -0
- data/lib/v1gittools/qa_tool.rb +62 -0
- data/lib/v1gittools/version.rb +3 -0
- data/v1git.conf.example +46 -0
- data/v1gittools.gemspec +40 -0
- metadata +189 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e07221ee4155c7323cc3eeaf864e4a6db17d6aeb
|
4
|
+
data.tar.gz: e86cfcba657a41c90e5ec8f001d946432b0b6b1c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 93d58ced68b07ca92cec63749b18e19ed017c9f39e8c31665dd07f098db6df345b43ed408ce02c67eb4f4cee6546b9296d7f5156ce27c46c3938b62d7bda1733
|
7
|
+
data.tar.gz: 526ae7d0c7718f1150da48623d0ba09f8748b46d25bb18b40127b14d52177190ffa15d2b08ae57dbd656b785e220888cef3e5b709463b213e23b3c51a6ab7d95
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at jchan@malwarebytes.org. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Jonathan Chan
|
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,157 @@
|
|
1
|
+
# V1GitTools
|
2
|
+
|
3
|
+
VersionOne Git tool integrates VersionOne Story/Defect statuses with git branches and github PRs. The result encourages developers to only make code changes based off of stories and defects promoting better tracking for projects. v1git is roughly based off of git flow, though it should be flexible enough to be used in other workflows.
|
4
|
+
### Screencast Tutorial
|
5
|
+
* [Setting up VersionOne Project for V1GitTools](https://malwarebytes.box.com/shared/static/m67wyc7tgctgz2pfdzdl9z20kwl3oudw.mov)
|
6
|
+
* [V1GitTools Demo & How To Setup](https://malwarebytes.box.com/shared/static/v2arkjgut0e7e3rt04p1mfmkj89p2cqc.mov)
|
7
|
+
|
8
|
+
## VersionOne Prerequisites
|
9
|
+
|
10
|
+
```v1git``` is designed for an environment where after code is written, testers or QA runs the code and verifies that it meets the requirement. In order for VersionOne to support that flow, an additional story status of "Test" must be created. The following resources are useful for setting up the "Test" status and adding it to the storyboard workflow.
|
11
|
+
|
12
|
+
https://community.versionone.com/Help-Center/Portfolio_Planning/Portfolio_Kanban/Working_with_Portfolio_Kanbans#Changing_Columns
|
13
|
+
https://community.versionone.com/Help-Center/Setup_and_Administration/List_Type_Administration
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
### Installing it via Rubygems
|
20
|
+
|
21
|
+
$ gem install v1gittools
|
22
|
+
|
23
|
+
### Or Without Rubygems Install
|
24
|
+
|
25
|
+
|
26
|
+
```
|
27
|
+
gem install specific_install
|
28
|
+
gem specific_install https://github.com/Malwarebytes/v1gittools.git master
|
29
|
+
```
|
30
|
+
|
31
|
+
|
32
|
+
## Setup
|
33
|
+
|
34
|
+
Copy ```v1git.conf.example``` to ```~/.v1git.conf```. Please refer to the example file for details on each parameter that needs to be set.
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
Demonstration of workflow:
|
39
|
+
|
40
|
+
### Initialize
|
41
|
+
|
42
|
+
When you start a new project with v1git, run:
|
43
|
+
|
44
|
+
```shell
|
45
|
+
$ v1git init
|
46
|
+
NOTICE: v1git has never been used for this project before. Generating default config...
|
47
|
+
|
48
|
+
Config generated with the following guessed/assumed values:
|
49
|
+
|
50
|
+
Develop branch: develop
|
51
|
+
github_url: https://github.com/acme/cool_project
|
52
|
+
|
53
|
+
github_owner: acme
|
54
|
+
|
55
|
+
github_repo: cool_project
|
56
|
+
|
57
|
+
If these values are not correct, please correct it in "~/code/cool_project/.git/v1git.conf".
|
58
|
+
|
59
|
+
Running validations... (if any of these fail, run 'exec/v1git validate' after correcting the issue.)
|
60
|
+
Validating VersionOne URL... PASSED
|
61
|
+
Validating VersionOne credentials... PASSED
|
62
|
+
Validating git config... PASSED
|
63
|
+
Validating github endpoint... PASSED
|
64
|
+
Validating github credentials...PASSED
|
65
|
+
```
|
66
|
+
|
67
|
+
If there are any issues with the setup, please correct and verify all settings are correct.
|
68
|
+
|
69
|
+
### Develop
|
70
|
+
|
71
|
+
Stories/Defects are created and added to the current sprint. They will be in the "None" column in the TeamRoom Storyboard.
|
72
|
+
|
73
|
+
![Story in None](doc_assets/none.png "Story in None")
|
74
|
+
|
75
|
+
When beginning work on a story/defect, identify the story or defect ID. ```B-10147``` in our example. Run the following command:
|
76
|
+
|
77
|
+
```shell
|
78
|
+
$ v1git develop B-10147 more_widgets # optional branch name; if branch name not included, defaults to story/defect ID
|
79
|
+
|
80
|
+
- Switched to a new branch 'more_widgets' based off of 'develop'.
|
81
|
+
- Set B-10147 to the status In Progress.
|
82
|
+
|
83
|
+
Implement story/defect in branch (Don't forget to push!). When complete, use:
|
84
|
+
|
85
|
+
v1git qa
|
86
|
+
|
87
|
+
|
88
|
+
```
|
89
|
+
|
90
|
+
![Story in In Progress](doc_assets/in_progress.png "Story In Progress")
|
91
|
+
|
92
|
+
The story is put into "In Progress" and the git repository is put into a state ready to be worked on.
|
93
|
+
|
94
|
+
### QA
|
95
|
+
|
96
|
+
After making the necessary commit(s) to satsify the defect/story requirements, v1git can be used to move the story into "Test" status.
|
97
|
+
|
98
|
+
```shell
|
99
|
+
$ v1git qa
|
100
|
+
- Created PR for this branch (complete creating PR in browser).
|
101
|
+
- Set 'Build' field in story to 'more_widgets'.
|
102
|
+
- Set B-10147 to the status QA.
|
103
|
+
```
|
104
|
+
|
105
|
+
A Pull Request dialog box will be opened up for allowing other developers to review the code. QA testers can refer to the 'Build' field in the story to know which branch to refer to when testing the code. The story/defect is moved into the "test" column.
|
106
|
+
|
107
|
+
![Story in Test](doc_assets/qa.png "Story in Test")
|
108
|
+
|
109
|
+
|
110
|
+
The next steps after this are out of the scope of v1git tool. Testers must verify that the code meets the requirements. Other developers must peer review the Pull Request. Once QA signs off on the story meeting the requirements and the developers have approved and merged the Pull Request, the story can manually moved to "Done".
|
111
|
+
|
112
|
+
### Changelog
|
113
|
+
|
114
|
+
At the end of the sprint, when the stories/defects are all "Done" and merged into the develop branch, a release branch can be created. This branch can be deployed to a staging area and viewed by the stakeholders or the product owner during the end of sprint demo. When the stories meet the stakeholders/product owner's expectation, the story can be promoted to "Approved".
|
115
|
+
|
116
|
+
v1git can generate a changelog of defects and stories that have been merged into the release branch.
|
117
|
+
|
118
|
+
```shell
|
119
|
+
# creating release branch
|
120
|
+
$ git checkout develop
|
121
|
+
$ git pull
|
122
|
+
$ git checkout -b release/1.10.0
|
123
|
+
$ v1git changelog 1.9.0 release/1.10.0
|
124
|
+
[No Story ID] - Added logging system. - PR #123
|
125
|
+
[B-10147] - Testing1 (https://www14.v1host.com/v1sdktesting/story.mvc/Summary?oidToken=Story%3A37879&RoomContext=TeamRoom%3A37878) - PR #124
|
126
|
+
```
|
127
|
+
|
128
|
+
This changelog can be used in release notes or to distribute to the stakeholders/product owner to quickly see what was implemented and what was not.
|
129
|
+
|
130
|
+
Finally, release can be merged into master, a tag can be created and master can be merged back into develop.
|
131
|
+
|
132
|
+
```
|
133
|
+
$ git checkout master
|
134
|
+
$ git merge --no-ff release/1.10.0
|
135
|
+
$ git tag -a 1.10.0 # you can place the changelog in the annotations of the tagged version
|
136
|
+
$ git checkout develop
|
137
|
+
$ git merge --no-ff master
|
138
|
+
$ git push --all
|
139
|
+
```
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
## Development
|
144
|
+
|
145
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
146
|
+
|
147
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
148
|
+
|
149
|
+
## Contributing
|
150
|
+
|
151
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/malwarebytes/v1gittools. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
152
|
+
|
153
|
+
|
154
|
+
## License
|
155
|
+
|
156
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
157
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "v1gittools"
|
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
|
data/bin/setup
ADDED
Binary file
|
data/doc_assets/none.png
ADDED
Binary file
|
data/doc_assets/qa.png
ADDED
Binary file
|
data/exec/v1git
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#require 'bundler/setup'
|
4
|
+
require 'thor'
|
5
|
+
require 'v1gittools'
|
6
|
+
include V1gittools
|
7
|
+
|
8
|
+
|
9
|
+
# for dev purposes
|
10
|
+
Dir.chdir(ENV['v1git_cwd']) if ENV['v1git_cwd']
|
11
|
+
|
12
|
+
class V1git < Thor
|
13
|
+
option :title, :type => :string, :banner => 'v1|git', :aliases => :t, desc: '"git" or "versionone" title, defaults to v1 title [v1,git]'
|
14
|
+
desc 'changelog start end', 'Generates a changelog of merged PRs with VersionOne tags'
|
15
|
+
def changelog(start_snapshot, end_snapshot)
|
16
|
+
tool = ChangeLogTool.new options
|
17
|
+
tool.generate_changelog(start_snapshot,end_snapshot)
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'develop v1_story_id [branch_name]', 'Move a v1 story into "in-progress" and start a new branch off of develop. If branch is not specified, defaults to story id name'
|
21
|
+
def develop(v1_story_id, branch_name = nil)
|
22
|
+
tool = DevelopStoryTool.new
|
23
|
+
tool.develop(v1_story_id,branch_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'qa', 'Moves the current story/branch to "In-Test" and creates a PR in github'
|
27
|
+
def qa
|
28
|
+
tool = QATool.new
|
29
|
+
tool.qa
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
desc 'init', 'Initializes repo settings for v1git'
|
34
|
+
def init
|
35
|
+
V1gittools::generate_repo_config V1gittools::repo_config_path
|
36
|
+
V1gittools::initialize_github
|
37
|
+
|
38
|
+
puts "Running validations... (if any of these fail, run '#{$0} validate' after correcting the issue.)"
|
39
|
+
V1gittools::validate_config
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'validate', 'Validates config and connections and verifies if everything works.'
|
43
|
+
def validate
|
44
|
+
V1gittools::validate_config
|
45
|
+
|
46
|
+
puts "All validations pass! You're ready to run v1git on your project!"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
V1git.start(ARGV)
|
data/lib/v1gittools.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'v1gittools/version'
|
2
|
+
require 'v1gittools/config'
|
3
|
+
require 'v1gittools/base_tool'
|
4
|
+
require 'v1gittools/change_log_tool'
|
5
|
+
require 'v1gittools/develop_story_tool'
|
6
|
+
require 'v1gittools/qa_tool'
|
7
|
+
require 'git'
|
8
|
+
require 'github_api'
|
9
|
+
require 'versionone_sdk'
|
10
|
+
|
11
|
+
module V1gittools
|
12
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module V1gittools
|
2
|
+
class BaseTool
|
3
|
+
attr_reader :v1, :git, :args, :config, :repo_config, :github
|
4
|
+
|
5
|
+
def initialize args=nil
|
6
|
+
@args = args
|
7
|
+
|
8
|
+
|
9
|
+
@config = V1gittools::config
|
10
|
+
check_proper_init
|
11
|
+
@repo_config = V1gittools::repo_config
|
12
|
+
git_root_path = `git rev-parse --show-toplevel`.strip
|
13
|
+
@git = Git.open(git_root_path)
|
14
|
+
@v1 = VersiononeSdk::Client.new(@config[:v1config])
|
15
|
+
@github = Github.new(Hash[@config[:github].map{ |k, v| [k.to_sym, v] }])
|
16
|
+
end
|
17
|
+
|
18
|
+
def check_proper_init
|
19
|
+
if @config[:github] && @config[:github][:oauth_token] == 'AUTOGENERATE'
|
20
|
+
puts "v1git has not been setup for this repository yet. Please run #{$0} init to initialize and setup v1git."
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module V1gittools
|
2
|
+
class ChangeLogTool < V1gittools::BaseTool
|
3
|
+
|
4
|
+
def generate_changelog (start_snapshot, end_snapshot)
|
5
|
+
@git.log(999999).between(start_snapshot,end_snapshot).each do |commit|
|
6
|
+
if match = commit.message.match(/Merge pull request (#\d+) from ((.*?)\n\n([^\n]*).*)/m)
|
7
|
+
pr_id, full_message, branch_name, pr_title = match.captures
|
8
|
+
|
9
|
+
# puts "pr_id = #{pr_id}"
|
10
|
+
# puts "branch_name = #{branch_name}"
|
11
|
+
# puts "pr_title = #{pr_title}"
|
12
|
+
# puts "full_message = #{full_message}"
|
13
|
+
prefixes = @config[:v1config][:type_prefixes].keys.join('|')
|
14
|
+
stories = full_message.scan(/\b([#{prefixes}]-\d{4,6})\b/m)
|
15
|
+
v1_stories = {}
|
16
|
+
|
17
|
+
stories.each do |story_array|
|
18
|
+
story = story_array[0]
|
19
|
+
|
20
|
+
v1_stories[story] = @v1.getAsset(story)
|
21
|
+
end
|
22
|
+
|
23
|
+
v1_stories.each do |story_id, v1_story|
|
24
|
+
if v1_story.nil?
|
25
|
+
v1_title = 'Cannot Find Story in V1!'
|
26
|
+
v1_url = ''
|
27
|
+
|
28
|
+
puts "WARNING: Cannot Find Story [#{story_id}] in V1!"
|
29
|
+
else
|
30
|
+
v1_title = v1_story.getProp('Name')
|
31
|
+
v1_url = "https://#{config[:v1config][:hostname]}/#{config[:v1config][:instance]}/story.mvc/Summary?oidToken=#{v1_story.getProp(:_sObjectType__id)}:#{v1_story.getProp(:_iObjectId__id)}"
|
32
|
+
end
|
33
|
+
|
34
|
+
if @args[:title] == 'git'
|
35
|
+
title = pr_title
|
36
|
+
else
|
37
|
+
title = v1_title
|
38
|
+
end
|
39
|
+
puts "[#{story_id}] - #{title} (#{v1_url}) - PR #{pr_id} "
|
40
|
+
end
|
41
|
+
|
42
|
+
if v1_stories.empty?
|
43
|
+
puts "[No Story ID] - #{pr_title} - PR #{pr_id}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yaml'
|
3
|
+
require 'v1gittools/deep_hash_with_indifferent_access'
|
4
|
+
require 'socket'
|
5
|
+
require 'faraday'
|
6
|
+
require 'io/console'
|
7
|
+
|
8
|
+
module V1gittools
|
9
|
+
@config = nil
|
10
|
+
def self.config
|
11
|
+
if @config.nil?
|
12
|
+
# load config from file
|
13
|
+
load_config_file
|
14
|
+
end
|
15
|
+
|
16
|
+
@config
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.default_config_file
|
20
|
+
'~/.v1git.conf'
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.load_config_file filename=nil
|
24
|
+
filename = V1gittools::default_config_file if filename.nil?
|
25
|
+
filename = File.expand_path(filename)
|
26
|
+
unless File.exists?(filename)
|
27
|
+
raise "Config file #{filename} must exist and be properly configured before running this tool! Please refer to v1git.conf.example."
|
28
|
+
end
|
29
|
+
|
30
|
+
@config = DeepHashWithIndifferentAccess.convert_hash(YAML::load(File.open(filename)))
|
31
|
+
end
|
32
|
+
|
33
|
+
@repo_config = nil
|
34
|
+
def self.repo_config
|
35
|
+
if @repo_config.nil?
|
36
|
+
load_repo_config
|
37
|
+
end
|
38
|
+
|
39
|
+
@repo_config
|
40
|
+
end
|
41
|
+
|
42
|
+
@repo_config_path = nil
|
43
|
+
def self.repo_config_path
|
44
|
+
if @repo_config_path.nil?
|
45
|
+
git_root_path = `git rev-parse --show-toplevel`.strip
|
46
|
+
raise git_root_path if $?.to_i != 0
|
47
|
+
@repo_config_path = git_root_path + '/.git/v1git.conf'
|
48
|
+
end
|
49
|
+
|
50
|
+
@repo_config_path
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.load_repo_config
|
54
|
+
config_path = repo_config_path
|
55
|
+
|
56
|
+
unless File.exists?(config_path)
|
57
|
+
puts "v1git has not been setup for this repository yet. Please run #{$0} init to initialize and setup v1git."
|
58
|
+
exit
|
59
|
+
end
|
60
|
+
|
61
|
+
@repo_config = DeepHashWithIndifferentAccess.convert_hash(YAML::load(File.open(config_path)))
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.generate_repo_config config_path
|
65
|
+
return if File.exists?(config_path)
|
66
|
+
puts "NOTICE: v1git has never been used for this project before. Generating default config...\n\n"
|
67
|
+
|
68
|
+
`git status`
|
69
|
+
if $?.to_i == 128
|
70
|
+
puts "Your current working directory isn't even a git repository! Goodbye!"
|
71
|
+
exit
|
72
|
+
end
|
73
|
+
|
74
|
+
# guessing github address from git remote origin url
|
75
|
+
git_remote_url = `git config --get remote.origin.url`.strip
|
76
|
+
|
77
|
+
if git_remote_url.start_with?('http')
|
78
|
+
# http connection is easy!
|
79
|
+
github_url=git_remote_url.chomp('.git')
|
80
|
+
|
81
|
+
elsif git_remote_url.start_with?('git')
|
82
|
+
github_host, github_project_uri = git_remote_url.match(/git@(.+?):(.+?)\.git/).captures
|
83
|
+
github_url = "https://#{github_host}/#{github_project_uri}"
|
84
|
+
else
|
85
|
+
github_url=''
|
86
|
+
end
|
87
|
+
url_parts = github_url.split('/')
|
88
|
+
github_repo = url_parts.pop
|
89
|
+
github_owner = url_parts.pop
|
90
|
+
|
91
|
+
default_config_hash = {
|
92
|
+
github_url: github_url,
|
93
|
+
github_owner: github_owner,
|
94
|
+
github_repo: github_repo,
|
95
|
+
develop_branch: 'develop',
|
96
|
+
github_remote: 'origin',
|
97
|
+
branches: {}
|
98
|
+
}
|
99
|
+
|
100
|
+
write_repo_config(config_path,default_config_hash)
|
101
|
+
|
102
|
+
if github_url == ''
|
103
|
+
raise "ERROR: Couldn't guess github config options. Please modify github config options manually in '#{config_path}'"
|
104
|
+
else
|
105
|
+
puts "Config generated with the following guessed/assumed values:\n\n"
|
106
|
+
puts "Develop branch: #{default_config_hash[:develop_branch]}"
|
107
|
+
puts "github_url: #{github_url}\n\n"
|
108
|
+
puts "github_remote: #{default_config_hash[:github_remote]}\n\n"
|
109
|
+
puts "github_owner: #{github_owner}\n\n"
|
110
|
+
puts "github_repo: #{github_repo}\n\n"
|
111
|
+
puts "If these values are not correct, please correct it in \"#{config_path}\".\n\n"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.update_repo_config
|
116
|
+
write_repo_config(V1gittools::repo_config_path, @repo_config)
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.write_repo_config config_path, config_hash
|
120
|
+
File.open(config_path,'w') do |f|
|
121
|
+
f.write("# This file is autogenerated and updated by v1git! Comments and formatting will be lost!\n\n")
|
122
|
+
f.write(config_hash.to_yaml)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.initialize_github
|
127
|
+
if config[:github] && config[:github][:oauth_token] == 'AUTOGENERATE'
|
128
|
+
print 'V1git requires a github access token for creating PRs. We will be requesting github for an access token'
|
129
|
+
puts 'using your credentials. This will only be a one time operation.'
|
130
|
+
if config[:github] && config[:github][:endpoint]
|
131
|
+
endpoint = config[:github][:endpoint]
|
132
|
+
puts "\nV1Git is configured to connect to: #{endpoint}\n"
|
133
|
+
else
|
134
|
+
endpoint = nil
|
135
|
+
puts "\nV1Git is configured to connect to: api.github.com\n\n"
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
print "\nGithub Username: "
|
140
|
+
username = STDIN.gets.chomp
|
141
|
+
print "Github Password (no echo): "
|
142
|
+
password = STDIN.noecho(&:gets).chomp
|
143
|
+
puts "\nAutogenerating github repo authtoken with #{username} credentials..."
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
gh = Github.new basic_auth: "#{username}:#{password}", endpoint: endpoint
|
148
|
+
token = gh.oauth.create scopes: ['repo'], note: "v1gittools token for computer #{Socket.gethostname}"
|
149
|
+
|
150
|
+
|
151
|
+
# load the file as a string
|
152
|
+
config_data = File.read(File.expand_path(default_config_file))
|
153
|
+
# globally substitute "install" for "latest"
|
154
|
+
filtered_data = config_data.gsub(/oauth_token: *"AUTOGENERATE"/, "oauth_token: \"#{token.token}\"")
|
155
|
+
# open the file for writing
|
156
|
+
File.open(File.expand_path(default_config_file), "w") do |f|
|
157
|
+
f.write(filtered_data)
|
158
|
+
end
|
159
|
+
|
160
|
+
@config = nil # need to force reload of the config params after writing to it.
|
161
|
+
|
162
|
+
puts "Credential generated and written to #{default_config_file} config file."
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.validate_config
|
167
|
+
# write some checks here to make sure that
|
168
|
+
# - v1 works
|
169
|
+
response = Faraday.get "https://#{config[:v1config][:hostname]}/#{config[:v1config][:instance]}/Account.mvc/LogIn"
|
170
|
+
|
171
|
+
if response.status == 200
|
172
|
+
puts 'Validating VersionOne URL... PASSED'
|
173
|
+
else
|
174
|
+
puts 'Validating VersionOne URL... FAILED'
|
175
|
+
puts 'Please verify that the VersionOne (v1config block) hostname and instance is correct.'
|
176
|
+
exit
|
177
|
+
end
|
178
|
+
|
179
|
+
print 'Validating VersionOne credentials... '
|
180
|
+
response = VersiononeSdk::Client.new(config[:v1config]).getAssets('State') # run a test query
|
181
|
+
|
182
|
+
if response.empty?
|
183
|
+
puts 'FAILED'
|
184
|
+
puts 'Please validate that the VersionOne credentials is correct (you may need to regenerate a new token).'
|
185
|
+
else
|
186
|
+
puts 'PASSED'
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
# - git works
|
192
|
+
`git status`
|
193
|
+
if $?.to_i == 128
|
194
|
+
puts 'Validating git config... FAILED'
|
195
|
+
puts "Your current working directory isn't even a git repository! Please make sure you're in the correct directory."
|
196
|
+
exit
|
197
|
+
else
|
198
|
+
puts 'Validating git config... PASSED'
|
199
|
+
end
|
200
|
+
|
201
|
+
# - github works
|
202
|
+
print 'Validating github endpoint... '
|
203
|
+
response = Faraday.get config[:github][:endpoint]
|
204
|
+
begin
|
205
|
+
json_response = JSON.parse(response.body)
|
206
|
+
rescue
|
207
|
+
puts 'FAILED'
|
208
|
+
puts 'Please verify that github[:endpoint] config option is set correctly. Could not contact github.'
|
209
|
+
exit
|
210
|
+
end
|
211
|
+
|
212
|
+
if json_response['message'] == 'Must authenticate to access this API.' || json_response['current_user_url'] != nil
|
213
|
+
puts 'PASSED'
|
214
|
+
else
|
215
|
+
puts 'FAILED'
|
216
|
+
puts 'Please verify that github[:endpoint] config option is set correctly. Could not contact github.'
|
217
|
+
exit
|
218
|
+
end
|
219
|
+
|
220
|
+
print 'Validating github credentials...'
|
221
|
+
github = Github.new(Hash[config[:github].map{ |k, v| [k.to_sym, v] }])
|
222
|
+
begin
|
223
|
+
response = github.pull_requests.list(repo_config[:github_owner], repo_config[:github_repo])
|
224
|
+
rescue ArgumentError
|
225
|
+
puts 'FAILED'
|
226
|
+
puts "Please verify that github_owner and github_repo in #{repo_config_path} is set."
|
227
|
+
exit
|
228
|
+
rescue Github::Error::NotFound
|
229
|
+
puts 'FAILED'
|
230
|
+
puts 'Please verify that your github endpoint and credentials are correct:'
|
231
|
+
puts ' - Did you put github.com creds when you meant to use github enterprise creds or visa versa?'
|
232
|
+
exit
|
233
|
+
rescue Github::Error::Unauthorized
|
234
|
+
puts 'FAILED'
|
235
|
+
puts "Please verify that the github oauth configuration setting is set correctly in #{default_config_file}"
|
236
|
+
exit
|
237
|
+
end
|
238
|
+
|
239
|
+
puts 'PASSED'
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'thor/core_ext/hash_with_indifferent_access'
|
2
|
+
# A hash with indifferent access and magic predicates.
|
3
|
+
#
|
4
|
+
# hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
|
5
|
+
#
|
6
|
+
# hash[:foo] #=> 'bar'
|
7
|
+
# hash['foo'] #=> 'bar'
|
8
|
+
# hash.foo? #=> true
|
9
|
+
#
|
10
|
+
module V1gittools
|
11
|
+
class DeepHashWithIndifferentAccess#:nodoc:
|
12
|
+
#HashWithIndifferentAccess does not convert the hash deeply so we must do this ourselves :(
|
13
|
+
def self.convert_hash(hash)
|
14
|
+
new_hash = Thor::CoreExt::HashWithIndifferentAccess.new
|
15
|
+
hash.each do |key, value|
|
16
|
+
if value.is_a? Hash
|
17
|
+
new_hash[key] = convert_hash value
|
18
|
+
elsif value.is_a? Array
|
19
|
+
new_hash[key] = convert_array value
|
20
|
+
else
|
21
|
+
new_hash[key] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
new_hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.convert_array(arr)
|
28
|
+
new_array = []
|
29
|
+
arr.each do |value|
|
30
|
+
if value.is_a? Hash
|
31
|
+
new_array << (convert_hash value)
|
32
|
+
elsif value.is_a? Array
|
33
|
+
new_array << (convert_array value)
|
34
|
+
else
|
35
|
+
new_array << value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
new_array
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module V1gittools
|
2
|
+
class DevelopStoryTool < V1gittools::BaseTool
|
3
|
+
|
4
|
+
def develop(v1_story_id, branch_name=nil)
|
5
|
+
branch_name ||= v1_story_id
|
6
|
+
v1_story = v1.getAsset(v1_story_id.dup)
|
7
|
+
if v1_story.nil?
|
8
|
+
puts "Sorry, story/defect #{v1_story_id} not found! Can't start development on unknown story/defect!"
|
9
|
+
return
|
10
|
+
end
|
11
|
+
|
12
|
+
git.checkout(repo_config[:develop_branch])
|
13
|
+
git.pull(repo_config[:github_remote], repo_config[:develop_branch])
|
14
|
+
git.branch(branch_name).checkout
|
15
|
+
|
16
|
+
repo_config[:branches][branch_name] = v1_story_id
|
17
|
+
V1gittools::update_repo_config
|
18
|
+
v1.updateAsset(v1_story.getProp(:_sObjectType__id), v1_story.getProp(:_iObjectId__id),'Status', config[:v1_story_statuses][:develop])
|
19
|
+
|
20
|
+
v1_story = v1.getAsset(v1_story_id.dup)
|
21
|
+
|
22
|
+
puts " - Switched to a new branch '#{branch_name}' based off of '#{repo_config[:develop_branch]}'."
|
23
|
+
puts " - Set #{v1_story_id} to the status #{v1_story.getProp(:"Status.Name")}.\n\n"
|
24
|
+
puts "Implement story/defect in branch (Don't forget to push!). When complete, use:\n\n"
|
25
|
+
puts " v1git qa\n\n"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'launchy'
|
2
|
+
module V1gittools
|
3
|
+
class QATool < V1gittools::BaseTool
|
4
|
+
def qa
|
5
|
+
branch = git.current_branch
|
6
|
+
v1_story_id = repo_config[:branches][branch.to_sym]
|
7
|
+
|
8
|
+
|
9
|
+
if v1_story_id.nil?
|
10
|
+
puts "This branch was not created with v1git tool. Cannot send this branch/story to QA."
|
11
|
+
return
|
12
|
+
end
|
13
|
+
v1_story = v1.getAsset(v1_story_id.dup)
|
14
|
+
if v1_story.nil?
|
15
|
+
puts "Sorry, story/defect #{v1_story_id} not found! Can't mark story for QA! Was the story deleted?"
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
if v1_story.asHash.keys.include?(:FixedInBuild)
|
20
|
+
build_field = 'FixedInBuild'
|
21
|
+
else
|
22
|
+
build_field = 'LastVersion'
|
23
|
+
end
|
24
|
+
|
25
|
+
v1.updateAsset(v1_story.getProp(:_sObjectType__id), v1_story.getProp(:_iObjectId__id),'Status', config[:v1_story_statuses][:test])
|
26
|
+
v1.updateAsset(v1_story.getProp(:_sObjectType__id), v1_story.getProp(:_iObjectId__id),build_field,branch)
|
27
|
+
|
28
|
+
v1_story = v1.getAsset(v1_story_id.dup)
|
29
|
+
|
30
|
+
begin
|
31
|
+
pr = @github.pull_requests.create(repo_config[:github_owner], repo_config[:github_repo],
|
32
|
+
{
|
33
|
+
title: "[#{v1_story_id}] #{v1_story.getProp(:Name)}",
|
34
|
+
body: "https://#{config[:v1config][:hostname]}/#{config[:v1config][:instance]}/story.mvc/Summary?oidToken=#{v1_story.getProp(:_sObjectType__id)}:#{v1_story.getProp(:_iObjectId__id)}",
|
35
|
+
head: branch,
|
36
|
+
base: repo_config[:develop_branch]
|
37
|
+
})
|
38
|
+
puts " - Created PR for this branch (PR ##{pr.number})."
|
39
|
+
rescue Github::Error::UnprocessableEntity => e
|
40
|
+
## TODO: Need to change all these errors to use error_messages instead of trying to analyze it manually.
|
41
|
+
if e.error_messages.include?({:resource=>"PullRequest", :code=>"custom", :message=>"No commits between develop and add_truth_statements"})
|
42
|
+
puts "Cannot create Pull Request! There have been no changes between #{branch} and #{repo_config[:develop_branch]}. Did you forget to commit your code?"
|
43
|
+
exit
|
44
|
+
elsif e.to_s.include?('field: head, code: invalid')
|
45
|
+
puts "Branch '#{branch}' does not exist on github. Did you forget to `git push`? Cannot create PR!"
|
46
|
+
exit
|
47
|
+
elsif e.to_s.include?('A pull request already exists for')
|
48
|
+
puts " - Pull Request for branch '#{branch}' already exists. Skipped creating PR."
|
49
|
+
else
|
50
|
+
raise e
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
puts " - Set 'Build' field in story to '#{branch}'."
|
57
|
+
puts " - Set #{v1_story_id} to the status '#{v1_story.getProp(:"Status.Name")}'.\n\n"
|
58
|
+
|
59
|
+
Launchy.open(pr.html_url) if pr
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/v1git.conf.example
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
v1config:
|
2
|
+
# The following is taken from the url of your v1 instance
|
3
|
+
# eg. https://www14.v1host.com/v1sdktesting/Default.aspx
|
4
|
+
hostname: 'www14.v1host.com'
|
5
|
+
instance: 'v1sdktesting'
|
6
|
+
|
7
|
+
# we can either use username/password or access_token - access_token is preferred
|
8
|
+
#username: ''
|
9
|
+
#password: ''
|
10
|
+
|
11
|
+
# You can get the access_token by mousing over your name in the upper-right
|
12
|
+
# corner of v1 and clicking "Applications". A dialog will walk you through
|
13
|
+
# generating an access_token.
|
14
|
+
access_token: ''
|
15
|
+
|
16
|
+
# probably do not need to change this.
|
17
|
+
type_prefixes:
|
18
|
+
D: 'Defect'
|
19
|
+
B: 'Story'
|
20
|
+
port: 443
|
21
|
+
protocol: 'https'
|
22
|
+
v1_story_statuses:
|
23
|
+
# go to #{YOURHOST}/rest-1.v1/Data/StoryStatus ie.
|
24
|
+
# https://www14.v1host.com/v1sdktesting/rest-1.v1/Data/StoryStatus
|
25
|
+
# to find the story status IDs for your v1 account.
|
26
|
+
# 'test' status probably won't be setup by default in your versionone
|
27
|
+
# account. You may need to have to add a column in kanban:
|
28
|
+
# https://community.versionone.com/Help-Center/Portfolio_Planning/Portfolio_Kanban/Working_with_Portfolio_Kanbans#Changing_Columns
|
29
|
+
# https://community.versionone.com/Help-Center/Setup_and_Administration/List_Type_Administration
|
30
|
+
# Story Status to set when running `v1git develop`
|
31
|
+
develop: 'StoryStatus:134' # In Progress
|
32
|
+
# Story status to set when running `v1git qa`
|
33
|
+
test: 'StoryStatus:1073' # In Test
|
34
|
+
github:
|
35
|
+
# If you're using github enterprise, point this to your domain name
|
36
|
+
# Typically, you'll need to add /api/v3 to it. Eg.
|
37
|
+
# https://github.mydomain.com/api/v3/
|
38
|
+
endpoint: https://api.github.com
|
39
|
+
|
40
|
+
# Auth token used to access github. Leave this as "AUTOGENERATE" to have v1git create one for you. The auth token
|
41
|
+
# needs to have permission to read/write code in repos
|
42
|
+
|
43
|
+
# if you use 2FA, you will need to generate the auth_token yourself. Please follow
|
44
|
+
# https://help.github.com/articles/creating-an-access-token-for-command-line-use/
|
45
|
+
# make sure to check "repo" permissions
|
46
|
+
oauth_token: "AUTOGENERATE"
|
data/v1gittools.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'v1gittools/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'v1gittools'
|
8
|
+
spec.version = V1gittools::VERSION
|
9
|
+
spec.authors = ['Jonathan Chan']
|
10
|
+
spec.email = ['jchan@malwarebytes.org']
|
11
|
+
|
12
|
+
spec.summary = %q{VersionOne/Git/Github Integration tools}
|
13
|
+
spec.description = %q{Tool(s) to integrate the VersionOne project managemente system with git/github developer workflow.}
|
14
|
+
spec.homepage = 'http://malwarebytes.org'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.bindir = 'exec'
|
27
|
+
spec.executables = spec.files.grep(%r{^exec}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ['lib']
|
29
|
+
|
30
|
+
spec.add_runtime_dependency 'git', '~> 1.3.0'
|
31
|
+
spec.add_runtime_dependency 'versionone_sdk', '~>0.2'
|
32
|
+
spec.add_runtime_dependency 'thor', '~> 0.19'
|
33
|
+
spec.add_runtime_dependency 'launchy', '~> 2.4', '>= 2.4.3'
|
34
|
+
spec.add_runtime_dependency 'github_api', '~> 0.14.4'
|
35
|
+
|
36
|
+
|
37
|
+
spec.add_development_dependency 'bundler', '~> 1.12'
|
38
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
39
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: v1gittools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonathan Chan
|
8
|
+
autorequire:
|
9
|
+
bindir: exec
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-08-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: git
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.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: 1.3.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: versionone_sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: thor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.19'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.19'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: launchy
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.4'
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 2.4.3
|
65
|
+
type: :runtime
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - "~>"
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '2.4'
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 2.4.3
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: github_api
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 0.14.4
|
82
|
+
type: :runtime
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 0.14.4
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: bundler
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.12'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.12'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: rake
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '10.0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '10.0'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: rspec
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '3.0'
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '3.0'
|
131
|
+
description: Tool(s) to integrate the VersionOne project managemente system with git/github
|
132
|
+
developer workflow.
|
133
|
+
email:
|
134
|
+
- jchan@malwarebytes.org
|
135
|
+
executables:
|
136
|
+
- v1git
|
137
|
+
extensions: []
|
138
|
+
extra_rdoc_files: []
|
139
|
+
files:
|
140
|
+
- ".gitignore"
|
141
|
+
- ".rspec"
|
142
|
+
- ".travis.yml"
|
143
|
+
- CODE_OF_CONDUCT.md
|
144
|
+
- Gemfile
|
145
|
+
- LICENSE.txt
|
146
|
+
- README.md
|
147
|
+
- Rakefile
|
148
|
+
- bin/console
|
149
|
+
- bin/setup
|
150
|
+
- doc_assets/in_progress.png
|
151
|
+
- doc_assets/none.png
|
152
|
+
- doc_assets/qa.png
|
153
|
+
- exec/v1git
|
154
|
+
- lib/v1gittools.rb
|
155
|
+
- lib/v1gittools/base_tool.rb
|
156
|
+
- lib/v1gittools/change_log_tool.rb
|
157
|
+
- lib/v1gittools/config.rb
|
158
|
+
- lib/v1gittools/deep_hash_with_indifferent_access.rb
|
159
|
+
- lib/v1gittools/develop_story_tool.rb
|
160
|
+
- lib/v1gittools/qa_tool.rb
|
161
|
+
- lib/v1gittools/version.rb
|
162
|
+
- v1git.conf.example
|
163
|
+
- v1gittools.gemspec
|
164
|
+
homepage: http://malwarebytes.org
|
165
|
+
licenses:
|
166
|
+
- MIT
|
167
|
+
metadata:
|
168
|
+
allowed_push_host: https://rubygems.org
|
169
|
+
post_install_message:
|
170
|
+
rdoc_options: []
|
171
|
+
require_paths:
|
172
|
+
- lib
|
173
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
174
|
+
requirements:
|
175
|
+
- - ">="
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: '0'
|
178
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
|
+
requirements:
|
180
|
+
- - ">="
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: '0'
|
183
|
+
requirements: []
|
184
|
+
rubyforge_project:
|
185
|
+
rubygems_version: 2.4.5.1
|
186
|
+
signing_key:
|
187
|
+
specification_version: 4
|
188
|
+
summary: VersionOne/Git/Github Integration tools
|
189
|
+
test_files: []
|