Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5b81dc2
Add Control Plane flow to migration example
justin808 Apr 15, 2026
9238039
Sync Control Plane flow hardening
justin808 Apr 15, 2026
ea290be
Preserve frontend precompile hooks in Dockerfile
justin808 Apr 15, 2026
1e769a0
Tighten Docker build action arguments
justin808 Apr 15, 2026
e8bc403
docs: clarify review app security
justin808 Jun 2, 2026
6783fcf
Merge remote-tracking branch 'origin/main' into add-cpflow-github-flow
justin808 Sep 17, 2026
2ceb6b9
Update cpflow deployment flow to v5.3.0
justin808 Sep 17, 2026
55270b1
Harden generated deployment defaults
justin808 Sep 17, 2026
56c8922
Fix production promotion and rendering
justin808 Sep 17, 2026
3cf019b
Document review app secret bootstrap
justin808 Sep 17, 2026
87c86aa
Keep Navbar SSR deterministic
justin808 Sep 17, 2026
6c3f884
Keep SQLite data separate from migrations
justin808 Sep 17, 2026
f4bd700
Address deployment flow review feedback
justin808 Sep 19, 2026
037b0f0
Merge remote-tracking branch 'origin/main' into add-cpflow-github-flow
justin808 Oct 15, 2026
de57965
Cover migrated routes with SSR regression tests
justin808 Oct 15, 2026
0368ee1
Keep preview deployment focused and load hydration bundles
justin808 Oct 15, 2026
80503ee
Trim trailing blank lines in deployment configuration
justin808 Oct 15, 2026
8a9a5dc
Meet preview volume limits and record the deployed revision
justin808 Oct 15, 2026
570364e
Use a stateful SQLite workload and tighten deployment guidance
justin808 Oct 15, 2026
2f96b84
Prepare SQLite in the stateful entrypoint instead of a cron runner
justin808 Oct 15, 2026
8b5bc03
Keep CI deployment outside secret-policy administration
justin808 Oct 17, 2026
0b887a3
Align CI help with maintainer-owned secret bootstrap
justin808 Oct 17, 2026
d6e8ea2
Pin Ruby for the deployment runner
justin808 Oct 17, 2026
a73baa3
Merge remote-tracking branch 'origin/main' into add-cpflow-github-flow
justin808 Oct 18, 2026
b77c2f9
Use temporary verification and upgrade React on Rails to 17.2.0
justin808 Oct 18, 2026
0f8ff18
Handle malformed Unicode in the server encoder shim
justin808 Oct 18, 2026
2e927f9
Apply server environment overrides before database preparation
justin808 Oct 18, 2026
8467bc5
Limit temporary database preparation to direct Rails commands
justin808 Oct 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/bin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ means that capability is n/a here.
| `validate` | Pre-push gate | JavaScript and Tailwind CSS builds, production Shakapacker compilation, then `bin/rails test` |
| `test` | Run tests | `bin/rails test` |
| `lint` | Lint / format | n/a |
| `build` | Build / type-check | `bin/yarn build`, `bin/yarn build:css`, then `RAILS_ENV=production bin/shakapacker` |
| `build` | Build / type-check | JavaScript and Tailwind builds, React on Rails pack generation, then production Shakapacker compilation |
| `docs` | Validate documentation | n/a |
| `ci-detect` | Detect CI impact | n/a |

