ruact 0.0.1 → 0.0.3

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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/.codecov.yml +31 -0
  3. data/.github/workflows/ci.yml +160 -94
  4. data/.github/workflows/server-functions-bench.yml +54 -0
  5. data/.rubocop.yml +19 -1
  6. data/.rubocop_todo.yml +175 -0
  7. data/CHANGELOG.md +86 -5
  8. data/README.md +2 -0
  9. data/RELEASING.md +9 -3
  10. data/bench/server_functions_dispatch_bench.rb +309 -0
  11. data/bench/server_functions_dispatch_bench.results.md +121 -0
  12. data/docs/internal/README.md +9 -0
  13. data/docs/internal/decisions/server-functions-api.md +1680 -0
  14. data/lib/generators/ruact/install/install_generator.rb +43 -0
  15. data/lib/generators/ruact/install/templates/application.jsx.tt +1 -1
  16. data/lib/generators/ruact/install/templates/initializer.rb.tt +1 -1
  17. data/lib/ruact/client_manifest.rb +125 -12
  18. data/lib/ruact/configuration.rb +264 -23
  19. data/lib/ruact/controller.rb +459 -32
  20. data/lib/ruact/doctor.rb +34 -2
  21. data/lib/ruact/erb_preprocessor.rb +6 -6
  22. data/lib/ruact/errors.rb +89 -0
  23. data/lib/ruact/flight/serializer.rb +2 -2
  24. data/lib/ruact/html_converter.rb +131 -31
  25. data/lib/ruact/query.rb +107 -0
  26. data/lib/ruact/railtie.rb +220 -3
  27. data/lib/ruact/render_context.rb +30 -0
  28. data/lib/ruact/render_pipeline.rb +201 -59
  29. data/lib/ruact/routing.rb +81 -0
  30. data/lib/ruact/serializable.rb +11 -11
  31. data/lib/ruact/server.rb +341 -0
  32. data/lib/ruact/server_action.rb +131 -0
  33. data/lib/ruact/server_functions/backtrace_cleaner.rb +32 -0
  34. data/lib/ruact/server_functions/bucket_two_payload.rb +109 -0
  35. data/lib/ruact/server_functions/codegen.rb +330 -0
  36. data/lib/ruact/server_functions/codegen_v2.rb +176 -0
  37. data/lib/ruact/server_functions/endpoint_controller.rb +237 -0
  38. data/lib/ruact/server_functions/error_payload.rb +93 -0
  39. data/lib/ruact/server_functions/error_rendering.rb +188 -0
  40. data/lib/ruact/server_functions/error_suggestion.rb +38 -0
  41. data/lib/ruact/server_functions/name_bridge.rb +113 -0
  42. data/lib/ruact/server_functions/query_context.rb +62 -0
  43. data/lib/ruact/server_functions/query_dispatch.rb +248 -0
  44. data/lib/ruact/server_functions/registry.rb +148 -0
  45. data/lib/ruact/server_functions/registry_entry.rb +26 -0
  46. data/lib/ruact/server_functions/route_source.rb +201 -0
  47. data/lib/ruact/server_functions/snapshot.rb +195 -0
  48. data/lib/ruact/server_functions/snapshot_writer.rb +65 -0
  49. data/lib/ruact/server_functions/standalone_context.rb +103 -0
  50. data/lib/ruact/server_functions/standalone_dispatcher.rb +178 -0
  51. data/lib/ruact/server_functions.rb +75 -0
  52. data/lib/ruact/version.rb +1 -1
  53. data/lib/ruact/view_helper.rb +17 -9
  54. data/lib/ruact.rb +85 -6
  55. data/lib/rubocop/cop/ruact/no_shared_state.rb +1 -1
  56. data/lib/tasks/benchmark.rake +15 -11
  57. data/lib/tasks/ruact.rake +81 -0
  58. data/spec/benchmarks/render_pipeline_benchmark_spec.rb +1 -1
  59. data/spec/fixtures/flight/README.md +55 -7
  60. data/spec/fixtures/flight/bigint.txt +1 -0
  61. data/spec/fixtures/flight/infinity.txt +1 -0
  62. data/spec/fixtures/flight/nan.txt +1 -0
  63. data/spec/fixtures/flight/negative_infinity.txt +1 -0
  64. data/spec/fixtures/flight/undefined.txt +1 -0
  65. data/spec/fixtures/story_7_9_views/controller_request_spec_support/demo/show.html.erb +3 -0
  66. data/spec/ruact/client_manifest_spec.rb +108 -0
  67. data/spec/ruact/configuration_spec.rb +501 -0
  68. data/spec/ruact/controller_request_spec.rb +204 -0
  69. data/spec/ruact/controller_spec.rb +427 -39
  70. data/spec/ruact/doctor_spec.rb +118 -0
  71. data/spec/ruact/erb_preprocessor_hook_spec.rb +3 -3
  72. data/spec/ruact/erb_preprocessor_spec.rb +7 -7
  73. data/spec/ruact/errors_spec.rb +95 -0
  74. data/spec/ruact/flight/renderer_spec.rb +14 -3
  75. data/spec/ruact/flight/serializer_spec.rb +129 -88
  76. data/spec/ruact/html_converter_spec.rb +183 -5
  77. data/spec/ruact/install_generator_spec.rb +93 -0
  78. data/spec/ruact/query_request_spec.rb +446 -0
  79. data/spec/ruact/query_spec.rb +105 -0
  80. data/spec/ruact/railtie_spec.rb +2 -3
  81. data/spec/ruact/render_context_spec.rb +58 -0
  82. data/spec/ruact/render_pipeline_concurrency_spec.rb +78 -0
  83. data/spec/ruact/render_pipeline_spec.rb +784 -330
  84. data/spec/ruact/serializable_spec.rb +8 -8
  85. data/spec/ruact/server_bucket_request_spec.rb +352 -0
  86. data/spec/ruact/server_function_name_spec.rb +53 -0
  87. data/spec/ruact/server_functions/backtrace_cleaner_spec.rb +63 -0
  88. data/spec/ruact/server_functions/bucket_two_payload_spec.rb +200 -0
  89. data/spec/ruact/server_functions/codegen_spec.rb +429 -0
  90. data/spec/ruact/server_functions/csrf_request_spec.rb +380 -0
  91. data/spec/ruact/server_functions/dispatch_request_spec.rb +819 -0
  92. data/spec/ruact/server_functions/error_payload_spec.rb +222 -0
  93. data/spec/ruact/server_functions/error_suggestion_spec.rb +79 -0
  94. data/spec/ruact/server_functions/name_bridge_spec.rb +188 -0
  95. data/spec/ruact/server_functions/query_context_spec.rb +72 -0
  96. data/spec/ruact/server_functions/railtie_integration_spec.rb +345 -0
  97. data/spec/ruact/server_functions/rake_spec.rb +86 -0
  98. data/spec/ruact/server_functions/registry_spec.rb +199 -0
  99. data/spec/ruact/server_functions/route_source_spec.rb +202 -0
  100. data/spec/ruact/server_functions/snapshot_spec.rb +256 -0
  101. data/spec/ruact/server_functions/snapshot_writer_spec.rb +71 -0
  102. data/spec/ruact/server_functions/standalone_action_spec.rb +224 -0
  103. data/spec/ruact/server_functions/standalone_context_spec.rb +142 -0
  104. data/spec/ruact/server_functions/standalone_dispatcher_spec.rb +273 -0
  105. data/spec/ruact/server_rescue_request_spec.rb +416 -0
  106. data/spec/ruact/server_spec.rb +180 -0
  107. data/spec/ruact/server_upload_request_spec.rb +311 -0
  108. data/spec/ruact/view_helper_spec.rb +23 -17
  109. data/spec/spec_helper.rb +52 -1
  110. data/spec/support/fixtures/pixel.png +0 -0
  111. data/spec/support/flight_wire_parser.rb +135 -0
  112. data/spec/support/flight_wire_parser_spec.rb +93 -0
  113. data/spec/support/matchers/flight_fixture_matcher.rb +356 -0
  114. data/spec/support/matchers/flight_fixture_matcher_spec.rb +250 -0
  115. data/spec/support/rails_stub.rb +75 -5
  116. data/vendor/javascript/ruact-server-functions-runtime/index.d.ts +139 -0
  117. data/vendor/javascript/ruact-server-functions-runtime/index.js +438 -0
  118. data/vendor/javascript/ruact-server-functions-runtime/index.test.mjs +827 -0
  119. data/vendor/javascript/ruact-server-functions-runtime/package.json +22 -0
  120. data/vendor/javascript/vite-plugin-ruact/index.js +3 -2
  121. data/vendor/javascript/vite-plugin-ruact/package-lock.json +1429 -0
  122. data/vendor/javascript/vite-plugin-ruact/package.json +15 -0
  123. data/vendor/javascript/vite-plugin-ruact/server-functions-codegen.mjs +761 -0
  124. data/vendor/javascript/vite-plugin-ruact/server-functions-codegen.test.mjs +866 -0
  125. data/vendor/javascript/vite-plugin-ruact/vitest.config.mjs +15 -0
  126. metadata +87 -6
  127. data/Users/luiz/workspace/rails-rsc/gem/vendor/javascript/vite-plugin-ruact/index.js +0 -163
  128. data/lib/ruact/component_registry.rb +0 -31
  129. data/lib/tasks/rsc.rake +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2178853f396d86e745518fafefe519f54ee2d941aefa956d14dce639567e0dc3
