Skip to content

feat(hosting-cli): add reflex cloud gcp deploy for Cloud Run#6450

Open
Kastier1 wants to merge 1 commit intomainfrom
cloudrun-deploy-cli
Open

feat(hosting-cli): add reflex cloud gcp deploy for Cloud Run#6450
Kastier1 wants to merge 1 commit intomainfrom
cloudrun-deploy-cli

Conversation

@Kastier1
Copy link
Copy Markdown
Contributor

@Kastier1 Kastier1 commented May 4, 2026

ENG-9469

Summary

  • New reflex cloud gcp deploy command 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.
  • Pre-flights bash/gcloud/docker on PATH and an active gcloud account, with actionable error messages. Surfaces a clear "Enterprise tier required" message on a 403 from flexgen.
  • Deploy parameters (--gcp-project required; --region, --service-name, --ar-repo, --version) are passed to the script via GCP_PROJECT, GCP_REGION, SERVICE_NAME, AR_REPO, VERSION environment variables. --dry-run prints the manifest without writing or executing; --overwrite-dockerfile skips the existing-Dockerfile prompt.

Auth model

  • Reflex hosting auth uses the existing X-API-Token flow (hosting.get_authenticated_client).
  • GCP credentials never leave the user's machine — the CLI relies on the user's existing gcloud login (detected via gcloud auth list --filter=status:ACTIVE).

Test plan

  • uv run pytest tests/units/reflex_cli — 152 tests pass (12 new in tests/units/reflex_cli/v2/test_gcp.py).
  • uv run ruff check, uv run ruff format, uv run pyright clean on changed files.
  • uv run pre-commit run --files <changed> passes (ruff-format, ruff-check, codespell, update-pyi-files, pyright).
  • End-to-end run against a real flexgen + GCP project (requires Enterprise-tier account).

🤖 Generated with Claude Code

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>
@Kastier1 Kastier1 requested a review from a team as a code owner May 4, 2026 20:21
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 4, 2026

Merging this PR will not alter performance

✅ 17 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing cloudrun-deploy-cli (a100bf8) with main (a84e29b)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 4, 2026

Greptile Summary

This PR adds a reflex cloud gcp deploy command that fetches a Dockerfile and bash deploy script from the flexgen API, writes the Dockerfile to the user's source directory, and executes the script via bash -s after a user confirmation prompt. Pre-flight checks for bash, gcloud, and docker on PATH and an active gcloud login are well-implemented, and error handling (including the 403 → Enterprise tier message) is thorough.

  • Security: _run_deploy_script forwards os.environ.copy() — including any cloud credentials, API keys, or SSH tokens present in the parent shell — to a bash script whose content is controlled server-side. A compromised or MITM'd flexgen response can silently exfiltrate these even if the script appears benign to the reviewing user.
  • Usability: Both the "Overwrite Dockerfile?" and "Run the deploy script now?" prompts block on stdin with no bypass flag, making the command unusable in non-interactive CI/CD pipelines.

Confidence Score: 3/5

Mergeable 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 _run_deploy_script and the missing --yes flag.

Security Review

  • Full environment passed to remote script (gcp.py _run_deploy_script): os.environ.copy() is forwarded to a bash script fetched from a remote server. Any credentials, API keys, or cloud tokens present in the user's environment are accessible to the script. If flexgen is compromised or the TLS session is tampered with, this enables silent credential exfiltration.
  • Remote code execution by design: The feature intentionally fetches and runs server-supplied bash code. There is no cryptographic signature or hash verification of the script content; trust relies entirely on TLS and Reflex API authentication. This is the intended architecture but represents a systemic RCE risk surface that should be clearly documented for users.

Important Files Changed

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
Loading

Reviews (1): Last reviewed commit: "feat(hosting-cli): add `reflex cloud gcp..." | Re-trigger Greptile

Comment on lines +348 to +364
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security 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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maintaining an explicit allowlist of variables the script actually needs

this seems more ideal, both from a security and a documentation perspective

Comment on lines +185 to +191
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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably should respect the --no-interactive flag that is already part of the hosting cli

Comment on lines +25 to +27
GCP_MANIFEST_ENDPOINT = "/api/v1/cli/gcp-cloud-run-manifest"

DOCKERFILE_NAME = "Dockerfile"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Suggested change
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants