Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ jobs:

- name: Install reflex-docs dependencies
working-directory: ./docs/app
run: sfw uv sync
run: sfw uv sync --frozen

- name: Init Website for reflex-docs
working-directory: ./docs/app
Expand Down
32 changes: 32 additions & 0 deletions packages/reflex-hosting-cli/src/reflex_cli/v2/gcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
referenced with ``gcloud builds submit --config=...``) — the user's project
tree is never modified. The script reads its parameters from environment
variables (GCP_PROJECT, GCP_REGION, SERVICE_NAME, AR_REPO, VERSION,
CLOUD_RUN_CPU, CLOUD_RUN_MEMORY, CLOUD_RUN_MIN_INSTANCES,
REFLEX_CLOUDBUILD_YAML).
"""

Expand Down Expand Up @@ -37,6 +38,9 @@
ENV_SERVICE_NAME = "SERVICE_NAME"
ENV_AR_REPO = "AR_REPO"
ENV_VERSION = "VERSION"
ENV_CPU = "CLOUD_RUN_CPU"
ENV_MEMORY = "CLOUD_RUN_MEMORY"
ENV_MIN_INSTANCES = "CLOUD_RUN_MIN_INSTANCES"
# Path to the Cloud Build config file written by the CLI. The rewritten
# deploy script references it as ``--config="${REFLEX_CLOUDBUILD_YAML}"``.
ENV_REFLEX_CLOUDBUILD_YAML = "REFLEX_CLOUDBUILD_YAML"
Expand Down Expand Up @@ -136,6 +140,28 @@
default=None,
help="The image version tag (sets VERSION). Defaults to a UTC timestamp.",
)
@click.option(
"--cpu",
"cpu",
default="1",
show_default=True,
help="Cloud Run CPU allocation, e.g. '1', '2', '4' (sets CLOUD_RUN_CPU).",
)
@click.option(
"--memory",
"memory",
default="1Gi",
show_default=True,
help="Cloud Run memory allocation, e.g. '512Mi', '1Gi', '2Gi' (sets CLOUD_RUN_MEMORY).",
)
@click.option(
"--min-instances",
"min_instances",
default=1,
show_default=True,
type=click.IntRange(min=0),
help="Minimum number of Cloud Run instances to keep warm (sets CLOUD_RUN_MIN_INSTANCES). Set to 0 to scale to zero.",
)
Comment thread
Kastier1 marked this conversation as resolved.
@click.option(
"--source",
"source_dir",
Expand Down Expand Up @@ -170,6 +196,9 @@ def deploy_command(
service_name: str,
ar_repo: str,
version_tag: str | None,
cpu: str,
memory: str,
min_instances: int,
source_dir: str,
token: str | None,
interactive: bool,
Expand Down Expand Up @@ -252,6 +281,9 @@ def deploy_command(
ENV_SERVICE_NAME: service_name,
ENV_AR_REPO: ar_repo,
ENV_VERSION: version_value,
ENV_CPU: cpu,
ENV_MEMORY: memory,
ENV_MIN_INSTANCES: str(min_instances),
}

console.info("Received deploy manifest from Reflex.")
Expand Down
73 changes: 73 additions & 0 deletions tests/units/reflex_cli/v2/test_gcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,79 @@ def capture(**kwargs):
assert get_mock.call_args.kwargs["headers"] == {"X-API-TOKEN": "fake-token"}


def test_gcp_deploy_forwards_resource_flags(mocker: MockFixture, tmp_path: Path):
"""--cpu / --memory / --min-instances flow through to the deploy script env."""
run_mock = _patch_environment(mocker)
_mock_manifest_response(mocker)

result = runner.invoke(
hosting_cli,
[
"deploy",
"--gcp",
"--gcp-project",
"p",
"--source",
str(tmp_path),
"--cpu",
"4",
"--memory",
"2Gi",
"--min-instances",
"0",
],
input="y\n",
)

assert result.exit_code == 0, result.output
env_overrides = run_mock.call_args.kwargs["env_overrides"]
assert env_overrides["CLOUD_RUN_CPU"] == "4"
assert env_overrides["CLOUD_RUN_MEMORY"] == "2Gi"
assert env_overrides["CLOUD_RUN_MIN_INSTANCES"] == "0"


def test_gcp_deploy_resource_flags_have_defaults(mocker: MockFixture, tmp_path: Path):
"""When the user omits the new flags, defaults reach the deploy script env."""
run_mock = _patch_environment(mocker)
_mock_manifest_response(mocker)

result = runner.invoke(
hosting_cli,
["deploy", "--gcp", "--gcp-project", "p", "--source", str(tmp_path)],
input="y\n",
)

assert result.exit_code == 0, result.output
env_overrides = run_mock.call_args.kwargs["env_overrides"]
assert env_overrides["CLOUD_RUN_CPU"] == "1"
assert env_overrides["CLOUD_RUN_MEMORY"] == "1Gi"
assert env_overrides["CLOUD_RUN_MIN_INSTANCES"] == "1"


def test_gcp_deploy_rejects_negative_min_instances(mocker: MockFixture, tmp_path: Path):
"""--min-instances is IntRange(min=0); negative values fail at the CLI layer."""
run_mock = _patch_environment(mocker)
_mock_manifest_response(mocker)

result = runner.invoke(
hosting_cli,
[
"deploy",
"--gcp",
"--gcp-project",
"p",
"--source",
str(tmp_path),
"--min-instances",
"-1",
],
)

assert result.exit_code == 2
assert "min-instances" in result.output.lower()
assert run_mock.call_count == 0


def test_gcp_deploy_aborts_on_no(mocker: MockFixture, tmp_path: Path):
"""Declining the run prompt aborts before any staging."""
run_mock = _patch_environment(mocker)
Expand Down
Loading