neetob 0.5.69 → 0.5.77

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.env +2 -1
  3. data/.neetoci/default.yml +1 -1
  4. data/.ruby-version +1 -1
  5. data/Gemfile.lock +44 -21
  6. data/README.md +11 -0
  7. data/bookmarks.md +113 -113
  8. data/data/github-labels.json +80 -45
  9. data/data/repo-team-leads.json +82 -0
  10. data/exe/neetob +1 -1
  11. data/lib/neetob/cli/base.rb +35 -5
  12. data/lib/neetob/cli/cloudflare/automatic_https_rewrites.rb +34 -0
  13. data/lib/neetob/cli/cloudflare/base.rb +2 -2
  14. data/lib/neetob/cli/cloudflare/commands.rb +7 -0
  15. data/lib/neetob/cli/github/active_record_doctor.rb +1 -1
  16. data/lib/neetob/cli/github/brakeman.rb +1 -1
  17. data/lib/neetob/cli/github/bundle_audit.rb +1 -1
  18. data/lib/neetob/cli/github/issues/helpers.rb +17 -4
  19. data/lib/neetob/cli/github/make_pr/base.rb +1 -1
  20. data/lib/neetob/cli/github/repositories/pull_requests.rb +19 -0
  21. data/lib/neetob/cli/github/repositories/team_leads.rb +34 -0
  22. data/lib/neetob/cli/github/unused_assets_audit.rb +5 -1
  23. data/lib/neetob/cli/monthly_audit/github_issue_creation.rb +57 -17
  24. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/{bot_protection_enabled.rb → automatic_https_rewrites_is_enabled.rb} +11 -9
  25. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/main.rb +2 -2
  26. data/lib/neetob/cli/monthly_audit/instances_and_addons/cronitor/setup_correctly_for_landing_pages.rb +3 -2
  27. data/lib/neetob/cli/monthly_audit/instances_and_addons/honeybadger/setup_correctly_for_apps.rb +28 -29
  28. data/lib/neetob/cli/monthly_audit/instances_and_addons/main.rb +5 -5
  29. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/cloudfront_cdn_enabled.rb +0 -17
  30. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/essential_environment_variables_set.rb +0 -15
  31. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/main.rb +0 -3
  32. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/scheduled_exports_enabled.rb +2 -4
  33. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/ssl_certificates_over_thirty_days_from_expiry.rb +67 -34
  34. data/lib/neetob/cli/monthly_audit/misc/main.rb +1 -1
  35. data/lib/neetob/cli/monthly_audit/misc/sparkpost_sub_account_used_for_all_apps.rb +24 -18
  36. data/lib/neetob/cli/monthly_audit/security/code/active_record_doctor.rb +2 -2
  37. data/lib/neetob/cli/monthly_audit/security/code/brakeman.rb +7 -4
  38. data/lib/neetob/cli/monthly_audit/security/code/bundle_audit.rb +13 -4
  39. data/lib/neetob/cli/monthly_audit/security/code/fasterer.rb +2 -2
  40. data/lib/neetob/cli/monthly_audit/security/code/yarn_audit.rb +1 -1
  41. data/lib/neetob/cli/monthly_audit/security/github/dependabot_prs_merged.rb +20 -5
  42. data/lib/neetob/cli/monthly_audit/security/github/dependabot_turned_on.rb +25 -21
  43. data/lib/neetob/cli/neeto_deploy/autoscaling_config.rb +1 -1
  44. data/lib/neetob/cli/neeto_deploy/certificates.rb +1 -1
  45. data/lib/neetob/cli/neeto_deploy/commands.rb +7 -0
  46. data/lib/neetob/cli/neeto_deploy/config_vars/list.rb +1 -1
  47. data/lib/neetob/cli/neeto_deploy/config_vars/remove.rb +1 -1
  48. data/lib/neetob/cli/neeto_deploy/config_vars/upsert.rb +1 -1
  49. data/lib/neetob/cli/neeto_deploy/scheduled_exports.rb +1 -1
  50. data/lib/neetob/cli/neeto_deploy/unique_email_domains.rb +165 -0
  51. data/lib/neetob/cli/sre/base.rb +13 -13
  52. data/lib/neetob/cli/sre/check_essential_env.rb +7 -2
  53. data/lib/neetob/cli/sre/checklist.rb +2 -2
  54. data/lib/neetob/version.rb +1 -1
  55. data/neetob.gemspec +1 -1
  56. data/package.json +30 -0
  57. data/playwright.config.ts +39 -0
  58. data/scripts/config/.env.local +17 -0
  59. data/scripts/constants/auditData.ts +402 -0
  60. data/scripts/constants/routes.ts +30 -0
  61. data/scripts/constants/selectors.ts +4 -0
  62. data/scripts/constants/table.ts +30 -0
  63. data/scripts/constants/texts.ts +46 -0
  64. data/scripts/constants/userAgents.ts +14 -0
  65. data/scripts/utils/markdown.ts +23 -0
  66. data/scripts/workflows/dependabot.ts +104 -0
  67. data/scripts/workflows/honeybadger.ts +169 -0
  68. data/scripts/workflows/sparkpost.ts +204 -0
  69. data/tsconfig.json +35 -0
  70. data/yarn.lock +2216 -0
  71. metadata +24 -6
  72. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/auto_scaling_enabled.rb +0 -67
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../../../github/repositories/get_security_details.rb"
4
+ require "json"
4
5
 
