Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
136 commits
Select commit Hold shift + click to select a range
c4f772e
Add additional question text fields to form parameters
rileyseaburg Sep 2, 2025
bece872
Refactor Gemfile: remove duplicate gems and clean up comments
rileyseaburg Sep 2, 2025
127d6dd
Refactor FormsController: replace individual question text parameters…
rileyseaburg Sep 2, 2025
d75246c
Add test script for dynamic question parameter generation
rileyseaburg Sep 2, 2025
ca46af3
Replace unprocessable_entity with unprocessable_content status codes
rileyseaburg Sep 2, 2025
22df7b4
Update Ruby version and dependencies in Gemfile and Gemfile.lock; adj…
rileyseaburg Sep 2, 2025
d8db7b1
Fix tzinfo-data gem platform specification for Windows compatibility
rileyseaburg Sep 2, 2025
1519ea8
Update action cable, mailbox, mailer, pack, text, view, job, model, r…
rileyseaburg Sep 3, 2025
f9e91dd
Update Ruby version in CircleCI configuration to 3.4.5
rileyseaburg Sep 3, 2025
f9f726b
Add .ruby-version file specifying Ruby version 3.4.5
rileyseaburg Sep 4, 2025
5d59088
Change response status from unprocessable_entity to unprocessable_con…
rileyseaburg Sep 4, 2025
d46f9be
Change response status from unprocessable_content to unprocessable_en…
rileyseaburg Sep 10, 2025
8ccedcf
Downgrade Ruby version from 3.4.5 to 3.2.8
rileyseaburg Sep 10, 2025
0869a38
Add Rust-based widget renderer for performance optimization
rileyseaburg Oct 29, 2025
e56a9ba
Downgrade Ruby version from 3.4.7 to 3.2.8 in Gemfile
rileyseaburg Oct 29, 2025
d70eba2
Update config.yml
rileyseaburg Oct 29, 2025
b0f1e10
Update Gemfile
rileyseaburg Oct 29, 2025
8914d6e
Update form.rb
rileyseaburg Oct 29, 2025
7294149
Update form_data.rs
rileyseaburg Oct 29, 2025
a9e2849
Upgrade Ruby version from 3.2.8 to 3.4.7 in .ruby-version and Gemfile
rileyseaburg Oct 30, 2025
344b7f5
feat: Add Rust widget renderer with 12.1x performance improvement
rileyseaburg Nov 4, 2025
b7e9d93
security: Fix CSRF vulnerability in benchmark controller
rileyseaburg Nov 4, 2025
c618186
Update process.yml
rileyseaburg Nov 4, 2025
1f894a8
feat: Add ERB direct render benchmark for accurate comparison
rileyseaburg Nov 4, 2025
ed9d065
Fix Rust syntax errors in template_renderer.rs
rileyseaburg Nov 4, 2025
ef922fd
Fix widget rendering test failures by providing request context
rileyseaburg Nov 5, 2025
51ba429
config: enable public file server in test environment
rileyseaburg Nov 5, 2025
28b23ed
Fix Rust widget renderer: Implement HTML body generation and JSON ser…
rileyseaburg Nov 20, 2025
689c266
Fix spec failure in forms_spec.rb and update error-fix.md
rileyseaburg Nov 20, 2025
06c3468
Fix Rust widget logo rendering and flaky admin form spec
rileyseaburg Nov 20, 2025
b70a34b
Merge pull request #1898 from GSA/feature/rust-widget-renderer
rileyseaburg Nov 20, 2025
f92d949
Add custom Rust buildpack, update manifests, and fix CSV export 500 e…
rileyseaburg Nov 20, 2025
e3a6ebe
Add gemspec and lib for widget_renderer gem
rileyseaburg Nov 20, 2025
bf136ac
Fix widget_renderer gem loading and dependencies
rileyseaburg Nov 20, 2025
2b6bf2c
Revert path change in widget_renderer.rb
rileyseaburg Nov 20, 2025
5a17716
Add robust path detection for widget_renderer library
rileyseaburg Nov 20, 2025
087321a
Add file existence check for widget_renderer library path
rileyseaburg Nov 20, 2025
87ab8a2
Fix Rutie path resolution for Cloud Foundry
rileyseaburg Nov 20, 2025
dc64737
Fix Rutie path resolution by appending dummy lib directory
rileyseaburg Nov 20, 2025
82c1c34
Add ldd debug to extconf.rb
rileyseaburg Nov 20, 2025
ea05dbe
Bump widget_renderer version to 0.1.1
rileyseaburg Nov 21, 2025
e233401
Update Gemfile.lock for widget_renderer 0.1.1
rileyseaburg Nov 21, 2025
077aaea
Add ldd debug to widget_renderer.rb
rileyseaburg Nov 21, 2025
711f520
Add file details debug to widget_renderer.rb
rileyseaburg Nov 21, 2025
0002ceb
Add target directories to .cfignore
rileyseaburg Nov 21, 2025
7e0f3ac
Explicitly build Rust extension in supply script
rileyseaburg Nov 21, 2025
1a7c15c
Add debug logging to supply script
rileyseaburg Nov 21, 2025
183aca1
Update Ruby version and buildpack config
rileyseaburg Nov 21, 2025
926d00f
Add more debug logging to widget_renderer.rb
rileyseaburg Nov 21, 2025
4e633b9
Add debug logging to extconf.rb
rileyseaburg Nov 21, 2025
c63db54
Update manifest and widget renderer initializer
rileyseaburg Nov 21, 2025
9195b1f
Fix manifest indentation
rileyseaburg Nov 21, 2025
ec30c15
Fix rust buildpack PATH export
rileyseaburg Nov 21, 2025
a23288b
Ensure PATH includes cargo for downstream buildpacks
rileyseaburg Nov 21, 2025
b4839c0
Use CARGO_HOME for Rust build and avoid PATH override
rileyseaburg Nov 21, 2025
b4e34e9
Seed PATH with cargo and node for subsequent buildpacks
rileyseaburg Nov 21, 2025
6f24ae5
Only prepend cargo bin without overriding PATH
rileyseaburg Nov 21, 2025
572f97e
Stop exporting PATH from rust buildpack supply
rileyseaburg Nov 21, 2025
d06de70
Install Node via nvm in rust buildpack supply
rileyseaburg Nov 21, 2025
af4c7f4
Use rust + ruby buildpacks only for staging
rileyseaburg Nov 21, 2025
555f654
Remove nvm install and keep PATH untouched in rust supply
rileyseaburg Nov 21, 2025
55b17d7
Run ruby buildpack before rust buildpack in staging
rileyseaburg Nov 21, 2025
d624fbb
Prepend cargo to PATH for ruby buildpack and run rust first
rileyseaburg Nov 21, 2025
8040a69
Remove cargo PATH export in rust supply
rileyseaburg Nov 21, 2025
5ecfac5
Install rust via extconf and use ruby buildpack only
rileyseaburg Nov 21, 2025
618fca9
Copy built Rust lib into extension root
rileyseaburg Nov 21, 2025
c482ff8
Attempt runtime build of widget renderer if missing
rileyseaburg Nov 21, 2025
80836cc
Build widget_renderer at runtime via profile.d hook
rileyseaburg Nov 21, 2025
1bef659
Copy widget_renderer .so into target/release and root
rileyseaburg Nov 21, 2025
209cd21
Move runtime build hook to .profile.d and keep lib copies
rileyseaburg Nov 21, 2025
e1ef470
Add GitHub Action to build Linux widget .so artifact
rileyseaburg Nov 21, 2025
cd7dc87
Run widget build action on develop, staging, production releases
rileyseaburg Nov 21, 2025
4009bf8
Enable manual dispatch for widget build workflow
rileyseaburg Nov 21, 2025
37b8b0e
Restore rust buildpack and prepend cargo for bundler
rileyseaburg Nov 21, 2025
d623261
Fix duplicate workflow_dispatch in widget build workflow
rileyseaburg Nov 21, 2025
83ba4e8
Use native rustup in widget workflow (no third-party action)
rileyseaburg Nov 21, 2025
cdadddb
Use actions-rs toolchain to install Rust in widget workflow
rileyseaburg Nov 21, 2025
b100b09
Fix artifact path format in widget workflow
rileyseaburg Nov 21, 2025
539f661
Tighten ignore for env/vars files
rileyseaburg Nov 21, 2025
bb3cd67
Look for widget artifact in workspace target dir
rileyseaburg Nov 21, 2025
8af0c02
Fix widget build script app root detection
rileyseaburg Nov 21, 2025
2db8e84
Make widget build script locate ext directory flexibly
rileyseaburg Nov 21, 2025
51bbc6a
Link rutie against shared ruby in runtime build
rileyseaburg Nov 21, 2025
36b2721
Avoid rutie static link; skip linking when building at runtime
rileyseaburg Nov 21, 2025
a80e494
Prefer cached Rust toolchain in runtime build
rileyseaburg Nov 21, 2025
2eaaf05
Ensure widget .so is included in CF bits
rileyseaburg Nov 21, 2025
6143f92
Copy shipped widget .so into target/release at runtime
rileyseaburg Nov 21, 2025
fa7dbbe
Let Rutie define WidgetRenderer (drop Ruby module wrapper)
rileyseaburg Nov 22, 2025
d598548
Load widget renderer from lib/widget_renderer
rileyseaburg Nov 22, 2025
8d0bbd1
Remove stale WidgetRenderer module before Rutie init
rileyseaburg Nov 22, 2025
936da01
Guard WidgetRenderer constant as class before Rutie init
rileyseaburg Nov 22, 2025
a23cc09
Allow app-staging host in staging manifest
rileyseaburg Nov 22, 2025
1fa69f2
Use app-staging as asset host to satisfy SRI
rileyseaburg Nov 22, 2025
c1ecb52
Scope session cookie domain via optional env
rileyseaburg Nov 22, 2025
d98161b
Point LOGIN_GOV_REDIRECT_URI to app-staging host
rileyseaburg Nov 22, 2025
9c96767
Pass question text field to Rust renderer
rileyseaburg Nov 22, 2025
faf7e69
Clean up cx collections export tests and fix service csv list
rileyseaburg Nov 22, 2025
b420feb
Merge branch 'feature/rust-widget-renderer' into develop
rileyseaburg Nov 22, 2025
4580b73
Bump Ruby to 3.4.7
rileyseaburg Nov 24, 2025
6cc6804
Build Rust widget renderer in CircleCI
rileyseaburg Nov 24, 2025
4b566d9
Use cargo build for widget renderer in CI
rileyseaburg Nov 24, 2025
8ff23e8
Coerce nil booleans before calling Rust renderer
rileyseaburg Nov 24, 2025
2dfa28e
Skip Rust widget renderer in test env to stabilize specs
rileyseaburg Nov 24, 2025
0c32cce
Default element_selector for widget renderer
rileyseaburg Nov 24, 2025
609891d
Stabilize form permissions spec expectations
rileyseaburg Nov 24, 2025
c6fe6d2
Revert Ruby target to 3.2.8 for CF buildpack
rileyseaburg Nov 24, 2025
1b0c575
Use prebuilt widget renderer .so before compiling at runtime
rileyseaburg Nov 24, 2025
b92b49b
Fix widget renderer build artifact detection
rileyseaburg Nov 24, 2025
0081849
Make widget renderer tolerate null booleans
rileyseaburg Nov 24, 2025
d48eeb4
Add logging and extra fallback for widget renderer .so in pre-start
rileyseaburg Nov 24, 2025
cf2a312
Allow widget renderer pre-start to build if no prebuilt lib
rileyseaburg Nov 24, 2025
0395cf6
Set HOME to /home/vcap before building widget renderer
rileyseaburg Nov 24, 2025
fc0ebbe
Harden widget renderer runtime build paths
rileyseaburg Nov 24, 2025
42d9cc4
Trigger CircleCI deploy
rileyseaburg Nov 24, 2025
21c7ccf
Guard CF deploy steps to a single parallel node
rileyseaburg Nov 24, 2025
16004a5
Precompile done.svg so the landing page renders
rileyseaburg Nov 24, 2025
1bea5e4
Link done.svg in manifest and clean precompile entry
rileyseaburg Nov 24, 2025
ee4a044
Allow asset fallback in staging
rileyseaburg Nov 24, 2025
4203ce3
Enable Rack::Attack middleware
rileyseaburg Nov 24, 2025
12b0b2f
Stabilize digital product create feature spec
rileyseaburg Nov 24, 2025
2e448c4
Loosen digital product path assertion
rileyseaburg Nov 24, 2025
82b503d
Avoid runtime Rust build in widget renderer
rileyseaburg Nov 24, 2025
2c78f55
Increase memory allocation to 2G for Rust widget renderer
rileyseaburg Nov 25, 2025
273f1d0
Fix SRI CORS issue by adding crossorigin attribute to asset tags
rileyseaburg Nov 25, 2025
54b8531
Add CORS headers for static assets to support SRI cross-origin requests
rileyseaburg Nov 25, 2025
2af401e
Add prebuilt Linux libwidget_renderer.so for Cloud Foundry deployment
rileyseaburg Nov 25, 2025
051deb6
Add debug logging for WidgetRenderer initialization
rileyseaburg Nov 25, 2025
73380ed
Add debugging output to widget_renderer.rb
rileyseaburg Dec 1, 2025
0480a2b
Fix: Copy library to expected location when found in workspace target…
rileyseaburg Dec 1, 2025
d3e0271
Fix: Set LD_LIBRARY_PATH for libruby.so at runtime on Cloud Foundry
rileyseaburg Dec 1, 2025
6a9cac1
Fix flaky test: explicitly set service to twitter for digital_service…
rileyseaburg Dec 1, 2025
3596c94
Add retry logic to CF deploy scripts to handle staging race conditions
rileyseaburg Dec 1, 2025
e7b114a
Add deployment wait logic to prevent CF supersession errors
rileyseaburg Dec 1, 2025
3475fe0
Add CF env-based deploy lock to serialize concurrent pipelines
rileyseaburg Dec 1, 2025
14288a9
Build Rust extension at runtime on CF to fix libruby.so linking
rileyseaburg Dec 1, 2025
e304dff
Check library linkage before using - rebuild if libruby not found
rileyseaburg Dec 1, 2025
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
8 changes: 8 additions & 0 deletions .cfignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,11 @@
/public/packs
/public/packs-test
/node_modules

