Skip to content

Commit 41f7eaa

Browse files
authored
Merge pull request #981 from codeflash-ai/clicker
Clicker
2 parents 2f55782 + 5db28e2 commit 41f7eaa

4 files changed

Lines changed: 82 additions & 102 deletions

File tree

codeflash/cli_cmds/cmd_init.py

Lines changed: 71 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
import re
55
import subprocess
66
import sys
7+
import webbrowser
78
from enum import Enum, auto
89
from functools import lru_cache
910
from pathlib import Path
1011
from typing import TYPE_CHECKING, Any, Optional, Union, cast
1112

12-
import click
1313
import git
1414
import tomlkit
1515
from git import InvalidGitRepositoryError, Repo
@@ -287,8 +287,8 @@ def collect_setup_info() -> CLISetupInfo:
287287
curdir = Path.cwd()
288288
# Check if the cwd is writable
289289
if not os.access(curdir, os.W_OK):
290-
click.echo(f"❌ The current directory isn't writable, please check your folder permissions and try again.{LF}")
291-
click.echo("It's likely you don't have write permissions for this folder.")
290+
console.print(f"❌ The current directory isn't writable, please check your folder permissions and try again.{LF}")
291+
console.print("It's likely you don't have write permissions for this folder.")
292292
sys.exit(1)
293293

