Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
356 changes: 97 additions & 259 deletions templates/autofix/pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pipeline:
working_directory: /harness
harness_execution_id: <+inputs.executionId>
env:
ANTHROPIC_API_KEY: <+inputs.anthropicKey>
ANTHROPIC_API_KEY: <+inputs.llmConnector.token>
- name: coding_agent
run:
container:
Expand All @@ -33,285 +33,123 @@ pipeline:
working_directory: /harness
show_diff: "false"
env:
ANTHROPIC_API_KEY: <+inputs.anthropicKey>
ANTHROPIC_API_KEY: <+inputs.llmConnector.token>
- name: show_git_diff
run:
shell: bash
script: |-
rm -r 5c5b7e75_813d_40ec_9836_40b5bcf750e6_autofix
git add -A
git diff --cached
- name: createNetrc
run:
shell: sh
script: |-
cat <<EOF > ${HOME}/.netrc
machine ${DRONE_NETRC_MACHINE}
login ${DRONE_NETRC_USERNAME}
password ${DRONE_NETRC_PASSWORD}
EOF
- name: push_to_branch
- name: fix_env_variables
run:
shell: bash
script: >-
echo "Current working directory: $(pwd)"

git config --global user.email "harness-auto-fix-ai@harness.io"

git config --global user.name "harness-auto-fix"

git log

IS_DIFF_PRESENT=true

if [ -n "$(git status --porcelain)" ]; then
ORIGINAL_COMMIT_SHA=$(git rev-parse HEAD)
export ORIGINAL_COMMIT_SHA
echo "Original commit SHA (before autofix): $ORIGINAL_COMMIT_SHA"

git add .
git commit -m "harness-auto-fix created this fix"

COMMIT_SHA=$(git rev-parse HEAD)
export COMMIT_SHA
echo "Autofix commit SHA: $COMMIT_SHA"

CURRENT_BRANCH="$DRONE_SOURCE_BRANCH"
echo "Current branch: $CURRENT_BRANCH"

if [ "$CURRENT_BRANCH" = "HEAD" ]; then
SHORT_COMMIT_ID=$(git rev-parse --short HEAD)
FIX_BRANCH="commit-${SHORT_COMMIT_ID}-ai-autofix"
git checkout -b $FIX_BRANCH
echo "Detached HEAD detected. Creating branch: $FIX_BRANCH"
git push -f origin HEAD:$FIX_BRANCH
elif [ "$(echo "$CURRENT_BRANCH" | grep "ai-autofix")" != "" ]; then
echo "Branch already has ai-autofix suffix, pushing to same branch"
FIX_BRANCH="$CURRENT_BRANCH"
echo "Pushing changes to branch: $FIX_BRANCH"
git push origin HEAD:$FIX_BRANCH
script: |-
# Get SCM provider from DRONE
SCM_PROVIDER="${DRONE_REPO_SCM}"

# Convert "Git" to "harness" for Harness Code repositories
if [ "$SCM_PROVIDER" = "Git" ]; then
echo "Detected Git SCM, converting to harness"
SCM_PROVIDER="harness"
fi

# Convert SCM provider to lowercase for consistency
SCM_PROVIDER=$(echo "$SCM_PROVIDER" | tr '[:upper:]' '[:lower:]')
echo "SCM Provider (lowercase): $SCM_PROVIDER"

# Set credentials based on SCM provider
if [ "$SCM_PROVIDER" = "harness" ]; then
echo "Setting credentials for Harness Code"
# Use HARNESS_API_KEY if available, otherwise fall back to SCM_TOKEN (git connector)
if [ -n "$HARNESS_API_KEY" ]; then
TOKEN="${HARNESS_API_KEY}"
echo "Using Harness API Key for authentication"
else
echo "Creating new branch with ai-autofix suffix"
FIX_BRANCH="${CURRENT_BRANCH}-ai-autofix"
echo "Do a force push to the new branch"
git push -f origin HEAD:$FIX_BRANCH
TOKEN="${SCM_TOKEN}"
echo "Using Git Connector token for authentication"
fi

