v1gittools 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.4
5
+ before_install: gem install bundler -v 1.12.5
@@ -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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in v1gittools.gemspec
4
+ gemspec
@@ -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.
@@ -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
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -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
@@ -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
Binary file
Binary file
@@ -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)
@@ -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
@@ -0,0 +1,3 @@
1
+ module V1gittools
2
+ VERSION = "0.1.1"
3
+ end
@@ -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"
@@ -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: []