5
6
  module Neetob
6
7
  class CLI
@@ -13,29 +14,32 @@ module Neetob
13
14
  end
14
15
 
15
16
  def run
16
- ui.success("### 1.2.1. [Manual] Checking whether dependabot is turned on for the repository")
17
- ui.info "\n"
18
- ui.info "#### Please manually check and add Yes/No for all the following checks on the Honeybadger dashboard for the apps listed in the table below:"
19
- ui.info "- Repository > Settings > Code security > Dependabot alerts are enabled"
20
- ui.info "- Repository > Settings > Code security > Dependabot security updates are enabled"
21
- ui.info "- Repository > Settings > Actions > Dependabot on Actions runners is enabled"
22
- ui.info "- Finally, set Audit Passed as Yes only if all the checks are passed for the app, otherwise set it as No, and add a comment in the Comments column"
23
- ui.info "\n"
17
+ ui.success("### 1.2.1. Checking whether dependabot is turned on for the repository")
18
+ result = run_dependabot_check
19
+ formatted_result = extract_json_array_from_output(result)
20
+ create_issue(formatted_result)
21
+ ui.print_table(formatted_result)
22
+ end
23
+
24
+ private
24
25
 
25
- repo_data = [[
26
- "Repository",
27
- "Dependabot alerts enabled",
28
- "Dependabot security updates enabled",
29
- "Dependabot on Actions runners",
30
- "Comments",
31
- "Audit Passed"
32
- ]
33
- ]
34
- NeetoCompliance::NeetoRepos.products.keys.each do |repo|
35
- repo_data << [repo, nil, nil, nil, nil]
26
+ def run_dependabot_check
27
+ `yarn audit:dependabot`
28
+ end
29
+
30
+ def create_issue(formatted_result)
31
+ formatted_result.drop(1).each do |result|
32
+ repo = result[0].split("]")[0].gsub("[", "")
33
+ audit_passed = result.last == "Yes"
34
+ if !audit_passed
35
+ comment = "Dependabot alerts or security updates are not enabled for this repository."
36
+ issue_url = GithubIssueCreation.new.create_issue(
37
+ repo:, title: "Fix Dependabot settings",
38
+ description: comment)
39
+ result[-1] += " #{issue_url}" if issue_url
40
+ end
41
+ end
36
42
  end
37
- ui.print_table(repo_data)
38
- end
39
43
  end
40
44
  end
41
45
  end
@@ -12,7 +12,7 @@ module Neetob
12
12
  end
13
13
 
14
14
  def run
15
- result = `neetodeploy autoscaling_config list -a #{app}`
15
+ result = `bundle exec neetodeploy autoscaling_config list -a #{app}`
16
16
  if Thread.current[:audit_mode]
17
17
  JSON.parse(result) rescue result
18
18
  else
@@ -14,7 +14,7 @@ module Neetob
14
14
  end
15
15
 
16
16
  def run
17
- certificates_command_result = `neetodeploy certificates list -a #{app}`
17
+ certificates_command_result = `bundle exec neetodeploy certificates list -a #{app}`
18
18
  parseable_result = certificates_command_result.strip.gsub("=>", ":")
19
19
 
20
20
  parsed_certificates_result = JSON.parse(parseable_result)
@@ -5,6 +5,7 @@ require_relative "config_vars/commands"
5
5
  require_relative "./autoscaling_config"
6
6
  require_relative "./scheduled_exports"
7
7
  require_relative "./certificates"
8
+ require_relative "./unique_email_domains"
8
9
 
9
10
  module Neetob
10
11
  class CLI
