Skip to content

Update proxy.lua

Update proxy.lua #33

Workflow file for this run

name: Build and Release Executable
on:
workflow_dispatch:
inputs:
manual_previous_tag:
description: 'Optional: Manually set the previous tag to generate the changelog from.'
required: false
default: ''
dry_run:
description: 'Dry run mode for pruning (preview without deleting)'
required: false
type: boolean
default: false
push:
paths:
- 'mic_python/python/**'
- 'launch_mic.bat'
- '.github/workflows/build.yml'
- 'cliff.toml'
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: "mic_python/python/requirements.txt"
- name: Set up Python with uv
shell: bash
run: |
uv python install 3.12
uv venv --python 3.12
- name: Install dependencies
shell: bash
run: |
cd mic_python/python
source ../../.venv/bin/activate || . ../../.venv/Scripts/activate
uv pip install -r requirements.txt
uv pip install pyinstaller
- name: Get PyInstaller cache directory
id: pyinstaller-cache-dir
shell: bash
run: |
if [ "${{ runner.os }}" == "Windows" ]; then
echo "path=$USERPROFILE/AppData/Local/pyinstaller" >> $GITHUB_OUTPUT
elif [ "${{ runner.os }}" == "Linux" ]; then
echo "path=$HOME/.cache/pyinstaller" >> $GITHUB_OUTPUT
elif [ "${{ runner.os }}" == "macOS" ]; then
echo "path=$HOME/Library/Application Support/pyinstaller" >> $GITHUB_OUTPUT
fi
- name: Cache PyInstaller build data
uses: actions/cache@v4
with:
path: ${{ steps.pyinstaller-cache-dir.outputs.path }}
key: ${{ runner.os }}-pyinstaller-3.12-${{ hashFiles('mic_python/python/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pyinstaller-3.12-
- name: Build executable
shell: bash
run: |
cd mic_python/python
source ../../.venv/bin/activate || . ../../.venv/Scripts/activate
if [ "${{ runner.os }}" == "Windows" ]; then
./build.bat
else
# Run PyInstaller directly on Unix systems
# Note: --icon parameter works differently on different platforms but PyInstaller handles it
python -m PyInstaller --onefile --name talker_mic --icon=talker_mic.ico --hidden-import=gemini_proxy --hidden-import=whisper_local --hidden-import=whisper_api main.py
fi
- name: Get short SHA
id: version
shell: bash
run: |
sha=$(git rev-parse --short HEAD)
echo "sha=$sha" >> $GITHUB_OUTPUT
- name: Prepare files for artifact
shell: bash
run: |
stagingDir="staging"
mkdir -p $stagingDir
if [ "${{ runner.os }}" == "Windows" ]; then
cp mic_python/python/dist/talker_mic.exe "$stagingDir/"
else
cp mic_python/python/dist/talker_mic "$stagingDir/"
fi
cp launch_mic.bat "$stagingDir/"
echo "--- Staging directory contents ---"
ls -R $stagingDir
echo "------------------------------------"
- name: Archive build artifact
uses: actions/upload-artifact@v4
with:
name: talker-mic-build-${{ runner.os }}-${{ steps.version.outputs.sha }}
path: staging/
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
env:
WHITELISTED_BRANCHES: "main"
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch all tags and history
shell: bash
run: git fetch --prune --tags
- name: Get short SHA
id: get_sha
shell: bash
run: echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Generate Build Version
id: version
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH_NAME=${{ github.ref_name }}
DATE_STAMP_NEW=$(date +'%Y%m%d')
DATE_STAMP_OLD=$(date +'%Y.%m.%d')
# Find the number of releases already created today for this branch, matching either old or new format.
# We use grep -E for an OR condition and wrap it to prevent failures when no matches are found.
BUILD_COUNT=$(gh release list --repo "${{ github.repository }}" --limit 100 | { grep -E "$BRANCH_NAME/build-($DATE_STAMP_NEW|$DATE_STAMP_OLD)" || true; } | wc -l)
# Increment the build number for the new release
BUILD_NUMBER=$((BUILD_COUNT + 1))
# Create the new, sortable version string using the new format
VERSION="$DATE_STAMP_NEW-$BUILD_NUMBER-${{ steps.get_sha.outputs.sha }}"
# Define all naming components
echo "release_title=Build ($BRANCH_NAME): $VERSION" >> $GITHUB_OUTPUT
echo "release_tag=$BRANCH_NAME/build-$VERSION" >> $GITHUB_OUTPUT
echo "archive_version_part=$BRANCH_NAME-$VERSION" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "timestamp=$(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_OUTPUT
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
path: release-assets
pattern: talker-mic-build-*-${{ steps.get_sha.outputs.sha }}
- name: Archive release files
id: archive
shell: bash
run: |
ASSET_PATHS=""
for dir in release-assets/talker-mic-build-*; do
if [ -d "$dir" ]; then
os_name=$(basename "$dir" | cut -d'-' -f4)
archive_name="TALKER-Mic-${os_name}-${{ steps.version.outputs.archive_version_part }}.zip"
(
cd "$dir"
zip -r "../../$archive_name" .
)
if [ -z "$ASSET_PATHS" ]; then
ASSET_PATHS="$archive_name"
else
ASSET_PATHS="$ASSET_PATHS $archive_name"
fi
fi
done
echo "ASSET_PATHS=$ASSET_PATHS" >> $GITHUB_OUTPUT
- name: Install git-cliff
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
API_RESPONSE=$(curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/orhun/git-cliff/releases/latest)
LATEST_CLIFF_URL=$(echo "$API_RESPONSE" | jq -r '.assets[] | select(.name | endswith("x86_64-unknown-linux-gnu.tar.gz")) | .browser_download_url')
if [ -z "$LATEST_CLIFF_URL" ]; then
echo "::error::Could not find git-cliff asset URL."
echo "API Response: $API_RESPONSE"
exit 1
fi
curl -L "$LATEST_CLIFF_URL" | tar xz
sudo mv git-cliff-*/git-cliff /usr/local/bin/
- name: Prepare git-cliff config
shell: bash
run: |
# Inject the GitHub repo URL into your template
sed -i "s|{{ repository_url }}|https://github.com/${GITHUB_REPOSITORY}|g" .github/cliff.toml
echo "✅ cliff.toml:"
head -20 .github/cliff.toml
- name: Generate Changelog
id: changelog
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH_NAME=${{ github.ref_name }}
if [ -n "${{ github.event.inputs.manual_previous_tag }}" ]; then
echo "Manual tag provided: ${{ github.event.inputs.manual_previous_tag }}"
LAST_TAG="${{ github.event.inputs.manual_previous_tag }}"
else
echo "No manual tag, searching for latest tag on branch '$BRANCH_NAME'..."
# Prioritize finding the latest tag with the new format (e.g., build-20250707-1-...).
echo "Attempting to find latest tag with new format..."
LAST_TAG=$(git describe --tags --abbrev=0 --match="$BRANCH_NAME/build-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-*" 2>/dev/null || true)
# If no new format tag is found, fall back to the old, more generic pattern.
if [ -z "$LAST_TAG" ]; then
echo "No new format tag found. Falling back to search for any older build tag..."
LAST_TAG=$(git describe --tags --abbrev=0 --match="$BRANCH_NAME/build-*" 2>/dev/null || echo "")
fi
fi
echo "✅ Using tag: $LAST_TAG"
if [ -n "$LAST_TAG" ]; then
# Standard run: A previous tag was found.
echo "🔍 Generating changelog for range: $LAST_TAG..HEAD"
git-cliff \
--config .github/cliff.toml \
--strip all \
--output changelog.md \
"$LAST_TAG..HEAD"
else
# First run: No previous tag found.
echo "⚠️ No previous build tag found. Generating initial release changelog."
echo "## Initial Release" > changelog.md
echo "" >> changelog.md
echo "This is the first automated build release using this format. Future releases will contain a detailed list of changes." >> changelog.md
fi
# This part of the script remains to handle the output
if [ -s changelog.md ]; then
echo "✅ Changelog generated successfully"
CHANGELOG_B64=$(base64 -w 0 changelog.md)
echo "changelog_b64=$CHANGELOG_B64" >> $GITHUB_OUTPUT
echo "has_changelog=true" >> $GITHUB_OUTPUT
echo "previous_tag=$LAST_TAG" >> $GITHUB_OUTPUT
else
# This is now a true error condition
echo "❌ Critical error: Changelog is empty after generation."
echo "has_changelog=false" >> $GITHUB_OUTPUT
fi
- name: Debug artifact contents
shell: bash
run: |
echo "🔍 Debugging artifact contents..."
echo "Current directory:"
pwd
echo ""
echo "Release assets directory contents:"
ls -la release-assets/ || echo "release-assets directory not found"
echo ""
echo "All files in current directory:"
find . -name "*.exe" -o -name "*.bat" -o -name ".env*" | head -20
echo ""
echo "Directory structure:"
find release-assets -type f 2>/dev/null || echo "No files found in release-assets"
- name: Generate Build Metadata
id: metadata
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Find executable files and get their sizes
WINDOWS_EXE=$(find release-assets -name "talker_mic.exe" -type f | head -1)
if [ -n "$WINDOWS_EXE" ]; then
WIN_SIZE=$(du -sh "$WINDOWS_EXE" | cut -f1)
else
WIN_SIZE="Unknown"
fi
echo "win_build_size=$WIN_SIZE" >> $GITHUB_OUTPUT
LINUX_EXE=$(find release-assets -path "*/talker-mic-build-Linux-*/talker_mic" -type f | head -1)
if [ -n "$LINUX_EXE" ]; then
LINUX_SIZE=$(du -sh "$LINUX_EXE" | cut -f1)
else
LINUX_SIZE="Unknown"
fi
echo "linux_build_size=$LINUX_SIZE" >> $GITHUB_OUTPUT
MACOS_EXE=$(find release-assets -path "*/talker-mic-build-macOS-*/talker_mic" -type f | head -1)
if [ -n "$MACOS_EXE" ]; then
MACOS_SIZE=$(du -sh "$MACOS_EXE" | cut -f1)
else
MACOS_SIZE="Unknown"
fi
echo "macos_build_size=$MACOS_SIZE" >> $GITHUB_OUTPUT
COMMIT_COUNT=$(git rev-list --count HEAD)
# Generate rich contributor list
if [ -n "${{ steps.changelog.outputs.previous_tag }}" ]; then
echo "✅ Found previous tag, getting contributors since ${{ steps.changelog.outputs.previous_tag }}"
CONTRIBUTOR_LOG=$(git log ${{ steps.changelog.outputs.previous_tag }}..HEAD --format='%ae' | sort -u)
else
echo "⚠️ No previous tag found, getting author of the last commit."
CONTRIBUTOR_LOG=$(git log -1 --format='%ae')
fi
CONTRIBUTORS_LIST=""
while read -r email; do
# Find user by email
USER_INFO=$(gh api "search/users?q=$email+in:email" --jq '.items[0]')
if [ -n "$USER_INFO" ]; then
USERNAME=$(echo "$USER_INFO" | jq -r '.login')
AVATAR_URL=$(echo "$USER_INFO" | jq -r '.avatar_url')
CONTRIBUTORS_LIST="$CONTRIBUTORS_LIST [![$USERNAME](https://images.weserv.nl/?url=$AVATAR_URL&w=32&h=32&fit=cover&mask=circle)](https://github.com/$USERNAME) "
fi
done <<< "$CONTRIBUTOR_LOG"
echo "commit_count=$COMMIT_COUNT" >> $GITHUB_OUTPUT
echo "contributors_list=$CONTRIBUTORS_LIST" >> $GITHUB_OUTPUT
echo "📊 Build metadata:"
echo " - Size (Windows): $WIN_SIZE"
echo " - Size (Linux): $LINUX_SIZE"
echo " - Size (macOS): $MACOS_SIZE"
echo " - Commits: $COMMIT_COUNT"
echo " - Contributors: $CONTRIBUTORS_LIST"
- name: Create Release
shell: bash
run: |
# Prepare changelog content
if [ "${{ steps.changelog.outputs.has_changelog }}" == "true" ]; then
echo "${{ steps.changelog.outputs.changelog_b64 }}" | base64 -d > decoded_changelog.md
CHANGELOG_CONTENT=$(cat decoded_changelog.md)
else
CHANGELOG_CONTENT="No significant changes detected in this release."
fi
# Prepare the full release notes in a temporary file
if [ -n "${{ steps.changelog.outputs.previous_tag }}" ]; then
CHANGELOG_URL="**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.changelog.outputs.previous_tag }}...${{ steps.version.outputs.release_tag }}"
else
CHANGELOG_URL=""
fi
# Generate file descriptions
FILE_TABLE="| File | Description |
|------|-------------|
| \`talker_mic.exe\` | Main executable for **Windows** |
| \`talker_mic\` | Main executable for **Linux** and **macOS** |
| \`launch_mic.bat\` | Setup script - use it to launch |"
# List archives
WINDOWS_ARCHIVE=$(echo "${{ steps.archive.outputs.ASSET_PATHS }}" | tr ' ' '\n' | grep 'Windows' || echo "N/A")
LINUX_ARCHIVE=$(echo "${{ steps.archive.outputs.ASSET_PATHS }}" | tr ' ' '\n' | grep 'Linux' || echo "N/A")
MACOS_ARCHIVE=$(echo "${{ steps.archive.outputs.ASSET_PATHS }}" | tr ' ' '\n' | grep 'macOS' || echo "N/A")
ARCHIVE_LIST="- **Windows**: \`$WINDOWS_ARCHIVE\`
- **Linux**: \`$LINUX_ARCHIVE\`
- **macOS**: \`$MACOS_ARCHIVE\`"
cat > releasenotes.md <<-EOF
## Build Information
| Field | Value |
|-------|-------|
| 📦 **Version** | \`${{ steps.version.outputs.version }}\` |
| 💾 **Binary Size** | Win: \`${{ steps.metadata.outputs.win_build_size }}\`, Linux: \`${{ steps.metadata.outputs.linux_build_size }}\`, macOS: \`${{ steps.metadata.outputs.macos_build_size }}\` |
| 🔗 **Commit** | [\`${{ steps.get_sha.outputs.sha }}\`](https://github.com/${{ github.repository }}/commit/${{ github.sha }}) |
| 📅 **Build Date** | \`${{ steps.version.outputs.timestamp }}\` |
| ⚡ **Trigger** | \`${{ github.event_name }}\` |
## 📋 What's Changed
$CHANGELOG_CONTENT
### 📁 Included Files
Each OS-specific archive contains the following files:
$FILE_TABLE
### 📦 Archives
$ARCHIVE_LIST
## 🔗 Useful Links
- 📖 [Documentation](https://github.com/${{ github.repository }}/wiki)
- 🐛 [Report Issues](https://github.com/${{ github.repository }}/issues)
- 💬 [Discussions](https://github.com/${{ github.repository }}/discussions)
- 🌟 [Star this repo](https://github.com/${{ github.repository }}) if you find it useful!
---
> **Note**: This is an automated build release.
$CHANGELOG_URL
EOF
# Set release flags and notes based on the branch
CURRENT_BRANCH="${{ github.ref_name }}"
PRERELEASE_FLAG=""
LATEST_FLAG="--latest"
EXPERIMENTAL_NOTE=""
# Check if the current branch is in the comma-separated whitelist
if ! [[ ",${{ env.WHITELISTED_BRANCHES }}," == *",$CURRENT_BRANCH,"* ]]; then
PRERELEASE_FLAG="--prerelease"
LATEST_FLAG="" # Do not mark non-whitelisted branches as 'latest'
EXPERIMENTAL_NOTE=$(cat <<-EOF
> [!WARNING]
> | ⚠️ **EXPERIMENTAL BUILD** ⚠️ |
> |:---------------------------:|
> This release is from the [\`$CURRENT_BRANCH\`](https://github.com/${{ github.repository }}/tree/$CURRENT_BRANCH) branch and is **highly unstable**. It contains features that are under active development, may be feature-incomplete, contain bugs, or have features that will be removed in the future.
>
> **Do not use in production environments.**
>
> ---
>
> **Found an issue?** Please [report it here](https://github.com/${{ github.repository }}/issues/new/choose) and include the build version (\`${{ steps.version.outputs.version }}\`) in your report.
EOF
)
fi
# Prepend the experimental note if it exists
if [ -n "$EXPERIMENTAL_NOTE" ]; then
echo "$EXPERIMENTAL_NOTE" > releasenotes_temp.md
echo "" >> releasenotes_temp.md
cat releasenotes.md >> releasenotes_temp.md
mv releasenotes_temp.md releasenotes.md
fi
# Create the release using the notes file
gh release create ${{ steps.version.outputs.release_tag }} \
--target ${{ github.sha }} \
--title "${{ steps.version.outputs.release_title }}" \
--notes-file releasenotes.md \
$LATEST_FLAG \
$PRERELEASE_FLAG \
${{ steps.archive.outputs.ASSET_PATHS }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Prune Old Releases
if: always() # Run even if release creation failed (optional, but safer to run only on success usually. Let's stick to default behavior which is success)
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PRUNE_ENABLED: false
PROTECTED_BRANCHES: "main,master,production,prod,staging,develop"
RETENTION_DAYS_FULL: 2
RETENTION_KEEP_ONE_DAILY_OLDER: true
RETENTION_MAX_COUNT: 10
DRY_RUN: ${{ github.event.inputs.dry_run }}
CURRENT_TAG: ${{ steps.version.outputs.release_tag }}
run: |
# 1. Check if enabled
if [ "$PRUNE_ENABLED" != "true" ]; then
echo "ℹ️ Pruning is disabled."
exit 0
fi
CURRENT_BRANCH="${{ github.ref_name }}"
# 2. Check Protected Branches
IFS=',' read -ra PROTECTED <<< "$PROTECTED_BRANCHES"
for branch in "${PROTECTED[@]}"; do
# Trim whitespace
branch=$(echo "$branch" | xargs)
if [ "$CURRENT_BRANCH" == "$branch" ]; then
echo "🛡️ Branch '$CURRENT_BRANCH' is protected. Skipping pruning."
exit 0
fi
done
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✂️ Smart Release Pruning"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Configuration:"
echo " • Retention Window: $RETENTION_DAYS_FULL days (Full retention)"
echo " • Keep Daily Snapshot: $RETENTION_KEEP_ONE_DAILY_OLDER"
echo " • Max Total Releases: $RETENTION_MAX_COUNT"
echo " • Dry Run: $DRY_RUN"
echo ""
# Calculate Cutoff Date (YYYY-MM-DD)
# We want to keep releases from Today, Yesterday, ... up to RETENTION_DAYS_FULL days ago.
# So if RETENTION_DAYS_FULL is 2, we keep Today (0), 1 day ago, 2 days ago.
# Anything strictly OLDER than (Current - 2 days) is candidate for pruning.
CUTOFF_DATE=$(date -d "$RETENTION_DAYS_FULL days ago" +%Y-%m-%d)
echo "📅 Cutoff Date: $CUTOFF_DATE (Releases older than this are subject to daily thinning)"
echo ""
# Fetch releases
# We need tagName and createdAt.
# Filter by branch prefix to be safe, though we are on the branch.
# Note: gh release list lists releases for the repository. We need to filter by tag pattern.
# Tag pattern: $BRANCH_NAME/build-*
echo "🔍 Fetching releases for branch '$CURRENT_BRANCH'..."
# Get JSON data
RELEASES_JSON=$(gh release list --repo "${{ github.repository }}" --limit 1000 --json tagName,createdAt,isDraft,isPrerelease)
# Process in a loop to handle logic
# We will build a list of "TO_DELETE" and "KEPT"
# We need to sort releases by date descending (newest first) to handle the "Max Count" logic correctly.
# gh release list usually returns newest first, but let's be sure.
# We'll use jq to filter and sort, then process line by line
# Filter: tagName starts with "$CURRENT_BRANCH/"
FILTERED_RELEASES=$(echo "$RELEASES_JSON" | jq -c --arg branch "$CURRENT_BRANCH/" --arg current_tag "$CURRENT_TAG" '
map(select(.tagName | startswith($branch))) |
map(select(.tagName != $current_tag)) |
sort_by(.createdAt) | reverse
')
COUNT=$(echo "$FILTERED_RELEASES" | jq 'length')
echo "📦 Found $COUNT historical releases (excluding current build)."
if [ "$COUNT" -eq 0 ]; then
echo "✅ No old releases to prune."
exit 0
fi
# Arrays to track status
declare -a TO_DELETE
declare -a KEPT_RELEASES
# Associative array to track "seen days" for daily snapshot logic
declare -A SEEN_DAYS
# Iterate through releases (Newest to Oldest)
while read -r release; do
TAG=$(echo "$release" | jq -r '.tagName')
CREATED_AT=$(echo "$release" | jq -r '.createdAt')
# Convert ISO8601 to YYYY-MM-DD
RELEASE_DATE=$(date -d "$CREATED_AT" +%Y-%m-%d)
# Logic Check
KEEP=false
REASON=""
# Check 1: Is it within the Full Retention Window?
# We compare strings: If RELEASE_DATE >= CUTOFF_DATE
if [[ "$RELEASE_DATE" > "$CUTOFF_DATE" ]] || [[ "$RELEASE_DATE" == "$CUTOFF_DATE" ]]; then
KEEP=true
REASON="Within retention window ($RETENTION_DAYS_FULL days)"
else
# Check 2: Daily Snapshot
if [ "$RETENTION_KEEP_ONE_DAILY_OLDER" == "true" ]; then
if [ -z "${SEEN_DAYS[$RELEASE_DATE]}" ]; then
KEEP=true
REASON="Daily snapshot for $RELEASE_DATE"
SEEN_DAYS[$RELEASE_DATE]="seen"
else
KEEP=false
REASON="Redundant build for $RELEASE_DATE"
fi
else
KEEP=false
REASON="Older than window and snapshots disabled"
fi
fi
if [ "$KEEP" == "true" ]; then
KEPT_RELEASES+=("$TAG")
echo " ✅ KEEP: $TAG ($RELEASE_DATE) - $REASON"
else
TO_DELETE+=("$TAG")
echo " ❌ PRUNE: $TAG ($RELEASE_DATE) - $REASON"
fi
done < <(echo "$FILTERED_RELEASES" | jq -c '.[]')
echo ""
echo "📊 Phase 1 Result: ${#KEPT_RELEASES[@]} kept, ${#TO_DELETE[@]} marked for pruning."
# Phase 2: Max Count Cap
# KEPT_RELEASES is sorted Newest -> Oldest
if [ "${#KEPT_RELEASES[@]}" -gt "$RETENTION_MAX_COUNT" ]; then
echo "⚠️ Total kept releases (${#KEPT_RELEASES[@]}) exceeds limit ($RETENTION_MAX_COUNT). Trimming oldest..."
# The first MAX_COUNT are safe. The rest must go.
# Bash array slicing: ${array[@]:start:length}
# New kept list is just the first N
FINAL_KEPT=("${KEPT_RELEASES[@]:0:$RETENTION_MAX_COUNT}")
# The overflow are added to delete list
OVERFLOW=("${KEPT_RELEASES[@]:$RETENTION_MAX_COUNT}")
for tag in "${OVERFLOW[@]}"; do
TO_DELETE+=("$tag")
echo " ❌ PRUNE (Overflow): $tag"
done
KEPT_RELEASES=("${FINAL_KEPT[@]}")
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🗑️ Executing Deletions (${#TO_DELETE[@]} items)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [ "${#TO_DELETE[@]}" -eq 0 ]; then
echo "✅ Nothing to delete."
exit 0
fi
for tag in "${TO_DELETE[@]}"; do
if [ "$DRY_RUN" == "true" ]; then
echo " [DRY RUN] Would delete: $tag"
else
echo " Deleting: $tag"
gh release delete "$tag" --repo "${{ github.repository }}" --cleanup-tag --yes || echo " ⚠️ Failed to delete $tag"
fi
done
echo ""
echo "✅ Pruning complete."