4
- data.tar.gz: c48ae371e9ba39e90a4db67e6a815fc6e5b2a982d9fe87709b41b36e9b83ff6b
3
+ metadata.gz: ce264ae503bd6e5c3239399e6bc39491724eec59b8f166a7e69923e40e2e0a10
4
+ data.tar.gz: 6ae79fc064960c97e8345a9030778dc61489c1179ea70123aad28a0de0806bf3
5
5
  SHA512:
6
- metadata.gz: 3343bf04f0e4133aad881e65c97b1d701de33f00337a9eab1a98d9369d30dd2d355e7e566356505602a5041b575efc8547a142bc6ac997a8e36f753a919123bf
7
- data.tar.gz: 328442e7e003bed71f9757805ad2620f89f57a49a8f5823b38a5082c7886618054dcfd0276bc68b59568514b2f1d6a69217e4213b5a947514f5a6d7e767b9324
6
+ metadata.gz: 660f2dbc8b3783eef80a3f935aa39a4afa34f02190578ada873fdd3074fe4182e77e9d5c486cd220edc68d89c8ff04581ab412eacd0bf3115c481841a4df9049
7
+ data.tar.gz: b4ddd9632c82d7a7d10e4113730bf4fedbd3b8b0007f917c4dc49fb880362e7b0b6a1bb7c2f953355d0926c39a893a7c377a693d0f8512330cfe226315e682cf
data/.codecov.yml ADDED
@@ -0,0 +1,31 @@
1
+ # Codecov configuration for the ruact gem.
2
+ # Story 6.7 — coverage is informativo (not a CI gate); Codecov reports status
3
+ # but never fails a PR. Diff coverage targets (90% line / 80% branch) are
4
+ # project DoD, surfaced via the per-PR comment for visibility.
5
+
6
+ coverage:
7
+ status:
8
+ project:
9
+ default:
10
+ informational: true
11
+ target: auto
12
+ threshold: 1%
13
+ patch:
14
+ default:
15
+ informational: true
16
+ target: 90%
17
+ threshold: 0%
18
+
19
+ comment:
20
+ layout: "diff, files"
21
+ behavior: default
22
+ require_changes: false
23
+
24
+ flags:
25
+ gem:
26
+ # Paths are relative to the gem repo root (where this CI runs and uploads
27
+ # from). lcov.info emits source paths like `./lib/ruact.rb`; Codecov
28
+ # normalizes the leading `./` so a `lib/` prefix matches.
29
+ paths:
30
+ - lib/
31
+ carryforward: false
@@ -6,7 +6,85 @@ on:
6
6
  pull_request:
