Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Resolver]: Gitlab support #5210

Open
josx opened this issue Nov 22, 2024 · 15 comments
Open

[Resolver]: Gitlab support #5210

josx opened this issue Nov 22, 2024 · 15 comments
Labels
enhancement New feature or request resolver Related to OpenHands Resolver

Comments

@josx
Copy link

josx commented Nov 22, 2024

What problem or use case are you trying to solve?
There are plenty source code in gitlab software in public and private instances. It has a plus is free software.
So would be nice to have resolve issues , Responding to PR Comments, and CI/CD (like github workflow but for gitlab).

Do you have thoughts on the technical implementation?

Additional context
With this feature will enable a complete workflow local or on-premise with free software.

@josx josx added the enhancement New feature or request label Nov 22, 2024
@neubig
Copy link
Contributor

neubig commented Nov 23, 2024

Hi @josx , we (the current maintainers me and @malhotra5) probably aren't going to work on this ourselves right now (just due to lack of time), but we'd 100% welcome a contribution in this direction!

@mamoodi mamoodi added the resolver Related to OpenHands Resolver label Nov 25, 2024
@josx
Copy link
Author

josx commented Nov 25, 2024

Ok, Something that we have to take into account so that it can be done. Any advice?

@malhotra5
Copy link
Contributor

malhotra5 commented Nov 25, 2024

Hey @josx! I'd recommend taking a look at Openhands/openhands/resolver. All of the resolver logic is contained within this folder.

The files Openhands/openhands/resolver/issue_definitions.py (downloads issues) and Openhands/openhands/resolver/send_pull_request.py (uploads code) use Github APIs, so we'd probably want to do a larger refactor to further abstract the code to support Gitlab endpoints.

Unit tests in tests/unit/resolver should make sure the appropriate APIs and response data are being used (larger refactor of tests is probably necessary for this as well).

Lastly, we'd need another workflow definition file similar to .github/workflows/openhands-resolver.yml to trigger the resolver inside Gitlab.

Copy link
Contributor

This issue is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions github-actions bot added the Stale Inactive for 30 days label Dec 27, 2024
@neubig
Copy link
Contributor

neubig commented Dec 27, 2024

This is still on our eventual roadmap

@Orianamartina
Copy link

Hello folks! We started working on this issue here on our forked repo.

  • Our first step was to assign a new handler to manage all pull request and issue logic
  • The next step was to abstract the resolver's GitHub related logic and delegate GitHub's logic to GithubIssueHandler class. (WIP) When this is finished we can create a pull request with the progress so far

The next steps that we think can be correct are:

  • Create the GitlabIssueHandler to resolve Gitlab related logic
  • Then integrate Gitlab Continuous integration to imitate Github actions behavior

We worked on this with @cuococarlos, @juanmanueldaza, @josx

Let us know if you have any comments and if you think it is the correct approach

@enyst
Copy link
Collaborator

enyst commented Dec 27, 2024

@Orianamartina That's awesome! Please feel free to create a draft PR if you want, it will make it easier to see the diff or comment if needed, although of course it's up to you.

@github-actions github-actions bot removed the Stale Inactive for 30 days label Dec 28, 2024
@souzomain
Copy link

Hello @neubig, If possible, it would be interesting to make it more generic, I work on opensource projects like Forgejo and Gitea to manage my projects

@neubig
Copy link
Contributor

neubig commented Jan 4, 2025

Hey @Orianamartina , this sounds awesome, looking forward to it! I think on a high level the plan sounds great. I'd also echo @enyst 's suggestion to feel free to send over a pull request for review sooner rather than later. We can pull on each step of the process as it's finished to keep the code bases in sync and the review process manageable

@codykociemba
Copy link

Hey,

We're working on integrating Gitlab into OpenHands based off of where @Orianamartina left off, however there seems to be one issue that needs to be discussed. Currently, OpenHands primarily works off of "issues to PR/MR" which works well for Github, however Gitlab doesn't have this logic in its .gitlab-ci.yml pipeline system. We can trigger off of merge requests and pushes, but for issue events, we'll likely have to use a webhook of some sort or use a cron job. Any thoughts on this?