# Ignore Rust build artifacts, but keep the prebuilt widget library
target/
ext/widget_renderer/target/
!ext/widget_renderer/target/
!ext/widget_renderer/target/release/
!ext/widget_renderer/target/release/libwidget_renderer.so
!ext/widget_renderer/libwidget_renderer.so
64 changes: 55 additions & 9 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
#
version: 2.1
orbs:
browser-tools: circleci/browser-tools@1.5.0

# Cancel redundant builds when new commits are pushed
# This prevents multiple pipelines from racing to deploy
# Note: This must also be enabled in CircleCI project settings
# Settings > Advanced > Auto-cancel redundant builds

jobs:
build:
docker:
- image: cimg/ruby:3.3.4-browsers
- image: cimg/ruby:3.2.8-browsers # Matches deployed Ruby version in CF
environment:
RAILS_ENV: test
PGHOST: 127.0.0.1
Expand All @@ -27,19 +30,37 @@ jobs:
POSTGRES_USER: root
POSTGRES_DB: touchpoints_test

parallelism: 1
parallelism: 4
working_directory: ~/repo

steps:
- run:
name: Update packages
command: sudo apt-get update

- browser-tools/install-chrome: # required for selenium used by tachometer benchmark smoke tests
chrome-version: 116.0.5845.96
- run:
name: Ensure Chrome is available
command: |
# cimg/ruby:*-browsers images already include Chrome; skip orb command to avoid "Cannot find declaration" errors
echo "Using cimg/ruby:3.4.7-browsers which includes Chrome"

