Skip to content

ENG-11433 Move reflex deploy out of the framework into the Cloud CLI - #6924

Open
FarhanAliRaza wants to merge 3 commits into
reflex-dev:mainfrom
FarhanAliRaza:farhan/eng-11433-move-reflex-deploy-command-out-of-the-reflex-package-into
Open

ENG-11433 Move reflex deploy out of the framework into the Cloud CLI#6924
FarhanAliRaza wants to merge 3 commits into
reflex-dev:mainfrom
FarhanAliRaza:farhan/eng-11433-move-reflex-deploy-command-out-of-the-reflex-package-into

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Closes ENG-11433.

reflex deploy lived in reflex/reflex.py, so cloud-specific code shipped inside the open-source framework. The implementation now lives in reflex-hosting-cli.

What changed

The command moved. reflex/reflex.py loses 245 lines; reflex_cli/v2/deploy.py gains 236. The framework contains no cloud code.

The import is guarded. reflex/reflex.py imports the moved command and falls back when it cannot:

try:
    from reflex_cli.v2.deploy import deploy
    from reflex_cli.v2.deployments import hosting_cli
except ImportError:
    cli.add_command(_missing_command("deploy"), name="deploy")
    cli.add_command(_missing_command("cloud"), name="cloud")
else:
    ...
    cli.add_command(deploy, name="deploy")
    cli.add_command(hosting_cli, name="cloud")

The typer conversion for cloud is unchanged from before; it just moved inside the else.

A missing package no longer fails opaquely. The stand-in is a real command of the same name. ignore_unknown_options plus a click.UNPROCESSED argument make it swallow every flag, so reflex deploy --app-name demo reports pip install reflex-hosting-cli instead of dying on "No such option". login and logout use the same helper on ImportError.

Standalone importability, fixed. deploy.py imported reflex.utils.cli_options, but reflex is not a dependency of reflex-hosting-cli, so importing the package on its own raised ModuleNotFoundError. The shared click options moved to reflex_base.utils.cli_options (adding click >=8.2 to reflex-base); reflex/utils/cli_options.py re-exports them for its existing callers.

Answers to the issue's open questions

How is it wired up? A guarded import, with reflex-hosting-cli still a required dependency. pip install reflex gives a working reflex deploy and nothing changes for existing users. Dropping the dependency would break them, which the "no breaking changes" bullet forbids.

An earlier revision of this PR used a reflex.cli_commands entry point group instead. It was removed: the only thing it bought over a direct import was that the framework did not write the name reflex_cli in its source, which did not justify 173 lines of discovery machinery and an indirection that hides where reflex deploy comes from. The cloud code is out of the framework either way, which is what the issue asks for.

What if the Cloud CLI is not installed? The command name still exists and reports what to install, as above.

No UX change

Verified rather than assumed: the pre-move command is reconstructed from the merge-base copy of reflex/reflex.py and diffed against the live one. All 23 parameters match on name, opts, secondary opts, type, default, multiple and required, and the rendered --help is identical. A separate check runs the command against a real app and asserts the 13 arguments still reach reflex_cli.v2.cli.deploy, and that --no-ssr and --exclude-from-backend still reach export() through the export_fn closure.

Note for the release

reflex deploy now lives in reflex_cli.v2.deploy, which older hosting CLI releases do not carry, so the floor must exclude them. That release is unpublished, so the pin is the workspace development version 0.1.70.post18.dev0 — the same convention reflex-hosting-cli already uses for its reflex-base pin. scripts/check_min_deps.py --check-dev-pins reflex fails while it stands, so it must be re-pinned to the published version before release.

One side effect: check_min_deps.py reflex cannot build the root package while the dev pin stands. [tool.hatch.build.hooks.custom] sets require-runtime-dependencies = true, so runtime deps resolve inside the isolated build environment, which the script's local editable overrides never reach. The other packages still check.

Tests

reflex deploy had no dedicated tests before this branch.

  • tests/units/reflex_cli/v2/test_deploy.py — registration, the flag surface, --help, and a sys.meta_path probe that imports the module with every reflex import blocked. That last one covers the standalone-import bug above.
  • tests/units/test_reflex.py — the cloud commands resolve into reflex_cli, and the stand-in reports the package rather than a usage error, including when the real command's flags are passed.

Full suite: 7616 passed, 17 skipped. Ruff clean. Pyright: 0 errors.

@FarhanAliRaza
FarhanAliRaza requested a review from a team as a code owner August 21, 2026 17:48
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…sting CLI

