Skip to content

Commit 3f39a42

Browse files
Merge branch 'develop' into 5621-add-regional-user-support-for-stt-feedback-reports-page
2 parents 5882276 + 1d29acc commit 3f39a42

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2214
-243
lines changed

.circleci/deployment/jobs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
app-dir: tdrs-frontend
126126
- run:
127127
name: Run Cypress e2e tests
128-
command: cd tdrs-frontend; yarn test:e2e-ci -- --config baseUrl="https://tdp-frontend-develop.acf.hhs.gov" --env cypressToken=$CYPRESS_TOKEN,apiUrl="https://tdp-frontend-develop.acf.hhs.gov/v1",adminUrl="https://tdp-frontend-develop.acf.hhs.gov/admin"
128+
command: cd tdrs-frontend; yarn test:e2e-ci --config baseUrl="https://tdp-frontend-develop.acf.hhs.gov" --env cypressToken=$CYPRESS_TOKEN,apiUrl="https://tdp-frontend-develop.acf.hhs.gov/v1",adminUrl="https://tdp-frontend-develop.acf.hhs.gov/admin"
129129
- store_artifacts:
130130
path: tdrs-frontend/cypress/screenshots/
131131
- store_artifacts:
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
name: Release Tracker
3+
about: Track the release handoff to OFA, staging validation, and production deployment.
4+
title: Release Tracker vX.X.X
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
10+
# Release Tracker Issue Template
11+
12+
**TITLE:** Release vX.X.X
13+
14+
**CONTENT:**
15+
16+
### 🔗 Included Pull Requests (Dev Team)
17+
*List the PRs included in this release. **Testing instructions for each feature must be located within these linked PRs.***
18+
* #XXX - [Feature/Bugfix Title]
19+
* #XXX - [Feature/Bugfix Title]
20+
21+
### ➡️ URL of GitHub Release Tag:
22+
https://github.com/raft-tech/TANF-app/releases/tag/vX.X.X
23+
24+
---
25+
26+
### 📦 1. Preparation & Handoff (Dev / UX / PM)
27+
*Ensuring the release branch is ready and documented before ACF takes over.*
28+
29+
- [ ] **Release Tagged:** `develop` branch tagged with the new release version.
30+
- [ ] **Release Branch Created:** Branch cut from `develop`.
31+
- [ ] **PR Opened to Staging:** PR opened from the release branch to `HHS:main`.
32+
- [ ] **Testing Instructions Verified:** All linked PRs contain clear testing instructions for ACF validation.
33+
- [ ] **UX/Documentation Check:** UX team has reviewed the PRs, confirmed user-facing changes, and started drafting Release Notes and Knowledge Center guidance.
34+
- [ ] **Migration Flag:** Does this release include a database migration? **[Yes / No]** *(If Yes, rollback from production will be highly complex).*
35+
- [ ] **Handoff Complete:** Issue assigned to @[Alex_username] for Staging validation.
36+
37+
---
38+
39+
### 🧪 2. Staging Validation & QASP (ACF / Alex)
40+
*Tracking the status once ACF takes over deployment and testing.*
41+
42+
- [ ] **Staging Cleared:** Team notified that Staging is about to be updated/restarted.
43+
- [ ] **Deployed to Staging:** PR merged and deployed to the Staging environment.
44+
- [ ] **Feature Validation:** Testing instructions from the linked PRs have been executed and passed.
45+
- [ ] **Regression Validation:** Core workflows (login, submissions, data integrity, etc.) remain functional.
46+
47+
**🐛 Bug Tracking Protocol (If issues are found in Staging):**
48+
1. **Non-Dev Team:** Add a comment on this issue describing the bug/unexpected behavior.
49+
2. **Dev Team:** Review the comment, investigate root cause, and open a formal GitHub Issue.
50+
3. **Triage Decision:**
51+
* *Revert:* If isolated to a new feature, revert the PR out of the release candidate.
52+
* *Hotfix:* Warranted **only if** the bug blocks the release entirely AND the production release is needed ASAP. (Dev cuts hotfix PR against the release branch -> merged to `HHS:main` for re-testing).
53+
54+
- [ ] **Documentation Finalized:** UX team confirms all Release Notes and Knowledge Center updates are finalized and ready for launch.
55+
56+
---
57+
58+
### 🚦 3. Production-Ready Sign-Off
59+
- [ ] ACF/Alex confirms all PRs are validated, no blocking bugs exist, documentation is finalized, and the release is approved for production deployment.
60+
61+
---
62+
63+
### 🛟 4. Rollback & Contingency Reference
64+
*Review before production deployment.*
65+
66+
* **Pipeline/CircleCI Failure:** Retry the pipeline. If it fails again, requires a hotfix to unblock.
67+
* **Missing Config/Secrets (App crashes on boot):** Do not rollback. ACF updates environment variables in the production console and restarts the app.
68+
* **Third-Party API Blocked in Prod:** Dev provides an emergency hotfix to hide the broken UI component.
69+
* **Performance/Database Lockup Under Load:** Dev writes an emergency hotfix for the offending query.
70+
* **Critical Regression Post-Deploy:**
71+
* *No migrations in release:* Rollback the deployment to the previous stable version.
72+
* *Migrations in release:* Rollback is generally not possible; requires an emergency hotfix.
73+
74+
---
75+
76+
### 🚀 5. Production Deployment (ACF / Alex)
77+
*The final deployment executed by ACF.*
78+
79+
- [ ] **Maintenance Mode:** Alex has enabled the maintenance page to ensure users are out of the system.
80+
- [ ] **Deployed to Prod:** PR opened and merged to `HHS:master`, triggering deployment.
81+
- [ ] **Post-Launch Verification:** Quick check that the production environment is stable post-deploy, and the maintenance page is deactivated.
82+
83+
---
84+
85+
### 📢 6. Post-Release Communication (PM / UX / ACF)
86+
*Closing the loop with users and stakeholders.*
87+
88+
- [ ] **Release Notes Published:** (UX team) Plain-language, user-facing release notes and Knowledge Center guidance have been published.
89+
- [ ] **Stakeholders Notified:** (ACF) Any required external communication regarding the new version has been sent.
90+
- [ ] **Close this Issue:** (ACF / PM) The release is fully deployed and stable.