echo "Successfully pushed changes to $FIX_BRANCH"
NETRC_USERNAME="${DRONE_NETRC_USERNAME}"
NETRC_PASSWORD="${DRONE_NETRC_PASSWORD}"
else
echo "No changes to commit"
IS_DIFF_PRESENT=false
FIX_BRANCH="$DRONE_SOURCE_BRANCH"
echo "No fix branch created, using current branch: $FIX_BRANCH"
echo "Setting credentials for $SCM_PROVIDER"
# For GitHub, GitLab, Bitbucket, etc.
# Extract username from DRONE_REPO_LINK (e.g., https://github.com/ahimanshu56/test -> ahimanshu56)
if [ -n "$DRONE_REPO_LINK" ]; then
# Remove protocol and domain, get path, extract first part (username/owner)
NETRC_USERNAME=$(echo "$DRONE_REPO_LINK" | sed -E 's|https?://[^/]+/([^/]+)/.*|\1|')
echo "Extracted username from DRONE_REPO_LINK: $NETRC_USERNAME"
else
NETRC_USERNAME="${DRONE_NETRC_USERNAME}"
fi
TOKEN="${SCM_TOKEN}"
NETRC_PASSWORD="${SCM_TOKEN}"
fi

echo "FIX_BRANCH=$FIX_BRANCH" >> $DRONE_OUTPUT

echo "IS_DIFF_PRESENT=$IS_DIFF_PRESENT" >> $DRONE_OUTPUT

echo "Exported FIX_BRANCH: $FIX_BRANCH"

echo "Exported IS_DIFF_PRESENT: $IS_DIFF_PRESENT"
with:
detailed_logging: "true"
- name: create_pr
# Construct base URL (IMPORTANT!)
BASE_URL="${DRONE_SYSTEM_PROTO}://${DRONE_SYSTEM_HOST}"

# Export for next step
echo "SCM_PROVIDER=$SCM_PROVIDER" >> $DRONE_OUTPUT
echo "TOKEN=$TOKEN" >> $HARNESS_OUTPUT_SECRET_FILE
echo "NETRC_USERNAME=$NETRC_USERNAME" >> $DRONE_OUTPUT
echo "NETRC_PASSWORD=$NETRC_PASSWORD" >> $HARNESS_OUTPUT_SECRET_FILE
echo "BASE_URL=$BASE_URL" >> $DRONE_OUTPUT
echo "Detected SCM Provider: $SCM_PROVIDER"
env:
HARNESS_API_KEY: <+inputs.harnessKey>
SCM_TOKEN: <+inputs.gitConnector.token>

- name: push_and_create_pr
run:
shell: sh
script: |-
die() {
echo "Error: $*" >&2
exit 1
}


echo "=========================================="

echo "Environment Variables:"

echo "=========================================="

env | sort

echo "=========================================="

echo ""

echo "FIX_BRANCH from previous step: $FIX_BRANCH"

echo "IS_DIFF_PRESENT from previous step: $IS_DIFF_PRESENT"

echo ""


API_KEY="$HARNESS_API_KEY"

REPO_IDENTIFIER="$REPO_NAME"

SOURCE_BRANCH="$FIX_BRANCH"

TARGET_BRANCH="$DRONE_SOURCE_BRANCH"

ACCOUNT_ID="${HARNESS_ACCOUNT_ID:-}"

ORG_ID="${HARNESS_ORG_ID:-}"

PROJECT_ID="${HARNESS_PROJECT_ID:-}"

# Construct BASE_URL from DRONE variables if available
if [ -n "$DRONE_SYSTEM_PROTO" ] && [ -n "$DRONE_SYSTEM_HOST" ]; then
BASE_URL="${DRONE_SYSTEM_PROTO}://${DRONE_SYSTEM_HOST}"
echo "Constructed BASE_URL from DRONE variables: $BASE_URL"
else
BASE_URL="${HARNESS_BASE_URL:-https://unifiedpipeline.harness.io}"
echo "Using fallback BASE_URL: $BASE_URL"
fi

