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.
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