diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 21013bc..53d3c41 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -18,6 +18,12 @@ # Only feature/*, bugfix/*, hotfix/*, dev, stg have PR pipelines defined. # Any other branch name simply gets NO PR pipeline → checks won't pass # → PR cannot be merged. No catch-all '**' pattern (that caused double runs). +# +# SETUP REQUIRED: enforce these gates by enabling "Require a successful +# build to merge" for dev, stg, and main in: +# Bitbucket → Repository settings → Branch permissions +# Without this, the gates are advisory only — PRs can be merged regardless +# of whether the gate, lint, tests, or AI review pass or fail. # ============================================================================= @@ -43,7 +49,7 @@ definitions: # then syncs both dev and test dependency groups from pyproject.toml - script: &install pip install uv==0.11.5; - uv sync --group dev --group test; + uv sync --frozen --group dev --group test; # --------------------------------------------------------------------------- # Reusable step definitions @@ -89,12 +95,17 @@ definitions: # and posts review comments directly on the pull request. - step: &ai-code-review name: AI Code Review - image: python:3.12-slim + image: python:3.12.7-slim # slim is fine here — no uv sync + # SETUP REQUIRED: configure OIDC provider in + # Bitbucket → Workspace settings → Security → OpenID Connect + # and add the matching trust policy to the IAM role below. + # Without it, the step fails at startup before any script runs. oidc: true # enables OIDC token injection script: # Slim image doesn't include git — install it + AWS/HTTP libs - - apt-get update && apt-get install -y git - - pip install boto3 requests + # --no-install-recommends skips git-man/less/openssh-client etc. + - apt-get update && apt-get install -y --no-install-recommends git + - pip install "boto3==1.35.*" "requests==2.32.*" # --- AWS OIDC authentication setup --- # How it works: @@ -105,16 +116,30 @@ definitions: - export AWS_REGION=us-east-1 - export AWS_ROLE_ARN="arn:aws:iam::975049960860:role/DoczyAI-Bitbucket-OIDC" - export AWS_WEB_IDENTITY_TOKEN_FILE="$(pwd)/web-identity-token" - - echo "$BITBUCKET_STEP_OIDC_TOKEN" > "$(pwd)/web-identity-token" - # ↑ Quoted to prevent word-splitting if token has special chars + - printf '%s' "$BITBUCKET_STEP_OIDC_TOKEN" > "$(pwd)/web-identity-token" + # printf (not echo) avoids appending a trailing newline to the JWT # Clone the review agent repo (private — needs BITBUCKET_CLONE_TOKEN) + # SETUP REQUIRED: set BITBUCKET_CLONE_TOKEN (marked "Secured") in + # Bitbucket → Repository settings → Repository variables + # Guard against missing token so failure is self-diagnosing + - | + if [ -z "${BITBUCKET_CLONE_TOKEN}" ]; then + echo "BLOCKED: BITBUCKET_CLONE_TOKEN not set." + echo "Set it in Repository settings → Repository variables (Secured)." + exit 1 + fi # -q flag suppresses URL output to avoid leaking the token in logs - git clone -q "https://x-token-auth:${BITBUCKET_CLONE_TOKEN}@bitbucket.org/${BITBUCKET_WORKSPACE}/code-review-agent.git" - # Build the PR URL and run the agent + # Build the PR URL and run the agent. + # The || echo below only covers the python main.py call: + # agent crashes, AWS STS errors, and runtime exceptions become + # warnings instead of hard failures. A failure in git clone or + # apt-get above is still a hard failure (rare in practice). + # AI review is advisory, not a gate. - export PR_URL="https://bitbucket.org/${BITBUCKET_WORKSPACE}/${BITBUCKET_REPO_SLUG}/pull-requests/${BITBUCKET_PR_ID}" - - cd code-review-agent && python main.py "$PR_URL" + - cd code-review-agent && python main.py "$PR_URL" || echo "WARNING: AI review step failed (non-blocking)" # ============================================================================= @@ -139,7 +164,7 @@ pipelines: # =========================================================================== # Each entry matches a SOURCE branch pattern (the branch the PR comes # FROM, not the branch it targets). The gate step inside validates - # the DESTINATION branch using ALLOWED_TARGETS. + # the DESTINATION branch. # # Flow for every PR: # Step 1: Gate → is this PR targeting the correct branch? @@ -356,7 +381,10 @@ pipelines: patch=$((patch + 1)) fi - # Create the annotated tag and push it to the remote + # Create the annotated tag and push it to the remote. + # SETUP REQUIRED: enable "Repository write access" in + # Bitbucket → Repository settings → Pipelines + # Without it, git push fails with 403 and releases don't work. - | new_tag="v${major}.${minor}.${patch}" echo "Creating tag: ${new_tag}" @@ -375,7 +403,7 @@ pipelines: image: atlassian/default-image:4 script: # Read the tag that was created in step 1 - - export RELEASE_TAG=$(cat release_tag.txt) + - export RELEASE_TAG="$(cat release_tag.txt)" - echo "Deploying ${RELEASE_TAG}" # DEPLOY_COMMAND is a repository variable configured in @@ -400,7 +428,7 @@ pipelines: rollback-prod: - variables: - name: ROLLBACK_TAG - default: v0.0.0 # user must change this to a real tag + default: "" # user MUST provide a real tag (e.g. v1.2.0) # Step 1: Validate the tag exists - step: @@ -422,11 +450,14 @@ pipelines: exit 1 fi - # Verify the tag exists in the remote + # Verify the ref exists AND is specifically a tag (not a branch, + # HEAD, or commit hash). git rev-parse would accept any ref; + # `git tag -l` + exact-match grep ensures we only accept tags. - git fetch --tags --force - | - if ! git rev-parse "${ROLLBACK_TAG}" >/dev/null 2>&1; then - echo "BLOCKED: tag ${ROLLBACK_TAG} does not exist." + if ! git tag -l "${ROLLBACK_TAG}" | grep -qxF "${ROLLBACK_TAG}"; then + echo "BLOCKED: ${ROLLBACK_TAG} is not a tag." + echo "Rollback requires a release tag (e.g. v1.2.0), not a branch/commit." exit 1 fi - echo "Tag ${ROLLBACK_TAG} found — proceeding with rollback."