TITLE="${HARNESS_PR_TITLE:-}"

DESCRIPTION="${HARNESS_PR_DESCRIPTION:-}"

IS_DRAFT="${IS_DRAFT:-false}"

BYPASS_RULES="${BYPASS_RULES:-false}"


[ -n "$REPO_IDENTIFIER" ] || die "Repository identifier must be
provided"

[ -n "$API_KEY" ] || die "API key must be provided"

[ -n "$SOURCE_BRANCH" ] || die "Source branch must be provided"

[ -n "$TARGET_BRANCH" ] || die "Target branch must be provided"

[ -n "$ACCOUNT_ID" ] || die "Account ID must be provided"


echo "DEBUG: REPO_IDENTIFIER = '$REPO_IDENTIFIER'"

echo "DEBUG: ACCOUNT_ID = '$ACCOUNT_ID'"

echo "DEBUG: ORG_ID = '$ORG_ID'"

echo "DEBUG: PROJECT_ID = '$PROJECT_ID'"

echo "DEBUG: BASE_URL = '$BASE_URL'"


API_URL="${BASE_URL}/code/api/v1/repos/${REPO_IDENTIFIER}/pullreq"

QUERY="accountIdentifier=${ACCOUNT_ID}"

[ -n "$ORG_ID" ] && QUERY="${QUERY}&orgIdentifier=${ORG_ID}"

[ -n "$PROJECT_ID" ] &&
QUERY="${QUERY}&projectIdentifier=${PROJECT_ID}"


if [ -z "$TITLE" ]; then
TITLE="Merge ${SOURCE_BRANCH} into ${TARGET_BRANCH}"
fi


if [ -n "$DESCRIPTION" ]; then
BODY=$(jq -n \
--arg source "$SOURCE_BRANCH" \
--arg target "$TARGET_BRANCH" \
--arg title "$TITLE" \
--arg desc "$DESCRIPTION" \
--argjson draft "$IS_DRAFT" \
--argjson bypass "$BYPASS_RULES" \
'{source_branch: $source, target_branch: $target, title: $title, description: $desc, is_draft: $draft, bypass_rules: $bypass}')
else
BODY=$(jq -n \
--arg source "$SOURCE_BRANCH" \
--arg target "$TARGET_BRANCH" \
--arg title "$TITLE" \
--argjson draft "$IS_DRAFT" \
--argjson bypass "$BYPASS_RULES" \
'{source_branch: $source, target_branch: $target, title: $title, is_draft: $draft, bypass_rules: $bypass}')
fi


echo "Making request to: ${API_URL}"

echo "Full URL with params: ${API_URL}?${QUERY}"

echo "With body:"

echo "$BODY" | jq .


RESPONSE_FILE=$(mktemp)

HTTP_STATUS=$(curl -sS \
-o "$RESPONSE_FILE" \
-w "%{http_code}" \
-X POST "${API_URL}?${QUERY}" \
-H "Content-Type: application/json" \
-H "x-api-key: ${API_KEY}" \
-d "$BODY")

echo "Response status code: ${HTTP_STATUS}"

echo "Response body:"

cat "$RESPONSE_FILE"

echo ""


PR_ID=""

case "$HTTP_STATUS" in
409)
echo "Pull request already exists"
PR_ID=$(jq -r '.values.number // empty' < "$RESPONSE_FILE" 2>/dev/null || echo "")
;;
2*)
PR_ID=$(jq -r '.number // empty' < "$RESPONSE_FILE" 2>/dev/null || echo "")
;;
*)
die "Failed to create pull request with status $HTTP_STATUS"
;;
esac


[ -n "$PR_ID" ] || die "Missing PR number in response"