7
7
  branches: [main]
8
8
 
9
+ # Note: this workflow runs in the `luizcg/ruact` repo, where the gem source
10
+ # lives at the repo root (no `gem/` subdirectory). The previous version had
11
+ # `working-directory: gem` on every step, copy-pasted from a monorepo layout;
12
+ # every run since the initial commit failed at `setup-ruby` with
13
+ # `ENOENT: no such file or directory, chdir 'gem'`. The e2e jobs depended
14
+ # on an `e2e/` Rails app that lives in the planning monorepo, not in this
15
+ # repo, and likewise could never run here. Both issues fixed in Story 6.7.
16
+
9
17
  jobs:
18
+ name-propagation:
19
+ # Required status check — fails if the legacy gem name or the legacy
20
+ # API-surface prefix appear in user-facing files. Two complementary
21
+ # guards:
22
+ #
23
+ # (a) Story 5.1 — guards against the legacy gem-name regression
24
+ # (the `rails[_-]rsc` / PascalCase form). The legacy names are
25
+ # written here in the same regex-character-class form that the
26
+ # grep step itself uses, so this workflow file stays grep-clean
27
+ # under its own checks.
28
+ # (b) Story 5.12 — guards against the residual legacy prefix on the
29
+ # API surface (controller methods, header name, Rake task, view
30
+ # helper, token format, Serializable DSL) being re-introduced
31
+ # after the pre-v0.1.0 clean cut.
32
+ #
33
+ # Exclusion list mirrors the AC1 / AC6 contracts exactly: CHANGELOG.md
34
+ # (historical record), doc/ (generated YARD), *.gem (built artifacts).
35
+ name: No legacy gem-name or legacy-prefix API references
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v5
39
+ - name: Grep for legacy gem-name references (Story 5.1)
40
+ # Pattern is assembled from concatenated string fragments so this
41
+ # very workflow file is grep-clean even though it talks about the
42
+ # legacy names — that lets the canonical exclusion list stay at
43
+ # three entries (Story 5.1 review F4).
44
+ run: |
45
+ PATTERN='rails[_-]rsc|Rails''Rsc'
46
+ if git grep -nE "$PATTERN" -- \
47
+ ':!CHANGELOG.md' \
48
+ ':!doc/' \
49
+ ':!*.gem'; then
50
+ echo "::error::Legacy gem-name reference found in user-facing file."
51
+ echo "::error::Rename to 'ruact' / 'Ruact'. See Story 5.1 in _bmad-output/implementation-artifacts/."
52
+ exit 1
53
+ fi
54
+ echo "::notice::No legacy gem-name references found."
55
+ - name: Grep for residual rsc_* API references (Story 5.12)
56
+ # AC1 of Story 5.12: every identifier renamed in the clean cut.
57
+ # Uses Perl mode (-P) for word boundaries. Pattern fragments are
58
+ # split with $R / $RU shell-vars so this workflow file is itself
59
+ # grep-clean — the file contains `${R}` / `${RU}` references, not
60
+ # literal `rsc` / `RSC` strings that would match the pattern (same
61
+ # strategy as Story 5.1 review F4).
62
+ #
63
+ # Coverage (Story 5.12 review F5 — broaden beyond curated list):
64
+ # - any <legacy>_<identifier> (catches private helpers),
65
+ # - any __<legacy>_<X>__ helper,
66
+ # - any __<UPPER>_<X>__ token format,
67
+ # - any data-<legacy> / data-<legacy>-<X> DOM attribute,
68
+ # - the [-:] router / suspense / doctor literals,
69
+ # - the legacy Request HTTP header,
70
+ # - the legacy lib/tasks/<legacy>.rake file path.
71
+ # Literals are intentionally written with placeholder text so this
72
+ # workflow file is itself grep-clean under its own regex.
73
+ run: |
74
+ R='rsc'
75
+ RU="$(printf '%s' "$R" | tr a-z A-Z)"
76
+ PATTERN="\\b(${R}_\\w+|__${R}_\\w+__|__${RU}_\\w*[0-9]+__|data-${R}(-\\w+|=)|${R}[-:](router|suspense|doctor)|${RU}-Request|lib/tasks/${R}\\.rake)\\b"
77
+ if git grep -nP "$PATTERN" -- \
78
+ ':!CHANGELOG.md' \
79
+ ':!doc/' \
80
+ ':!*.gem' \
81
+ ':!coverage/'; then
82
+ echo "::error::Residual rsc_* API reference found in gem source."
83
+ echo "::error::Rename to the ruact_* equivalent. See Story 5.12 in _bmad-output/implementation-artifacts/."
84
+ exit 1
85
+ fi
86
+ echo "::notice::No residual rsc_* API references found."
87
+
10
88
  rspec:
11
89
  # Required status check — all 8 matrix combinations must pass to merge.
12
90
  name: RSpec (Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }})
@@ -18,30 +96,46 @@ jobs:
18
96
  rails: ["7.0", "7.1", "7.2", "8.0"]
19
97
  env:
20
98
  RAILS_VERSION: ${{ matrix.rails }}
99
+ # The Gemfile injects `gem "rails", "~> #{ENV['RAILS_VERSION']}"` for
100
+ # matrix testing, but Gemfile.lock cannot pin all four Rails versions
101
+ # at once. `bundler-cache: true` defaults to deployment/frozen mode
102
+ # which rejects any Gemfile change vs lockfile, so disable that here
103
+ # while keeping the gem cache for everything else.
104
+ BUNDLE_DEPLOYMENT: "false"
105
+ BUNDLE_FROZEN: "false"
21
106
  steps:
22
- - uses: actions/checkout@v4
107
+ - uses: actions/checkout@v5
23
108
  - uses: ruby/setup-ruby@v1
24
109
  with:
25
110
  ruby-version: ${{ matrix.ruby }}
26
111
  bundler-cache: true
27
- working-directory: gem
28
112
  - name: Run RSpec
29
- working-directory: gem
30
113
  run: bundle exec rspec --format progress
114
+ - name: Upload coverage to Codecov
115
+ # Upload from one canonical matrix cell only (Ruby 3.3 × Rails 7.2)
116
+ # to avoid quota burn and Codecov-side flag merge conflicts. Other
117
+ # cells run RSpec but skip the upload. `success()` ensures we never
118
+ # upload partial/aborted runs. `fail_ci_if_error: false` keeps a
119
+ # Codecov outage from reddening a build whose tests passed.
120
+ if: matrix.ruby == '3.3' && matrix.rails == '7.2' && success()
121
+ uses: codecov/codecov-action@v5
122
+ with:
123
+ token: ${{ secrets.CODECOV_TOKEN }}
124
+ files: coverage/lcov.info
125
+ flags: gem
126
+ fail_ci_if_error: false
31
127
 
32
128
  rubocop:
33
129
  # Required status check.
34
130
  name: RuboCop
35
131
  runs-on: ubuntu-latest