- checkout

- run:
name: Install Rust toolchain
command: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
echo 'source $HOME/.cargo/env' >> $BASH_ENV
source $HOME/.cargo/env
rustc --version
cargo --version

- run:
name: Build widget renderer (Rust)
command: |
source $HOME/.cargo/env
cargo build --release --manifest-path ext/widget_renderer/Cargo.toml

# Download and cache dependencies
- restore_cache:
keys:
Expand Down Expand Up @@ -90,11 +111,37 @@ jobs:

- run:
name: Deploy Sidekiq worker servers
command: ./.circleci/deploy-sidekiq.sh
command: |
# Only deploy from a single parallel node to avoid concurrent CF pushes.
if [ "${CIRCLE_NODE_INDEX:-0}" != "0" ]; then
echo "Skipping Sidekiq deploy on parallel node ${CIRCLE_NODE_INDEX}"
exit 0
fi
# Remove prebuilt Rust library - it must be built on CF with the correct Ruby paths
# The library built on CircleCI links against /usr/local/lib/libruby.so.3.2
# but on CF, Ruby is in /home/vcap/deps/*/ruby/lib/
echo "Removing prebuilt Rust library (will be rebuilt on CF)..."
rm -rf ext/widget_renderer/target/release/libwidget_renderer.so 2>/dev/null || true
rm -f ext/widget_renderer/libwidget_renderer.so 2>/dev/null || true
./.circleci/deploy-sidekiq.sh
no_output_timeout: 30m

