neetob-ud 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +10 -0
  3. data/.env +1 -0
  4. data/.rubocop.yml +596 -0
  5. data/.ruby-version +1 -0
  6. data/.semaphore/semaphore.yml +30 -0
  7. data/CHANGELOG.md +96 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/Gemfile +28 -0
  10. data/Gemfile.lock +250 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +412 -0
  13. data/Rakefile +16 -0
  14. data/config/secrets.yml +0 -0
  15. data/data/branch-protection-rules.json +25 -0
  16. data/data/config-vars-audit.json +18 -0
  17. data/data/config-vars-list.json +1 -0
  18. data/data/github-labels.json +197 -0
  19. data/env.sample +1 -0
  20. data/exe/neetob +25 -0
  21. data/install.sh +21 -0
  22. data/lib/neetob/cli/base.rb +96 -0
  23. data/lib/neetob/cli/fetchorupdate_repos/execute.rb +55 -0
  24. data/lib/neetob/cli/github/auth.rb +134 -0
  25. data/lib/neetob/cli/github/base.rb +42 -0
  26. data/lib/neetob/cli/github/commands.rb +54 -0
  27. data/lib/neetob/cli/github/issues/commands.rb +51 -0
  28. data/lib/neetob/cli/github/issues/create.rb +42 -0
  29. data/lib/neetob/cli/github/issues/list.rb +94 -0
  30. data/lib/neetob/cli/github/labels/commands.rb +65 -0
  31. data/lib/neetob/cli/github/labels/delete.rb +46 -0
  32. data/lib/neetob/cli/github/labels/delete_all.rb +50 -0
  33. data/lib/neetob/cli/github/labels/list.rb +38 -0
  34. data/lib/neetob/cli/github/labels/show.rb +39 -0
  35. data/lib/neetob/cli/github/labels/update.rb +42 -0
  36. data/lib/neetob/cli/github/labels/upsert.rb +64 -0
  37. data/lib/neetob/cli/github/login.rb +16 -0
  38. data/lib/neetob/cli/github/make_pr/base.rb +72 -0
  39. data/lib/neetob/cli/github/make_pr/commands.rb +37 -0
  40. data/lib/neetob/cli/github/make_pr/compliance_fix.rb +49 -0
  41. data/lib/neetob/cli/github/make_pr/script.rb +55 -0
  42. data/lib/neetob/cli/github/protect_branch.rb +48 -0
  43. data/lib/neetob/cli/github/search.rb +38 -0
  44. data/lib/neetob/cli/heroku/access/add.rb +38 -0
  45. data/lib/neetob/cli/heroku/access/commands.rb +41 -0
  46. data/lib/neetob/cli/heroku/access/list.rb +36 -0
  47. data/lib/neetob/cli/heroku/access/remove.rb +38 -0
  48. data/lib/neetob/cli/heroku/commands.rb +28 -0
  49. data/lib/neetob/cli/heroku/config_vars/audit.rb +64 -0
  50. data/lib/neetob/cli/heroku/config_vars/base.rb +19 -0
  51. data/lib/neetob/cli/heroku/config_vars/commands.rb +49 -0
  52. data/lib/neetob/cli/heroku/config_vars/list.rb +56 -0
  53. data/lib/neetob/cli/heroku/config_vars/remove.rb +39 -0
  54. data/lib/neetob/cli/heroku/config_vars/upsert.rb +80 -0
  55. data/lib/neetob/cli/heroku/execute.rb +37 -0
  56. data/lib/neetob/cli/local/commands.rb +19 -0
  57. data/lib/neetob/cli/local/ls.rb +29 -0
  58. data/lib/neetob/cli/sub_command_base.rb +17 -0
  59. data/lib/neetob/cli/ui.rb +41 -0
  60. data/lib/neetob/cli/users/audit.rb +121 -0
  61. data/lib/neetob/cli/users/commands.rb +30 -0
  62. data/lib/neetob/cli/users/commits.rb +171 -0
  63. data/lib/neetob/cli.rb +42 -0
  64. data/lib/neetob/exception_handler.rb +58 -0
  65. data/lib/neetob/utils.rb +16 -0
  66. data/lib/neetob/version.rb +5 -0
  67. data/lib/neetob.rb +10 -0
  68. data/neetob.gemspec +50 -0
  69. data/overcommit.yml +43 -0
  70. data/scripts/delete_unused_assets.rb +67 -0
  71. 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