36
132
  steps:
37
- - uses: actions/checkout@v4
133
+ - uses: actions/checkout@v5
38
134
  - uses: ruby/setup-ruby@v1
39
135
  with:
40
136
  ruby-version: "3.3"
41
137
  bundler-cache: true
42
- working-directory: gem
43
138
  - name: Run RuboCop
44
- working-directory: gem
45
139
  run: bundle exec rubocop --format github
46
140
 
47
141
  yard:
@@ -49,14 +143,12 @@ jobs:
49
143
  name: YARD Docs
50
144
  runs-on: ubuntu-latest
51
145
  steps:
52
- - uses: actions/checkout@v4
146
+ - uses: actions/checkout@v5
53
147
  - uses: ruby/setup-ruby@v1
54
148
  with:
55
149
  ruby-version: "3.3"
56
150
  bundler-cache: true
57
- working-directory: gem
58
151
  - name: Validate YARD docs
59
- working-directory: gem
60
152
  run: bundle exec yard --fail-on-warning
61
153
 
62
154
  benchmark:
@@ -64,103 +156,77 @@ jobs:
64
156
  name: Memory Benchmark
65
157
  runs-on: ubuntu-latest
66
158
  steps:
67
- - uses: actions/checkout@v4
159
+ - uses: actions/checkout@v5
68
160
  - uses: ruby/setup-ruby@v1
69
161
  with:
70
162
  ruby-version: "3.3"
71
163
  bundler-cache: true
72
- working-directory: gem
73
164
  - name: Run memory benchmark
74
- working-directory: gem
75
165
  run: bundle exec rake benchmark:memory
76
166
 
77
- e2e:
78
- # Required status check both react@19.0.0 (pinned) and react@19.x (latest) must pass.
79
- name: E2E (React ${{ matrix.react }})
80
- runs-on: ubuntu-latest
81
- strategy:
82
- fail-fast: false
83
- matrix:
84
- react: ["19.0.0", "19.x"]
85
- steps:
86
- - uses: actions/checkout@v4
87
- - uses: ruby/setup-ruby@v1
88
- with:
89
- ruby-version: "3.3"
90
- bundler-cache: true
91
- working-directory: e2e
92
- - uses: actions/setup-node@v4
93
- with:
94
- node-version: "20"
95
- - name: Install npm packages
96
- working-directory: e2e
97
- run: npm install
98
- - name: Pin React version
99
- working-directory: e2e
100
- run: npm install react@${{ matrix.react }} react-dom@${{ matrix.react }}
101
- - name: Build Vite assets
102
- working-directory: e2e
103
- run: npx vite build
104
- - name: Run system tests
105
- working-directory: e2e
106
- env:
107
- RAILS_ENV: test
108
- run: bundle exec rails test:system
109
- - name: Upload screenshots on failure
110
- uses: actions/upload-artifact@v4
111
- if: failure()
112
- with:
113
- name: e2e-screenshots-react-${{ matrix.react }}
114
- path: e2e/tmp/screenshots
115
- if-no-files-found: ignore
167
+ # E2E jobs (e2e and e2e-next) were removed from this workflow in Story 6.7.
168
+ # They depend on the `e2e/` Rails app in the planning monorepo and cannot run
169
+ # here (this repo has no `e2e/` directory). When/if a host-app system test
170
+ # is desired in CI, host the workflow in the monorepo (luizcg/ruact-dev) where
171
+ # both the gem submodule and the e2e/ Rails app live side by side.
116
172
 
117
- e2e-next:
118
- # Non-blocking failures open a GitHub issue with label react-next-compat.
119
- # This job is NOT a required status check for branch protection.
120
- name: E2E (React next non-blocking)
173
+ release:
174
+ # Auto-release to RubyGems on every GREEN push to main, gated on the full
175
+ # required-check set via `needs:`. Versioning (source of truth:
176
+ # lib/ruact/version.rb): story verde -> patch; epic verde -> minor via an
177
+ # `[epic-done]` marker in the merge-commit message; major manual for 1.0.0.
178
+ # Auth is RubyGems Trusted Publishing (OIDC) — satisfies
179
+ # rubygems_mfa_required. The bump commit carries [skip ci] (no re-trigger).
180
+ name: Release to RubyGems
181
+ needs: [name-propagation, rspec, rubocop, yard, benchmark]
182
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
121
183
  runs-on: ubuntu-latest
122
- continue-on-error: true
184
+ permissions:
185
+ id-token: write
186
+ contents: write
187
+ concurrency:
188
+ group: release
189
+ cancel-in-progress: false
123
190
  steps:
124
- - uses: actions/checkout@v4
191
+ - uses: actions/checkout@v5
192
+ with:
193
+ fetch-depth: 0
125
194
  - uses: ruby/setup-ruby@v1
126
195
  with:
