brut 0.5.0 → 0.8.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 (265) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/CHANGELOG.md +7 -0
  4. data/Dockerfile.dx +19 -0
  5. data/Gemfile.lock +1 -1
  6. data/README.md +19 -0
  7. data/assets/YouTubeThumb.pxd +0 -0
  8. data/bin/build +86 -0
  9. data/bin/ci +36 -0
  10. data/bin/docs +39 -9
  11. data/bin/publish +61 -0
  12. data/bin/setup +6 -0
  13. data/brut-css/bin/build +19 -0
  14. data/brut-css/bin/ci +19 -0
  15. data/brut-css/bin/docs +19 -0
  16. data/brut-css/bin/publish +21 -0
  17. data/brut-css/bin/setup +1 -0
  18. data/brut-css/package-lock.json +2 -2
  19. data/brut-css/package.json +1 -1
  20. data/brut-js/bin/build +15 -6
  21. data/brut-js/bin/docs +25 -0
  22. data/brut-js/bin/publish +21 -0
  23. data/brut-js/bin/setup +1 -0
  24. data/brut-js/dx +1 -0
  25. data/brut-js/package-lock.json +2 -2
  26. data/brut-js/package.json +1 -1
  27. data/brut.gemspec +2 -2
  28. data/brutrb.com/bin/setup +1 -0
  29. data/brutrb.com/getting-started.md +3 -0
  30. data/brutrb.com/overview.md +6 -0
  31. data/brutrb.com/tutorial.md +7 -3
  32. data/docs/404.html +2 -2
  33. data/docs/adrs.html +3 -3
  34. data/docs/ai.html +3 -3
  35. data/docs/assets/{app.D6BuVHo9.js → app.DyQLb4Ot.js} +1 -1
  36. data/docs/assets/chunks/@localSearchIndexroot.CmtZyrFA.js +1 -0
  37. data/docs/assets/chunks/{VPLocalSearchBox.BpvHMbx6.js → VPLocalSearchBox.T1iA-eJx.js} +1 -1
  38. data/docs/assets/chunks/{theme.wlAOvi2f.js → theme.ChwsbWjK.js} +2 -2
  39. data/docs/assets/{components.md.iLiv2E9X.js → components.md.DHh-NwKs.js} +3 -3
  40. data/docs/assets/{configuration.md.DmuAdsli.js → configuration.md.D8Wz3oJU.js} +1 -1
  41. data/docs/assets/{forms.md.D8aa_qI-.js → forms.md.BRE85eju.js} +1 -1
  42. data/docs/assets/{getting-started.md.DLplsDUd.js → getting-started.md.2ioiTe-B.js} +6 -3
  43. data/docs/assets/{getting-started.md.DLplsDUd.lean.js → getting-started.md.2ioiTe-B.lean.js} +1 -1
  44. data/docs/assets/overview.md.DlKiRRG_.js +1 -0
  45. data/docs/assets/overview.md.DlKiRRG_.lean.js +1 -0
  46. data/docs/assets/tutorial.md.BIb7XT6j.js +1 -0
  47. data/docs/assets/tutorial.md.BIb7XT6j.lean.js +1 -0
  48. data/docs/assets.html +3 -3
  49. data/docs/brut-js.html +3 -3
  50. data/docs/business-logic.html +3 -3
  51. data/docs/cli.html +3 -3
  52. data/docs/components.html +7 -7
  53. data/docs/configuration.html +5 -5
  54. data/docs/css.html +3 -3
  55. data/docs/custom-element-tests.html +3 -3
  56. data/docs/database-access.html +3 -3
  57. data/docs/database-schema.html +3 -3
  58. data/docs/deployment.html +3 -3
  59. data/docs/dev-environment.html +3 -3
  60. data/docs/dir-structure.html +3 -3
  61. data/docs/doc-conventions.html +3 -3
  62. data/docs/end-to-end-tests.html +3 -3
  63. data/docs/features.html +3 -3
  64. data/docs/flash-and-session.html +3 -3
  65. data/docs/form-constraints.html +3 -3
  66. data/docs/forms.html +5 -5
  67. data/docs/getting-started.html +9 -6
  68. data/docs/handlers.html +3 -3
  69. data/docs/hashmap.json +1 -1
  70. data/docs/hooks.html +3 -3
  71. data/docs/i18n.html +3 -3
  72. data/docs/index.html +3 -3
  73. data/docs/instrumentation.html +3 -3
  74. data/docs/javascript.html +3 -3
  75. data/docs/jobs.html +3 -3
  76. data/docs/keyword-injection.html +3 -3
  77. data/docs/layouts.html +3 -3
  78. data/docs/lsp.html +3 -3
  79. data/docs/markdown-examples.html +3 -3
  80. data/docs/middleware.html +3 -3
  81. data/docs/overview.html +5 -5
  82. data/docs/pages.html +3 -3
  83. data/docs/recipes/alternate-layouts.html +3 -3
  84. data/docs/recipes/authentication.html +3 -3
  85. data/docs/recipes/blank-layouts.html +3 -3
  86. data/docs/recipes/custom-flash.html +3 -3
  87. data/docs/recipes/indexed-forms.html +3 -3
  88. data/docs/recipes/migrations.html +3 -3
  89. data/docs/recipes/text-field-component.html +3 -3
  90. data/docs/roadmap.html +3 -3
  91. data/docs/routes.html +3 -3
  92. data/docs/security.html +3 -3
  93. data/docs/seed-data.html +3 -3
  94. data/docs/space-time-continuum.html +3 -3
  95. data/docs/tutorial.html +5 -5
  96. data/docs/unit-tests.html +3 -3
  97. data/docs/why.html +3 -3
  98. data/lib/brut/framework/mcp.rb +1 -1
  99. data/lib/brut/front_end/components/form_tag.rb +2 -2
  100. data/lib/brut/version.rb +1 -1
  101. data/mkbrut/.gitignore +16 -0
  102. data/mkbrut/CODE_OF_CONDUCT.txt +100 -0
  103. data/mkbrut/Gemfile +3 -0
  104. data/mkbrut/Gemfile.lock +19 -0
  105. data/mkbrut/LICENSE.txt +370 -0
  106. data/mkbrut/README.md +145 -0
  107. data/mkbrut/Rakefile +2 -0
  108. data/mkbrut/bin/build +36 -0
  109. data/mkbrut/bin/ci +19 -0
  110. data/mkbrut/bin/docs +19 -0
  111. data/mkbrut/bin/publish +129 -0
  112. data/mkbrut/bin/rake +16 -0
  113. data/mkbrut/bin/setup +30 -0
  114. data/mkbrut/brut-welcome.png +0 -0
  115. data/mkbrut/deploy/.dockerignore +2 -0
  116. data/mkbrut/deploy/Dockerfile +25 -0
  117. data/mkbrut/exe/mkbrut +5 -0
  118. data/mkbrut/lib/mkbrut/app.rb +79 -0
  119. data/mkbrut/lib/mkbrut/app_id.rb +8 -0
  120. data/mkbrut/lib/mkbrut/app_name.rb +29 -0
  121. data/mkbrut/lib/mkbrut/app_options.rb +36 -0
  122. data/mkbrut/lib/mkbrut/base.rb +57 -0
  123. data/mkbrut/lib/mkbrut/cli.rb +107 -0
  124. data/mkbrut/lib/mkbrut/erb_binding_delegate.rb +20 -0
  125. data/mkbrut/lib/mkbrut/internet_identifier.rb +32 -0
  126. data/mkbrut/lib/mkbrut/invalid_identifier.rb +4 -0
  127. data/mkbrut/lib/mkbrut/ops/add_css_import.rb +42 -0
  128. data/mkbrut/lib/mkbrut/ops/add_i18n_message.rb +74 -0
  129. data/mkbrut/lib/mkbrut/ops/add_method.rb +48 -0
  130. data/mkbrut/lib/mkbrut/ops/append_to_file.rb +20 -0
  131. data/mkbrut/lib/mkbrut/ops/base_op.rb +21 -0
  132. data/mkbrut/lib/mkbrut/ops/copy_file.rb +12 -0
  133. data/mkbrut/lib/mkbrut/ops/insert_code_in_method.rb +58 -0
  134. data/mkbrut/lib/mkbrut/ops/insert_route.rb +52 -0
  135. data/mkbrut/lib/mkbrut/ops/mkdir.rb +13 -0
  136. data/mkbrut/lib/mkbrut/ops/prism_parsing_op.rb +70 -0
  137. data/mkbrut/lib/mkbrut/ops/render_template.rb +26 -0
  138. data/mkbrut/lib/mkbrut/ops/skip_file.rb +10 -0
  139. data/mkbrut/lib/mkbrut/ops.rb +16 -0
  140. data/mkbrut/lib/mkbrut/organization.rb +5 -0
  141. data/mkbrut/lib/mkbrut/prefix.rb +26 -0
  142. data/mkbrut/lib/mkbrut/prefixed_io.rb +16 -0
  143. data/mkbrut/lib/mkbrut/segments/bare_bones.rb +185 -0
  144. data/mkbrut/lib/mkbrut/segments/demo.rb +121 -0
  145. data/mkbrut/lib/mkbrut/segments/heroku.rb +30 -0
  146. data/mkbrut/lib/mkbrut/segments/sidekiq.rb +3 -0
  147. data/mkbrut/lib/mkbrut/segments.rb +8 -0
  148. data/mkbrut/lib/mkbrut/version.rb +3 -0
  149. data/mkbrut/lib/mkbrut/versions.rb +13 -0
  150. data/mkbrut/lib/mkbrut.rb +18 -0
  151. data/mkbrut/mkbrut.gemspec +32 -0
  152. data/mkbrut/templates/Base/.dockerignore +25 -0
  153. data/mkbrut/templates/Base/.env.development.erb +60 -0
  154. data/mkbrut/templates/Base/.env.test.erb +8 -0
  155. data/mkbrut/templates/Base/.gitignore +31 -0
  156. data/mkbrut/templates/Base/.projections.json +59 -0
  157. data/mkbrut/templates/Base/Dockerfile.dx +205 -0
  158. data/mkbrut/templates/Base/Gemfile.erb +53 -0
  159. data/mkbrut/templates/Base/Procfile.development +5 -0
  160. data/mkbrut/templates/Base/Procfile.test +1 -0
  161. data/mkbrut/templates/Base/README.md +4 -0
  162. data/mkbrut/templates/Base/README.md.erb +40 -0
  163. data/mkbrut/templates/Base/app/bootstrap.rb +61 -0
  164. data/mkbrut/templates/Base/app/config/i18n/en/1_defaults.rb +128 -0
  165. data/mkbrut/templates/Base/app/config/i18n/en/2_app.rb +24 -0
  166. data/mkbrut/templates/Base/app/public/static/manifest.json.erb +33 -0
  167. data/mkbrut/templates/Base/app/src/app.rb.erb +37 -0
  168. data/mkbrut/templates/Base/app/src/back_end/data_models/app_data_model.rb +5 -0
  169. data/mkbrut/templates/Base/app/src/back_end/data_models/db.rb +19 -0
  170. data/mkbrut/templates/Base/app/src/back_end/data_models/migrations/20240101130000_citext.rb +6 -0
  171. data/mkbrut/templates/Base/app/src/back_end/data_models/seed/seed_data.rb +9 -0
  172. data/mkbrut/templates/Base/app/src/front_end/components/app_component.rb +8 -0
  173. data/mkbrut/templates/Base/app/src/front_end/components/custom_element_registration.rb.erb +7 -0
  174. data/mkbrut/templates/Base/app/src/front_end/css/index.css +2 -0
  175. data/mkbrut/templates/Base/app/src/front_end/css/svgs.css +12 -0
  176. data/mkbrut/templates/Base/app/src/front_end/forms/app_form.rb +4 -0
  177. data/mkbrut/templates/Base/app/src/front_end/handlers/app_handler.rb +4 -0
  178. data/mkbrut/templates/Base/app/src/front_end/images/LogoPylon.png +0 -0
  179. data/mkbrut/templates/Base/app/src/front_end/images/LogoTransit.png +0 -0
  180. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-120x120.png +0 -0
  181. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-152x152.png +0 -0
  182. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-167x167.png +0 -0
  183. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-180x180.png +0 -0
  184. data/mkbrut/templates/Base/app/src/front_end/images/favicon.ico +0 -0
  185. data/mkbrut/templates/Base/app/src/front_end/images/icon.png +0 -0
  186. data/mkbrut/templates/Base/app/src/front_end/images/mkicons.sh +6 -0
  187. data/mkbrut/templates/Base/app/src/front_end/js/index.js +6 -0
  188. data/mkbrut/templates/Base/app/src/front_end/layouts/default_layout.rb.erb +73 -0
  189. data/mkbrut/templates/Base/app/src/front_end/pages/app_page.rb +11 -0
  190. data/mkbrut/templates/Base/app/src/front_end/pages/home_page.rb +62 -0
  191. data/mkbrut/templates/Base/app/src/front_end/support/app_session.rb +6 -0
  192. data/mkbrut/templates/Base/app/src/front_end/svgs/README.md +5 -0
  193. data/mkbrut/templates/Base/app/src/front_end/svgs/comment-button.svg +59 -0
  194. data/mkbrut/templates/Base/bin/README.md.erb +5 -0
  195. data/mkbrut/templates/Base/bin/build-assets +7 -0
  196. data/mkbrut/templates/Base/bin/ci +39 -0
  197. data/mkbrut/templates/Base/bin/console +31 -0
  198. data/mkbrut/templates/Base/bin/db +9 -0
  199. data/mkbrut/templates/Base/bin/dbconsole +51 -0
  200. data/mkbrut/templates/Base/bin/dev +25 -0
  201. data/mkbrut/templates/Base/bin/release +26 -0
  202. data/mkbrut/templates/Base/bin/run +86 -0
  203. data/mkbrut/templates/Base/bin/scaffold +9 -0
  204. data/mkbrut/templates/Base/bin/setup +256 -0
  205. data/mkbrut/templates/Base/bin/startup-message +65 -0
  206. data/mkbrut/templates/Base/bin/test +9 -0
  207. data/mkbrut/templates/Base/bin/test-server +29 -0
  208. data/mkbrut/templates/Base/bin/watch-and-build-assets +37 -0
  209. data/mkbrut/templates/Base/config.ru +16 -0
  210. data/mkbrut/templates/Base/docker-compose.dx.yml +92 -0
  211. data/mkbrut/templates/Base/dx/README.md +28 -0
  212. data/mkbrut/templates/Base/dx/bash_customizations +12 -0
  213. data/mkbrut/templates/Base/dx/bash_customizations.local +8 -0
  214. data/mkbrut/templates/Base/dx/build +107 -0
  215. data/mkbrut/templates/Base/dx/docker-compose.env.erb +25 -0
  216. data/mkbrut/templates/Base/dx/dx.sh.lib +137 -0
  217. data/mkbrut/templates/Base/dx/exec +68 -0
  218. data/mkbrut/templates/Base/dx/prune +19 -0
  219. data/mkbrut/templates/Base/dx/show-help-in-app-container-then-wait.sh +38 -0
  220. data/mkbrut/templates/Base/dx/start +30 -0
  221. data/mkbrut/templates/Base/dx/stop +23 -0
  222. data/mkbrut/templates/Base/package.json.erb +37 -0
  223. data/mkbrut/templates/Base/puma.config.rb +53 -0
  224. data/mkbrut/templates/Base/specs/e2e/home_page.spec.rb.erb +23 -0
  225. data/mkbrut/templates/Base/specs/front_end/js/SpecHelper.js +24 -0
  226. data/mkbrut/templates/Base/specs/front_end/pages/home_page.spec.rb +22 -0
  227. data/mkbrut/templates/Base/specs/lint_factories.spec.rb +7 -0
  228. data/mkbrut/templates/Base/specs/spec_helper.rb +78 -0
  229. data/mkbrut/templates/Base/specs/support.rb +2 -0
  230. data/mkbrut/templates/segments/BareBones/app/src/front_end/handlers/trigger_exception_handler.rb +24 -0
  231. data/mkbrut/templates/segments/BareBones/app/src/front_end/js/Example.js.erb +49 -0
  232. data/mkbrut/templates/segments/BareBones/specs/front_end/handlers/trigger_exception_handler.spec.rb +41 -0
  233. data/mkbrut/templates/segments/BareBones/specs/front_end/js/Example.spec.js.erb +38 -0
  234. data/mkbrut/templates/segments/Demo/app/src/back_end/data_models/db/guestbook_message.rb +3 -0
  235. data/mkbrut/templates/segments/Demo/app/src/back_end/data_models/migrations/20250628194124_guestbook.rb +14 -0
  236. data/mkbrut/templates/segments/Demo/app/src/front_end/components/flash_component.rb +36 -0
  237. data/mkbrut/templates/segments/Demo/app/src/front_end/css/constraint-violations.css +18 -0
  238. data/mkbrut/templates/segments/Demo/app/src/front_end/css/fonts.css +19 -0
  239. data/mkbrut/templates/segments/Demo/app/src/front_end/fonts/monaspace-xenon.ttf +0 -0
  240. data/mkbrut/templates/segments/Demo/app/src/front_end/forms/guestbook_message_form.rb +4 -0
  241. data/mkbrut/templates/segments/Demo/app/src/front_end/handlers/guestbook_message_handler.rb +64 -0
  242. data/mkbrut/templates/segments/Demo/app/src/front_end/pages/guestbook_page/message_component.rb +41 -0
  243. data/mkbrut/templates/segments/Demo/app/src/front_end/pages/guestbook_page.rb +43 -0
  244. data/mkbrut/templates/segments/Demo/app/src/front_end/pages/new_guestbook_message_page.rb +64 -0
  245. data/mkbrut/templates/segments/Demo/specs/back_end/data_models/db/guestbook_message.spec.rb +5 -0
  246. data/mkbrut/templates/segments/Demo/specs/e2e/guest_message.spec.rb +54 -0
  247. data/mkbrut/templates/segments/Demo/specs/factories/db/guestbook_message.factory.rb +7 -0
  248. data/mkbrut/templates/segments/Demo/specs/front_end/components/flash_component.spec.rb +5 -0
  249. data/mkbrut/templates/segments/Demo/specs/front_end/handlers/guestbook_message_handler.spec.rb +122 -0
  250. data/mkbrut/templates/segments/Demo/specs/front_end/pages/guestbook_page/message_component.spec.rb +5 -0
  251. data/mkbrut/templates/segments/Demo/specs/front_end/pages/guestbook_page.spec.rb +52 -0
  252. data/mkbrut/templates/segments/Demo/specs/front_end/pages/new_guestbook_message_page.spec.rb +5 -0
  253. data/mkbrut/templates/segments/Heroku/bin/deploy +11 -0
  254. data/mkbrut/templates/segments/Heroku/deploy/Dockerfile +125 -0
  255. data/mkbrut/templates/segments/Heroku/deploy/docker-entrypoint +15 -0
  256. data/mkbrut/templates/segments/Heroku/deploy/heroku_config.rb +26 -0
  257. metadata +185 -21
  258. data/docs/assets/chunks/@localSearchIndexroot.COP2Bcmp.js +0 -1
  259. data/docs/assets/overview.md.iMnwLO4x.js +0 -1
  260. data/docs/assets/overview.md.iMnwLO4x.lean.js +0 -1
  261. data/docs/assets/tutorial.md.BYXj4cOu.js +0 -1
  262. data/docs/assets/tutorial.md.BYXj4cOu.lean.js +0 -1
  263. /data/docs/assets/{components.md.iLiv2E9X.lean.js → components.md.DHh-NwKs.lean.js} +0 -0
  264. /data/docs/assets/{configuration.md.DmuAdsli.lean.js → configuration.md.D8Wz3oJU.lean.js} +0 -0
  265. /data/docs/assets/{forms.md.D8aa_qI-.lean.js → forms.md.BRE85eju.lean.js} +0 -0