PR_URL="${BASE_URL}/ng/account/${ACCOUNT_ID}/module/code/orgs/${ORG_ID}/projects/${PROJECT_ID}/repos/${REPO_IDENTIFIER}/pulls/${PR_ID}"

export PR_URL

export AUTOFIX_PR_ID="$PR_ID"


echo

echo "Pull request successful"

echo "PR ID: ${PR_ID}"

echo "PR URL: ${PR_URL}"
with:
detailed_logging: "true"
container:
image: himanshu6956/create-pr-plugin:latest
env:
FIX_BRANCH: <+pipeline.stages.autofix_1.steps.push_to_branch_1.output.outputVariables.FIX_BRANCH>
IS_DIFF_PRESENT: <+pipeline.stages.autofix_1.steps.push_to_branch_1.output.outputVariables.IS_DIFF_PRESENT>
HARNESS_API_KEY: ${{inputs.harnessKey}}
REPO_NAME: ${{inputs.repo}}
HARNESS_BASE_URL: https://unifiedpipeline.harness.io
HARNESS_PR_DESCRIPTION: working on autofixing your CI checks
HARNESS_PR_TITLE: Test PR
# Core Plugin Settings
PLUGIN_SCM_PROVIDER: <+pipeline.stages.autofix_1.steps.fix_env_variables_1.output.outputVariables.SCM_PROVIDER>
PLUGIN_TOKEN: <+pipeline.stages.autofix_1.steps.fix_env_variables_1.output.outputVariables.TOKEN>
PLUGIN_REPO: ${{inputs.repo}}
PLUGIN_SOURCE_BRANCH: ${{inputs.branch}}

# Git Authentication (Netrc)
PLUGIN_NETRC_MACHINE: <+env.DRONE_NETRC_MACHINE>
PLUGIN_NETRC_USERNAME: <+pipeline.stages.autofix_1.steps.fix_env_variables_1.output.outputVariables.NETRC_USERNAME>
PLUGIN_NETRC_PASSWORD: <+pipeline.stages.autofix_1.steps.fix_env_variables_1.output.outputVariables.NETRC_PASSWORD>

# Branch Configuration
PLUGIN_BRANCH_SUFFIX: ai-autofix
PLUGIN_UNIQUE_PER_EXECUTION: "false"
PLUGIN_FORCE_PUSH: "true"

# Commit Configuration
PLUGIN_COMMIT_MESSAGE: "Autofix: harness-auto-fix created this fix"

# Git Operations
PLUGIN_PUSH_CHANGES: "true"

# Pull Request Configuration
PLUGIN_CREATE_PR: "true"
PLUGIN_PR_TITLE: "Autofix: AI automated fixes by Harness"
PLUGIN_PR_DESCRIPTION: "Working on autofixing your CI checks. Please review the changes before merging."
PLUGIN_IS_DRAFT: "false"
PLUGIN_BYPASS_RULES: "false"

# Harness Code Specific Settings (used only when SCM_PROVIDER=harness)
PLUGIN_HARNESS_ACCOUNT_ID: <+env.HARNESS_ACCOUNT_ID>
PLUGIN_HARNESS_ORG_ID: <+env.HARNESS_ORG_ID>
PLUGIN_HARNESS_PROJECT_ID: <+env.HARNESS_PROJECT_ID>
PLUGIN_HARNESS_BASE_URL: <+pipeline.stages.autofix_1.steps.fix_env_variables_1.output.outputVariables.BASE_URL>

# Debug & Output
PLUGIN_DEBUG: "false"
PLUGIN_OUTPUT_FILE: <+env.DRONE_OUTPUT>

platform:
os: linux
arch: arm64
inputs:
anthropicKey:
type: secret
default: autofix_anthropic_api_key
llmConnector:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - can rename to anthropicConnector

type: connector
harnessKey:
type: secret
default: harness_api_key
Expand Down
Loading