neetob-ud 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +10 -0
- data/.env +1 -0
- data/.rubocop.yml +596 -0
- data/.ruby-version +1 -0
- data/.semaphore/semaphore.yml +30 -0
- data/CHANGELOG.md +40 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +28 -0
- data/Gemfile.lock +250 -0
- data/LICENSE.txt +21 -0
- data/README.md +396 -0
- data/Rakefile +16 -0
- data/config/secrets.yml +0 -0
- data/data/branch-protection-rules.json +25 -0
- data/data/config-vars-audit.json +18 -0
- data/data/config-vars-list.json +1 -0
- data/data/github-labels.json +197 -0
- data/env.sample +1 -0
- data/exe/neetob +25 -0
- data/install.sh +21 -0
- data/lib/neetob/cli/base.rb +96 -0
- data/lib/neetob/cli/fetchorupdate_repos/execute.rb +55 -0
- data/lib/neetob/cli/github/auth.rb +134 -0
- data/lib/neetob/cli/github/base.rb +42 -0
- data/lib/neetob/cli/github/commands.rb +54 -0
- data/lib/neetob/cli/github/issues/commands.rb +51 -0
- data/lib/neetob/cli/github/issues/create.rb +42 -0
- data/lib/neetob/cli/github/issues/list.rb +94 -0
- data/lib/neetob/cli/github/labels/commands.rb +65 -0
- data/lib/neetob/cli/github/labels/delete.rb +46 -0
- data/lib/neetob/cli/github/labels/delete_all.rb +50 -0
- data/lib/neetob/cli/github/labels/list.rb +38 -0
- data/lib/neetob/cli/github/labels/show.rb +39 -0
- data/lib/neetob/cli/github/labels/update.rb +42 -0
- data/lib/neetob/cli/github/labels/upsert.rb +64 -0
- data/lib/neetob/cli/github/login.rb +16 -0
- data/lib/neetob/cli/github/make_pr/base.rb +72 -0
- data/lib/neetob/cli/github/make_pr/commands.rb +37 -0
- data/lib/neetob/cli/github/make_pr/compliance_fix.rb +49 -0
- data/lib/neetob/cli/github/make_pr/script.rb +55 -0
- data/lib/neetob/cli/github/protect_branch.rb +48 -0
- data/lib/neetob/cli/github/search.rb +38 -0
- data/lib/neetob/cli/heroku/access/add.rb +38 -0
- data/lib/neetob/cli/heroku/access/commands.rb +41 -0
- data/lib/neetob/cli/heroku/access/list.rb +36 -0
- data/lib/neetob/cli/heroku/access/remove.rb +38 -0
- data/lib/neetob/cli/heroku/commands.rb +28 -0
- data/lib/neetob/cli/heroku/config_vars/audit.rb +64 -0
- data/lib/neetob/cli/heroku/config_vars/base.rb +19 -0
- data/lib/neetob/cli/heroku/config_vars/commands.rb +49 -0
- data/lib/neetob/cli/heroku/config_vars/list.rb +56 -0
- data/lib/neetob/cli/heroku/config_vars/remove.rb +39 -0
- data/lib/neetob/cli/heroku/config_vars/upsert.rb +80 -0
- data/lib/neetob/cli/heroku/execute.rb +37 -0
- data/lib/neetob/cli/local/commands.rb +19 -0
- data/lib/neetob/cli/local/ls.rb +29 -0
- data/lib/neetob/cli/sub_command_base.rb +17 -0
- data/lib/neetob/cli/ui.rb +41 -0
- data/lib/neetob/cli/users/audit.rb +121 -0
- data/lib/neetob/cli/users/commands.rb +30 -0
- data/lib/neetob/cli/users/commits.rb +171 -0
- data/lib/neetob/cli.rb +42 -0
- data/lib/neetob/exception_handler.rb +58 -0
- data/lib/neetob/utils.rb +16 -0
- data/lib/neetob/version.rb +5 -0
- data/lib/neetob.rb +10 -0
- data/overcommit.yml +43 -0
- data/scripts/delete_unused_assets.rb +67 -0
- metadata +186 -0
data/install.sh
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
last_wd=$(pwd)
|
4
|
+
cd "/tmp/neetob/"
|
5
|
+
bundle install
|
6
|
+
echo "Building gem..."
|
7
|
+
bundle exec rake build
|
8
|
+
echo -e "\nHold tight! The next step might take some time..."
|
9
|
+
gem install "/tmp/neetob/pkg/neetob*.gem"
|
10
|
+
cd "${last_wd}"
|
11
|
+
rm -rf "/tmp/neetob/"
|
12
|
+
echo -e "\n"
|
13
|
+
cat <<EOF
|
14
|
+
=========================
|
15
|
+
_ _
|
16
|
+
___ ___ ___| |_ ___| |_
|
17
|
+
| | -_| -_| _| . | . |
|
18
|
+
|_|_|___|___|_| |___|___|
|
19
|
+
=========================
|
20
|
+
EOF
|
21
|
+
neetob help
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "ui"
|
4
|
+
require_relative "../utils"
|
5
|
+
require_relative "../../../neeto_compliance/lib/neeto_compliance/neeto_repos.rb"
|
6
|
+
|
7
|
+
module Neetob
|
8
|
+
class CLI::Base
|
9
|
+
include Utils
|
10
|
+
|
11
|
+
NEETO_APPS_LIST_LINK = "https://github.com/bigbinary/neeto-compliance/blob/main/lib/neeto_compliance/neeto_apps.rb"
|
12
|
+
|
13
|
+
attr_reader :ui
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@ui = CLI::UI.new
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def find_all_matching_apps(apps, platform_name, sandbox_mode, quiet = false, all_neeto_repos = false)
|
22
|
+
inform_about_current_working_mode(sandbox_mode, quiet)
|
23
|
+
all_available_apps = sandbox_mode ?
|
24
|
+
testing_apps(platform_name) :
|
25
|
+
build_app_list_from_neeto_compliance(platform_name, all_neeto_repos)
|
26
|
+
matching_apps = match_apps(apps || ["*"], all_available_apps)
|
27
|
+
if matching_apps.length == 0
|
28
|
+
error_msg = sandbox_mode ?
|
29
|
+
"Only \"neeto-dummy\" app is available for sandbox mode. Remove the \"--sandbox\" flag to run the given command for all the neeto applications." :
|
30
|
+
"No matching app is found in the neeto apps list maintained at \"#{NEETO_APPS_LIST_LINK}\""
|
31
|
+
ui.error(error_msg)
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
|
35
|
+
matching_apps
|
36
|
+
end
|
37
|
+
|
38
|
+
def match_apps(required_apps, available_apps)
|
39
|
+
apps = required_apps&.map { |app| Regexp.new "#{app.gsub("*", "[-a-zA-Z0-9]*")}" }
|
40
|
+
available_apps.select do |available_app|
|
41
|
+
apps&.any? { |app| app.match?(available_app) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def read_json_file(path)
|
46
|
+
file = File.read(path)
|
47
|
+
JSON.parse(file)
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_app_list_from_neeto_compliance(platform_name, all_neeto_repos)
|
51
|
+
apps = all_neeto_repos ? fetch_all_neeto_repos : NeetoCompliance::NeetoRepos.products.keys
|
52
|
+
platform_name == :heroku ? add_env_suffix(apps) : prefix_org_name(apps)
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_env_suffix(apps)
|
56
|
+
[
|
57
|
+
suffix_slug(apps, :staging),
|
58
|
+
suffix_slug(apps, :production)
|
59
|
+
].flatten.sort
|
60
|
+
end
|
61
|
+
|
62
|
+
def suffix_slug(apps, suffix)
|
63
|
+
apps.map { |app| "#{app}-#{suffix}" }
|
64
|
+
end
|
65
|
+
|
66
|
+
def prefix_org_name(apps)
|
67
|
+
apps.map { |app| "bigbinary/#{app}" }
|
68
|
+
end
|
69
|
+
|
70
|
+
def testing_apps(platform_name)
|
71
|
+
platform_name == :heroku ? ["neeto-dummy"] : ["bigbinary/neeto-dummy"]
|
72
|
+
end
|
73
|
+
|
74
|
+
def inform_about_current_working_mode(sandbox_mode, quiet)
|
75
|
+
return if quiet
|
76
|
+
|
77
|
+
callout_message = "Running in the sandbox mode. \nOnly \"neeto-dummy\" app will be available."
|
78
|
+
unless sandbox_mode
|
79
|
+
callout_message = "Running in non-sandbox mode. \nAll neeto applications are available. Some of the actions are irreversible."
|
80
|
+
end
|
81
|
+
ui.say(callout_message)
|
82
|
+
end
|
83
|
+
|
84
|
+
def fetch_all_neeto_repos
|
85
|
+
NeetoCompliance::NeetoRepos::products.keys +
|
86
|
+
NeetoCompliance::NeetoRepos::nanos +
|
87
|
+
NeetoCompliance::NeetoRepos::widgets +
|
88
|
+
NeetoCompliance::NeetoRepos::chrome_extensions +
|
89
|
+
NeetoCompliance::NeetoRepos::helper_packages +
|
90
|
+
NeetoCompliance::NeetoRepos::electron_apps +
|
91
|
+
NeetoCompliance::NeetoRepos::executables +
|
92
|
+
NeetoCompliance::NeetoRepos::mobile_apps +
|
93
|
+
NeetoCompliance::NeetoRepos::other_repos
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../base"
|
4
|
+
|
5
|
+
module Neetob
|
6
|
+
class CLI
|
7
|
+
module FetchorupdateRepos
|
8
|
+
class Execute < Base
|
9
|
+
attr_accessor :sandbox, :apps, :all_neeto_repos
|
10
|
+
|
11
|
+
def initialize(sandbox = false, all_neeto_repos = false, apps = ["*"])
|
12
|
+
super()
|
13
|
+
@sandbox = sandbox
|
14
|
+
@apps = apps
|
15
|
+
@all_neeto_repos = all_neeto_repos
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
neeto_apps = find_all_matching_apps(apps, :github, sandbox, false, all_neeto_repos)
|
20
|
+
neeto_apps.each do |app|
|
21
|
+
app_name = app.split("/").last
|
22
|
+
ui.info("\nWorking on #{app_name}\n")
|
23
|
+
if directory_exists(app_name)
|
24
|
+
checkout_to_main_and_fetch_commits(app_name)
|
25
|
+
else
|
26
|
+
clone_repo(app_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def directory_exists(app_name)
|
32
|
+
File.directory?(app_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def checkout_to_main_and_fetch_commits(app_name)
|
36
|
+
%x[ cd #{app_name} && git checkout main && git pull origin main ]
|
37
|
+
if $?.success?
|
38
|
+
puts "------Successfully pulled main branch of #{app_name}------"
|
39
|
+
else
|
40
|
+
puts "------Unable to pull the main branch of #{app_name} due to conflicts------"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def clone_repo(app_name)
|
45
|
+
`git clone git@github.com:bigbinary/#{app_name}.git`
|
46
|
+
if $?.success?
|
47
|
+
puts "------Done cloning #{app_name}------"
|
48
|
+
else
|
49
|
+
puts "------Failed cloning #{app_name}------"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../ui"
|
4
|
+
require "net/http"
|
5
|
+
require "uri"
|
6
|
+
require "json"
|
7
|
+
require "launchy"
|
8
|
+
require "fileutils"
|
9
|
+
|
10
|
+
module Neetob
|
11
|
+
class CLI
|
12
|
+
module Github
|
13
|
+
class Auth
|
14
|
+
attr_accessor :client_id, :grant_type, :uris, :provider, :scope, :access_token, :ui
|
15
|
+
|
16
|
+
def initialize(client_id:, grant_type:, auth_uris:, provider:, scope:)
|
17
|
+
@client_id = client_id
|
18
|
+
@grant_type = grant_type
|
19
|
+
@uris = auth_uris
|
20
|
+
@provider = provider
|
21
|
+
@scope = scope
|
22
|
+
@access_token = retrieve_persisted_token
|
23
|
+
@ui = CLI::UI.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def token_persisted?
|
27
|
+
access_token_present?
|
28
|
+
end
|
29
|
+
|
30
|
+
def open_url_in_browser!(url)
|
31
|
+
Launchy.open(url) do |exception|
|
32
|
+
raise(StandardError, "Attempted to open #{url} in browser and failed because #{exception}.")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def start_oauth2_device_flow
|
37
|
+
auth_data = request_authorization
|
38
|
+
show_user_code(auth_data[:user_code])
|
39
|
+
open_url_in_browser!(auth_data[:verification_uri])
|
40
|
+
poll_for_token(auth_data)
|
41
|
+
end
|
42
|
+
|
43
|
+
def request_authorization
|
44
|
+
post(uris["auth_req"], params: { client_id: client_id, scope: scope })
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def show_user_code(code)
|
50
|
+
ui.say("You'll be redirected to the browser in 5 secs. Enter the following code in the browser:")
|
51
|
+
ui.success(code)
|
52
|
+
sleep 5
|
53
|
+
end
|
54
|
+
|
55
|
+
def tmp_token_path(provider)
|
56
|
+
"/tmp/neetob_#{provider}_token"
|
57
|
+
end
|
58
|
+
|
59
|
+
def provider_local_token_path
|
60
|
+
@_provider_local_token_path ||= tmp_token_path(provider)
|
61
|
+
end
|
62
|
+
|
63
|
+
def retrieve_persisted_token
|
64
|
+
File.read(provider_local_token_path) if File.file?(provider_local_token_path)
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_response(http_result)
|
68
|
+
case http_result
|
69
|
+
when Net::HTTPOK
|
70
|
+
JSON.parse(http_result.body, { symbolize_names: true })
|
71
|
+
else
|
72
|
+
raise(StandardError, "Request failed with status code #{http_result.code}. #{http_result.body}")
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def post(url, params:, headers: { "Accept" => "application/json" })
|
78
|
+
base_uri = URI(url)
|
79
|
+
enc_params = URI.encode_www_form(params)
|
80
|
+
http_result = Net::HTTP.post(base_uri, enc_params, headers)
|
81
|
+
parse_response(http_result)
|
82
|
+
end
|
83
|
+
|
84
|
+
def poll_for_token(auth_data)
|
85
|
+
interval = auth_data[:interval]
|
86
|
+
loop do
|
87
|
+
res = post(
|
88
|
+
uris["token_req"],
|
89
|
+
params: {
|
90
|
+
client_id: client_id,
|
91
|
+
device_code: auth_data[:device_code],
|
92
|
+
grant_type: grant_type
|
93
|
+
}
|
94
|
+
)
|
95
|
+
|
96
|
+
if res.key?(:error)
|
97
|
+
case res[:error]
|
98
|
+
when "authorization_pending"
|
99
|
+
# dont hit the endpoint too fast such that we get rate limited
|
100
|
+
sleep interval
|
101
|
+
next
|
102
|
+
when "slow_down"
|
103
|
+
# increase polling interval incase rate limited
|
104
|
+
interval = res["interval"]
|
105
|
+
sleep interval
|
106
|
+
next
|
107
|
+
when "access_denied"
|
108
|
+
# user clicks cancel button
|
109
|
+
raise(StandardError, "Access denied while authorizing #{provider} access.")
|
110
|
+
break
|
111
|
+
else
|
112
|
+
raise(StandardError, res)
|
113
|
+
break
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# We won't be running this script regularly. Thus storing in /tmp/
|
118
|
+
# for temporary reuse of token till machine is rebooted
|
119
|
+
File.write(provider_local_token_path, res[:access_token])
|
120
|
+
FileUtils.chmod(0600, provider_local_token_path)
|
121
|
+
|
122
|
+
self.access_token = res[:access_token]
|
123
|
+
ui.info("You've been authenticated!")
|
124
|
+
break
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def access_token_present?
|
129
|
+
!(access_token.nil? || access_token.empty?)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "octokit"
|
4
|
+
|
5
|
+
require_relative "../base"
|
6
|
+
require_relative "../../exception_handler"
|
7
|
+
require_relative "auth"
|
8
|
+
require_relative "../../utils"
|
9
|
+
|
10
|
+
module Neetob
|
11
|
+
class CLI
|
12
|
+
module Github
|
13
|
+
class Base < CLI::Base
|
14
|
+
include Utils
|
15
|
+
attr_accessor :client
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
super()
|
19
|
+
auth_client = Auth.new(**read_and_parse_auth_params_from_env)
|
20
|
+
unless auth_client.token_persisted?
|
21
|
+
auth_client.start_oauth2_device_flow
|
22
|
+
end
|
23
|
+
@client = Octokit::Client.new(access_token: auth_client.access_token)
|
24
|
+
end
|
25
|
+
|
26
|
+
def check_for_apps_and_all_neeto_repos_option(apps, all_neeto_repos)
|
27
|
+
if (apps.nil? && !all_neeto_repos) || (!apps.nil? && all_neeto_repos)
|
28
|
+
ui.error("Please provide either \"apps\" or \"all-neeto-repos\" option.")
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def read_and_parse_auth_params_from_env
|
36
|
+
hash_with_keys_of_string_type = JSON.parse(ENV["AUTH_PARAMS"])
|
37
|
+
symbolize_keys(hash_with_keys_of_string_type)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require_relative "labels/commands"
|
5
|
+
require_relative "issues/commands"
|
6
|
+
require_relative "search"
|
7
|
+
require_relative "protect_branch"
|
8
|
+
require_relative "login"
|
9
|
+
require_relative "make_pr/commands"
|
10
|
+
|
11
|
+
module Neetob
|
12
|
+
class CLI
|
13
|
+
module Github
|
14
|
+
class Commands < Thor
|
15
|
+
desc "labels", "Interact with the labels of Github repos"
|
16
|
+
subcommand "labels", Labels::Commands
|
17
|
+
|
18
|
+
desc "issues", "Interact with the issues of Github repos"
|
19
|
+
subcommand "issues", Issues::Commands
|
20
|
+
|
21
|
+
desc "make-pr", "Create PRs across multiple Github repos"
|
22
|
+
subcommand "make_pr", MakePr::Commands
|
23
|
+
|
24
|
+
desc "search", "Find the lines matching the given keyword in the specified file across all the neeto apps"
|
25
|
+
option :apps, type: :array, aliases: "-a", required: true, desc: "Github app names. Can be matched using the '*' wildcard. Example: \"neeto*\" \"neeto-cal-web\""
|
26
|
+
option :keyword, type: :string, aliases: "-k", desc: "Keyword which needs to be searched", required: true
|
27
|
+
option :path, type: :string, aliases: "-p",
|
28
|
+
desc: "Path of the file in which you want to search the given keyword.", required: true
|
29
|
+
def search
|
30
|
+
Search.new(options[:apps], options[:keyword], options[:path], options[:sandbox]).run
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "login", "Update the Github access token by authenticating via browser"
|
34
|
+
def login
|
35
|
+
Login.new
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "protect_branch", "Protect the specified branch with the given rules"
|
39
|
+
option :branch, type: :string, aliases: "-b",
|
40
|
+
desc: "Name of the branch whose protections rules needs to be updated", required: true
|
41
|
+
option :path, type: :string, aliases: "-p",
|
42
|
+
desc: "The JSON file path which specify all the required rules for branch protection"
|
43
|
+
option :apps, type: :array, aliases: "-a", desc: "Github app names. Can be matched using the '*' wildcard. Example: \"neeto*\" \"neeto-cal-web\""
|
44
|
+
option :all_neeto_repos, type: :boolean, aliases: "--all",
|
45
|
+
desc: "Use this flag for working with all neeto repos", default: false
|
46
|
+
def protect_branch
|
47
|
+
ProtectBranch.new(
|
48
|
+
options[:branch], options[:apps], options[:path], options[:sandbox],
|
49
|
+
options[:all_neeto_repos]).run
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
|
5
|
+
require_relative "list"
|
6
|
+
require_relative "create"
|
7
|
+
require_relative "../../sub_command_base"
|
8
|
+
|
9
|
+
module Neetob
|
10
|
+
class CLI
|
11
|
+
module Github
|
12
|
+
module Issues
|
13
|
+
class Commands < SubCommandBase
|
14
|
+
class_option :apps,
|
15
|
+
{
|
16
|
+
type: :array, aliases: "-a", required: true,
|
17
|
+
desc: "Github app names. Can be matched using the '*' wildcard. Example: \"neeto*\" \"neeto-cal-web\""
|
18
|
+
}
|
19
|
+
|
20
|
+
desc "list", "List the issues in the Github repos"
|
21
|
+
option :state, type: :string, default: "open", aliases: "-s", desc: "State of the issues. Can be open or closed."
|
22
|
+
option :assignee, type: :string, desc: "Username of the current assignee. Can also use \"none\"."
|
23
|
+
option :label, type: :string, aliases: "-l", desc: "Label name to filter out the issues"
|
24
|
+
option :count, type: :boolean, aliases: "-c", desc: "Also shows the count of issues"
|
25
|
+
option :search, type: :string, desc: "Can provide custom query to filter or sort issues. Example: \"created:2022-11-07..2022-11-08 sort:comments-asc\""
|
26
|
+
def list
|
27
|
+
List.new(
|
28
|
+
options[:apps], options[:assignee], options[:state], options[:search], options[:count],
|
29
|
+
options[:label], options[:sandbox]).run
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "create", "Create a issue in the Github repos"
|
33
|
+
option :title, type: :string, required: true, aliases: "-t", desc: "Title of the issue"
|
34
|
+
option :description, type: :string, aliases: "-d", desc: "Description of the issue"
|
35
|
+
option :assignee, type: :string, desc: "Username of the user you want to assign this issue."
|
36
|
+
option :labels, type: :string, desc: "List of comma separated labels you want to add in this issue. Example: \"--labels bug,ui\""
|
37
|
+
def create
|
38
|
+
Create.new(
|
39
|
+
options[:apps],
|
40
|
+
options[:title],
|
41
|
+
options[:description],
|
42
|
+
options[:assignee],
|
43
|
+
options[:labels],
|
44
|
+
options[:sandbox]
|
45
|
+
).run
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
|
5
|
+
require_relative "../base"
|
6
|
+
|
7
|
+
module Neetob
|
8
|
+
class CLI
|
9
|
+
module Github
|
10
|
+
module Issues
|
11
|
+
class Create < Base
|
12
|
+
attr_accessor :apps, :issue_title, :issue_description, :issue_assignee, :issue_labels, :sandbox
|
13
|
+
|
14
|
+
def initialize(apps, issue_title, issue_description = "", issue_assignee = "", issue_labels = "",
|
15
|
+
sandbox = false)
|
16
|
+
super()
|
17
|
+
@apps = apps
|
18
|
+
@issue_title = issue_title
|
19
|
+
@issue_description = issue_description
|
20
|
+
@issue_assignee = issue_assignee
|
21
|
+
@issue_labels = issue_labels
|
22
|
+
@sandbox = sandbox
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
matching_apps = find_all_matching_apps(apps, :github, sandbox)
|
27
|
+
matching_apps.each do |app|
|
28
|
+
ui.info("\n Creating issue in \"#{app}\" \n")
|
29
|
+
begin
|
30
|
+
issue_options = { assignee: issue_assignee, labels: issue_labels }
|
31
|
+
issue = client.create_issue(app, issue_title, issue_description, issue_options)
|
32
|
+
ui.success("Created the issue successfully \nLink: #{issue[:html_url]}")
|
33
|
+
rescue StandardError => e
|
34
|
+
ExceptionHandler.new(e).process
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "action_view"
|
5
|
+
|
6
|
+
require_relative "../base"
|
7
|
+
|
8
|
+
module Neetob
|
9
|
+
class CLI
|
10
|
+
module Github
|
11
|
+
module Issues
|
12
|
+
class List < Base
|
13
|
+
include ActionView::Helpers::DateHelper
|
14
|
+
|
15
|
+
attr_accessor :apps, :issue_state, :issue_assignee, :issue_search_query, :show_issues_count, :issue_label,
|
16
|
+
:sandbox
|
17
|
+
|
18
|
+
def initialize(apps, issue_assignee = "", issue_state = "open",
|
19
|
+
issue_search_query = "", show_issues_count = false, issue_label = "",
|
20
|
+
sandbox = false)
|
21
|
+
super()
|
22
|
+
@apps = apps
|
23
|
+
@issue_state = issue_state
|
24
|
+
@issue_assignee = issue_assignee
|
25
|
+
@show_issues_count = show_issues_count
|
26
|
+
@issue_search_query = issue_search_query
|
27
|
+
@issue_label = issue_label
|
28
|
+
@sandbox = sandbox
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
matching_apps = find_all_matching_apps(apps, :github, sandbox)
|
33
|
+
matching_apps.each do |app|
|
34
|
+
ui.info("\n Issues of #{app} \n")
|
35
|
+
begin
|
36
|
+
issues = client.search_issues(uri_with_query_options(app))
|
37
|
+
ui.info("There are #{issues[:total_count]} issues with matching query") if show_issues_count
|
38
|
+
table_rows = create_table(issues[:items])
|
39
|
+
table = Terminal::Table.new headings: table_columns, rows: table_rows
|
40
|
+
ui.success(table)
|
41
|
+
rescue StandardError => e
|
42
|
+
ExceptionHandler.new(e).process
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def table_columns
|
50
|
+
issue_label.nil? ?
|
51
|
+
["Title", "Created at", "Assignee", "Link"] :
|
52
|
+
["Title", "Created at", "Assignee", "Link", "Labels"]
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_table(issues)
|
56
|
+
issues.map do |issue|
|
57
|
+
row_data = [
|
58
|
+
issue[:title],
|
59
|
+
"#{distance_of_time_in_words(issue[:created_at], Time.now)} ago",
|
60
|
+
issue.dig(:assignee, :login),
|
61
|
+
issue[:html_url]
|
62
|
+
]
|
63
|
+
issue_label.nil? ? row_data : row_data.push(labels_names(issue[:labels]))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def uri_with_query_options(app)
|
68
|
+
uri = "repo:#{app} is:issue state:#{issue_state}"
|
69
|
+
unless issue_label.nil?
|
70
|
+
check_valid_labels(app)
|
71
|
+
uri.concat(" label:#{issue_label}")
|
72
|
+
end
|
73
|
+
uri.concat(issue_assignee == "none" ? " no:assignee" : " assignee:#{issue_assignee}") if issue_assignee
|
74
|
+
"#{uri} #{issue_search_query}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def labels_names(labels)
|
78
|
+
labels.map { |label| label[:name] }.join(", ")
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_valid_labels(app)
|
82
|
+
issue_label.split(",").each { |label| valid_label?(app, label) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def valid_label?(app, label)
|
86
|
+
client.label(app, label)
|
87
|
+
rescue Octokit::NotFound => e
|
88
|
+
ui.error("There is no \"#{label}\" label in the \"#{app}\" repo.")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
|
5
|
+
require_relative "list"
|
6
|
+
require_relative "show"
|
7
|
+
require_relative "upsert"
|
8
|
+
require_relative "delete_all"
|
9
|
+
require_relative "delete"
|
10
|
+
require_relative "update"
|
11
|
+
require_relative "../../sub_command_base"
|
12
|
+
|
13
|
+
module Neetob
|
14
|
+
class CLI
|
15
|
+
module Github
|
16
|
+
module Labels
|
17
|
+
class Commands < SubCommandBase
|
18
|
+
class_option :apps,
|
19
|
+
type: :array, aliases: "-a",
|
20
|
+
desc: "Github app names. Can be matched using the '*' wildcard. Example: \"neeto*\" \"neeto-cal-web\""
|
21
|
+
class_option :all_neeto_repos,
|
22
|
+
type: :boolean, aliases: "--all", default: false,
|
23
|
+
desc: "Use this flag for working with all neeto repos"
|
24
|
+
|
25
|
+
desc "list", "List all the labels in the Github repos"
|
26
|
+
def list
|
27
|
+
List.new(options[:apps], options[:sandbox], options[:all_neeto_repos]).run
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "show", "Show details about the given label in the Github repos"
|
31
|
+
option :name, type: :string, aliases: "-n", required: true, desc: "Name of the label"
|
32
|
+
def show
|
33
|
+
Show.new(options[:apps], options[:name], options[:sandbox], options[:all_neeto_repos]).run
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "upsert", "Create and update labels in the Github repos"
|
37
|
+
option :path, type: :string, aliases: "-p", desc: "The JSON file path which has a list of all the required labels. Each label should have name. The color and description are optional"
|
38
|
+
def upsert
|
39
|
+
Upsert.new(options[:apps], options[:path], options[:sandbox], options[:all_neeto_repos]).run
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "delete_all", "Delete all the labels from the Github repos"
|
43
|
+
def delete_all
|
44
|
+
DeleteAll.new(options[:apps], options[:sandbox], options[:all_neeto_repos]).run
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "delete", "Delete some labels from the Github repos"
|
48
|
+
option :labels, type: :array, required: true, desc: "Labels you want to delete from the repos."
|
49
|
+
def delete
|
50
|
+
Delete.new(options[:apps], options[:labels], options[:sandbox], options[:all_neeto_repos]).run
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "update", "Update a label name in Github repos"
|
54
|
+
option :old_name, type: :string, required: true, desc: "Current label name which needs to be updated"
|
55
|
+
option :new_name, type: :string, required: true, desc: "New name for the updated label"
|
56
|
+
def update
|
57
|
+
Update.new(
|
58
|
+
options[:apps], options[:old_name], options[:new_name], options[:sandbox],
|
59
|
+
options[:all_neeto_repos]).run
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|