git-issues 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2b352d1ae23ec5fc4f51ec2d58eb42d57a6eee6e
4
+ data.tar.gz: 8d8e773f36ebc8cbde198cb1f26be428a91f7fb7
5
+ SHA512:
6
+ metadata.gz: 9de948b03cd307f3a3baf6bfe32ae9960339d1298e79708fe139f9421608d1e8110287e09725fe9cfd03f45e5a6544c713cb1b2ac31607e8f967c389a8c46117
7
+ data.tar.gz: 77c372dd04795a028f8399c2dd5524461124678e6db37480d58fe4443f1f7f27bb328531bca20e939d56ca2dca0e885467f3443a7d19ed44e97ab71e4192c827
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ script: "bundle exec rake"
3
+ rvm:
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ - "jruby-19mode"
7
+ - "rbx-2"
data/CHANGELOG.md ADDED
@@ -0,0 +1,55 @@
1
+ # Changelog
2
+
3
+ ## 0.1.4
4
+
5
+ * feature: add https-style urls to github and bitbucket
6
+ * bugfix: check if login config was created before using it
7
+ * bugfix: fix rubinius tests and support
8
+
9
+ ## 0.1.3
10
+
11
+ * feature: close issues (github, bitbucket, gitlab)
12
+ * feature: reopen issues (github, bitbucket, gitlab)
13
+ * improvement: check response when closing issue (github, bitbucket)
14
+ * bugfix: add minitest dependency to Gemfile
15
+ * bugfix: update travis ci tests to 2.1.1
16
+ * bugfix: update gemfile.lock
17
+ * bugfix: remove ruby 1.9.2 from the list of supported platforms
18
+ * bugfix: add missing rake to gemfile
19
+
20
+ ## 0.1.2
21
+
22
+ * feature: add an option --short to git issues list to show a shorter overview
23
+ * feature: show description for issues when listing them
24
+ * feature: support ssh://-style urls
25
+ * feature: gitlab: add support for creating issues + clarify delete actoin
26
+ * feature: add support for listing issues from gitlab
27
+ * feature: support adding and deleting issues to/from github
28
+ * feature: added github provider
29
+ * feature: add configuration file
30
+ * feature: added add, delete, cli
31
+ * feature: list issues
32
+ * improvement: give feedback when an issue is created
33
+ * improvement: expose the provider of gitlab, bitbucket, and github (useful on cli)
34
+ * improvement: run the `git issues list` by default if no argument is provided
35
+ * improvement: added gemfile and lock for dependency tracking
36
+ * improvement: sort issues list by id number (decreasing)
37
+ * improvement: add support for creating issues without a body
38
+ * improvement: add links to api documentations to all providers
39
+ * improvement: gitlab: only detect project id once, then reuse it
40
+ * improvement: unified github handling under octokit api
41
+ * improvement: save configuration options under the groupname of each provider
42
+ * improvement: limit file permissions for config file (default: ~/.git-issues.conf to 0600)
43
+ * improvement: let bitbucket use oauth token and secret
44
+ * improvement: modular structure for providers created
45
+ * bugfix: support old bitbucket ssh-style urls
46
+ * bugfix: add missing requirement of zlog to gemspec
47
+ * bugfix: gitlab: show only non-closed issues by default
48
+ * bugfix: gitlab: use internal id (iid) instead of id for issue identification
49
+ * bugfix: show all issues in github if called with --all
50
+ * bugfix: set the oauth token for github via access_token field
51
+ * bugfix: moved bitbucket gem to inside the class\n\nit will otherwise conflict with later github gem on nokogiri dependency
52
+ * bugfix: updated bitbucket oauth authentication to work correctly with consumer key and secret
53
+ * bugfix: expand file path before writing
54
+ * bugfix: abort oauth authentication if token is empty, not nil
55
+ * bugfix: write configuration to file correctly
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+ gem 'minitest'
3
+ gem 'rake'
4
+ gem 'thor'
5
+ gem 'parseconfig'
6
+ gem 'bitbucket_rest_api', '>= 0.1.5'
7
+ gem 'octokit'
8
+ gem 'gitlab'
9
+ gem 'zlog'
10
+ gem 'highline'
data/Gemfile.lock ADDED
@@ -0,0 +1,62 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.3.6)
5
+ bitbucket_rest_api (0.1.5)
6
+ faraday (~> 0.8.1)
7
+ faraday_middleware (~> 0.9.0)
8
+ hashie (~> 2.0.5)
9
+ multi_json (~> 1.3)
10
+ nokogiri (>= 1.5.2)
11
+ simple_oauth
12
+ faraday (0.8.9)
13
+ multipart-post (~> 1.2.0)
14
+ faraday_middleware (0.9.1)
15
+ faraday (>= 0.7.4, < 0.10)
16
+ gitlab (3.0.0)
17
+ httparty
18
+ hashie (2.0.5)
19
+ highline (1.6.21)
20
+ httparty (0.13.1)
21
+ json (~> 1.8)
22
+ multi_xml (>= 0.5.2)
23
+ json (1.8.1)
24
+ json (1.8.1-java)
25
+ little-plugger (1.1.3)
26
+ logging (1.8.2)
27
+ little-plugger (>= 1.1.3)
28
+ multi_json (>= 1.8.4)
29
+ mini_portile (0.5.3)
30
+ minitest (5.3.3)
31
+ multi_json (1.10.0)
32
+ multi_xml (0.5.5)
33
+ multipart-post (1.2.0)
34
+ nokogiri (1.6.2)
35
+ mini_portile (~> 0.5.2)
36
+ nokogiri (1.6.2-java)
37
+ octokit (3.1.0)
38
+ sawyer (~> 0.5.3)
39
+ parseconfig (1.0.4)
40
+ rake (10.3.1)
41
+ sawyer (0.5.4)
42
+ addressable (~> 2.3.5)
43
+ faraday (~> 0.8, < 0.10)
44
+ simple_oauth (0.2.0)
45
+ thor (0.19.1)
46
+ zlog (0.9.0)
47
+ logging (~> 1.8.1)
48
+
49
+ PLATFORMS
50
+ java
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ bitbucket_rest_api (>= 0.1.5)
55
+ gitlab
56
+ highline
57
+ minitest
58
+ octokit
59
+ parseconfig
60
+ rake
61
+ thor
62
+ zlog
data/LICENSE ADDED
@@ -0,0 +1,176 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # git-issue
2
+ [![Build Status](https://travis-ci.org/gittalk/git-issues.png)](https://travis-ci.org/gittalk/git-issues)
3
+
4
+ ✔ supported, ∅ not supported by this service, ✘ not supported by git-issues (yet)
5
+
6
+ * list issues (bitbucket ✔, github ✔, gitlab ✔)
7
+ * create issue (bitbucket ✔, github ✔, gitlab ✔)
8
+ * close issue (bitbucket ✔, github ✔, gitlab ✔)
9
+ * reopen issue (bitbucket ✔, github ✔, gitlab ✔)
10
+ * delete issue (bitbucket ✔, github ∅, gitlab ∅)
11
+ * integrated cli (needs improvement)
12
+
13
+ Why? There's excellent tools for GitHub out there, but I found integration for BitBucket or Gitlab lacking.
14
+
15
+ # Installation
16
+
17
+ You can build the gem and install it on your system:
18
+
19
+ bundle install
20
+ bundle exec gem build git-issues.gemspec
21
+ bundle exec gem install *gem
22
+
23
+ Requires ruby >= 1.9.3.
24
+
25
+ # Usage
26
+
27
+ ## Setup a cloned repo
28
+
29
+ $ cd /path/to/your/repo
30
+ $ git issues
31
+
32
+ You will be asked for your login credentials. Further information is described in section security.
33
+
34
+ ## Get issues
35
+
36
+ $ git issues
37
+ -- Using: git@github.com:gittalk/git-issues.git
38
+ 22 | open | Usage instructions
39
+ 20 | open | Display instructions for setup when the command is run the first time
40
+ 18 | open | clearly describe OAuth service when requesting the token e.g. Github OAuth
41
+
42
+ ## Add new issues
43
+
44
+ The format is `add <title> [<content>]`
45
+
46
+ $ git issues add "Git issue title" "Git issue descripton"
47
+ -- Using: git@github.com:gittalk/git-issues.git
48
+ ++ created issue 24
49
+
50
+ ## Closing or deleting issues
51
+
52
+ Close an issue with `close <id>`:
53
+
54
+ $ git issues close 24
55
+
56
+ Use `delete <id>` to remove an issue. It is currently only supported by Bitbucket repos.
57
+
58
+ $ git issues delete 24
59
+
60
+ # Security
61
+
62
+ Your login information will be stored by default in: `~/.git-issues.conf`. Make sure to keep this file's permissions limited (e.g. `0600`).
63
+
64
+ If you can, **do not use username and password** for login. Use OAuth instead. If you use username and password, both will be stored in the configuration file in an unencrypted state, which is highly discouraged.
65
+
66
+ How to get OAuth tokens:
67
+
68
+ * GitHub: Go to _Account Settings_, _Applications_, and click on _Create a new Personal Access Token_. Enter `git-issues` as the description. Use this token during first login.
69
+ * [OAuth on BitBucket](https://confluence.atlassian.com/display/BITBUCKET/OAuth+on+Bitbucket)
70
+ * Gitlab: Click on your profile, _Profile Settings_, _Account_ and use the _Private token_ found here.
71
+
72
+
73
+ # Contributing
74
+
75
+ Any contributions are welcome!
76
+
77
+ 1. Fork the repository on Github
78
+ 2. Create a named feature branch (like `add_component_x`)
79
+ 3. Write you change
80
+ 4. Write tests for your change (if applicable)
81
+ 5. Run the tests, ensuring they all pass
82
+ 6. Submit a Pull Request using Github
83
+
84
+
85
+ # License and Author
86
+
87
+ Author: Dominik Richter <dominik.richter@googlemail.com>
88
+
89
+ Licensed under the Apache License, Version 2.0 (the "License");
90
+ you may not use this file except in compliance with the License.
91
+ You may obtain a copy of the License at
92
+
93
+ http://www.apache.org/licenses/LICENSE-2.0
94
+
95
+ Unless required by applicable law or agreed to in writing, software
96
+ distributed under the License is distributed on an "AS IS" BASIS,
97
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98
+ See the License for the specific language governing permissions and
99
+ limitations under the License.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rake/testtask'
4
+
5
+ # Immediately sync all stdout so that tools like buildbot can
6
+ # immediately load in the output.
7
+ $stdout.sync = true
8
+ $stderr.sync = true
9
+
10
+ # Change to the directory of this file.
11
+ Dir.chdir(File.expand_path("../", __FILE__))
12
+
13
+ # This installs the tasks that help with gem creation and
14
+ # publishing.
15
+ Bundler::GemHelper.install_tasks
16
+
17
+ Rake::TestTask.new do |t|
18
+ t.libs << "test"
19
+ t.pattern = "test/classes/*_spec.rb"
20
+ t.verbose = true
21
+ end
22
+
23
+ task default: [:test]
data/bin/git-issues ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thor'
4
+ require 'zlog'
5
+ require 'git-issues'
6
+
7
+ Zlog.init_stdout loglevel: :debug
8
+
9
+ class GitIssuesCLI < Thor
10
+ Log = Logging.logger[self]
11
+ GI = GitIssues.new
12
+
13
+ desc "list", "list issues of a repository"
14
+ method_options :all => :boolean
15
+ method_options :short => :boolean
16
+ def list opts = {}
17
+ o = ({
18
+ all: (options.all)
19
+ }).merge(opts)
20
+
21
+ issues = repo.issues_list o
22
+ issues.
23
+ sort_by{|i| i['number']}.
24
+ reverse.
25
+ each do |i|
26
+ puts "%-3i | %-8s | %s" % [ i['number'], i['state'], i['title'] ]
27
+ if not options.short
28
+ puts " %s" % i['description'] if i['description'].to_s.length > 0
29
+ puts ""
30
+ end
31
+ end
32
+ nil
33
+ end
34
+
35
+ desc "add <title> [<content>]", "add a new issue"
36
+ def add( title, content = nil )
37
+ r = repo.issue_create title, content
38
+ Log.ok "created issue #{r}" if r.instance_of?(Fixnum)
39
+ end
40
+
41
+ desc "reopen <id>", "reopen an issue"
42
+ def reopen( id )
43
+ is_ok = repo.issue_reopen id
44
+ Log.ok "reopened issue #{id}" if is_ok
45
+ end
46
+
47
+ desc "delete <id>", "delete an issue"
48
+ def delete( id )
49
+ repo.issue_delete id
50
+ end
51
+
52
+ desc "close <id>", "close an issue"
53
+ def close( id )
54
+ is_closed = repo.issue_close id
55
+ Log.ok "closed issue #{id}" if is_closed
56
+ end
57
+
58
+ desc "cli", "open a cli"
59
+ def cli
60
+ require 'pry'
61
+ binding.pry
62
+ end
63
+
64
+ default_task "list"
65
+
66
+ private
67
+
68
+ def repo path = '.'
69
+ @repo ||= getRepo path
70
+ end
71
+
72
+ def getRepo path
73
+ repos = GI.gitReposFor '.'
74
+ if repos.empty?
75
+ Log.abort "No known repositories found."
76
+ end
77
+ Log.info "Using: #{repos.first.repo_url}"
78
+ repos.first
79
+ end
80
+
81
+ end
82
+
83
+ GitIssuesCLI.start(ARGV)
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+ require 'git-issues'
5
+
6
+ spec = Gem::Specification.new do |s|
7
+ s.name = 'git-issues'
8
+ s.licenses = ['MPLv2']
9
+ s.version = GitIssues.new.version
10
+ s.platform = Gem::Platform::RUBY
11
+ s.summary = "manage git issues in cli"
12
+ s.description = s.summary
13
+ s.author = "Dominik Richter"
14
+ s.email = "dominik.richter@googlemail.com"
15
+ #s.homepage = 'n/a'
16
+
17
+ s.add_dependency 'thor'
18
+ s.add_dependency 'parseconfig'
19
+ s.add_dependency 'bitbucket_rest_api', '>= 0.1.5'
20
+ s.add_dependency 'octokit'
21
+ s.add_dependency 'gitlab'
22
+ s.add_dependency 'zlog'
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,106 @@
1
+ require 'highline/import'
2
+ require 'parseconfig'
3
+ require 'zlog'
4
+
5
+ module LoginHelper
6
+ Log = Logging.logger[self]
7
+
8
+ def config_file
9
+ @config_file ||= "~/.git-issues.conf"
10
+ end
11
+
12
+ def oauth_token
13
+ @oauth_token ||= get_secret("Enter OAuth consumer secret: ", "oauth-token")
14
+ end
15
+
16
+ def oauth_consumer_key
17
+ @oauth_consumer_key ||= get_open("Enter OAuth consumer key: ", "oauth-consumer-key")
18
+ end
19
+
20
+ def oauth_consumer_secret
21
+ @oauth_consumer_secret ||= get_secret("Enter OAuth consumer secret: ", "oauth-consumer-secret")
22
+ end
23
+
24
+ def oauth_consumer_key_and_secret
25
+ t = oauth_consumer_key
26
+ ( t.empty? ) ? [nil,nil] : [t, oauth_consumer_secret]
27
+ end
28
+
29
+ def user
30
+ @user ||= get_open("Enter username: ", "username")
31
+ end
32
+
33
+ def password
34
+ @password ||= get_secret("Enter password for user '#{user}': ", "secret")
35
+ end
36
+
37
+ def get_or_set field, &block
38
+ # all configuration fields belong to a group
39
+ group = ( self.respond_to?('name') ) ? name : nil
40
+ # get the field from file...
41
+ f = get_from_config_file field, group
42
+ # ... or ask the user to provide it and save it then
43
+ if f.nil?
44
+ f = block.()
45
+ save_to_config_file field, f, group
46
+ end
47
+ f
48
+ end
49
+
50
+ private
51
+
52
+ def get_conf
53
+ conf_path = File::expand_path(config_file)
54
+ if not File::exists?(conf_path)
55
+ Log.info "Creating configuration in #{conf_path}"
56
+ File::write(conf_path,"")
57
+ elsif not File::file?(conf_path)
58
+ Log.abort "Can't write configuration file to #{conf_path}"
59
+ end
60
+ ParseConfig.new(conf_path)
61
+ end
62
+
63
+ def get_from_config_file field, group = nil
64
+ config = get_conf
65
+ return config.params[field] if (group.nil?)
66
+ (config.params[group].nil?) ? nil : config.params[group][field]
67
+ end
68
+
69
+ def save_to_config_file field, value, group = nil
70
+ config = get_conf
71
+ # add the field/value/group
72
+ if group.nil?
73
+ config.add field, value
74
+ else
75
+ config.add_to_group group, field, value.to_s
76
+ end
77
+ # get the path
78
+ path = File::expand_path(config_file)
79
+ # write the contents to the file
80
+ file = File.open(path, 'w+')
81
+ config.write(file)
82
+ file.close
83
+ # check if the file exists
84
+ if not File.file?(path)
85
+ Log.error "Couldn't save login configuration to #{path}."
86
+ else
87
+ # limit permissions to this file
88
+ File.chmod(0600, path)
89
+ end
90
+ end
91
+
92
+ def get_open prompt, field
93
+ get_or_set field do
94
+ cli.ask(prompt){|q| q.echo = true}.strip
95
+ end
96
+ end
97
+
98
+ def get_secret prompt, field
99
+ get_or_set field do
100
+ cli.ask(prompt){|q| q.echo = '*'}.strip
101
+ end
102
+ end
103
+
104
+ def cli; @cli ||= HighLine.new; end
105
+
106
+ end
@@ -0,0 +1,85 @@
1
+ require 'bitbucket_rest_api'
2
+
3
+ # API documentation:
4
+ # https://confluence.atlassian.com/display/BITBUCKET/Use+the+Bitbucket+REST+APIs
5
+
6
+ class RepoProvider::Bitbucket
7
+
8
+ URL_PATTERNS = [
9
+ /^git@bitbucket.org:(?<user>[^\/]+)\/(?<repo>.+)\.git$/,
10
+ /^(ssh:\/\/)?git@bitbucket.org\/(?<user>[^\/]+)\/(?<repo>.+)\.git$/,
11
+ /^https:\/\/([^@]+@)?bitbucket.org\/(?<user>[^\/]+)\/(?<repo>.+)\.git$/
12
+ ]
13
+
14
+ def self.get_repo url
15
+ # find the url pattern that matches the url
16
+ URL_PATTERNS.map{|p| p.match url }.compact.first
17
+ end
18
+
19
+ def issues_list opts = {}
20
+ # get issues for this repo
21
+ issues = bitbucket.issues.list_repo(repo['user'], repo['repo'])
22
+ # filter closed issues if the user doesn't want all
23
+ if not opts[:all]
24
+ issues = issues.find_all{|i|
25
+ 'resolved' != i['status']
26
+ }
27
+ end
28
+ # return issues
29
+ format_issues( issues )
30
+ end
31
+
32
+ def issue_create title, content
33
+ ret = bitbucket.issues.create( repo['user'], repo['repo'], {
34
+ title: title,
35
+ content: content
36
+ })
37
+ id = ret['resource_uri'].match(/[0-9]+$/)
38
+ id && id[0].to_i || -1
39
+ end
40
+
41
+ def issue_reopen id
42
+ res = bitbucket.issues.edit( repo['user'], repo['repo'], id, {status: 'open'})
43
+ res['status'] == 'open'
44
+ end
45
+
46
+ def issue_close id
47
+ res = bitbucket.issues.edit( repo['user'], repo['repo'], id, {status: 'resolved'})
48
+ res['status'] == 'resolved'
49
+ end
50
+
51
+ def issue_delete id
52
+ bitbucket.issues.delete( repo['user'], repo['repo'], id)
53
+ end
54
+
55
+ def provider
56
+ bitbucket
57
+ end
58
+
59
+ private
60
+
61
+ def format_issues is
62
+ Array(is).map do |i|
63
+ i['number'] = i['local_id']
64
+ i['state'] = i['status']
65
+ i
66
+ end
67
+ end
68
+
69
+ def bitbucket
70
+ init_bitbucket if @bitbucket.nil?
71
+ @bitbucket
72
+ end
73
+
74
+ def init_bitbucket
75
+ ot,os = oauth_consumer_key_and_secret
76
+ # get configuration from oauth token and secret
77
+ if( not ot.nil? and not os.nil? )
78
+ @bitbucket = BitBucket.new client_id: ot, client_secret: os
79
+ else
80
+ # use login and password otherwise
81
+ @bitbucket = BitBucket.new login: user, password: password
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,79 @@
1
+ require 'octokit'
2
+
3
+ # API documentation:
4
+ # http://developer.github.com/v3/
5
+
6
+ class RepoProvider::Github
7
+
8
+ URL_PATTERNS = [
9
+ /^(ssh:\/\/)?git@github.com:(?<user>[^\/]+)\/(?<repo>.+)\.git$/,
10
+ /^https:\/\/github.com\/(?<user>[^\/]+)\/(?<repo>.+)\.git$/
11
+ ]
12
+
13
+ def self.get_repo url
14
+ # find the url pattern that matches the url
15
+ URL_PATTERNS.map{|p| p.match url }.compact.first
16
+ end
17
+
18
+ def issues_list opts = {}
19
+ # get open issues for this repo
20
+ issues = github.issues gh_repo
21
+ # get all closed issues if required
22
+ issues += github.issues( gh_repo, state: 'closed' ) if opts[:all]
23
+ # return issues
24
+ format_issues( issues )
25
+ end
26
+
27
+ def issue_create title, content
28
+ ret = github.create_issue gh_repo, title, content
29
+ ret.attrs && ret.attrs[:number] || -1
30
+ end
31
+
32
+ def issue_reopen id
33
+ ret = github.reopen_issue gh_repo, id
34
+ ret[:state] == 'open'
35
+ end
36
+
37
+ def issue_close id
38
+ ret = github.close_issue gh_repo, id
39
+ ret[:state] == 'closed'
40
+ end
41
+
42
+ def issue_delete id
43
+ log.warn "You can't delete issues on GitHub. Please close/resolve them instead."
44
+ end
45
+
46
+ def provider
47
+ github
48
+ end
49
+
50
+ private
51
+
52
+ def gh_repo
53
+ "#{repo['user']}/#{repo['repo']}"
54
+ end
55
+
56
+ def format_issues is
57
+ Array(is).map do |i|
58
+ i['description'] = i['body']
59
+ i
60
+ end
61
+ end
62
+
63
+ def github
64
+ init_github if @github.nil?
65
+ @github
66
+ end
67
+
68
+ def init_github
69
+ ot = oauth_token
70
+ # get configuration from oauth token and secret
71
+ if( not ot.nil? )
72
+ @github = Octokit::Client.new access_token: ot
73
+ else
74
+ # use login and password otherwise
75
+ @github = Octokit::Client.new login: user, password: password
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,77 @@
1
+ require 'gitlab'
2
+
3
+ # API documentation:
4
+ # http://api.gitlab.org/
5
+
6
+ class RepoProvider::Gitlab
7
+
8
+ URL_PATTERNS = [
9
+ /^(ssh:\/\/)?git@(?<host>[^:]*):(?<user>[^\/]+)\/(?<repo>.+)\.git$/
10
+ ]
11
+
12
+ def self.get_repo url
13
+ # find the url pattern that matches the url
14
+ URL_PATTERNS.map{|p| p.match url }.compact.first
15
+ end
16
+
17
+ def issues_list opts = {}
18
+ issues = gitlab.issues gl_project_id
19
+ # filter out closed issues if desired
20
+ issues = issues.find_all{|i| i.state != 'closed' } if not opts[:all]
21
+ # return issues
22
+ format_issues( issues )
23
+ end
24
+
25
+ def issue_create title, content
26
+ gitlab.create_issue gl_project_id, title, description: content
27
+ end
28
+
29
+ def issue_reopen id
30
+ gitlab.reopen_issue gl_project_id, id
31
+ end
32
+
33
+ def issue_close id
34
+ gitlab.close_issue gl_project_id, id
35
+ end
36
+
37
+ def issue_delete id
38
+ log.warn "You can't delete issues on Gitlab anymore, it is deprecated. Please close/resolve them instead."
39
+ end
40
+
41
+ def provider
42
+ gitlab
43
+ end
44
+
45
+ private
46
+
47
+ def gl_project_id
48
+ @gl_project_id ||= begin
49
+ path = "#{repo['user']}/#{repo['repo']}"
50
+ p = gitlab.projects.find{|p| p.path_with_namespace == path}
51
+ log.info "using project id = #{p.id} (#{p.path_with_namespace})" if not p.nil?
52
+ (p.nil?) ? nil : p.id
53
+ end
54
+ end
55
+
56
+ def format_issues is
57
+ Array(is).map do |i|
58
+ {
59
+ 'number' => i.iid,
60
+ 'title' => i.title,
61
+ 'description' => i.description,
62
+ 'state' => i.state
63
+ }
64
+ end
65
+ end
66
+
67
+ def gitlab
68
+ init_gitlab if @gitlab.nil?
69
+ @gitlab
70
+ end
71
+
72
+ def init_gitlab
73
+ ot = oauth_token
74
+ @gitlab = Gitlab.client endpoint: "http://#{repo['host']}/api/v3", private_token: ot
75
+ end
76
+
77
+ end
@@ -0,0 +1,68 @@
1
+ require 'zlog'
2
+ require 'git-issues/login_helper'
3
+
4
+ # helper: quicly get all submodules
5
+ # http://www.natontesting.com/2010/06/30/how-to-get-the-submodules-of-a-ruby-module/
6
+ class Module
7
+ def submodules
8
+ constants.collect {|const_name| const_get(const_name)}.select {|const| const.class == Module}
9
+ end
10
+
11
+ def subclasses
12
+ constants.collect {|const_name| const_get(const_name)}.select {|const| const.class == Class}
13
+ end
14
+ end
15
+
16
+ # create the holder module for all providers
17
+ module RepoProvider; end
18
+ # get all predefined providers
19
+ require 'git-issues/providers/bitbucket'
20
+ require 'git-issues/providers/github'
21
+ require 'git-issues/providers/gitlab'
22
+
23
+ class RepoProviders
24
+ attr_reader :providers
25
+ def initialize
26
+ @providers = RepoProvider.subclasses.map do |c|
27
+ add_methods_to_provider(c)
28
+ end
29
+ end
30
+
31
+ def map_urls_to_provider urls
32
+ urls.map do |url|
33
+ # get the first valid provider (or nil otherwise)
34
+ @providers.map do |p|
35
+ create_provider(p,url)
36
+ end.compact.first
37
+ end.compact
38
+ end
39
+
40
+ private
41
+
42
+ def create_provider p, url
43
+ # break out if this is not a url for this provider
44
+ repo = p.get_repo url
45
+ return nil if repo.nil?
46
+ # create the provider with the url
47
+ pc = p.new
48
+ pc.instance_variable_set(:@repo_url, url)
49
+ pc.instance_variable_set(:@repo, repo)
50
+ pc
51
+ end
52
+
53
+ def add_methods_to_provider c
54
+ # get the base name of the provider
55
+ name = c.name.sub(/.*::/,'').downcase
56
+ # add simple fields
57
+ ['repo_url', 'repo'].each do |arg|
58
+ c.class_eval("def #{arg};@#{arg};end")
59
+ end
60
+ # add login helper
61
+ c.class_eval <<-EOF
62
+ def log; @log ||= Logging.logger[#{c}]; end
63
+ def name; #{name.inspect} end
64
+ include ::LoginHelper
65
+ EOF
66
+ c
67
+ end
68
+ end
data/lib/git-issues.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'git-issues/providers'
2
+
3
+ require 'parseconfig'
4
+ require 'zlog'
5
+
6
+ class GitIssues
7
+ def version; '0.1.4' end
8
+ Log = Logging.logger[self]
9
+
10
+ attr_reader :providers
11
+ def initialize
12
+ @providers = RepoProviders.new
13
+ end
14
+
15
+ def gitReposFor path
16
+ p = File::expand_path(path)
17
+ git_path = File::join(p, '.git')
18
+ git_conf = File::join(git_path, 'config')
19
+
20
+ if not File::directory?(git_path)
21
+ Log.error "This is not a git repository (path: #{p})"
22
+ return []
23
+ end
24
+
25
+ if not File::file?(git_conf)
26
+ Log.error "Missing git configuration file (missing: #{git_conf})"
27
+ return []
28
+ end
29
+
30
+ config = ParseConfig.new(git_conf)
31
+
32
+ remotes = config.params.keys.find_all{|i|i.start_with?('remote ')}
33
+ remote_repos = remotes.map{|r| config.params[r]['url']}
34
+
35
+ @providers.map_urls_to_provider( remote_repos )
36
+ end
37
+
38
+ end
@@ -0,0 +1,66 @@
1
+ require 'minitest_helper'
2
+
3
+ describe LoginHelper do
4
+ before :each do
5
+ @l = Class.new do
6
+ include LoginHelper
7
+ end.new
8
+ @ln = Class.new do
9
+ def name; 'testgroup' end
10
+ include LoginHelper
11
+ end.new
12
+ @config_path = Tempfile.new('foo').path
13
+ @config_pathn = Tempfile.new('foo').path
14
+ File::write(@config_path, '')
15
+ File::write(@config_pathn, '')
16
+ @l.instance_variable_set :@config_file, @config_path
17
+ @ln.instance_variable_set :@config_file, @config_pathn
18
+ end
19
+
20
+ it "should use the config file if one was configured" do
21
+ @l.config_file.must_equal @config_path
22
+ end
23
+
24
+ it "should make sure the config file permissions are restricted" do
25
+ @l.get_or_set "field" do "value" end
26
+ ( File.stat( @config_path ).mode & 0177 ).must_equal 0
27
+ end
28
+
29
+ it "should default to config file in home folder if none is configured" do
30
+ @l.instance_variable_set :@config_file, nil
31
+ @l.config_file.must_equal "~/.git-issues.conf"
32
+ end
33
+
34
+ it "should read values from config file" do
35
+ v = "testvalue"
36
+ f = "testfield"
37
+ File::write(@config_path, "#{f} = #{v}")
38
+ @l.get_or_set f do nil end.must_equal v
39
+ end
40
+
41
+ it "should write a config item to file if none is configured" do
42
+ v = "testvalue"
43
+ f = "testfield"
44
+ @l.get_or_set f do v end.must_equal v
45
+ File::read( @config_path ).strip.must_equal "#{f} = #{v}"
46
+
47
+ @ln.get_or_set f do v end.must_equal v
48
+ File::read( @config_pathn ).strip.must_equal "[#{@ln.name}]\n#{f} = #{v}"
49
+ end
50
+
51
+ it "should have a method to provide an oauth_consumer_key" do
52
+ @l.must_respond_to :oauth_consumer_key
53
+ end
54
+
55
+ it "should have a method to provide an oauth_consumer_secret" do
56
+ @l.must_respond_to :oauth_consumer_secret
57
+ end
58
+
59
+ it "should have a method to provide a username" do
60
+ @l.must_respond_to :user
61
+ end
62
+
63
+ it "should have a method to provide a password for a username" do
64
+ @l.must_respond_to :password
65
+ end
66
+ end
@@ -0,0 +1,64 @@
1
+ require 'minitest_helper'
2
+
3
+ describe RepoProviders do
4
+ before :each do
5
+ @repo = RepoProviders.new
6
+ @providers = @repo.providers.map do |c|
7
+ c.new
8
+ end
9
+ end
10
+
11
+ it "contains providers" do
12
+ @repo.must_respond_to :providers
13
+ @repo.providers.must_be_instance_of Array
14
+ end
15
+
16
+ it "contains bitbucket provider" do
17
+ @repo.providers.must_include RepoProvider::Bitbucket
18
+ end
19
+
20
+ it "contains github provider" do
21
+ @repo.providers.must_include RepoProvider::Github
22
+ end
23
+
24
+ it "contains gitlab provider" do
25
+ @repo.providers.must_include RepoProvider::Gitlab
26
+ end
27
+
28
+ it "every provider has issues_list" do
29
+ @providers.each do |provider|
30
+ provider.must_respond_to :issues_list
31
+ end
32
+ end
33
+
34
+ it "every provider has issue_create" do
35
+ @providers.each do |provider|
36
+ provider.must_respond_to :issue_create
37
+ end
38
+ end
39
+
40
+ it "every provider has issue_reopen" do
41
+ @providers.each do |provider|
42
+ provider.must_respond_to :issue_reopen
43
+ end
44
+ end
45
+
46
+ it "every provider has issue_close" do
47
+ @providers.each do |provider|
48
+ provider.must_respond_to :issue_close
49
+ end
50
+ end
51
+
52
+ it "every provider has issue_delete" do
53
+ @providers.each do |provider|
54
+ provider.must_respond_to :issue_delete
55
+ end
56
+ end
57
+
58
+ it "every provider must provider its low-level provider" do
59
+ @providers.each do |provider|
60
+ provider.must_respond_to :provider
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,4 @@
1
+ require 'minitest/autorun'
2
+ require 'tempfile'
3
+ require 'git-issues'
4
+
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git-issues
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Dominik Richter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: parseconfig
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bitbucket_rest_api
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.1.5
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.1.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: octokit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: gitlab
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: zlog
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: manage git issues in cli
98
+ email: dominik.richter@googlemail.com
99
+ executables:
100
+ - git-issues
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".travis.yml"
105
+ - CHANGELOG.md
106
+ - Gemfile
107
+ - Gemfile.lock
108
+ - LICENSE
109
+ - README.md
110
+ - Rakefile
111
+ - bin/git-issues
112
+ - git-issues.gemspec
113
+ - lib/git-issues.rb
114
+ - lib/git-issues/login_helper.rb
115
+ - lib/git-issues/providers.rb
116
+ - lib/git-issues/providers/bitbucket.rb
117
+ - lib/git-issues/providers/github.rb
118
+ - lib/git-issues/providers/gitlab.rb
119
+ - test/classes/login_helper_spec.rb
120
+ - test/classes/providers_spec.rb
121
+ - test/minitest_helper.rb
122
+ homepage:
123
+ licenses:
124
+ - MPLv2
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.2.2
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: manage git issues in cli
146
+ test_files: []