- run:
name: Deploy web server(s)
command: ./.circleci/deploy.sh
command: |
# Only deploy from a single parallel node to avoid concurrent CF pushes.
if [ "${CIRCLE_NODE_INDEX:-0}" != "0" ]; then
echo "Skipping web deploy on parallel node ${CIRCLE_NODE_INDEX}"
exit 0
fi
# Wait for Sidekiq deployment to complete before starting web deploy
sleep 120
# Remove prebuilt Rust library - it must be built on CF with the correct Ruby paths
echo "Removing prebuilt Rust library (will be rebuilt on CF)..."
rm -rf ext/widget_renderer/target/release/libwidget_renderer.so 2>/dev/null || true
rm -f ext/widget_renderer/libwidget_renderer.so 2>/dev/null || true
./.circleci/deploy.sh
no_output_timeout: 30m

cron_tasks:
docker:
Expand All @@ -112,7 +159,6 @@ jobs:
command: ./.circleci/cron.sh

workflows:
version: 2
daily_workflow:
triggers:
- schedule:
Expand Down
128 changes: 125 additions & 3 deletions .circleci/deploy-sidekiq.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,135 @@
# a non-zero exit code
set -e

# Acquire a deployment lock using CF environment variable
# This prevents multiple pipelines from deploying simultaneously
acquire_deploy_lock() {
local app_name="$1"
local lock_name="DEPLOY_LOCK"
local lock_value="${CIRCLE_BUILD_NUM:-$$}_$(date +%s)"
local max_wait=600 # 10 minutes max
local wait_interval=30
local waited=0

echo "Attempting to acquire deploy lock for $app_name..."

while [ $waited -lt $max_wait ]; do
# Check if there's an existing lock
local current_lock=$(cf env "$app_name" 2>/dev/null | grep "$lock_name:" | awk '{print $2}' || echo "")

if [ -z "$current_lock" ] || [ "$current_lock" == "null" ]; then
# No lock exists, try to acquire it
echo "Setting deploy lock: $lock_value"
cf set-env "$app_name" "$lock_name" "$lock_value" > /dev/null 2>&1 || true
sleep 5 # Small delay to handle race conditions

# Verify we got the lock
current_lock=$(cf env "$app_name" 2>/dev/null | grep "$lock_name:" | awk '{print $2}' || echo "")
if [ "$current_lock" == "$lock_value" ]; then
echo "Deploy lock acquired: $lock_value"
return 0
fi
fi

# Check if lock is stale (older than 15 minutes)
local lock_time=$(echo "$current_lock" | cut -d'_' -f2)
local now=$(date +%s)
if [ -n "$lock_time" ] && [ $((now - lock_time)) -gt 900 ]; then
echo "Stale lock detected (age: $((now - lock_time))s), clearing..."
cf unset-env "$app_name" "$lock_name" > /dev/null 2>&1 || true
continue
fi

echo "Deploy lock held by another process ($current_lock), waiting ${wait_interval}s... (waited ${waited}s)"
sleep $wait_interval
waited=$((waited + wait_interval))
done

echo "Warning: Could not acquire lock after ${max_wait}s, proceeding anyway..."
return 0
}

