git-issues 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []