ninny 0.1.9 → 0.1.10
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/.github/pull_request_template.md +47 -0
- data/.github/workflows/main.yml +34 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +24 -0
- data/.ruby-version +1 -1
- data/Gemfile +4 -2
- data/Guardfile +7 -0
- data/LICENSE.txt +1 -1
- data/README.md +78 -12
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/exe/ninny +4 -3
- data/lib/ninny.rb +4 -2
- data/lib/ninny/cli.rb +6 -12
- data/lib/ninny/commands/create_dated_branch.rb +13 -9
- data/lib/ninny/commands/output_dated_branch.rb +2 -1
- data/lib/ninny/commands/pull_request_merge.rb +8 -4
- data/lib/ninny/commands/setup.rb +12 -12
- data/lib/ninny/git.rb +20 -24
- data/lib/ninny/project_config.rb +4 -2
- data/lib/ninny/repository/gitlab.rb +24 -14
- data/lib/ninny/repository/pull_request.rb +5 -2
- data/lib/ninny/user_config.rb +4 -4
- data/lib/ninny/version.rb +1 -1
- data/ninny.gemspec +34 -58
- metadata +126 -43
- data/.travis.yml +0 -7
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile.lock +0 -159
- data/lib/ninny/commands/.gitkeep +0 -1
- data/lib/ninny/templates/.gitkeep +0 -1
@@ -6,12 +6,13 @@ module Ninny
|
|
6
6
|
module Commands
|
7
7
|
class OutputDatedBranch < Ninny::Command
|
8
8
|
attr_reader :branch_type
|
9
|
+
|
9
10
|
def initialize(options)
|
10
11
|
@branch_type = options[:branch_type] || Git::STAGING_PREFIX
|
11
12
|
@options = options
|
12
13
|
end
|
13
14
|
|
14
|
-
def execute(
|
15
|
+
def execute(output: $stdout)
|
15
16
|
output.puts Ninny.git.latest_branch_for(branch_type)
|
16
17
|
end
|
17
18
|
end
|
@@ -14,11 +14,12 @@ module Ninny
|
|
14
14
|
self.options = options
|
15
15
|
end
|
16
16
|
|
17
|
-
def execute(
|
18
|
-
|
17
|
+
def execute(*)
|
18
|
+
unless pull_request_id
|
19
19
|
current = Ninny.repo.current_pull_request
|
20
20
|
self.pull_request_id = current.number if current
|
21
21
|
end
|
22
|
+
|
22
23
|
self.pull_request_id ||= select_pull_request
|
23
24
|
|
24
25
|
check_out_branch
|
@@ -26,12 +27,13 @@ module Ninny
|
|
26
27
|
comment_about_merge
|
27
28
|
end
|
28
29
|
|
29
|
-
|
30
|
+
def select_pull_request
|
30
31
|
choices = Ninny.repo.open_pull_requests.map { |pr| { name: pr.title, value: pr.number } }
|
31
32
|
prompt.select("Which #{Ninny.repo.pull_request_label}?", choices)
|
32
33
|
end
|
34
|
+
private :select_pull_request
|
33
35
|
|
34
|
-
|
36
|
+
# Public: Check out the branch
|
35
37
|
def check_out_branch
|
36
38
|
Ninny.git.check_out(branch_to_merge_into, false)
|
37
39
|
Ninny.git.track_current_branch
|
@@ -60,9 +62,11 @@ module Ninny
|
|
60
62
|
# Public: Find the pull request
|
61
63
|
#
|
62
64
|
# Returns a Ninny::Repository::PullRequest
|
65
|
+
# rubocop:disable Lint/DuplicateMethods
|
63
66
|
def pull_request
|
64
67
|
@pull_request ||= Ninny.repo.pull_request(pull_request_id)
|
65
68
|
end
|
69
|
+
# rubocop:enable Lint/DuplicateMethods
|
66
70
|
|
67
71
|
# Public: Find the branch
|
68
72
|
#
|
data/lib/ninny/commands/setup.rb
CHANGED
@@ -6,12 +6,13 @@ module Ninny
|
|
6
6
|
module Commands
|
7
7
|
class Setup < Ninny::Command
|
8
8
|
attr_reader :config
|
9
|
+
|
9
10
|
def initialize(options)
|
10
11
|
@options = options
|
11
12
|
@config = Ninny.user_config
|
12
13
|
end
|
13
14
|
|
14
|
-
def execute(
|
15
|
+
def execute(output: $stdout)
|
15
16
|
try_reading_user_config
|
16
17
|
|
17
18
|
prompt_for_gitlab_private_token
|
@@ -22,24 +23,23 @@ module Ninny
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def try_reading_user_config
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
@result = 'created'
|
30
|
-
end
|
26
|
+
config.read
|
27
|
+
@result = 'updated'
|
28
|
+
rescue MissingUserConfig
|
29
|
+
@result = 'created'
|
31
30
|
end
|
32
31
|
|
33
32
|
def prompt_for_gitlab_private_token
|
34
33
|
begin
|
35
34
|
new_token_text = config.gitlab_private_token ? ' new' : ''
|
36
35
|
rescue MissingUserConfig
|
37
|
-
new_token_text = '
|
38
|
-
end
|
39
|
-
if prompt.yes?("Do you have a#{new_token_text} gitlab private token?")
|
40
|
-
private_token = prompt.ask("Enter private token", required: true)
|
41
|
-
config.set(:gitlab_private_token, value: private_token)
|
36
|
+
new_token_text = ''
|
42
37
|
end
|
38
|
+
|
39
|
+
return unless prompt.yes?("Do you have a#{new_token_text} GitLab private token?")
|
40
|
+
|
41
|
+
private_token = prompt.ask('Enter private token:', required: true)
|
42
|
+
config.set(:gitlab_private_token, value: private_token)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
data/lib/ninny/git.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Ninny
|
4
|
+
# rubocop:disable Metrics/ClassLength
|
4
5
|
class Git
|
5
6
|
extend Forwardable
|
6
|
-
NO_BRANCH =
|
7
|
-
DEFAULT_DIRTY_MESSAGE =
|
8
|
-
|
7
|
+
NO_BRANCH = '(no branch)'
|
8
|
+
DEFAULT_DIRTY_MESSAGE = 'Your Git index is not clean. Commit, stash, or otherwise clean' \
|
9
|
+
' up the index before continuing.'
|
10
|
+
DIRTY_CONFIRM_MESSAGE = 'Your Git index is not clean. Do you want to continue?'
|
9
11
|
|
10
12
|
# branch prefixes
|
11
|
-
DEPLOYABLE_PREFIX =
|
12
|
-
STAGING_PREFIX =
|
13
|
-
QAREADY_PREFIX =
|
13
|
+
DEPLOYABLE_PREFIX = 'deployable'
|
14
|
+
STAGING_PREFIX = 'staging'
|
15
|
+
QAREADY_PREFIX = 'qaready'
|
14
16
|
|
15
17
|
def_delegators :git, :branch
|
16
18
|
|
@@ -29,12 +31,9 @@ module Ninny
|
|
29
31
|
end
|
30
32
|
|
31
33
|
def current_branch_name
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
name
|
37
|
-
end
|
34
|
+
raise NotOnBranch, 'Not currently checked out to a particular branch' if git.current_branch == NO_BRANCH
|
35
|
+
|
36
|
+
git.current_branch
|
38
37
|
end
|
39
38
|
|
40
39
|
def merge(branch_name)
|
@@ -42,6 +41,7 @@ module Ninny
|
|
42
41
|
git.fetch
|
43
42
|
command 'merge', ['--no-ff', "origin/#{branch_name}"]
|
44
43
|
raise MergeFailed unless clean?
|
44
|
+
|
45
45
|
push
|
46
46
|
end
|
47
47
|
end
|
@@ -64,24 +64,21 @@ module Ninny
|
|
64
64
|
#
|
65
65
|
# branch_name - The name of the branch to check out
|
66
66
|
# do_after_pull - Should a pull be done after checkout?
|
67
|
-
def check_out(branch, do_after_pull=true)
|
67
|
+
def check_out(branch, do_after_pull = true)
|
68
68
|
git.fetch
|
69
69
|
branch.checkout
|
70
70
|
pull if do_after_pull
|
71
|
-
unless current_branch.name == branch.name
|
72
|
-
raise CheckoutFailed, "Failed to check out '#{branch}'"
|
73
|
-
end
|
71
|
+
raise CheckoutFailed, "Failed to check out '#{branch}'" unless current_branch.name == branch.name
|
74
72
|
end
|
75
73
|
|
76
74
|
# Public: Track remote branch matching current branch
|
77
75
|
#
|
78
76
|
# do_after_pull - Should a pull be done after tracking?
|
79
|
-
def track_current_branch(do_after_pull=true)
|
77
|
+
def track_current_branch(do_after_pull = true)
|
80
78
|
command('branch', ['-u', "origin/#{current_branch_name}"])
|
81
79
|
pull if do_after_pull
|
82
80
|
end
|
83
81
|
|
84
|
-
|
85
82
|
# Public: Create a new branch from the given source
|
86
83
|
#
|
87
84
|
# new_branch_name - The name of the branch to create
|
@@ -108,7 +105,7 @@ module Ninny
|
|
108
105
|
# Returns an Array of Strings containing the branch names
|
109
106
|
def remote_branches
|
110
107
|
git.fetch
|
111
|
-
git.branches.remote.map{ |branch| git.branch(branch.name) }.sort_by(&:name)
|
108
|
+
git.branches.remote.map { |branch| git.branch(branch.name) }.sort_by(&:name)
|
112
109
|
end
|
113
110
|
|
114
111
|
# Public: List of branches starting with the given string
|
@@ -131,7 +128,6 @@ module Ninny
|
|
131
128
|
branches_for(prefix).last || raise(NoBranchOfType, "No #{prefix} branch")
|
132
129
|
end
|
133
130
|
|
134
|
-
|
135
131
|
# Public: Whether the Git index is clean (has no uncommited changes)
|
136
132
|
#
|
137
133
|
# Returns a Boolean
|
@@ -140,7 +136,7 @@ module Ninny
|
|
140
136
|
end
|
141
137
|
|
142
138
|
# Public: Perform the block if the Git index is clean
|
143
|
-
def if_clean(message=DEFAULT_DIRTY_MESSAGE)
|
139
|
+
def if_clean(message = DEFAULT_DIRTY_MESSAGE)
|
144
140
|
if clean? || prompt.yes?(DIRTY_CONFIRM_MESSAGE)
|
145
141
|
yield
|
146
142
|
else
|
@@ -151,9 +147,9 @@ module Ninny
|
|
151
147
|
|
152
148
|
# Public: Display the message and show the git status
|
153
149
|
def alert_dirty_index(message)
|
154
|
-
prompt.say
|
150
|
+
prompt.say ' '
|
155
151
|
prompt.say message
|
156
|
-
prompt.say
|
152
|
+
prompt.say ' '
|
157
153
|
prompt.say command('status')
|
158
154
|
raise DirtyIndex
|
159
155
|
end
|
@@ -163,11 +159,11 @@ module Ninny
|
|
163
159
|
TTY::Prompt.new(options)
|
164
160
|
end
|
165
161
|
|
166
|
-
|
167
162
|
# Exceptions
|
168
163
|
CheckoutFailed = Class.new(StandardError)
|
169
164
|
NotOnBranch = Class.new(StandardError)
|
170
165
|
NoBranchOfType = Class.new(StandardError)
|
171
166
|
DirtyIndex = Class.new(StandardError)
|
172
167
|
end
|
168
|
+
# rubocop:enable Metrics/ClassLength
|
173
169
|
end
|
data/lib/ninny/project_config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Ninny
|
2
4
|
class ProjectConfig
|
3
5
|
attr_reader :config
|
@@ -31,14 +33,14 @@ module Ninny
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def gitlab_endpoint
|
34
|
-
config.fetch(:gitlab_endpoint, default:
|
36
|
+
config.fetch(:gitlab_endpoint, default: 'https://gitlab.com/api/v4')
|
35
37
|
end
|
36
38
|
|
37
39
|
def repo
|
38
40
|
return unless repo_type
|
39
41
|
|
40
42
|
repo_class = { gitlab: Repository::Gitlab }[repo_type.to_sym]
|
41
|
-
repo_class
|
43
|
+
repo_class&.new
|
42
44
|
end
|
43
45
|
|
44
46
|
def self.config
|
@@ -1,18 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Ninny
|
2
4
|
module Repository
|
3
5
|
class Gitlab
|
4
|
-
attr_reader :gitlab
|
5
|
-
|
6
|
+
attr_reader :gitlab, :project_id
|
7
|
+
|
6
8
|
def initialize
|
7
|
-
@gitlab = ::Gitlab.client(
|
8
|
-
|
9
|
+
@gitlab = ::Gitlab.client(
|
10
|
+
endpoint: Ninny.project_config.gitlab_endpoint,
|
11
|
+
private_token: Ninny.user_config.gitlab_private_token
|
12
|
+
)
|
9
13
|
@project_id = Ninny.project_config.gitlab_project_id
|
10
14
|
end
|
11
15
|
|
12
16
|
def current_pull_request
|
13
|
-
to_pr(
|
14
|
-
|
15
|
-
|
17
|
+
to_pr(
|
18
|
+
gitlab.merge_requests(
|
19
|
+
project_id,
|
20
|
+
{ source_branch: Ninny.git.current_branch.name, target_branch: Ninny.project_config.deploy_branch }
|
21
|
+
).last
|
22
|
+
)
|
16
23
|
end
|
17
24
|
|
18
25
|
def pull_request_label
|
@@ -20,7 +27,7 @@ module Ninny
|
|
20
27
|
end
|
21
28
|
|
22
29
|
def open_pull_requests
|
23
|
-
gitlab.merge_requests(project_id, { state: 'opened' }).map{ |mr| to_pr(mr) }
|
30
|
+
gitlab.merge_requests(project_id, { state: 'opened' }).map { |mr| to_pr(mr) }
|
24
31
|
end
|
25
32
|
|
26
33
|
def pull_request(id)
|
@@ -31,13 +38,16 @@ module Ninny
|
|
31
38
|
gitlab.create_merge_request_note(project_id, id, body)
|
32
39
|
end
|
33
40
|
|
34
|
-
|
35
|
-
request && PullRequest.new(
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
41
|
+
def to_pr(request)
|
42
|
+
request && PullRequest.new(
|
43
|
+
number: request.iid,
|
44
|
+
title: request.title,
|
45
|
+
branch: request.source_branch,
|
46
|
+
description: request.description,
|
47
|
+
comment_lambda: ->(body) { Ninny.repo.create_merge_request_note(request.iid, body) }
|
48
|
+
)
|
40
49
|
end
|
50
|
+
private :to_pr
|
41
51
|
end
|
42
52
|
end
|
43
53
|
end
|
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Ninny
|
2
4
|
module Repository
|
3
5
|
class PullRequest
|
4
6
|
attr_accessor :number, :title, :description, :branch, :comment_lambda
|
5
|
-
|
7
|
+
|
8
|
+
def initialize(opts = {})
|
6
9
|
self.number = opts[:number]
|
7
10
|
self.title = opts[:title]
|
8
11
|
self.description = opts[:description]
|
@@ -11,7 +14,7 @@ module Ninny
|
|
11
14
|
end
|
12
15
|
|
13
16
|
def write_comment(body)
|
14
|
-
|
17
|
+
comment_lambda.call(body)
|
15
18
|
end
|
16
19
|
end
|
17
20
|
end
|
data/lib/ninny/user_config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Ninny
|
2
4
|
class UserConfig
|
3
5
|
attr_reader :config
|
@@ -25,11 +27,9 @@ module Ninny
|
|
25
27
|
end
|
26
28
|
|
27
29
|
def read
|
28
|
-
begin
|
29
30
|
config.read unless @read
|
30
|
-
|
31
|
-
|
32
|
-
end
|
31
|
+
rescue TTY::Config::ReadError
|
32
|
+
raise MissingUserConfig, 'User config not found, run `ninny setup`'
|
33
33
|
end
|
34
34
|
|
35
35
|
def with_read
|
data/lib/ninny/version.rb
CHANGED
data/ninny.gemspec
CHANGED
@@ -1,72 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
5
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
-
require
|
5
|
+
require 'ninny/version'
|
7
6
|
|
8
7
|
Gem::Specification.new do |spec|
|
9
|
-
spec.name =
|
10
|
-
spec.license =
|
8
|
+
spec.name = 'ninny'
|
9
|
+
spec.license = 'MIT'
|
11
10
|
spec.version = Ninny::VERSION
|
12
|
-
spec.authors = [
|
13
|
-
spec.email = [
|
14
|
-
|
15
|
-
spec.summary = "ninny (n): an foolish person, see: git"
|
16
|
-
spec.description = "Ninny is a command line workflow for git"
|
17
|
-
# spec.homepage = "TODO: Put your gem's website or public repo URL here."
|
11
|
+
spec.authors = ['Dispatch Engineers']
|
12
|
+
spec.email = ['engineers@dispatchit.com']
|
18
13
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
23
|
-
|
24
|
-
# spec.metadata["homepage_uri"] = spec.homepage
|
25
|
-
# spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
26
|
-
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
27
|
-
# else
|
28
|
-
# raise "RubyGems 2.0 or newer is required to protect against " \
|
29
|
-
# "public gem pushes."
|
30
|
-
# end
|
14
|
+
spec.summary = 'ninny (n): an foolish person, see: git'
|
15
|
+
spec.description = 'Ninny is a command line workflow for git with GitLab'
|
16
|
+
spec.homepage = 'https://github.com/dispatchinc/ninny'
|
31
17
|
|
32
18
|
# Specify which files should be added to the gem when it is released.
|
33
19
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
34
|
-
spec.files = Dir.chdir(File.expand_path(
|
20
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
35
21
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
36
22
|
end
|
37
|
-
spec.bindir = "exe"
|
38
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
39
|
-
spec.require_paths = ["lib"]
|
40
|
-
|
41
|
-
spec.add_dependency "tty-box", "~> 0.3.0"
|
42
|
-
spec.add_dependency "tty-color", "~> 0.4"
|
43
|
-
spec.add_dependency "tty-command", "~> 0.8.0"
|
44
|
-
spec.add_dependency "tty-config", "~> 0.3.0"
|
45
|
-
spec.add_dependency "tty-prompt", "~> 0.18.0"
|
46
|
-
|
47
|
-
# spec.add_dependency "tty-cursor", "~> 0.6"
|
48
|
-
# spec.add_dependency "tty-editor", "~> 0.5.0"
|
49
|
-
# spec.add_dependency "tty-file", "~> 0.7.0"
|
50
|
-
# spec.add_dependency "tty-font", "~> 0.2.0"
|
51
|
-
# spec.add_dependency "tty-markdown", "~> 0.5.0"
|
52
|
-
# spec.add_dependency "tty-pager", "~> 0.12.0"
|
53
|
-
# spec.add_dependency "tty-pie", "~> 0.1.0"
|
54
|
-
# spec.add_dependency "tty-platform", "~> 0.2.0"
|
55
|
-
# spec.add_dependency "tty-progressbar", "~> 0.16.0"
|
56
|
-
# spec.add_dependency "tty-screen", "~> 0.6"
|
57
|
-
# spec.add_dependency "tty-spinner", "~> 0.9.0"
|
58
|
-
# spec.add_dependency "tty-table", "~> 0.10.0"
|
59
|
-
# spec.add_dependency "tty-tree", "~> 0.2.0"
|
60
|
-
# spec.add_dependency "tty-which", "~> 0.4"
|
61
|
-
|
62
|
-
spec.add_dependency "pastel", "~> 0.7.2"
|
63
|
-
spec.add_dependency "thor", "< 2"
|
64
|
-
|
65
|
-
spec.add_dependency "git", "~> 1.5.0"
|
66
|
-
spec.add_dependency "gitlab", "~> 4.11"
|
67
23
|
|
68
|
-
spec.
|
69
|
-
spec.
|
70
|
-
spec.
|
71
|
-
|
24
|
+
spec.bindir = 'exe'
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ['lib']
|
27
|
+
|
28
|
+
spec.add_dependency 'git', '~> 1.5.0'
|
29
|
+
spec.add_dependency 'gitlab', '~> 4.11'
|
30
|
+
spec.add_dependency 'pastel', '~> 0.7.2'
|
31
|
+
spec.add_dependency 'thor', '< 2'
|
32
|
+
spec.add_dependency 'tty-box', '~> 0.3.0'
|
33
|
+
spec.add_dependency 'tty-color', '~> 0.4'
|
34
|
+
spec.add_dependency 'tty-command', '~> 0.8.0'
|
35
|
+
spec.add_dependency 'tty-config', '~> 0.3.0'
|
36
|
+
spec.add_dependency 'tty-prompt', '~> 0.18.0'
|
37
|
+
|
38
|
+
spec.add_development_dependency 'bundler', '~> 1.17'
|
39
|
+
spec.add_development_dependency 'byebug', '~> 11.1'
|
40
|
+
spec.add_development_dependency 'guard-rspec', '~> 4.3'
|
41
|
+
spec.add_development_dependency 'pronto', '~> 0.11'
|
42
|
+
spec.add_development_dependency 'pronto-rubocop', '~> 0.11'
|
43
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
44
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
45
|
+
spec.add_development_dependency 'rubocop', '~> 1.11'
|
46
|
+
spec.add_development_dependency 'rubocop-rails', '~> 2.9'
|
47
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.2'
|
72
48
|
end
|