Repository: gruntwork-io/terragrunt (language: go)

Issue:
Terragrunt version: 'v0.59.5' causes BlobNotFound error
## Describe the bug

This error was previously seen in version v0.59.0 and was resolved. It appears it has been re-released in version 0.59.5. In that broken version the version was missing the complied binaries.

[#3195](https://github.com/gruntwork-io/terragrunt/issues/3195)

## Steps To Reproduce

Our code:
```hcl
LATEST_URL=$(curl -sL  https://api.github.com/repos/gruntwork-io/terragrunt/releases  | jq -r '.[0].assets[].browser_download_url' | egrep 'linux.*amd64' | tail -1)
curl -sL ${LATEST_URL} > /usr/local/bin/terragrunt
chmod u+x /usr/local/bin/terragrunt
terragrunt --version
```

Result:
```hcl
 /usr/local/bin/terragrunt: line 1: syntax error near unexpected token `<'
/usr/local/bin/terragrunt: line 1: `﻿<?xml version="1.0" encoding="utf-8"?><Error><Code>BlobNotFound</Code><Message>The specified blob does not exist.'
```

## Expected behavior

The pipeline should be able to download and run terragrunt and cannot.

## Nice to haves

- [x] Terminal output
- [ ] Screenshots

## Versions

- Terragrunt version: v0.59.5



Discussion / hints:
Got the same issue in a github action
Thanks for reporting this! Re-running the build.
<img width="1138" alt="Terragrunt Error Screenshot" src="https://github.com/gruntwork-io/terragrunt/assets/102659542/d27d3aeb-0c67-4e2a-847a-e9fe59832650">

This was resolved by re-running the build.

We're not sure why this happens specifically for amd64 asset uploads. The upload goes through just fine, but the underlying Azure blob storage shows the asset as missing. We'll be adding some automated checks to at least detect when this happens, but it's definitely bizarre.

Fix patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 82c02a98cb..2c69a2cd56 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -415,6 +415,10 @@ jobs:
             brew install coreutils
             cd bin && sha256sum * > SHA256SUMS
       - run: upload-github-release-assets bin/*
+      - run:
+          name: Verify that assets attached to the release
+          command: |
+            ./_ci/verify-release-assets.sh
 
 workflows:
   version: 2
diff --git a/_ci/verify-release-assets.sh b/_ci/verify-release-assets.sh
new file mode 100755
index 0000000000..5c1b4da1e9
--- /dev/null
+++ b/_ci/verify-release-assets.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+set -e
+set -u
+set -o pipefail
+
+# Script to verify the release assets by downloading them from the GitHub release
+# and checking if they are accessible. If the asset is not accessible, the script
+# will try to delete asset and re-upload it to the release.
+# https://github.com/gruntwork-io/terragrunt/issues/3220
+
+# Ensure CIRCLE_TAG and GITHUB_OAUTH_TOKEN are set
+if [ -z "${CIRCLE_TAG:-}" ]; then
+  echo "CIRCLE_TAG environment variable is not set. Exiting..."
+  exit 1
+fi
+
+if [ -z "${GITHUB_OAUTH_TOKEN:-}" ]; then
+  echo "GITHUB_OAUTH_TOKEN environment variable is not set. Exiting..."
+  exit 1
+fi
+
+RELEASE_TAG=$CIRCLE_TAG
+REPO_OWNER="gruntwork-io"
+REPO_NAME="terragrunt"
+MAX_RETRIES=10
+
+RELEASE_RESPONSE=$(curl -s \
+  -H "Accept: application/vnd.github.v3+json" \
+  -H "Authorization: token $GITHUB_OAUTH_TOKEN" \
+  -H "X-GitHub-Api-Version: 2022-11-28" \
+  "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/releases/tags/$RELEASE_TAG")
+
+# Check if the release exists
+if jq -e '.message == "Not Found"' <<< "$RELEASE_RESPONSE" > /dev/null; then
+  echo "Release $RELEASE_TAG not found. Exiting..."
+  exit 1
+fi
+
+# Get the release id
+RELEASE_ID=$(echo "$RELEASE_RESPONSE" | jq -r '.id')
+ASSET_URLS=$(echo "$RELEASE_RESPONSE" | jq -r '.assets[].browser_download_url')
+
+# Loop through each asset URL and attempt to download
+for ASSET_URL in $ASSET_URLS; do
+  ASSET_NAME=$(basename "$ASSET_URL")
+
+  for ((i=0; i<MAX_RETRIES; i++)); do
+    if ! curl -sILf "$ASSET_URL" > /dev/null; then
+      echo "Failed to download the asset $ASSET_NAME. Retrying..."
+
+      # Delete the asset
+      ASSET_ID=$(jq -r --arg asset_name "$ASSET_NAME" '.assets[] | select(.name == $asset_name) | .id' <<< "$RELEASE_RESPONSE")
+      curl -s -L -XDELETE \
+        -H "Accept: application/vnd.github.v3+json" \
+        -H "Authorization: token $GITHUB_OAUTH_TOKEN" \
+        -H "X-GitHub-Api-Version: 2022-11-28" \
+        "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/releases/assets/$ASSET_ID" > /dev/null
+
+      # Re-upload the asset
+      curl -s -L -XPOST \
+        -H "Accept: application/vnd.github.v3+json" \
+        -H "Authorization: token $GITHUB_OAUTH_TOKEN" \
+        -H "X-GitHub-Api-Version: 2022-11-28" \
+        -H "Content-Type: application/octet-stream" \
+        --data-binary "@bin/$ASSET_NAME" \
+        "https://uploads.github.com/repos/$REPO_OWNER/$REPO_NAME/releases/$RELEASE_ID/assets?name=$ASSET_NAME" > /dev/null
+    else
+      echo "Successfully checked the asset $ASSET_NAME"
+      break
+    fi
+  done
+
+  if (( i == MAX_RETRIES )); then
+    echo "Failed to download the asset $ASSET_NAME after $MAX_RETRIES retries. Exiting..."
+    exit 1
+  fi
+done
+
+echo "All assets checks passed."


Test patch: