Triggering CircleCI Builds Across Branches with Conditional Fallbacks

Overview

CircleCI users often need to trigger builds in one project from another, specifying branch conditions. This article guides you through the process of triggering a CircleCI build on a specific branch and, if that branch does not exist, defaulting to the main branch. Additionally, it addresses the challenge of triggering builds on branches without open pull requests while maintaining the "Only build pull requests" setting.

Prerequisites

  • A CircleCI account with a project set up.
  • The curl and jq tools installed in your CI environment.
  • A CircleCI API token (CIRCLE_TOKEN) with the appropriate permissions.
  • A GitHub API token (GH_TOKEN) with the necessary repository permissions.

Instructions

To trigger a build on a specific branch and fallback to the main branch if the target branch does not exist, follow these steps:

  1. Define pipeline parameters for the target and default branches.
  2. Use the GitHub API to check if the target branch exists.
  3. Trigger a CircleCI pipeline on the target branch if it exists, or on the default branch otherwise.
  4. Implement a loop to check the status of the triggered workflow and output the result.

Solution

To achieve the desired workflow, you can use the following script:

parameters:
  target_trigger_branch:
    default: "develop"
    type: string
  default_trigger_branch:
    default: "main"
    type: string
  gh_project_slug:
    default: ""
    type: string
    description: "The GH Project in OWNER/REPO format"
  circleci_project_slug:
    default: ""
    type: string
    description: "The CircleCI Project in VCS/ORG/PROJECT format"


jobs:
  trigger-api:
    environment:
      TARGET_TRIGGER_BRANCH: << pipeline.parameters.target_trigger_branch >>
      DEFAULT_TRIGGER_BRANCH: << pipeline.parameters.default_trigger_branch >>
      GH_PROJECT_SLUG: << pipeline.parameters.gh_project_slug >>
      PROJECT_SLUG: << pipeline.parameters.circleci_project_slug >>
      
      - run: 
          name: api-trigger
          command: |
            # Checks to see if a branch exist in GitHub
            # If $TARGET_TRIGGER_BRANCH does not exist, trigger on $DEFAULT_TRIGGER_BRANCH
            TARGET_BRANCH_EXISTS=$(curl -L -H "Accept: application/vnd.github+json" -H "Authorization: Token ${GH_TOKEN}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/${GH_PROJECT_SLUG}/branches/${TARGET_TRIGGER_BRANCH})
            if [ $(echo $TARGET_BRANCH_EXISTS | jq -r .name ) == "${TARGET_TRIGGER_BRANCH}" ]; then
              echo "Triggering a build on ${TARGET_TRIGGER_BRANCH}"
              export BRANCH_TO_TRIGGER="${TARGET_TRIGGER_BRANCH}"
            else
              echo "Triggering on ${DEFAULT_TRIGGER_BRANCH} due to ${TARGET_TRIGGER_BRANCH} not existing."
              export BRANCH_TO_TRIGGER="${DEFAULT_TRIGGER_BRANCH}"
            fi

            # Checks default branches to make sure that we can trigger a build on $TARGET_TRIGGER_BRANCH
            # If we cannot trigger it (due it not being on the default branches), disable Only Build Pull Requests.
            if [ "$BRANCH_TO_TRIGGER" != "$DEFAULT_TRIGGER_BRANCH" ]; then
              CURRENT_PR_BRANCHES=$(curl --request GET --url "https://circleci.com/api/v2/project/gh/${GH_PROJECT_SLUG}/settings" --header "Circle-Token: ${CIRCLE_TOKEN}" --header "Accept: application/json")
              PARSED_PR_BRANCHES=$(echo $CURRENT_PR_BRANCHES | jq -r '.advanced.pr_only_branch_overrides[]')
              echo "$PARSED_PR_BRANCHES" > temp.txt
              mapfile -t CURRENT_PR_BRANCHES_ARRAY < temp.txt
              rm temp.txt
              FOUND=0
              for regex in "${CURRENT_PR_BRANCHES_ARRAY[@]}"; do
                if [[ "$TARGET_TRIGGER_BRANCH" =~ $regex ]]; then
                  FOUND=1
                  echo "$TARGET_TRIGGER_BRANCH matches pattern $regex. No need to change any settings."
                  break
                fi
              done

              if [ $FOUND -eq 0 ]; then
                curl --request PATCH --url "https://circleci.com/api/v2/project/gh/${GH_PROJECT_SLUG}/settings" --header "Circle-Token: ${CIRCLE_TOKEN}" --header 'content-type: application/json' --data '{"advanced":{"build_prs_only":false}}'
                echo "Disabling Only Build Pull Requests for ${GH_PROJECT_SLUG} so we can trigger a build."
              fi
            else
              echo "${BRANCH_TO_TRIGGER} is equal to main"
            fi

            # Trigger the build and watch it 
            TRIGGER_API=$(curl --location --request POST "https://circleci.com/api/v2/project/${PROJECT_SLUG}/pipeline" --header "Circle-Token: ${CIRCLE_TOKEN}" --header 'Content-Type: application/json' --header "Accept: application/json" --data-raw "{\"branch\":\"${BRANCH_TO_TRIGGER}\")
            PIPELINE_ID=$(echo ${TRIGGER_API} | jq -r .id)
            TOTAL_SECONDS=300
            WAIT_TIME=15
            for (( i=0; i <= $TOTAL_SECONDS; i=i+$WAIT_TIME )); do
              PIPELINE_WORKFLOW_STATUS=$(curl --request GET --url "https://circleci.com/api/v2/pipeline/${PIPELINE_ID}/workflow" --header "Circle-Token: ${CIRCLE_TOKEN}" | jq -r .items[].status)
              if [ "$PIPELINE_WORKFLOW_STATUS" == "success" ]; then
                echo "Status is: $PIPELINE_WORKFLOW_STATUS"
                LINK="https://app.circleci.com/pipelines/${PROJECT_SLUG}/$(echo $PIPELINE_WORKFLOW | jq .items.pipeline_number)"
                echo "Job has successed. Please view the link below \n ${LINK}"
              elif [ "$PIPELINE_WORKFLOW_STATUS" == "failed" ]; then
                echo "Status is: $PIPELINE_WORKFLOW_STATUS"
                LINK="https://app.circleci.com/pipelines/${PROJECT_SLUG}/$(echo $PIPELINE_WORKFLOW | jq .items.pipeline_number)"
                echo "Please correct an errors at the job link below. \n ${LINK}"
              else
                echo "Status is: $PIPELINE_WORKFLOW_STATUS"
                echo "Sleeping for $WAIT_TIME"
                sleep $WAIT_TIME
              fi
            done

            # If we had to disable Only Build Pull Requests, reenable it.
            if [ "${FOUND}" == "0" ]; then
              echo "The build on ${BRANCH_TO_TRIGGER} has ${PIPELINE_WORKFLOW_STATUS} so we can Enable Only Build Pull Requests on ${GH_PROJECT_SLUG} again."
              curl --request PATCH --url "https://circleci.com/api/v2/project/gh/${GH_PROJECT_SLUG}/settings" --header "Circle-Token: ${CIRCLE_TOKEN}" --header 'content-type: application/json' --data '{"advanced":{"build_prs_only":true}}'
            else
              echo "Build was on ${BRANCH_TO_TRIGGER} so no need to change settings again."
            fi

Replace gh_project_slug and circleci_project_slug pipeline parameters with your GitHub and CircleCI project identifiers, respectively.

Additional Resources

For more complex workflows or additional assistance, refer to the CircleCI documentation or explore community forums for shared solutions.

Was this article helpful?
0 out of 0 found this helpful

Comments

0 comments

Article is closed for comments.