@@ -30,6 +31,12 @@ module Neetob
30
31
  def certificates
31
32
  Certificates.new(options[:app]).run
32
33
  end
34
+
35
+ desc "unique_email_domains", "Get the unique email domains for an app deployed on NeetoDeploy"
36
+ option :app, type: :string, aliases: :a, desc: "App name"
37
+ def unique_email_domains
38
+ UniqueEmailDomains.new(options[:app]).run
39
+ end
33
40
  end
34
41
  end
35
42
  end
@@ -20,7 +20,7 @@ module Neetob
20
20
  results = []
21
21
  matching_apps.each do |app|
22
22
  ui.info("\nConfig of #{app}\n") unless Thread.current[:audit_mode]
23
- result = `neetodeploy env list -a #{app}`
23
+ result = `bundle exec neetodeploy env list -a #{app}`
24
24
  results << result
25
25
  ui.success(result) unless Thread.current[:audit_mode]
26
26
  end
@@ -21,7 +21,7 @@ module Neetob
21
21
  matching_apps.each do |app|
22
22
  ui.info("\nWorking on #{app}")
23
23
  keys.each do |key|
24
- ui.info(`neetodeploy env unset #{key} -a #{app}`)
24
+ ui.info(`bundle exec neetodeploy env unset #{key} -a #{app}`)
25
25
  end
26
26
  end
27
27
  end
@@ -48,7 +48,7 @@ required_config_vars_with_project_keys_json_file_path, sandbox = false)
48
48
  def upsert_config_variables(app, vars)
49
49
  ui.info("\nWorking on #{app}")
50
50
  vars.each do |key, val|
51
- ui.info(`neetodeploy env set #{key}='#{val}' -a #{app}`)
51
+ ui.info(`bundle exec neetodeploy env set #{key}='#{val}' -a #{app}`)
52
52
  end
53
53
  end
54
54
 
@@ -12,7 +12,7 @@ module Neetob
12
12
  end
13
13
 
14
14
  def run
15
- result = `neetodeploy addon scheduled_exports_enabled -a #{app}`
15
+ result = `bundle exec neetodeploy addon scheduled_exports_enabled -a #{app}`
16
16
  if Thread.current[:audit_mode]
17
17
  result
