|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +apk add yq || { echo "ERROR: Failed to install yq. Aborting." >&2; exit 1; } |
| 4 | + |
| 5 | +log() { |
| 6 | + |
| 7 | + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 |
| 8 | +} |
| 9 | + |
| 10 | +error_exit() { |
| 11 | + log "ERROR: $1" |
| 12 | +} |
| 13 | + |
| 14 | +command -v yq >/dev/null 2>&1 || error_exit "yq is required but not installed or not in PATH. Aborting." |
| 15 | +command -v git >/dev/null 2>&2 || error_exit "git is required but not installed or not in PATH. Aborting." |
| 16 | +command -v jq >/dev/null 2>&1 || error_exit "jq is required but not installed or not in PATH. Aborting." |
| 17 | +command -v curl >/dev/null 2>&1 || error_exit "curl is required but not installed or not in PATH. Aborting." |
| 18 | + |
| 19 | +: "${GITHUB_API_TOKEN:?GITHUB_API_TOKEN is not set or empty}" |
| 20 | +: "${GIT_USER_EMAIL:?GIT_USER_EMAIL is not set or empty}" |
| 21 | +: "${GIT_USER_NAME:?GIT_USER_NAME is not set or empty}" |
| 22 | +: "${CI_CD_EVENT:?CI_CD_EVENT is not set or empty}" |
| 23 | + |
| 24 | +log "Setting Git user config" |
| 25 | +git config --global user.email "${GIT_USER_EMAIL}" |
| 26 | +git config --global user.name "${GIT_USER_NAME}" |
| 27 | + |
| 28 | +REPO_OWNER=$(echo "$REPO_URL" | sed -E 's|https://github.com/([^/]+)/.*|\1|') |
| 29 | +REPO_NAME=$(echo "$REPO_URL" | sed -E 's|https://github.com/[^/]+/([^/]+)\.git|\1|') |
| 30 | +FULL_IMAGE_PATH=$(echo "$CI_CD_EVENT" | jq -r '.commonWorkflowRequest.dockerRepository') |
| 31 | +IMAGE_TAG=$(echo "$CI_CD_EVENT" | jq -r '.commonWorkflowRequest.dockerImageTag') |
| 32 | + |
| 33 | + |
| 34 | +IMAGE_NAME=$(echo "$FULL_IMAGE_PATH" | sed -E 's|.*/([^/]+)|\1|') |
| 35 | + |
| 36 | +log "Full Image path from event: $FULL_IMAGE_PATH" |
| 37 | +log "Image Name to search for in YAML: $IMAGE_NAME" |
| 38 | +log "New Image Tag: $IMAGE_TAG" |
| 39 | + |
| 40 | +PREVIOUS_TAG="" |
| 41 | +CHANGES_MADE=false |
| 42 | + |
| 43 | +update_image() { |
| 44 | + local image_name="$1" |
| 45 | + local new_tag="$2" |
| 46 | + local file_to_change="$3" |
| 47 | + local found_image_string=false |
| 48 | + local update_required=false |
| 49 | + |
| 50 | + log "Searching for image matching pattern '${image_name}:*' in file: $file_to_change" |
| 51 | + |
| 52 | + while IFS='=' read -r current_path current_value; do |
| 53 | + |
| 54 | + current_value=$(echo "$current_value" | xargs) |
| 55 | + current_value="${current_value%\"}" |
| 56 | + current_value="${current_value#\"}" |
| 57 | + |
| 58 | + if [[ "$current_value" == "${image_name}":* ]]; then |
| 59 | + found_image_string=true |
| 60 | + |
| 61 | + local current_tag="${current_value##*:}" |
| 62 | + |
| 63 | + log "Found image : '$current_value' at path '.$current_path'" |
| 64 | + log "Extracted current tag: '$current_tag'" |
| 65 | + |
| 66 | + if [ "$current_tag" != "$new_tag" ]; then |
| 67 | + log "New tag '$new_tag'." |
| 68 | + update_required=true |
| 69 | + PREVIOUS_TAG="$current_tag" |
| 70 | + |
| 71 | + local new_image_string="${image_name}:${new_tag}" |
| 72 | + |
| 73 | + log "Attempting to update path .$current_path in $file_to_change" |
| 74 | + if yq e ".${current_path} = \"${new_image_string}\"" -i "$file_to_change"; then |
| 75 | + log "Successfully updated path .$current_path" |
| 76 | + CHANGES_MADE=true |
| 77 | + break |
| 78 | + else |
| 79 | + log "ERROR: Failed to update path .$current_path in file $file_to_change." |
| 80 | + |
| 81 | + fi |
| 82 | + else |
| 83 | + log "Image '$image_name' at path '.$current_path' already at desired version '$new_tag'." |
| 84 | + update_required=true |
| 85 | + break |
| 86 | + fi |
| 87 | + fi |
| 88 | + |
| 89 | + done < <(yq e '.. | select(tag == "!!str") | {(path | join(".")): .} | to_props' "$file_to_change" 2>/dev/null) |
| 90 | + |
| 91 | + if ! $found_image_string; then |
| 92 | + log "Image matching pattern '${image_name}:*' was NOT found in $file_to_change. No changes made for this file." |
| 93 | + elif ! $update_required; then |
| 94 | + log "Image matching pattern '${image_name}:*' found, but update logic didn't flag update required. This is unexpected." |
| 95 | + fi |
| 96 | + |
| 97 | +} |
| 98 | + |
| 99 | +log "Cloning repository: ${REPO_URL}" |
| 100 | + |
| 101 | +if ! git clone "https://${GITHUB_API_TOKEN}@github.com/${REPO_OWNER}/${REPO_NAME}.git"; then |
| 102 | + error_exit "Failed to clone repository ${REPO_URL}" |
| 103 | +fi |
| 104 | +cd "${REPO_NAME}" || error_exit "Failed to change directory to ${REPO_NAME}" |
| 105 | +log "Changed directory to $(pwd)" |
| 106 | + |
| 107 | +log "Fetching latest changes from origin" |
| 108 | +if ! git fetch origin; then |
| 109 | + error_exit "Failed to fetch latest changes from origin" |
| 110 | +fi |
| 111 | + |
| 112 | +log "Checking out branch: ${BRANCH_NAME}" |
| 113 | + |
| 114 | +if git ls-remote --exit-code --heads origin "$BRANCH_NAME" >/dev/null 2>&1; then |
| 115 | + log "Branch $BRANCH_NAME already exists remotely. Checking it out and pulling latest changes." |
| 116 | + if ! git checkout "$BRANCH_NAME"; then |
| 117 | + error_exit "Failed to checkout existing branch $BRANCH_NAME" |
| 118 | + fi |
| 119 | + log "Pulling latest changes for branch $BRANCH_NAME" |
| 120 | + if ! git pull origin "$BRANCH_NAME"; then |
| 121 | + error_exit "Failed to pull latest changes for branch $BRANCH_NAME" |
| 122 | + fi |
| 123 | +else |
| 124 | + log "Branch $BRANCH_NAME does not exist remotely. Creating a new branch based on origin/main." |
| 125 | + |
| 126 | + if ! git rev-parse origin/main >/dev/null 2>&1; then |
| 127 | + error_exit "origin/main branch not found. Cannot create new branch $BRANCH_NAME." |
| 128 | + fi |
| 129 | + if ! git checkout -b "$BRANCH_NAME" origin/main; then |
| 130 | + error_exit "Failed to create new branch $BRANCH_NAME based on origin/main" |
| 131 | + fi |
| 132 | + log "Created new branch $BRANCH_NAME" |
| 133 | +fi |
| 134 | + |
| 135 | +log "Updating Image Tag in $IMAGE_LIST_FILE" |
| 136 | + |
| 137 | +sed -i "s|^quay.io/devtron/${FULL_IMAGE_PATH}:.*|quay.io/devtron/${FULL_IMAGE_PATH}:${IMAGE_TAG}|" "$IMAGE_LIST_FILE" |
| 138 | + |
| 139 | +update_image "$IMAGE_NAME" "$IMAGE_TAG" "$FILE_TO_CHANGE_1" |
| 140 | + |
| 141 | +update_image "$IMAGE_NAME" "$IMAGE_TAG" "$FILE_TO_CHANGE_2" |
| 142 | +log "Finished image updates." |
| 143 | + |
| 144 | +if ! $CHANGES_MADE; then |
| 145 | + log "No changes to commit. Image $IMAGE_NAME:$IMAGE_TAG already present in all locations. Exiting." |
| 146 | +fi |
| 147 | + |
| 148 | +COMMIT_MESSAGE="Updated $IMAGE_NAME to $IMAGE_TAG tag in values file" |
| 149 | + |
| 150 | +log "Committing changes" |
| 151 | +if ! git add "$FILE_TO_CHANGE_1" "$FILE_TO_CHANGE_2" "$IMAGE_LIST_FILE"; then |
| 152 | + error_exit "Failed to stage changes" |
| 153 | +fi |
| 154 | +if ! git commit -m "$COMMIT_MESSAGE"; then |
| 155 | + error_exit "Failed to commit changes" |
| 156 | +fi |
| 157 | + |
| 158 | +push_changes() { |
| 159 | + if ! git push origin "$BRANCH_NAME"; then |
| 160 | + log "Push failed. Pulling latest changes, rebasing, and trying again." |
| 161 | + if ! git pull --rebase origin "$BRANCH_NAME"; then |
| 162 | + error_exit "Failed to pull and rebase latest changes" |
| 163 | + fi |
| 164 | + if ! git push origin "$BRANCH_NAME"; then |
| 165 | + error_exit "Failed to push changes after rebase" |
| 166 | + fi |
| 167 | + fi |
| 168 | +} |
| 169 | + |
| 170 | +log "Pushing changes" |
| 171 | +push_changes |
| 172 | + |
| 173 | +if [ "$RAISE_PR" == "TRUE" ]; then |
| 174 | + PR_TITLE="Chore: Update $IMAGE_NAME" |
| 175 | + PR_BODY="This PR updates the image version for $IMAGE_NAME from $PREVIOUS_TAG to $IMAGE_TAG" |
| 176 | + |
| 177 | + log "Checking for existing Pull Request" |
| 178 | + EXISTING_PR=$(curl -s -H "Authorization: token $GITHUB_API_TOKEN" \ |
| 179 | + -H "Accept: application/vnd.github.v3+json" \ |
| 180 | + "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/pulls?head=$REPO_OWNER:$BRANCH_NAME&state=open") |
| 181 | + |
| 182 | + |
| 183 | + if [ "$(echo "$EXISTING_PR" | jq '. | length')" -gt 0 ]; then |
| 184 | + PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.[0].number') |
| 185 | + log "Existing Pull Request found. Updating PR #$PR_NUMBER" |
| 186 | + |
| 187 | + UPDATE_PR_RESPONSE=$(curl -s -X PATCH \ |
| 188 | + -H "Authorization: token $GITHUB_API_TOKEN" \ |
| 189 | + -H "Accept: application/vnd.github.v3+json" \ |
| 190 | + "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/pulls/$PR_NUMBER" \ |
| 191 | + -d '{ |
| 192 | + "title": "'"$PR_TITLE"'", |
| 193 | + "body": "'"$PR_BODY"'" |
| 194 | + }') |
| 195 | + |
| 196 | + PR_URL=$(echo "$UPDATE_PR_RESPONSE" | jq -r '.html_url') |
| 197 | + if [ "$PR_URL" != "null" ]; then |
| 198 | + log "Pull Request updated successfully: $PR_URL" |
| 199 | + else |
| 200 | + error_exit "Failed to update Pull Request. Response: $UPDATE_PR_RESPONSE" |
| 201 | + fi |
| 202 | + else |
| 203 | + log "Creating new Pull Request" |
| 204 | + PR_RESPONSE=$(curl -s -X POST \ |
| 205 | + -H "Authorization: token $GITHUB_API_TOKEN" \ |
| 206 | + -H "Accept: application/vnd.github.v3+json" \ |
| 207 | + "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/pulls" \ |
| 208 | + -d '{ |
| 209 | + "title": "'"$PR_TITLE"'", |
| 210 | + "body": "'"$PR_BODY"'", |
| 211 | + "head": "'"$BRANCH_NAME"'", |
| 212 | + "base": "main" |
| 213 | + }') |
| 214 | + |
| 215 | + PR_URL=$(echo "$PR_RESPONSE" | jq -r '.html_url') |
| 216 | + if [ "$PR_URL" != "null" ]; then |
| 217 | + log "Pull Request created successfully: $PR_URL" |
| 218 | + else |
| 219 | + error_exit "Failed to create Pull Request. Response: $PR_RESPONSE" |
| 220 | + fi |
| 221 | + fi |
| 222 | +else |
| 223 | + log "Done and Dusted" |
| 224 | +fi |
0 commit comments