reflex-packager 0.2.0 → 0.3.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/package.yml +310 -0
  3. data/.github/workflows/release-gem.yml +2 -49
  4. data/.github/workflows/tag.yml +11 -28
  5. data/.github/workflows/test.yml +16 -37
  6. data/.gitignore +2 -0
  7. data/ChangeLog.md +28 -0
  8. data/Gemfile.lock +26 -0
  9. data/README.md +232 -27
  10. data/Rakefile +21 -0
  11. data/VERSION +1 -1
  12. data/bin/reflex +2 -32
  13. data/examples/hello/icon.png +0 -0
  14. data/examples/hello/main.rb +18 -0
  15. data/examples/hello/reflex.yml +3 -0
  16. data/lib/reflex/packager/app_store_connect.rb +162 -0
  17. data/lib/reflex/packager/cli.rb +42 -9
  18. data/lib/reflex/packager/config.rb +233 -36
  19. data/lib/reflex/packager/data_compiler.rb +59 -0
  20. data/lib/reflex/packager/data_file.rb +183 -0
  21. data/lib/reflex/packager/data_loader.rb +127 -0
  22. data/lib/reflex/packager/gems.rb +282 -0
  23. data/lib/reflex/packager/library.rb +120 -0
  24. data/lib/reflex/packager/macos.rb +592 -94
  25. data/lib/reflex/packager/platform.rb +289 -10
  26. data/lib/reflex/packager/profile.rb +38 -26
  27. data/lib/reflex/packager/profiles/reflex.rb +43 -0
  28. data/lib/reflex/packager/rake.rb +115 -0
  29. data/lib/reflex/packager/stores/app_store.rb +60 -0
  30. data/lib/reflex/packager/stores/itchio.rb +53 -0
  31. data/lib/reflex/packager/templates/macos/AppStore.entitlements.erb +8 -0
  32. data/lib/reflex/packager/templates/macos/AppStoreExportOptions.plist.erb +14 -0
  33. data/lib/reflex/packager/templates/macos/ExportOptions.plist.erb +20 -0
  34. data/lib/reflex/packager/templates/macos/InfoPlist.strings.erb +5 -0
  35. data/lib/reflex/packager/templates/macos/boot.rb.erb +29 -0
  36. data/lib/reflex/packager/templates/macos/compile.rb.erb +7 -0
  37. data/lib/reflex/packager/templates/macos/main.mm.erb +10 -7
  38. data/lib/reflex/packager/templates/macos/project.yml.erb +60 -3
  39. data/lib/reflex/packager/templates/windows/app.manifest.erb +25 -0
  40. data/lib/reflex/packager/templates/windows/app.rc.erb +38 -0
  41. data/lib/reflex/packager/templates/windows/boot.rb.erb +32 -0
  42. data/lib/reflex/packager/templates/windows/main.cpp.erb +47 -0
  43. data/lib/reflex/packager/templates/windows/runtime.manifest.erb +7 -0
  44. data/lib/reflex/packager/windows.rb +370 -0
  45. data/lib/reflex/packager.rb +18 -1
  46. data/reflex-packager.gemspec +8 -7
  47. data/test/helper.rb +50 -5
  48. data/test/test_packager_app_store_connect.rb +108 -0
  49. data/test/test_packager_cli.rb +38 -3
  50. data/test/test_packager_config.rb +166 -27
  51. data/test/test_packager_data_file.rb +96 -0
  52. data/test/test_packager_data_loader.rb +114 -0
  53. data/test/test_packager_library.rb +81 -0
  54. data/test/test_packager_macos.rb +714 -86
  55. data/test/test_packager_platform.rb +16 -0
  56. data/test/test_packager_rake.rb +165 -0
  57. data/test/test_packager_stores_app_store.rb +66 -0
  58. data/test/test_packager_stores_itchio.rb +38 -0
  59. data/test/test_packager_windows.rb +685 -0
  60. metadata +72 -15
  61. data/.github/workflows/utils.rb +0 -127
  62. data/lib/reflex/packager/templates/macos/Podfile.erb +0 -30