18
18
  else
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module Neetob
6
+ class CLI
7
+ module NeetoDeploy
8
+ class UniqueEmailDomains < CLI::Base
9
+ attr_accessor :app
10
+
11
+ def initialize(app)
12
+ super()
13
+ @app = app
14
+ end
15
+
16
+ def run
17
+ ui.info("Executing for app: #{app}")
18
+ query = get_query_for_app(app)
19
+
20
+ return ui.error("No query defined for app: #{app}") unless query
21
+
22
+ Open3.popen3("neetodeploy exec -a #{app}") do |stdin, stdout, stderr, wait_thr|
23
+ return handle_forbidden_error(stderr) if forbidden?(stderr)
24
+
25
+ send_command(stdin, "bundle exec rails console")
26
+ sleep(2)
27
+ return handle_forbidden_error(stderr) if forbidden?(stderr)
28
+
29
+ stdin.puts query
30
+ sleep(1)
31
+ stdin.puts "exit" if wait_thr.alive?
32
+ sleep(1)
33
+ stdin.puts "exit" if wait_thr.alive?
34
+ stdin.close
35
+
36
+ output = safely_read(stdout, "stdout")
37
+ errors = safely_read(stderr, "stderr")
38
+
39
+ if (exit_status = wait_thr.value.exitstatus) == 0
40
+ email_domains = extract_email_domains(output)
41
+ ui.success("Unique email domains for #{app}:")
42
+
43
+ if email_domains.empty?
44
+ ui.info("No email domains found")
45
+ ui.info("Debug: Raw output length: #{output.length}")
46
+ ui.info("Debug: Output preview: #{output[0..200]}...")
47
+ else
48
+
49
+ email_domains.each { |domain| puts domain }
50
+ end
51
+ else
52
+ ui.error("Command failed with exit status: #{exit_status}")
53
+ ui.error("Error output: #{errors}") unless errors.empty?
54
+ end
55
+
56
+ exit_status == 0
57
+ end
58
+ rescue Errno::EPIPE
59
+ handle_forbidden_error
60
+ rescue => e
61
+ ui.error("Exception: #{e.message}")
62
+ ui.error("Exception class: #{e.class}")
63
+ false
64
+ end
65
+
66
+ private
67
+
68
+ def forbidden?(stderr)
69
+ error_output = stderr.read_nonblock(1024) rescue ""
70
+ error_output.include?('{"error":"Forbidden"}')
71
+ end
72
+
73
+ def handle_forbidden_error(stderr = nil)
74
+ ui.error("Access denied: You don't have permission to access this app or your session is not authenticated")
75
+ ui.error("Please run `neetodeploy login` and ensure you have access to the app: #{app}")
76
+ false
77
+ end
78
+
79
+ def send_command(stdin, command)
80
+ stdin.puts command
81
+ end
82
+
83
+ def safely_read(io, label)
84
+ io.read
85
+ rescue => e
86
+ ui.error("Error reading #{label}: #{e.message}")
87
+ ""
88
+ end
89
+
90
+ def get_query_for_app(app_name)
91
+ six_months_ago = (Time.now.utc - 6 * 30 * 24 * 60 * 60).strftime("%Y-%m-%d %H:%M:%S")
92
+
93
+ {
94
+ "neeto-form-web-production" =>
95
+ "User.joins(:forms).where('forms.updated_at >= ?', '#{six_months_ago}').pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
96
+
97
+ "neeto-record-web-production" =>
98
+ "User.joins(:recordings).where('recordings.updated_at >= ?', '#{six_months_ago}').pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
99
+
100
+ "neeto-kb-web-production" =>
101
+ "User.joins(:articles).where.not(articles: { slug: ['welcome', 'getting-started', 'getting-help', 'feedback'] }).pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
102
+
103
+ "neeto-code-web-production" =>
104
+ "User.joins(:projects).where('projects.updated_at >= ?', '#{six_months_ago}').pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
105
+
106
+ "neeto-playdash-web-production" =>
107
+ "Organization.joins(:users, projects: :runs).where('runs.updated_at >= ?', '#{six_months_ago}').pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
108
+
109
+ "neeto-desk-web-production" =>
110
+ "User.joins(:tickets).where.not(tickets: { number: [1,2,3,4,5] }).where('tickets.updated_at >= ?', '#{six_months_ago}').pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
111
+
112
+ "neeto-invoice-web-production" =>
113
+ "User.joins(:time_entries).where('time_entries.updated_at >= ?', '#{six_months_ago}').pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
114
+
115
+ "neeto-chat-web-production" =>
116
+ "User.joins(:conversations).where('conversations.updated_at >= ?', '#{six_months_ago}').pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
117
+
118
+ "neeto-quiz-web-production" =>
119
+ "User.joins(quizzes: :attempts).where('attempts.updated_at >= ?', '#{six_months_ago}').pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
120
+
121
+ "neeto-site-web-production" =>
122
+ "User.joins(sites: :pages).where('sites.updated_at >= :date OR pages.updated_at >= :date', date: '#{six_months_ago}').pluck(Arel.sql('DISTINCT SPLIT_PART(users.email, \\'@\\', 2)'))",
123
+
124
+ "neeto-cal-web-production" => <<~RUBY
125
+ result = ActiveRecord::Base.connection.execute("WITH active_users AS (
126
+ SELECT users.email AS email FROM users
127
+ WHERE (
128
+ EXISTS (
129
+ SELECT 1 FROM bookings_users
130
+ JOIN bookings ON bookings.id = bookings_users.booking_id
131
+ WHERE bookings.user_id = users.id AND bookings.updated_at >= '#{six_months_ago}'
132
+ )
133
+ OR EXISTS (
134
+ SELECT 1 FROM meetings_users
135
+ JOIN meetings ON meetings.id = meetings_users.meeting_id
136
+ WHERE meetings_users.user_id = users.id AND meetings.updated_at >= '#{six_months_ago}'
137
+ )
138
+ )
139
+ )
140
+ SELECT array_agg(DISTINCT SPLIT_PART(email, '@', 2)) AS unique_domains FROM active_users;");
141
+ unique_domains = result.first['unique_domains']
142
+ unique_domains = unique_domains[1...-1].split(',') if unique_domains.is_a?(String)
143
+ unique_domains
144
+ RUBY
145
+ }[app_name] || "User.pluck(:email).map { |e| e.split('@').last }.uniq"
146
+ end
147
+
148
+ def extract_email_domains(output)
149
+ domains = []
150
+
151
+ array_match = output.match(/=>\s*\n(\[.*?\])/m)
152
+ return [] unless array_match
153
+
154
+ array_content = array_match[1]
155
+
156
+ array_content.scan(/"([^"]+)"/).flatten.each do |domain|
157
+ domains << domain if domain.match?(/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/)
158
+ end
159
+
160
+ domains.uniq.sort
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -46,8 +46,6 @@ module Neetob
46
46
  "neeto-replay-web-staging",