.gitignore

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ tdrs-frontend/yarn-error.log*
1212
tdrs-frontend/vars-frontend.yml
1313
tdrs-frontend/pa11y-screenshots/*
1414
tdrs-frontend/cypress/reports*
15+
tdrs-frontend/test-results*.xml
1516
.eslintcache
1617
downloads/
1718

@@ -125,8 +126,3 @@ cypress.env.json
125126
tdrs-backend/*.pg
126127
tdrs-backend/django.log
127128

128-
# Pytest cache
129-
tdrs-backend/.pytest_cache/*
130-
131-
# frontend cypress test results
132-
tdrs-frontend/test-results*.xml

Taskfile.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,8 @@ tasks:
293293
dir: tdrs-backend
294294
cmds:
295295
- export CYPRESS_TOKEN=local-cypress-token
296-
- docker-compose exec web python manage.py delete_cypress_users -usernames new-cypress@teamraft.com cypress-admin@teamraft.com
297-
- docker-compose exec web python manage.py loaddata cypress/users cypress/data_files cypress/regions cypress/profile_editing_regions cypress/profile_editing_users
296+
- docker compose -f docker-compose.yml exec web python manage.py delete_cypress_users -usernames new-cypress@teamraft.com cypress-admin@teamraft.com cypress-data-analyst-dana@teamraft.com cypress-fra-data-analyst-derek@teamraft.com cypress-data-analyst-donna@teamraft.com cypress-fra-data-analyst-david@teamraft.com cypress-fra-ofa-regional-staff-rachel@acf.hhs.gov cypress-fra-ofa-regional-staff-robert@acf.hhs.gov cypress-fra-ofa-regional-staff-rita@acf.hhs.gov cypress-fra-ofa-regional-staff-ryan@acf.hhs.gov
297+
- docker compose -f docker-compose.yml exec web python manage.py loaddata cypress/users cypress/data_files cypress/regions cypress/profile_editing_regions cypress/profile_editing_users
298298

299299
frontend-e2e-local:
300300
desc: Run Cypress E2E tests locally (Cypress on host, app in docker)
@@ -303,9 +303,9 @@ tasks:
303303
- docker compose -f docker-compose.local.yml up -d --build tdp-frontend
304304
- |
305305
if command -v corepack >/dev/null 2>&1; then
306-
corepack prepare yarn@4.6.0 --activate && CYPRESS_API_URL=http://localhost:8989/v1 CYPRESS_ADMIN_URL=http://localhost:8989/admin env -u ELECTRON_RUN_AS_NODE yarn test:e2e
306+
corepack prepare yarn@4.6.0 --activate && env -u ELECTRON_RUN_AS_NODE yarn test:e2e
307307
else
308-
CYPRESS_API_URL=http://localhost:8989/v1 CYPRESS_ADMIN_URL=http://localhost:8989/admin env -u ELECTRON_RUN_AS_NODE npx --yes @yarnpkg/cli-dist@4.6.0 test:e2e
308+
env -u ELECTRON_RUN_AS_NODE npx --yes @yarnpkg/cli-dist@4.6.0 test:e2e
309309
fi
310310
311311
frontend-e2e-ci-local:
@@ -320,11 +320,18 @@ tasks:
320320
- env -u ELECTRON_RUN_AS_NODE npx cypress verify || env -u ELECTRON_RUN_AS_NODE npx cypress install --force
321321
- |
322322
if command -v corepack >/dev/null 2>&1; then
323-
corepack prepare yarn@4.6.0 --activate && CYPRESS_API_URL=http://localhost:8989/v1 CYPRESS_ADMIN_URL=http://localhost:8989/admin CYPRESS_TOKEN={{.CYPRESS_TOKEN}} env -u ELECTRON_RUN_AS_NODE yarn test:e2e-ci
323+
env -u ELECTRON_RUN_AS_NODE yarn test:e2e-ci
324324
else
325-
CYPRESS_API_URL=http://localhost:8989/v1 CYPRESS_ADMIN_URL=http://localhost:8989/admin CYPRESS_TOKEN={{.CYPRESS_TOKEN}} env -u ELECTRON_RUN_AS_NODE npx --yes @yarnpkg/cli-dist@4.6.0 test:e2e-ci
325+
env -u ELECTRON_RUN_AS_NODE npx --yes @yarnpkg/cli-dist@4.6.0 test:e2e-ci
326326
fi
327327
328+
frontend-e2e-cmd:
329+
desc: Run Cypress E2E tests headlessly from the command line (app in docker)
330+
dir: tdrs-frontend
331+
cmds:
332+
- docker compose -f docker-compose.yml up -d --build tdp-frontend
333+
- CYPRESS_TOKEN=${CYPRESS_TOKEN:-local-cypress-token} npm run test:e2e-ci
334+
328335
k6:
329336
desc: Run k6 performance tests
330337
dir: performance-tests

tdrs-backend/celery_start.sh

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,46 @@
33
echo "Starting celery"
44

55
if [[ $1 == "cloud" ]]; then
6-
# Get the computed URI from VCAP_SERVICES
7-
REDIS_URI=$(echo $VCAP_SERVICES | jq -r '."aws-elasticache-redis"[0].credentials.uri')
6+
# Build the broker URL with TLS scheme and correct DB number (matching cloudgov.py logic)
7+
REDIS_BASE=$(echo $VCAP_SERVICES | jq -r '."aws-elasticache-redis"[0].credentials | "rediss://:\(.password)@\(.host):\(.port)"')
8+
ENV_NAME=$(echo $VCAP_APPLICATION | jq -r '.application_name | split("-") | last')
9+
# Running python method, to not duplicate logic for determining the DB number
10+
BROKER_DB=$(python -c "from tdpservice.common.util import get_cloudgov_broker_db_numbers; print(get_cloudgov_broker_db_numbers('${ENV_NAME}')['celery'])")
11+
REDIS_URI="${REDIS_BASE}/${BROKER_DB}"
12+
13+
# Log the broker config (redact password)
14+
echo "REDIS_URI: $(echo $REDIS_URI | sed 's/:.*@/:\/\/***@/')"
15+
echo "ENV_NAME: ${ENV_NAME}, BROKER_DB: ${BROKER_DB}"
16+
17+
if [[ -z "$REDIS_BASE" || "$REDIS_BASE" == "rediss://:@:" ]]; then
18+
echo "ERROR: Failed to extract Redis credentials from VCAP_SERVICES"
19+
exit 1
20+
fi
21+
if [[ -z "$BROKER_DB" ]]; then
22+
echo "ERROR: Failed to determine broker DB number for env '${ENV_NAME}'"
23+
exit 1
24+
fi
825

926
echo "Starting Alloy"
1027
mkdir /home/vcap/app/alloy-data
1128
wget https://github.com/grafana/alloy/releases/download/v1.9.1/alloy-boringcrypto-linux-amd64.zip
12-
unzip -a alloy-boringcrypto-linux-amd64.zip && rm -rf alloy-boringcrypto-linux-amd64.zip
13-
chmod +x alloy-boringcrypto-linux-amd64
14-
./alloy-boringcrypto-linux-amd64 run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/home/vcap/app/alloy-data /home/vcap/app/plg/alloy/alloy.config &
29+
if ! unzip -a alloy-boringcrypto-linux-amd64.zip; then
30+
echo "ERROR: Failed to unzip Alloy"
31+
else
32+
rm -rf alloy-boringcrypto-linux-amd64.zip
33+
chmod +x alloy-boringcrypto-linux-amd64
34+
./alloy-boringcrypto-linux-amd64 run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/home/vcap/app/alloy-data /home/vcap/app/plg/alloy/alloy.config &
35+
echo "Alloy started (PID: $!)"
36+
fi
1537

1638
echo "Starting the Celery Exporter"
17-
curl -L https://github.com/danihodovic/celery-exporter/releases/download/latest/celery-exporter -o ./celery-exporter
18-
chmod +x ./celery-exporter
19-
./celery-exporter --broker-url="$REDIS_URI" --port 9808 &
39+
if ! curl -fL https://github.com/danihodovic/celery-exporter/releases/download/latest/celery-exporter -o ./celery-exporter; then
40+
echo "ERROR: Failed to download celery-exporter"
41+
else
42+
chmod +x ./celery-exporter
43+
./celery-exporter --broker-url="$REDIS_URI" --port 9808 &
44+
echo "Celery Exporter started (PID: $!)"
45+
fi
2046
fi
2147

2248
# Celery worker config can be found here: https://docs.celeryq.dev/en/stable/userguide/workers.html#:~:text=The-,hostname,-argument%20can%20expand

tdrs-backend/tdpservice/fixtures/cypress/profile_editing_regions.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,37 @@
3030
"user": "f6a7b8c9-d0e1-4f5a-3b4c-5d6e7f8a9b0c",
3131
"region": 3
3232
}
33+
},
34+
{
35+
"model": "users.regionmeta",
36+
"pk": 1005,
37+
"fields": {
38+
"user": "0a1b2c3d-4e5f-4a6b-8c9d-1e2f3a4b5c6d",
39+
"region": 5
40+
}
41+
},
42+
{
43+
"model": "users.regionmeta",
44+
"pk": 1006,
45+
"fields": {
46+
"user": "0a1b2c3d-4e5f-4a6b-8c9d-1e2f3a4b5c6d",
47+
"region": 3
48+
}
49+
},
50+
{
51+
"model": "users.regionmeta",
52+
"pk": 1007,
53+
"fields": {
54+
"user": "1b2c3d4e-5f6a-4b7c-8d9e-2f3a4b5c6d7e",
55+
"region": 5
56+
}
57+
},
58+
{
59+
"model": "users.regionmeta",
60+
"pk": 1008,
61+
"fields": {
62+
"user": "1b2c3d4e-5f6a-4b7c-8d9e-2f3a4b5c6d7e",
63+
"region": 3
64+
}
3365
}
3466
]

tdrs-backend/tdpservice/fixtures/cypress/profile_editing_users.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,5 +190,73 @@
190190
]
191191
]
192192
}
193+
},
194+
{
195+
"model": "users.user",
196+
"pk": "0a1b2c3d-4e5f-4a6b-8c9d-1e2f3a4b5c6d",
197+
"fields": {
198+
"password": "",
199+
"last_login": null,
200+
"is_superuser": false,
201+
"username": "cypress-fra-ofa-regional-staff-rita@acf.hhs.gov",
202+
"first_name": "FRA OFA Regional Staff",
203+
"last_name": "Rita",
204+
"email": "cypress-fra-ofa-regional-staff-rita@acf.hhs.gov",
205+
"is_staff": false,
206+
"is_active": true,
207+
"date_joined": "2025-11-26T00:00:00Z",
208+
"stt": null,
209+
"login_gov_uuid": null,
210+
"hhs_id": null,
211+
"account_approval_status": "Approved",
212+
"access_requested_date": "2025-11-26T00:00:00Z",
213+
"feature_flags": {},
214+
"groups": [
215+
[
216+
"OFA Regional Staff"
217+
]
218+
],
219+
"user_permissions": [
220+
[
221+
"has_fra_access",
222+
"users",
223+
"user"
224+
]
225+
]
226+
}
227+
},
228+
{
229+
"model": "users.user",
230+
"pk": "1b2c3d4e-5f6a-4b7c-8d9e-2f3a4b5c6d7e",
231+
"fields": {
232+
"password": "",
233+
"last_login": null,
234+
"is_superuser": false,
235+
"username": "cypress-fra-ofa-regional-staff-ryan@acf.hhs.gov",
236+
"first_name": "FRA OFA Regional Staff",
237+
"last_name": "Ryan",
238+
"email": "cypress-fra-ofa-regional-staff-ryan@acf.hhs.gov",
239+
"is_staff": false,
240+
"is_active": true,
241+
"date_joined": "2025-11-26T00:00:00Z",
242+
"stt": null,
243+
"login_gov_uuid": null,
244+
"hhs_id": null,
245+
"account_approval_status": "Approved",
246+
"access_requested_date": "2025-11-26T00:00:00Z",
247+
"feature_flags": {},
248+
"groups": [
249+
[
250+
"OFA Regional Staff"
251+
]
252+
],
253+
"user_permissions": [
254+
[
255+
"has_fra_access",
256+
"users",
257+
"user"
258+
]
259+
]
260+
}
193261
}
194262
]

tdrs-backend/tdpservice/settings/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
from __future__ import absolute_import
22

33
import logging
4-
import os
5-
64
from django.conf import settings
75

86
# This will make sure the app is always imported when

tdrs-backend/tdpservice/users/admin.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,11 @@ def status_with_indicator(self, obj):
223223
"pending": "orange",
224224
"approved": "green",
225225
"rejected": "red",
226+
"cancelled": "gray",
226227
}
228+
color = colors.get(obj.status, "black")
227229
return mark_safe(
228-
f'<span style="color: {colors[obj.status]};">{obj.get_status_display()}</span>'
230+
f'<span style="color: {color};">{obj.get_status_display()}</span>'
229231
)
230232

231233
status_with_indicator.short_description = "Status"
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.2.10 on 2026-03-17 13:30
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('users', '0056_historicaluser_historicalregionmeta_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='userchangerequest',
15+
name='status',
16+
field=models.CharField(choices=[('pending', 'Pending'), ('approved', 'Approved'), ('rejected', 'Rejected'), ('cancelled', 'Cancelled')], default='pending', help_text='The current status of this change request', max_length=20),
17+
),
18+
]

0 commit comments

Comments
 (0)