diff --git a/docs/docs/installation/bitbucket.md b/docs/docs/installation/bitbucket.md index a15da9f1..507e88ae 100644 --- a/docs/docs/installation/bitbucket.md +++ b/docs/docs/installation/bitbucket.md @@ -1,33 +1,33 @@ ## Run as a Bitbucket Pipeline - You can use the Bitbucket Pipeline system to run PR-Agent on every pull request open or update. 1. Add the following file in your repository bitbucket-pipelines.yml ```yaml pipelines: - pull-requests: - '**': - - step: - name: PR Agent Review - image: python:3.12 - services: - - docker - script: - - docker run -e CONFIG.GIT_PROVIDER=bitbucket -e OPENAI.KEY=$OPENAI_API_KEY -e BITBUCKET.BEARER_TOKEN=$BITBUCKET_BEARER_TOKEN codiumai/pr-agent:latest --pr_url=https://bitbucket.org/$BITBUCKET_WORKSPACE/$BITBUCKET_REPO_SLUG/pull-requests/$BITBUCKET_PR_ID review + pull-requests: + "**": + - step: + name: PR Agent Review + image: python:3.12 + services: + - docker + script: + - docker run -e CONFIG.GIT_PROVIDER=bitbucket -e OPENAI.KEY=$OPENAI_API_KEY -e BITBUCKET.BEARER_TOKEN=$BITBUCKET_BEARER_TOKEN codiumai/pr-agent:latest --pr_url=https://bitbucket.org/$BITBUCKET_WORKSPACE/$BITBUCKET_REPO_SLUG/pull-requests/$BITBUCKET_PR_ID review ``` 2. Add the following secure variables to your repository under Repository settings > Pipelines > Repository variables. -OPENAI_API_KEY: `` -BITBUCKET_BEARER_TOKEN: `` + OPENAI_API_KEY: `` + BITBUCKET.AUTH_TYPE: `basic` or `bearer` (default is `bearer`) + BITBUCKET.BEARER_TOKEN: `` (required when auth_type is bearer) + BITBUCKET.BASIC_TOKEN: `` (required when auth_type is basic) You can get a Bitbucket token for your repository by following Repository Settings -> Security -> Access Tokens. +For basic auth, you can generate a base64 encoded token from your username:password combination. Note that comments on a PR are not supported in Bitbucket Pipeline. - - ## Bitbucket Server and Data Center Login into your on-prem instance of Bitbucket with your service account username and password. @@ -48,6 +48,7 @@ git_provider="bitbucket_server" ``` and pass the Pull request URL: + ```shell python cli.py --pr_url https://git.onpreminstanceofbitbucket.com/projects/PROJECT/repos/REPO/pull-requests/1 review ``` @@ -55,6 +56,7 @@ python cli.py --pr_url https://git.onpreminstanceofbitbucket.com/projects/PROJEC ### Run it as service To run PR-Agent as webhook, build the docker image: + ``` docker build . -t codiumai/pr-agent:bitbucket_server_webhook --target bitbucket_server_webhook -f docker/Dockerfile docker push codiumai/pr-agent:bitbucket_server_webhook # Push to your Docker repository diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 969ddf9e..73db18b9 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -29,17 +29,36 @@ class BitbucketProvider(GitProvider): self, pr_url: Optional[str] = None, incremental: Optional[bool] = False ): s = requests.Session() - try: - self.bearer_token = bearer = context.get("bitbucket_bearer_token", None) - if not bearer and get_settings().get("BITBUCKET.BEARER_TOKEN", None): - self.bearer_token = bearer = get_settings().get("BITBUCKET.BEARER_TOKEN", None) - s.headers["Authorization"] = f"Bearer {bearer}" - except Exception: - self.bearer_token = get_settings().get("BITBUCKET.BEARER_TOKEN", None) - s.headers[ - "Authorization" - ] = f'Bearer {self.bearer_token}' s.headers["Content-Type"] = "application/json" + + self.auth_type = get_settings().get("BITBUCKET.AUTH_TYPE", "bearer") + + try: + def get_token(token_name, auth_type_name): + token = get_settings().get(f"BITBUCKET.{token_name.upper()}", None) + if not token: + raise ValueError(f"{auth_type_name} auth requires a token") + return token + + if self.auth_type == "basic": + self.basic_token = get_token("basic_token", "Basic") + s.headers["Authorization"] = f"Basic {self.basic_token}" + elif self.auth_type == "bearer": + try: + self.bearer_token = context.get("bitbucket_bearer_token", None) + except: + self.bearer_token = None + + if not self.bearer_token: + get_token("bearer_token", "Bearer") + s.headers["Authorization"] = f"Bearer {self.bearer_token}" + else: + raise ValueError(f"Unsupported auth_type: {self.auth_type}") + + except Exception as e: + get_logger().exception(f"Failed to initialize Bitbucket authentication: {e}") + raise + self.headers = s.headers self.bitbucket_client = Cloud(session=s) self.max_comment_length = 31000 @@ -608,16 +627,21 @@ class BitbucketProvider(GitProvider): if "bitbucket.org" not in repo_url_to_clone: get_logger().error("Repo URL is not a valid bitbucket URL.") return None - bearer_token = self.bearer_token - if not bearer_token: - get_logger().error("No bearer token provided. Returning None") - return None - #For example: For repo: https://bitbucket.org/codiumai/pr-agent-tests.git - #clone url will be: https://x-token-auth:@bitbucket.org/codiumai/pr-agent-tests.git (scheme, base_url) = repo_url_to_clone.split("bitbucket.org") if not all([scheme, base_url]): get_logger().error(f"repo_url_to_clone: {repo_url_to_clone} is not a valid bitbucket URL.") return None - clone_url = f"{scheme}x-token-auth:{bearer_token}@bitbucket.org{base_url}" + + if self.auth_type == "basic": + # Basic auth with token + clone_url = f"{scheme}x-token-auth:{self.basic_token}@bitbucket.org{base_url}" + elif self.auth_type == "bearer": + # Bearer token + clone_url = f"{scheme}x-token-auth:{self.bearer_token}@bitbucket.org{base_url}" + else: + # This case should ideally not be reached if __init__ validates auth_type + get_logger().error(f"Unsupported or uninitialized auth_type: {getattr(self, 'auth_type', 'N/A')}. Returning None") + return None + return clone_url diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml index 28201a0d..f1bb30d4 100644 --- a/pr_agent/settings/.secrets_template.toml +++ b/pr_agent/settings/.secrets_template.toml @@ -69,8 +69,12 @@ personal_access_token = "" shared_secret = "" # webhook secret [bitbucket] -# For Bitbucket personal/repository bearer token +# For Bitbucket authentication +auth_type = "bearer" # "bearer" or "basic" +# For bearer token authentication bearer_token = "" +# For basic authentication (uses token only) +basic_token = "" [bitbucket_server] # For Bitbucket Server bearer token