-
Notifications
You must be signed in to change notification settings - Fork 0
fix: npx pre-flight, JSON parse safety, wave redirect reason #68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| import argparse | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import shutil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import stat | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import subprocess | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -54,7 +55,12 @@ def op_get(path): | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with urllib.request.urlopen(req, timeout=30) as r: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return json.loads(r.read()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body = r.read() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return json.loads(body) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except json.JSONDecodeError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"ERROR: 1Password Connect returned non-JSON: {body[:200]}", file=sys.stderr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sys.exit(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except urllib.error.HTTPError as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body = e.read().decode("utf-8", errors="replace")[:200] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"ERROR: 1Password Connect returned HTTP {e.code}: {body}", file=sys.stderr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -76,7 +82,12 @@ def neon_post(path, api_key): | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with urllib.request.urlopen(req, timeout=30) as r: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return json.loads(r.read()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body = r.read() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return json.loads(body) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except json.JSONDecodeError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"ERROR: Neon API returned non-JSON: {body[:200]}", file=sys.stderr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sys.exit(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+90
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except urllib.error.HTTPError as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body = e.read().decode("utf-8", errors="replace")[:200] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"ERROR: Neon API returned HTTP {e.code}: {body}", file=sys.stderr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -143,6 +154,13 @@ def deploy_secret(database_url, wrangler_config, env_name): | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| print("ERROR: OP_CONNECT_TOKEN environment variable is required", file=sys.stderr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sys.exit(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Pre-flight: verify npx/wrangler available BEFORE rotating the password. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # If wrangler is missing, rotating would leave a new password that can't be deployed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not shutil.which("npx"): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print("ERROR: npx not found on PATH. Cannot deploy secrets to Cloudflare Workers.", file=sys.stderr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print("ABORTING before password rotation to avoid partial failure.", file=sys.stderr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sys.exit(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+157
to
+163
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Pre-flight: verify npx/wrangler available BEFORE rotating the password. | |
| # If wrangler is missing, rotating would leave a new password that can't be deployed. | |
| if not shutil.which("npx"): | |
| print("ERROR: npx not found on PATH. Cannot deploy secrets to Cloudflare Workers.", file=sys.stderr) | |
| print("ABORTING before password rotation to avoid partial failure.", file=sys.stderr) | |
| sys.exit(1) | |
| # Pre-flight: verify npx and wrangler are available BEFORE rotating the password. | |
| # If wrangler is missing, rotating would leave a new password that can't be deployed. | |
| if not shutil.which("npx"): | |
| print("ERROR: npx not found on PATH. Cannot deploy secrets to Cloudflare Workers.", file=sys.stderr) | |
| print("ABORTING before password rotation to avoid partial failure.", file=sys.stderr) | |
| sys.exit(1) | |
| try: | |
| # Ensure that `npx wrangler` is runnable before proceeding. | |
| subprocess.run( | |
| ["npx", "wrangler", "--version"], | |
| stdout=subprocess.DEVNULL, | |
| stderr=subprocess.DEVNULL, | |
| check=True, | |
| timeout=10, | |
| ) | |
| except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError): | |
| print("ERROR: 'npx wrangler' is not available or failed to run. Cannot deploy secrets to Cloudflare Workers.", file=sys.stderr) | |
| print("ABORTING before password rotation to avoid partial failure.", file=sys.stderr) | |
| sys.exit(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the JSONDecodeError path,
bodyis bytes and the error message printsbody[:200]directly, which ends up as ab'...'repr and may include non-printable characters. For consistency with the HTTPError branch (which decodes witherrors="replace") and to keep logs readable/safe, decode/sanitize the snippet (and consider including response status/content-type instead of dumping raw bytes).