127
196
  ruby-version: "3.3"
128
197
  bundler-cache: true
129
- working-directory: e2e
130
- - uses: actions/setup-node@v4
131
- with:
132
- node-version: "20"
133
- - name: Install npm packages
134
- working-directory: e2e
135
- run: npm install
136
- - name: Pin React@next
137
- working-directory: e2e
138
- run: npm install react@next react-dom@next
139
- - name: Build Vite assets
140
- working-directory: e2e
141
- run: npx vite build
142
- - name: Run system tests
143
- id: system-tests
144
- working-directory: e2e
198
+ - name: Decide bump kind & compute next version
199
+ id: bump
145
200
  env:
146
- RAILS_ENV: test
147
- run: bundle exec rails test:system
148
- - name: Upload screenshots on failure
149
- uses: actions/upload-artifact@v4
150
- if: failure()
151
- with:
152
- name: e2e-screenshots-react-next
153
- path: e2e/tmp/screenshots
154
- if-no-files-found: ignore
155
- - name: Open compatibility issue on failure
156
- if: failure()
157
- uses: actions/github-script@v7
158
- with:
159
- script: |
160
- await github.rest.issues.create({
161
- owner: context.repo.owner,
162
- repo: context.repo.repo,
163
- title: `react@next compatibility failure (run #${context.runNumber})`,
164
- labels: ['react-next-compat'],
165
- body: `CI run [#${context.runNumber}](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) failed against react@next on commit \`${context.sha}\`.\n\nTriaged within 2 weeks per NFR7. Does not block release.`
166
- });
201
+ HEAD_MSG: ${{ github.event.head_commit.message }}
202
+ run: |
203
+ KIND=patch
204
+ printf '%s' "$HEAD_MSG" | grep -qi '\[epic-done\]' && KIND=minor
205
+ VERSION=$(ruby -e '
206
+ kind = ARGV[0]
207
+ path = "lib/ruact/version.rb"
208
+ src = File.read(path)
209
+ maj, min, pat = src.match(/VERSION = "(\d+)\.(\d+)\.(\d+)"/).captures.map(&:to_i)
210
+ case kind
211
+ when "major" then maj, min, pat = maj + 1, 0, 0
212
+ when "minor" then min, pat = min + 1, 0
213
+ else pat += 1
214
+ end
215
+ v = "#{maj}.#{min}.#{pat}"
216
+ File.write(path, src.sub(/VERSION = "[^"]+"/, %(VERSION = "#{v}")))
217
+ print v
218
+ ' "$KIND")
219
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
220
+ echo "::notice::Release bump $KIND -> v$VERSION"
221
+ - uses: rubygems/configure-rubygems-credentials@v2.0.0 # OIDC -> short-lived credential
222
+ - name: Commit, tag & publish
223
+ env:
224
+ VERSION: ${{ steps.bump.outputs.version }}
225
+ run: |
226
+ git config user.name "github-actions[bot]"
227
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
228
+ git commit -am "Release v$VERSION [skip ci]"
229
+ git tag "v$VERSION"
230
+ git push origin HEAD --tags
231
+ gem build ruact.gemspec
232
+ gem push "ruact-$VERSION.gem"
@@ -0,0 +1,54 @@
1
+ name: Server-functions bench (AC12)
2
+
3
+ # Story 8.1 AC12 — non-blocking nightly perf guard. Runs the AR-backed
4
+ # end-to-end dispatch benchmark and posts the numbers as a workflow
5
+ # summary. Does NOT gate merge (the bench measures wall-clock and is
6
+ # subject to runner noise); it exists to surface 10× regressions in
7
+ # the open instead of after release.
8
+ #
9
+ # Triggers:
10
+ # - nightly cron (06:00 UTC)
11
+ # - PRs touching `lib/ruact/server_functions/**` or the bench script
12
+ # itself
13
+ # - manual `workflow_dispatch`
14
+
15
+ on:
16
+ schedule:
17
+ - cron: "0 6 * * *"
18
+ pull_request:
19
+ paths:
20
+ - "lib/ruact/server_functions/**"
21
+ - "lib/ruact/controller.rb"
22
+ - "vendor/javascript/ruact-server-functions-runtime/**"
23
+ - "bench/server_functions_dispatch_bench.rb"
24
+ - ".github/workflows/server-functions-bench.yml"
25
+ workflow_dispatch:
26
+
27
+ jobs:
28
+ bench:
29
+ name: Run dispatch bench
30
+ runs-on: ubuntu-latest
31
+ env:
32
+ BUNDLE_DEPLOYMENT: "false"
33
+ BUNDLE_FROZEN: "false"
34
+ steps:
35
+ - uses: actions/checkout@v5
36
+ - uses: ruby/setup-ruby@v1
37
+ with:
38
+ ruby-version: "3.3"
39
+ bundler-cache: true
40
+ - name: Run bench
41
+ id: bench
42
+ run: |
43
+ bundle exec ruby bench/server_functions_dispatch_bench.rb \
44
+ | tee bench-output.txt
45
+ - name: Post numbers as workflow summary
46
+ if: always()
47
+ run: |
48
+ {
49
+ echo "## Server-functions dispatch bench"
50
+ echo ""
51
+ echo '```'
52
+ cat bench-output.txt
53
+ echo '```'
54
+ } >> "$GITHUB_STEP_SUMMARY"
data/.rubocop.yml CHANGED
@@ -1,3 +1,13 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ # Merge (union) Exclude arrays across .rubocop_todo.yml and this file —
4
+ # without this, a cop that declares its own Exclude below (e.g.
5
+ # Metrics/ModuleLength) would silently DISCARD the todo file's frozen
6
+ # exclusions for that cop (RuboCop's per-parameter override semantics).
7
+ inherit_mode:
8
+ merge:
9
+ - Exclude
10
+
1
11
  require:
2
12
  - rubocop/cop/ruact
3
13
 
@@ -31,6 +41,14 @@ Metrics/BlockLength:
31
41
  Naming/VariableNumber:
32
42
  Exclude:
33
43
  - "lib/tasks/**/*.rake"
44
+ # Spec story-tag convention: `describe ..., :story_7_7` mirrors the story
45
+ # ID (7.7 → :story_7_7) so a single rspec invocation runs the regression
46
+ # suite for that story (project-context §11). Narrowed to the exact symbol
47
+ # shape rather than excluding all of spec/ — anything else under spec/ that
48
+ # tries to use snake_case-with-numbers still trips the cop. The optional
49
+ # trailing letter accommodates carve-out stories like 8.0a → :story_8_0a.
50
+ AllowedPatterns:
51
+ - '\bstory_\d+_\d+[a-z]?\b'
34
52
 
35
53
  Metrics/ClassLength:
36
54
  Max: 150
@@ -42,7 +60,7 @@ Metrics/ModuleLength:
42
60
 
43
61
  Metrics/AbcSize:
44
62
  Max: 50
45
- # Note: PoC methods (rsc_render, each, convert_element) will be refactored
63
+ # Note: PoC methods (ruact_render, each, convert_element) will be refactored
46
64
  # in later stories. Current values reflect tech debt, not design intent.
47
65
 
48
66
  Metrics/CyclomaticComplexity:
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,175 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 100`
3
+ # on 2026-06-06 01:48:31 UTC using RuboCop version 1.85.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ # This cop supports safe autocorrection (--autocorrect).
11
+ # Configuration parameters: AllowAliasSyntax, AllowedMethods.
12
+ # AllowedMethods: alias_method, public, protected, private
13
+ Layout/EmptyLinesAroundAttributeAccessor:
14
+ Exclude:
15
+ - 'spec/ruact/server_functions/error_payload_spec.rb'
16
+
17
+ # Offense count: 14
18
+ # This cop supports safe autocorrection (--autocorrect).
19
+ # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
20
+ # URISchemes: http, https
21
+ Layout/LineLength:
22
+ Exclude:
23
+ - 'bench/server_functions_dispatch_bench.rb'
24
+ - 'lib/ruact/errors.rb'
25
+ - 'lib/ruact/server_functions/error_suggestion.rb'
26
+ - 'spec/ruact/server_functions/endpoint_controller_rescue_spec.rb'
27
+ - 'spec/ruact/server_functions/error_suggestion_spec.rb'
28
+ - 'spec/ruact/server_functions/railtie_integration_spec.rb'
29
+ - 'spec/ruact/server_functions/snapshot_spec.rb'
30
+
31
+ # Offense count: 1
32
+ # Configuration parameters: AllowedMethods.
33
+ # AllowedMethods: enums
34
+ Lint/ConstantDefinitionInBlock:
35
+ Exclude:
36
+ - 'spec/ruact/server_functions/csrf_request_spec.rb'
37
+
38
+ # Offense count: 1
39
+ # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
40
+ Metrics/AbcSize:
41
+ Exclude:
42
+ - 'lib/ruact/controller.rb'
43
+
44
+ # Offense count: 1
45
+ # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
46
+ # AllowedMethods: refine
47
+ Metrics/BlockLength:
48
+ Exclude:
49
+ - 'lib/ruact/controller.rb'
50
+
51
+ # Offense count: 1
52
+ # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
53
+ Metrics/CyclomaticComplexity:
54
+ Exclude:
55
+ - 'lib/ruact/controller.rb'
56
+
57
+ # Offense count: 2
58
+ # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
59
+ Metrics/MethodLength:
60
+ Exclude:
61
+ - 'lib/ruact/controller.rb'
62
+ - 'lib/ruact/server_action.rb'
63
+
64
+ # Offense count: 2
65
+ # Configuration parameters: CountComments, Max, CountAsOne.
66
+ Metrics/ModuleLength:
67
+ Exclude:
68
+ - 'spec/**/*'
69
+ - 'lib/ruact/controller.rb'
70
+ - 'lib/ruact/server_functions/codegen.rb'
71
+
72
+ # Offense count: 1
73
+ # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
74
+ Metrics/PerceivedComplexity:
75
+ Exclude:
76
+ - 'lib/ruact/controller.rb'
77
+
78
+ # Offense count: 1
79
+ RSpec/BeforeAfterAll:
80
+ Exclude:
81
+ - '**/spec/spec_helper.rb'
82
+ - '**/spec/rails_helper.rb'
83
+ - '**/spec/support/**/*.rb'
84
+ - 'spec/ruact/server_functions/dispatch_request_spec.rb'
85
+
86
+ # Offense count: 1
87
+ # Configuration parameters: Prefixes, AllowedPatterns.
88
+ # Prefixes: when, with, without
89
+ RSpec/ContextWording:
90
+ Exclude:
91
+ - 'spec/ruact/controller_spec.rb'
92
+
93
+ # Offense count: 2
94
+ RSpec/LeakyConstantDeclaration:
95
+ Exclude:
96
+ - 'spec/ruact/server_functions/csrf_request_spec.rb'
97
+
98
+ # Offense count: 1
99
+ # Configuration parameters: EnforcedStyle.
100
+ # SupportedStyles: have_received, receive
101
+ RSpec/MessageSpies:
102
+ Exclude:
103
+ - 'spec/ruact/server_functions/railtie_integration_spec.rb'
104
+
105
+ # Offense count: 5
106
+ # Configuration parameters: Max.
107
+ RSpec/MultipleExpectations:
108
+ Exclude:
109
+ - 'spec/ruact/controller_spec.rb'
110
+ - 'spec/ruact/server_functions/csrf_request_spec.rb'
111
+ - 'spec/ruact/server_functions/endpoint_controller_rescue_spec.rb'
112
+ - 'spec/ruact/server_functions/standalone_action_spec.rb'
113
+
114
+ # Offense count: 2
115
+ RSpec/RemoveConst:
116
+ Exclude:
117
+ - 'spec/ruact/server_functions/railtie_integration_spec.rb'
118
+
119
+ # Offense count: 1
120
+ # Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
121
+ # SupportedInflectors: default, active_support
122
+ RSpec/SpecFilePathFormat:
123
+ Exclude:
124
+ - '**/spec/routing/**/*'
125
+ - 'spec/ruact/server_functions/standalone_action_spec.rb'
126
+
127
+ # Offense count: 3
128
+ Ruact/NoSharedState:
129
+ Exclude:
130
+ - 'lib/ruact/controller.rb'
131
+ - 'lib/ruact/server_functions/endpoint_controller.rb'
132
+
133
+ # Offense count: 2
134
+ # This cop supports unsafe autocorrection (--autocorrect-all).
135
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
136
+ # AllowedMethods: ==, equal?, eql?
137
+ Style/ClassEqualityComparison:
138
+ Exclude:
139
+ - 'lib/ruact/server_functions/error_payload.rb'
140
+
141
+ # Offense count: 1
142
+ # This cop supports safe autocorrection (--autocorrect).
143
+ Style/IfUnlessModifier:
144
+ Exclude:
145
+ - 'bench/server_functions_dispatch_bench.rb'
146
+
147
+ # Offense count: 1
148
+ Style/MixinUsage:
149
+ Exclude:
150
+ - 'bench/server_functions_dispatch_bench.rb'
151
+
152
+ # Offense count: 7
153
+ Style/MultilineBlockChain:
154
+ Exclude:
155
+ - 'spec/ruact/server_functions/registry_spec.rb'
156
+ - 'spec/ruact/server_functions/standalone_action_spec.rb'
157
+
158
+ # Offense count: 5
159
+ # Configuration parameters: AllowedClasses.
160
+ Style/OneClassPerFile:
161
+ Exclude:
162
+ - 'bench/server_functions_dispatch_bench.rb'
163
+ - 'spec/ruact/server_functions/csrf_request_spec.rb'
164
+
165
+ # Offense count: 2
166
+ # This cop supports safe autocorrection (--autocorrect).
167
+ Style/RedundantFreeze:
168
+ Exclude:
169
+ - 'spec/ruact/server_functions/error_payload_spec.rb'
170
+
171
+ # Offense count: 1
172
+ # Configuration parameters: Max.
173
+ Style/SafeNavigationChainLength:
174
+ Exclude:
175
+ - 'lib/ruact/server_functions/standalone_dispatcher.rb'