From 947e2ab8aca59c916220e49972d0c82b973f1142 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Thu, 13 Nov 2025 21:48:00 +0200 Subject: [PATCH 01/11] calling cf-api while setting up github actions during init command --- codeflash/api/cfapi.py | 25 ++++++ codeflash/cli_cmds/cmd_init.py | 146 ++++++++++++++++++++++++++++----- 2 files changed, 150 insertions(+), 21 deletions(-) diff --git a/codeflash/api/cfapi.py b/codeflash/api/cfapi.py index fad6eaa4d..0cdf2e984 100644 --- a/codeflash/api/cfapi.py +++ b/codeflash/api/cfapi.py @@ -239,6 +239,31 @@ def create_pr( return make_cfapi_request(endpoint="/create-pr", method="POST", payload=payload) +def setup_github_actions( + owner: str, + repo: str, + base_branch: str, + workflow_content: str, + api_key: str | None = None, +) -> Response: + """Setup GitHub Actions workflow by creating a PR with the workflow file. + + :param owner: Repository owner (username or organization) + :param repo: Repository name + :param base_branch: Base branch to create PR against (e.g., "main", "master") + :param workflow_content: Content of the GitHub Actions workflow file (YAML) + :param api_key: Optional API key (uses default if not provided) + :return: Response object with pr_url and pr_number on success + """ + payload = { + "owner": owner, + "repo": repo, + "baseBranch": base_branch, + "workflowContent": workflow_content, + } + return make_cfapi_request(endpoint="/setup-github-actions", method="POST", payload=payload, api_key=api_key) + + def create_staging( original_code: dict[Path, str], new_code: dict[Path, str], diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index a53d18ce6..ffdfb3f4d 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -22,14 +22,14 @@ from rich.table import Table from rich.text import Text -from codeflash.api.cfapi import is_github_app_installed_on_repo +from codeflash.api.cfapi import is_github_app_installed_on_repo, setup_github_actions from codeflash.cli_cmds.cli_common import apologize_and_exit from codeflash.cli_cmds.console import console, logger from codeflash.cli_cmds.extension import install_vscode_extension from codeflash.code_utils.compat import LF from codeflash.code_utils.config_parser import parse_config_file from codeflash.code_utils.env_utils import check_formatter_installed, get_codeflash_api_key -from codeflash.code_utils.git_utils import get_git_remotes, get_repo_owner_and_name +from codeflash.code_utils.git_utils import get_current_branch, get_git_remotes, get_repo_owner_and_name from codeflash.code_utils.github_utils import get_github_secrets_page_url from codeflash.code_utils.shell_utils import get_shell_rc_path, save_api_key_to_rc from codeflash.either import is_successful @@ -807,21 +807,110 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n materialized_optimize_yml_content = customize_codeflash_yaml_content( optimize_yml_content, config, git_root, benchmark_mode ) - with optimize_yaml_path.open("w", encoding="utf8") as optimize_yml_file: - optimize_yml_file.write(materialized_optimize_yml_content) - # Success panel for workflow creation - workflow_success_panel = Panel( - Text( - f"βœ… Created GitHub action workflow at {optimize_yaml_path}\n\n" - "Your repository is now configured for continuous optimization!", - style="green", - justify="center", - ), - title="πŸŽ‰ Workflow Created!", - border_style="bright_green", - ) - console.print(workflow_success_panel) - console.print() + + # Get repository information for API call + git_remote = config.get("git_remote", "origin") + pr_created_via_api = False + pr_url = None + + try: + owner, repo_name = get_repo_owner_and_name(repo, git_remote) + except Exception as e: + logger.error(f"[cmd_init.py:install_github_actions] Failed to get repository owner and name: {e}") + # Fall back to local file creation + workflows_path.mkdir(parents=True, exist_ok=True) + with optimize_yaml_path.open("w", encoding="utf8") as optimize_yml_file: + optimize_yml_file.write(materialized_optimize_yml_content) + workflow_success_panel = Panel( + Text( + f"βœ… Created GitHub action workflow at {optimize_yaml_path}\n\n" + "Your repository is now configured for continuous optimization!", + style="green", + justify="center", + ), + title="πŸŽ‰ Workflow Created!", + border_style="bright_green", + ) + console.print(workflow_success_panel) + console.print() + else: + # Try to create PR via API + try: + base_branch = get_current_branch(repo) if repo.active_branch else "main" + console.print("Creating PR with GitHub Actions workflow...") + logger.info( + f"[cmd_init.py:install_github_actions] Calling setup_github_actions API for {owner}/{repo_name} on branch {base_branch}" + ) + + response = setup_github_actions( + owner=owner, + repo=repo_name, + base_branch=base_branch, + workflow_content=materialized_optimize_yml_content, + ) + + if response.status_code == 200: + response_data = response.json() + if response_data.get("success"): + pr_url = response_data.get("pr_url") + if pr_url: + pr_created_via_api = True + workflow_success_panel = Panel( + Text( + f"βœ… PR created: {pr_url}\n\n" + "Your repository is now configured for continuous optimization!", + style="green", + justify="center", + ), + title="πŸŽ‰ Workflow PR Created!", + border_style="bright_green", + ) + console.print(workflow_success_panel) + console.print() + logger.info( + f"[cmd_init.py:install_github_actions] Successfully created PR #{response_data.get('pr_number')} for {owner}/{repo_name}" + ) + else: + # File already exists with same content + pr_created_via_api = True # Mark as handled (no PR needed) + already_exists_panel = Panel( + Text( + "βœ… Workflow file already exists with the same content.\n\n" + "No changes needed - your repository is already configured!", + style="green", + justify="center", + ), + title="βœ… Already Configured", + border_style="bright_green", + ) + console.print(already_exists_panel) + console.print() + else: + raise Exception(response_data.get("error", "Unknown error")) + else: + # API call failed, fall back to local file creation + raise Exception(f"API returned status {response.status_code}") + + except Exception as api_error: + # Fall back to local file creation if API call fails + logger.warning( + f"[cmd_init.py:install_github_actions] API call failed, falling back to local file creation: {api_error}" + ) + workflows_path.mkdir(parents=True, exist_ok=True) + with optimize_yaml_path.open("w", encoding="utf8") as optimize_yml_file: + optimize_yml_file.write(materialized_optimize_yml_content) + workflow_success_panel = Panel( + Text( + f"βœ… Created GitHub action workflow at {optimize_yaml_path}\n\n" + "Your repository is now configured for continuous optimization!", + style="green", + justify="center", + ), + title="πŸŽ‰ Workflow Created!", + border_style="bright_green", + ) + console.print(workflow_success_panel) + console.print() try: existing_api_key = get_codeflash_api_key() @@ -866,10 +955,25 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n console.print(launch_panel) click.pause() click.echo() - click.echo( - f"Please edit, commit and push this GitHub actions file to your repo, and you're all set!{LF}" - f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" - ) + # Show appropriate message based on whether PR was created via API + if pr_created_via_api: + if pr_url: + click.echo( + f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" + f"Once you merge the PR and add the secret, the workflow will be active.{LF}" + ) + else: + # File already exists + click.echo( + f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" + f"Just add the secret and the workflow will be active.{LF}" + ) + else: + # Fell back to local file creation + click.echo( + f"Please edit, commit and push this GitHub actions file to your repo, and you're all set!{LF}" + f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" + ) ph("cli-github-workflow-created") except KeyboardInterrupt: apologize_and_exit() From bea874f023c90e976bf996090ed5b27be072522c Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 14 Nov 2025 02:17:12 +0200 Subject: [PATCH 02/11] updating the cf-api request for github action installation --- codeflash/api/cfapi.py | 10 +- codeflash/cli_cmds/cmd_init.py | 190 +++++++++++++++++++++++---------- 2 files changed, 139 insertions(+), 61 deletions(-) diff --git a/codeflash/api/cfapi.py b/codeflash/api/cfapi.py index 0cdf2e984..f05ce9bdb 100644 --- a/codeflash/api/cfapi.py +++ b/codeflash/api/cfapi.py @@ -246,14 +246,14 @@ def setup_github_actions( workflow_content: str, api_key: str | None = None, ) -> Response: - """Setup GitHub Actions workflow by creating a PR with the workflow file. + """Setup GitHub Actions workflow by creating a PR with the workflow file and optionally setting up the repository secret. :param owner: Repository owner (username or organization) :param repo: Repository name :param base_branch: Base branch to create PR against (e.g., "main", "master") :param workflow_content: Content of the GitHub Actions workflow file (YAML) - :param api_key: Optional API key (uses default if not provided) - :return: Response object with pr_url and pr_number on success + :param api_key: API key to store as repository secret (if provided, will attempt to set up secret automatically) + :return: Response object with pr_url, pr_number, secret_setup_success, and secret_setup_error on success """ payload = { "owner": owner, @@ -261,6 +261,10 @@ def setup_github_actions( "baseBranch": base_branch, "workflowContent": workflow_content, } + # Include apiKey in payload if provided - this will be encrypted and stored as a repository secret + if api_key: + payload["apiKey"] = api_key + return make_cfapi_request(endpoint="/setup-github-actions", method="POST", payload=payload, api_key=api_key) diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index ffdfb3f4d..afd1cef65 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -812,6 +812,17 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n git_remote = config.get("git_remote", "origin") pr_created_via_api = False pr_url = None + secret_setup_success = False + secret_setup_error = None + + # Get API key for secret setup + try: + api_key = get_codeflash_api_key() + except OSError: + api_key = None + logger.info( + "[cmd_init.py:install_github_actions] No API key found, will skip secret setup" + ) try: owner, repo_name = get_repo_owner_and_name(repo, git_remote) @@ -847,18 +858,27 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n repo=repo_name, base_branch=base_branch, workflow_content=materialized_optimize_yml_content, + api_key=api_key, ) if response.status_code == 200: response_data = response.json() if response_data.get("success"): pr_url = response_data.get("pr_url") + secret_setup_success = response_data.get("secret_setup_success", False) + secret_setup_error = response_data.get("secret_setup_error") + if pr_url: pr_created_via_api = True + # Build success message with secret status + success_message = f"βœ… PR created: {pr_url}\n\n" + if secret_setup_success: + success_message += "βœ… Repository secret CODEFLASH_API_KEY configured\n\n" + success_message += "Your repository is now configured for continuous optimization!" + workflow_success_panel = Panel( Text( - f"βœ… PR created: {pr_url}\n\n" - "Your repository is now configured for continuous optimization!", + success_message, style="green", justify="center", ), @@ -867,16 +887,40 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n ) console.print(workflow_success_panel) console.print() + + # Show warning if secret setup failed + if not secret_setup_success and api_key: + warning_message = ( + "⚠️ Secret setup failed. You'll need to add CODEFLASH_API_KEY manually.\n\n" + ) + if secret_setup_error: + warning_message += f"Error: {secret_setup_error}\n\n" + warning_message += f"πŸ“ Add secret at: {get_github_secrets_page_url(repo)}" + + warning_panel = Panel( + Text(warning_message, style="yellow"), + title="⚠️ Manual Secret Setup Required", + border_style="yellow", + ) + console.print(warning_panel) + console.print() + logger.info( - f"[cmd_init.py:install_github_actions] Successfully created PR #{response_data.get('pr_number')} for {owner}/{repo_name}" + f"[cmd_init.py:install_github_actions] Successfully created PR #{response_data.get('pr_number')} for {owner}/{repo_name}, secret_setup_success={secret_setup_success}" ) else: # File already exists with same content pr_created_via_api = True # Mark as handled (no PR needed) + already_exists_message = ( + "βœ… Workflow file already exists with the same content.\n\n" + ) + if secret_setup_success: + already_exists_message += "βœ… Repository secret CODEFLASH_API_KEY configured\n\n" + already_exists_message += "No changes needed - your repository is already configured!" + already_exists_panel = Panel( Text( - "βœ… Workflow file already exists with the same content.\n\n" - "No changes needed - your repository is already configured!", + already_exists_message, style="green", justify="center", ), @@ -885,6 +929,23 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n ) console.print(already_exists_panel) console.print() + + # Show warning if secret setup failed + if not secret_setup_success and api_key: + warning_message = ( + "⚠️ Secret setup failed. You'll need to add CODEFLASH_API_KEY manually.\n\n" + ) + if secret_setup_error: + warning_message += f"Error: {secret_setup_error}\n\n" + warning_message += f"πŸ“ Add secret at: {get_github_secrets_page_url(repo)}" + + warning_panel = Panel( + Text(warning_message, style="yellow"), + title="⚠️ Manual Secret Setup Required", + border_style="yellow", + ) + console.print(warning_panel) + console.print() else: raise Exception(response_data.get("error", "Unknown error")) else: @@ -912,64 +973,77 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n console.print(workflow_success_panel) console.print() - try: - existing_api_key = get_codeflash_api_key() - except OSError: - existing_api_key = None - - # GitHub secrets setup panel - secrets_message = ( - "πŸ” Next Step: Add API Key as GitHub Secret\n\n" - "You'll need to add your CODEFLASH_API_KEY as a secret to your GitHub repository.\n\n" - "πŸ“‹ Steps:\n" - "1. Press Enter to open your repo's secrets page\n" - "2. Click 'New repository secret'\n" - "3. Add your API key with the variable name CODEFLASH_API_KEY" - ) - - if existing_api_key: - secrets_message += f"\n\nπŸ”‘ Your API Key: {existing_api_key}" - - secrets_panel = Panel( - Text(secrets_message, style="blue"), title="πŸ” GitHub Secrets Setup", border_style="bright_blue" - ) - console.print(secrets_panel) - - console.print(f"\nπŸ“ Press Enter to open: {get_github_secrets_page_url(repo)}") - console.input() - - click.launch(get_github_secrets_page_url(repo)) - - # Post-launch message panel - launch_panel = Panel( - Text( - "πŸ™ I opened your GitHub secrets page!\n\n" - "Note: If you see a 404, you probably don't have access to this repo's secrets. " - "Ask a repo admin to add it for you, or (not recommended) you can temporarily " - "hard-code your API key into the workflow file.", - style="cyan", - ), - title="🌐 Browser Opened", - border_style="bright_cyan", - ) - console.print(launch_panel) - click.pause() - click.echo() - # Show appropriate message based on whether PR was created via API + # Show appropriate message based on whether PR was created via API and secret setup status if pr_created_via_api: if pr_url: - click.echo( - f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" - f"Once you merge the PR and add the secret, the workflow will be active.{LF}" - ) + if secret_setup_success: + click.echo( + f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" + f"Once you merge the PR, the workflow will be active.{LF}" + ) + else: + # PR created but secret setup failed or skipped + click.echo( + f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" + f"Once you merge the PR and add the secret, the workflow will be active.{LF}" + ) else: # File already exists - click.echo( - f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" - f"Just add the secret and the workflow will be active.{LF}" - ) + if secret_setup_success: + click.echo( + f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" + f"The workflow is ready to use.{LF}" + ) + else: + click.echo( + f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" + f"Just add the secret and the workflow will be active.{LF}" + ) else: - # Fell back to local file creation + # Fell back to local file creation - show manual secret setup + try: + existing_api_key = get_codeflash_api_key() + except OSError: + existing_api_key = None + + # GitHub secrets setup panel (only shown when falling back to local file creation) + secrets_message = ( + "πŸ” Next Step: Add API Key as GitHub Secret\n\n" + "You'll need to add your CODEFLASH_API_KEY as a secret to your GitHub repository.\n\n" + "πŸ“‹ Steps:\n" + "1. Press Enter to open your repo's secrets page\n" + "2. Click 'New repository secret'\n" + "3. Add your API key with the variable name CODEFLASH_API_KEY" + ) + + if existing_api_key: + secrets_message += f"\n\nπŸ”‘ Your API Key: {existing_api_key}" + + secrets_panel = Panel( + Text(secrets_message, style="blue"), title="πŸ” GitHub Secrets Setup", border_style="bright_blue" + ) + console.print(secrets_panel) + + console.print(f"\nπŸ“ Press Enter to open: {get_github_secrets_page_url(repo)}") + console.input() + + click.launch(get_github_secrets_page_url(repo)) + + # Post-launch message panel + launch_panel = Panel( + Text( + "πŸ™ I opened your GitHub secrets page!\n\n" + "Note: If you see a 404, you probably don't have access to this repo's secrets. " + "Ask a repo admin to add it for you, or (not recommended) you can temporarily " + "hard-code your API key into the workflow file.", + style="cyan", + ), + title="🌐 Browser Opened", + border_style="bright_cyan", + ) + console.print(launch_panel) + click.pause() + click.echo() click.echo( f"Please edit, commit and push this GitHub actions file to your repo, and you're all set!{LF}" f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" From 6471a5c0cb42018e87fbfc6437b8eb168c08f95e Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 14 Nov 2025 02:20:32 +0200 Subject: [PATCH 03/11] User experience improvements --- codeflash/cli_cmds/cmd_init.py | 109 +++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 4 deletions(-) diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index afd1cef65..381e68a80 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -947,13 +947,114 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n console.print(warning_panel) console.print() else: - raise Exception(response_data.get("error", "Unknown error")) + # API returned success=false, extract error details + error_data = response_data + error_msg = error_data.get("error", "Unknown error") + error_message = error_data.get("message", error_msg) + error_help = error_data.get("help", "") + installation_url = error_data.get("installation_url") + + # Show detailed error panel + error_panel_text = f"❌ {error_msg}\n\n{error_message}\n" + if error_help: + error_panel_text += f"\nπŸ’‘ {error_help}\n" + if installation_url: + error_panel_text += f"\nπŸ”— Install GitHub App: {installation_url}" + + error_panel = Panel( + Text(error_panel_text, style="red"), + title="❌ Setup Failed", + border_style="red", + ) + console.print(error_panel) + console.print() + + # For GitHub App not installed, don't fall back - show clear instructions + if response.status_code == 404 and installation_url: + logger.error( + f"[cmd_init.py:install_github_actions] GitHub App not installed on {owner}/{repo_name}" + ) + click.echo( + f"Please install the CodeFlash GitHub App on your repository to continue.{LF}" + f"Visit: {installation_url}{LF}" + ) + return + + # For permission errors, don't fall back - show clear instructions + if response.status_code == 403: + logger.error( + f"[cmd_init.py:install_github_actions] Permission denied for {owner}/{repo_name}" + ) + click.echo( + f"Please ensure you have write access to {owner}/{repo_name} and try again.{LF}" + ) + return + + # For other errors, fall back to local file creation + raise Exception(error_message) else: - # API call failed, fall back to local file creation - raise Exception(f"API returned status {response.status_code}") + # API call returned non-200 status, try to parse error response + try: + error_data = response.json() + error_msg = error_data.get("error", "API request failed") + error_message = error_data.get("message", f"API returned status {response.status_code}") + error_help = error_data.get("help", "") + installation_url = error_data.get("installation_url") + + # Show detailed error panel + error_panel_text = f"❌ {error_msg}\n\n{error_message}\n" + if error_help: + error_panel_text += f"\nπŸ’‘ {error_help}\n" + if installation_url: + error_panel_text += f"\nπŸ”— Install GitHub App: {installation_url}" + + error_panel = Panel( + Text(error_panel_text, style="red"), + title="❌ Setup Failed", + border_style="red", + ) + console.print(error_panel) + console.print() + + # For GitHub App not installed, don't fall back - show clear instructions + if response.status_code == 404 and installation_url: + logger.error( + f"[cmd_init.py:install_github_actions] GitHub App not installed on {owner}/{repo_name}" + ) + click.echo( + f"Please install the CodeFlash GitHub App on your repository to continue.{LF}" + f"Visit: {installation_url}{LF}" + ) + return + + # For permission errors, don't fall back - show clear instructions + if response.status_code == 403: + logger.error( + f"[cmd_init.py:install_github_actions] Permission denied for {owner}/{repo_name}" + ) + click.echo( + f"Please ensure you have write access to {owner}/{repo_name} and try again.{LF}" + ) + return + + # For authentication errors, don't fall back + if response.status_code == 401: + logger.error( + f"[cmd_init.py:install_github_actions] Authentication failed for {owner}/{repo_name}" + ) + click.echo( + f"Authentication failed. Please check your API key and try again.{LF}" + ) + return + + # For other errors, fall back to local file creation + raise Exception(error_message) + except (ValueError, KeyError): + # Couldn't parse error response, use generic message + raise Exception(f"API returned status {response.status_code}") except Exception as api_error: - # Fall back to local file creation if API call fails + # Fall back to local file creation if API call fails (for non-critical errors) logger.warning( f"[cmd_init.py:install_github_actions] API call failed, falling back to local file creation: {api_error}" ) From d971d0803735bba2fca475614452abf4ea391439 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 14 Nov 2025 02:32:38 +0200 Subject: [PATCH 04/11] fix lint errors --- codeflash/api/cfapi.py | 15 ++----- codeflash/cli_cmds/cmd_init.py | 72 ++++++++++++---------------------- 2 files changed, 28 insertions(+), 59 deletions(-) diff --git a/codeflash/api/cfapi.py b/codeflash/api/cfapi.py index f05ce9bdb..81855b4ed 100644 --- a/codeflash/api/cfapi.py +++ b/codeflash/api/cfapi.py @@ -240,13 +240,9 @@ def create_pr( def setup_github_actions( - owner: str, - repo: str, - base_branch: str, - workflow_content: str, - api_key: str | None = None, + owner: str, repo: str, base_branch: str, workflow_content: str, api_key: str | None = None ) -> Response: - """Setup GitHub Actions workflow by creating a PR with the workflow file and optionally setting up the repository secret. + """Set up GitHub Actions workflow by creating a PR with the workflow file and optionally setting up the repository secret. :param owner: Repository owner (username or organization) :param repo: Repository name @@ -255,12 +251,7 @@ def setup_github_actions( :param api_key: API key to store as repository secret (if provided, will attempt to set up secret automatically) :return: Response object with pr_url, pr_number, secret_setup_success, and secret_setup_error on success """ - payload = { - "owner": owner, - "repo": repo, - "baseBranch": base_branch, - "workflowContent": workflow_content, - } + payload = {"owner": owner, "repo": repo, "baseBranch": base_branch, "workflowContent": workflow_content} # Include apiKey in payload if provided - this will be encrypted and stored as a repository secret if api_key: payload["apiKey"] = api_key diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index 381e68a80..e3c1863c7 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -704,7 +704,7 @@ def create_empty_pyproject_toml(pyproject_toml_path: Path) -> None: apologize_and_exit() -def install_github_actions(override_formatter_check: bool = False) -> None: # noqa: FBT001, FBT002 +def install_github_actions(override_formatter_check: bool = False) -> None: # noqa: FBT001, FBT002, PLR0911 try: config, _config_file_path = parse_config_file(override_formatter_check=override_formatter_check) @@ -820,9 +820,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n api_key = get_codeflash_api_key() except OSError: api_key = None - logger.info( - "[cmd_init.py:install_github_actions] No API key found, will skip secret setup" - ) + logger.info("[cmd_init.py:install_github_actions] No API key found, will skip secret setup") try: owner, repo_name = get_repo_owner_and_name(repo, git_remote) @@ -877,11 +875,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n success_message += "Your repository is now configured for continuous optimization!" workflow_success_panel = Panel( - Text( - success_message, - style="green", - justify="center", - ), + Text(success_message, style="green", justify="center"), title="πŸŽ‰ Workflow PR Created!", border_style="bright_green", ) @@ -911,19 +905,13 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n else: # File already exists with same content pr_created_via_api = True # Mark as handled (no PR needed) - already_exists_message = ( - "βœ… Workflow file already exists with the same content.\n\n" - ) + already_exists_message = "βœ… Workflow file already exists with the same content.\n\n" if secret_setup_success: already_exists_message += "βœ… Repository secret CODEFLASH_API_KEY configured\n\n" already_exists_message += "No changes needed - your repository is already configured!" already_exists_panel = Panel( - Text( - already_exists_message, - style="green", - justify="center", - ), + Text(already_exists_message, style="green", justify="center"), title="βœ… Already Configured", border_style="bright_green", ) @@ -962,9 +950,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n error_panel_text += f"\nπŸ”— Install GitHub App: {installation_url}" error_panel = Panel( - Text(error_panel_text, style="red"), - title="❌ Setup Failed", - border_style="red", + Text(error_panel_text, style="red"), title="❌ Setup Failed", border_style="red" ) console.print(error_panel) console.print() @@ -985,13 +971,11 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n logger.error( f"[cmd_init.py:install_github_actions] Permission denied for {owner}/{repo_name}" ) - click.echo( - f"Please ensure you have write access to {owner}/{repo_name} and try again.{LF}" - ) + click.echo(f"Please ensure you have write access to {owner}/{repo_name} and try again.{LF}") return # For other errors, fall back to local file creation - raise Exception(error_message) + raise Exception(error_message) # noqa: TRY002, TRY301 else: # API call returned non-200 status, try to parse error response try: @@ -1009,9 +993,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n error_panel_text += f"\nπŸ”— Install GitHub App: {installation_url}" error_panel = Panel( - Text(error_panel_text, style="red"), - title="❌ Setup Failed", - border_style="red", + Text(error_panel_text, style="red"), title="❌ Setup Failed", border_style="red" ) console.print(error_panel) console.print() @@ -1032,9 +1014,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n logger.error( f"[cmd_init.py:install_github_actions] Permission denied for {owner}/{repo_name}" ) - click.echo( - f"Please ensure you have write access to {owner}/{repo_name} and try again.{LF}" - ) + click.echo(f"Please ensure you have write access to {owner}/{repo_name} and try again.{LF}") return # For authentication errors, don't fall back @@ -1042,16 +1022,15 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n logger.error( f"[cmd_init.py:install_github_actions] Authentication failed for {owner}/{repo_name}" ) - click.echo( - f"Authentication failed. Please check your API key and try again.{LF}" - ) + click.echo(f"Authentication failed. Please check your API key and try again.{LF}") return # For other errors, fall back to local file creation - raise Exception(error_message) - except (ValueError, KeyError): + raise Exception(error_message) # noqa: TRY002 + except (ValueError, KeyError) as parse_error: # Couldn't parse error response, use generic message - raise Exception(f"API returned status {response.status_code}") + status_msg = f"API returned status {response.status_code}" + raise Exception(status_msg) from parse_error # noqa: TRY002 except Exception as api_error: # Fall back to local file creation if API call fails (for non-critical errors) @@ -1088,18 +1067,17 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" f"Once you merge the PR and add the secret, the workflow will be active.{LF}" ) + # File already exists + elif secret_setup_success: + click.echo( + f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" + f"The workflow is ready to use.{LF}" + ) else: - # File already exists - if secret_setup_success: - click.echo( - f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" - f"The workflow is ready to use.{LF}" - ) - else: - click.echo( - f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" - f"Just add the secret and the workflow will be active.{LF}" - ) + click.echo( + f"πŸš€ Codeflash is now configured to automatically optimize new Github PRs!{LF}" + f"Just add the secret and the workflow will be active.{LF}" + ) else: # Fell back to local file creation - show manual secret setup try: From 7f6b758ff151fe0060d49561d0ed5113ff46f5da Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 14 Nov 2025 03:27:36 +0200 Subject: [PATCH 05/11] update Set up GitHub Action message during init command --- codeflash/cli_cmds/cmd_init.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index e3c1863c7..7d3cc4e7a 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -759,7 +759,9 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n creation_questions = [ inquirer.Confirm( - "confirm_creation", message="Set up GitHub Actions for continuous optimization?", default=True + "confirm_creation", + message="Set up GitHub Actions for continuous optimization? We'll create a pull request with the workflow file.", + default=True, ) ] From d3c55d2b992d401f7d600e6519bd6b3461643f19 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 14 Nov 2025 03:42:02 +0200 Subject: [PATCH 06/11] check if the workflow file exists before setting up the github action --- codeflash/api/cfapi.py | 18 +++++++- codeflash/cli_cmds/cmd_init.py | 84 +++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/codeflash/api/cfapi.py b/codeflash/api/cfapi.py index 81855b4ed..396011319 100644 --- a/codeflash/api/cfapi.py +++ b/codeflash/api/cfapi.py @@ -55,13 +55,17 @@ def make_cfapi_request( *, api_key: str | None = None, suppress_errors: bool = False, + params: dict[str, Any] | None = None, ) -> Response: """Make an HTTP request using the specified method, URL, headers, and JSON payload. :param endpoint: The endpoint URL to send the request to. :param method: The HTTP method to use ('GET', 'POST', etc.). :param payload: Optional JSON payload to include in the POST request body. + :param extra_headers: Optional extra headers to include in the request. + :param api_key: Optional API key to use for authentication. :param suppress_errors: If True, suppress error logging for HTTP errors. + :param params: Optional query parameters for GET requests. :return: The response object from the API. """ url = f"{get_cfapi_base_urls().cfapi_base_url}/cfapi{endpoint}" @@ -75,7 +79,7 @@ def make_cfapi_request( cfapi_headers["Content-Type"] = "application/json" response = requests.post(url, data=json_payload, headers=cfapi_headers, timeout=60) else: - response = requests.get(url, headers=cfapi_headers, timeout=60) + response = requests.get(url, headers=cfapi_headers, params=params, timeout=60) response.raise_for_status() return response # noqa: TRY300 except requests.exceptions.HTTPError: @@ -239,6 +243,18 @@ def create_pr( return make_cfapi_request(endpoint="/create-pr", method="POST", payload=payload) +def check_workflow_file_exists(owner: str, repo: str, base_branch: str) -> Response: + """Check if the GitHub Actions workflow file exists on the repository. + + :param owner: Repository owner (username or organization) + :param repo: Repository name + :param base_branch: Base branch to check (e.g., "main", "master") + :return: Response object with exists (bool) and content (str | None) fields + """ + params = {"owner": owner, "repo": repo, "baseBranch": base_branch} + return make_cfapi_request(endpoint="/check-workflow-file-exists", method="GET", params=params) + + def setup_github_actions( owner: str, repo: str, base_branch: str, workflow_content: str, api_key: str | None = None ) -> Response: diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index 7d3cc4e7a..e9155f52b 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -22,7 +22,11 @@ from rich.table import Table from rich.text import Text -from codeflash.api.cfapi import is_github_app_installed_on_repo, setup_github_actions +from codeflash.api.cfapi import ( + check_workflow_file_exists, + is_github_app_installed_on_repo, + setup_github_actions, +) from codeflash.cli_cmds.cli_common import apologize_and_exit from codeflash.cli_cmds.console import console, logger from codeflash.cli_cmds.extension import install_vscode_extension @@ -848,6 +852,84 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n # Try to create PR via API try: base_branch = get_current_branch(repo) if repo.active_branch else "main" + + # Check if workflow file already exists on remote + logger.info( + f"[cmd_init.py:install_github_actions] Checking if workflow file exists for {owner}/{repo_name} on branch {base_branch}" + ) + check_response = check_workflow_file_exists(owner, repo_name, base_branch) + + if check_response.status_code == 200: + check_data = check_response.json() + if check_data.get("exists"): + # Workflow file already exists - check if content matches + existing_content = check_data.get("content", "") + if existing_content == materialized_optimize_yml_content: + # File exists with same content - skip PR creation + pr_created_via_api = True + already_exists_message = "βœ… Workflow file already exists with the same content.\n\n" + already_exists_message += "No changes needed - your repository is already configured!" + + already_exists_panel = Panel( + Text(already_exists_message, style="green", justify="center"), + title="βœ… Already Configured", + border_style="bright_green", + ) + console.print(already_exists_panel) + console.print() + + # Still try to set up secret if API key is available + if api_key: + logger.info( + f"[cmd_init.py:install_github_actions] Workflow exists, attempting secret setup for {owner}/{repo_name}" + ) + # Call setup API just for secret setup (it will handle the already_exists case) + secret_response = setup_github_actions( + owner=owner, + repo=repo_name, + base_branch=base_branch, + workflow_content=materialized_optimize_yml_content, + api_key=api_key, + ) + if secret_response.status_code == 200: + secret_data = secret_response.json() + secret_setup_success = secret_data.get("secret_setup_success", False) + secret_setup_error = secret_data.get("secret_setup_error") + + if secret_setup_success: + console.print( + Panel( + Text( + "βœ… Repository secret CODEFLASH_API_KEY configured", + style="green", + justify="center", + ), + title="βœ… Secret Configured", + border_style="bright_green", + ) + ) + console.print() + elif secret_setup_error: + warning_message = ( + "⚠️ Secret setup failed. You'll need to add CODEFLASH_API_KEY manually.\n\n" + ) + warning_message += f"Error: {secret_setup_error}\n\n" + warning_message += f"πŸ“ Add secret at: {get_github_secrets_page_url(repo)}" + + warning_panel = Panel( + Text(warning_message, style="yellow"), + title="⚠️ Manual Secret Setup Required", + border_style="yellow", + ) + console.print(warning_panel) + console.print() + + logger.info( + f"[cmd_init.py:install_github_actions] Workflow file already exists for {owner}/{repo_name}, skipping PR creation" + ) + return + + # Workflow file doesn't exist or content differs - proceed with PR creation console.print("Creating PR with GitHub Actions workflow...") logger.info( f"[cmd_init.py:install_github_actions] Calling setup_github_actions API for {owner}/{repo_name} on branch {base_branch}" From 9b6c0c18bcf1f4df14eec35a8425d4139bf08772 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 14 Nov 2025 03:42:17 +0200 Subject: [PATCH 07/11] skip user's prompt for github action installatin if already installed --- codeflash/cli_cmds/cmd_init.py | 248 +++++++++++++++++---------------- 1 file changed, 128 insertions(+), 120 deletions(-) diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index e9155f52b..b05c0ef2d 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -725,6 +725,132 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n workflows_path = git_root / ".github" / "workflows" optimize_yaml_path = workflows_path / "codeflash.yaml" + # Get repository information early for remote check + git_remote = config.get("git_remote", "origin") + base_branch = get_current_branch(repo) if repo.active_branch else "main" + + # Generate workflow content early (needed for comparison) + from importlib.resources import files + + benchmark_mode = False + benchmarks_root = config.get("benchmarks_root", "").strip() + if benchmarks_root and benchmarks_root != "": + benchmark_panel = Panel( + Text( + "πŸ“Š Benchmark Mode Available\n\n" + "I noticed you've configured a benchmarks_root in your config. " + "Benchmark mode will show the performance impact of Codeflash's optimizations on your benchmarks.", + style="cyan", + ), + title="πŸ“Š Benchmark Mode", + border_style="bright_cyan", + ) + console.print(benchmark_panel) + console.print() + + benchmark_questions = [ + inquirer.Confirm("benchmark_mode", message="Run GitHub Actions in benchmark mode?", default=True) + ] + + benchmark_answers = inquirer.prompt(benchmark_questions, theme=CodeflashTheme()) + benchmark_mode = benchmark_answers["benchmark_mode"] if benchmark_answers else False + + optimize_yml_content = ( + files("codeflash").joinpath("cli_cmds", "workflows", "codeflash-optimize.yaml").read_text(encoding="utf-8") + ) + materialized_optimize_yml_content = customize_codeflash_yaml_content( + optimize_yml_content, config, git_root, benchmark_mode + ) + + # Get API key for secret setup + try: + api_key = get_codeflash_api_key() + except OSError: + api_key = None + logger.info("[cmd_init.py:install_github_actions] No API key found, will skip secret setup") + + # Check if workflow file already exists on remote BEFORE showing prompt + try: + owner, repo_name = get_repo_owner_and_name(repo, git_remote) + logger.info( + f"[cmd_init.py:install_github_actions] Checking if workflow file exists for {owner}/{repo_name} on branch {base_branch}" + ) + check_response = check_workflow_file_exists(owner, repo_name, base_branch) + + if check_response.status_code == 200: + check_data = check_response.json() + if check_data.get("exists"): + existing_content = check_data.get("content", "") + if existing_content == materialized_optimize_yml_content: + # Workflow file already exists with same content - skip prompt and setup + pr_created_via_api = True + already_exists_message = "βœ… Workflow file already exists with the same content.\n\n" + already_exists_message += "No changes needed - your repository is already configured!" + + already_exists_panel = Panel( + Text(already_exists_message, style="green", justify="center"), + title="βœ… Already Configured", + border_style="bright_green", + ) + console.print(already_exists_panel) + console.print() + + # Still try to set up secret if API key is available + if api_key: + logger.info( + f"[cmd_init.py:install_github_actions] Workflow exists, attempting secret setup for {owner}/{repo_name}" + ) + secret_response = setup_github_actions( + owner=owner, + repo=repo_name, + base_branch=base_branch, + workflow_content=materialized_optimize_yml_content, + api_key=api_key, + ) + if secret_response.status_code == 200: + secret_data = secret_response.json() + secret_setup_success = secret_data.get("secret_setup_success", False) + secret_setup_error = secret_data.get("secret_setup_error") + + if secret_setup_success: + console.print( + Panel( + Text( + "βœ… Repository secret CODEFLASH_API_KEY configured", + style="green", + justify="center", + ), + title="βœ… Secret Configured", + border_style="bright_green", + ) + ) + console.print() + elif secret_setup_error: + warning_message = ( + "⚠️ Secret setup failed. You'll need to add CODEFLASH_API_KEY manually.\n\n" + ) + warning_message += f"Error: {secret_setup_error}\n\n" + warning_message += f"πŸ“ Add secret at: {get_github_secrets_page_url(repo)}" + + warning_panel = Panel( + Text(warning_message, style="yellow"), + title="⚠️ Manual Secret Setup Required", + border_style="yellow", + ) + console.print(warning_panel) + console.print() + + logger.info( + f"[cmd_init.py:install_github_actions] Workflow file already exists for {owner}/{repo_name}, skipping setup" + ) + return + except Exception as e: + logger.warning( + f"[cmd_init.py:install_github_actions] Could not check remote workflow file, will proceed with prompt: {e}" + ) + # Continue to show prompt if we can't check remote + + # Show prompt only if workflow doesn't exist on remote actions_panel = Panel( Text( "πŸ€– GitHub Actions Setup\n\n" @@ -738,7 +864,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n console.print(actions_panel) console.print() - # Check if the workflow file already exists + # Check if the workflow file already exists locally if optimize_yaml_path.exists(): overwrite_questions = [ inquirer.Confirm( @@ -782,52 +908,12 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n {"confirm_creation": creation_answers["confirm_creation"]}, ) workflows_path.mkdir(parents=True, exist_ok=True) - from importlib.resources import files - benchmark_mode = False - benchmarks_root = config.get("benchmarks_root", "").strip() - if benchmarks_root and benchmarks_root != "": - benchmark_panel = Panel( - Text( - "πŸ“Š Benchmark Mode Available\n\n" - "I noticed you've configured a benchmarks_root in your config. " - "Benchmark mode will show the performance impact of Codeflash's optimizations on your benchmarks.", - style="cyan", - ), - title="πŸ“Š Benchmark Mode", - border_style="bright_cyan", - ) - console.print(benchmark_panel) - console.print() - - benchmark_questions = [ - inquirer.Confirm("benchmark_mode", message="Run GitHub Actions in benchmark mode?", default=True) - ] - - benchmark_answers = inquirer.prompt(benchmark_questions, theme=CodeflashTheme()) - benchmark_mode = benchmark_answers["benchmark_mode"] if benchmark_answers else False - - optimize_yml_content = ( - files("codeflash").joinpath("cli_cmds", "workflows", "codeflash-optimize.yaml").read_text(encoding="utf-8") - ) - materialized_optimize_yml_content = customize_codeflash_yaml_content( - optimize_yml_content, config, git_root, benchmark_mode - ) - - # Get repository information for API call - git_remote = config.get("git_remote", "origin") pr_created_via_api = False pr_url = None secret_setup_success = False secret_setup_error = None - # Get API key for secret setup - try: - api_key = get_codeflash_api_key() - except OSError: - api_key = None - logger.info("[cmd_init.py:install_github_actions] No API key found, will skip secret setup") - try: owner, repo_name = get_repo_owner_and_name(repo, git_remote) except Exception as e: @@ -851,85 +937,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n else: # Try to create PR via API try: - base_branch = get_current_branch(repo) if repo.active_branch else "main" - - # Check if workflow file already exists on remote - logger.info( - f"[cmd_init.py:install_github_actions] Checking if workflow file exists for {owner}/{repo_name} on branch {base_branch}" - ) - check_response = check_workflow_file_exists(owner, repo_name, base_branch) - - if check_response.status_code == 200: - check_data = check_response.json() - if check_data.get("exists"): - # Workflow file already exists - check if content matches - existing_content = check_data.get("content", "") - if existing_content == materialized_optimize_yml_content: - # File exists with same content - skip PR creation - pr_created_via_api = True - already_exists_message = "βœ… Workflow file already exists with the same content.\n\n" - already_exists_message += "No changes needed - your repository is already configured!" - - already_exists_panel = Panel( - Text(already_exists_message, style="green", justify="center"), - title="βœ… Already Configured", - border_style="bright_green", - ) - console.print(already_exists_panel) - console.print() - - # Still try to set up secret if API key is available - if api_key: - logger.info( - f"[cmd_init.py:install_github_actions] Workflow exists, attempting secret setup for {owner}/{repo_name}" - ) - # Call setup API just for secret setup (it will handle the already_exists case) - secret_response = setup_github_actions( - owner=owner, - repo=repo_name, - base_branch=base_branch, - workflow_content=materialized_optimize_yml_content, - api_key=api_key, - ) - if secret_response.status_code == 200: - secret_data = secret_response.json() - secret_setup_success = secret_data.get("secret_setup_success", False) - secret_setup_error = secret_data.get("secret_setup_error") - - if secret_setup_success: - console.print( - Panel( - Text( - "βœ… Repository secret CODEFLASH_API_KEY configured", - style="green", - justify="center", - ), - title="βœ… Secret Configured", - border_style="bright_green", - ) - ) - console.print() - elif secret_setup_error: - warning_message = ( - "⚠️ Secret setup failed. You'll need to add CODEFLASH_API_KEY manually.\n\n" - ) - warning_message += f"Error: {secret_setup_error}\n\n" - warning_message += f"πŸ“ Add secret at: {get_github_secrets_page_url(repo)}" - - warning_panel = Panel( - Text(warning_message, style="yellow"), - title="⚠️ Manual Secret Setup Required", - border_style="yellow", - ) - console.print(warning_panel) - console.print() - - logger.info( - f"[cmd_init.py:install_github_actions] Workflow file already exists for {owner}/{repo_name}, skipping PR creation" - ) - return - - # Workflow file doesn't exist or content differs - proceed with PR creation + # Workflow file doesn't exist on remote or content differs - proceed with PR creation console.print("Creating PR with GitHub Actions workflow...") logger.info( f"[cmd_init.py:install_github_actions] Calling setup_github_actions API for {owner}/{repo_name} on branch {base_branch}" From 0c6065cd1a5e8a9351fbe1c752ca93e7831b0595 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 14 Nov 2025 04:37:09 +0200 Subject: [PATCH 08/11] local check for the codeflash.yaml instead of remote --- codeflash/cli_cmds/cmd_init.py | 198 +++++++++++++++------------------ 1 file changed, 90 insertions(+), 108 deletions(-) diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index b05c0ef2d..4a037b459 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -725,11 +725,97 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n workflows_path = git_root / ".github" / "workflows" optimize_yaml_path = workflows_path / "codeflash.yaml" - # Get repository information early for remote check + # Check if workflow file already exists locally BEFORE showing prompt + if optimize_yaml_path.exists(): + # Workflow file already exists locally - skip prompt and setup + already_exists_message = "βœ… GitHub Actions workflow file already exists.\n\n" + already_exists_message += "No changes needed - your repository is already configured!" + + already_exists_panel = Panel( + Text(already_exists_message, style="green", justify="center"), + title="βœ… Already Configured", + border_style="bright_green", + ) + console.print(already_exists_panel) + console.print() + + # Still try to set up secret if API key is available + try: + api_key = get_codeflash_api_key() + git_remote = config.get("git_remote", "origin") + owner, repo_name = get_repo_owner_and_name(repo, git_remote) + base_branch = get_current_branch(repo) if repo.active_branch else "main" + + # Generate workflow content for secret setup + from importlib.resources import files + + optimize_yml_content = ( + files("codeflash") + .joinpath("cli_cmds", "workflows", "codeflash-optimize.yaml") + .read_text(encoding="utf-8") + ) + materialized_optimize_yml_content = customize_codeflash_yaml_content( + optimize_yml_content, config, git_root, False + ) + + logger.info( + f"[cmd_init.py:install_github_actions] Workflow exists locally, attempting secret setup for {owner}/{repo_name}" + ) + secret_response = setup_github_actions( + owner=owner, + repo=repo_name, + base_branch=base_branch, + workflow_content=materialized_optimize_yml_content, + api_key=api_key, + ) + if secret_response.status_code == 200: + secret_data = secret_response.json() + secret_setup_success = secret_data.get("secret_setup_success", False) + secret_setup_error = secret_data.get("secret_setup_error") + + if secret_setup_success: + console.print( + Panel( + Text( + "βœ… Repository secret CODEFLASH_API_KEY configured", + style="green", + justify="center", + ), + title="βœ… Secret Configured", + border_style="bright_green", + ) + ) + console.print() + elif secret_setup_error: + warning_message = ( + "⚠️ Secret setup failed. You'll need to add CODEFLASH_API_KEY manually.\n\n" + ) + warning_message += f"Error: {secret_setup_error}\n\n" + warning_message += f"πŸ“ Add secret at: {get_github_secrets_page_url(repo)}" + + warning_panel = Panel( + Text(warning_message, style="yellow"), + title="⚠️ Manual Secret Setup Required", + border_style="yellow", + ) + console.print(warning_panel) + console.print() + except Exception as e: + logger.debug( + f"[cmd_init.py:install_github_actions] Could not set up secret (workflow exists locally): {e}" + ) + # Secret setup is optional, so we continue + + logger.info( + f"[cmd_init.py:install_github_actions] Workflow file already exists locally, skipping setup" + ) + return + + # Get repository information for API call git_remote = config.get("git_remote", "origin") base_branch = get_current_branch(repo) if repo.active_branch else "main" - # Generate workflow content early (needed for comparison) + # Generate workflow content from importlib.resources import files benchmark_mode = False @@ -769,88 +855,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n api_key = None logger.info("[cmd_init.py:install_github_actions] No API key found, will skip secret setup") - # Check if workflow file already exists on remote BEFORE showing prompt - try: - owner, repo_name = get_repo_owner_and_name(repo, git_remote) - logger.info( - f"[cmd_init.py:install_github_actions] Checking if workflow file exists for {owner}/{repo_name} on branch {base_branch}" - ) - check_response = check_workflow_file_exists(owner, repo_name, base_branch) - - if check_response.status_code == 200: - check_data = check_response.json() - if check_data.get("exists"): - existing_content = check_data.get("content", "") - if existing_content == materialized_optimize_yml_content: - # Workflow file already exists with same content - skip prompt and setup - pr_created_via_api = True - already_exists_message = "βœ… Workflow file already exists with the same content.\n\n" - already_exists_message += "No changes needed - your repository is already configured!" - - already_exists_panel = Panel( - Text(already_exists_message, style="green", justify="center"), - title="βœ… Already Configured", - border_style="bright_green", - ) - console.print(already_exists_panel) - console.print() - - # Still try to set up secret if API key is available - if api_key: - logger.info( - f"[cmd_init.py:install_github_actions] Workflow exists, attempting secret setup for {owner}/{repo_name}" - ) - secret_response = setup_github_actions( - owner=owner, - repo=repo_name, - base_branch=base_branch, - workflow_content=materialized_optimize_yml_content, - api_key=api_key, - ) - if secret_response.status_code == 200: - secret_data = secret_response.json() - secret_setup_success = secret_data.get("secret_setup_success", False) - secret_setup_error = secret_data.get("secret_setup_error") - - if secret_setup_success: - console.print( - Panel( - Text( - "βœ… Repository secret CODEFLASH_API_KEY configured", - style="green", - justify="center", - ), - title="βœ… Secret Configured", - border_style="bright_green", - ) - ) - console.print() - elif secret_setup_error: - warning_message = ( - "⚠️ Secret setup failed. You'll need to add CODEFLASH_API_KEY manually.\n\n" - ) - warning_message += f"Error: {secret_setup_error}\n\n" - warning_message += f"πŸ“ Add secret at: {get_github_secrets_page_url(repo)}" - - warning_panel = Panel( - Text(warning_message, style="yellow"), - title="⚠️ Manual Secret Setup Required", - border_style="yellow", - ) - console.print(warning_panel) - console.print() - - logger.info( - f"[cmd_init.py:install_github_actions] Workflow file already exists for {owner}/{repo_name}, skipping setup" - ) - return - except Exception as e: - logger.warning( - f"[cmd_init.py:install_github_actions] Could not check remote workflow file, will proceed with prompt: {e}" - ) - # Continue to show prompt if we can't check remote - - # Show prompt only if workflow doesn't exist on remote + # Show prompt only if workflow doesn't exist locally actions_panel = Panel( Text( "πŸ€– GitHub Actions Setup\n\n" @@ -864,33 +869,10 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n console.print(actions_panel) console.print() - # Check if the workflow file already exists locally - if optimize_yaml_path.exists(): - overwrite_questions = [ - inquirer.Confirm( - "confirm_overwrite", - message=f"GitHub Actions workflow already exists at {optimize_yaml_path}. Overwrite?", - default=False, - ) - ] - - overwrite_answers = inquirer.prompt(overwrite_questions, theme=CodeflashTheme()) - if not overwrite_answers or not overwrite_answers["confirm_overwrite"]: - skip_panel = Panel( - Text("⏩️ Skipping workflow creation.", style="yellow"), title="⏩️ Skipped", border_style="yellow" - ) - console.print(skip_panel) - ph("cli-github-workflow-skipped") - return - ph( - "cli-github-optimization-confirm-workflow-overwrite", - {"confirm_overwrite": overwrite_answers["confirm_overwrite"]}, - ) - creation_questions = [ inquirer.Confirm( "confirm_creation", - message="Set up GitHub Actions for continuous optimization? We'll create a pull request with the workflow file.", + message="Set up GitHub Actions for continuous optimization? We'll open a pull request with the workflow file.", default=True, ) ] From 03875b968fbabb7350ccc02896e886fd8ebf0fba Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Mon, 17 Nov 2025 23:34:45 +0200 Subject: [PATCH 09/11] adding collect_repo_files_for_workflow and generate_workflow_steps for github action --- codeflash/api/aiservice.py | 37 ++++++ codeflash/cli_cmds/cmd_init.py | 225 ++++++++++++++++++++++++++++++++- 2 files changed, 260 insertions(+), 2 deletions(-) diff --git a/codeflash/api/aiservice.py b/codeflash/api/aiservice.py index d206a00b6..348e6eb32 100644 --- a/codeflash/api/aiservice.py +++ b/codeflash/api/aiservice.py @@ -631,6 +631,43 @@ def get_optimization_review( console.rule() return "" + def generate_workflow_steps( + self, + repo_files: dict[str, str], + directory_structure: dict[str, Any], + codeflash_config: dict[str, Any] | None = None, + ) -> str | None: + """Generate GitHub Actions workflow steps based on repository analysis. + + :param repo_files: Dictionary mapping file paths to their contents + :param directory_structure: 2-level nested directory structure + :param codeflash_config: Optional codeflash configuration + :return: YAML string for workflow steps section, or None on error + """ + payload = { + "repo_files": repo_files, + "directory_structure": directory_structure, + "codeflash_config": codeflash_config, + } + + logger.info("Generating workflow steps via AI service...") + try: + response = self.make_ai_service_request("/workflow-gen", payload=payload, timeout=60) + except requests.exceptions.RequestException as e: + logger.warning(f"Error generating workflow steps: {e}") + return None + + if response.status_code == 200: + return cast("str", response.json().get("workflow_steps")) + else: + logger.warning(f"Failed to generate workflow steps: {response.status_code}") + try: + error = cast("str", response.json().get("error", "Unknown error")) + logger.debug(f"Error details: {error}") + except Exception: + pass + return None + class LocalAiServiceClient(AiServiceClient): """Client for interacting with the local AI service.""" diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index 4a037b459..269f05197 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -22,6 +22,7 @@ from rich.table import Table from rich.text import Text +from codeflash.api.aiservice import AiServiceClient from codeflash.api.cfapi import ( check_workflow_file_exists, is_github_app_installed_on_repo, @@ -754,7 +755,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n .joinpath("cli_cmds", "workflows", "codeflash-optimize.yaml") .read_text(encoding="utf-8") ) - materialized_optimize_yml_content = customize_codeflash_yaml_content( + materialized_optimize_yml_content = generate_dynamic_workflow_content( optimize_yml_content, config, git_root, False ) @@ -844,7 +845,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n optimize_yml_content = ( files("codeflash").joinpath("cli_cmds", "workflows", "codeflash-optimize.yaml").read_text(encoding="utf-8") ) - materialized_optimize_yml_content = customize_codeflash_yaml_content( + materialized_optimize_yml_content = generate_dynamic_workflow_content( optimize_yml_content, config, git_root, benchmark_mode ) @@ -1283,6 +1284,226 @@ def get_github_action_working_directory(toml_path: Path, git_root: Path) -> str: working-directory: ./{working_dir}""" +def collect_repo_files_for_workflow(git_root: Path) -> dict[str, Any]: + """Collect important repository files and directory structure for workflow generation. + + :param git_root: Root directory of the git repository + :return: Dictionary with 'files' (path -> content) and 'directory_structure' (nested dict) + """ + logger.info(f"[cmd_init.py:collect_repo_files_for_workflow] Collecting repo files from {git_root}") + + # Important files to collect with contents + important_files = [ + "pyproject.toml", + "requirements.txt", + "requirements-dev.txt", + "requirements/requirements.txt", + "requirements/dev.txt", + "Pipfile", + "Pipfile.lock", + "poetry.lock", + "uv.lock", + "setup.py", + "setup.cfg", + "Dockerfile", + "docker-compose.yml", + "docker-compose.yaml", + "Makefile", + "README.md", + "README.rst", + ] + + # Also collect GitHub workflows + workflows_path = git_root / ".github" / "workflows" + if workflows_path.exists(): + for workflow_file in workflows_path.glob("*.yml"): + important_files.append(str(workflow_file.relative_to(git_root))) + for workflow_file in workflows_path.glob("*.yaml"): + important_files.append(str(workflow_file.relative_to(git_root))) + + files_dict: dict[str, str] = {} + max_file_size = 8 * 1024 # 8KB limit per file + + for file_path_str in important_files: + file_path = git_root / file_path_str + if file_path.exists() and file_path.is_file(): + try: + content = file_path.read_text(encoding="utf-8", errors="ignore") + # Limit file size + if len(content) > max_file_size: + content = content[:max_file_size] + "\n... (truncated)" + files_dict[file_path_str] = content + logger.debug(f"[cmd_init.py:collect_repo_files_for_workflow] Collected {file_path_str} ({len(content)} chars)") + except Exception as e: + logger.warning(f"[cmd_init.py:collect_repo_files_for_workflow] Failed to read {file_path_str}: {e}") + + # Collect 2-level directory structure + directory_structure: dict[str, Any] = {} + try: + for item in sorted(git_root.iterdir()): + if item.name.startswith(".") and item.name not in [".github", ".git"]: + continue # Skip hidden files/folders except .github + + if item.is_dir(): + # Level 1: directory + dir_dict: dict[str, Any] = {"type": "directory", "contents": {}} + try: + # Level 2: contents of directory + for subitem in sorted(item.iterdir()): + if subitem.name.startswith("."): + continue + if subitem.is_dir(): + dir_dict["contents"][subitem.name] = {"type": "directory"} + else: + dir_dict["contents"][subitem.name] = {"type": "file"} + except PermissionError: + pass # Skip directories we can't read + directory_structure[item.name] = dir_dict + elif item.is_file(): + directory_structure[item.name] = {"type": "file"} + except Exception as e: + logger.warning(f"[cmd_init.py:collect_repo_files_for_workflow] Error collecting directory structure: {e}") + + logger.info( + f"[cmd_init.py:collect_repo_files_for_workflow] Collected {len(files_dict)} files and {len(directory_structure)} top-level items" + ) + + return {"files": files_dict, "directory_structure": directory_structure} + + +def generate_dynamic_workflow_content( + optimize_yml_content: str, + config: tuple[dict[str, Any], Path], + git_root: Path, + benchmark_mode: bool = False, # noqa: FBT001, FBT002 +) -> str: + """Generate workflow content with dynamic steps from AI service, falling back to static template. + + :param optimize_yml_content: Base workflow template content + :param config: Codeflash configuration tuple (dict, Path) + :param git_root: Root directory of the git repository + :param benchmark_mode: Whether to enable benchmark mode + :return: Complete workflow YAML content + """ + # First, do the basic replacements that are always needed + module_path = str(Path(config["module_root"]).relative_to(git_root) / "**") + optimize_yml_content = optimize_yml_content.replace("{{ codeflash_module_path }}", module_path) + + # Get working directory + toml_path = Path.cwd() / "pyproject.toml" + try: + with toml_path.open(encoding="utf8") as pyproject_file: + pyproject_data = tomlkit.parse(pyproject_file.read()) + except FileNotFoundError: + click.echo( + f"I couldn't find a pyproject.toml in the current directory.{LF}" + 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." + ) + apologize_and_exit() + + working_dir = get_github_action_working_directory(toml_path, git_root) + optimize_yml_content = optimize_yml_content.replace("{{ working_directory }}", working_dir) + + # Try to generate dynamic steps using AI service + try: + logger.info("[cmd_init.py:generate_dynamic_workflow_content] Attempting to generate dynamic workflow steps") + repo_data = collect_repo_files_for_workflow(git_root) + + # Prepare codeflash config for AI + codeflash_config = { + "module_root": config["module_root"], + "tests_root": config.get("tests_root", ""), + "benchmark_mode": benchmark_mode, + } + + aiservice_client = AiServiceClient() + dynamic_steps = aiservice_client.generate_workflow_steps( + repo_files=repo_data["files"], + directory_structure=repo_data["directory_structure"], + codeflash_config=codeflash_config, + ) + + if dynamic_steps: + logger.info("[cmd_init.py:generate_dynamic_workflow_content] Successfully generated dynamic workflow steps") + # Replace the entire steps section with AI-generated steps + # Find the steps section in the template + steps_start = optimize_yml_content.find(" steps:") + if steps_start != -1: + # Find the end of the steps section (next line at same or less indentation) + lines = optimize_yml_content.split("\n") + steps_start_line = optimize_yml_content[:steps_start].count("\n") + steps_end_line = len(lines) + + # Find where steps section ends (next job or end of file) + for i in range(steps_start_line + 1, len(lines)): + line = lines[i] + # Stop if we hit a line that's not indented (new job or end of jobs) + if line and not line.startswith(" ") and not line.startswith("\t"): + steps_end_line = i + break + + # Extract steps content from AI response (remove "steps:" prefix if present) + steps_content = dynamic_steps + if steps_content.startswith("steps:"): + # Remove "steps:" and leading newline + steps_content = steps_content[6:].lstrip("\n") + + # Ensure proper indentation (8 spaces for steps section in YAML) + indented_steps = [] + for line in steps_content.split("\n"): + if line.strip(): + # If line doesn't start with enough spaces, add them + if not line.startswith(" "): + indented_steps.append(" " + line) + else: + # Preserve existing indentation but ensure minimum 8 spaces + current_indent = len(line) - len(line.lstrip()) + if current_indent < 8: + indented_steps.append(" " * 8 + line.lstrip()) + else: + indented_steps.append(line) + else: + indented_steps.append("") + + # Add codeflash command step at the end + dep_manager = determine_dependency_manager(pyproject_data) + codeflash_cmd = get_codeflash_github_action_command(dep_manager) + if benchmark_mode: + codeflash_cmd += " --benchmark" + + # Format codeflash command properly + if "|" in codeflash_cmd: + # Multi-line command + cmd_lines = codeflash_cmd.split("\n") + codeflash_step = f" - name: ⚑️Codeflash Optimization\n run: {cmd_lines[0].strip()}" + for cmd_line in cmd_lines[1:]: + codeflash_step += f"\n {cmd_line.strip()}" + else: + codeflash_step = f" - name: ⚑️Codeflash Optimization\n run: {codeflash_cmd}" + + indented_steps.append(codeflash_step) + + # Reconstruct the workflow + new_lines = lines[:steps_start_line] + [" steps:"] + indented_steps + lines[steps_end_line:] + optimize_yml_content = "\n".join(new_lines) + + logger.info("[cmd_init.py:generate_dynamic_workflow_content] Dynamic workflow generation successful") + return optimize_yml_content + else: + logger.warning("[cmd_init.py:generate_dynamic_workflow_content] Could not find steps section in template") + else: + logger.info("[cmd_init.py:generate_dynamic_workflow_content] AI service returned no steps, falling back to static") + + except Exception as e: + logger.warning( + f"[cmd_init.py:generate_dynamic_workflow_content] Error generating dynamic workflow, falling back to static: {e}" + ) + + # Fallback to static template + logger.info("[cmd_init.py:generate_dynamic_workflow_content] Using static workflow template") + return customize_codeflash_yaml_content(optimize_yml_content, config, git_root, benchmark_mode) + + def customize_codeflash_yaml_content( optimize_yml_content: str, config: tuple[dict[str, Any], Path], From cb20ff86f555ddc761128d39ef6922df04b2b299 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Tue, 18 Nov 2025 06:06:36 +0200 Subject: [PATCH 10/11] logs clean up & update --- codeflash/api/aiservice.py | 27 ++++++++++++++++++++------- codeflash/cli_cmds/cmd_init.py | 30 +++++++++++------------------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/codeflash/api/aiservice.py b/codeflash/api/aiservice.py index 348e6eb32..81c574745 100644 --- a/codeflash/api/aiservice.py +++ b/codeflash/api/aiservice.py @@ -650,22 +650,35 @@ def generate_workflow_steps( "codeflash_config": codeflash_config, } - logger.info("Generating workflow steps via AI service...") + logger.debug( + f"[aiservice.py:generate_workflow_steps] Sending request to AI service with {len(repo_files)} files, " + f"{len(directory_structure)} top-level directories" + ) + try: response = self.make_ai_service_request("/workflow-gen", payload=payload, timeout=60) except requests.exceptions.RequestException as e: - logger.warning(f"Error generating workflow steps: {e}") + logger.warning(f"[aiservice.py:generate_workflow_steps] Request exception: {e}") return None if response.status_code == 200: - return cast("str", response.json().get("workflow_steps")) + response_data = response.json() + workflow_steps = cast("str", response_data.get("workflow_steps")) + logger.debug( + f"[aiservice.py:generate_workflow_steps] Successfully received workflow steps " + f"({len(workflow_steps) if workflow_steps else 0} chars)" + ) + return workflow_steps else: - logger.warning(f"Failed to generate workflow steps: {response.status_code}") + logger.warning( + f"[aiservice.py:generate_workflow_steps] Failed with status {response.status_code}" + ) try: - error = cast("str", response.json().get("error", "Unknown error")) - logger.debug(f"Error details: {error}") + error_response = response.json() + error = cast("str", error_response.get("error", "Unknown error")) + logger.debug(f"[aiservice.py:generate_workflow_steps] Error: {error}") except Exception: - pass + logger.debug(f"[aiservice.py:generate_workflow_steps] Could not parse error response") return None diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index 269f05197..7ab73dd13 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -842,13 +842,6 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n benchmark_answers = inquirer.prompt(benchmark_questions, theme=CodeflashTheme()) benchmark_mode = benchmark_answers["benchmark_mode"] if benchmark_answers else False - optimize_yml_content = ( - files("codeflash").joinpath("cli_cmds", "workflows", "codeflash-optimize.yaml").read_text(encoding="utf-8") - ) - materialized_optimize_yml_content = generate_dynamic_workflow_content( - optimize_yml_content, config, git_root, benchmark_mode - ) - # Get API key for secret setup try: api_key = get_codeflash_api_key() @@ -890,6 +883,16 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n "cli-github-optimization-confirm-workflow-creation", {"confirm_creation": creation_answers["confirm_creation"]}, ) + + # Generate workflow content AFTER user confirmation + logger.info("[cmd_init.py:install_github_actions] User confirmed, generating workflow content...") + optimize_yml_content = ( + files("codeflash").joinpath("cli_cmds", "workflows", "codeflash-optimize.yaml").read_text(encoding="utf-8") + ) + materialized_optimize_yml_content = generate_dynamic_workflow_content( + optimize_yml_content, config, git_root, benchmark_mode + ) + workflows_path.mkdir(parents=True, exist_ok=True) pr_created_via_api = False @@ -1290,8 +1293,6 @@ def collect_repo_files_for_workflow(git_root: Path) -> dict[str, Any]: :param git_root: Root directory of the git repository :return: Dictionary with 'files' (path -> content) and 'directory_structure' (nested dict) """ - logger.info(f"[cmd_init.py:collect_repo_files_for_workflow] Collecting repo files from {git_root}") - # Important files to collect with contents important_files = [ "pyproject.toml", @@ -1333,7 +1334,6 @@ def collect_repo_files_for_workflow(git_root: Path) -> dict[str, Any]: if len(content) > max_file_size: content = content[:max_file_size] + "\n... (truncated)" files_dict[file_path_str] = content - logger.debug(f"[cmd_init.py:collect_repo_files_for_workflow] Collected {file_path_str} ({len(content)} chars)") except Exception as e: logger.warning(f"[cmd_init.py:collect_repo_files_for_workflow] Failed to read {file_path_str}: {e}") @@ -1364,10 +1364,6 @@ def collect_repo_files_for_workflow(git_root: Path) -> dict[str, Any]: except Exception as e: logger.warning(f"[cmd_init.py:collect_repo_files_for_workflow] Error collecting directory structure: {e}") - logger.info( - f"[cmd_init.py:collect_repo_files_for_workflow] Collected {len(files_dict)} files and {len(directory_structure)} top-level items" - ) - return {"files": files_dict, "directory_structure": directory_structure} @@ -1406,7 +1402,6 @@ def generate_dynamic_workflow_content( # Try to generate dynamic steps using AI service try: - logger.info("[cmd_init.py:generate_dynamic_workflow_content] Attempting to generate dynamic workflow steps") repo_data = collect_repo_files_for_workflow(git_root) # Prepare codeflash config for AI @@ -1424,7 +1419,6 @@ def generate_dynamic_workflow_content( ) if dynamic_steps: - logger.info("[cmd_init.py:generate_dynamic_workflow_content] Successfully generated dynamic workflow steps") # Replace the entire steps section with AI-generated steps # Find the steps section in the template steps_start = optimize_yml_content.find(" steps:") @@ -1487,12 +1481,11 @@ def generate_dynamic_workflow_content( new_lines = lines[:steps_start_line] + [" steps:"] + indented_steps + lines[steps_end_line:] optimize_yml_content = "\n".join(new_lines) - logger.info("[cmd_init.py:generate_dynamic_workflow_content] Dynamic workflow generation successful") return optimize_yml_content else: logger.warning("[cmd_init.py:generate_dynamic_workflow_content] Could not find steps section in template") else: - logger.info("[cmd_init.py:generate_dynamic_workflow_content] AI service returned no steps, falling back to static") + logger.debug("[cmd_init.py:generate_dynamic_workflow_content] AI service returned no steps, falling back to static") except Exception as e: logger.warning( @@ -1500,7 +1493,6 @@ def generate_dynamic_workflow_content( ) # Fallback to static template - logger.info("[cmd_init.py:generate_dynamic_workflow_content] Using static workflow template") return customize_codeflash_yaml_content(optimize_yml_content, config, git_root, benchmark_mode) From fc88658c5e0e7d94c46e57cd3c7921a171f5b687 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Wed, 26 Nov 2025 03:08:29 +0200 Subject: [PATCH 11/11] fix formatting --- codeflash/api/aiservice.py | 19 ++++++++----------- codeflash/cli_cmds/cmd_init.py | 25 ++++++++----------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/codeflash/api/aiservice.py b/codeflash/api/aiservice.py index 81c574745..54d07d980 100644 --- a/codeflash/api/aiservice.py +++ b/codeflash/api/aiservice.py @@ -669,17 +669,14 @@ def generate_workflow_steps( f"({len(workflow_steps) if workflow_steps else 0} chars)" ) return workflow_steps - else: - logger.warning( - f"[aiservice.py:generate_workflow_steps] Failed with status {response.status_code}" - ) - try: - error_response = response.json() - error = cast("str", error_response.get("error", "Unknown error")) - logger.debug(f"[aiservice.py:generate_workflow_steps] Error: {error}") - except Exception: - logger.debug(f"[aiservice.py:generate_workflow_steps] Could not parse error response") - return None + logger.warning(f"[aiservice.py:generate_workflow_steps] Failed with status {response.status_code}") + try: + error_response = response.json() + error = cast("str", error_response.get("error", "Unknown error")) + logger.debug(f"[aiservice.py:generate_workflow_steps] Error: {error}") + except Exception: + logger.debug("[aiservice.py:generate_workflow_steps] Could not parse error response") + return None class LocalAiServiceClient(AiServiceClient): diff --git a/codeflash/cli_cmds/cmd_init.py b/codeflash/cli_cmds/cmd_init.py index 7ab73dd13..f1647caec 100644 --- a/codeflash/cli_cmds/cmd_init.py +++ b/codeflash/cli_cmds/cmd_init.py @@ -23,11 +23,7 @@ from rich.text import Text from codeflash.api.aiservice import AiServiceClient -from codeflash.api.cfapi import ( - check_workflow_file_exists, - is_github_app_installed_on_repo, - setup_github_actions, -) +from codeflash.api.cfapi import is_github_app_installed_on_repo, setup_github_actions from codeflash.cli_cmds.cli_common import apologize_and_exit from codeflash.cli_cmds.console import console, logger from codeflash.cli_cmds.extension import install_vscode_extension @@ -778,9 +774,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n console.print( Panel( Text( - "βœ… Repository secret CODEFLASH_API_KEY configured", - style="green", - justify="center", + "βœ… Repository secret CODEFLASH_API_KEY configured", style="green", justify="center" ), title="βœ… Secret Configured", border_style="bright_green", @@ -788,9 +782,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n ) console.print() elif secret_setup_error: - warning_message = ( - "⚠️ Secret setup failed. You'll need to add CODEFLASH_API_KEY manually.\n\n" - ) + warning_message = "⚠️ Secret setup failed. You'll need to add CODEFLASH_API_KEY manually.\n\n" warning_message += f"Error: {secret_setup_error}\n\n" warning_message += f"πŸ“ Add secret at: {get_github_secrets_page_url(repo)}" @@ -807,9 +799,7 @@ def install_github_actions(override_formatter_check: bool = False) -> None: # n ) # Secret setup is optional, so we continue - logger.info( - f"[cmd_init.py:install_github_actions] Workflow file already exists locally, skipping setup" - ) + logger.info("[cmd_init.py:install_github_actions] Workflow file already exists locally, skipping setup") return # Get repository information for API call @@ -1482,10 +1472,11 @@ def generate_dynamic_workflow_content( optimize_yml_content = "\n".join(new_lines) return optimize_yml_content - else: - logger.warning("[cmd_init.py:generate_dynamic_workflow_content] Could not find steps section in template") + logger.warning("[cmd_init.py:generate_dynamic_workflow_content] Could not find steps section in template") else: - logger.debug("[cmd_init.py:generate_dynamic_workflow_content] AI service returned no steps, falling back to static") + logger.debug( + "[cmd_init.py:generate_dynamic_workflow_content] AI service returned no steps, falling back to static" + ) except Exception as e: logger.warning(