name: 'Restore Cache' inputs: cache_path: description: 'Path where cache should be restored' required: true type: string cache_key: description: 'Primary key to restore cache' required: true type: string restore_keys: description: 'Multiline list of keys (fallback prefixes)' required: false type: string default: "" cache_bucket: description: 'The tag name for the release (e.g., general-cache)' required: false default: 'general-cache' debug: description: 'Enable Logs' required: false type: boolean default: false outputs: cache-hit: description: 'Returns "true" if a cache was found and restored, otherwise "false"' value: ${{ steps.restore.outputs.cache-hit }} runs: using: 'composite' steps: - name: Detect and Download Cache id: restore shell: bash env: TARGET_REPO: "${{ github.repository }}" run: | # Detect and Download Cache if [ "${{ inputs.debug }}" = "true" ]; then set -x; fi cd "$GITHUB_WORKSPACE" TAG_NAME="${{ inputs.cache_bucket }}" BASE_URL="https://github.com/${TARGET_REPO}/releases/download/$TAG_NAME" ASSETS_URL="https://github.com/${TARGET_REPO}/releases/expanded_assets/$TAG_NAME" ARIA2_OPTS=( "-x16" "-s16" "-k1M" "-j5" "--file-allocation=none" "--header=User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "--header=Accept: */*" "--header=Connection: keep-alive" ) if [ "${{ inputs.debug }}" != "true" ]; then ARIA2_OPTS+=("--quiet" "--summary-interval=0" "--console-log-level=error") fi echo "::group:: Fetching Asset List" HTTP_RESPONSE=$(curl -sL -A "Mozilla/5.0" -w "%{http_code}" "$ASSETS_URL" -o assets.html || echo "404") if [ "$HTTP_RESPONSE" -eq 200 ]; then ALL_ASSETS=$(grep -oP "download/$TAG_NAME/\K[^\"' ]+" assets.html | sort -u || echo "") else echo "Status $HTTP_RESPONSE: Failed to fetch assets from scraper endpoint." ALL_ASSETS="" fi rm -f assets.html if [ -z "$ALL_ASSETS" ]; then echo "No assets found in bucket '$TAG_NAME'. Skipping search." echo "cache-hit=false" >> "$GITHUB_OUTPUT" echo "::endgroup::" else echo "Successfully fetched asset list. Has $(echo "$ALL_ASSETS" | wc -l) assets." echo "::endgroup::" SEARCH_LIST=$(printf "%s\n%s" "${{ inputs.cache_key }}" "${{ inputs.restore_keys }}" | sed '/^$/d') FOUND=false while read -r KEY; do [ -z "$KEY" ] && continue echo "::group:: Searching Prefix: $KEY" MATCH=$(echo "$ALL_ASSETS" | grep "^cache-$KEY" | sort -r | head -n 1 || true) if [ -z "$MATCH" ]; then echo "Not found." echo "::endgroup::" continue fi BASE_FILENAME=$(echo "$MATCH" | sed -E 's/\.(tzst|tar)(\.part[a-z]{2})?$//') EXT=$(echo "$MATCH" | grep -oP '\.(tzst|tar)' | head -n 1) [[ "$MATCH" == *".part"* ]] && IS_SPLIT=true || IS_SPLIT=false [ "$EXT" = ".tzst" ] && COMPRESSED=true || COMPRESSED=false FOUND=true echo "✅ Hit! Restoring $BASE_FILENAME$EXT" echo "::endgroup::" echo "::group:: Downloading Cache" download_asset() { local filename="$1" local url="$2" local max_retries=3 local attempt=1 while [ $attempt -le $max_retries ]; do echo " Attempt $attempt: Downloading $filename..." if timeout 10m aria2c "${ARIA2_OPTS[@]}" --retry-wait=10 --max-tries=10 -o "$filename" "$url"; then return 0 fi echo " ⚠️ Download failed or timed out. Retrying in 5s..." sleep 5 attempt=$((attempt + 1)) done return 1 } if [ "$IS_SPLIT" = "true" ]; then for part in {a..z}{a..z}; do PART_NAME="$BASE_FILENAME$EXT.part$part" if echo "$ALL_ASSETS" | grep -q "^$PART_NAME$"; then if ! download_asset "$PART_NAME" "$BASE_URL/$PART_NAME"; then echo "::error::Failed to download $PART_NAME after multiple retries." exit 1 fi else break; fi done else if ! download_asset "$BASE_FILENAME$EXT" "$BASE_URL/$BASE_FILENAME$EXT"; then echo "::error::Failed to download $BASE_FILENAME$EXT after multiple retries." exit 1 fi fi echo "::endgroup::" echo "::group:: Extracting Cache" mkdir -p "${{ inputs.cache_path }}" FINAL_ARCHIVE="$BASE_FILENAME$EXT" EXTRACT_DIR="$(dirname "${{ inputs.cache_path }}")" extract_cache() { local cmd="$1" if [ "${{ inputs.debug }}" = "true" ]; then sh -c "$cmd" else sh -c "$cmd" > /dev/null 2>&1 fi } if [ "$IS_SPLIT" = "true" ]; then PARTS=$(ls "$FINAL_ARCHIVE.part"* | sort | tr '\n' ' ') if [ "$COMPRESSED" = "true" ]; then extract_cache "cat $PARTS | tar -I 'zstd -d -T0' -xvf - -C '$EXTRACT_DIR'" else extract_cache "cat $PARTS | tar -xvf - -C '$EXTRACT_DIR'" fi rm -f $PARTS else if [ "$COMPRESSED" = "true" ]; then extract_cache "tar -I 'zstd -d -T0' -xvf '$FINAL_ARCHIVE' -C '$EXTRACT_DIR'" else extract_cache "tar -xvf '$FINAL_ARCHIVE' -C '$EXTRACT_DIR'" fi rm -f "$FINAL_ARCHIVE" fi echo "✅ Restore complete." echo "::endgroup::" break done <<< "$SEARCH_LIST" if [ "$FOUND" = "false" ]; then echo "⚠️ No cache matches found. Proceeding with fresh run." echo "cache-hit=false" >> "$GITHUB_OUTPUT" else echo "cache-hit=true" >> "$GITHUB_OUTPUT" fi fi