Skip to content

Conversation

@mashraf-222
Copy link
Contributor

@mashraf-222 mashraf-222 commented Nov 13, 2025

User description

CLI: Automate GitHub Actions Workflow Setup via API

Summary:
Updated codeflash init to create GitHub Actions workflow PRs via the CF-API instead of writing files locally.

Technical Changes:

  • Added API integration (codeflash/api/cfapi.py):

    • New setup_github_actions() function calls /cfapi/setup-github-actions to create workflow PRs remotely
  • Updated GitHub Actions installation (codeflash/cli_cmds/cmd_init.py):

    • Replaced local file creation with API call to create PRs automatically
    • Added progress indicator: "Creating PR with GitHub Actions workflow..."
    • Displays PR URL on success: "✅ PR created: {pr_url}"
    • Maintains backward compatibility: falls back to local file creation if API fails
    • Context-aware messaging based on whether PR was created via API or locally

User Experience:

Users running codeflash init now see PRs created automatically with the workflow file, eliminating manual commit/push steps. The CLI handles errors gracefully and falls back to the previous behavior when needed.

CLI.demo.mp4

PR Type

Enhancement


Description

  • Add CF-API endpoint to create workflow PR

  • Init uses API, with local fallback

  • Detect current branch for PR base

  • Improved user messaging and logging


Diagram Walkthrough

flowchart LR
  CLI["init: install_github_actions"] --> Branch["Detect current branch"]
  CLI --> API["Call setup_github_actions"]
  API -- "200 + success, PR URL" --> Success["Show PR created panel"]
  API -- "200 + already exists" --> Exists["Show already configured panel"]
  API -- "error/exception" --> Fallback["Write workflow locally"]
  Success --> Final["Guide to merge PR and add secret"]
  Exists --> Final
  Fallback --> Final
Loading

File Walkthrough

Relevant files
Enhancement
cfapi.py
New API client for workflow PR creation                                   

codeflash/api/cfapi.py

  • Add setup_github_actions API wrapper.
  • Build payload and POST to /setup-github-actions.
  • Accept owner, repo, base branch, workflow content.
+25/-0   
cmd_init.py
Init flow uses API with fallback and UX updates                   

codeflash/cli_cmds/cmd_init.py

  • Integrate setup_github_actions into init flow.
  • Determine repo/branch; call API; handle responses.
  • Graceful fallback to local file creation on failure.
  • Contextual user messaging and detailed logging.
+125/-21

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Branch Detection

Using get_current_branch(repo) combined with repo.active_branch may raise when HEAD is detached; ensure robust fallback logic and user messaging for such cases.

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,
API Key Handling

setup_github_actions accepts api_key but callers never pass it; verify that make_cfapi_request will inject default auth or plumb api_key from config to avoid 401s.

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)
Success Criteria

Response handling assumes 200 + success boolean; consider guarding against missing/invalid JSON and partial responses to prevent exceptions and unintended fallbacks.

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}")

@github-actions
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Safely determine current branch

Accessing repo.active_branch can raise errors in detached HEAD or non-branch states.
Resolve the current branch safely and fall back to a sensible default without
touching repo.active_branch directly.

codeflash/cli_cmds/cmd_init.py [839]

-base_branch = get_current_branch(repo) if repo.active_branch else "main"
+try:
+    base_branch = get_current_branch(repo)
+    if not base_branch:
+        raise ValueError("No current branch detected")
+except Exception:
+    base_branch = "main"
Suggestion importance[1-10]: 7

__

Why: Accessing repo.active_branch can fail in detached HEAD; the proposed try/except around get_current_branch(repo) is a safer, context-appropriate fallback to "main" and aligns with the PR logic.

Medium
General
Validate inputs before API call

Validate critical parameters before sending the request to prevent server-side
errors and unclear failures. Ensure owner, repo, and workflow_content are non-empty
strings and strip whitespace.

codeflash/api/cfapi.py [242-265]

 def setup_github_actions(
     owner: str,
     repo: str,
     base_branch: str,
     workflow_content: str,
     api_key: str | None = None,
 ) -> Response:
-    ...
+    owner = (owner or "").strip()
+    repo = (repo or "").strip()
+    base_branch = (base_branch or "").strip()
+    if not owner or not repo or not workflow_content.strip():
+        raise ValueError("owner, repo, and workflow_content must be non-empty")
+    payload = {
+        "owner": owner,
+        "repo": repo,
+        "baseBranch": base_branch or "main",
+        "workflowContent": workflow_content,
+    }
     return make_cfapi_request(endpoint="/setup-github-actions", method="POST", payload=payload, api_key=api_key)
Suggestion importance[1-10]: 7

__

Why: Validating owner, repo, and workflow_content pre-request prevents avoidable server errors and clarifies failures; it's accurate and low-risk, improving reliability.

Medium
Harden API response handling

Parsing JSON without guarding response.json() may raise. Also, treat 2xx responses
consistently and verify required keys to avoid false positives. Add a safe JSON
parse and explicit handling for known statuses and missing keys.

codeflash/cli_cmds/cmd_init.py [852-893]

-if response.status_code == 200:
-    response_data = response.json()
-    if response_data.get("success"):
+if 200 <= response.status_code < 300:
+    try:
+        response_data = response.json()
+    except Exception:
+        raise Exception("Invalid JSON response from API")
+    if response_data.get("success") is True:
         pr_url = response_data.get("pr_url")
         if pr_url:
             pr_created_via_api = True
             ...
+        elif response_data.get("already_exists") is True:
+            pr_created_via_api = True
+            ...
         else:
-            # File already exists with same content
-            pr_created_via_api = True  # Mark as handled (no PR needed)
-            ...
+            raise Exception("Missing expected fields in API response")
     else:
-        raise Exception(response_data.get("error", "Unknown error"))
+        raise Exception(response_data.get("error") or "API indicated failure")
 else:
-    # API call failed, fall back to local file creation
     raise Exception(f"API returned status {response.status_code}")
Suggestion importance[1-10]: 6

__

Why: Adding guarded response.json() parsing and handling broader 2xx statuses improves robustness, though it's a defensive enhancement rather than fixing a clear bug shown in the diff.

Low

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants