neetob-ud 0.1.0
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 +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 +96 -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 +412 -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/neetob.gemspec +50 -0
- data/overcommit.yml +43 -0
- data/scripts/delete_unused_assets.rb +67 -0
- metadata +187 -0
data/exe/neetob
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dotenv"
|
5
|
+
require "bundler/setup"
|
6
|
+
|
7
|
+
Dotenv.load(
|
8
|
+
File.expand_path("../.env", __dir__)
|
9
|
+
)
|
10
|
+
|
11
|
+
base_path = File.expand_path("../lib", __dir__)
|
12
|
+
|
13
|
+
if File.exist?(base_path)
|
14
|
+
require_relative "../lib/neetob"
|
15
|
+
else
|
16
|
+
require "neetob"
|
17
|
+
end
|
18
|
+
|
19
|
+
args = ARGV
|
20
|
+
|
21
|
+
begin
|
22
|
+
Neetob::CLI.start(args, debug: true)
|
23
|
+
rescue Exception => e
|
24
|
+
Neetob::ExceptionHandler.new(e).process
|
25
|
+
end
|
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
|