294294
# Check for the existence of pyproject.toml or setup.py
@@ -344,13 +344,13 @@ def collect_setup_info() -> CLISetupInfo:
344344
# Validate the path is safe and exists
345345
path_obj = Path(custom_path_str)
346346
if not path_obj.exists() or not path_obj.is_dir():
347-
click.echo(f"❌ Path does not exist or is not a directory: {custom_path_str}")
347+
console.print(f"❌ Path does not exist or is not a directory: {custom_path_str}")
348348
console.print() # Add spacing before retry
349349
continue
350350
is_valid, error_msg = validate_relative_directory_path(custom_path_str)
351351
if not is_valid:
352-
click.echo(f"❌ Invalid path: {error_msg}")
353-
click.echo("Please enter a valid relative directory path.")
352+
console.print(f"❌ Invalid path: {error_msg}")
353+
console.print("Please enter a valid relative directory path.")
354354
console.print() # Add spacing before retry
355355
continue # Retry the prompt
356356
module_root = Path(custom_path_str)
@@ -392,7 +392,7 @@ def collect_setup_info() -> CLISetupInfo:
392392
if tests_root_answer == create_for_me_option:
393393
tests_root = Path(curdir) / (default_tests_subdir or "tests")
394394
tests_root.mkdir()
395-
click.echo(f"✅ Created directory {tests_root}{os.path.sep}{LF}")
395+
console.print(f"✅ Created directory {tests_root}{os.path.sep}{LF}")
396396
elif tests_root_answer == custom_dir_option:
397397
custom_tests_panel = Panel(
398398
Text(
@@ -417,14 +417,14 @@ def collect_setup_info() -> CLISetupInfo:
417417
# Validate the path exists
418418
path_obj = Path(custom_tests_path_str)
419419
if not path_obj.exists() or not path_obj.is_dir():
420-
click.echo(f"❌ Path does not exist or is not a directory: {custom_tests_path_str}")
420+
console.print(f"❌ Path does not exist or is not a directory: {custom_tests_path_str}")
421421
console.print() # Add spacing before retry
422422
continue
423423
# Validate the path is safe
424424
is_valid, error_msg = validate_relative_directory_path(custom_tests_path_str)
425425
if not is_valid:
426-
click.echo(f"❌ Invalid path: {error_msg}")
427-
click.echo("Please enter a valid relative directory path.")
426+
console.print(f"❌ Invalid path: {error_msg}")
427+
console.print("Please enter a valid relative directory path.")
428428
console.print() # Add spacing before retry
429429
continue # Retry the prompt
430430
tests_root = Path(curdir) / Path(custom_tests_path_str)
@@ -507,7 +507,7 @@ def collect_setup_info() -> CLISetupInfo:
507507
else:
508508
git_remote = git_remotes[0]
509509
else:
510-
click.echo(
510+
console.print(
511511
"No git remotes found. You can still use Codeflash locally, but you'll need to set up a remote "
512512
"repository to use GitHub features."
513513
)
@@ -529,8 +529,8 @@ def collect_setup_info() -> CLISetupInfo:
529529

530530

531531
def check_for_toml_or_setup_file() -> str | None:
532-
click.echo()
533-
click.echo("Checking for pyproject.toml or setup.py…\r", nl=False)
532+
console.print()
533+
console.print("Checking for pyproject.toml or setup.py…\r")
534534
curdir = Path.cwd()
535535
pyproject_toml_path = curdir / "pyproject.toml"
536536
setup_py_path = curdir / "setup.py"
@@ -539,21 +539,21 @@ def check_for_toml_or_setup_file() -> str | None:
539539
try:
540540
pyproject_toml_content = pyproject_toml_path.read_text(encoding="utf8")
541541
project_name = tomlkit.parse(pyproject_toml_content)["tool"]["poetry"]["name"]
542-
click.echo(f"✅ I found a pyproject.toml for your project {project_name}.")
542+
console.print(f"✅ I found a pyproject.toml for your project {project_name}.")
543543
ph("cli-pyproject-toml-found-name")
544544
except Exception:
545-
click.echo("✅ I found a pyproject.toml for your project.")
545+
console.print("✅ I found a pyproject.toml for your project.")
546546
ph("cli-pyproject-toml-found")
547547
else:
548548
if setup_py_path.exists():
549549
setup_py_content = setup_py_path.read_text(encoding="utf8")
550550
project_name_match = re.search(r"setup\s*\([^)]*?name\s*=\s*['\"](.*?)['\"]", setup_py_content, re.DOTALL)
551551
if project_name_match:
552552
project_name = project_name_match.group(1)
553-
click.echo(f"✅ Found setup.py for your project {project_name}")
553+
console.print(f"✅ Found setup.py for your project {project_name}")
554554
ph("cli-setup-py-found-name")
555555
else:
556-
click.echo("✅ Found setup.py.")
556+
console.print("✅ Found setup.py.")
557557
ph("cli-setup-py-found")
558558
toml_info_panel = Panel(
559559
Text(
@@ -574,7 +574,7 @@ def check_for_toml_or_setup_file() -> str | None:
574574
if result.command is None or result.value == "No":
575575
apologize_and_exit()
576576
create_empty_pyproject_toml(pyproject_toml_path)
577-
click.echo()
577+
console.print()
578578
return cast("str", project_name)
579579

580580

@@ -604,7 +604,7 @@ def create_empty_pyproject_toml(pyproject_toml_path: Path) -> None:
604604
console.input()
605605
ph("cli-created-pyproject-toml")
606606
except OSError:
607-
click.echo("❌ Failed to create pyproject.toml. Please check your disk permissions and available space.")
607+
console.print("❌ Failed to create pyproject.toml. Please check your disk permissions and available space.")
608608
apologize_and_exit()
609609

610610

@@ -616,7 +616,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n
616616
try:
617617
repo = Repo(config["module_root"], search_parent_directories=True)
618618
except git.InvalidGitRepositoryError:
619-
click.echo(
619+
console.print(
620620
"Skipping GitHub action installation for continuous optimization because you're not in a git repository."
621621
)
622622
return
@@ -735,7 +735,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n
735735
console.print(f"\n📍 Press Enter to open: {get_github_secrets_page_url(repo)}")
736736
console.input()
737737

738-
click.launch(get_github_secrets_page_url(repo))
738+
webbrowser.open(get_github_secrets_page_url(repo))
739739

740740
# Post-launch message panel
741741
launch_panel = Panel(
@@ -750,9 +750,9 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n
750750
border_style="bright_cyan",
751751
)
752752
console.print(launch_panel)
753-
click.pause()
754-
click.echo()
755-
click.echo(
753+
console.input("Press Enter to continue...")
754+
console.print()
755+
console.print(
756756
f"Please edit, commit and push this GitHub actions file to your repo, and you're all set!{LF}"
757757
f"🚀 Codeflash is now configured to automatically optimize new Github PRs!{LF}"
758758
)
@@ -853,7 +853,7 @@ def customize_codeflash_yaml_content(
853853
with toml_path.open(encoding="utf8") as pyproject_file:
854854
pyproject_data = tomlkit.parse(pyproject_file.read())
855855
except FileNotFoundError:
856-
click.echo(
856+
console.print(
857857
f"I couldn't find a pyproject.toml in the current directory.{LF}"
858858
f"Please create a new empty pyproject.toml file here, OR if you use poetry then run `poetry init`, OR run `codeflash init` again from a directory with an existing pyproject.toml file."
859859
)
@@ -885,7 +885,7 @@ def get_formatter_cmds(formatter: str) -> list[str]:
885885
if formatter == "ruff":
886886
return ["ruff check --exit-zero --fix $file", "ruff format $file"]
887887
if formatter == "other":
888-
click.echo(
888+
console.print(
889889
"🔧 In pyproject.toml, please replace 'your-formatter' with the command you use to format your code."
890890
)
891891
return ["your-formatter $file"]
@@ -906,7 +906,7 @@ def configure_pyproject_toml(
906906
with toml_path.open(encoding="utf8") as pyproject_file:
907907
pyproject_data = tomlkit.parse(pyproject_file.read())
908908
except FileNotFoundError:
909-
click.echo(
909+
console.print(
910910
f"I couldn't find a pyproject.toml in the current directory.{LF}"
911911
f"Please create a new empty pyproject.toml file here, OR if you use poetry then run `poetry init`, OR run `codeflash init` again from a directory with an existing pyproject.toml file."
912912
)
@@ -951,88 +951,65 @@ def configure_pyproject_toml(
951951

952952
with toml_path.open("w", encoding="utf8") as pyproject_file:
953953
pyproject_file.write(tomlkit.dumps(pyproject_data))
954-
click.echo(f"Added Codeflash configuration to {toml_path}")
955-
click.echo()
954+
console.print(f"Added Codeflash configuration to {toml_path}")
955+
console.print()
956956
return True
957957

958958

959959
def install_github_app(git_remote: str) -> None:
960960
try:
961961
git_repo = git.Repo(search_parent_directories=True)
962962
except git.InvalidGitRepositoryError:
963-
click.echo("Skipping GitHub app installation because you're not in a git repository.")
963+
console.print("Skipping GitHub app installation because you're not in a git repository.")
964964
return
965965

966966
if git_remote not in get_git_remotes(git_repo):
967-
click.echo(f"Skipping GitHub app installation, remote ({git_remote}) does not exist in this repository.")
967+
console.print(f"Skipping GitHub app installation, remote ({git_remote}) does not exist in this repository.")
968968
return
969969

970970
owner, repo = get_repo_owner_and_name(git_repo, git_remote)
971971

972972
if is_github_app_installed_on_repo(owner, repo, suppress_errors=True):
973-
click.echo(
973+
console.print(
974974
f"🐙 Looks like you've already installed the Codeflash GitHub app on this repository ({owner}/{repo})! Continuing…"
975975
)
976976

977977
else:
978978
try:
979-
click.prompt(
979+
console.print(
980980
f"Finally, you'll need to install the Codeflash GitHub app by choosing the repository you want to install Codeflash on.{LF}"
981981
f"I will attempt to open the github app page - https://github.com/apps/codeflash-ai/installations/select_target {LF}"
982-
f"Please, press ENTER to open the app installation page{LF}",
983-
default="",
984-
type=click.STRING,
985-
prompt_suffix=">>> ",
986-
show_default=False,
982+
f"Please, press ENTER to open the app installation page{LF}"
987983
)
988-
click.launch("https://github.com/apps/codeflash-ai/installations/select_target")
989-
click.prompt(
990-
f"Please, press ENTER once you've finished installing the github app from https://github.com/apps/codeflash-ai/installations/select_target{LF}",
991-
default="",
992-
type=click.STRING,
993-
prompt_suffix=">>> ",
994-
show_default=False,
984+
console.input(">>> ")
985+
webbrowser.open("https://github.com/apps/codeflash-ai/installations/select_target")
986+
console.print(
987+
f"Please, press ENTER once you've finished installing the github app from https://github.com/apps/codeflash-ai/installations/select_target{LF}"
995988
)
989+
console.input(">>> ")
996990

997991
count = 2
998992
while not is_github_app_installed_on_repo(owner, repo, suppress_errors=True):
999993
if count == 0:
1000-
click.echo(
994+
console.print(
1001995
f"❌ It looks like the Codeflash GitHub App is not installed on the repository {owner}/{repo}.{LF}"
1002996
f"You won't be able to create PRs with Codeflash until you install the app.{LF}"
1003997
f"In the meantime you can make local only optimizations by using the '--no-pr' flag with codeflash.{LF}"
1004998
)
1005999
break
1006-
click.prompt(
1000+
console.print(
10071001
f"❌ It looks like the Codeflash GitHub App is not installed on the repository {owner}/{repo}.{LF}"
10081002
f"Please install it from https://github.com/apps/codeflash-ai/installations/select_target {LF}"
1009-
f"Please, press ENTER to continue once you've finished installing the github app…{LF}",
1010-
default="",
1011-
type=click.STRING,
1012-
prompt_suffix=">>> ",
1013-
show_default=False,
1003+
f"Please, press ENTER to continue once you've finished installing the github app…{LF}"
10141004
)
1005+
console.input(">>> ")
10151006
count -= 1
1016-
except (KeyboardInterrupt, EOFError, click.exceptions.Abort):
1007+
except (KeyboardInterrupt, EOFError):
10171008
# leave empty line for the next prompt to be properly rendered
1018-
click.echo()
1019-
1020-
1021-
class CFAPIKeyType(click.ParamType):
1022-
name = "cfapi-key"
1023-
1024-
def convert(self, value: str, param: click.Parameter | None, ctx: click.Context | None) -> str | None:
1025-
value = value.strip()
1026-
if not value.startswith("cf-") and value != "":
1027-
self.fail(
1028-
f"That key [{value}] seems to be invalid. It should start with a 'cf-' prefix. Please try again.",
1029-
param,
1030-
ctx,
1031-
)
1032-
return value
1009+
console.print()
10331010

10341011

1035-
# Returns True if the user entered a new API key, False if they used an existing one
1012+
#
10361013
def prompt_api_key() -> bool:
10371014
"""Prompt user for API key via OAuth or manual entry."""
10381015
# Check for existing API key
@@ -1081,15 +1058,15 @@ def prompt_api_key() -> bool:
10811058
shell_rc_path = get_shell_rc_path()
10821059
if not shell_rc_path.exists() and os.name == "nt":
10831060
shell_rc_path.touch()
1084-
click.echo(f"✅ Created {shell_rc_path}")
1061+
console.print(f"✅ Created {shell_rc_path}")
10851062

10861063
result = save_api_key_to_rc(api_key)
10871064
if is_successful(result):
1088-
click.echo(result.unwrap())
1089-
click.echo("✅ Signed in successfully and API key saved!")
1065+
console.print(result.unwrap())
1066+
console.print("✅ Signed in successfully and API key saved!")
10901067
else:
1091-
click.echo(result.failure())
1092-
click.pause()
1068+
console.print(result.failure())
1069+
console.input("Press Enter to continue...")
10931070

10941071
os.environ["CODEFLASH_API_KEY"] = api_key
10951072
ph("cli-oauth-signin-completed")
@@ -1100,35 +1077,42 @@ def enter_api_key_and_save_to_rc() -> None:
11001077
browser_launched = False
11011078
api_key = ""
11021079
while api_key == "":
1103-
api_key = click.prompt(
1104-
f"Enter your Codeflash API key{' [or press Enter to open your API key page]' if not browser_launched else ''}",
1105-
hide_input=False,
1106-
default="",
1107-
type=CFAPIKeyType(),
1108-
show_default=False,
1109-
).strip()
1080+
result = prompts.text(
1081+
f"Enter your Codeflash API key{' [or press Enter to open your API key page]' if not browser_launched else ''}"
1082+
)
1083+
if result.command is None:
1084+
apologize_and_exit()
1085+
return
1086+
api_key = result.value.strip()
1087+
11101088
if api_key:
1089+
# Validate API key format
1090+
if not api_key.startswith("cf-"):
1091+
console.print(f"❌ That key [{api_key}] seems to be invalid. It should start with a 'cf-' prefix. Please try again.")
1092+
api_key = "" # Reset to retry
1093+
continue
11111094
break
1095+
11121096
if not browser_launched:
1113-
click.echo(
1097+
console.print(
11141098
f"Opening your Codeflash API key page. Grab a key from there!{LF}"
11151099
"You can also open this link manually: https://app.codeflash.ai/app/apikeys"
11161100
)
1117-
click.launch("https://app.codeflash.ai/app/apikeys")
1101+
webbrowser.open("https://app.codeflash.ai/app/apikeys")
11181102
browser_launched = True # This does not work on remote consoles
11191103
shell_rc_path = get_shell_rc_path()
11201104
if not shell_rc_path.exists() and os.name == "nt":
11211105
# On Windows, create the appropriate file (PowerShell .ps1 or CMD .bat) in the user's home directory
11221106
shell_rc_path.parent.mkdir(parents=True, exist_ok=True)
11231107
shell_rc_path.touch()
1124-
click.echo(f"✅ Created {shell_rc_path}")
1108+
console.print(f"✅ Created {shell_rc_path}")
11251109
get_user_id(api_key=api_key) # Used to verify whether the API key is valid.
11261110
result = save_api_key_to_rc(api_key)
11271111
if is_successful(result):
1128-
click.echo(result.unwrap())
1112+
console.print(result.unwrap())
11291113
else:
1130-
click.echo(result.failure())
1131-
click.pause()
1114+
console.print(result.failure())
1115+
console.input("Press Enter to continue...")
11321116

11331117
os.environ["CODEFLASH_API_KEY"] = api_key
11341118

0 commit comments

Comments
 (0)