Expand Down
3 changes: 2 additions & 1 deletion .agents/bin/build
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
set -euo pipefail
cd "$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)"
bin/yarn build
bin/yarn build:css
bin/rails tailwindcss:build
bin/shakapacker-precompile-hook
exec env RAILS_ENV=production bin/shakapacker "$@"
19 changes: 19 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.git
.github
.agents
.bundle
.env
.env.*
config/master.key
config/credentials/*.key
db/*.sqlite3
log/*
storage/*
tmp/*
node_modules
vendor/bundle
app/assets/builds/*
public/assets
public/packs
public/packs-test
coverage
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.4.6
97 changes: 97 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
ARG RUBY_VERSION=3.4.6

# Node and Ruby base images share the same Debian release so that glibc, libssl, and
# other system libraries line up between stages.
FROM docker.io/library/node:22-bookworm-slim AS node
FROM ruby:$RUBY_VERSION-slim-bookworm

# Yarn Classic / pnpm fallbacks for projects that haven't adopted corepack via
# `packageManager` in package.json. Override at build time (`--build-arg=YARN_CLASSIC_VERSION=...`)
# rather than editing this file so regenerating the Dockerfile keeps your pin.
ARG YARN_CLASSIC_VERSION=1.22.22
ARG PNPM_FALLBACK_VERSION=9.12.3

WORKDIR /app

# Keep Node.js available both for asset compilation and for SSR runtimes that
# rely on ExecJS in production. Narrowed to just what the node stage actually
# ships under /usr/local so we don't drag in unused Debian libs from that image.
COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
COPY --from=node /usr/local/include/node /usr/local/include/node

RUN ln -sf ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \
ln -sf ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx && \
ln -sf ../lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack && \
chmod +x /usr/local/lib/node_modules/npm/bin/npm-cli.js \
/usr/local/lib/node_modules/npm/bin/npx-cli.js \
/usr/local/lib/node_modules/corepack/dist/corepack.js && \
node --version && npm --version && corepack --version

# Expose Corepack-managed shims so later build steps can call yarn/pnpm
# directly during asset precompilation hooks.
RUN printf '%s\n' '#!/bin/sh' 'exec corepack yarn "$@"' > /usr/bin/yarn && \
chmod +x /usr/bin/yarn && \
printf '%s\n' '#!/bin/sh' 'exec corepack pnpm "$@"' > /usr/bin/pnpm && \
chmod +x /usr/bin/pnpm

# install ruby gems
COPY Gemfile* ./

RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential && \
bundle config set without 'development test' && \
bundle config set with 'production' && \
bundle install --jobs=3 --retry=3 && \
apt-get purge -y --auto-remove build-essential && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

COPY . ./

# Install JavaScript dependencies only when the project actually has them.
# Pin `packageManager` in package.json to take the corepack path and avoid the
# YARN_CLASSIC_VERSION / PNPM_FALLBACK_VERSION fallbacks below; the fallbacks exist for
# projects that haven't adopted corepack and should be reviewed periodically as they go stale.
RUN if [ -f package.json ]; then \
package_manager="$(node -p "require('./package.json').packageManager || ''")"; \
if [ -f yarn.lock ]; then \
if printf '%s' "$package_manager" | grep -q '^yarn@'; then \
corepack prepare "$package_manager" --activate && \
(corepack yarn install --immutable || corepack yarn install --frozen-lockfile); \
else \
npm install -g "yarn@${YARN_CLASSIC_VERSION}" && \
(yarn install --immutable || yarn install --frozen-lockfile); \
fi; \
elif [ -f pnpm-lock.yaml ]; then \
if printf '%s' "$package_manager" | grep -q '^pnpm@'; then \
corepack prepare "$package_manager" --activate && \
corepack pnpm install --frozen-lockfile; \
else \
corepack prepare "pnpm@${PNPM_FALLBACK_VERSION}" --activate && \
corepack pnpm install --frozen-lockfile; \
fi; \
elif [ -f package-lock.json ]; then \
npm ci; \
else \
npm install; \
fi; \
fi

ENV RAILS_ENV=production
ENV RAILS_SERVE_STATIC_FILES=true

# Compiling assets requires any non-blank value, but the placeholder must not
# remain in the final image environment.
RUN SECRET_KEY_BASE=NOT_USED_NON_BLANK bin/shakapacker-precompile-hook


RUN SECRET_KEY_BASE=NOT_USED_NON_BLANK rails assets:precompile

# add entrypoint
COPY bin/docker-entrypoint /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
ARG GIT_COMMIT=unknown
ENV GIT_COMMIT=$GIT_COMMIT
ENTRYPOINT ["/app/entrypoint.sh"]

CMD ["rails", "s"]
Comment thread
justin808 marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ gem "mutex_m"

# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
gem "sprockets-rails"
# Sprockets 4.2 still passes JSON options removed in JSON 3.
gem "json", "< 3"

# Use sqlite3 as the database for Active Record
gem "sqlite3", "~> 2.9"
Expand All @@ -27,7 +29,7 @@ gem "jbuilder"
gem "mini_racer", "~> 0.22.1"

# The library which provides react helpers and more in rails
gem "react_on_rails", "17.2.0.rc.0"
gem "react_on_rails", "17.2.0"

# Modern javscript compressor
gem "terser"
Expand Down
7 changes: 4 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ GEM
sorbet-runtime
jsbundling-rails (1.3.1)
railties (>= 6.0.0)
json (3.0.2)
json (2.21.2)
libv8-node (24.12.0.1-arm64-darwin)
libv8-node (24.12.0.1-x86_64-linux)
logger (1.7.0)
Expand Down Expand Up @@ -226,7 +226,7 @@ GEM
prism (>= 1.6.0)
rbs (>= 4.0.0)
tsort
react_on_rails (17.2.0.rc.0)
react_on_rails (17.2.0)
addressable
connection_pool
execjs (~> 2.5)
Expand Down Expand Up @@ -309,13 +309,14 @@ DEPENDENCIES
jbuilder
js-routes
jsbundling-rails (~> 1.3)
json (< 3)
mini_racer (~> 0.22.1)
minitest (< 7)
mutex_m
pry-remote
puma (~> 8.0)
rails (~> 8.1.4)
react_on_rails (= 17.2.0.rc.0)
react_on_rails (= 17.2.0)
selenium-webdriver
shakapacker (= 10.3.2)
sprockets-rails
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,18 @@ This repository is a migrated version of [ganchdev/react-rails-example](https://
## Notes
- This app is intentionally minimal and focused on migration outcomes.
- Generated React on Rails files are kept in-repo so users can inspect exact changes after migration.

## Temporary Verification

Run `bin/verify-container` to build the production image and check server-rendered
HTML on `/`, `/posts/0`, and `/hello_world`. Docker must be running. The command
uses a random signing key, an ephemeral SQLite database, and a random localhost
port. It removes its container and image tag on success, failure, or interruption.
No cloud app, persistent volume, registry upload, or permanent endpoint is created.

The image prepares SQLite automatically only for direct `rails server` / `rails s`
commands, including `bin/rails`. Pass runtime overrides with Docker's `--env`
flags. Custom wrapped commands must prepare their database explicitly.

For interactive browser checks, use `bin/dev` and stop it afterward. This repository
has no deployment workflows, automatic deployments, or permanent staging app.
3 changes: 3 additions & 0 deletions app/javascript/src/Navbar/ror_components/Navbar.client.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Navbar from '../../components/Navbar';

export default Navbar;
3 changes: 3 additions & 0 deletions app/javascript/src/Navbar/ror_components/Navbar.server.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Navbar from './Navbar.client';

export default Navbar;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import PostsIndex from '../../pages/PostsIndex';

export default PostsIndex;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import PostsIndex from './PostsIndex.client';

export default PostsIndex;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import PostsShow from '../../pages/PostsShow';

export default PostsShow;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import PostsShow from './PostsShow.client';

export default PostsShow;
10 changes: 6 additions & 4 deletions app/javascript/src/components/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import Card from "./Card";
/**
* @param {String} menus
* @param {String} homeUrl
* @param {Number} avatarSeed
* @return {JSX.Element}
*/
function Navbar({ menus, homeUrl }) {
function Navbar({ menus, homeUrl, avatarSeed = 0 }) {
return (
<div className="navbar bg-base-100">
<div className="flex-1">
Expand All @@ -23,7 +24,7 @@ function Navbar({ menus, homeUrl }) {
<div className="dropdown dropdown-end">
<label tabIndex={0} className="btn btn-ghost btn-circle avatar">
<div className="w-10 rounded-full">
<Jazzicon diameter={40} seed={Math.round(Math.random() * 10000000)} />
<Jazzicon diameter={40} seed={avatarSeed} />
</div>
</label>
<ul tabIndex={0} className="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
Expand All @@ -42,9 +43,10 @@ function Navbar({ menus, homeUrl }) {
);
}

Card.propTypes = {
Navbar.propTypes = {
menus: PropTypes.string,
homeUrl: PropTypes.string,
}
avatarSeed: PropTypes.number,
};

export default Navbar;
4 changes: 2 additions & 2 deletions app/javascript/src/pages/PostsShow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import PropTypes from "prop-types";
function PostsShow({ post }) {
const parsedPost = JSON.parse(post);

return(
return (
<div className="prose">
<h1>{parsedPost.title}</h1>
<p>{parsedPost.content}</p>
Expand All @@ -18,6 +18,6 @@ function PostsShow({ post }) {

PostsShow.propTypes = {
post: PropTypes.string
}
};

export default PostsShow;
8 changes: 4 additions & 4 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
Comment thread
justin808 marked this conversation as resolved.
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
<%= javascript_pack_tag "application" %>
</head>

<body>
<%=
react_component(
"Navbar",
{
menus: JSON.generate([{name: "Posts", href: root_path}]),
props: {
menus: JSON.generate([{ name: "Posts", href: root_path }]),
homeUrl: root_path
},
prerender: true)
Expand All @@ -26,5 +25,6 @@
<div class="container pt-10 mx-auto">
<%= yield %>
</div>
<%= javascript_pack_tag %>
</body>
</html>
2 changes: 1 addition & 1 deletion app/views/posts/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<%=
react_component("PostsIndex", { posts: @posts }, prerender: true)
react_component("PostsIndex", props: { posts: @posts }, prerender: true)
%>
2 changes: 1 addition & 1 deletion app/views/posts/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<%=
react_component("PostsShow", {post: @post}, prerender: true)
react_component("PostsShow", props: { post: @post }, prerender: true)
%>
13 changes: 13 additions & 0 deletions bin/docker-entrypoint
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh
set -e

# Only direct Rails server commands prepare the temporary database automatically.
# Pass runtime overrides with docker run --env; wrapped commands prepare explicitly.
case "${1:-}:${2:-}" in
rails:s|rails:server|bin/rails:s|bin/rails:server|./bin/rails:s|./bin/rails:server)
echo " -- Preparing database"
./bin/rails db:prepare
;;
esac

exec "$@"
Loading
Loading