feat(hosting-cli): add reflex cloud gcp deploy for Cloud Run#6450
feat(hosting-cli): add reflex cloud gcp deploy for Cloud Run#6450
Conversation
Fetches a Dockerfile and bash deploy script from flexgen (`GET /api/v1/cli/gcp-cloud-run-manifest`), writes the Dockerfile into the user's source directory, prints the script, and runs it via bash after the user confirms. Pre-flights `bash`/`gcloud`/`docker` on PATH and an active gcloud account, and surfaces a clear message on 403 (Enterprise tier required). Deploy parameters (project, region, service name, AR repo, version) are passed via env vars to the script. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Merging this PR will not alter performance
Comparing Footnotes
|
Greptile SummaryThis PR adds a
Confidence Score: 3/5Mergeable after reviewing the env-inheritance risk; the P1 security finding warrants either documentation or a mitigation before shipping to users. The P1 security finding (full os.environ forwarded to a server-provided bash script) is a real, present risk on every invocation — not theoretical. There are no P0s (TLS + auth token + user confirmation provide meaningful mitigation), but the combination of the env-inheritance issue and the lack of a CI bypass flag makes the score land at 3 rather than the P1 ceiling of 4. packages/reflex-hosting-cli/src/reflex_cli/v2/gcp.py — specifically
|
| Filename | Overview |
|---|---|
| packages/reflex-hosting-cli/src/reflex_cli/v2/gcp.py | New module implementing reflex cloud gcp deploy; well-structured with pre-flight checks and user confirmation, but passes the full parent environment to a server-provided bash script (credential exposure risk) and lacks a --yes flag for CI use. |
| packages/reflex-hosting-cli/src/reflex_cli/v2/deployments.py | Minimal change: imports gcp_cli and registers it as the gcp sub-command on hosting_cli. No issues. |
| tests/units/reflex_cli/v2/test_gcp.py | 12 new unit tests covering the happy path, dry-run, abort, script failure propagation, tool-not-found paths, overwrite prompt, and 403 enterprise-tier messaging; coverage is good but no test exercises the real _run_deploy_script (which inherits the full env). |
Sequence Diagram
sequenceDiagram
participant User
participant CLI as reflex cloud gcp deploy
participant Hosting as hosting.get_authenticated_client
participant Flexgen as flexgen API
participant FS as Filesystem
participant Bash as bash -s
User->>CLI: gcp deploy --gcp-project ... [options]
CLI->>Hosting: get_authenticated_client(token, interactive=True)
Hosting-->>CLI: AuthenticatedClient(token)
CLI->>CLI: pre-flight checks (bash, gcloud, docker, gcloud auth list)
CLI->>Flexgen: GET /api/v1/cli/gcp-cloud-run-manifest (X-API-TOKEN header)
alt 403
Flexgen-->>CLI: 403 Forbidden
CLI-->>User: Enterprise tier required error, exit 1
else success
Flexgen-->>CLI: {dockerfile, deploy_command}
end
CLI-->>User: display Dockerfile path, deploy env vars, deploy script
alt --dry-run
CLI-->>User: display Dockerfile contents, exit 0
else interactive
CLI->>FS: write Dockerfile (prompt if exists)
CLI->>User: Run the deploy script now? [Y/n]
User->>CLI: y
CLI->>Bash: bash -s stdin=deploy_script env=os.environ+GCP vars cwd=source_dir
Bash->>User: stream stdout/stderr
Bash-->>CLI: exit code
CLI-->>User: success or error message
end
Reviews (1): Last reviewed commit: "feat(hosting-cli): add `reflex cloud gcp..." | Re-trigger Greptile
| env = os.environ.copy() | ||
| env.update(env_overrides) | ||
| try: | ||
| result = subprocess.run( | ||
| [bash_path, "-s"], | ||
| input=script, | ||
| text=True, | ||
| cwd=cwd, | ||
| env=env, | ||
| check=False, | ||
| stdout=sys.stdout, | ||
| stderr=sys.stderr, | ||
| ) | ||
| except OSError as ex: | ||
| console.error(f"Failed to launch bash: {ex}") | ||
| return 1 | ||
| return result.returncode |
There was a problem hiding this comment.
Full environment passed to server-provided script
os.environ.copy() forwards every parent process environment variable — including AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN, cloud-provider credential files, and SSH agent sockets — to a bash script whose content is determined server-side. If the flexgen endpoint is compromised or the response is tampered with, the script can silently exfiltrate all of these. The user sees the deploy script before confirming, but reviewers may miss nested variable expansions or obfuscated commands.
At minimum, document the environmental exposure prominently in the help text; consider stripping well-known credential variables (e.g. AWS_*, GITHUB_TOKEN, *_SECRET_*) from the copy before passing it, or maintaining an explicit allowlist of variables the script actually needs.
There was a problem hiding this comment.
maintaining an explicit allowlist of variables the script actually needs
this seems more ideal, both from a security and a documentation perspective
| if not _write_dockerfile(dockerfile_path, dockerfile, overwrite_dockerfile): | ||
| raise click.exceptions.Exit(1) | ||
|
|
||
| answer = console.ask("Run the deploy script now?", choices=["y", "n"], default="y") | ||
| if answer != "y": | ||
| console.warn("Aborted by user. The Dockerfile has been written for later use.") | ||
| raise click.exceptions.Exit(1) |
There was a problem hiding this comment.
No
--yes / --auto-approve flag for unattended use
Both the "Overwrite Dockerfile?" and "Run the deploy script now?" prompts unconditionally call console.ask, which blocks on stdin. Running gcp deploy from a CI/CD pipeline (e.g. GitHub Actions, Cloud Build) with no TTY will stall indefinitely. Adding a --yes boolean flag that short-circuits both prompts — analogous to the existing --overwrite-dockerfile flag — would make the command automation-friendly without changing the interactive default.
There was a problem hiding this comment.
probably should respect the --no-interactive flag that is already part of the hosting cli
| GCP_MANIFEST_ENDPOINT = "/api/v1/cli/gcp-cloud-run-manifest" | ||
|
|
||
| DOCKERFILE_NAME = "Dockerfile" |
There was a problem hiding this comment.
Hardcoded env-var and response-field key strings
Per the project rule, string literals used as identifiers or dictionary keys should be extracted into named constants. The five GCP environment variable keys and the two response field names ("dockerfile", "deploy_command") appear inline in multiple places, making typos hard to catch and refactoring error-prone. Extracting them to module-level constants (as is already done for GCP_MANIFEST_ENDPOINT and DOCKERFILE_NAME) keeps the file consistent.
| GCP_MANIFEST_ENDPOINT = "/api/v1/cli/gcp-cloud-run-manifest" | |
| DOCKERFILE_NAME = "Dockerfile" | |
| GCP_MANIFEST_ENDPOINT = "/api/v1/cli/gcp-cloud-run-manifest" | |
| DOCKERFILE_NAME = "Dockerfile" | |
| # Environment variable keys passed to the deploy script | |
| _ENV_GCP_PROJECT = "GCP_PROJECT" | |
| _ENV_GCP_REGION = "GCP_REGION" | |
| _ENV_SERVICE_NAME = "SERVICE_NAME" | |
| _ENV_AR_REPO = "AR_REPO" | |
| _ENV_VERSION = "VERSION" | |
| # Flexgen manifest response field names | |
| _FIELD_DOCKERFILE = "dockerfile" | |
| _FIELD_DEPLOY_COMMAND = "deploy_command" |
Rule Used: String literals that are used as identifiers or ke... (source)
Learned From
reflex-dev/flexgen#2170
ENG-9469
Summary
reflex cloud gcp deploycommand that fetches a Dockerfile + bash deploy script from flexgen (GET /api/v1/cli/gcp-cloud-run-manifest), writes the Dockerfile into the user's source directory, prints the script, and runs it via bash after a single y/n confirmation.bash/gcloud/dockeron PATH and an active gcloud account, with actionable error messages. Surfaces a clear "Enterprise tier required" message on a 403 from flexgen.--gcp-projectrequired;--region,--service-name,--ar-repo,--version) are passed to the script viaGCP_PROJECT,GCP_REGION,SERVICE_NAME,AR_REPO,VERSIONenvironment variables.--dry-runprints the manifest without writing or executing;--overwrite-dockerfileskips the existing-Dockerfile prompt.Auth model
X-API-Tokenflow (hosting.get_authenticated_client).gcloudlogin (detected viagcloud auth list --filter=status:ACTIVE).Test plan
uv run pytest tests/units/reflex_cli— 152 tests pass (12 new intests/units/reflex_cli/v2/test_gcp.py).uv run ruff check,uv run ruff format,uv run pyrightclean on changed files.uv run pre-commit run --files <changed>passes (ruff-format, ruff-check, codespell, update-pyi-files, pyright).🤖 Generated with Claude Code