@@ -0,0 +1,16 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestPackagerPlatform < Test::Unit::TestCase
5
+
6
+ RP = Reflex::Packager
7
+
8
+ def test_methods_to_implement()
9
+ platform = RP::Platform.new nil
10
+ %i[generate build archive_command platform_name].each do |name|
11
+ error = assert_raise(RuntimeError) {platform.send name}
12
+ assert_include error.message, "must implement #{name}"
13
+ end
14
+ end
15
+
16
+ end# TestPackagerPlatform
@@ -0,0 +1,165 @@
1
+ require_relative 'helper'
2
+ require 'reflex/packager/rake'
3
+
4
+
5
+ class TestPackagerRake < Test::Unit::TestCase
6
+
7
+ include ::Rake::DSL
8
+ include Xot::Rake
9
+ include Reflex::Packager::Rake
10
+
11
+ def application(&block)
12
+ Dir.mktmpdir do |dir|
13
+ Dir.chdir dir do
14
+ {
15
+ 'reflex.yml' => "name: My Game\nfiles: ['lib/**/*']\n",
16
+ 'main.rb' => '',
17
+ 'VERSION' => '1.2.3',
18
+ 'lib/a/b.rb' => ''
19
+ }.each do |path, text|
20
+ FileUtils.mkdir_p File.dirname(path)
21
+ File.write path, text
22
+ end
23
+ # not signed, whatever this runs with
24
+ with_env(ENV_TO_SIGN) {block.call File.realpath(dir)}
25
+ end
26
+ end
27
+ end
28
+
29
+ def setup()
30
+ ::Rake::Task.clear
31
+ ::Rake::TaskManager.record_task_metadata = true # for the descriptions
32
+ end
33
+
34
+ def test_package_application()
35
+ application do |dir|
36
+ stub Gem, :win_platform?, false do
37
+ package_application
38
+ end
39
+ app = File.join dir, 'dist/adhoc/My Game.app'
40
+ archive = File.join dir, 'dist/adhoc/MyGame-1.2.3-macos-universal.zip'
41
+ assert_equal [app], ::Rake::Task['package'].prerequisites
42
+ assert_equal [archive], ::Rake::Task['archive'].prerequisites
43
+ assert_equal [app], ::Rake::Task[archive].prerequisites
44
+ assert_equal [], ::Rake::Task['release'].prerequisites # made apart on CI
45
+ assert_equal %w[lib/a/b.rb main.rb reflex.yml VERSION].sort,
46
+ ::Rake::Task[app].prerequisites.sort # the files of the app, not lib/a
47
+ assert_include ::Rake::Task['package'].comment, 'dist/adhoc/My Game.app'
48
+ assert_include ::Rake::Task['release'].comment, 'v1.2.3'
49
+
50
+ pkg = File.join dir, 'dist/appstore/MyGame-1.2.3.pkg'
51
+ assert_equal [pkg], ::Rake::Task['package:appstore'].prerequisites
52
+ assert_equal ::Rake::Task[app].prerequisites, ::Rake::Task[pkg].prerequisites
53
+ assert_include ::Rake::Task['package:appstore'].comment, 'dist/appstore/MyGame-1.2.3.pkg'
54
+ end
55
+
56
+ ::Rake::Task.clear
57
+ application do
58
+ stub Gem, :win_platform?, true do
59
+ package_application
60
+ end
61
+ assert_false ::Rake::Task.task_defined?('package:appstore') # only on macos
62
+ end
63
+ end
64
+
65
+ def test_release()
66
+ application do |dir|
67
+ archive = File.join dir, 'dist/adhoc/MyGame-1.2.3-macos-universal.zip'
68
+ pkg = File.join dir, 'dist/appstore/MyGame-1.2.3.pkg'
69
+ releases = []
70
+ define_singleton_method(:release_on_github) {|*args, **kwargs| releases << [args, kwargs]}
71
+ stub Gem, :win_platform?, false do
72
+ package_application
73
+ end
74
+ release = -> *paths do
75
+ paths.each {FileUtils.mkdir_p File.dirname(_1); FileUtils.touch _1}
76
+ stub(self, :release_notes, 'notes') {::Rake::Task['release'].execute}
77
+ ensure
78
+ FileUtils.rm_f paths
79
+ end
80
+
81
+ with_env 'GITHUB_REF_NAME' => nil do
82
+ error = assert_raise(RuntimeError) {release.call}
83
+ assert_include error.message, 'nothing to release'
84
+ release.call archive
85
+ release.call pkg
86
+ release.call archive, pkg
87
+ end
88
+ notes = {tag: 'v1.2.3', notes: 'notes'}
89
+ assert_equal [[[archive], notes], [[pkg], notes], [[archive, pkg], notes]], releases
90
+
91
+ with_env 'GITHUB_REF_NAME' => 'v9.9.9' do
92
+ error = assert_raise(RuntimeError) {release.call archive}
93
+ assert_include error.message, 'not the tag of the version'
94
+ end
95
+ end
96
+ end
97
+
98
+ def test_upload()
99
+ application do |dir|
100
+ archive = File.join dir, 'dist/adhoc/MyGame-1.2.3-macos-universal.zip'
101
+ stub Gem, :win_platform?, false do
102
+ package_application
103
+ end
104
+ assert_equal [archive], ::Rake::Task['upload:itchio'].prerequisites
105
+ assert_include ::Rake::Task['upload:itchio'].comment, 'itch.io'
106
+ # only the actions of the task, not building the zip it takes
107
+ error = assert_raise(RuntimeError) {::Rake::Task['upload:itchio'].execute}
108
+ assert_include error.message, 'upload.itchio.project'
109
+
110
+ ::Rake::Task.clear
111
+ File.write 'reflex.yml', "name: My Game\nupload: {itchio: {project: user/game}}\n"
112
+ uploads = []
113
+ uploader = Object.new
114
+ uploader.define_singleton_method(:upload) {|*args, **kwargs| uploads << [args, kwargs]}
115
+ stub Gem, :win_platform?, false do
116
+ package_application
117
+ stub Reflex::Packager::Stores::Itchio, :new, uploader do
118
+ ::Rake::Task['upload:itchio'].execute
119
+ end
120
+ end
121
+ assert_equal [[[archive], {channel: 'macos', version: '1.2.3'}]], uploads
122
+ end
123
+
124
+ ::Rake::Task.clear
125
+ application do |dir|
126
+ pkg = File.join dir, 'dist/appstore/MyGame-1.2.3.pkg'
127
+ stub Gem, :win_platform?, false do
128
+ package_application
129
+ end
130
+ assert_equal [pkg], ::Rake::Task['upload:appstore'].prerequisites
131
+ assert_include ::Rake::Task['upload:appstore'].comment, 'App Store Connect'
132
+ # only the actions of the task, not building the package it takes
133
+ error = assert_raise(Reflex::Packager::Error) {::Rake::Task['upload:appstore'].execute}
134
+ assert_include error.message, 'no API key'
135
+
136
+ uploads = []
137
+ uploader = Object.new
138
+ uploader.define_singleton_method(:upload) {|*args, **kwargs| uploads << [args, kwargs]}
139
+ stub Reflex::Packager::Stores::AppStore, :new, uploader do
140
+ ::Rake::Task['upload:appstore'].execute
141
+ end
142
+ assert_equal [[[pkg], {platform: 'macos'}]], uploads
143
+
144
+ ::Rake::Task.clear
145
+ stub Gem, :win_platform?, true do
146
+ package_application
147
+ end
148
+ assert_false ::Rake::Task.task_defined?('upload:appstore') # only on macos
149
+ end
150
+ end
151
+
152
+ def test_release_notes()
153
+ application do
154
+ File.write 'ChangeLog.md', "# My Game ChangeLog\n\n\n## [v1.2.3] - 2026-10-13\n\n- Add a\n"
155
+ stub self, :git, "abc\n" do
156
+ assert_equal "- Add a\n\nBuilt with xord/all@abc.", release_notes('v1.2.3')
157
+ assert_equal 'Built with xord/all@abc.', release_notes('v9.9.9')
158
+ end
159
+ stub self, :git, '' do # the libraries of no checkout
160
+ assert_equal '- Add a', release_notes('v1.2.3')
161
+ end
162
+ end
163
+ end
164
+
165
+ end# TestPackagerRake
@@ -0,0 +1,66 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestPackagerStoresAppStore < Test::Unit::TestCase
5
+
6
+ RP = Reflex::Packager
7
+
8
+ # The App Store with the commands it runs, which succeed by +result+, and
9
+ # the content of the key file each command is given.
10
+ def app_store(credentials, result = true, &block)
11
+ app_store = RP::Stores::AppStore.new RP::AppStoreConnect.new(credentials)
12
+ commands = []
13
+ keys = []
14
+ app_store.define_singleton_method(:system) do |*cmd|
15
+ commands << cmd
16
+ keys << File.read(cmd.last)
17
+ result
18
+ end
19
+ app_store.define_singleton_method(:puts) {|*|}
20
+ Dir.mktmpdir do |dir|
21
+ pkg = File.join dir, 'a.pkg'
22
+ File.write pkg, ''
23
+ block.call app_store, pkg, commands, keys
24
+ end
25
+ end
26
+
27
+ def test_upload()
28
+ creds = {issuer_id: 'ISSUER', key_id: 'KEY', key_p8: 'P8'}
29
+ app_store creds do |app_store, pkg, commands, keys|
30
+ app_store.upload pkg, platform: 'macos'
31
+ assert_equal [
32
+ 'xcrun', 'altool', '--upload-package', pkg, '--platform', 'macos',
33
+ '--api-key', 'KEY', '--api-issuer', 'ISSUER', '--p8-file-path'
34
+ ], commands.first[0..-2]
35
+ assert_equal 'AuthKey_KEY.p8', File.basename(commands.first.last)
36
+ assert_equal ['P8'], keys
37
+ assert_false File.exist?(commands.first.last) # only while uploading
38
+
39
+ error = assert_raise(RP::Error) {app_store.upload "#{pkg}.no", platform: 'macos'}
40
+ assert_include error.message, 'nothing to upload'
41
+ end
42
+
43
+ Tempfile.create 'key' do |key|
44
+ key.write 'FILE'
45
+ key.flush
46
+ app_store({issuer_id: 'ISSUER', key_id: 'KEY', key_path: key.path}) do |app_store, pkg, commands, keys|
47
+ app_store.upload pkg, platform: 'macos'
48
+ assert_equal key.path, commands.first.last # the file as it is
49
+ assert_equal ['FILE'], keys
50
+ end
51
+ end
52
+
53
+ [[false, 'failed to upload'], [nil, 'xcrun not found']].each do |result, message|
54
+ app_store creds, result do |app_store, pkg, _|
55
+ error = assert_raise(RP::Error) {app_store.upload pkg, platform: 'macos'}
56
+ assert_include error.message, message
57
+ end
58
+ end
59
+
60
+ with_env(ENV_TO_SIGN) do
61
+ error = assert_raise(RP::Error) {RP::Stores::AppStore.new}
62
+ assert_include error.message, 'no API key'
63
+ end
64
+ end
65
+
66
+ end# TestPackagerStoresAppStore
@@ -0,0 +1,38 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestPackagerStoresItchio < Test::Unit::TestCase
5
+
6
+ RP = Reflex::Packager
7
+
8
+ # itch.io with the commands it runs, which succeed by +result+.
9
+ def itchio(result = true, &block)
10
+ itchio = RP::Stores::Itchio.new 'user/game'
11
+ commands = []
12
+ itchio.define_singleton_method(:system) {|*cmd| commands << cmd; result}
13
+ itchio.define_singleton_method(:puts) {|*|}
14
+ Dir.mktmpdir do |dir|
15
+ zip = File.join dir, 'a.zip'
16
+ File.write zip, ''
17
+ block.call itchio, zip, commands
18
+ end
19
+ end
20
+
21
+ def test_upload()
22
+ itchio do |itchio, zip, commands|
23
+ itchio.upload zip, channel: 'macos', version: '1.2.3'
24
+ assert_equal [['butler', 'push', zip, 'user/game:macos', '--userversion', '1.2.3']], commands
25
+
26
+ error = assert_raise(RP::Error) {itchio.upload "#{zip}.no", channel: 'macos', version: '1'}
27
+ assert_include error.message, 'nothing to upload'
28
+ end
29
+
30
+ [[false, 'failed to upload'], [nil, 'butler not found']].each do |result, message|
31
+ itchio result do |itchio, zip, _|
32
+ error = assert_raise(RP::Error) {itchio.upload zip, channel: 'macos', version: '1'}
33
+ assert_include error.message, message
34
+ end
35
+ end
36
+ end
37
+
38
+ end# TestPackagerStoresItchio