octokom 0.0.2 → 0.0.3
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 +4 -4
- data/.gitignore +1 -0
- data/.rspec +0 -1
- data/.travis.yml +8 -1
- data/LICENSE.txt +1 -1
- data/README.md +11 -3
- data/bin/octokom +1 -1
- data/lib/octokom.rb +8 -12
- data/lib/octokom/cli.rb +6 -0
- data/lib/octokom/client.rb +99 -0
- data/lib/octokom/command.rb +65 -2
- data/lib/octokom/command/issue.rb +26 -0
- data/lib/octokom/command/pull_request.rb +31 -22
- data/lib/octokom/core_ext/blank.rb +51 -0
- data/lib/octokom/editor.rb +106 -0
- data/lib/octokom/repository.rb +59 -24
- data/lib/octokom/shell.rb +22 -0
- data/lib/octokom/version.rb +1 -1
- data/octokom.gemspec +8 -6
- data/spec/octokom/cli_spec.rb +13 -0
- data/spec/octokom/client_spec.rb +67 -0
- data/spec/octokom/command/issue_spec.rb +58 -0
- data/spec/octokom/command/pull_request_spec.rb +3 -4
- data/spec/octokom/command_spec.rb +56 -3
- data/spec/octokom/editor_spec.rb +33 -0
- data/spec/octokom/repository_spec.rb +62 -5
- data/spec/octokom/shell_spec.rb +54 -0
- data/spec/spec_helper.rb +7 -1
- data/spec/support/OCTOKOM-123 +11 -0
- data/spec/support/git_branch_no_pending_commits.txt +5 -0
- data/spec/support/git_branch_no_remote.txt +5 -0
- data/spec/support/git_branch_pending_commits.txt +5 -0
- data/spec/support/git_log_commits.txt +1 -0
- metadata +59 -42
- data/lib/octokom/authentication.rb +0 -42
- data/lib/octokom/keychain.rb +0 -54
- data/spec/octokom/authentication_spec.rb +0 -25
- data/spec/octokom/keychain_spec.rb +0 -45
- data/spec/octokom_spec.rb +0 -8
data/lib/octokom/repository.rb
CHANGED
@@ -1,29 +1,64 @@
|
|
1
1
|
module Octokom
|
2
2
|
class Repository
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
3
|
+
include Octokom::Shell
|
4
|
+
|
5
|
+
def initialize(verbose)
|
6
|
+
@verbose = verbose
|
7
|
+
end
|
8
|
+
|
9
|
+
def repo_path
|
10
|
+
error('Git remote not defined') if remote_url.blank?
|
11
|
+
path = remote_url.split('/')[-2..-1]
|
12
|
+
path.join('/')[/(.*)\.git$/, 1]
|
13
|
+
end
|
14
|
+
|
15
|
+
def toplevel_path
|
16
|
+
git('rev-parse --show-toplevel')
|
17
|
+
end
|
18
|
+
|
19
|
+
# TODO Make base branch configurable.
|
20
|
+
def base_branch
|
21
|
+
'master'
|
22
|
+
end
|
23
|
+
|
24
|
+
def head_branch
|
25
|
+
git('rev-parse --abbrev-ref HEAD')
|
26
|
+
end
|
27
|
+
|
28
|
+
def pending_commits?
|
29
|
+
current_sha1 = last_commits[/^\*\s((\w|-|_)*)\s+(\w{7})/, 3]
|
30
|
+
remote_sha1 = last_commits[/remotes\/origin\/#{head_branch}\s+(\w{7})/, 1]
|
31
|
+
|
32
|
+
return true if remote_sha1.blank?
|
33
|
+
current_sha1 != remote_sha1 ? true : false
|
34
|
+
end
|
35
|
+
|
36
|
+
def last_commit_message
|
37
|
+
git('log -1 --oneline')[/^\w+\s(.*)$/, 1]
|
38
|
+
end
|
39
|
+
|
40
|
+
def push(branch)
|
41
|
+
git("push origin #{branch}")
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# TODO Make origin configurable.
|
47
|
+
def remote_url
|
48
|
+
@remote_url ||= git('config --get remote.origin.url')
|
49
|
+
end
|
50
|
+
|
51
|
+
def last_commits
|
52
|
+
@last_commits ||= git('branch -avvv')
|
53
|
+
end
|
54
|
+
|
55
|
+
def verbose?
|
56
|
+
!!@verbose
|
57
|
+
end
|
58
|
+
|
59
|
+
def git(cmd)
|
60
|
+
puts "git #{cmd}" if verbose?
|
61
|
+
`git #{cmd}`.chomp
|
27
62
|
end
|
28
63
|
end
|
29
64
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Octokom
|
2
|
+
module Shell
|
3
|
+
def info(message)
|
4
|
+
puts "Info #{message}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def error(message)
|
8
|
+
puts "Error #{message}"
|
9
|
+
exit(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Print a waiting message before a long running task and replace
|
13
|
+
# the message as soon as the task has finished.
|
14
|
+
def task(message)
|
15
|
+
print "Waiting #{message}"
|
16
|
+
yield
|
17
|
+
print "\r"
|
18
|
+
print "Done #{message}"
|
19
|
+
puts
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/octokom/version.rb
CHANGED
data/octokom.gemspec
CHANGED
@@ -8,20 +8,22 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.version = Octokom::VERSION
|
9
9
|
gem.authors = 'Martin Jagusch'
|
10
10
|
gem.email = '_@mj.io'
|
11
|
-
gem.description = '
|
11
|
+
gem.description = 'GitHub For Your Command Line'
|
12
12
|
gem.summary = "octokom-#{Octokom::VERSION}"
|
13
13
|
gem.homepage = 'https://github.com/mjio/octokom'
|
14
14
|
gem.license = 'MIT'
|
15
15
|
|
16
|
+
gem.required_ruby_version = '>= 1.9.3'
|
17
|
+
|
16
18
|
gem.files = `git ls-files`.split($/)
|
17
19
|
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
20
|
gem.test_files = gem.files.grep(%r{^spec/})
|
19
21
|
gem.require_paths = ['lib']
|
20
22
|
|
21
|
-
gem.add_dependency 'clamp', '~> 0.6.
|
22
|
-
gem.add_dependency '
|
23
|
-
gem.add_dependency '
|
23
|
+
gem.add_dependency 'clamp', '~> 0.6.3'
|
24
|
+
gem.add_dependency 'netrc', '~> 0.7.7'
|
25
|
+
gem.add_dependency 'octokit', '~> 2.7.1'
|
24
26
|
|
25
|
-
gem.add_development_dependency 'rake', '~> 10.1.
|
26
|
-
gem.add_development_dependency 'rspec', '~>
|
27
|
+
gem.add_development_dependency 'rake', '~> 10.1.1'
|
28
|
+
gem.add_development_dependency 'rspec', '~> 3.0.0.beta2'
|
27
29
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Octokom::CLI do
|
4
|
+
it 'has a "issue" sub command' do
|
5
|
+
command = Octokom::CLI.find_subcommand('issue')
|
6
|
+
expect(command).to be_kind_of(Clamp::Subcommand::Definition)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'has a "pull-request" sub command' do
|
10
|
+
command = Octokom::CLI.find_subcommand('pull-request')
|
11
|
+
expect(command).to be_kind_of(Clamp::Subcommand::Definition)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'octokit'
|
3
|
+
|
4
|
+
describe Octokom::Client do
|
5
|
+
let(:netrc) { {Octokom::Client::KEY => credentials} }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(Netrc).to receive(:new) { netrc }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#authenticate' do
|
12
|
+
context 'with valid netrc file' do
|
13
|
+
let(:credentials) { ['fox', 'chunky'] }
|
14
|
+
|
15
|
+
it 'parses the credentials' do
|
16
|
+
auth = Octokom::Client.authenticate
|
17
|
+
expect(auth.token).to eq('chunky')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns a client instance' do
|
21
|
+
auth = Octokom::Client.authenticate
|
22
|
+
expect(auth).to be_kind_of(Octokom::Client)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'handels invalid API tokens on GitHub'
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with invalid netrc file' do
|
29
|
+
let(:credentials) { [] }
|
30
|
+
let(:octokit) { double }
|
31
|
+
let(:auth) { double(:token => nil) }
|
32
|
+
|
33
|
+
before do
|
34
|
+
allow_any_instance_of(Octokom::Client).to receive(:ask_for_login) { 'fox' }
|
35
|
+
allow_any_instance_of(Octokom::Client).to receive(:ask_for_password) { 'bacon' }
|
36
|
+
allow(Octokit::Client).to receive(:new) { octokit }
|
37
|
+
allow(octokit).to receive(:create_authorization) { auth }
|
38
|
+
allow(netrc).to receive(:save) { true }
|
39
|
+
allow(STDOUT).to receive(:puts)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'initializes a new Octokit client' do
|
43
|
+
expect(Octokit::Client).to receive(:new).with(login: 'fox', password: 'bacon')
|
44
|
+
Octokom::Client.authenticate
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'creates a authorization token' do
|
48
|
+
expect(octokit).to receive(:create_authorization)
|
49
|
+
Octokom::Client.authenticate
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'creates a authorization token' do
|
53
|
+
expect(netrc).to receive(:save)
|
54
|
+
Octokom::Client.authenticate
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#client' do
|
60
|
+
let(:credentials) { ['fox', 'chunky'] }
|
61
|
+
|
62
|
+
it 'initializes a new Octokit client' do
|
63
|
+
auth = Octokom::Client.authenticate
|
64
|
+
expect(auth.client).to be_kind_of(Octokit::Client)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'octokom/command/issue'
|
3
|
+
|
4
|
+
describe Issue do
|
5
|
+
describe '#create' do
|
6
|
+
let(:git) { double(repo_path: 'user/repo') }
|
7
|
+
let(:auth) { double(client: client) }
|
8
|
+
let(:client) { double }
|
9
|
+
|
10
|
+
before do
|
11
|
+
allow(Octokom::Repository).to receive(:new) { git }
|
12
|
+
allow(Octokom::Client).to receive(:authenticate) { auth }
|
13
|
+
allow(client).to receive(:create_issue)
|
14
|
+
allow_any_instance_of(Octokom::Command).to receive(:print)
|
15
|
+
allow_any_instance_of(Octokom::Command).to receive(:puts)
|
16
|
+
end
|
17
|
+
|
18
|
+
subject(:cmd) { Issue.new('test') }
|
19
|
+
|
20
|
+
context 'when no "title" option is set' do
|
21
|
+
let(:options) { %w(create) }
|
22
|
+
|
23
|
+
it 'opens the editor' do
|
24
|
+
expect(Octokom::Editor).to receive(:open).with(git, nil, nil) { ['Bacon'] }
|
25
|
+
|
26
|
+
cmd.run(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'exits when no "title" can be parsed'
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'when "title" option is set' do
|
33
|
+
let(:options) { %w(create --title Bacon) }
|
34
|
+
|
35
|
+
it 'creates a GitHub issue' do
|
36
|
+
expect(client).to receive(:create_issue).with('user/repo', 'Bacon', nil)
|
37
|
+
|
38
|
+
cmd.run(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'and "dry-run" tag is set' do
|
42
|
+
it 'does not create a GitHub issue' do
|
43
|
+
expect(client).to_not receive(:create_issue)
|
44
|
+
|
45
|
+
cmd.run(options.push('--dry-run'))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'and "editor" tag is set' do
|
50
|
+
it 'opens the editor' do
|
51
|
+
expect(Octokom::Editor).to receive(:open).with(git, 'Bacon', nil) { ['Bacon'] }
|
52
|
+
|
53
|
+
cmd.run(options.push('--editor'))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'octokom/command/pull_request'
|
2
3
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
command = Octokom::Command::PullRequest.find_subcommand('create')
|
6
|
-
expect(command).to be_kind_of(Clamp::Subcommand::Definition)
|
4
|
+
describe PullRequest do
|
5
|
+
describe '#create' do
|
7
6
|
end
|
8
7
|
end
|
@@ -1,8 +1,61 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Octokom::Command do
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
describe '#flag' do
|
5
|
+
subject(:cmd) do
|
6
|
+
class FlagCmd < Octokom::Command
|
7
|
+
flag ['--bacon'], 'Bacon'
|
8
|
+
execute { @bacon }
|
9
|
+
end
|
10
|
+
|
11
|
+
FlagCmd.new('test')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns true when the flag option is set' do
|
15
|
+
expect(cmd.run(['--bacon'])).to be(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns nil when the flag option is not set' do
|
19
|
+
expect(cmd.run([])).to be_nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#option' do
|
24
|
+
subject(:cmd) do
|
25
|
+
class OptionCmd < Octokom::Command
|
26
|
+
option ['--bacon'], 'Bacon'
|
27
|
+
execute { @bacon }
|
28
|
+
end
|
29
|
+
|
30
|
+
OptionCmd.new('test')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns the the option value' do
|
34
|
+
expect(cmd.run(['--bacon', '1'])).to eq('1')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns nil when the option is not set' do
|
38
|
+
expect(cmd.run([])).to be_nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#git' do
|
43
|
+
subject(:cmd) do
|
44
|
+
class GitCmd < Octokom::Command
|
45
|
+
execute { git }
|
46
|
+
end
|
47
|
+
|
48
|
+
GitCmd.new('test')
|
49
|
+
end
|
50
|
+
|
51
|
+
let(:git) { double }
|
52
|
+
|
53
|
+
before do
|
54
|
+
allow(Octokom::Repository).to receive(:new) { git }
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'initializes a Repository instance' do
|
58
|
+
expect(cmd.run([])).to eq(git)
|
59
|
+
end
|
7
60
|
end
|
8
61
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Octokom::Editor do
|
4
|
+
let(:repo) { double(toplevel_path: '~') }
|
5
|
+
let(:title) { 'Feature' }
|
6
|
+
let(:desc) { '...' }
|
7
|
+
let(:editor) { Octokom::Editor.new(repo, nil, nil) }
|
8
|
+
|
9
|
+
describe '#open_editor' do
|
10
|
+
before do
|
11
|
+
allow(editor).to receive(:editor_name) { 'vi' }
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'build the command' do
|
15
|
+
expect(editor).to receive(:system).with(/^vi --nofork +6*+/)
|
16
|
+
editor.open_editor
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#parse_user_input' do
|
21
|
+
before do
|
22
|
+
allow(editor).to receive(:file_name) { './spec/support/OCTOKOM-123' }
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'finds the title' do
|
26
|
+
expect(editor.parse_user_input[0]).to eq('Chunky Bacon')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'finds the description' do
|
30
|
+
expect(editor.parse_user_input[1]).to eq("Really\n remarkable!")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,15 +1,72 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Octokom::Repository do
|
4
|
-
subject(:repo) { Octokom::Repository }
|
4
|
+
subject(:repo) { Octokom::Repository.new(false) }
|
5
5
|
|
6
|
-
describe '#
|
6
|
+
describe '#repo_path' do
|
7
|
+
context 'when a remote path is defined' do
|
8
|
+
before do
|
9
|
+
allow(repo).to receive(:remote_url) { 'https://github.com/fox-1/bacon.rb.git' }
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'returns the GitHub repository path' do
|
13
|
+
expect(repo.repo_path).to eq('fox-1/bacon.rb')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when no remote path is defined' do
|
18
|
+
let(:stdout) { double }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(repo).to receive(:remote_url) { nil }
|
22
|
+
allow(stdout).to receive(:puts)
|
23
|
+
allow(repo).to receive(:puts) { |msg| stdout.puts(msg) }
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'prints a error message and exits' do
|
27
|
+
expect(stdout).to receive(:puts).with(/Git remote not defined/)
|
28
|
+
expect { repo.repo_path }.to raise_error(SystemExit)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#pending_commits?' do
|
34
|
+
before do
|
35
|
+
allow(repo).to receive(:head_branch) { 'test-1' }
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'with pending commits' do
|
39
|
+
before do
|
40
|
+
allow(repo).to receive(:last_commits) { load_example(:git_branch_pending_commits) }
|
41
|
+
end
|
42
|
+
|
43
|
+
specify { expect(repo.pending_commits?).to be(true) }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with no pending commits' do
|
47
|
+
before do
|
48
|
+
allow(repo).to receive(:last_commits) { load_example(:git_branch_no_pending_commits) }
|
49
|
+
end
|
50
|
+
|
51
|
+
specify { expect(repo.pending_commits?).to be(false) }
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with no remote branch' do
|
55
|
+
before do
|
56
|
+
allow(repo).to receive(:last_commits) { load_example(:git_branch_no_remote) }
|
57
|
+
end
|
58
|
+
|
59
|
+
specify { expect(repo.pending_commits?).to be(true) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#last_commit_message' do
|
7
64
|
before do
|
8
|
-
allow(repo).to receive(:
|
65
|
+
allow(repo).to receive(:git) { load_example(:git_log_commits) }
|
9
66
|
end
|
10
67
|
|
11
|
-
it 'returns
|
12
|
-
expect(repo.
|
68
|
+
it 'returns the commit message' do
|
69
|
+
expect(repo.last_commit_message).to eq('Add all the features')
|
13
70
|
end
|
14
71
|
end
|
15
72
|
end
|