@@ -0,0 +1,121 @@
1
+ # Additional classes and configuration for a new Brut
2
+ # app that provides a more demonstration of the features.
3
+ class MKBrut::Segments::Demo < MKBrut::Base
4
+
5
+ def self.friendly_name = "Demo features and files"
6
+
7
+ def initialize(app_options:, current_dir:, templates_dir:)
8
+ @project_root = current_dir / app_options.app_name
9
+ @templates_dir = templates_dir / "segments" / "Demo"
10
+ @erb_binding = ErbBindingDelegate.new(app_options)
11
+ end
12
+
13
+ def add!
14
+ operations = copy_files(@templates_dir, @project_root) +
15
+ other_operations(@project_root)
16
+
17
+ operations.each do |operation|
18
+ operation.call
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def other_operations(project_root)
25
+ [
26
+ MKBrut::Ops::AddI18nMessage.new(
27
+ project_root: project_root,
28
+ hash: {
29
+ en: {
30
+ cv: {
31
+ ss: {
32
+ not_enough_words: "%{field} does not have enough words",
33
+ already_posted: "You've already posted a message. Thanks for that!",
34
+ },
35
+ },
36
+ pages: {
37
+ "NewGuestbookMessagePage": {
38
+ title: "New guestbook message page",
39
+ },
40
+ "GuestbookPage": {
41
+ title: "Guestbook page",
42
+ },
43
+ },
44
+ guestbook_not_saved: "Your guestbook message was not saved",
45
+ guestbook_saved: "Thanks for writing!",
46
+ },
47
+ }
48
+ ),
49
+ MKBrut::Ops::InsertRoute.new(
50
+ project_root: project_root,
51
+ code: %{page "/guestbook"},
52
+ ),
53
+ MKBrut::Ops::InsertRoute.new(
54
+ project_root: project_root,
55
+ code: %{form "/guestbook_message"},
56
+ ),
57
+ MKBrut::Ops::InsertRoute.new(
58
+ project_root: project_root,
59
+ code: %{page "/new_guestbook_message"},
60
+ ),
61
+ MKBrut::Ops::InsertCodeInMethod.new(
62
+ file: project_root / "app" / "src" / "back_end" / "data_models" / "seed" / "seed_data.rb",
63
+ class_name: "SeedData",
64
+ method_name: "seed!",
65
+ code: %{
66
+ 10.times do
67
+ create(:guestbook_message, created_at: Date.today - rand(1..100))
68
+ end
69
+ }
70
+ ),
71
+ MKBrut::Ops::AddCSSImport.new(
72
+ project_root: project_root,
73
+ import: "constraint-violations.css"
74
+ ),
75
+ MKBrut::Ops::AddCSSImport.new(
76
+ project_root: project_root,
77
+ import: "fonts.css"
78
+ ),
79
+ MKBrut::Ops::InsertCodeInMethod.new(
80
+ file: project_root / "app" / "src" / "front_end" / "pages"/ "home_page.rb",
81
+ class_name: "HomePage",
82
+ method_name: "page_template",
83
+ where: :start,
84
+ code: %{
85
+ div(class: "w-50 mh-auto mt-4") do
86
+ a(href: NewGuestbookMessagePage.routing,
87
+ class: "db f-3 ff-sans blue-400 fw-bold tc"
88
+ ) do
89
+ "Sign The Guestbook!"
90
+ end
91
+ end
92
+ }
93
+ ),
94
+ MKBrut::Ops::AddMethod.new(
95
+ file: project_root / "app" / "src" / "front_end" / "support" / "app_session.rb",
96
+ class_name: "AppSession",
97
+ code: %{def signed_guestbook? = !!self.guestbook_message},
98
+ ),
99
+ MKBrut::Ops::AddMethod.new(
100
+ file: project_root / "app" / "src" / "front_end" / "support" / "app_session.rb",
101
+ class_name: "AppSession",
102
+ code: %{
103
+ def guestbook_message
104
+ DB::GuestbookMessage.find(
105
+ external_id: self[:guestbook_message_external_id]
106
+ )
107
+ end
108
+ }
109
+ ),
110
+ MKBrut::Ops::AddMethod.new(
111
+ file: project_root / "app" / "src" / "front_end" / "support" / "app_session.rb",
112
+ class_name: "AppSession",
113
+ code: %{
114
+ def signed_guestbook(guestbook_message)
115
+ self[:guestbook_message_external_id] = guestbook_message.external_id
116
+ end
117
+ }
118
+ ),
119
+ ]
120
+ end
121
+ end
@@ -0,0 +1,30 @@
1
+ class MKBrut::Segments::Heroku < MKBrut::Base
2
+ def self.friendly_name = "Heroku-based Deployment"
3
+
4
+ def initialize(app_options:, current_dir:, templates_dir:)
5
+ @project_root = current_dir / app_options.app_name
6
+ @templates_dir = templates_dir / "segments" / "Heroku"
7
+ @erb_binding = MKBrut::ErbBindingDelegate.new(app_options)
8
+ end
9
+
10
+ def add!
11
+ operations = copy_files(@templates_dir, @project_root) +
12
+ other_operations
13
+
14
+ operations.each do |operation|
15
+ operation.call
16
+ end
17
+ end
18
+
19
+ def other_operations
20
+ [
21
+ MKBrut::Ops::AppendToFile.new(
22
+ file: @project_root / ".gitignore",
23
+ content: %{
24
+ # These are generated by bin/deploy
25
+ deploy/Dockerfile.*
26
+ }
27
+ )
28
+ ]
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ # Adds Sidekiq to a Brut app
2
+ class MKBrut::Segments::Sidekiq
3
+ end
@@ -0,0 +1,8 @@
1
+ module MKBrut
2
+ module Segments
3
+ autoload :BareBones, "mkbrut/segments/bare_bones"
4
+ autoload :Demo, "mkbrut/segments/demo"
5
+ autoload :Sidekiq, "mkbrut/segments/demo"
6
+ autoload :Heroku, "mkbrut/segments/heroku"
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module MKBrut
2
+ VERSION = "0.8.0"
3
+ end
@@ -0,0 +1,13 @@
1
+ require "pathname"
2
+ require "json"
3
+ require_relative "version"
4
+
5
+ class MKBrut::Versions
6
+ def initialize
7
+ @brut_version = MKBrut::VERSION
8
+ end
9
+
10
+ def brut_version_specifier = "~> #{@brut_version}"
11
+ def brut_css_version_specifier = "~#{@brut_version}"
12
+ def brut_js_version_specifier = "~#{@brut_version}"
13
+ end
@@ -0,0 +1,18 @@
1
+ module MKBrut
2
+ autoload :App, "mkbrut/app"
3
+ autoload :AppId, "mkbrut/app_id"
4
+ autoload :AppName, "mkbrut/app_name"
5
+ autoload :AppOptions, "mkbrut/app_options"
6
+ autoload :Base, "mkbrut/base"
7
+ autoload :CLI, "mkbrut/cli"
8
+ autoload :ErbBindingDelegate, "mkbrut/erb_binding_delegate"
9
+ autoload :InternetIdentifier, "mkbrut/internet_identifier"
10
+ autoload :InvalidIdentifier, "mkbrut/invalid_identifier"
11
+ autoload :Ops, "mkbrut/ops"
12
+ autoload :Organization, "mkbrut/organization"
13
+ autoload :Prefix, "mkbrut/prefix"
14
+ autoload :Segments, "mkbrut/segments"
15
+ autoload :Versions, "mkbrut/versions"
16
+ autoload :PrefixedIO, "mkbrut/prefixed_io"
17
+ autoload :VERSION, "mkbrut/version"
18
+ end
@@ -0,0 +1,32 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "mkbrut/version"
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "mkbrut"
6
+ spec.version = MKBrut::VERSION
7
+ spec.authors = ["Dave Copeland"]
8
+ spec.email = ["davec@naildrivin5.com"]
9
+
10
+ spec.summary = "Create a new Brut App"
11
+ spec.description = "mkbrut is how you go from zero to having a Brut app where you can start working."
12
+ spec.homepage = "https://brutrb.com"
13
+ spec.bindir = "exe"
14
+
15
+ spec.files = Dir.glob("lib/**/*.rb") + # Gem source code
16
+ Dir.glob( # Templates used to create a new Brut app
17
+ "templates/**/*",
18
+ flags: File::FNM_DOTMATCH
19
+ ).reject {
20
+ it =~ /\/\.{1,2}$/ # Pesky dotfile
21
+ }.reject {
22
+ it =~ /\.DS_Store$/ # FML
23
+ } +
24
+ [
25
+ "exe/mkbrut", # executable
26
+ ]
27
+
28
+ spec.executables = ["mkbrut"]
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_development_dependency "rake"
32
+ end
@@ -0,0 +1,25 @@
1
+ # Ignore git directory.
2
+ /.git/
3
+ /.gitignore
4
+
5
+ # Ignore bundler config.
6
+ /.bundle
7
+
8
+ # Ignore all environment files
9
+ /.env*
10
+
11
+ # Ignore all logfiles and tempfiles.
12
+ /logs/*
13
+ /tmp/*
14
+
15
+ # The most important thing to ignore of all time
16
+ /node_modules/
17
+
18
+ # Ignore generated assets
19
+ /app/public/*
20
+
21
+ # Ignore Docker-related files
22
+ /.dockerignore
23
+ /Dockerfile*
24
+ /docker-compose.*
25
+ /deploy/docker-compose.*
@@ -0,0 +1,60 @@
1
+ # Environment variables needed for running in development. This file
2
+ # also serves as the canonical list of ALL environment variables the app
3
+ # must have in production.
4
+ #
5
+ # Each env var should be preceded by a comment explaining its purpose.
6
+ # The env var itself can be written in one of two ways:
7
+ #
8
+ # * Conventionally set a value:
9
+ #
10
+ # ```
11
+ # # the API key to interact with the OCP Widgets Service.
12
+ # WIDGETS_API_KEY=some-value
13
+ # ```
14
+ # Ideally, all values are set this way so that everyone is using the same
15
+ # value and the app just works without having to go to an external service
16
+ # to get a real secret for local development.
17
+ #
18
+ # * Indicates a local override is required:
19
+ #
20
+ # ```
21
+ # # the API key to interact with the OCP Widgets Service.
22
+ # LOCAL:WIDGETS_API_KEY
23
+ # ```
24
+ #
25
+ # In this form, bin/setup will ask the developer for the value and place
26
+ # it in .env.development.local. As your app evolves and adds more
27
+ # environment variables, you can add them like this and `bin/setup` will
28
+ # see which ones are present vs missing.
29
+
30
+ # Session secret used to encrypt the session
31
+ SESSION_SECRET=<%= session_secret %>
32
+ # URL to the database
33
+ DATABASE_URL=postgres://postgres:postgres@postgres:5432/<%= app_id %>_development
34
+
35
+ # Configures the endpoint where OTel traces are sent.
36
+ # In dev and test, it's the otel-desktop-viewer set up in
37
+ # docker-compose.dx.yml. For production, consult your
38
+ # OTel vendor's documentation.
39
+ OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-desktop-viewer:4318"
40
+
41
+ # OTel exporter to use for sending traces to the endpoint. You usually
42
+ # want otlp, however, in production, consult your vendor's documentation to be sure.
43
+ OTEL_TRACES_EXPORTER="otlp"
44
+
45
+ # OTel exporter protocol. You usually want http/protobuf, however,
46
+ # in production, consult your vendor's documentation to be sure.
47
+ OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
48
+
49
+ # This controls the background thread used by the span processor.
50
+ # I swear I had to set this to false to make stuff work, however
51
+ # various corporate AI chatbots said it should be true for use with
52
+ # Puma. I'm going with my hazy memory for now.
53
+ OTEL_RUBY_BSP_START_THREAD_ON_BOOT=false
54
+
55
+ # OTel log level. Set this to "debug" to try to figure out why OTel isn't working.
56
+ OTEL_LOG_LEVEL=warn
57
+
58
+ # If set to true, Puma will log its configuration, which is handy to understand
59
+ # if what you think you configured actually got configured.
60
+ PUMA_LOG_CONFIG=true
@@ -0,0 +1,8 @@
1
+ SESSION_SECRET=<%= session_secret %>
2
+ DATABASE_URL=postgres://postgres:postgres@postgres:5432/<%= app_id %>_test
3
+ OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-desktop-viewer:4318"
4
+ OTEL_TRACES_EXPORTER="otlp"
5
+ OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
6
+ OTEL_RUBY_BSP_START_THREAD_ON_BOOT=false
7
+ OTEL_LOG_LEVEL=warn
8
+ PUMA_LOG_CONFIG=true
@@ -0,0 +1,31 @@
1
+ # Never checkin tmp
2
+ /tmp
3
+ # This should never be checked in
4
+ /node_modules
5
+ # This contains generated code, so should not be checked in
6
+ /app/public/css
7
+ # This contains generated code, so should not be checked in
8
+ /app/public/js
9
+ # This contains generated code, so should not be checked in
10
+ /app/public/static/images/
11
+ # Don't want to check in the generated JSDocs dir
12
+ /jsdocs
13
+ # This is generated per environment
14
+ /app/config/asset_metadata.json
15
+ # Don't check in logs
16
+ /logs
17
+
18
+ # This could contain actual secrets and should not be checked in
19
+ /.env.development.local
20
+
21
+ # These are generated artifacts
22
+ /deploy/Dockerfile.*
23
+ /deploy/docker-compose.yml
24
+
25
+ # RSpec configuration should be in one place.
26
+ # Devs can override as needed, but this is not how
27
+ # configuration is to be shared
28
+ /.rspec
29
+
30
+ # Gems installed locally should not be checked in
31
+ /local-gems
@@ -0,0 +1,59 @@
1
+ {
2
+ "app/src/*.rb": {
3
+ "alternate": "specs/{}.spec.rb",
4
+ "type": "source"
5
+ },
6
+ "app/src/front_end/js/*.js": {
7
+ "alternate": "specs/front_end/js/{}.spec.js",
8
+ "type": "js"
9
+ },
10
+ "specs/*.spec.rb": {
11
+ "alternate": "app/src/{}.rb",
12
+ "type": "spec"
13
+ },
14
+ "specs/front_end/js/*.spec.js": {
15
+ "alternate": "app/src/front_end/js/{}.js",
16
+ "type": "jsspec"
17
+ },
18
+ "app/src/front_end/components/*.rb": {
19
+ "type": "component"
20
+ },
21
+ "app/src/front_end/forms/*.rb": {
22
+ "type": "form"
23
+ },
24
+ "app/src/front_end/handlers/*.rb": {
25
+ "type": "handler"
26
+ },
27
+ "app/src/front_end/pages/*.rb": {
28
+ "type": "page"
29
+ },
30
+ "app/src/front_end/pages/*.html.rb": {
31
+ "type": "erb"
32
+ },
33
+ "app/src/back_end/data_models/db/*.rb": {
34
+ "type": "db"
35
+ },
36
+ "app/src/back_end/data_models/migrations/*.rb": {
37
+ "type": "migration"
38
+ },
39
+ "app/src/back_end/data_models/migrations/*.sql": {
40
+ "type": "migration"
41
+ },
42
+ "app/src/back_end/data_models/seed/*.rb": {
43
+ "type": "seed"
44
+ },
45
+ "app/src/back_end/domain/*.rb": {
46
+ "type": "domain"
47
+ },
48
+ "app/src/back_end/jobs/*.rb": {
49
+ "type": "job"
50
+ },
51
+ "app/src/back_end/service_wrappers/*.rb": {
52
+ "type": "service_wrapper"
53
+ },
54
+ "specs/factories/*.factory.rb": {
55
+ "type": "factory",
56
+ "alternate": "app/src/back_end/data_models/{}.rb"
57
+ }
58
+
59
+ }
@@ -0,0 +1,205 @@
1
+ # This file is heavily documented to explain what is going on and why.
2
+ # If you are reading this, however, you now own this file and can
3
+ # change it how you like. Just be sure you understand how it works in the
4
+ # context of docker-compose.dx.yml and the files in dx/,
5
+ # which are referred to as the Workspace
6
+ #
7
+ # This file is a set of instructions to build a Docker _image_, which is
8
+ # then use to _start_ a _container_.
9
+
10
+ # This uses a base image where Ruby is already installed. This saves
11
+ # us from having to install Ruby ourselves. Change this whenever you update
12
+ # Ruby. Note that by convention, "3.4" will be whatever the latest point
13
+ # release of Ruby 3.4 is. If you don't want that, specify an exact Ruby version here.
14
+ # Note that this value is intended to be in sync with the tag of the image
15
+ # found in dx/docker-compose.env
16
+ FROM ruby:3.4
17
+
18
+ # This attempts to make RUN directives fail when UNIX pipelines are involved
19
+ SHELL [ "/bin/bash", "-o", "pipefail", "-c" ]
20
+
21
+ # This tells apt-get and friends not to ask for interactive input
22
+ # as this creates massive confusion in the context of building a Docker image.
23
+ ENV DEBIAN_FRONTEND=noninteractive
24
+
25
+ # These packages are needed to set up other repos to install other
26
+ # packages and/or are useful in installing other software
27
+ #
28
+ # - ca-certificates - needed by Postgres client
29
+ # - curl - needed by various installation instructions
30
+ # - gnupg - needed to install Docker
31
+ # - lsb-release - needed by Postgres install instructions
32
+ # - rsync - needed by Brut's build_assets command
33
+ # - vim - needed when we want to edit files inside container
34
+ RUN apt-get -y clean && \
35
+ apt-get -y update && \
36
+ apt-get install --quiet --yes \
37
+ ca-certificates \
38
+ curl \
39
+ gnupg \
40
+ lsb-release \
41
+ rsync \
42
+ vim
43
+
44
+ # These are dependencies needed to install Chromium via Playright.
45
+ # You can check this list via
46
+ #
47
+ # npx playwright install-deps --dry-run chromium
48
+ #
49
+ RUN apt-get install -y --no-install-recommends libasound2 \
50
+ libatk-bridge2.0-0 \
51
+ libatk1.0-0 \
52
+ libatspi2.0-0 \
53
+ libcairo2 \
54
+ libcups2 \
55
+ libdbus-1-3 \
56
+ libdrm2 \
57
+ libgbm1 \
58
+ libglib2.0-0 \
59
+ libnspr4 \
60
+ libnss3 \
61
+ libpango-1.0-0 \
62
+ libx11-6 \
63
+ libxcb1 \
64
+ libxcomposite1 \
65
+ libxdamage1 \
66
+ libxext6 \
67
+ libxfixes3 \
68
+ libxkbcommon0 \
69
+ libxrandr2 \
70
+ xvfb \
71
+ fonts-noto-color-emoji \
72
+ fonts-unifont \
73
+ libfontconfig1 \
74
+ libfreetype6 \
75
+ xfonts-scalable \
76
+ fonts-liberation \
77
+ fonts-ipafont-gothic \
78
+ fonts-wqy-zenhei \
79
+ fonts-tlwg-loma-otf \
80
+ fonts-freefont-ttf
81
+
82
+
83
+ # Install Docker itself. Note that this is installed only what is needed to run
84
+ # the Docker CLI - the docker-compose.dx.yml will connect this docker's socket
85
+ # to the one on the host. This sets up "Docker outside Docker" and
86
+ # is here only to build the production Docker containers and to push
87
+ # them to a repository. This is not intended to run containers from
88
+ # inside this one.
89
+ RUN install -m 0755 -d /etc/apt/keyrings && \
90
+ curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
91
+ chmod a+r /etc/apt/keyrings/docker.gpg && \
92
+ echo \
93
+ "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
94
+ "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
95
+ tee /etc/apt/sources.list.d/docker.list > /dev/null && \
96
+ apt-get update --quiet --yes && \
97
+ apt-get install --quiet --yes docker-ce \
98
+ docker-ce-cli \
99
+ containerd.io \
100
+ docker-buildx-plugin \
101
+ docker-compose-plugin
102
+
103
+
104
+ # Install Postgres Client, per https://www.postgresql.org/download/linux/debian/
105
+ # Note that ca-certificates and curl are installed above
106
+ #
107
+ # Also note that the version here should match the version set up in
108
+ # docker-compose.dx.yml
109
+ RUN sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' && \
110
+ wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
111
+ apt-get update && \
112
+ apt-get -y install postgresql-client-16
113
+
114
+ # Update RubyGems and Bundler, setting RubyGems not to generate
115
+ # documentation as that is slow and you likely won't use it.
116
+ RUN echo "gem: --no-document" >> ~/.gemrc && \
117
+ gem update --system && \
118
+ gem install bundler
119
+
120
+ # Installs the Heroku CLI, per Heroku docs at
121
+ # https://devcenter.heroku.com/articles/heroku-cli#install-with-ubuntu-debian-apt-get
122
+ RUN curl https://cli-assets.heroku.com/install-ubuntu.sh | sh
123
+
124
+ # Up to now, RUN directives ran as the root user. When using this container
125
+ # for development, we do not want to run as the root user. On macOS, this is not
126
+ # a big deal, but in Linux, the root user inside this container can create files
127
+ # as the root user on your computer. You don't want that.
128
+
129
+ # What is tricky is that we ultimately want the non-root user here
130
+ # to have the same UID and same groups as your user on your host/computer.
131
+ # And, we want that user to be able to run Docker commands.
132
+
133
+ # First, set up some build args that `dx/build` will override with the actual values.
134
+ # The reason these all have values is that Docker will not fail if they are omitted,
135
+ # but it will behave strangely.
136
+ #
137
+ # Their user id, which ideally is overridden to match your user id on the host
138
+ ARG user_uid=10001
139
+
140
+ # Their group id, which ideally is overridden to match your group id on the host
141
+ ARG user_gid=10002
142
+
143
+ # The group id that can access /var/run/docker.sock on the host.
144
+ # If this isn't mapped properly, no docker-out-of-docker commands will work
145
+ ARG docker_gid=10003
146
+
147
+ # This can be blank if the OS is such that the user inside the container can access
148
+ # /var/run/docker.sock simply from being in docker_gid. If this is not
149
+ # the case (as on macOS), it is not only sad, but we must add the group
150
+ # 0 (root) to the groups our new user is in.
151
+ ARG sadly_user_must_be_added_to_root=
152
+
153
+ # Create the user's group ID if it does not exist
154
+ RUN getent group ${user_gid} || groupadd --gid ${user_gid} appgroup
155
+
156
+ # Create the docker group id if it does not exist
157
+ RUN getent group ${docker_gid} || groupadd --gid ${docker_gid} hostdocker
158
+
159
+ # Create the user and set up their shell.
160
+ # Note that we put bash_customizations in both .profile and .bashrc
161
+ # to increase the chances they are used when running bash in various configurations
162
+ RUN useradd --uid ${user_uid} --gid ${user_gid} --groups ${sadly_user_must_be_added_to_root}${docker_gid} --create-home --home-dir /home/appuser appuser && \
163
+ echo ". ~/.bash_customizations" >> /home/appuser/.profile && \
164
+ echo ". ~/.bash_customizations.local" >> /home/appuser/.profile && \
165
+ echo ". ~/.bash_customizations" >> /home/appuser/.bashrc && \
166
+ echo ". ~/.bash_customizations.local" >> /home/appuser/.bashrc
167
+
168
+ # Now we must grant ownership to the new user over various files they will need to
169
+ # run or use
170
+ COPY --chown=appuser:${user_gid} dx/show-help-in-app-container-then-wait.sh /home/appuser
171
+ COPY --chown=appuser:${user_gid} dx/bash_customizations /home/appuser/.bash_customizations
172
+ COPY --chown=appuser:${user_gid} dx/bash_customizations.local /home/appuser/.bash_customizations.local
173
+
174
+ # This switches to the new user, meaniung all subsequent RUN directives will
175
+ # be run as this user. The USER directive allows changing both the user
176
+ # and the current group. We are omitting the group part of that because
177
+ # to include would exclude the user from all other groups we just set them up
178
+ # as belonging to.
179
+ USER appuser
180
+
181
+ # Install NodeJS, per https://nodejs.org/en/download
182
+ #
183
+ # Yes, this is seting up nvm just to install one version of node we will ever user in
184
+ # here and yes, this sucks. But it's better to use the vendor's official
185
+ # recommendation.
186
+ RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash && \
187
+ \. "$HOME/.nvm/nvm.sh" && \
188
+ nvm install 22 && \
189
+ node -v && nvm current && npm -v
190
+
191
+
192
+
193
+ # This arg should have the version of playwright present in the app's Gemfile.lock.
194
+ # This is because the playright NodeJS library and the Ruby gem must have the same
195
+ # version or things won't work right. The value is blank because it really has to be
196
+ # detected during `dx/build`.
197
+ ARG PLAYWRIGHT_VERSION
198
+ # Now install the version of Playwright we detected as well as Chromium. NOTE, we
199
+ # must install Chromium, not chrome, because there is no version of Chrome for an
200
+ # ARM-based Debian linux, which is what this Dockerfile will build on an Apple
201
+ # Silicon Mac.
202
+ RUN \. "$HOME/.nvm/nvm.sh" && \
203
+ nvm use default && \
204
+ npm install -g playwright@$PLAYWRIGHT_VERSION && \
205
+ npx playwright install chromium