@VincenzoFab
Copy link

It would be even more useful to be able to use Gitea as a repository as well.

@neubig
Copy link
Contributor

neubig commented Jan 16, 2025

Hi @codykociemba , sorry about the late reply! And hmm, that's a bit annoying. I'm not familiar enough (yet) with gitlab webhooks to make an informed decision about this... maybe a cron-job would be simpler, but I'm not really sure.

@codykociemba
Copy link

Just a quick update, we've modified the project to add Gitlab support, using either Webhook or Gitlab CI/CD to trigger the agent/runtime. We're cleaning up the code a bit before submitting a PR, however if anyone is wanting to dabble with OpenHands yourself using Gitlab, here is the .gitlab-ci.yml file our team has been using.

image: ubuntu:latest

services:
  - name: docker:dind
    alias: docker

variables:
  DOCKER_DIND: "docker:24.0.5-dind"                                                          
  DOCKER_TLS_CERTDIR: ""
  DOCKER_HOST: "tcp://docker:2375"
  FF_NETWORK_PER_BUILD: "true"
  CI_DEBUG_SERVICES: "true"
  DOCKER_DRIVER: overlay2

stages:
  - auto-fix

auto-fix:
  stage: auto-fix
  tags: [ saas-linux-medium-amd64 ]

  rules:
    - if: $ISSUE_TYPE == "note" || $ISSUE_TYPE == "issue" || $ISSUE_TYPE == "mr" || ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /\bfix-me\b/)
      when: always
    - when: never

  cache:
    key: pip-openhands-cache
    paths:
      - .cache/pip

  variables:
    MAX_ITERATIONS: "90"
    TARGET_BRANCH: CI_COMMIT_REF_NAME

  before_script:
    # ----------------------------------------------------------------------------
    # 1) Install Docker + Python + venv, then create and activate a virtual environment
    # ----------------------------------------------------------------------------
    - apt-get update
    - apt-get install -y --no-install-recommends docker.io python3.12 python3.12-venv python3-pip git ca-certificates build-essential python3.12-dev libffi-dev curl
    - python3.12 -m venv /builds/$CI_PROJECT_PATH/venv
    - source /builds/$CI_PROJECT_PATH/venv/bin/activate
    - pip install --upgrade pip
    - docker --version
    - python --version
    - pip --version
    - docker version # Check if docker daemon is running before continuing

  script:
    - source /builds/$CI_PROJECT_PATH/venv/bin/activate

    # --------------------------------------------------------
    # 1) Clone & "editable" install + symlink pyproject.toml
    # --------------------------------------------------------
    - |
      echo "=== Step 1: Cloning & Installing OpenHands in editable mode ==="
      git clone https://github.com/All-Hands-AI/OpenHands.git /builds/$CI_PROJECT_PATH/OpenHands
      cd /builds/$CI_PROJECT_PATH/OpenHands

      if [ "$IS_EXPERIMENTAL" = "true" ]; then
        echo "Switching to 'resolver_gitlab' branch..."
        git checkout resolver_gitlab
      else
        echo "Using 'main' branch..."
        git checkout main
      fi

      echo "Installing OpenHands in editable mode..."
      pip install -e .

      # Symlink pyproject.toml into the site-packages directory so code that
      # tries to read it there won't crash with FileNotFoundError
      PYPROJECT_LOCAL="/builds/$CI_PROJECT_PATH/OpenHands/pyproject.toml"
      PYPROJECT_SITE="$(python -c 'import site; print(site.getsitepackages()[0])')/pyproject.toml"

      if [ -f "$PYPROJECT_LOCAL" ]; then
        echo "Symlinking $PYPROJECT_LOCAL => $PYPROJECT_SITE"
        ln -sf "$PYPROJECT_LOCAL" "$PYPROJECT_SITE"
      else
        echo "Warning: pyproject.toml not found in /builds/$CI_PROJECT_PATH/OpenHands?"
      fi


    # ----------------------------------------------------------------------------
    # 2) Check required environment variables
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 2: Checking required environment variables ==="
      if [ -z "$LLM_MODEL" ]; then
        echo "Error: LLM_MODEL is not set."
        exit 1
      fi
	  
      if [ -z "$LLM_API_KEY" ]; then
        echo "Error: LLM_API_KEY is not set."
        exit 1
      fi
      if [ -z "$GITLAB_API_TOKEN" ]; then
        echo "Error: GITLAB_API_TOKEN is not set. Needed for GitLab API calls and/or pulling private repos."
        exit 1
      fi
      if [ -z "$GITLAB_PROJECT_ID" ]; then
        echo "Error: GITLAB_PROJECT_ID is not set. Needed for GitLab API calls."
        exit 1
      fi

      # Optional warnings:
      if [ -z "$LLM_BASE_URL" ]; then
        echo "Warning: LLM_BASE_URL is not set; default endpoint will be used."
      fi

    # ----------------------------------------------------------------------------
    # 3) (Optional) Comment on the GitLab Issue or MR to note that the pipeline started
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 3: (Optional) Comment on GitLab to note pipeline start ==="
      # First determine if this is a merge request case
      IS_MERGE_REQUEST="false"
      MR_IID=""
      if [ "$ISSUE_TYPE" = "mr" ]; then
        IS_MERGE_REQUEST="true"
        MR_IID="$MERGE_IID"
      elif [ "$CI_PIPELINE_SOURCE" = "merge_request_event" ]; then
        IS_MERGE_REQUEST="true"
        MR_IID="$CI_MERGE_REQUEST_IID"
      fi

      if [ "$ISSUE_TYPE" = "issue" ] && [ -n "$ISSUE_IID" ]; then
        NOTE_BODY="OpenHands started fixing this issue! You can monitor the progress in GitLab pipeline #$CI_PIPELINE_ID."
        curl --request POST \
             --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
             --form "body=${NOTE_BODY}" \
             "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/issues/$ISSUE_IID/notes"
      elif [ "$IS_MERGE_REQUEST" = "true" ] && [ -n "$MR_IID" ]; then
        NOTE_BODY="OpenHands started fixing this Merge Request! You can monitor the progress in GitLab pipeline #$CI_PIPELINE_ID."
        curl --request POST \
             --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
             --form "body=${NOTE_BODY}" \
             "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/merge_requests/$MR_IID/notes"
      else
        echo "Skipping note creation — missing \$ISSUE_TYPE or \$ISSUE_IID / \$MR_IID."
      fi

    # ----------------------------------------------------------------------------
    # 4) Attempt to resolve the issue with OpenHands
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 4: Attempt to resolve issue with OpenHands ==="
      cd /builds/$CI_PROJECT_PATH
      export SANDBOX_LOCAL_RUNTIME_URL=http://docker
      export LLM_MODEL="${LLM_MODEL}"
      export LLM_API_KEY="${LLM_API_KEY}"
      export GITLAB_TOKEN="${GITLAB_API_TOKEN}"

      # Set the issue type and number based on whether it's a merge request
      if [ "$IS_MERGE_REQUEST" = "true" ]; then
        RESOLVER_ISSUE_TYPE="pr"
        RESOLVER_ISSUE_NUMBER="$MR_IID"
      else
        RESOLVER_ISSUE_TYPE="issue"
        RESOLVER_ISSUE_NUMBER="$ISSUE_IID"
      fi

      python -m openhands.resolver.resolve_issue \
        --repo "$CI_PROJECT_PATH" \
        --issue-number "${RESOLVER_ISSUE_NUMBER:-0}" \
        --issue-type "${RESOLVER_ISSUE_TYPE:-issue}" \
        --max-iterations "$MAX_ITERATIONS" \
        --username "${GITLAB_USERNAME}" \
        --token "${GITLAB_API_TOKEN}" \
        --llm-model "${LLM_MODEL}" \
        --llm-api-key "${LLM_API_KEY}" \
        --comment-id "${COMMENT_ID:-None}" \
        --output-dir "/builds/$CI_PROJECT_PATH/output" \
        --is-experimental "${IS_EXPERIMENTAL:-false}" \
        --runtime-container-image "docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik" \
        || true

    # ----------------------------------------------------------------------------
    # 5) Check resolution result
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 5: Checking resolution result ==="
      RESOLUTION_SUCCESS="false"
      if grep -q '"success":true' /builds/$CI_PROJECT_PATH/output/output.jsonl; then
        RESOLUTION_SUCCESS="true"
      fi
      echo "Resolution success? => $RESOLUTION_SUCCESS"

    # ----------------------------------------------------------------------------
    # 6) Create "draft MR" or push a branch depending on success
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 6: Create draft MR or push branch ==="
      cd /builds/$CI_PROJECT_PATH
      if [ "$RESOLUTION_SUCCESS" = "true" ]; then
        python -m openhands.resolver.send_pull_request \
              --issue-number "${RESOLVER_ISSUE_NUMBER:-0}" \
              --pr-type draft \
              --token "$GITLAB_API_TOKEN" \
              --username OpenHands \
              --pr-title "Potential fix for ${RESOLVER_ISSUE_TYPE} ${RESOLVER_ISSUE_NUMBER} from OpenHands" | tee pr_result.txt && \
              grep "draft created" pr_result.txt | sed 's/.*\///g' > pr_number.txt
      else
        python -m openhands.resolver.send_pull_request \
              --issue-number "${RESOLVER_ISSUE_NUMBER:-0}" \
              --pr-type branch \
              --send-on-failure | tee branch_result.txt && \
              grep "branch created" branch_result.txt | sed 's/.*\///g; s/.expand=1//g' > branch_name.txt
      fi

    # ----------------------------------------------------------------------------
    # 7) Analyze logs and handle success/failure comments
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 7: Analyze push logs and create response comments ==="
      cd /builds/$CI_PROJECT_PATH
      AGENT_RESPONDED="false"

      # Read result files
      PR_NUMBER=""
      BRANCH_NAME=""
      RESULT_EXPLANATION=""

      if [ "$RESOLUTION_SUCCESS" = "true" ] && [ -f "pr_number.txt" ]; then
        PR_NUMBER=$(cat pr_number.txt)
      elif [ -f "branch_name.txt" ]; then
        BRANCH_NAME=$(cat branch_name.txt)
      fi

      # Get result explanation from output.jsonl for failed resolution
      if [ "$RESOLUTION_SUCCESS" != "true" ] && [ -f "output/output.jsonl" ]; then
        RESULT_EXPLANATION=$(head -n 1 output/output.jsonl | python3 -c '
        import sys, json
        try:
            data = json.loads(sys.stdin.read())
            print(data.get("result_explanation", ""))
        except json.JSONDecodeError:
            print("Unable to parse resolution output. The resolution attempt failed.")
        except Exception as e:
            print(f"An unexpected error occurred while parsing the resolution output: {str(e)}")' || echo "Failed to process resolution output")
      fi

      # Handle successful resolution with PR
      if [ "$RESOLUTION_SUCCESS" = "true" ] && [ -n "$PR_NUMBER" ]; then
        if [ "$IS_MERGE_REQUEST" = "true" ] && [ -n "$MR_IID" ]; then
          NOTE_BODY="A potential fix has been generated and a draft MR #${PR_NUMBER} has been created. Please review the changes."
          curl --request POST \
              --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
              --form "body=$NOTE_BODY" \
              "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/merge_requests/$MR_IID/notes"
          AGENT_RESPONDED="true"
        elif [ -n "$ISSUE_IID" ]; then
          NOTE_BODY="A potential fix has been generated and a draft MR #${PR_NUMBER} has been created. Please review the changes."
          curl --request POST \
              --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
              --form "body=$NOTE_BODY" \
              "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/issues/$ISSUE_IID/notes"
          AGENT_RESPONDED="true"
        fi

      # Handle unsuccessful resolution with branch creation
      elif [ "$RESOLUTION_SUCCESS" != "true" ] && [ -n "$BRANCH_NAME" ]; then
        NOTE_BODY="An attempt was made to automatically fix this issue, but it was unsuccessful. A branch named '${BRANCH_NAME}' has been created with the attempted changes. You can view the branch at $CI_PROJECT_URL/-/tree/${BRANCH_NAME}. Manual intervention may be required."
        
        if [ -n "$RESULT_EXPLANATION" ]; then
          NOTE_BODY="$NOTE_BODY\n\nAdditional details about the failure:\n$RESULT_EXPLANATION"
        fi

        if [ "$IS_MERGE_REQUEST" = "true" ] && [ -n "$MR_IID" ]; then
          curl --request POST \
              --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
              --form "body=$NOTE_BODY" \
              "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/merge_requests/$MR_IID/notes"
          AGENT_RESPONDED="true"
        elif [ -n "$ISSUE_IID" ]; then
          curl --request POST \
              --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
              --form "body=$NOTE_BODY" \
              "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/issues/$ISSUE_IID/notes"
          AGENT_RESPONDED="true"
        fi
      fi
      
  artifacts:
    name: "resolver-output"
    when: always
    paths:
      - /builds/$CI_PROJECT_PATH/output/output.jsonl
    expire_in: 30 days

You'll need to add the following Gitlab CI/CD variables:

  • LLM_MODEL: Model identifier for the language model
  • LLM_API_KEY: API key for accessing the language model
  • GITLAB_API_TOKEN:- Token for GitLab API authentication
  • GITLAB_PROJECT_ID: Project ID in GitLab
  • GITLAB_USERNAME: Username for GitLab operations
  • LLM_BASE_URL (optional): Base URL for the LLM API (will use default if not set)
  • IS_EXPERIMENTAL (optional): Boolean flag for experimental features (defaults to false)

Without a Webhook, the agent will only trigger on merge request events with the "fix-me" label. In order to get the agent to trigger on Issues, you'll need to setup a Webhook to trigger a pipeline. The webhook should send the following variables to the pipeline:

  • ISSUE_TYPE: Type of the issue ("note", "issue", or "mr")
  • ISSUE_IID or MERGE_IID: Issue or Merge Request IID (depending on the issue type)
  • COMMENT_ID: ID of the comment (optional)

@ChengranYan
Copy link

Just a quick update, we've modified the project to add Gitlab support, using either Webhook or Gitlab CI/CD to trigger the agent/runtime. We're cleaning up the code a bit before submitting a PR, however if anyone is wanting to dabble with OpenHands yourself using Gitlab, here is the .gitlab-ci.yml file our team has been using.

image: ubuntu:latest

services:
  - name: docker:dind
    alias: docker

variables:
  DOCKER_DIND: "docker:24.0.5-dind"                                                          
  DOCKER_TLS_CERTDIR: ""
  DOCKER_HOST: "tcp://docker:2375"
  FF_NETWORK_PER_BUILD: "true"
  CI_DEBUG_SERVICES: "true"
  DOCKER_DRIVER: overlay2

stages:
  - auto-fix

auto-fix:
  stage: auto-fix
  tags: [ saas-linux-medium-amd64 ]

  rules:
    - if: $ISSUE_TYPE == "note" || $ISSUE_TYPE == "issue" || $ISSUE_TYPE == "mr" || ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /\bfix-me\b/)
      when: always
    - when: never

  cache:
    key: pip-openhands-cache
    paths:
      - .cache/pip

  variables:
    MAX_ITERATIONS: "90"
    TARGET_BRANCH: CI_COMMIT_REF_NAME

  before_script:
    # ----------------------------------------------------------------------------
    # 1) Install Docker + Python + venv, then create and activate a virtual environment
    # ----------------------------------------------------------------------------
    - apt-get update
    - apt-get install -y --no-install-recommends docker.io python3.12 python3.12-venv python3-pip git ca-certificates build-essential python3.12-dev libffi-dev curl
    - python3.12 -m venv /builds/$CI_PROJECT_PATH/venv
    - source /builds/$CI_PROJECT_PATH/venv/bin/activate
    - pip install --upgrade pip
    - docker --version
    - python --version
    - pip --version
    - docker version # Check if docker daemon is running before continuing

  script:
    - source /builds/$CI_PROJECT_PATH/venv/bin/activate

    # --------------------------------------------------------
    # 1) Clone & "editable" install + symlink pyproject.toml
    # --------------------------------------------------------
    - |
      echo "=== Step 1: Cloning & Installing OpenHands in editable mode ==="
      git clone https://github.com/All-Hands-AI/OpenHands.git /builds/$CI_PROJECT_PATH/OpenHands
      cd /builds/$CI_PROJECT_PATH/OpenHands

      if [ "$IS_EXPERIMENTAL" = "true" ]; then
        echo "Switching to 'resolver_gitlab' branch..."
        git checkout resolver_gitlab
      else
        echo "Using 'main' branch..."
        git checkout main
      fi

      echo "Installing OpenHands in editable mode..."
      pip install -e .

      # Symlink pyproject.toml into the site-packages directory so code that
      # tries to read it there won't crash with FileNotFoundError
      PYPROJECT_LOCAL="/builds/$CI_PROJECT_PATH/OpenHands/pyproject.toml"
      PYPROJECT_SITE="$(python -c 'import site; print(site.getsitepackages()[0])')/pyproject.toml"

      if [ -f "$PYPROJECT_LOCAL" ]; then
        echo "Symlinking $PYPROJECT_LOCAL => $PYPROJECT_SITE"
        ln -sf "$PYPROJECT_LOCAL" "$PYPROJECT_SITE"
      else
        echo "Warning: pyproject.toml not found in /builds/$CI_PROJECT_PATH/OpenHands?"
      fi


    # ----------------------------------------------------------------------------
    # 2) Check required environment variables
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 2: Checking required environment variables ==="
      if [ -z "$LLM_MODEL" ]; then
        echo "Error: LLM_MODEL is not set."
        exit 1
      fi
	  
      if [ -z "$LLM_API_KEY" ]; then
        echo "Error: LLM_API_KEY is not set."
        exit 1
      fi
      if [ -z "$GITLAB_API_TOKEN" ]; then
        echo "Error: GITLAB_API_TOKEN is not set. Needed for GitLab API calls and/or pulling private repos."
        exit 1
      fi
      if [ -z "$GITLAB_PROJECT_ID" ]; then
        echo "Error: GITLAB_PROJECT_ID is not set. Needed for GitLab API calls."
        exit 1
      fi

      # Optional warnings:
      if [ -z "$LLM_BASE_URL" ]; then
        echo "Warning: LLM_BASE_URL is not set; default endpoint will be used."
      fi

    # ----------------------------------------------------------------------------
    # 3) (Optional) Comment on the GitLab Issue or MR to note that the pipeline started
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 3: (Optional) Comment on GitLab to note pipeline start ==="
      # First determine if this is a merge request case
      IS_MERGE_REQUEST="false"
      MR_IID=""
      if [ "$ISSUE_TYPE" = "mr" ]; then
        IS_MERGE_REQUEST="true"
        MR_IID="$MERGE_IID"
      elif [ "$CI_PIPELINE_SOURCE" = "merge_request_event" ]; then
        IS_MERGE_REQUEST="true"
        MR_IID="$CI_MERGE_REQUEST_IID"
      fi

      if [ "$ISSUE_TYPE" = "issue" ] && [ -n "$ISSUE_IID" ]; then
        NOTE_BODY="OpenHands started fixing this issue! You can monitor the progress in GitLab pipeline #$CI_PIPELINE_ID."
        curl --request POST \
             --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
             --form "body=${NOTE_BODY}" \
             "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/issues/$ISSUE_IID/notes"
      elif [ "$IS_MERGE_REQUEST" = "true" ] && [ -n "$MR_IID" ]; then
        NOTE_BODY="OpenHands started fixing this Merge Request! You can monitor the progress in GitLab pipeline #$CI_PIPELINE_ID."
        curl --request POST \
             --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
             --form "body=${NOTE_BODY}" \
             "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/merge_requests/$MR_IID/notes"
      else
        echo "Skipping note creation — missing \$ISSUE_TYPE or \$ISSUE_IID / \$MR_IID."
      fi

    # ----------------------------------------------------------------------------
    # 4) Attempt to resolve the issue with OpenHands
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 4: Attempt to resolve issue with OpenHands ==="
      cd /builds/$CI_PROJECT_PATH
      export SANDBOX_LOCAL_RUNTIME_URL=http://docker
      export LLM_MODEL="${LLM_MODEL}"
      export LLM_API_KEY="${LLM_API_KEY}"
      export GITLAB_TOKEN="${GITLAB_API_TOKEN}"

      # Set the issue type and number based on whether it's a merge request
      if [ "$IS_MERGE_REQUEST" = "true" ]; then
        RESOLVER_ISSUE_TYPE="pr"
        RESOLVER_ISSUE_NUMBER="$MR_IID"
      else
        RESOLVER_ISSUE_TYPE="issue"
        RESOLVER_ISSUE_NUMBER="$ISSUE_IID"
      fi

      python -m openhands.resolver.resolve_issue \
        --repo "$CI_PROJECT_PATH" \
        --issue-number "${RESOLVER_ISSUE_NUMBER:-0}" \
        --issue-type "${RESOLVER_ISSUE_TYPE:-issue}" \
        --max-iterations "$MAX_ITERATIONS" \
        --username "${GITLAB_USERNAME}" \
        --token "${GITLAB_API_TOKEN}" \
        --llm-model "${LLM_MODEL}" \
        --llm-api-key "${LLM_API_KEY}" \
        --comment-id "${COMMENT_ID:-None}" \
        --output-dir "/builds/$CI_PROJECT_PATH/output" \
        --is-experimental "${IS_EXPERIMENTAL:-false}" \
        --runtime-container-image "docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik" \
        || true

    # ----------------------------------------------------------------------------
    # 5) Check resolution result
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 5: Checking resolution result ==="
      RESOLUTION_SUCCESS="false"
      if grep -q '"success":true' /builds/$CI_PROJECT_PATH/output/output.jsonl; then
        RESOLUTION_SUCCESS="true"
      fi
      echo "Resolution success? => $RESOLUTION_SUCCESS"

    # ----------------------------------------------------------------------------
    # 6) Create "draft MR" or push a branch depending on success
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 6: Create draft MR or push branch ==="
      cd /builds/$CI_PROJECT_PATH
      if [ "$RESOLUTION_SUCCESS" = "true" ]; then
        python -m openhands.resolver.send_pull_request \
              --issue-number "${RESOLVER_ISSUE_NUMBER:-0}" \
              --pr-type draft \
              --token "$GITLAB_API_TOKEN" \
              --username OpenHands \
              --pr-title "Potential fix for ${RESOLVER_ISSUE_TYPE} ${RESOLVER_ISSUE_NUMBER} from OpenHands" | tee pr_result.txt && \
              grep "draft created" pr_result.txt | sed 's/.*\///g' > pr_number.txt
      else
        python -m openhands.resolver.send_pull_request \
              --issue-number "${RESOLVER_ISSUE_NUMBER:-0}" \
              --pr-type branch \
              --send-on-failure | tee branch_result.txt && \
              grep "branch created" branch_result.txt | sed 's/.*\///g; s/.expand=1//g' > branch_name.txt
      fi

    # ----------------------------------------------------------------------------
    # 7) Analyze logs and handle success/failure comments
    # ----------------------------------------------------------------------------
    - |
      echo "=== Step 7: Analyze push logs and create response comments ==="
      cd /builds/$CI_PROJECT_PATH
      AGENT_RESPONDED="false"

      # Read result files
      PR_NUMBER=""
      BRANCH_NAME=""
      RESULT_EXPLANATION=""

      if [ "$RESOLUTION_SUCCESS" = "true" ] && [ -f "pr_number.txt" ]; then
        PR_NUMBER=$(cat pr_number.txt)
      elif [ -f "branch_name.txt" ]; then
        BRANCH_NAME=$(cat branch_name.txt)
      fi

      # Get result explanation from output.jsonl for failed resolution
      if [ "$RESOLUTION_SUCCESS" != "true" ] && [ -f "output/output.jsonl" ]; then
        RESULT_EXPLANATION=$(head -n 1 output/output.jsonl | python3 -c '
        import sys, json
        try:
            data = json.loads(sys.stdin.read())
            print(data.get("result_explanation", ""))
        except json.JSONDecodeError:
            print("Unable to parse resolution output. The resolution attempt failed.")
        except Exception as e:
            print(f"An unexpected error occurred while parsing the resolution output: {str(e)}")' || echo "Failed to process resolution output")
      fi

      # Handle successful resolution with PR
      if [ "$RESOLUTION_SUCCESS" = "true" ] && [ -n "$PR_NUMBER" ]; then
        if [ "$IS_MERGE_REQUEST" = "true" ] && [ -n "$MR_IID" ]; then
          NOTE_BODY="A potential fix has been generated and a draft MR #${PR_NUMBER} has been created. Please review the changes."
          curl --request POST \
              --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
              --form "body=$NOTE_BODY" \
              "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/merge_requests/$MR_IID/notes"
          AGENT_RESPONDED="true"
        elif [ -n "$ISSUE_IID" ]; then
          NOTE_BODY="A potential fix has been generated and a draft MR #${PR_NUMBER} has been created. Please review the changes."
          curl --request POST \
              --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
              --form "body=$NOTE_BODY" \
              "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/issues/$ISSUE_IID/notes"
          AGENT_RESPONDED="true"
        fi

      # Handle unsuccessful resolution with branch creation
      elif [ "$RESOLUTION_SUCCESS" != "true" ] && [ -n "$BRANCH_NAME" ]; then
        NOTE_BODY="An attempt was made to automatically fix this issue, but it was unsuccessful. A branch named '${BRANCH_NAME}' has been created with the attempted changes. You can view the branch at $CI_PROJECT_URL/-/tree/${BRANCH_NAME}. Manual intervention may be required."
        
        if [ -n "$RESULT_EXPLANATION" ]; then
          NOTE_BODY="$NOTE_BODY\n\nAdditional details about the failure:\n$RESULT_EXPLANATION"
        fi

        if [ "$IS_MERGE_REQUEST" = "true" ] && [ -n "$MR_IID" ]; then
          curl --request POST \
              --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
              --form "body=$NOTE_BODY" \
              "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/merge_requests/$MR_IID/notes"
          AGENT_RESPONDED="true"
        elif [ -n "$ISSUE_IID" ]; then
          curl --request POST \
              --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
              --form "body=$NOTE_BODY" \
              "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/issues/$ISSUE_IID/notes"
          AGENT_RESPONDED="true"
        fi
      fi
      
  artifacts:
    name: "resolver-output"
    when: always
    paths:
      - /builds/$CI_PROJECT_PATH/output/output.jsonl
    expire_in: 30 days

You'll need to add the following Gitlab CI/CD variables:

  • LLM_MODEL: Model identifier for the language model
  • LLM_API_KEY: API key for accessing the language model
  • GITLAB_API_TOKEN:- Token for GitLab API authentication
  • GITLAB_PROJECT_ID: Project ID in GitLab
  • GITLAB_USERNAME: Username for GitLab operations
  • LLM_BASE_URL (optional): Base URL for the LLM API (will use default if not set)
  • IS_EXPERIMENTAL (optional): Boolean flag for experimental features (defaults to false)

Without a Webhook, the agent will only trigger on merge request events with the "fix-me" label. In order to get the agent to trigger on Issues, you'll need to setup a Webhook to trigger a pipeline. The webhook should send the following variables to the pipeline:

  • ISSUE_TYPE: Type of the issue ("note", "issue", or "mr")
  • ISSUE_IID or MERGE_IID: Issue or Merge Request IID (depending on the issue type)
  • COMMENT_ID: ID of the comment (optional)

Sounds great, but when will it be released? Is there an alpha version to experience

@codykociemba
Copy link

@ChengranYan Here is our initial PR which adds Gitlab support: #6458

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request resolver Related to OpenHands Resolver
Projects
None yet
Development

No branches or pull requests

10 participants