The managed-platform deploy command (options and body) now lives in
reflex_cli.v2.deploy; the reflex CLI registers it via cli.add_command.
Flags and behavior are unchanged. The module lazily imports reflex
internals in the command body since it only runs through the reflex CLI.
@FarhanAliRaza
FarhanAliRaza force-pushed the farhan/eng-11433-move-reflex-deploy-command-out-of-the-reflex-package-into branch from d82f9ea to b822d53 Compare August 21, 2026 17:51
@greptile-apps

greptile-apps Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR moves reflex deploy into reflex-hosting-cli while preserving its command surface and adding graceful missing-package fallbacks.

  • Moves deploy implementation and shared logging options into independently importable packages.
  • Registers deploy and cloud through guarded direct imports.
  • Updates dependency floors, lock data, changelog entries, and command-registration tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/reflex-hosting-cli/src/reflex_cli/v2/deploy.py Hosts the moved deploy command with lazy framework imports and preserves argument forwarding to the hosting deployment flow.
reflex/reflex.py Replaces the in-framework deploy implementation with guarded external command registration and missing-package placeholders.
packages/reflex-base/src/reflex_base/utils/cli_options.py Centralizes shared Click logging options so the hosting CLI can import them without depending on the framework.
pyproject.toml Raises the hosting CLI dependency floor to the workspace version containing the moved deploy command.
tests/units/reflex_cli/v2/test_deploy.py Verifies registration, option compatibility, help rendering, and standalone importability.
tests/units/test_reflex.py Verifies real cloud command registration and user-facing behavior when the hosting package is unavailable.

Reviews (3): Last reviewed commit: "ENG-11433 docs(cli): drop the stale entr..." | Re-trigger Greptile

Comment thread reflex/utils/cli_plugins.py Outdated
@codspeed-hq

codspeed-hq Bot commented Aug 21, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 27 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing FarhanAliRaza:farhan/eng-11433-move-reflex-deploy-command-out-of-the-reflex-package-into (f6945ca) with main (d86f167)

Open in CodSpeed

Footnotes

  1. 8 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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

4 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/units/utils/test_cli_plugins.py">

<violation number="1" location="tests/units/utils/test_cli_plugins.py:282">
P3: This reflex-side test hardcodes `reflex_cli.v2.deploy`, a private module path of the separate reflex-hosting-cli package, to assert eager loading. If the hosting CLI renames or reorganizes that module, this framework test fails spuriously even though the decoupling behavior is unchanged. Derive the expected module from the discovered entry points instead (e.g., check that every module referenced by the entry-point value is present in sys.modules) so the test stays coupled to the entry-point contract rather than to the hosting CLI's internal layout.</violation>
</file>

<file name="reflex/utils/cli_plugins.py">

<violation number="1" location="reflex/utils/cli_plugins.py:75">
P2: When a Typer entry point cannot be converted, `typer.main.get_command` aborts construction of the entire `reflex` CLI. Catch conversion errors, log the plugin failure, and continue so the remaining commands and fallback placeholders remain available.</violation>

<violation number="2" location="reflex/utils/cli_plugins.py:163">
P2: When a contributed entry point exists but fails to load (`entry_point.load()` raises) or does not resolve to a click command, the code still registers the "install/upgrade reflex-hosting-cli" placeholder. That message is wrong for an installed, current package, so the user is told to reinstall or upgrade an already-correct package instead of the real cause. Track names that already had an entry point separately from truly absent ones, and give those a diagnostic that reflects the actual failure (the load error already logged, or the non-click object type).</violation>

<violation number="3" location="reflex/utils/cli_plugins.py:173">
P2: Guard duplicate names before registering the command. Click stores commands by name, so this call overwrites an existing built-in or earlier contributed command; reject or skip collisions instead.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread pyproject.toml
Comment thread reflex/utils/cli_plugins.py Outdated
Comment thread reflex/utils/cli_plugins.py Outdated
Comment thread reflex/utils/cli_plugins.py Outdated
Comment thread tests/units/utils/test_cli_plugins.py Outdated
Comment thread reflex/reflex.py
The command body now lives in reflex_cli.v2.deploy, so reflex/reflex.py
imports it instead of defining it. That import is guarded: when the
hosting CLI is absent, a stand-in of the same name is registered. It
accepts any flags, so the user is told which package to install rather
than getting a usage error about an option the real command understands.
`login` and `logout` report the same way.

deploy.py imported log_options from `reflex`, which is not a dependency
of reflex-hosting-cli, so the package failed to import on its own. The
shared click options move to reflex_base.utils.cli_options, which both
packages already depend on; reflex/utils/cli_options.py re-exports them.

The hosting CLI floor moves to the release carrying the moved module,
held at the workspace development version until that ships.
@FarhanAliRaza
FarhanAliRaza force-pushed the farhan/eng-11433-move-reflex-deploy-command-out-of-the-reflex-package-into branch from b822d53 to 03a451c Compare August 21, 2026 18:07
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.

1 participant