Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
b70a34b
Merge pull request #1898 from GSA/feature/rust-widget-renderer
rileyseaburg Nov 20, 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
0768a3b
Merge pull request #1888 from GSA/develop
rileyseaburg Dec 1, 2025
2f0c4ae
Add Cargo caching and library linkage verification to CircleCI
rileyseaburg Dec 1, 2025
decad73
Fix cx_collections export_csv 500 error
rileyseaburg Dec 10, 2025
446fc3c
Update schema.rb with new indexes for CircleCI
rileyseaburg Dec 10, 2025
2a919af
Update schema version to include new migration
rileyseaburg Dec 10, 2025
39a848c
Add tests for User#cx_collections method
rileyseaburg Dec 11, 2025
91a8a15
Production release: Fix CX Collections export CSV error (#1911)
rileyseaburg Dec 11, 2025
fea936d
fix: use git URL for rust buildpack in production manifest
rileyseaburg Dec 11, 2025
df21164
fix: correct redis service name in production manifest
rileyseaburg Dec 11, 2025
f376cab
Fix widget renderer load when native lib missing (#1913)
rileyseaburg Dec 15, 2025
1a85d02
Release: WidgetRenderer load fix (#1914)
rileyseaburg Dec 15, 2025
2b1fb3a
Fix User#cx_collections specs for Service owner (#1915)
rileyseaburg Dec 15, 2025
4a17475
Fix User#cx_collections specs for Service owner (#1915) (#1917)
rileyseaburg Dec 15, 2025
bbcb0ac
Fix production manifest for rust buildpack (#1918)
rileyseaburg Dec 18, 2025
b304af6
Release: set prod disk quota to 2G (#1919)
rileyseaburg Dec 18, 2025
0befb69
fix: remove empty secret keys from manifest to prevent wiping env var…
rileyseaburg Dec 18, 2025
9dd5ae4
fix: Use fba-usa-modal class for USWDS Modal compatibility
rileyseaburg Dec 18, 2025
c75835b
fix: Add CSS support to Rust widget renderer to fix modal positioning
rileyseaburg Dec 18, 2025
e37e530
fix: Use parent_id instead of parent in Organization factory
rileyseaburg Dec 18, 2025
d073960
Increase Cloud Foundry start timeout to 180s and fix Sidekiq health c…
rileyseaburg Dec 19, 2025
96a834b
Fix Sidekiq crash and optimize Rust build script
rileyseaburg Dec 19, 2025
17c5edb
build(cf): simplify widget_renderer build script and ignore rules
rileyseaburg Dec 19, 2025
4a038a7
Increase deployment timeout and add widget_renderer fallback to resol…
rileyseaburg Dec 19, 2025
20ba1df
fix(widget_renderer): restore LoadError and adjust app timeout
rileyseaburg Dec 19, 2025
00aae42
ci: increase deployment wait time and enable static files in dev
rileyseaburg Dec 19, 2025
bec0f54
ci(deploy): increase timeouts and enable static file serving
rileyseaburg Dec 19, 2025
c31ee4d
Decouple db:migrate from deploy: migrations must be run separately to…
rileyseaburg Dec 19, 2025
a5513bd
Add automated pre-deploy migrations via cf run-task to avoid 180s tim…
rileyseaburg Dec 19, 2025
63126ac
Set health check type to process for sidekiq worker before rolling de…
rileyseaburg Dec 19, 2025
7b1902e
Fix sidekiq worker timeout: explicitly set to 180s before rolling deploy
rileyseaburg Dec 19, 2025
8c589a3
Fix flaky logo upload test: add wait time to prevent Selenium stale e…
rileyseaburg Dec 19, 2025
6694a60
Fix cf set-health-check: use --invocation-timeout instead of --timeout
rileyseaburg Dec 19, 2025
33aa4ca
Fix Rack::Attack test: create actual form fixture to avoid 404 responses
rileyseaburg Dec 19, 2025
48b4e42
Scale sidekiq worker to 1 instance during rolling deploy to avoid org…
rileyseaburg Dec 19, 2025
6cbc40a
Stop sidekiq worker before push to free memory for staging (avoids or…
rileyseaburg Dec 19, 2025
e654f1a
Fix cf run-task syntax: add --command flag for migrations
rileyseaburg Dec 19, 2025
b17086f
Skip WidgetRenderer load during migrations - library not built in tas…
rileyseaburg Dec 19, 2025
4bd89c1
Fix Rack::Attack test: add valid submission params to avoid 400 errors
rileyseaburg Dec 19, 2025
0979b8e
Temporarily disable pre-deploy migrations to unblock deployment
rileyseaburg Dec 19, 2025
2f19ac3
Fix widget_renderer initializer - use simpler skip detection logic
rileyseaburg Dec 19, 2025
572ce1c
Fix deployment: restore db:migrate in manifest and enable migrations …
rileyseaburg Dec 19, 2025
0ad8705
Revert migrations to start command - Rust library not available in cf…
rileyseaburg Dec 19, 2025
f1dd1e6
Build Rust library at runtime in .profile.d script
rileyseaburg Dec 19, 2025
a3b3e3b
Revert to working widget_renderer script that copies prebuilt library
rileyseaburg Dec 19, 2025
3ae31df
Keep prebuilt Rust library during deployment to ensure correct linkin…
rileyseaburg Dec 19, 2025
92b1f75
Fix deploy-sidekiq.sh: remove explicit buildpack flags to avoid re-in…
rileyseaburg Dec 19, 2025
722f8e5
Fix touchpoints.yml: comment out buildpacks to prevent Rust reinstall…
rileyseaburg Dec 19, 2025
d632d21
Fix flaky timing test in submission_digest mailer spec
rileyseaburg Dec 19, 2025
875cb7b
Fix custom-button-modal USWDS initialization
rileyseaburg Dec 19, 2025
4b11274
Bump Cargo version to force Rust rebuild
rileyseaburg Dec 19, 2025
3ee1dda
Bump widget_renderer version to force Cargo rebuild
rileyseaburg Dec 19, 2025
24b0e26
Force cargo clean before build to ensure recompilation
rileyseaburg Dec 19, 2025
93f4271
Bump widget_renderer gem version to 0.1.2 to force rebuild
rileyseaburg Dec 19, 2025
05fb5a0
Force Rust rebuild with BUILD_ID and version bump
rileyseaburg Dec 19, 2025
32df1a1
Bump widget_renderer to 0.1.2 to force CF to rebuild native extension
rileyseaburg Dec 19, 2025
763a305
Update Gemfile.lock for widget_renderer 0.1.2
rileyseaburg Dec 19, 2025
45d9661
Add Rust library verification before CF push
rileyseaburg Dec 19, 2025
513bf2b
Prioritize workspace-level Rust library and bump to 0.1.3
rileyseaburg Dec 19, 2025
3125b91
Invalidate CircleCI cargo cache to force fresh Rust build
rileyseaburg Dec 19, 2025
35b2d51
Fix Rust widget renderer modal button initialization (#1924)
rileyseaburg Dec 22, 2025
b07fa59
Address PR #1925 feedback
rileyseaburg Dec 22, 2025
f6b5555
Fix empty string check for customButtonSelector
rileyseaburg Dec 22, 2025
92d4e38
Merge pull request #1926 from GSA/fix/pr-1925-feedback
rileyseaburg Dec 22, 2025
edc19e4
Merge production into develop to resolve PR #1925 conflicts
rileyseaburg Dec 22, 2025
08faf20
Fix flaky inline title edit test by waiting for AJAX to complete
rileyseaburg Dec 22, 2025
d5a596b
Add post-mortem for widget modal button incident (Dec 2025)
rileyseaburg Dec 22, 2025
c653d1b
Revert "Add post-mortem for widget modal button incident (Dec 2025)"
rileyseaburg Dec 22, 2025
557f3a5
Fix deploy script rolling deployment timeout issue
rileyseaburg Dec 22, 2025
c3cf39c
Bust Cargo cache to force Rust widget rebuild (v2 -> v3)
rileyseaburg Dec 22, 2025
a69e34f
Update Cargo.lock to force cache invalidation and Rust rebuild
rileyseaburg Dec 22, 2025
dbd4605
Fix Cargo cache key to include source file checksum (v3 -> v4)
rileyseaburg Dec 22, 2025
adc4305
Fix cf-cli download URL to use direct GitHub release URL
rileyseaburg Dec 22, 2025
9ecb049
Fix cf-cli installation using APT repository instead of direct download
rileyseaburg Dec 22, 2025
f527d3b
Update .circleci/config.yml
rileyseaburg Dec 22, 2025
03fd10d
Update .circleci/config.yml
rileyseaburg Dec 22, 2025
324730f
Merge pull request #1932 from GSA/fix/cf-cli-apt-install
rileyseaburg Dec 22, 2025
6626b3f
Add missing touchpoints-s3-uploads service binding to manifest
rileyseaburg Dec 23, 2025
ff76f6b
Fix CF CLI GPG key installation for modern apt
rileyseaburg Dec 23, 2025
6baf5ca
Add SKIP_WIDGET_RENDERER to manifests to prevent env var removal on d…
rileyseaburg Dec 23, 2025
375dde5
Add SKIP_WIDGET_RENDERER check at gem load time to prevent crash
rileyseaburg Dec 23, 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
1 change: 1 addition & 0 deletions .cfignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@
# Ignore Rust build artifacts
target/
ext/widget_renderer/target/
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The negation pattern on line 44 won't work as intended because line 43 already ignores the parent directory ext/widget_renderer/target/. In gitignore-style patterns, once a parent directory is ignored, you cannot un-ignore it with a negation pattern before un-ignoring its contents.

Consider removing line 43 (ext/widget_renderer/target/) and keeping only line 42 (target/) which already covers this path. The specific negations on lines 45-46 should then work correctly.

Suggested change
ext/widget_renderer/target/

Copilot uses AI. Check for mistakes.
!ext/widget_renderer/libwidget_renderer.so
105 changes: 93 additions & 12 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
#
version: 2.1

# 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.4.7-browsers # Updated to match Gemfile Ruby version
- image: cimg/ruby:3.2.8-browsers # Matches deployed Ruby version in CF
environment:
RAILS_ENV: test
PGHOST: 127.0.0.1
Expand Down Expand Up @@ -41,6 +46,51 @@ jobs:

- 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

- restore_cache:
keys:
- v4-cargo-{{ checksum "ext/widget_renderer/Cargo.lock" }}-{{ checksum "ext/widget_renderer/src/template_renderer.rs" }}
- v4-cargo-{{ checksum "ext/widget_renderer/Cargo.lock" }}-

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

- run:
name: Verify Rust native library linkage
command: |
set -euo pipefail
LIB=ext/widget_renderer/target/release/libwidget_renderer.so
if [ -f "$LIB" ]; then
echo "Found built rust library; verifying linkage..."
if ldd "$LIB" 2>&1 | grep -q "not found"; then
echo "ERROR: Rust library has unresolved dependencies (ldd shows 'not found')."
ldd "$LIB" || true
exit 1
else
echo "Rust library linkage looks good"
fi
else
echo "No Rust library built - skipping linkage verification"
fi

- save_cache:
paths:
- ext/widget_renderer/target
- ~/.cargo/registry
- ~/.cargo/git
key: v4-cargo-{{ checksum "ext/widget_renderer/Cargo.lock" }}-{{ checksum "ext/widget_renderer/src/template_renderer.rs" }}

# Download and cache dependencies
- restore_cache:
keys:
Expand Down Expand Up @@ -71,17 +121,20 @@ jobs:
mkdir /tmp/test-results
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"

bundle exec rspec --format progress \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress \
$TEST_FILES
bundle exec rspec --format progress \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress \
$TEST_FILES
# Install Cloud Foundry cli (cf) before deploy step. cf is used to push to Cloud.gov
- run:
name: Install-cf-cli
command: |
curl -v -L -o cf-cli_amd64.deb 'https://packages.cloudfoundry.org/stable?release=debian64&source=github'
sudo dpkg -i cf-cli_amd64.deb
# Download and convert the GPG key to binary format for modern apt
wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo gpg --dearmor -o /usr/share/keyrings/cloudfoundry-cli.gpg
echo "deb [signed-by=/usr/share/keyrings/cloudfoundry-cli.gpg] https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list
sudo apt-get update
sudo apt-get install -y cf8-cli
cf -v
# collect reports
- store_test_results:
Expand All @@ -91,11 +144,36 @@ 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
# Keep prebuilt Rust library - extconf.rb builds it during bundle install with correct paths
# The library is built with rutie which properly links against the CF Ruby installation
# 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
# Keep prebuilt Rust library - extconf.rb builds it during bundle install with correct 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 @@ -105,8 +183,11 @@ jobs:
- run:
name: Install-cf-cli
command: |
curl -v -L -o cf-cli_amd64.deb 'https://packages.cloudfoundry.org/stable?release=debian64&source=github'
sudo dpkg -i cf-cli_amd64.deb
# Download and convert the GPG key to binary format for modern apt
wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo gpg --dearmor -o /usr/share/keyrings/cloudfoundry-cli.gpg
echo "deb [signed-by=/usr/share/keyrings/cloudfoundry-cli.gpg] https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list
sudo apt-get update
sudo apt-get install -y cf8-cli
cf -v
- run:
name: Run CRON tasks
Expand Down
169 changes: 166 additions & 3 deletions .circleci/deploy-sidekiq.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,176 @@
# 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"

# Update app to use 180s invocation timeout and process health check before rolling deploy
echo "Updating health check configuration for $app_name..."
cf set-health-check "$app_name" process --invocation-timeout 180 || true
sleep 2

# Get current instance count and scale down to 1 to avoid memory quota issues during rolling deploy
echo "Checking current instance count for $app_name..."
local current_instances=$(cf app "$app_name" | grep "^instances:" | awk '{print $2}' | cut -d'/' -f2 || echo "1")
echo "Current instances: $current_instances"

if [ "$current_instances" -gt 1 ]; then
echo "Scaling down to 1 instance to free memory for rolling deploy..."
cf scale "$app_name" -i 1 || true
sleep 5
fi

for i in $(seq 1 $max_retries); do
echo "Attempt $i of $max_retries to push $app_name..."

# Stop the app first to free memory for staging
echo "Stopping $app_name to free memory for staging..."
cf stop "$app_name" || true
sleep 5

# Push without rolling strategy (direct replacement since we stopped it)
# Let CF auto-detect buildpacks to avoid re-running supply phase (Rust already built in CircleCI)
if cf push "$app_name" \
-t 180 \
--health-check-type process; then
echo "Successfully pushed $app_name"

# Scale back up to original instance count
if [ "$current_instances" -gt 1 ]; then
echo "Scaling up to $current_instances instances..."
cf scale "$app_name" -i "$current_instances" || true
fi

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

# If we failed, try to scale back up anyway
if [ "$current_instances" -gt 1 ]; then
echo "Deploy failed, attempting to scale back up to $current_instances instances..."
cf scale "$app_name" -i "$current_instances" || true
fi

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
echo "Syncing Login.gov environment variables..."
./.circleci/sync-login-gov-env.sh touchpoints-production-sidekiq-worker
cf_push_with_retry touchpoints-production-sidekiq-worker
echo "Push to Production Complete."
else
echo "Not on the production branch."
Expand All @@ -22,7 +185,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 +197,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