fix(local): running locally audience and env vars#2797
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughUpdates encrypted environment configuration with a new notification ingress queue entry. Refactors FusionAuth local setup tasks to use idempotent patching with strict error handling and conditional service startup, adding a new recipe to coordinate environment variable injection before Docker services launch. Changes
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@infra/stacks/fusionauth-instance/justfile`:
- Line 72: Update the shell options in this Just recipe to include nounset:
replace the existing "set -eo pipefail" with "set -euo pipefail" so undefined
variable references (like ENV_FILE) cause immediate failures; locate the recipe
that currently uses set -eo pipefail (the same style used elsewhere) and make
the change to match the repo convention and the other recipe named
patch_local_fusionauth_env.
- Line 85: The API_KEY value in the Justfile is a hardcoded FusionAuth key;
update the file so the line with API_KEY clearly marks it as the local-only
kickstart value (e.g., add an inline comment stating "local-only kickstart key —
do not use in non-local environments"), and ensure any code that reads API_KEY
(search for references to API_KEY in deployment scripts or FuseAuth provisioning
scripts) is conditioned to only use it for the local stack or replaced by
environment-specific secrets for non-local stacks; if appropriate, replace usage
in non-local flows with a reference to a secure secret (env var or secret
manager) instead of this hardcoded value.
- Around line 89-94: The curl call that sets CURL_RESPONSE (curl ...
"http://localhost:9011/api/application/${APPLICATION_ID}") lacks connect/read
timeouts and doesn't validate HTTP status, so it can hang or pass through
non-200 responses into subsequent jq processing; update the curl invocation to
include --connect-timeout and --max-time values and capture the HTTP status
(e.g., via curl -w '%{http_code}') into a separate variable or appended marker,
then validate the status is 200 before attempting to parse CURL_RESPONSE (and
treat empty body as failure), and produce a clear error and exit if the request
timed out or returned a non-200 code.
- Around line 101-113: The patch_var function embeds unescaped ${value} into a
sed substitution using | as delimiter, which breaks on values containing |, &, \
or newlines (e.g., CLIENT_SECRET); update patch_var to safely write the
key/value by either (A) escaping the value before calling sed (replace
backslashes with \\\\, ampersands with \&, and the chosen delimiter | with \|,
and handle newlines) and then use sed -i with the escaped replacement, or (B)
replace the sed path with a small portable helper that writes/updates ENV_FILE
using awk or a short Python script that reads ENV_FILE, updates the matching
key, and preserves other lines (use the same key="$1" value="$2" interface and
ensure append when missing). Ensure both macOS (darwin) and Linux branches use
the same safe approach and keep the echo append fallback unchanged for missing
keys.
In `@justfile`:
- Line 74: The readiness probe using curl in the run_local check currently has
no timeout; update the curl invocation (the line containing curl -s
http://localhost:9011/api/status | grep -q '"Ok"') to include a short
connect/read timeout (for example use --connect-timeout and/or --max-time) so
the check fails fast instead of hanging indefinitely when port 9011 is
unresponsive.
- Around line 67-71: The current soft-skip exits 0 which allows run_local to
continue into docker_up with missing/stale FusionAuth env vars; change the
behavior so the script fails fast: in the if block that checks pulumi stack
output (the macroApplicationClientId check) replace exit 0 with a non-zero exit
(e.g., exit 1) and update the echo text to explicitly state that services will
be misconfigured and that the user should run 'just setup' (mentioning run_local
and docker_up in the message) so callers like run_local don't proceed with
stale/missing FusionAuth env vars.
- Around line 76-82: Two docker compose invocations (the "docker compose up
fusionauth -d --wait" and "docker compose stop fusionauth") are missing the
explicit compose file; update both commands to include -f
infra/stacks/fusionauth-instance/docker-compose.yml so they target the
FusionAuth stack used by insert_local_fusionauth_variables and match the
setup_fusionauth usage elsewhere in the file.
- Around line 73-83: The script starts FusionAuth and sets NEEDS_STOP=true but
can exit early if just
infra/stacks/fusionauth-instance/insert_local_fusionauth_variables fails,
leaving the container running; add a shell trap that on EXIT checks NEEDS_STOP
and stops FusionAuth so cleanup always runs regardless of errors. Specifically,
after you set NEEDS_STOP=true (the block that runs docker compose up fusionauth
-d --wait), register a trap function that runs on EXIT and executes the same
cleanup (if [ "$NEEDS_STOP" = true ]; then docker compose stop fusionauth; fi),
and ensure you unset or adjust the trap only if you intentionally want to keep
the container running later.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b8b56bf1-e4d7-4d26-9e23-0ea22e078b51
📒 Files selected for processing (3)
.env-local.encinfra/stacks/fusionauth-instance/justfilejustfile
| # values that differ between local and dev FusionAuth. | ||
| insert_local_fusionauth_variables: | ||
| #!/usr/bin/env bash | ||
| set -eo pipefail |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider set -euo pipefail for consistency.
The root justfile's patch_local_fusionauth_env (line 62) uses set -euo pipefail, but this recipe uses only set -eo pipefail. Enabling -u (nounset) would catch typos in variable references (e.g., the ENV_FILE expansion) early and matches the convention in the rest of the repo.
- set -eo pipefail
+ set -euo pipefail🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/stacks/fusionauth-instance/justfile` at line 72, Update the shell
options in this Just recipe to include nounset: replace the existing "set -eo
pipefail" with "set -euo pipefail" so undefined variable references (like
ENV_FILE) cause immediate failures; locate the recipe that currently uses set
-eo pipefail (the same style used elsewhere) and make the change to match the
repo convention and the other recipe named patch_local_fusionauth_env.
| CURL_RESPONSE=$(curl -s "http://localhost:9011/api/application/${APPLICATION_ID}" \ | ||
| -H "Authorization: ${API_KEY}") | ||
| if [ -z "$CURL_RESPONSE" ]; then | ||
| echo "Local FusionAuth instance is not running" | ||
| echo "Error: Local FusionAuth instance is not running." | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
Curl has no connect/read timeout; will hang if FusionAuth is reachable but unresponsive.
The readiness gate in the root justfile only checks /api/status before starting FusionAuth; if the container is up but the application endpoint hangs (e.g., startup stall, deadlock), this curl can block indefinitely because it has no --max-time/--connect-timeout. Also, an empty body is only one failure mode — non-200 responses with bodies (e.g., a 401 JSON) won't be caught here and will fall through to the jq call.
- CURL_RESPONSE=$(curl -s "http://localhost:9011/api/application/${APPLICATION_ID}" \
- -H "Authorization: ${API_KEY}")
- if [ -z "$CURL_RESPONSE" ]; then
- echo "Error: Local FusionAuth instance is not running."
+ HTTP_CODE=$(curl -s -o /tmp/fa_app.json -w '%{http_code}' \
+ --connect-timeout 5 --max-time 15 \
+ -H "Authorization: ${API_KEY}" \
+ "http://localhost:9011/api/application/${APPLICATION_ID}")
+ if [ "$HTTP_CODE" != "200" ]; then
+ echo "Error: FusionAuth /api/application returned HTTP ${HTTP_CODE}."
exit 1
fi
- CLIENT_SECRET=$(echo "$CURL_RESPONSE" | jq -r '.application.oauthConfiguration.clientSecret')
+ CLIENT_SECRET=$(jq -r '.application.oauthConfiguration.clientSecret' /tmp/fa_app.json)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| CURL_RESPONSE=$(curl -s "http://localhost:9011/api/application/${APPLICATION_ID}" \ | |
| -H "Authorization: ${API_KEY}") | |
| if [ -z "$CURL_RESPONSE" ]; then | |
| echo "Local FusionAuth instance is not running" | |
| echo "Error: Local FusionAuth instance is not running." | |
| exit 1 | |
| fi | |
| HTTP_CODE=$(curl -s -o /tmp/fa_app.json -w '%{http_code}' \ | |
| --connect-timeout 5 --max-time 15 \ | |
| -H "Authorization: ${API_KEY}" \ | |
| "http://localhost:9011/api/application/${APPLICATION_ID}") | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "Error: FusionAuth /api/application returned HTTP ${HTTP_CODE}." | |
| exit 1 | |
| fi | |
| CLIENT_SECRET=$(jq -r '.application.oauthConfiguration.clientSecret' /tmp/fa_app.json) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/stacks/fusionauth-instance/justfile` around lines 89 - 94, The curl
call that sets CURL_RESPONSE (curl ...
"http://localhost:9011/api/application/${APPLICATION_ID}") lacks connect/read
timeouts and doesn't validate HTTP status, so it can hang or pass through
non-200 responses into subsequent jq processing; update the curl invocation to
include --connect-timeout and --max-time values and capture the HTTP status
(e.g., via curl -w '%{http_code}') into a separate variable or appended marker,
then validate the status is 200 before attempting to parse CURL_RESPONSE (and
treat empty body as failure), and produce a clear error and exit if the request
timed out or returned a non-200 code.
| # Sets a key=value in the env file. Replaces in-place if present, appends if missing. | ||
| patch_var() { | ||
| local key="$1" value="$2" | ||
| if grep -q "^${key}=" "$ENV_FILE"; then | ||
| if [[ "$OSTYPE" == "darwin"* ]]; then | ||
| sed -i '' "s|^${key}=.*|${key}=\"${value}\"|" "$ENV_FILE" | ||
| else | ||
| sed -i "s|^${key}=.*|${key}=\"${value}\"|" "$ENV_FILE" | ||
| fi | ||
| else | ||
| echo "${key}=\"${value}\"" >> "$ENV_FILE" | ||
| fi | ||
| } |
There was a problem hiding this comment.
patch_var breaks on values containing |, &, or \.
The sed substitution uses | as the delimiter and embeds ${value} verbatim into the replacement. If any value ever contains |, &, \, or a newline, sed will misinterpret it. The static values here are safe, but CLIENT_SECRET comes from FusionAuth and could in principle contain such characters. Consider writing with a Python/awk helper or escaping the replacement, e.g.:
♻️ Proposed safer patching
patch_var() {
local key="$1" value="$2"
+ # Escape sed replacement metacharacters: \ & | and newlines
+ local esc
+ esc=$(printf '%s' "$value" | sed -e 's/[\\&|]/\\&/g' -e ':a;N;$!ba;s/\n/\\n/g')
if grep -q "^${key}=" "$ENV_FILE"; then
if [[ "$OSTYPE" == "darwin"* ]]; then
- sed -i '' "s|^${key}=.*|${key}=\"${value}\"|" "$ENV_FILE"
+ sed -i '' "s|^${key}=.*|${key}=\"${esc}\"|" "$ENV_FILE"
else
- sed -i "s|^${key}=.*|${key}=\"${value}\"|" "$ENV_FILE"
+ sed -i "s|^${key}=.*|${key}=\"${esc}\"|" "$ENV_FILE"
fi
else
echo "${key}=\"${value}\"" >> "$ENV_FILE"
fi
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Sets a key=value in the env file. Replaces in-place if present, appends if missing. | |
| patch_var() { | |
| local key="$1" value="$2" | |
| if grep -q "^${key}=" "$ENV_FILE"; then | |
| if [[ "$OSTYPE" == "darwin"* ]]; then | |
| sed -i '' "s|^${key}=.*|${key}=\"${value}\"|" "$ENV_FILE" | |
| else | |
| sed -i "s|^${key}=.*|${key}=\"${value}\"|" "$ENV_FILE" | |
| fi | |
| else | |
| echo "${key}=\"${value}\"" >> "$ENV_FILE" | |
| fi | |
| } | |
| # Sets a key=value in the env file. Replaces in-place if present, appends if missing. | |
| patch_var() { | |
| local key="$1" value="$2" | |
| # Escape sed replacement metacharacters: \ & | and newlines | |
| local esc | |
| esc=$(printf '%s' "$value" | sed -e 's/[\\&|]/\\&/g' -e ':a;N;$!ba;s/\n/\\n/g') | |
| if grep -q "^${key}=" "$ENV_FILE"; then | |
| if [[ "$OSTYPE" == "darwin"* ]]; then | |
| sed -i '' "s|^${key}=.*|${key}=\"${esc}\"|" "$ENV_FILE" | |
| else | |
| sed -i "s|^${key}=.*|${key}=\"${esc}\"|" "$ENV_FILE" | |
| fi | |
| else | |
| echo "${key}=\"${value}\"" >> "$ENV_FILE" | |
| fi | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/stacks/fusionauth-instance/justfile` around lines 101 - 113, The
patch_var function embeds unescaped ${value} into a sed substitution using | as
delimiter, which breaks on values containing |, &, \ or newlines (e.g.,
CLIENT_SECRET); update patch_var to safely write the key/value by either (A)
escaping the value before calling sed (replace backslashes with \\\\, ampersands
with \&, and the chosen delimiter | with \|, and handle newlines) and then use
sed -i with the escaped replacement, or (B) replace the sed path with a small
portable helper that writes/updates ENV_FILE using awk or a short Python script
that reads ENV_FILE, updates the matching key, and preserves other lines (use
the same key="$1" value="$2" interface and ensure append when missing). Ensure
both macOS (darwin) and Linux branches use the same safe approach and keep the
echo append fallback unchanged for missing keys.
| if ! pulumi stack output macroApplicationClientId -s local -C infra/stacks/fusionauth-instance &>/dev/null; then | ||
| echo "Warning: Pulumi local stack not found — skipping FusionAuth env patching." | ||
| echo " Run 'just setup' if this is a fresh checkout." | ||
| exit 0 | ||
| fi |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Soft-skip means run_local will proceed with stale/missing FusionAuth env vars.
When the Pulumi local stack is absent this exits 0, so run_local continues to docker_up without patching .env. On a fresh checkout that's the warning message, but users will still get cryptic auth failures at the app layer. Consider either (a) failing run_local here with a clearer message, or (b) only printing the skip warning when the caller isn't run_local (i.e., always fail when called standalone would be wrong too). At minimum, echo the consequence so users know services will be misconfigured:
if ! pulumi stack output macroApplicationClientId -s local -C infra/stacks/fusionauth-instance &>/dev/null; then
echo "Warning: Pulumi local stack not found — skipping FusionAuth env patching."
- echo " Run 'just setup' if this is a fresh checkout."
+ echo " Run 'just setup' if this is a fresh checkout."
+ echo " Services that depend on AUDIENCE/ISSUER/FUSIONAUTH_* will not work until you do."
exit 0
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if ! pulumi stack output macroApplicationClientId -s local -C infra/stacks/fusionauth-instance &>/dev/null; then | |
| echo "Warning: Pulumi local stack not found — skipping FusionAuth env patching." | |
| echo " Run 'just setup' if this is a fresh checkout." | |
| exit 0 | |
| fi | |
| if ! pulumi stack output macroApplicationClientId -s local -C infra/stacks/fusionauth-instance &>/dev/null; then | |
| echo "Warning: Pulumi local stack not found — skipping FusionAuth env patching." | |
| echo " Run 'just setup' if this is a fresh checkout." | |
| echo " Services that depend on AUDIENCE/ISSUER/FUSIONAUTH_* will not work until you do." | |
| exit 0 | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@justfile` around lines 67 - 71, The current soft-skip exits 0 which allows
run_local to continue into docker_up with missing/stale FusionAuth env vars;
change the behavior so the script fails fast: in the if block that checks pulumi
stack output (the macroApplicationClientId check) replace exit 0 with a non-zero
exit (e.g., exit 1) and update the echo text to explicitly state that services
will be misconfigured and that the user should run 'just setup' (mentioning
run_local and docker_up in the message) so callers like run_local don't proceed
with stale/missing FusionAuth env vars.
| NEEDS_STOP=false | ||
| if ! curl -s http://localhost:9011/api/status 2>/dev/null | grep -q '"Ok"'; then | ||
| echo "Starting FusionAuth temporarily to read config..." | ||
| docker compose up fusionauth -d --wait | ||
| NEEDS_STOP=true | ||
| fi | ||
| just infra/stacks/fusionauth-instance/insert_local_fusionauth_variables | ||
| if [ "$NEEDS_STOP" = true ]; then | ||
| echo "Stopping temporary FusionAuth..." | ||
| docker compose stop fusionauth | ||
| fi |
There was a problem hiding this comment.
Temporary FusionAuth won't be stopped if insert_local_fusionauth_variables fails.
With set -euo pipefail, a failure in the just infra/stacks/fusionauth-instance/insert_local_fusionauth_variables call (e.g., empty curl response, null client secret) aborts the script before reaching lines 80–83, leaving the container running despite NEEDS_STOP=true. Use a trap so cleanup runs regardless of exit status:
♻️ Proposed cleanup trap
# FusionAuth must be running to read the client secret
NEEDS_STOP=false
+ cleanup() {
+ if [ "$NEEDS_STOP" = true ]; then
+ echo "Stopping temporary FusionAuth..."
+ docker compose stop fusionauth || true
+ fi
+ }
+ trap cleanup EXIT
if ! curl -s http://localhost:9011/api/status 2>/dev/null | grep -q '"Ok"'; then
echo "Starting FusionAuth temporarily to read config..."
docker compose up fusionauth -d --wait
NEEDS_STOP=true
fi
just infra/stacks/fusionauth-instance/insert_local_fusionauth_variables
- if [ "$NEEDS_STOP" = true ]; then
- echo "Stopping temporary FusionAuth..."
- docker compose stop fusionauth
- fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@justfile` around lines 73 - 83, The script starts FusionAuth and sets
NEEDS_STOP=true but can exit early if just
infra/stacks/fusionauth-instance/insert_local_fusionauth_variables fails,
leaving the container running; add a shell trap that on EXIT checks NEEDS_STOP
and stops FusionAuth so cleanup always runs regardless of errors. Specifically,
after you set NEEDS_STOP=true (the block that runs docker compose up fusionauth
-d --wait), register a trap function that runs on EXIT and executes the same
cleanup (if [ "$NEEDS_STOP" = true ]; then docker compose stop fusionauth; fi),
and ensure you unset or adjust the trap only if you intentionally want to keep
the container running later.
| fi | ||
| # FusionAuth must be running to read the client secret | ||
| NEEDS_STOP=false | ||
| if ! curl -s http://localhost:9011/api/status 2>/dev/null | grep -q '"Ok"'; then |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add a timeout to the readiness probe.
curl -s has no default connect/read timeout, so if port 9011 is bound but unresponsive (e.g., a stale container or port collision with another process), this check blocks indefinitely and run_local will hang silently. A short timeout also makes the "not running → start temporarily" branch reliable on a cold host.
- if ! curl -s http://localhost:9011/api/status 2>/dev/null | grep -q '"Ok"'; then
+ if ! curl -sf --connect-timeout 2 --max-time 5 http://localhost:9011/api/status 2>/dev/null | grep -q '"Ok"'; then🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@justfile` at line 74, The readiness probe using curl in the run_local check
currently has no timeout; update the curl invocation (the line containing curl
-s http://localhost:9011/api/status | grep -q '"Ok"') to include a short
connect/read timeout (for example use --connect-timeout and/or --max-time) so
the check fails fast instead of hanging indefinitely when port 9011 is
unresponsive.
| docker compose up fusionauth -d --wait | ||
| NEEDS_STOP=true | ||
| fi | ||
| just infra/stacks/fusionauth-instance/insert_local_fusionauth_variables | ||
| if [ "$NEEDS_STOP" = true ]; then | ||
| echo "Stopping temporary FusionAuth..." | ||
| docker compose stop fusionauth |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate compose files and check for a fusionauth service definition.
fd -t f 'docker-compose*.y*ml' -H
echo "---"
fd -t f 'docker-compose*.y*ml' -H -x sh -c 'echo "=== {} ==="; grep -nE "^\s{2}fusionauth\s*:" "{}" || true'Repository: macro-inc/macro
Length of output: 377
🏁 Script executed:
sed -n '70,85p' justfileRepository: macro-inc/macro
Length of output: 583
Add -f infra/stacks/fusionauth-instance/docker-compose.yml to the docker compose commands on lines 76 and 82.
The docker compose up fusionauth -d --wait and docker compose stop fusionauth commands default to the root docker-compose.yml, which does not define a fusionauth service. Since insert_local_fusionauth_variables on line 80 targets the infra stack's FusionAuth instance, these commands must explicitly specify -f infra/stacks/fusionauth-instance/docker-compose.yml to match the setup used elsewhere in this file (e.g., setup_fusionauth target).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@justfile` around lines 76 - 82, Two docker compose invocations (the "docker
compose up fusionauth -d --wait" and "docker compose stop fusionauth") are
missing the explicit compose file; update both commands to include -f
infra/stacks/fusionauth-instance/docker-compose.yml so they target the
FusionAuth stack used by insert_local_fusionauth_variables and match the
setup_fusionauth usage elsewhere in the file.
Uh oh!
There was an error while loading. Please reload this page.