| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- name: 'Save Cache'
- inputs:
- cache_path:
- description: 'Path where cache is saved'
- required: true
- type: string
- cache_key:
- description: 'Unique key for the cache'
- required: true
- type: string
- cache_bucket:
- description: 'The tag name for the release (e.g., general-cache)'
- required: true
- default: 'general-cache'
- github_token:
- description: 'GitHub Token'
- required: true
- compression_level:
- description: 'Compression level (0-9). 0 = No compression, 1 = Fast, 19 = Best.'
- required: false
- type: number
- default: 6
- working_dir:
- description: 'Path where .git folder exists (use only if you clone multiple repos)'
- required: false
- type: string
- delete_after_upload:
- description: 'Delete Source files after uploading'
- required: false
- type: boolean
- default: false
- debug:
- description: 'Enable Logs'
- required: false
- type: boolean
- default: false
- runs:
- using: 'composite'
- steps:
- - name: Archive, Split, and Upload Cache
- shell: bash
- env:
- GH_TOKEN: ${{ inputs.github_token }}
- TARGET_REPO: "${{ github.repository }}"
- run: |
- # Archive, Split, and Upload Cache
- if [ "${{ inputs.debug }}" = "true" ]; then set -x; fi
-
- if [ "${{ inputs.working_dir }}" != "" ]; then
- cd "${{ inputs.working_dir }}"
- fi
- git config user.name "github-actions[bot]"
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
-
- TAG_NAME="${{ inputs.cache_bucket }}"
- BASE_FILENAME="cache-${{ inputs.cache_key }}"
-
- if [ "${{ inputs.compression_level }}" -eq 0 ]; then
- FILENAME="$BASE_FILENAME.tar"
- else
- FILENAME="$BASE_FILENAME.tzst"
- fi
-
- echo "::group:: Checking Release State"
-
- git fetch --tags --force
-
- if ! git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
- echo "Tag '$TAG_NAME' not found. Creating backdated tag..."
- GIT_COMMITTER_DATE="2015-01-01T12:00:00" git tag -a "$TAG_NAME" -m "General Cache Storage"
- if git push origin "$TAG_NAME" --force; then
- echo "Tag pushed"
- else
- echo "Push failed, tag might have been created by a parallel job. Continuing..."
- fi
- else
- echo "Tag '$TAG_NAME' exists locally."
- fi
- if ! gh release view "$TAG_NAME" --repo "$TARGET_REPO" >/dev/null 2>&1; then
- echo "Release '$TAG_NAME' not found. Creating release..."
- # ensure tag is pushed before creating release
- git push origin "$TAG_NAME" --force >/dev/null 2>&1 || true
- gh release create "$TAG_NAME" --title "General Cache" --notes "Automated storage for build assets" --prerelease --repo "$TARGET_REPO" || true
- # verify
- if gh release view "$TAG_NAME" --repo "$TARGET_REPO" >/dev/null 2>&1; then
- echo "Release '$TAG_NAME' created."
- else
- echo "⚠ Failed to create release '$TAG_NAME' — will retry upload anyway"
- gh release view "$TAG_NAME" --repo "$TARGET_REPO" 2>&1 | head -n 20 || true
- fi
- else
- echo "Release '$TAG_NAME' already exists. Ready for upload."
- fi
- echo "::endgroup::"
-
- echo "::group:: Archiving Path"
- echo "Target: ${{ inputs.cache_path }}"
-
- if [ ! -d "${{ inputs.cache_path }}" ] && [ ! -f "${{ inputs.cache_path }}" ]; then
- echo "⚠️ Target path not found. Skipping cache save."
- echo "::endgroup::"
- else
- DIR_NAME=$(dirname "${{ inputs.cache_path }}")
- BASE_NAME=$(basename "${{ inputs.cache_path }}")
-
- if [ "${{ inputs.debug }}" = "true" ]; then
- if [ "${{ inputs.compression_level }}" -eq 0 ]; then
- tar --posix -h -cvf "$FILENAME" -C "$DIR_NAME" "$BASE_NAME"
- else
- tar --posix -h -I "zstd -T0 -${{ inputs.compression_level }}" -cvf "$FILENAME" -C "$DIR_NAME" "$BASE_NAME"
- fi
- else
- if [ "${{ inputs.compression_level }}" -eq 0 ]; then
- tar --posix -h -cvf "$FILENAME" -C "$DIR_NAME" "$BASE_NAME" > /dev/null 2>&1
- else
- tar --posix -h -I "zstd -T0 -${{ inputs.compression_level }}" -cvf "$FILENAME" -C "$DIR_NAME" "$BASE_NAME" > /dev/null 2>&1
- fi
- fi
-
- FILE_SIZE=$(stat -c%s "$FILENAME")
- echo "Archive created: $FILENAME ($FILE_SIZE bytes)"
- echo "::endgroup::"
-
- ASSETS_URL="https://github.com/${TARGET_REPO}/releases/expanded_assets/$TAG_NAME"
- 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 || echo "")
- if [ "${{ inputs.debug }}" = "true" ]; then
- echo "Assets Found: $ALL_ASSETS"
- fi
- else
- echo "Status $HTTP_RESPONSE: Failed to fetch assets from scraper endpoint."
- ALL_ASSETS=""
- fi
- rm -f assets.html
-
- echo "::endgroup::"
-
- echo "::group:: Uploading Assets"
- MAX_SIZE=2000000000 # ~1.86GB
-
- upload_with_retry() {
- local file=$1
- local max_attempts=3
- for ((i=1; i<=max_attempts; i++)); do
- echo " Attempt $i for $file..."
- echo " File: $file ($(stat -c%s "$file" 2>/dev/null || echo "?") bytes) -> tag $TAG_NAME repo $TARGET_REPO"
-
- set +e
- tmp_log=$(mktemp)
- if [ "${{ inputs.debug }}" = "true" ]; then
- set -x
- timeout 10m gh release upload "$TAG_NAME" "$file" --clobber --repo "$TARGET_REPO" 2>&1 | tee "$tmp_log"; rc=${PIPESTATUS[0]}
- set +x
- else
- timeout 10m gh release upload "$TAG_NAME" "$file" --clobber --repo "$TARGET_REPO" >"$tmp_log" 2>&1; rc=$?
- cat "$tmp_log"
- fi
- set -e
-
- if [ $rc -eq 0 ]; then
- echo " Successfully uploaded $file"
- rm -f "$tmp_log"
- return 0
- fi
-
- if [ $rc -eq 124 ]; then
- echo " ⚠ Attempt $i timed out after 10m (timeout exit 124)"
- else
- echo " ⚠ Attempt $i failed with exit $rc"
- fi
- echo " --- gh upload log ---"
- cat "$tmp_log" || true
- echo " --- end log ---"
- rm -f "$tmp_log"
- echo " GH_TOKEN set: $([ -n "$GH_TOKEN" ] && echo yes || echo no) | tag exists: $(git rev-parse "$TAG_NAME" >/dev/null 2>&1 && echo yes || echo no)"
- gh auth status 2>&1 | head -n 20 || true
- echo " ⚠ Attempt $i failed or timed out. Retrying..."
- sleep 5
- done
- return 1
- }
-
- if [ "$FILE_SIZE" -gt "$MAX_SIZE" ]; then
- echo "⚠ File > 2GB. Splitting..."
- split -b 1500M -a 2 "$FILENAME" "${FILENAME}.part"
-
- NEW_PARTS=(${FILENAME}.part*)
- NEW_PARTS_COUNT=${#NEW_PARTS[@]}
-
- if echo "$ALL_ASSETS" | grep -xF "${FILENAME}" >/dev/null 2>&1; then
- echo "Removing old single asset to make way for split parts: ${FILENAME}"
- gh release delete-asset "$TAG_NAME" "${FILENAME}" --repo "$TARGET_REPO" -y || true
- fi
-
- OLD_PARTS=$(echo "$ALL_ASSETS" | grep -F "${FILENAME}.part" || echo "")
-
- if [ -n "$OLD_PARTS" ]; then
- OLD_PARTS_COUNT=$(echo "$OLD_PARTS" | wc -l)
- else
- OLD_PARTS_COUNT=0
- fi
-
- if [ "$OLD_PARTS_COUNT" -gt "$NEW_PARTS_COUNT" ]; then
- echo "Old build had $OLD_PARTS_COUNT parts, new has $NEW_PARTS_COUNT. Cleaning up excess parts..."
- EXT_PARTS=$(echo "$OLD_PARTS" | awk -v skip="$NEW_PARTS_COUNT" 'NR>skip')
- for asset in $EXT_PARTS; do
- echo "Removing leftover trailing part: $asset"
- gh release delete-asset "$TAG_NAME" "$asset" --repo "$TARGET_REPO" -y || true
- done
- fi
-
- for part in "${FILENAME}".part*; do
- echo "Uploading $part..."
- if ! upload_with_retry "$part"; then
- echo "::error::Failed to upload part $part after multiple attempts."
- exit 1
- fi
- rm -f "$part"
- done
- rm -rf "$FILENAME"
- else
- OLD_PARTS=$(echo "$ALL_ASSETS" | grep -F "${FILENAME}.part" || echo "")
- if [ -n "$OLD_PARTS" ]; then
- echo "New build shrunk to single file. Cleaning up old split parts for ${FILENAME}..."
- while read -r asset; do
- [ -z "$asset" ] && continue
- echo "Removing ghost split part: $asset"
- gh release delete-asset "$TAG_NAME" "$asset" --repo "$TARGET_REPO" -y || true
- done <<< "$OLD_PARTS"
- fi
-
- echo "Uploading $FILENAME..."
- if ! upload_with_retry "$FILENAME"; then
- echo "::error::Failed to upload $FILENAME after multiple attempts."
- exit 1
- fi
- rm -f "$FILENAME"
- fi
-
- if [ "${{ inputs.delete_after_upload }}" = "true" ]; then
- rm -rf "${{ inputs.cache_path }}"
- fi
-
- echo "::endgroup::"
- fi
|