# Release the deployment lock
release_deploy_lock() {
local app_name="$1"
local lock_name="DEPLOY_LOCK"
echo "Releasing deploy lock for $app_name..."
cf unset-env "$app_name" "$lock_name" > /dev/null 2>&1 || true
}

# Wait for any in-progress deployments to complete before starting
wait_for_deployment() {
local app_name="$1"
local max_wait=600 # 10 minutes max
local wait_interval=15
local waited=0

echo "Checking for in-progress deployments of $app_name..."

while [ $waited -lt $max_wait ]; do
# Get deployment status - look for ACTIVE deployments
local status=$(cf curl "/v3/deployments?app_guids=$(cf app "$app_name" --guid)&status_values=ACTIVE" 2>/dev/null | grep -o '"state":"[^"]*"' | head -1 || echo "")

if [ -z "$status" ] || [[ "$status" == *'"state":"FINALIZED"'* ]] || [[ "$status" == *'"state":"DEPLOYED"'* ]]; then
echo "No active deployment in progress, proceeding..."
return 0
fi

echo "Deployment in progress ($status), waiting ${wait_interval}s... (waited ${waited}s of ${max_wait}s)"
sleep $wait_interval
waited=$((waited + wait_interval))
done

echo "Warning: Timed out waiting for previous deployment, proceeding anyway..."
return 0
}

