Add production post-deploy validation script and README guidance for Cloudflare Tunnel#83
Add production post-deploy validation script and README guidance for Cloudflare Tunnel#83MEECHAIN1 wants to merge 8 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🚅 Deployed to the MeeChain-Connect-pr-83 environment in meechain 5 services not affected by this PR
|
📝 WalkthroughWalkthroughPR นี้นำเสนอโครงสร้างพื้นฐานสำหรับตรวจสอบหลังการปรับใช้ระบบโปรดักชั่น: สคริปต์ Bash ใหม่ ( This PR introduces production post-deployment validation infrastructure: a new Bash script ( ChangesProduction Validation Checkpoint / จุดตรวจสอบโปรดักชั่น
Sequence DiagramsequenceDiagram
actor User as ผู้ใช้ / User
participant Script as test-production.sh
participant App as MeeChain App / MeeChain แอป
participant RPC as RPC Proxy
participant DNS as DNS Resolver
participant CF as Cloudflare Access
User->>Script: npm run test:prod [--options]
Script->>App: GET /health (root health check)
App-->>Script: 200 + {status, version}
Script->>App: GET /api/health
App-->>Script: 200 + {status, chainId}
Script->>RPC: GET /health
RPC-->>Script: 200 + {chainId, mode}
Script->>RPC: POST eth_chainId (JSON-RPC call)
RPC-->>Script: 200 + {result: "0x..."}
Script->>RPC: POST eth_blockNumber
RPC-->>Script: 200 + {result: "0x..."}
Script->>DNS: Resolve DNS name / แก้ไขชื่อ DNS
DNS-->>Script: A/CNAME records
Script->>DNS: Verify CNAME target / ตรวจสอบเป้าหมาย CNAME
DNS-->>Script: Expected target confirmed / ยืนยันเป้าหมายที่คาดไว้
alt Cloudflare Access Required / จำเป็นต้องมี Cloudflare Access
Script->>CF: Include CF_ACCESS tokens in headers / รวมโทเค็น CF_ACCESS ในส่วนหัว
CF-->>Script: Access granted / ได้รับอนุญาตการเข้าถึง
end
Script-->>User: ✓ All checks passed / ✓ ผ่านการตรวจสอบทั้งหมด
Estimated Code Review Effort🎯 3 (Moderate / ปานกลาง) | ⏱️ ~25 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts❌ Error resolving conflicts.
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 |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
meechain-connect | 33be238 | May 06 2026, 11:06 AM |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/test-production.sh`:
- Around line 308-320: The join of multiple CNAME targets uses "$(printf '%s'
"$targets" | paste -sd ', ' -)" which incorrectly treats ', ' as alternating
single-character delimiters; update the message construction so targets are
joined with the literal string ", " instead of alternating delimiters — locate
the expression used inside the message variable (around the variables targets
and message and before the EXPECTED_CNAME_STRICT check) and replace it with a
join that produces "a, b, c" (for example: join with paste -sd',' - then insert
spaces, or use awk/printf to append ", " between entries and trim the trailing
separator) so the fail/warn output shows properly spaced comma-separated
targets.
- Around line 275-283: The assertion in check_network_config() that compares
"data.rpcUrls && data.rpcUrls[0]" to "${BASE_URL}/rpc" fails for secondary/proxy
URLs; change it to use an override ENV var (EXPECTED_RPC_URL) when present:
compute expected_rpc_url="${EXPECTED_RPC_URL:-${BASE_URL}/rpc}" and use that in
the assert_json_field_equals call for "network first rpcUrl"; keep BASE_URL
logic and other checks unchanged so callers can set EXPECTED_RPC_URL when
validating standby/Cloudflare-tunnel URLs.
- Around line 286-290: The normalize_dns_name function uses bash-only lowercase
expansion `${name,,}` which fails on macOS bash 3.2; update normalize_dns_name
to perform lowercase conversion using a POSIX-safe tr pipeline instead (preserve
the existing removal of a trailing dot via `${name%.}` then pipe the resulting
value through tr '[:upper:]' '[:lower:]'), and ensure the function still prints
the normalized name with printf '%s\n' so callers like those using
EXPECTED_CNAME_TARGET behave identically.
🪄 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: CHILL
Plan: Pro
Run ID: 98c8a11b-dd4f-4622-be77-f26a493a3b53
📒 Files selected for processing (2)
README.mdscripts/test-production.sh
| normalize_dns_name() { | ||
| local name="$1" | ||
| name="${name%.}" | ||
| printf '%s\n' "${name,,}" | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does bash 3.2 on macOS support ${var,,} lowercase parameter expansion?
💡 Result:
No. Bash 3.2 on macOS does not support the lowercase parameter expansion syntax ${var,,}. In bash, the case-modification parameter expansion forms ${parameter,pattern} and ${parameter,,pattern} (including ${parameter,,} to lowercase all matched characters) are defined in the “Shell Parameter Expansion” section of the Bash Reference Manual [1]. Those case-modification operators were introduced with Bash 4.0+; accordingly, they are not available in older Bash versions such as 3.2.x shipped on macOS [2][3]. What you’ll see in practice on Bash 3.2 is an error like “bad substitution” when trying ${var,,}, while Bash 4.0+ accepts it [4]. For macOS default Bash 3.2, use a different approach such as tr (e.g., echo "$VAR" | tr '[:upper:]' '[:lower:]') as suggested for Bash 3 [5].
Citations:
- 1: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
- 2: Ralph loop crashes on macOS due to Bash 3.2 incompatibility (${var,,} syntax) LarsCowe/bmalph#110
- 3: https://stackoverflow.com/questions/2264428/how-to-convert-a-string-to-lower-case-in-bash
- 4: https://stackoverflow.com/questions/29855489/string-replacement-to-lowercase-in-bash-4-3-33-bad-substitution-error/30402648
- 5: https://scriptingosx.com/2019/12/upper-or-lower-casing-strings-in-bash-and-zsh/
🏁 Script executed:
# First, check if the file exists and get its line count
wc -l scripts/test-production.shRepository: MEECHAIN1/MeeChain-Connect
Length of output: 101
🏁 Script executed:
# Read the relevant sections of the file to verify the claims
sed -n '275,320p' scripts/test-production.sh | cat -nRepository: MEECHAIN1/MeeChain-Connect
Length of output: 2178
${name,,} ไม่รองรับใน bash 3.2 ที่มาพร้อม macOS ค่าเริ่มต้น
English: The ${var,,} parameter expansion for lowercase conversion requires bash 4.0+. Since macOS ships with /bin/bash 3.2.57 by default, invoking normalize_dns_name when EXPECTED_CNAME_TARGET is set will abort with bad substitution. Lines 300 and 302 in the same file already use the POSIX-portable tr '[:upper:]' '[:lower:]' idiom for the same operation.
ภาษาไทย: ${name,,} ต้องการ bash 4.0 ขึ้นไป ขณะที่ macOS ใช้ /bin/bash 3.2.57 เป็นค่าเริ่มต้น ทำให้เมื่อ EXPECTED_CNAME_TARGET ถูกตั้งค่าและเรียก normalize_dns_name จะล้มเหลวด้วย bad substitution ควรใช้ tr '[:upper:]' '[:lower:]' เหมือนที่ใช้ในบรรทัด 300 และ 302 แทน
🐛 แนวทางแก้ไข / Proposed fix
normalize_dns_name() {
local name="$1"
name="${name%.}"
- printf '%s\n' "${name,,}"
+ printf '%s\n' "$(printf '%s' "$name" | tr '[:upper:]' '[:lower:]')"
}📝 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.
| normalize_dns_name() { | |
| local name="$1" | |
| name="${name%.}" | |
| printf '%s\n' "${name,,}" | |
| } | |
| normalize_dns_name() { | |
| local name="$1" | |
| name="${name%.}" | |
| printf '%s\n' "$(printf '%s' "$name" | tr '[:upper:]' '[:lower:]')" | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/test-production.sh` around lines 286 - 290, The normalize_dns_name
function uses bash-only lowercase expansion `${name,,}` which fails on macOS
bash 3.2; update normalize_dns_name to perform lowercase conversion using a
POSIX-safe tr pipeline instead (preserve the existing removal of a trailing dot
via `${name%.}` then pipe the resulting value through tr '[:upper:]'
'[:lower:]'), and ensure the function still prints the normalized name with
printf '%s\n' so callers like those using EXPECTED_CNAME_TARGET behave
identically.
| fi | ||
|
|
||
| local message="CNAME target for $host does not visibly match $EXPECTED_CNAME_TARGET" | ||
| if [[ -n "$targets" ]]; then | ||
| message="$message (observed: $(printf '%s' "$targets" | paste -sd ', ' -))" | ||
| else | ||
| message="$message (no CNAME visible; Cloudflare orange-cloud proxy can hide the target behind edge A/AAAA records)" | ||
| fi | ||
|
|
||
| if [[ "$EXPECTED_CNAME_STRICT" == "1" ]]; then | ||
| fail "$message" | ||
| fi | ||
| warn "$message" |
There was a problem hiding this comment.
paste -sd', ' วนซ้ำ delimiter แยกกัน , และ — ผลลัพธ์ผิดเมื่อมีหลาย CNAME targets
English: paste -d treats its argument as a list of individual delimiter characters that cycle in turn. So -d', ' means , is delimiter #1 and (space) is delimiter #2. For two targets the output is a,b (no space), and for three targets the paste command will cycle: , then alternating, producing a,b c instead of the intended a, b, c. While a single CNAME target is the overwhelming norm, the formatter should still be correct.
ภาษาไทย: paste -d', ' ใช้ , และ เป็น delimiter สองตัวสลับกัน ไม่ใช่สตริง ", " ทั้งก้อน สำหรับ 2 targets จะได้ a,b (ไม่มีช่องว่าง) สำหรับ 3 targets จะได้ a,b c แทนที่จะเป็น a, b, c
🐛 แนวทางแก้ไข / Proposed fix
- message="$message (observed: $(printf '%s' "$targets" | paste -sd ', ' -))"
+ message="$message (observed: $(printf '%s\n' $targets | paste -sd ',' - | sed 's/,/, /g'))"หรือวิธีที่ง่ายกว่า / alternatively:
- message="$message (observed: $(printf '%s' "$targets" | paste -sd ', ' -))"
+ message="$message (observed: $(printf '%s\n' $targets | tr '\n' ',' | sed 's/,$//' | sed 's/,/, /g'))"📝 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.
| fi | |
| local message="CNAME target for $host does not visibly match $EXPECTED_CNAME_TARGET" | |
| if [[ -n "$targets" ]]; then | |
| message="$message (observed: $(printf '%s' "$targets" | paste -sd ', ' -))" | |
| else | |
| message="$message (no CNAME visible; Cloudflare orange-cloud proxy can hide the target behind edge A/AAAA records)" | |
| fi | |
| if [[ "$EXPECTED_CNAME_STRICT" == "1" ]]; then | |
| fail "$message" | |
| fi | |
| warn "$message" | |
| fi | |
| local message="CNAME target for $host does not visibly match $EXPECTED_CNAME_TARGET" | |
| if [[ -n "$targets" ]]; then | |
| message="$message (observed: $(printf '%s\n' $targets | paste -sd ',' - | sed 's/,/, /g'))" | |
| else | |
| message="$message (no CNAME visible; Cloudflare orange-cloud proxy can hide the target behind edge A/AAAA records)" | |
| fi | |
| if [[ "$EXPECTED_CNAME_STRICT" == "1" ]]; then | |
| fail "$message" | |
| fi | |
| warn "$message" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/test-production.sh` around lines 308 - 320, The join of multiple
CNAME targets uses "$(printf '%s' "$targets" | paste -sd ', ' -)" which
incorrectly treats ', ' as alternating single-character delimiters; update the
message construction so targets are joined with the literal string ", " instead
of alternating delimiters — locate the expression used inside the message
variable (around the variables targets and message and before the
EXPECTED_CNAME_STRICT check) and replace it with a join that produces "a, b, c"
(for example: join with paste -sd',' - then insert spaces, or use awk/printf to
append ", " between entries and trim the trailing separator) so the fail/warn
output shows properly spaced comma-separated targets.
|
|
||
| json_field() { | ||
| local expression="$1" | ||
| node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); const value=($expression); if (value === undefined || value === null) process.exit(3); console.log(typeof value === 'object' ? JSON.stringify(value) : value);" "$BODY" |
There was a problem hiding this comment.
| node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); const value=($expression); if (value === undefined || value === null) process.exit(3); console.log(typeof value === 'object' ? JSON.stringify(value) : value);" "$BODY" | |
| node -e "const fs=require('fs'); const obj=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); const data = (typeof obj.data !== 'undefined') ? obj.data : obj; const value=($expression); if (value === undefined || value === null) process.exit(3); console.log(typeof value === 'object' ? JSON.stringify(value) : value);" "$BODY" |
Test script expects API responses wrapped in 'data' property, but server returns unwrapped responses, causing all JSON field assertions to fail with "missing or invalid JSON field" errors
|
Note Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
2 similar comments
|
Note Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
|
Note Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
|
✅ Unit tests committed locally. Commit: |
|
Note Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
test/test-production-script.test.js (1)
277-280: ⚡ Quick winหลีกเลี่ยงการรันสคริปต์ซ้ำใน assertion เดียว / Avoid running the script twice in one assertion
Line 279 เรียก
runScriptอีกรอบเพื่อเอาstderrไปใส่ข้อความ assert ทำให้เทสต์ช้าลงและเพิ่มโอกาส flaky โดยไม่จำเป็น.
Line 279 executes a second full script run just to build the assert message, which adds avoidable runtime and flakiness risk.♻️ Proposed refactor
- const { status, stdout } = runScript([base, '--skip-network']); - assert.strictEqual(status, 0, `Expected exit 0; stderr: ${runScript([base, '--skip-network']).stderr}`); + const { status, stdout, stderr } = runScript([base, '--skip-network']); + assert.strictEqual(status, 0, `Expected exit 0; stderr: ${stderr}`);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/test-production-script.test.js` around lines 277 - 280, The test is calling runScript twice unnecessarily - once to capture status and stdout, and again inside the assertion message to get stderr. Store the complete result of runScript in a single variable that captures status, stdout, and stderr together, then reuse that stored stderr value in the assertion message instead of executing runScript a second time. This will improve test performance and reduce flakiness risk.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/test-production-script.test.js`:
- Around line 114-120: The startMockServer function has a type mismatch: the
default parameter handlerOverrides is set to an empty object {} on line 114, but
line 119 treats it as a function by calling handlerOverrides(base). When
startMockServer is called without arguments, this causes a TypeError. Change the
default parameter from an empty object to a function that returns an empty
object, so the default should be handlerOverrides = () => ({}) instead of
handlerOverrides = {}, ensuring it can be safely called as a function regardless
of whether an argument is provided.
---
Nitpick comments:
In `@test/test-production-script.test.js`:
- Around line 277-280: The test is calling runScript twice unnecessarily - once
to capture status and stdout, and again inside the assertion message to get
stderr. Store the complete result of runScript in a single variable that
captures status, stdout, and stderr together, then reuse that stored stderr
value in the assertion message instead of executing runScript a second time.
This will improve test performance and reduce flakiness risk.
🪄 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: CHILL
Plan: Pro
Run ID: 509e4373-fd4f-484d-bbd1-f39f4fe9a274
📒 Files selected for processing (1)
test/test-production-script.test.js
|
✅ Created PR with unit tests: #99 |
|
Note Docstrings generation - SUCCESS |
Docstrings generation was requested by @MEECHAIN1. * #83 (comment) The following files were modified: * `scripts/test-production.sh`
Docstrings generation was requested by @coderabbitai[bot]. The following files were modified: * `scripts/test-production.sh` These files were ignored: * `test/test-production-script.test.js` These file types are not supported: * `README.md`
|
Request timed out after 900000ms (requestId=958bc22e-6223-4e68-87ff-a6e60f3744be) |
|
Request timed out after 900000ms (requestId=1930472a-1c97-429a-b4d5-4cd0db32fb69) |
|
Note Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
Docstrings generation was requested by @coderabbitai[bot]. The following files were modified: * `scripts/test-production.sh` These files were ignored: * `test/test-production-script.test.js` These file types are not supported: * `README.md`
|
✅ Created PR with unit tests: #101 |
Docstrings generation was requested by @MEECHAIN1. The following files were modified: * `scripts/test-production.sh` These file types are not supported: * `README.md`
Unit test generation was requested by @MEECHAIN1. * #83 (comment) The following files were modified: * `test/test-production-script.test.js`
…docstrings/b1ae117 Signed-off-by: ธณวัฒน์ ประวันตา <pouaun2499@gmail.com>
Docstrings generation was requested by @MEECHAIN1. * #83 (comment) The following files were modified: * `scripts/test-production.sh` <details> <summary>These file types are not supported</summary> * `README.md` </details> <details> <summary>ℹ️ Note</summary><blockquote> CodeRabbit cannot perform edits on its own pull requests yet. </blockquote></details>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
test/test-production-script.test.js (1)
114-120:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winค่าเริ่มต้น
handlerOverridesยังไม่สอดคล้องกับการเรียกใช้ /handlerOverridesdefault is still incompatible with invocationตอนนี้ Line 114 ตั้งค่าเริ่มต้นเป็น object แต่ Line 119 เรียกเป็นฟังก์ชัน (
handlerOverrides(base)) ทำให้มีโอกาสเกิดTypeErrorหากเรียกstartMockServer()โดยไม่ส่งอาร์กิวเมนต์
At Line 114 the default is an object, but Line 119 invokes it as a function, sostartMockServer()can throwTypeErrorwhen called without overrides.Proposed minimal fix
-function startMockServer(handlerOverrides = {}) { +function startMockServer(handlerOverrides = () => ({})) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/test-production-script.test.js` around lines 114 - 120, The startMockServer function has a default parameter mismatch where handlerOverrides is initialized as an empty object on line 114, but it is invoked as a function on line 119 with handlerOverrides(base). Change the default value of the handlerOverrides parameter from an object literal {} to a function that returns an empty object, so it matches the function invocation pattern used in the handlers assignment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/test-production.sh`:
- Around line 95-112: The functions pass(), fail(), warn(), and info() are
defined twice in the script—once before line 95 and again after line 102. The
first set of definitions is completely shadowed by the second set, making it
dead code. Remove the first set of duplicate function definitions (pass, fail,
warn, and info) that appear before the second set, keeping only one complete
definition of each function to eliminate the redundancy and clean up the script.
---
Duplicate comments:
In `@test/test-production-script.test.js`:
- Around line 114-120: The startMockServer function has a default parameter
mismatch where handlerOverrides is initialized as an empty object on line 114,
but it is invoked as a function on line 119 with handlerOverrides(base). Change
the default value of the handlerOverrides parameter from an object literal {} to
a function that returns an empty object, so it matches the function invocation
pattern used in the handlers assignment.
🪄 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: CHILL
Plan: Pro
Run ID: 24639983-96cd-45a5-bf23-8b978ae9abe7
📒 Files selected for processing (2)
scripts/test-production.shtest/test-production-script.test.js
|
❌ Failed to resolve merge conflicts Unexpected error: Sandbox execution failed: failed Please resolve conflicts manually. |
Motivation
docker compose up -d.meechain-connect) for stable production hostnames instead of ephemeral quick tunnels.Description
scripts/test-production.shthat performs DNS resolution checks, optional CNAME target verification, app shell content check,/health,/api/health,/rpc/health, RPC JSON-RPC calls (eth_chainId,eth_blockNumber), and/api/networkand/api/web3/statuschecks.--insecure,--skip-network,--skip-app,--also,--timeoutand env varsEXPECTED_CNAME_TARGET,EXPECTED_CNAME_STRICT,EXPECT_UPSTREAM_CONNECTED,CF_ACCESS_CLIENT_ID, andCF_ACCESS_CLIENT_SECRETfor Cloudflare Access headers.README.mdwith a new "Production post-deploy validation" section, usage examples fornpm run test:prodandbash scripts/test-production.sh, and detailed guidance on switching DNS to a named Cloudflare Tunnel and handling quick-tunnel caveats.Testing
Codex Task
Summary by CodeRabbit
บันทึกประจำรุ่น
Documentation
Tests