mxrb 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 (237) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.de-DE.md +108 -0
  4. data/README.md +172 -0
  5. data/README.pt-BR.md +109 -0
  6. data/bin/mxrb +1613 -0
  7. data/docs/README.md +10 -0
  8. data/docs/de-DE/README.md +24 -0
  9. data/docs/de-DE/architectural-patterns.md +26 -0
  10. data/docs/de-DE/architectural-standard.md +48 -0
  11. data/docs/de-DE/architecture.md +76 -0
  12. data/docs/de-DE/compiler.md +162 -0
  13. data/docs/de-DE/conventions.md +19 -0
  14. data/docs/de-DE/design-system.md +52 -0
  15. data/docs/de-DE/entity-dsl.md +53 -0
  16. data/docs/de-DE/native-runtime-quality-report.md +74 -0
  17. data/docs/de-DE/oql-sql.md +211 -0
  18. data/docs/de-DE/platform-api-integration.md +36 -0
  19. data/docs/de-DE/platform-operations.md +243 -0
  20. data/docs/de-DE/project-structure.md +32 -0
  21. data/docs/de-DE/ruby-first-roadmap.md +116 -0
  22. data/docs/de-DE/scaffolds.md +59 -0
  23. data/docs/de-DE/semantic-refactoring.md +56 -0
  24. data/docs/de-DE/team-server.md +33 -0
  25. data/docs/de-DE/validation-matrix.md +64 -0
  26. data/docs/de-DE/vetclinic-acceptance.md +24 -0
  27. data/docs/de-DE/writing.md +138 -0
  28. data/docs/en-US/README.md +24 -0
  29. data/docs/en-US/architectural-patterns.md +151 -0
  30. data/docs/en-US/architectural-standard.md +89 -0
  31. data/docs/en-US/architecture.md +170 -0
  32. data/docs/en-US/compiler.md +157 -0
  33. data/docs/en-US/conventions.md +82 -0
  34. data/docs/en-US/design-system.md +163 -0
  35. data/docs/en-US/entity-dsl.md +53 -0
  36. data/docs/en-US/native-runtime-quality-report.md +73 -0
  37. data/docs/en-US/oql-sql.md +201 -0
  38. data/docs/en-US/platform-api-integration.md +36 -0
  39. data/docs/en-US/platform-operations.md +244 -0
  40. data/docs/en-US/project-structure.md +49 -0
  41. data/docs/en-US/ruby-first-roadmap.md +186 -0
  42. data/docs/en-US/scaffolds.md +57 -0
  43. data/docs/en-US/semantic-refactoring.md +198 -0
  44. data/docs/en-US/team-server.md +32 -0
  45. data/docs/en-US/validation-matrix.md +257 -0
  46. data/docs/en-US/vetclinic-acceptance.md +23 -0
  47. data/docs/en-US/writing.md +655 -0
  48. data/docs/pt-BR/README.md +24 -0
  49. data/docs/pt-BR/architectural-patterns.md +25 -0
  50. data/docs/pt-BR/architectural-standard.md +533 -0
  51. data/docs/pt-BR/architecture.md +300 -0
  52. data/docs/pt-BR/compiler.md +162 -0
  53. data/docs/pt-BR/conventions.md +19 -0
  54. data/docs/pt-BR/design-system.md +65 -0
  55. data/docs/pt-BR/entity-dsl.md +116 -0
  56. data/docs/pt-BR/native-runtime-quality-report.md +73 -0
  57. data/docs/pt-BR/oql-sql.md +208 -0
  58. data/docs/pt-BR/platform-api-integration.md +36 -0
  59. data/docs/pt-BR/platform-operations.md +260 -0
  60. data/docs/pt-BR/project-structure.md +32 -0
  61. data/docs/pt-BR/ruby-first-roadmap.md +239 -0
  62. data/docs/pt-BR/scaffolds.md +104 -0
  63. data/docs/pt-BR/semantic-refactoring.md +55 -0
  64. data/docs/pt-BR/team-server.md +59 -0
  65. data/docs/pt-BR/validation-matrix.md +86 -0
  66. data/docs/pt-BR/vetclinic-acceptance.md +50 -0
  67. data/docs/pt-BR/writing.md +177 -0
  68. data/examples/sudoku_evaluation.rb +14 -0
  69. data/examples/sudoku_functional_test.rb +28 -0
  70. data/lib/mxrb/architecture/graph.rb +124 -0
  71. data/lib/mxrb/architecture/validator.rb +98 -0
  72. data/lib/mxrb/benchmark.rb +33 -0
  73. data/lib/mxrb/compare.rb +390 -0
  74. data/lib/mxrb/compiler/adapter.rb +96 -0
  75. data/lib/mxrb/compiler/artifact_document_compiler.rb +102 -0
  76. data/lib/mxrb/compiler/artifact_materializer.rb +53 -0
  77. data/lib/mxrb/compiler/client_model_materializer.rb +42 -0
  78. data/lib/mxrb/compiler/code_action_document_compiler.rb +64 -0
  79. data/lib/mxrb/compiler/code_action_materializer.rb +35 -0
  80. data/lib/mxrb/compiler/code_action_type_compiler.rb +32 -0
  81. data/lib/mxrb/compiler/combo_box_bundle_compiler.rb +314 -0
  82. data/lib/mxrb/compiler/compatibility_analyzer.rb +169 -0
  83. data/lib/mxrb/compiler/constants_materializer.rb +111 -0
  84. data/lib/mxrb/compiler/data_grid_bundle_compiler.rb +290 -0
  85. data/lib/mxrb/compiler/database_connector_action_compiler.rb +280 -0
  86. data/lib/mxrb/compiler/deployment_asset_copier.rb +60 -0
  87. data/lib/mxrb/compiler/deployment_bootstrapper.rb +208 -0
  88. data/lib/mxrb/compiler/deployment_materializer.rb +43 -0
  89. data/lib/mxrb/compiler/deployment_metadata.rb +64 -0
  90. data/lib/mxrb/compiler/domain_document_compiler.rb +153 -0
  91. data/lib/mxrb/compiler/domain_model_materializer.rb +35 -0
  92. data/lib/mxrb/compiler/domain_security_compiler.rb +82 -0
  93. data/lib/mxrb/compiler/gallery_bundle_compiler.rb +190 -0
  94. data/lib/mxrb/compiler/generic_widget_bundle_compiler.rb +281 -0
  95. data/lib/mxrb/compiler/image_bundle_compiler.rb +216 -0
  96. data/lib/mxrb/compiler/java_proxy_generator.rb +509 -0
  97. data/lib/mxrb/compiler/legacy_data_grid_compiler.rb +267 -0
  98. data/lib/mxrb/compiler/legacy_page_builder.rb +287 -0
  99. data/lib/mxrb/compiler/mda.rb +99 -0
  100. data/lib/mxrb/compiler/microflow_document_compiler.rb +85 -0
  101. data/lib/mxrb/compiler/microflow_materializer.rb +48 -0
  102. data/lib/mxrb/compiler/microflow_node_compiler.rb +250 -0
  103. data/lib/mxrb/compiler/model_package.rb +116 -0
  104. data/lib/mxrb/compiler/model_values.rb +67 -0
  105. data/lib/mxrb/compiler/nanoflow_program_compiler.rb +483 -0
  106. data/lib/mxrb/compiler/navigation_document_compiler.rb +93 -0
  107. data/lib/mxrb/compiler/packager.rb +100 -0
  108. data/lib/mxrb/compiler/page_bundle_builder.rb +56 -0
  109. data/lib/mxrb/compiler/page_bundle_compiler.rb +2004 -0
  110. data/lib/mxrb/compiler/page_document_compiler.rb +77 -0
  111. data/lib/mxrb/compiler/portable_packager.rb +356 -0
  112. data/lib/mxrb/compiler/project_jar_archive.rb +84 -0
  113. data/lib/mxrb/compiler/project_jar_builder.rb +121 -0
  114. data/lib/mxrb/compiler/project_materializer.rb +139 -0
  115. data/lib/mxrb/compiler/project_model_orderer.rb +52 -0
  116. data/lib/mxrb/compiler/runtime_data_types.rb +31 -0
  117. data/lib/mxrb/compiler/runtime_model_schema.rb +90 -0
  118. data/lib/mxrb/compiler/schemas/runtime-10.24.0.73019.json +1052 -0
  119. data/lib/mxrb/compiler/schemas/runtime-11.json +1215 -0
  120. data/lib/mxrb/compiler/schemas/runtime-6.10.8.json +1148 -0
  121. data/lib/mxrb/compiler/schemas/runtime-7.17.0.json +1144 -0
  122. data/lib/mxrb/compiler/schemas/runtime-7.5.0.json +1215 -0
  123. data/lib/mxrb/compiler/schemas/runtime-9.6.1.29396.json +868 -0
  124. data/lib/mxrb/compiler/schemas/system-model-10.24.0.73019.b64 +1135 -0
  125. data/lib/mxrb/compiler/schemas/system-model-11.12.1.b64 +815 -0
  126. data/lib/mxrb/compiler/schemas/system-model-6.10.8.b64 +497 -0
  127. data/lib/mxrb/compiler/schemas/system-model-7.17.0.b64 +519 -0
  128. data/lib/mxrb/compiler/schemas/system-model-7.5.0.b64 +504 -0
  129. data/lib/mxrb/compiler/schemas/system-model-9.6.1.29396.b64 +816 -0
  130. data/lib/mxrb/compiler/security_materializer.rb +104 -0
  131. data/lib/mxrb/compiler/settings_document_compiler.rb +45 -0
  132. data/lib/mxrb/compiler/settings_materializer.rb +25 -0
  133. data/lib/mxrb/compiler/source_model.rb +128 -0
  134. data/lib/mxrb/compiler/system_model_seed.rb +88 -0
  135. data/lib/mxrb/compiler/system_queue_materializer.rb +54 -0
  136. data/lib/mxrb/compiler/system_text_materializer.rb +49 -0
  137. data/lib/mxrb/compiler/translation_materializer.rb +95 -0
  138. data/lib/mxrb/compiler/web_bundle_builder.rb +155 -0
  139. data/lib/mxrb/compiler/web_list_data_source.rb +139 -0
  140. data/lib/mxrb/compiler/web_operation_compiler.rb +614 -0
  141. data/lib/mxrb/compiler/web_shell_materializer.rb +194 -0
  142. data/lib/mxrb/compiler/widget_package_extractor.rb +63 -0
  143. data/lib/mxrb/doctor.rb +116 -0
  144. data/lib/mxrb/dsl/builder.rb +1636 -0
  145. data/lib/mxrb/errors.rb +18 -0
  146. data/lib/mxrb/evaluation.rb +131 -0
  147. data/lib/mxrb/exporter.rb +1716 -0
  148. data/lib/mxrb/frontend/migrator.rb +775 -0
  149. data/lib/mxrb/functional.rb +307 -0
  150. data/lib/mxrb/github/annotator.rb +161 -0
  151. data/lib/mxrb/initializer.rb +257 -0
  152. data/lib/mxrb/integrity/validator.rb +136 -0
  153. data/lib/mxrb/io/bson_codec.rb +145 -0
  154. data/lib/mxrb/io/mpr_file.rb +558 -0
  155. data/lib/mxrb/io/mxunit_codec.rb +42 -0
  156. data/lib/mxrb/marketplace.rb +380 -0
  157. data/lib/mxrb/model/association.rb +98 -0
  158. data/lib/mxrb/model/attribute.rb +100 -0
  159. data/lib/mxrb/model/connector.rb +17 -0
  160. data/lib/mxrb/model/design_materializer.rb +133 -0
  161. data/lib/mxrb/model/design_migration.rb +126 -0
  162. data/lib/mxrb/model/design_system.rb +111 -0
  163. data/lib/mxrb/model/domain_model.rb +51 -0
  164. data/lib/mxrb/model/entity.rb +180 -0
  165. data/lib/mxrb/model/menu.rb +38 -0
  166. data/lib/mxrb/model/microflow.rb +100 -0
  167. data/lib/mxrb/model/module.rb +142 -0
  168. data/lib/mxrb/model/navigation.rb +127 -0
  169. data/lib/mxrb/model/page.rb +410 -0
  170. data/lib/mxrb/model/project.rb +187 -0
  171. data/lib/mxrb/model/unit.rb +48 -0
  172. data/lib/mxrb/module_initializer.rb +75 -0
  173. data/lib/mxrb/official_marketplace/content_api.rb +292 -0
  174. data/lib/mxrb/official_marketplace/dependency_resolver.rb +329 -0
  175. data/lib/mxrb/official_marketplace/lifecycle.rb +367 -0
  176. data/lib/mxrb/official_marketplace/module_package_importer.rb +318 -0
  177. data/lib/mxrb/official_marketplace/widget_package_installer.rb +414 -0
  178. data/lib/mxrb/official_marketplace.rb +720 -0
  179. data/lib/mxrb/oql/analyzer.rb +155 -0
  180. data/lib/mxrb/oql/index_advisor.rb +85 -0
  181. data/lib/mxrb/oql/plan_analyzer.rb +185 -0
  182. data/lib/mxrb/oql/server.rb +132 -0
  183. data/lib/mxrb/oql/sql_server_plan_analyzer.rb +190 -0
  184. data/lib/mxrb/oql/sql_server_workload_analyzer.rb +80 -0
  185. data/lib/mxrb/oql/workload_analyzer.rb +175 -0
  186. data/lib/mxrb/oql/workload_baseline.rb +60 -0
  187. data/lib/mxrb/oql.rb +424 -0
  188. data/lib/mxrb/project_lifecycle.rb +76 -0
  189. data/lib/mxrb/protocols/plan.rb +86 -0
  190. data/lib/mxrb/protocols.rb +123 -0
  191. data/lib/mxrb/runtime/database_workspace.rb +677 -0
  192. data/lib/mxrb/runtime/docker_executor.rb +41 -0
  193. data/lib/mxrb/runtime/docker_workspace.rb +29 -0
  194. data/lib/mxrb/runtime/executor.rb +169 -0
  195. data/lib/mxrb/runtime/java_locator.rb +26 -0
  196. data/lib/mxrb/runtime/native.rb +474 -0
  197. data/lib/mxrb/runtime/sql_server_database.rb +146 -0
  198. data/lib/mxrb/runtime/toolchain.rb +82 -0
  199. data/lib/mxrb/scaffold/cli.rb +113 -0
  200. data/lib/mxrb/scaffold/generator.rb +246 -0
  201. data/lib/mxrb/scaffold/help.rb +80 -0
  202. data/lib/mxrb/scaffold/page_templates.rb +54 -0
  203. data/lib/mxrb/scaffold/recipes.rb +198 -0
  204. data/lib/mxrb/scaffold/registry.rb +82 -0
  205. data/lib/mxrb/scaffold/templates.rb +723 -0
  206. data/lib/mxrb/scaffold/transaction.rb +84 -0
  207. data/lib/mxrb/schema/tables.rb +55 -0
  208. data/lib/mxrb/semantic/analyzer.rb +473 -0
  209. data/lib/mxrb/semantic/batch_plan.rb +68 -0
  210. data/lib/mxrb/semantic/domain_mutator.rb +419 -0
  211. data/lib/mxrb/semantic/embedder.rb +51 -0
  212. data/lib/mxrb/semantic/extractor.rb +290 -0
  213. data/lib/mxrb/semantic/index.rb +623 -0
  214. data/lib/mxrb/semantic/inliner.rb +216 -0
  215. data/lib/mxrb/semantic/mover.rb +153 -0
  216. data/lib/mxrb/semantic/onnx_embedder.rb +34 -0
  217. data/lib/mxrb/semantic/remover.rb +70 -0
  218. data/lib/mxrb/semantic/renamer.rb +211 -0
  219. data/lib/mxrb/semantic/tfidf_embedder.rb +46 -0
  220. data/lib/mxrb/semantic/vec_store.rb +75 -0
  221. data/lib/mxrb/team_server.rb +390 -0
  222. data/lib/mxrb/templates/project/10.24.0.73019.json +32 -0
  223. data/lib/mxrb/templates/project/11.12.1.json +32 -0
  224. data/lib/mxrb/templates/project/6.10.8.json +50 -0
  225. data/lib/mxrb/templates/project/7.17.0.json +50 -0
  226. data/lib/mxrb/templates/project/7.5.0.json +50 -0
  227. data/lib/mxrb/templates/project/9.6.1.29396.json +50 -0
  228. data/lib/mxrb/version.rb +5 -0
  229. data/lib/mxrb/widget_package.rb +338 -0
  230. data/lib/mxrb/widget_synchronizer.rb +31 -0
  231. data/lib/mxrb/writer.rb +3775 -0
  232. data/lib/mxrb.rb +188 -0
  233. data/marketplace/catalog.json +10 -0
  234. data/marketplace/modules/shared-kernel/domain/README.md +4 -0
  235. data/marketplace/modules/shared-kernel/module.rb +5 -0
  236. data/marketplace/modules/shared-kernel/mxrb-module.json +9 -0
  237. metadata +447 -0
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mxrb
4
+ module Compiler
5
+ # Compiles page routing, parameters, titles, and authorization for the Runtime model.
6
+ class PageDocumentCompiler
7
+ include ModelValues
8
+ include RuntimeDataTypes
9
+
10
+ def initialize(source)
11
+ @role_map = project_role_map(source)
12
+ end
13
+
14
+ def compile(unit)
15
+ source = unit.document
16
+ raise CompilationError, "unsupported page root #{source['$Type']}" unless source['$Type'] == 'Forms$Page'
17
+
18
+ page_fields(source).merge(identity_fields(source, unit.module_name)).merge(popup_fields(source))
19
+ end
20
+
21
+ private
22
+
23
+ def page_fields(source)
24
+ {
25
+ '$ID' => source['$ID'], '$Type' => source['$Type'],
26
+ 'Parameters' => array(source['Parameters']).map { parameter(_1) },
27
+ 'Title' => text_reference(source['Title']), 'UrlSegments' => url_segments(source['Url'])
28
+ }
29
+ end
30
+
31
+ def parameter(source)
32
+ {
33
+ '$ID' => source['$ID'], '$Type' => source['$Type'], 'Name' => source['Name'],
34
+ 'ParameterTypeRuntime' => data_type(source['ParameterType']),
35
+ 'IsRequired' => source.fetch('IsRequired', false) == true
36
+ }
37
+ end
38
+
39
+ def identity_fields(source, module_name)
40
+ {
41
+ 'Name' => source['Name'], 'QualifiedName' => "#{module_name}.#{source['Name']}",
42
+ 'ModelerAllowedUserRoles' => allowed_user_roles(source)
43
+ }
44
+ end
45
+
46
+ def popup_fields(source)
47
+ {
48
+ 'PopupWidth' => source['PopupWidth'].to_i, 'PopupHeight' => source['PopupHeight'].to_i,
49
+ 'PopupResizable' => source['PopupResizable'] == true, 'Url' => source['Url'].to_s
50
+ }
51
+ end
52
+
53
+ def text_reference(source)
54
+ return nil unless source
55
+
56
+ source.slice('$ID', '$Type')
57
+ end
58
+
59
+ def url_segments(url)
60
+ url.to_s.split('/').filter_map { _1.start_with?('{') && _1.end_with?('}') ? _1[1..-2] : nil }
61
+ end
62
+
63
+ def allowed_user_roles(page)
64
+ array(page['AllowedModuleRoles']).flat_map { @role_map.fetch(_1.to_s, []) }.uniq
65
+ end
66
+
67
+ def project_role_map(source)
68
+ security = source.documents('Security$ProjectSecurity').first
69
+ return {} unless security
70
+
71
+ array(security['UserRoles']).each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |role, map|
72
+ array(role['ModuleRoles']).each { |module_role| map[module_role.to_s] << role['Name'].to_s }
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,356 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+ require 'fileutils'
5
+ require 'tmpdir'
6
+ require 'zip'
7
+
8
+ module Mxrb
9
+ module Compiler
10
+ PortableResult = Data.define(:path, :mendix_version, :files, :sha256, :metadata)
11
+
12
+ # Renders the Runtime configuration files from deployment metadata.
13
+ class PortableConfiguration
14
+ def initialize(metadata)
15
+ @metadata = metadata
16
+ end
17
+
18
+ def files
19
+ {
20
+ 'etc/Default' => default_includes,
21
+ 'etc/StudioPro.conf' => studio,
22
+ 'etc/configurations/Default.conf' => default_configuration,
23
+ 'etc/constants/defaults.conf' => constants(variables: false),
24
+ 'etc/constants/variables.conf' => constants(variables: true)
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ def default_includes
31
+ <<~CONF
32
+ include file("etc/StudioPro.conf")
33
+ include file("etc/constants/defaults.conf")
34
+ include file("etc/configurations/Default.conf")
35
+ include file("etc/variables.conf")
36
+ include file("etc/constants/variables.conf")
37
+ CONF
38
+ end
39
+
40
+ def studio
41
+ events = Array(@metadata['ScheduledEvents']).filter_map { _1['Name'] }.join(',')
42
+ execution = events.empty? ? 'NONE' : 'SPECIFIED'
43
+ studio_document(events, execution)
44
+ end
45
+
46
+ def studio_document(events, execution)
47
+ <<~CONF
48
+ runtime {
49
+ params {
50
+ DTAPMode = D
51
+ ScheduledEventExecution = "#{execution}"
52
+ MyScheduledEvents = "#{hocon(events)}"
53
+ CACertificates = ""
54
+ ClientCertificates = ""
55
+ ClientCertificatePasswords = ""
56
+ HashAlgorithm = "BCRYPT:12"
57
+ }
58
+ adminUser.password = ""
59
+ #{debugger_configuration}
60
+ }
61
+ logging = [
62
+ {
63
+ name = MySubscriber
64
+ type = console
65
+ autoSubscribe = INFO
66
+ levels {}
67
+ }
68
+ ]
69
+ CONF
70
+ end
71
+
72
+ def debugger_configuration
73
+ @metadata['RuntimeVersion'].to_s.to_i >= 11 ? ' debugger.password = ""' : ''
74
+ end
75
+
76
+ def default_configuration
77
+ <<~CONF
78
+ runtime.params {
79
+ DatabaseType = HSQLDB
80
+ DatabaseName = default
81
+ DatabaseJdbcUrl = "jdbc:hsqldb:file:app/data/database/default"
82
+ }
83
+ admin { adminPassword = "", port = 8090, addresses = [ localhost ] }
84
+ runtime {
85
+ http { port = 8080, addresses = [ "*" ] }
86
+ params { ApplicationRootUrl = "http://localhost:8080/" }
87
+ }
88
+ CONF
89
+ end
90
+
91
+ def constants(variables:)
92
+ lines = Array(@metadata['Constants']).filter_map do |constant|
93
+ name = constant['Name'].to_s
94
+ next if name.empty?
95
+
96
+ value = variables ? variable(name) : %("#{hocon(constant['DefaultValue'])}")
97
+ %( "#{hocon(name)}" = #{value})
98
+ end
99
+ "runtime.params.MicroflowConstants {\n#{lines.join("\n")}\n}\n"
100
+ end
101
+
102
+ def variable(name)
103
+ environment = name.upcase.gsub(/[^A-Z0-9]+/, '_')
104
+ "${?CONSTANTS_#{environment}}"
105
+ end
106
+
107
+ def hocon(value)
108
+ value.to_s.gsub('\\', '\\\\').gsub('"', '\\"').gsub("\n", '\\n').gsub("\r", '\\r')
109
+ end
110
+ end
111
+
112
+ # Portable assets used by Runtime distributions that do not ship PAD.
113
+ module PortableFallbackAssets
114
+ START = <<~'SH'
115
+ #!/bin/sh
116
+ set -eu
117
+
118
+ ROOT_PATH=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
119
+ if [ -n "${JAVA_HOME:-}" ] && [ -x "$JAVA_HOME/bin/java" ]; then
120
+ JAVA="$JAVA_HOME/bin/java"
121
+ else
122
+ JAVA=$(command -v java || true)
123
+ fi
124
+ if [ -z "$JAVA" ] || [ ! -x "$JAVA" ]; then
125
+ echo "Cannot find java; set JAVA_HOME or add java to PATH." >&2
126
+ exit 1
127
+ fi
128
+
129
+ if [ -z "${M2EE_ADMIN_PASS:-}" ]; then
130
+ M2EE_ADMIN_PASS=$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n')
131
+ export M2EE_ADMIN_PASS
132
+ fi
133
+ RUNTIME_ADMINUSER_PASSWORD="${RUNTIME_ADMINUSER_PASSWORD:-$M2EE_ADMIN_PASS}"
134
+ export RUNTIME_ADMINUSER_PASSWORD
135
+
136
+ export MX_INSTALL_PATH="$ROOT_PATH/lib"
137
+ CONFIG="${CONFIG:-$ROOT_PATH/etc/Default}"
138
+ if [ "$#" -eq 0 ]; then
139
+ set -- "$CONFIG"
140
+ fi
141
+ cd "$ROOT_PATH"
142
+ exec "$JAVA" ${JAVA_OPTS:-} \
143
+ -Dfile.encoding=UTF-8 \
144
+ -Djava.io.tmpdir="${TMPDIR:-/tmp}" \
145
+ -Djava.library.path="$MX_INSTALL_PATH/runtime/lib/x64;$ROOT_PATH/app/model/lib/userlib" \
146
+ -jar "$MX_INSTALL_PATH/runtime/launcher/runtimelauncher.jar" \
147
+ "$ROOT_PATH/app/." "$@"
148
+ SH
149
+ VARIABLES = <<~CONF
150
+ admin {
151
+ port = ${?ADMIN_PORT}
152
+ addresses = ${?ADMIN_ADDRESSES}
153
+ adminPassword = ${?M2EE_ADMIN_PASS}
154
+ }
155
+ runtime.http {
156
+ port = ${?RUNTIME_HTTP_PORT}
157
+ addresses = ${?RUNTIME_HTTP_ADDRESSES}
158
+ }
159
+ runtime.params {
160
+ DatabaseHost = ${?RUNTIME_PARAMS_DATABASEHOST}
161
+ DatabaseJdbcUrl = ${?RUNTIME_PARAMS_DATABASEJDBCURL}
162
+ DatabaseName = ${?RUNTIME_PARAMS_DATABASENAME}
163
+ DatabaseUserName = ${?RUNTIME_PARAMS_DATABASEUSERNAME}
164
+ DatabasePassword = ${?RUNTIME_PARAMS_DATABASEPASSWORD}
165
+ DatabaseType = ${?RUNTIME_PARAMS_DATABASETYPE}
166
+ DatabaseUseSsl = ${?RUNTIME_PARAMS_DATABASEUSESSL}
167
+ ApplicationRootUrl = ${?RUNTIME_PARAMS_APPLICATIONROOTURL}
168
+ }
169
+ runtime.adminUser.password = ${?RUNTIME_ADMINUSER_PASSWORD}
170
+ CONF
171
+ EXAMPLE = <<~CONF
172
+ # Copy this file and pass its path to bin/start to override etc/Default.
173
+ runtime.params { DatabaseType = HSQLDB, DatabaseName = default }
174
+ admin { port = 8090, addresses = [ localhost ] }
175
+ runtime.http { port = 8080, addresses = [ "*" ] }
176
+ CONF
177
+ end
178
+
179
+ # Deterministic ZIP writer shared by portable package orchestration.
180
+ class PortableArchiveWriter
181
+ FIXED_TIME = Packager::FIXED_TIME
182
+ APP_ROOTS = (Adapter::ROOTS + ['run']).freeze
183
+
184
+ def initialize(deployment, runtime, metadata)
185
+ @deployment = deployment
186
+ @runtime = runtime
187
+ @metadata = metadata
188
+ end
189
+
190
+ def write(output)
191
+ FileUtils.mkdir_p(File.dirname(output))
192
+ Dir.mktmpdir('mxrb-portable-', File.dirname(output)) do |tmpdir|
193
+ temporary = File.join(tmpdir, 'runtime.zip')
194
+ Zip::File.open(temporary, create: true) do |archive|
195
+ add_tree(archive, @runtime, 'lib/runtime')
196
+ add_application(archive)
197
+ add_generated_files(archive)
198
+ end
199
+ FileUtils.mv(temporary, output, force: true)
200
+ end
201
+ end
202
+
203
+ private
204
+
205
+ def add_application(archive)
206
+ APP_ROOTS.each do |root_name|
207
+ source = File.join(@deployment, root_name)
208
+ add_tree(archive, source, "app/#{root_name}") if File.exist?(source)
209
+ end
210
+ %w[data data/database data/files data/tmp log].each do |path|
211
+ add_directory(archive, "app/#{path}/", 0o755)
212
+ end
213
+ end
214
+
215
+ def add_generated_files(archive)
216
+ add_start_scripts(archive)
217
+ %w[example.conf variables.conf].each do |name|
218
+ add_runtime_configuration(archive, name)
219
+ end
220
+ PortableConfiguration.new(@metadata).files.each do |path, content|
221
+ add_string(archive, path, content)
222
+ end
223
+ end
224
+
225
+ def add_start_scripts(archive)
226
+ templates = Dir.glob(File.join(@runtime, 'pad', 'bin', '*.hbs')).sort
227
+ return add_fallback_start(archive) if templates.empty?
228
+
229
+ templates.each do |source|
230
+ name = File.basename(source, '.hbs')
231
+ content = render_template(File.binread(source))
232
+ add_string(archive, "bin/#{name}", content, mode: name == 'start' ? 0o755 : 0o644)
233
+ end
234
+ end
235
+
236
+ def add_fallback_start(archive)
237
+ add_string(archive, 'bin/start', PortableFallbackAssets::START, mode: 0o755)
238
+ end
239
+
240
+ def add_runtime_configuration(archive, name)
241
+ source = File.join(@runtime, 'pad', 'etc', name)
242
+ return add_source(archive, source, "etc/#{name}") if File.file?(source)
243
+
244
+ fallback = if name == 'variables.conf'
245
+ PortableFallbackAssets::VARIABLES
246
+ else
247
+ PortableFallbackAssets::EXAMPLE
248
+ end
249
+ add_string(archive, "etc/#{name}", fallback)
250
+ end
251
+
252
+ def render_template(content)
253
+ rendered = content.start_with?("\xEF\xBB\xBF".b) ? content.byteslice(3..) : content
254
+ rendered.force_encoding(Encoding::UTF_8)
255
+ .gsub(/\{\{!--.*?--\}\}\s*/m, '')
256
+ .gsub('{{DefaultConfig}}', 'Default')
257
+ end
258
+
259
+ def add_tree(archive, source_root, destination_root)
260
+ add_directory(archive, "#{destination_root}/", File.stat(source_root).mode & 0o777)
261
+ tree_paths(source_root).each do |source|
262
+ destination = "#{destination_root}/#{source.delete_prefix("#{source_root}/").tr('\\', '/')}"
263
+ if File.directory?(source)
264
+ add_directory(archive, "#{destination}/", 0o755)
265
+ else
266
+ add_source(archive, source, destination)
267
+ end
268
+ end
269
+ end
270
+
271
+ def tree_paths(root)
272
+ Dir.glob(File.join(root, '**', '*'), File::FNM_DOTMATCH).sort
273
+ .reject { %w[. ..].include?(File.basename(_1)) }
274
+ end
275
+
276
+ def add_source(archive, source, destination)
277
+ archive.add(zip_entry(destination, File.stat(source).mode & 0o777), source)
278
+ end
279
+
280
+ def add_string(archive, destination, content, mode: 0o644)
281
+ archive.get_output_stream(zip_entry(destination, mode)) { _1.write(content) }
282
+ archive.find_entry(destination).unix_perms = mode
283
+ end
284
+
285
+ def add_directory(archive, destination, mode)
286
+ archive.mkdir(destination)
287
+ entry = archive.find_entry(destination)
288
+ entry.time = FIXED_TIME
289
+ entry.unix_perms = mode
290
+ end
291
+
292
+ def zip_entry(destination, mode)
293
+ Zip::Entry.new('', destination).tap do |entry|
294
+ entry.time = FIXED_TIME
295
+ entry.unix_perms = mode
296
+ end
297
+ end
298
+ end
299
+
300
+ # Creates an executable portable application from materialized app files
301
+ # and an installed Mendix Runtime distribution. It never invokes mxbuild.
302
+ class PortablePackager
303
+ REQUIRED_RUNTIME_FILES = %w[launcher/runtimelauncher.jar].freeze
304
+
305
+ def initialize(mpr_path, deployment: nil, mendix_home: nil)
306
+ @mpr_path = File.expand_path(mpr_path)
307
+ @project_root = File.dirname(@mpr_path)
308
+ @deployment = File.expand_path(deployment || File.join(@project_root, 'deployment'))
309
+ @mendix_home = mendix_home && File.expand_path(mendix_home)
310
+ end
311
+
312
+ def pack(output:, force: false)
313
+ output_path = File.expand_path(output)
314
+ validate_output!(output_path, force:)
315
+ version = Mxrb.open(@mpr_path, &:mendix_version)
316
+ adapter = Adapter.for(version)
317
+ metadata = adapter.validate_deployment!(@deployment)
318
+ adapter.validate_freshness!(@mpr_path, @deployment)
319
+ runtime = runtime_root(version)
320
+ validate_runtime!(runtime)
321
+ PortableArchiveWriter.new(@deployment, runtime, metadata).write(output_path)
322
+ result(output_path, version, metadata)
323
+ end
324
+
325
+ private
326
+
327
+ def validate_output!(output, force:)
328
+ raise CompilationError, "#{output}: file already exists" if File.exist?(output) && !force
329
+ return if File.directory?(@deployment)
330
+
331
+ raise CompilationError, "#{@deployment}: deployment directory not found"
332
+ end
333
+
334
+ def runtime_root(version)
335
+ root = @mendix_home || File.join(Dir.home, '.local', 'share', 'mendix', version)
336
+ root = File.join(root, 'runtime') unless File.basename(root) == 'runtime'
337
+ File.expand_path(root)
338
+ end
339
+
340
+ def validate_runtime!(runtime)
341
+ missing = REQUIRED_RUNTIME_FILES.reject { File.file?(File.join(runtime, _1)) }
342
+ return if missing.empty?
343
+
344
+ raise CompilationError, "Mendix Runtime is incomplete at #{runtime}; missing #{missing.join(', ')}"
345
+ end
346
+
347
+ def result(output, version, metadata)
348
+ count = Zip::File.open(output) { _1.count { |entry| !entry.directory? } }
349
+ PortableResult.new(
350
+ path: output, mendix_version: version, files: count,
351
+ sha256: Digest::SHA256.file(output).hexdigest, metadata:
352
+ )
353
+ end
354
+ end
355
+ end
356
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'tmpdir'
5
+ require 'zip'
6
+
7
+ module Mxrb
8
+ module Compiler
9
+ # Writes the deterministic OSGi project bundle around compiled Java classes.
10
+ class ProjectJarArchive
11
+ def initialize(mpr_path, project_root, deployment)
12
+ @mpr_path = mpr_path
13
+ @project_root = project_root
14
+ @deployment = deployment
15
+ end
16
+
17
+ def write(output, classes)
18
+ FileUtils.mkdir_p(File.dirname(output))
19
+ Dir.mktmpdir('mxrb-project-jar-', File.dirname(output)) do |root|
20
+ temporary = File.join(root, 'project.jar')
21
+ create(temporary, classes)
22
+ FileUtils.mv(temporary, output, force: true)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def create(path, classes)
29
+ Zip::File.open(path, create: true) do |archive|
30
+ add_string(archive, 'META-INF/MANIFEST.MF', manifest)
31
+ add_tree(archive, classes)
32
+ add_component(archive)
33
+ add_user_resources(archive)
34
+ end
35
+ end
36
+
37
+ def add_tree(archive, root)
38
+ Dir.glob(File.join(root, '**', '*')).sort.select { File.file?(_1) }.each do |source|
39
+ add_source(archive, source, source.delete_prefix("#{root}/"))
40
+ end
41
+ end
42
+
43
+ def add_component(archive)
44
+ source = File.join(@deployment, 'run', 'component.xml')
45
+ add_source(archive, source, 'OSGI-INF/component.xml') if File.file?(source)
46
+ end
47
+
48
+ def add_user_resources(archive)
49
+ root = File.join(@project_root, 'userlib')
50
+ return unless File.directory?(root)
51
+
52
+ resources(root).each { add_source(archive, _1, _1.delete_prefix("#{root}/")) }
53
+ end
54
+
55
+ def resources(root)
56
+ Dir.glob(File.join(root, '**', '*')).sort.select do |path|
57
+ File.file?(path) && File.extname(path).downcase != '.jar'
58
+ end
59
+ end
60
+
61
+ def add_source(archive, source, destination)
62
+ add_string(archive, destination, File.binread(source))
63
+ end
64
+
65
+ def add_string(archive, destination, content)
66
+ entry = Zip::Entry.new('', destination)
67
+ entry.time = Packager::FIXED_TIME
68
+ entry.unix_perms = 0o644
69
+ archive.get_output_stream(entry) { _1.write(content) }
70
+ end
71
+
72
+ def manifest
73
+ name = File.basename(@mpr_path, File.extname(@mpr_path)).downcase
74
+ <<~MANIFEST.gsub("\n", "\r\n")
75
+ Manifest-Version: 1.0
76
+ Bundle-Name: #{name}
77
+ Bundle-SymbolicName: project
78
+ Service-Component: OSGI-INF/component.xml
79
+
80
+ MANIFEST
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'open3'
5
+ require 'tmpdir'
6
+
7
+ module Mxrb
8
+ module Compiler
9
+ ProjectJarResult = Data.define(:path, :sources, :classes, :classpath_entries)
10
+
11
+ # Compiles generated/custom Java sources and writes project.jar without MxBuild or Gradle.
12
+ class ProjectJarBuilder
13
+ LEGACY_UNUSED_IMPORTS = ['com.mendix.webui.CustomJavaAction'].freeze
14
+
15
+ def initialize(mpr_path, deployment:, mendix_home:, java_home: nil)
16
+ @mpr_path = File.expand_path(mpr_path)
17
+ @project_root = File.dirname(@mpr_path)
18
+ @deployment = File.expand_path(deployment)
19
+ @mendix_home = File.expand_path(mendix_home)
20
+ @version = SourceModel.read(@mpr_path).version
21
+ @java_major = Runtime::Toolchain.new(@mpr_path).plan.java_version
22
+ @java_home = Runtime::JavaLocator.resolve(@java_major, configured: java_home)
23
+ end
24
+
25
+ def build
26
+ validate_java!
27
+ output = File.join(@deployment, 'model', 'bundles', 'project.jar')
28
+ JavaProxyGenerator.new(@mpr_path, project_root: @project_root).generate
29
+ sources = Dir.glob(File.join(@project_root, 'javasource', '**', '*.java')).sort
30
+ classpath = classpath_entries
31
+ compile_and_package(output, sources, classpath)
32
+ result(output, sources, classpath)
33
+ end
34
+
35
+ private
36
+
37
+ def compile_and_package(output, sources, classpath)
38
+ Dir.mktmpdir('mxrb-java-', @deployment) do |root|
39
+ classes = File.join(root, 'classes')
40
+ FileUtils.mkdir_p(classes)
41
+ compile(stage_legacy_sources(sources, root), classpath, classes, root) unless sources.empty?
42
+ ProjectJarArchive.new(@mpr_path, @project_root, @deployment).write(output, classes)
43
+ end
44
+ end
45
+
46
+ def stage_legacy_sources(sources, root)
47
+ sources.map do |source|
48
+ original = File.read(source)
49
+ rendered = strip_legacy_unused_imports(original)
50
+ next source if rendered == original
51
+
52
+ relative = source.delete_prefix("#{@project_root}/")
53
+ staged = File.join(root, 'sources', relative)
54
+ FileUtils.mkdir_p(File.dirname(staged))
55
+ File.write(staged, rendered)
56
+ staged
57
+ end
58
+ end
59
+
60
+ def strip_legacy_unused_imports(source)
61
+ LEGACY_UNUSED_IMPORTS.reduce(source) do |rendered, class_name|
62
+ simple_name = class_name.split('.').last
63
+ next rendered unless rendered.scan(/\b#{Regexp.escape(simple_name)}\b/).one?
64
+
65
+ rendered.gsub(/^import\s+#{Regexp.escape(class_name)};\r?\n/, '')
66
+ end
67
+ end
68
+
69
+ def result(output, sources, classpath)
70
+ ProjectJarResult.new(
71
+ path: output, sources: sources.length, classes: jar_classes(output),
72
+ classpath_entries: classpath.length
73
+ )
74
+ end
75
+
76
+ def validate_java!
77
+ executable = @java_home && File.join(@java_home, 'bin', 'javac')
78
+ return if executable && File.executable?(executable)
79
+
80
+ raise CompilationError, "Java #{@java_major} JDK not found; set MXRB_JAVA_HOME or JAVA_HOME"
81
+ end
82
+
83
+ def compile(sources, classpath, classes, root)
84
+ arguments = language_level_arguments + ['-encoding', 'UTF-8', '-d', classes]
85
+ arguments.concat(['-classpath', classpath.join(File::PATH_SEPARATOR)]) unless classpath.empty?
86
+ arguments.concat(sources)
87
+ argument_file = File.join(root, 'javac.args')
88
+ File.write(argument_file, arguments.map { quote_argument(_1) }.join("\n"))
89
+ output, status = Open3.capture2e(File.join(@java_home, 'bin', 'javac'), "@#{argument_file}")
90
+ return if status.success?
91
+
92
+ raise CompilationError, "javac failed for #{@mpr_path}:\n#{output}"
93
+ end
94
+
95
+ def language_level_arguments
96
+ return ['--release', @java_major] if @java_major.to_i >= 9
97
+
98
+ level = "1.#{@java_major}"
99
+ ['-source', level, '-target', level]
100
+ end
101
+
102
+ def quote_argument(value)
103
+ %("#{value.to_s.gsub('\\', '\\\\').gsub('"', '\\"')}")
104
+ end
105
+
106
+ def classpath_entries
107
+ runtime = File.basename(@mendix_home) == 'runtime' ? @mendix_home : File.join(@mendix_home, 'runtime')
108
+ roots = [
109
+ File.join(runtime, 'bundles'), File.join(@project_root, 'userlib'),
110
+ File.join(@project_root, 'vendorlib'), File.join(@deployment, 'model', 'lib', 'userlib')
111
+ ]
112
+ roots.flat_map { Dir.glob(File.join(_1, '*.jar')) }.uniq.sort
113
+ end
114
+
115
+ def jar_classes(path)
116
+ require 'zip'
117
+ Zip::File.open(path) { _1.count { |entry| entry.name.end_with?('.class') } }
118
+ end
119
+ end
120
+ end
121
+ end