# Retry function to handle staging and deployment conflicts
cf_push_with_retry() {
local app_name="$1"
local max_retries=5
local retry_delay=90

# Acquire lock first
acquire_deploy_lock "$app_name"

# Ensure lock is released on exit
trap "release_deploy_lock '$app_name'" EXIT

# Wait for any in-progress deployment
wait_for_deployment "$app_name"

for i in $(seq 1 $max_retries); do
echo "Attempt $i of $max_retries to push $app_name..."
if cf push "$app_name" --strategy rolling; then
echo "Successfully pushed $app_name"
release_deploy_lock "$app_name"
trap - EXIT # Clear the trap
return 0
else
local exit_code=$?
if [ $i -lt $max_retries ]; then
echo "Push failed (exit code: $exit_code), waiting ${retry_delay}s before retry..."
sleep $retry_delay
# Re-check for in-progress deployments before retrying
wait_for_deployment "$app_name"
fi
fi
done

release_deploy_lock "$app_name"
trap - EXIT # Clear the trap
echo "Failed to push $app_name after $max_retries attempts"
return 1
}

if [ "${CIRCLE_BRANCH}" == "production" ]
then
echo "Logging into cloud.gov"
# Log into CF and push
cf login -a $CF_API_ENDPOINT -u $CF_PRODUCTION_SPACE_DEPLOYER_USERNAME -p $CF_PRODUCTION_SPACE_DEPLOYER_PASSWORD -o $CF_ORG -s prod
echo "PUSHING to PRODUCTION..."
cf push touchpoints-production-sidekiq-worker --strategy rolling
cf_push_with_retry touchpoints-production-sidekiq-worker
echo "Push to Production Complete."
else
echo "Not on the production branch."
Expand All @@ -22,7 +144,7 @@ then
# Log into CF and push
cf login -a $CF_API_ENDPOINT -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE
echo "Pushing to Demo..."
cf push touchpoints-demo-sidekiq-worker --strategy rolling
cf_push_with_retry touchpoints-demo-sidekiq-worker
echo "Push to Demo Complete."
else
echo "Not on the main branch."
Expand All @@ -34,7 +156,7 @@ then
# Log into CF and push
cf login -a $CF_API_ENDPOINT -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE
echo "Pushing to Staging..."
cf push touchpoints-staging-sidekiq-worker --strategy rolling
cf_push_with_retry touchpoints-staging-sidekiq-worker
echo "Push to Staging Complete."
else
echo "Not on the develop branch."
Expand Down
Loading
Loading