rails_template_18f 0.1.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +4 -1
  5. data/README.md +16 -19
  6. data/exe/rails_template_18f +60 -0
  7. data/lib/generators/rails_template18f/active_storage/active_storage_generator.rb +135 -0
  8. data/lib/generators/rails_template18f/active_storage/templates/app/jobs/file_scan_job.rb +33 -0
  9. data/lib/generators/rails_template18f/active_storage/templates/app/models/file_upload.rb +25 -0
  10. data/lib/generators/rails_template18f/active_storage/templates/doc/adr/clamav.md.tt +30 -0
  11. data/lib/generators/rails_template18f/active_storage/templates/spec/jobs/file_scan_job_spec.rb +35 -0
  12. data/lib/generators/rails_template18f/active_storage/templates/spec/models/file_upload_spec.rb +38 -0
  13. data/lib/generators/rails_template18f/circleci/circleci_generator.rb +26 -10
  14. data/lib/generators/rails_template18f/cloud_gov_config/cloud_gov_config_generator.rb +28 -0
  15. data/lib/generators/rails_template18f/cloud_gov_config/templates/app/models/cloud_gov_config.rb +15 -0
  16. data/lib/generators/rails_template18f/cloud_gov_config/templates/spec/models/cloud_gov_config_spec.rb +44 -0
  17. data/lib/generators/rails_template18f/dap/dap_generator.rb +72 -0
  18. data/lib/generators/rails_template18f/github_actions/github_actions_generator.rb +27 -11
  19. data/lib/generators/rails_template18f/i18n/i18n_generator.rb +107 -0
  20. data/{templates → lib/generators/rails_template18f/i18n/templates}/config/locales/en.yml.tt +3 -3
  21. data/{templates → lib/generators/rails_template18f/i18n/templates}/config/locales/es.yml +3 -3
  22. data/{templates → lib/generators/rails_template18f/i18n/templates}/config/locales/fr.yml +3 -6
  23. data/{templates → lib/generators/rails_template18f/i18n/templates}/config/locales/zh.yml +0 -0
  24. data/lib/generators/rails_template18f/i18n_js/i18n_js_generator.rb +60 -0
  25. data/lib/generators/rails_template18f/i18n_js/templates/lib/tasks/i18n.rake +9 -0
  26. data/lib/generators/rails_template18f/newrelic/newrelic_generator.rb +79 -0
  27. data/{templates/config/newrelic.yml → lib/generators/rails_template18f/newrelic/templates/config/newrelic.yml.tt} +7 -7
  28. data/lib/generators/rails_template18f/sidekiq/sidekiq_generator.rb +70 -0
  29. data/lib/generators/rails_template18f/sidekiq/templates/config/initializers/redis.rb +14 -0
  30. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/README.md.tt +0 -0
  31. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/bootstrap/import.sh +0 -0
  32. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/bootstrap/main.tf.tt +3 -3
  33. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/bootstrap/providers.tf +0 -0
  34. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/bootstrap/run.sh.tt +1 -1
  35. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/teardown_creds.sh.tt +5 -0
  36. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/bootstrap/variables.tf +0 -0
  37. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/create_space_deployer.sh +0 -0
  38. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/destroy_space_deployer.sh +0 -0
  39. data/lib/generators/rails_template18f/terraform/templates/terraform/production/main.tf.tt +82 -0
  40. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/production/providers.tf.tt +0 -0
  41. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/production/variables.tf +0 -0
  42. data/lib/generators/rails_template18f/terraform/templates/terraform/shared/clamav/main.tf.tt +50 -0
  43. data/{templates/terraform/shared/database → lib/generators/rails_template18f/terraform/templates/terraform/shared/clamav}/providers.tf +0 -0
  44. data/lib/generators/rails_template18f/terraform/templates/terraform/shared/clamav/variables.tf +47 -0
  45. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/shared/database/main.tf.tt +0 -0
  46. data/{templates/terraform/shared/domain → lib/generators/rails_template18f/terraform/templates/terraform/shared/database}/providers.tf +0 -0
  47. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/shared/database/variables.tf +0 -0
  48. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/shared/domain/main.tf.tt +1 -1
  49. data/{templates/terraform/shared/s3 → lib/generators/rails_template18f/terraform/templates/terraform/shared/domain}/providers.tf +0 -0
  50. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/shared/domain/variables.tf +0 -0
  51. data/lib/generators/rails_template18f/terraform/templates/terraform/shared/redis/main.tf.tt +23 -0
  52. data/lib/generators/rails_template18f/terraform/templates/terraform/shared/redis/providers.tf +16 -0
  53. data/lib/generators/rails_template18f/terraform/templates/terraform/shared/redis/variables.tf +42 -0
  54. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/shared/s3/main.tf +0 -0
  55. data/lib/generators/rails_template18f/terraform/templates/terraform/shared/s3/providers.tf +16 -0
  56. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/shared/s3/variables.tf +0 -0
  57. data/lib/generators/rails_template18f/terraform/templates/terraform/staging/main.tf.tt +62 -0
  58. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/staging/providers.tf.tt +0 -0
  59. data/{templates → lib/generators/rails_template18f/terraform/templates}/terraform/staging/variables.tf +0 -0
  60. data/lib/generators/rails_template18f/terraform/terraform_generator.rb +84 -0
  61. data/lib/rails_template18f/app_updater.rb +19 -0
  62. data/lib/rails_template18f/generators/base.rb +53 -0
  63. data/lib/rails_template18f/generators/cloud_gov_options.rb +53 -0
  64. data/lib/rails_template18f/generators/pipeline_options.rb +18 -0
  65. data/lib/rails_template18f/generators.rb +11 -0
  66. data/lib/rails_template18f/version.rb +1 -1
  67. data/lib/rails_template_18f.rb +1 -4
  68. data/rails-template-18f.gemspec +1 -0
  69. data/template.rb +84 -148
  70. data/templates/README.md.tt +7 -44
  71. data/templates/config/deployment/staging.yml +1 -1
  72. data/templates/config/environments/ci.rb +0 -1
  73. data/templates/doc/compliance/apps/application.boundary.md.tt +4 -31
  74. data/templates/githooks/{pre-commit.tt → pre-commit} +0 -15
  75. data/templates/manifest.yml.tt +1 -2
  76. metadata +77 -34
  77. data/lib/rails_template18f/terraform_options.rb +0 -68
  78. data/templates/terraform/bootstrap/teardown_creds.sh.tt +0 -5
  79. data/templates/terraform/production/main.tf.tt +0 -50
  80. data/templates/terraform/staging/main.tf.tt +0 -30
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe CloudGovConfig, type: :model do
6
+ subject { described_class }
7
+
8
+ describe ".dig" do
9
+ context "VCAP_SERVICES is blank" do
10
+ it "returns nil" do
11
+ expect(subject.dig(:s3, :credentials, :bucket)).to be_nil
12
+ end
13
+ end
14
+
15
+ context "VCAP_SERVICES is set" do
16
+ let(:bucket_name) { "bucket-name" }
17
+ let(:vcap) {
18
+ {
19
+ s3: [
20
+ {
21
+ credentials: {
22
+ bucket: bucket_name
23
+ }
24
+ }
25
+ ]
26
+ }
27
+ }
28
+
29
+ around do |example|
30
+ ClimateControl.modify VCAP_SERVICES: vcap.to_json do
31
+ example.run
32
+ end
33
+ end
34
+
35
+ it "can find a path" do
36
+ expect(subject.dig(:s3, :credentials, :bucket)).to eq bucket_name
37
+ end
38
+
39
+ it "returns nil for a missing path" do
40
+ expect(subject.dig(:s3, :credentials, :other)).to be_nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RailsTemplate18f
6
+ module Generators
7
+ class DapGenerator < ::Rails::Generators::Base
8
+ include Base
9
+
10
+ class_option :agency_code, default: "GSA", desc: "Agency code to track DAP metrics"
11
+
12
+ desc <<~DESC
13
+ Description:
14
+ Install JS snippet for Digital Analytics Program (DAP)
15
+ DESC
16
+
17
+ def update_content_security_policy
18
+ csp_file = "config/initializers/content_security_policy.rb"
19
+ gsub_file csp_file, /(policy.img_src .*)$/, '\1, "https://www.google-analytics.com"'
20
+ gsub_file csp_file, /(policy.script_src .*)$/, '\1, "https://dap.digitalgov.gov", "https://www.google-analytics.com"'
21
+ if file_content(csp_file).match?(/policy.connect_src/)
22
+ gsub_file csp_file, /(policy.connect_src .*)$/, '\1, "https://dap.digitalgov.gov", "https://www.google-analytics.com"'
23
+ else
24
+ gsub_file csp_file, /((#?)(\s+)policy.script_src .*)$/, "\\1\n\\2\\3policy.connect_src :self, \"https://dap.digitalgov.gov\", \"https://www.google-analytics.com\""
25
+ end
26
+ end
27
+
28
+ def install_js_snippet
29
+ insert_into_file "app/views/layouts/application.html.erb", <<EODAP, before: /^\s+<\/head>/
30
+
31
+ <% if Rails.env.production? %>
32
+ <!-- We participate in the US government's analytics program. See the data at analytics.usa.gov. -->
33
+ <%= javascript_include_tag "https://dap.digitalgov.gov/Universal-Federated-Analytics-Min.js?agency=#{options[:agency_code]}", async: true, id:"_fed_an_ua_tag" %>
34
+ <% end %>
35
+ EODAP
36
+ end
37
+
38
+ def update_readme
39
+ insertion_regex = /^## Documentation$/
40
+ if file_content("README.md").match?(insertion_regex)
41
+ insert_into_file "README.md", readme, before: insertion_regex
42
+ else
43
+ append_to_file "README.md", readme
44
+ end
45
+ end
46
+
47
+ def update_boundary_diagram
48
+ boundary_filename = "doc/compliance/apps/application.boundary.md"
49
+ insert_into_file boundary_filename, <<EOB, after: "Boundary(gsa_saas, \"GSA-authorized SaaS\") {\n"
50
+ System_Ext(dap, "DAP", "Analytics collection")
51
+ EOB
52
+ insert_into_file boundary_filename, <<~EOB, before: "@enduml"
53
+ Rel(browser, dap, "reports usage", "https (443)")
54
+ Rel(developer, dap, "View traffic statistics", "https GET (443)")
55
+ EOB
56
+ end
57
+
58
+ no_tasks do
59
+ def readme
60
+ <<~EOM
61
+ ## Analytics
62
+
63
+ Digital Analytics Program (DAP) code has been included for the Production environment, associated with #{options[:agency_code]}.
64
+
65
+ If #{app_name.titleize} is for another agency, update the agency line in `app/views/layouts/application.html.erb`
66
+
67
+ EOM
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails/generators"
4
+
3
5
  module RailsTemplate18f
4
6
  module Generators
5
7
  class GithubActionsGenerator < ::Rails::Generators::Base
6
- include ::Rails::Generators::AppName
7
- include RailsTemplate18f::TerraformOptions
8
+ include Base
9
+ include PipelineOptions
8
10
 
9
11
  class_option :node_version, desc: "Node version to test against in actions"
10
12
 
@@ -13,10 +15,6 @@ module RailsTemplate18f
13
15
  Install Github Actions workflow files
14
16
  DESC
15
17
 
16
- def self.source_root
17
- @source_root ||= File.expand_path("templates", __dir__)
18
- end
19
-
20
18
  def install_actions
21
19
  directory "github", ".github"
22
20
  if !terraform?
@@ -26,10 +24,28 @@ module RailsTemplate18f
26
24
  end
27
25
 
28
26
  def update_readme
29
- insert_into_file "README.md", readme_cicd, after: "## CI/CD\n"
30
- insert_into_file "README.md", readme_staging_deploy, after: "#### Staging\n"
31
- insert_into_file "README.md", readme_prod_deploy, after: "#### Production\n"
32
- insert_into_file "README.md", readme_credentials, after: "#### Credentials and other Secrets\n"
27
+ if file_content("README.md").match?(/^## CI\/CD$/)
28
+ insert_into_file "README.md", readme_cicd, after: "## CI/CD\n"
29
+ insert_into_file "README.md", readme_staging_deploy, after: "#### Staging\n"
30
+ insert_into_file "README.md", readme_prod_deploy, after: "#### Production\n"
31
+ insert_into_file "README.md", readme_credentials, after: "#### Credentials and other Secrets\n"
32
+ else
33
+ append_to_file "README.md", <<~EOM
34
+ ## CI/CD
35
+ #{readme_cicd}
36
+
37
+ ### Deployment
38
+
39
+ #### Staging
40
+ #{readme_staging_deploy}
41
+
42
+ #### Production
43
+ #{readme_prod_deploy}
44
+
45
+ #### Credentials and other Secrets
46
+ #{readme_credentials}
47
+ EOM
48
+ end
33
49
  end
34
50
 
35
51
  def update_boundary_diagram
@@ -48,7 +64,7 @@ EOB
48
64
  readme_filename = "terraform/README.md"
49
65
  insert_into_file readme_filename, " |- .force-action-apply\n", after: " |- secrets.auto.tfvars\n"
50
66
  insert_into_file readme_filename, <<~EOM, after: /- `secrets.auto.tfvars`.*$/
51
- - `.force-action-apply` is a file that can be updated to force GitHub Actions to run `terraform apply` during the deploy phase
67
+ \n- `.force-action-apply` is a file that can be updated to force GitHub Actions to run `terraform apply` during the deploy phase
52
68
  EOM
53
69
  end
54
70
 
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "bundler"
5
+
6
+ module RailsTemplate18f
7
+ module Generators
8
+ class I18nGenerator < ::Rails::Generators::Base
9
+ include Base
10
+
11
+ class_option :languages, default: "es,fr,zh", desc: "Comma separated list of supported language short codes"
12
+
13
+ desc <<~DESC
14
+ Description:
15
+ Install translation framework and configuration for given languages.
16
+ Always installs configuration for English
17
+ DESC
18
+
19
+ def install_helper_gem_and_tasks
20
+ return if file_content("Gemfile").match?(/gem "i18n-tasks"/)
21
+ gem_group :development, :test do
22
+ gem "i18n-tasks", "~> 0.9"
23
+ end
24
+ Bundler.with_original_env do
25
+ in_root do
26
+ run "bundle install"
27
+ run "cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/"
28
+ run "cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/"
29
+ end
30
+ end
31
+ insert_into_file "config/i18n-tasks.yml", "\n#{indent("- app/assets/builds", 4)}", after: "exclude:"
32
+ uncomment_lines "config/i18n-tasks.yml", "ignore_missing:"
33
+ insert_into_file "config/i18n-tasks.yml", indent(<<~EOM), after: "ignore_missing:\n"
34
+ - 'shared.languages.*'
35
+ - 'shared.header.{title,close,demo_banner,menu}'
36
+ EOM
37
+ end
38
+
39
+ def install_translations
40
+ inside "config/locales" do
41
+ template "en.yml"
42
+ languages.each do |lang|
43
+ copy_file "#{lang}.yml"
44
+ end
45
+ end
46
+ end
47
+
48
+ def configure_i18n
49
+ application "config.i18n.fallbacks = [:en]"
50
+ available_regex = /^(\s*config.i18n.available_locales).*$/
51
+ if file_content("config/application.rb").match?(available_regex)
52
+ gsub_file "config/application.rb", available_regex, "\\1 = #{supported_languages}"
53
+ else
54
+ application "config.i18n.available_locales = #{supported_languages}"
55
+ end
56
+ end
57
+
58
+ def install_nav_helper
59
+ inject_into_module "app/helpers/application_helper.rb", "ApplicationHelper", indent(<<~'EOH')
60
+ def format_active_locale(locale_string)
61
+ link_classes = "usa-nav__link"
62
+ if locale_string.to_sym == I18n.locale
63
+ link_classes = "#{link_classes} usa-current"
64
+ end
65
+ link_to t("shared.languages.#{locale_string}"), root_path(locale: locale_string), class: link_classes
66
+ end
67
+ EOH
68
+ end
69
+
70
+ def install_around_action
71
+ return if languages.empty?
72
+ inject_into_class "app/controllers/application_controller.rb", "ApplicationController", indent(<<~EOM)
73
+ around_action :switch_locale
74
+
75
+ def switch_locale(&action)
76
+ locale = params[:locale] || I18n.default_locale
77
+ I18n.with_locale(locale, &action)
78
+ end
79
+ EOM
80
+ end
81
+
82
+ def install_route
83
+ return if languages.empty?
84
+ return if file_content("config/routes.rb").match?(/scope "\(:locale\)"/)
85
+ regex = /(^.+\.routes\.draw do\s*$)\n(.*)^end$/m
86
+ gsub_file "config/routes.rb", regex, <<~'EOR'
87
+ \1
88
+ scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
89
+ # Your application routes go here
90
+ \2
91
+ end
92
+ end
93
+ EOR
94
+ end
95
+
96
+ private
97
+
98
+ def supported_languages
99
+ @supported_languages ||= [:en, *languages]
100
+ end
101
+
102
+ def languages
103
+ @languages ||= options[:languages].split(",").map(&:to_sym)
104
+ end
105
+ end
106
+ end
107
+ end
@@ -12,11 +12,11 @@ en:
12
12
  secure_heading: Secure .gov websites use HTTPS
13
13
  us_flag: U.S. Flag
14
14
  header:
15
- title: <%= app_name.titleize %>
16
- menu: Menu
17
15
  close: Close
18
- primary: Primary navigation
19
16
  demo_banner: TEST SITE - Do not use real personal information (demo purposes only) - TEST SITE
17
+ menu: Menu
18
+ primary: Primary navigation
19
+ title: <%= app_name.titleize %>
20
20
  languages:
21
21
  en: English
22
22
  es: Español
@@ -12,8 +12,8 @@ es:
12
12
  secure_heading: Los sitios web seguros .gov usan HTTPS
13
13
  us_flag: Bandera de Estados Unidos
14
14
  header:
15
- menu: Menú
16
15
  close: Cerrar
17
- primary: Navegacion primaria
18
16
  demo_banner: SITIO DE PRUEBA - No utilice información personal real (sólo para propósitos de demostración) - SITIO DE PRUEBA
19
- skip_link: Salte al contenido principal
17
+ menu: Menú
18
+ primary: Navegacion primaria
19
+ skip_link: Salte al contenido principal
@@ -8,15 +8,12 @@ fr:
8
8
  lock: Verrou
9
9
  locked_padlock: Verrou fermé
10
10
  official_site: Un site web officiel du gouvernement des États-Unis
11
- secure_description_html: Un <strong>verrou</strong> (%{lock_icon}) ou
12
- <strong>https://</strong> signifie que vous êtes connecté en toute
13
- sécurité au site Web .gov. Partagez des informations sensibles
14
- uniquement sur des sites Web officiels et sécurisés.
11
+ secure_description_html: Un <strong>verrou</strong> (%{lock_icon}) ou <strong>https://</strong> signifie que vous êtes connecté en toute sécurité au site Web .gov. Partagez des informations sensibles uniquement sur des sites Web officiels et sécurisés.
15
12
  secure_heading: Les sites Web sécurisés .gov utilisent HTTPS
16
13
  us_flag: Drapeau américain
17
14
  header:
18
- menu: Menu
19
15
  close: Fermer
20
- primary: Navigation primaire
21
16
  demo_banner: SITE DE TEST - N’utilisez pas de véritables données personnelles (il s’agit d’une démonstration seulement) - SITE DE TEST
17
+ menu: Menu
18
+ primary: Navigation primaire
22
19
  skip_link: Passer au contenu principal
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "bundler"
5
+
6
+ module RailsTemplate18f
7
+ module Generators
8
+ class I18nJsGenerator < ::Rails::Generators::Base
9
+ include Base
10
+
11
+ desc <<~DESC
12
+ Description:
13
+ Install and configure i18n-js gem to provide translations to JS code.
14
+
15
+ By default, will only export translations with keys that match `*.js.*`
16
+ DESC
17
+
18
+ def install_gem_and_tasks
19
+ return if file_content("Gemfile").match?(/gem "i18n-js"/)
20
+ gem "i18n-js", "~> 3.9"
21
+ Bundler.with_original_env do
22
+ in_root do
23
+ run "bundle install"
24
+ run "yarn add i18n-js"
25
+ generate "i18n:js:config"
26
+ end
27
+ end
28
+ append_to_file "config/i18n-js.yml", <<~EOYAML
29
+ # remove `only` to include all translations
30
+ translations:
31
+ - file: "app/assets/builds/translations.js"
32
+ only: "*.js.*"
33
+ EOYAML
34
+ end
35
+
36
+ def configure_asset_pipeline
37
+ copy_file "lib/tasks/i18n.rake"
38
+ environment "config.middleware.use I18n::JS::Middleware", env: :development
39
+ insert_into_file "app/views/layouts/application.html.erb", indent(<<~EOHTML, 4), after: /<%= stylesheet_link_tag "application".*$\n/
40
+ <%= javascript_include_tag "i18n", "data-turbo-track": "reload" %>
41
+ <%= javascript_include_tag "translations", "data-turbo-track": "reload" %>
42
+ EOHTML
43
+ append_to_file "app/assets/config/manifest.js", <<~EOJS
44
+ //= link i18n.js
45
+ //= link translations.js
46
+ EOJS
47
+ end
48
+
49
+ def ignore_generated_file
50
+ unless skip_git?
51
+ append_to_file ".gitignore", <<~EOM
52
+
53
+ # Generated by i18n-js
54
+ /public/javascripts/i18n.js
55
+ EOM
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,9 @@
1
+ # export translations as part of asset precompile
2
+
3
+ Rake::Task["assets:precompile"].enhance(["i18n:js:export"])
4
+
5
+ if Rake::Task.task_defined?("test:prepare")
6
+ Rake::Task["test:prepare"].enhance(["i18n:js:export"])
7
+ elsif Rake::Task.task_defined?("db:test:prepare")
8
+ Rake::Task["db:test:prepare"].enhance(["i18n:js:export"])
9
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RailsTemplate18f
6
+ module Generators
7
+ class NewrelicGenerator < ::Rails::Generators::Base
8
+ include Base
9
+
10
+ desc <<~DESC
11
+ Description:
12
+ Install NewRelic config for FedRAMP collection
13
+ DESC
14
+
15
+ def update_content_security_policy
16
+ csp_file = "config/initializers/content_security_policy.rb"
17
+ gsub_file csp_file, /(policy.script_src .*)$/, '\1, "https://js-agent.newrelic.com", "https://*.nr-data.net"'
18
+ if file_content(csp_file).match?(/policy.connect_src/)
19
+ gsub_file csp_file, /(policy.connect_src .*)$/, '\1, "https://*.nr-data.net"'
20
+ else
21
+ gsub_file csp_file, /((#?)(\s+)policy.script_src .*)$/, "\\1\n\\2\\3policy.connect_src :self, \"https://*.nr-data.net\""
22
+ end
23
+ end
24
+
25
+ def install_gem
26
+ gem "newrelic_rpm", "~> 8.4"
27
+ end
28
+
29
+ def install_config
30
+ template "config/newrelic.yml"
31
+ end
32
+
33
+ def update_cloud_gov_manifest
34
+ insert_into_file "manifest.yml", " NEW_RELIC_LOG: stdout\n", before: /^\s+processes:/
35
+ end
36
+
37
+ def update_readme
38
+ insertion_regex = /^## Documentation$/
39
+ if file_content("README.md").match?(insertion_regex)
40
+ insert_into_file "README.md", readme, before: insertion_regex
41
+ else
42
+ append_to_file "README.md", readme
43
+ end
44
+ end
45
+
46
+ def update_boundary_diagram
47
+ boundary_filename = "doc/compliance/apps/application.boundary.md"
48
+ insert_into_file boundary_filename, <<EOB, after: "Boundary(gsa_saas, \"GSA-authorized SaaS\") {\n"
49
+ System_Ext(newrelic, "New Relic", "Monitoring SaaS")
50
+ EOB
51
+ insert_into_file boundary_filename, <<~EOB, before: "@enduml"
52
+ Rel(app, newrelic, "reports telemetry (ruby agent)", "tcp (443)")
53
+ Rel(browser, newrelic, "reports ux metrics (javascript agent)", "https (443)")
54
+ Rel(developer, newrelic, "Manage performance", "https (443)")
55
+ EOB
56
+ end
57
+
58
+ no_tasks do
59
+ def readme
60
+ <<~EOM
61
+ ## Monitoring with New Relic
62
+
63
+ The [New Relic Ruby agent](https://docs.newrelic.com/docs/apm/agents/ruby-agent/getting-started/introduction-new-relic-ruby) has been installed for monitoring this application.
64
+
65
+ The config lives at `config/newrelic.yml`, and points to a [FEDRAMP version of the New Relic service as its host](https://docs.newrelic.com/docs/security/security-privacy/compliance/fedramp-compliant-endpoints/). To access the metrics dashboard, you will need to be connected to VPN.
66
+
67
+ ### Getting started
68
+
69
+ To get started sending metrics via New Relic APM:
70
+ 1. Add your New Relic license key to the Rails credentials with key `new_relic_key`.
71
+ 1. Optionally, update `app_name` entries in `config/newrelic.yml` with what is registered for your application in New Relic
72
+ 1. Comment out the `agent_enabled: false` line in `config/newrelic.yml`
73
+ 1. Add the [Javascript snippet provided by New Relic](https://docs.newrelic.com/docs/browser/browser-monitoring/installation/install-browser-monitoring-agent) into `application.html.erb`. It is recommended to vary this based on environment (i.e. include one snippet for staging and another for production).
74
+ EOM
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -10,14 +10,14 @@
10
10
 
11
11
  common: &default_settings
12
12
  # Required license key associated with your New Relic account.
13
- license_key: <%= Rails.application.credentials.new_relic_key %>
13
+ license_key: <%%= Rails.application.credentials.new_relic_key %>
14
14
  # FEDRAMP-specific New Relic host
15
15
  # https://docs.newrelic.com/docs/security/security-privacy/compliance/fedramp-compliant-endpoints/
16
16
  host: 'gov-collector.newrelic.com'
17
17
 
18
18
  # Your application name. Renaming here affects where data displays in New
19
19
  # Relic. For more details, see https://docs.newrelic.com/docs/apm/new-relic-apm/maintenance/renaming-applications
20
- app_name: <APPNAME>
20
+ app_name: <%= app_name.titleize %>
21
21
 
22
22
  monitor_mode: true
23
23
 
@@ -30,8 +30,8 @@ common: &default_settings
30
30
 
31
31
  # This line disables agent regardless of other settings.
32
32
  # To enable the New Relic agent:
33
- # 1) Replace <APPNAME> in this file with the application name you want to show in New Relic
34
- # 2) add the New Relic license keys to the appropriate encrypted credentials file(s)
33
+ # 1) add the New Relic license keys to the appropriate encrypted credentials file(s)
34
+ # 2) Optionally, update app_name entries in this file with the application name you want to show in New Relic
35
35
  # 3) Comment out the line below
36
36
  agent_enabled: false
37
37
 
@@ -44,7 +44,7 @@ common: &default_settings
44
44
  # If your application has other named environments, configure them here.
45
45
  development:
46
46
  <<: *default_settings
47
- app_name: <APPNAME> (Development)
47
+ app_name: <%= app_name.titleize %> (Development)
48
48
 
49
49
  test:
50
50
  <<: *default_settings
@@ -58,8 +58,8 @@ ci:
58
58
 
59
59
  staging:
60
60
  <<: *default_settings
61
- app_name: <APPNAME> (Staging)
61
+ app_name: <%= app_name.titleize %> (Staging)
62
62
 
63
63
  production:
64
64
  <<: *default_settings
65
- app_name: <APPNAME> (Production)
65
+ app_name: <%= app_name.titleize %> (Production)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RailsTemplate18f
6
+ module Generators
7
+ class SidekiqGenerator < ::Rails::Generators::Base
8
+ include Base
9
+
10
+ desc <<~DESC
11
+ Description:
12
+ Install Sidekiq and configure it as the ActiveJob backend
13
+ DESC
14
+
15
+ def install_gem
16
+ gem "sidekiq", "~> 6.4"
17
+ end
18
+
19
+ def configure_server_runner
20
+ append_to_file "Procfile.dev", "worker: bundle exec sidekiq\n"
21
+ insert_into_file "manifest.yml", indent(<<~EOYAML), after: /processes:$\n/
22
+ - type: worker
23
+ instances: ((worker_instances))
24
+ memory: ((worker_memory))
25
+ command: bundle exec sidekiq
26
+ EOYAML
27
+ insert_into_file "manifest.yml", "\n - #{app_name}-redis-((env))", after: "services:"
28
+ inside "config/deployment" do
29
+ append_to_file "staging.yml", <<~EOYAML
30
+ worker_instances: 1
31
+ worker_memory: 256M
32
+ EOYAML
33
+ append_to_file "production.yml", <<~EOYAML
34
+ worker_instances: 1
35
+ worker_memory: 512M
36
+ EOYAML
37
+ end
38
+ end
39
+
40
+ def configure_active_job
41
+ generate "rails_template18f:cloud_gov_config"
42
+ copy_file "config/initializers/redis.rb"
43
+ application "config.active_job.queue_adapter = :sidekiq"
44
+ end
45
+
46
+ def configure_sidekiq_ui
47
+ prepend_to_file "config/routes.rb", "require \"sidekiq/web\"\n\n"
48
+ route <<~EOR
49
+ if Rails.env.development?
50
+ mount Sidekiq::Web => "/sidekiq"
51
+ end
52
+ EOR
53
+ end
54
+
55
+ def update_boundary_diagram
56
+ boundary_filename = "doc/compliance/apps/application.boundary.md"
57
+
58
+ insert_into_file boundary_filename, indent(<<~EOB, 16), after: /ContainerDb\(app_db.*$\n/
59
+ Container(worker, "<&layers> Sidekiq workers", "Ruby #{ruby_version}, Sidekiq", "Perform background work and data processing")
60
+ ContainerDb(redis, "Redis Database", "AWS ElastiCache (Redis)", "Background job queue")
61
+ EOB
62
+ insert_into_file boundary_filename, <<~EOB, before: "@enduml"
63
+ Rel(app, redis, "enqueue job parameters", "redis")
64
+ Rel(worker, redis, "dequeues job parameters", "redis")
65
+ Rel(worker, app_db, "reads/writes primary data", "psql (5432)")
66
+ EOB
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.config.to_prepare do
4
+ redis_url = CloudGovConfig.dig "aws-elasticache-redis", "credentials", "uri"
5
+ if redis_url.present?
6
+ Sidekiq.configure_server do |config|
7
+ config.redis = {url: redis_url, ssl: true}
8
+ end
9
+
10
+ Sidekiq.configure_client do |config|
11
+ config.redis = {url: redis_url, ssl: true}
12
+ end
13
+ end
14
+ end
@@ -9,9 +9,9 @@ module "s3" {
9
9
  cf_api_url = local.cf_api_url
10
10
  cf_user = var.cf_user
11
11
  cf_password = var.cf_password
12
- cf_org_name = "<%= @cloud_gov_organization %>"
13
- cf_space_name = "<%= @cloud_gov_production_space %>"
14
- s3_service_name = local.s3_service_name<% if @cloud_gov_organization == "sandbox-gsa" %>
12
+ cf_org_name = "<%= cloud_gov_organization %>"
13
+ cf_space_name = "<%= cloud_gov_production_space %>"
14
+ s3_service_name = local.s3_service_name<% if cloud_gov_organization == "sandbox-gsa" %>
15
15
  s3_plan_name = "basic-sandbox"<% end %>
16
16
  }
17
17
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
3
  if [[ ! -f "secrets.auto.tfvars" ]]; then
4
- ../create_space_deployer.sh <%= @cloud_gov_production_space %> config-bootstrap-deployer > secrets.auto.tfvars
4
+ ../create_space_deployer.sh <%= cloud_gov_production_space %> config-bootstrap-deployer > secrets.auto.tfvars
5
5
  fi
6
6
 
7
7
  if [[ $# -gt 0 ]]; then
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ../destroy_space_deployer.sh <%= cloud_gov_production_space %> config-bootstrap-deployer
4
+
5
+ rm secrets.auto.tfvars