47
47
  "neeto-site-web-production",
48
48
  "neeto-site-web-staging",
49
- "neeto-testify-web-production",
50
- "neeto-testify-web-staging",
51
49
  "neeto-tower-web-production",
52
50
  "neeto-tower-web-staging",
53
51
  "neeto-wireframe-web-production",
@@ -57,7 +55,9 @@ module Neetob
57
55
  "neeto-playdash-web-staging",
58
56
  "neeto-playdash-web-production",
59
57
  "neeto-engage-web-staging",
60
- "neeto-engage-web-production"
58
+ "neeto-engage-web-production",
59
+ "neeto-pay-web-production",
60
+ "neeto-pay-web-staging"
61
61
  ]
62
62
  }
63
63
 
@@ -254,16 +254,6 @@ module Neetob
254
254
  app: "neeto-site-web-production"
255
255
  }
256
256
  },
257
- "NeetoTestify": {
258
- "staging": {
259
- dns: "neetotestify.net",
260
- app: "neeto-testify-web-staging"
261
- },
262
- "production": {
263
- dns: "neetotestify.com",
264
- app: "neeto-testify-web-production"
265
- }
266
- },
267
257
  "NeetoTower": {
268
258
  "staging": {
269
259
  dns: "neetotower.net",
@@ -303,6 +293,16 @@ module Neetob
303
293
  dns: "neetoplaydash.com",
304
294
  app: "neeto-playdash-web-production"
305
295
  }
296
+ },
297
+ "NeetoPay": {
298
+ "staging": {
299
+ dns: "neetopay.net",
300
+ app: "neeto-pay-web-staging"
301
+ },
302
+ "production": {
303
+ dns: "neetopay.com",
304
+ app: "neeto-pay-web-production"
305
+ }
306
306
  }
307
307
  }
308
308
 
@@ -15,7 +15,11 @@ module Neetob
15
15
  "HONEYBADGER_JS_API_KEY",
16
16
  "NODE_MODULES_CACHE",
17
17
  "YARN_CACHE",
18
- "YARN_PRODUCTION"
18
+ "YARN_PRODUCTION",
19
+ "AUTH_APP_URL",
20
+ "DEFAULT_FROM_EMAIL",
21
+ "ORGANIZATION_SYNCING",
22
+ "SPARKPOST_DOMAIN"
19
23
  ]
20
24
  REQUIRED_KEYS_HEROKU = [
21
25
  "HEROKU_APP_NAME"
@@ -58,7 +62,7 @@ module Neetob
58
62
  def check_envs_neetodeploy(app)
59
63
  # TODO: Optimize once github.com/bigbinary/neeto-deploy-web/issues/3745 is done
60
64
  begin
61
- env_table = `neetodeploy env list -a #{app}`
65
+ env_table = `bundle exec neetodeploy env list -a #{app}`
62
66
  json_parse_result = JSON.parse(env_table) rescue nil
63
67
  if json_parse_result && json_parse_result["error"] == "Forbidden"
64
68
  if Thread.current[:audit_mode]
@@ -73,6 +77,7 @@ module Neetob
73
77
  match = line.match(/^\| (\w+) +\| (.+?) +\|$/)
74
78
  envs[match[1]] = match[2] if match
75
79
  end
80
+ REQUIRED_KEYS.filter! { |key| key != "AUTH_APP_URL" } if app == "neeto-auth-web-production"
76
81
  required_keys = REQUIRED_KEYS + REQUIRED_KEYS_NEETODEPLOY
77
82
  compare_envs(required_keys, envs, app)
78
83
  rescue => exception
@@ -45,9 +45,9 @@ module Neetob
45
45
  ui.info `neetob heroku autoscaling_config -a #{app_name} | sed 's/^/ /'`
46
46
  else
47
47
  ui.info "NeetoDeploy autoscaling status"
48
- ui.info `neetodeploy autoscaling_config list -a #{app_name} | sed 's/^/ /'`
48
+ ui.info `bundle exec neetodeploy autoscaling_config list -a #{app_name} | sed 's/^/ /'`
49
49
  ui.info "Scheduled exports status"
50
- ui.info `neetodeploy addon scheduled_exports_enabled -a #{app_name} | sed 's/^/ /'`
50
+ ui.info `bundle exec neetodeploy addon scheduled_exports_enabled -a #{app_name} | sed 's/^/ /'`
51
51
  end
52
52
  ui.info "Validating essential envs"
53
53
  ui.info `neetob sre check_essential_env -a #{app_name} | sed 's/^/ /'`
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Neetob
4
- VERSION = "0.5.69"
4
+ VERSION = "0.5.77"
5
5
  end
data/neetob.gemspec CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.require_paths = ["lib"]
31
31
 
32
32
  # Must have deps
33
- spec.add_dependency "thor", "~> 1.3.0" # for cli
33
+ spec.add_dependency "thor", "~> 1.5.0" # for cli
34
34
  spec.add_dependency "octokit", "~> 4.0" # for github client
35
35
  spec.add_dependency "terminal-table", "~> 3.0.2" # for building cli table
36
36
  spec.add_dependency "launchy", "~> 2.5.0" # for opening in browser
data/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "playwright-tests",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "audit:dependabot": "playwright test dependabot.ts",
8
+ "audit:honeybadger": "playwright test honeybadger.ts",
9
+ "audit:sparkpost": "playwright test sparkpost.ts"
10
+ },
11
+ "keywords": [],
12
+ "author": "",
13
+ "license": "ISC",
14
+ "devDependencies": {
15
+ "@playwright/test": "1.56.1",
16
+ "@types/node": "22.10.7"
17
+ },
18
+ "dependencies": {
19
+ "@bigbinary/neeto-cist": "1.0.17",
20
+ "@bigbinary/neeto-playwright-commons": "1.22.46",
21
+ "@bigbinary/neeto-playwright-reporter": "2.1.5",
22
+ "@faker-js/faker": "9.9.0",
23
+ "dayjs": "1.11.18",
24
+ "dotenv-expand": "^12.0.3",
25
+ "dotenv-webpack": "^8.1.1",
26
+ "lint-staged": "^16.2.6",
27
+ "puppeteer-extra-plugin-stealth": "2.11.2",
28
+ "ramda": "^0.32.0"
29
+ }
30
+ }
@@ -0,0 +1,39 @@
1
+ import { defineConfig, devices } from "@playwright/test";
2
+ import dotenv from "dotenv";
3
+ import dotenvExpand from "dotenv-expand";
4
+
5
+ const env = dotenv.config({
6
+ path: `scripts/config/.env.local`,
7
+ });
8
+ dotenvExpand.expand(env);
9
+
10
+ const neetoPlaywrightReporterConfig = {
11
+ apiKey: process.env.PLAYDASH_API_KEY,
12
+ ciBuildId: new Date().toString(),
13
+ projectKey: "9gnVzoWsfAnbcsmZugMrVUMo",
14
+ };
15
+
16
+ export default defineConfig({
17
+ testDir: "scripts",
18
+ fullyParallel: true,
19
+ forbidOnly: !!process.env.CI,
20
+ retries: process.env.CI ? 2 : 0,
21
+ workers: process.env.CI ? 1 : undefined,
22
+ reporter: [
23
+ ["@bigbinary/neeto-playwright-reporter", neetoPlaywrightReporterConfig],
24
+ // ["line"],
25
+ ],
26
+ use: {
27
+ trace: "on",
28
+ video: "on",
29
+ screenshot: "on",
30
+ },
31
+ timeout: 7 * 60_000,
32
+ projects: [
33
+ {
34
+ name: "chromium",
35
+ use: { ...devices["Desktop Chrome"] },
36
+ testMatch: "**/workflows/**/*.ts",
37
+ },
38
+ ],
39
+ });
@@ -0,0 +1,17 @@
1
+ # GitHub credentials for Dependabot audit
2
+ GITHUB_USERNAME=github_username
3
+ GITHUB_PASSWORD=github_password
4
+ GITHUB_2FA_SECRET_KEY=2fa_secret_key
5
+
6
+ # Honeybadger credentials
7
+ HONEYBADGER_EMAIL=honeybadger@example.com
8
+ HONEYBADGER_PASSWORD=honeybadger_password
9
+
10
+ # Sparkpost credentials
11
+ SPARKPOST_EMAIL=sparkpost@example.com
12
+ SPARKPOST_PASSWORD=sparkpost_password
13
+ NEETO_DEPLOY_EMAIL=neeto_deploy@example.com
14
+ NEETO_AUTOMATION_FASTMAIL_API_KEY=neeto_deploy_fast_mail_api_key
15
+
16
+ # Playwright reporter
17
+ PLAYDASH_API_KEY=playdash_api_key