diff --git a/.github/workflows/unsloth-sd-prebuilt.yml b/.github/workflows/unsloth-sd-prebuilt.yml index dceb15524..3b1222e2c 100644 --- a/.github/workflows/unsloth-sd-prebuilt.yml +++ b/.github/workflows/unsloth-sd-prebuilt.yml @@ -331,6 +331,7 @@ jobs: name: sd-${{ needs.resolve.outputs.tag }}-bin-${{ matrix.label }} path: dist/sd-${{ needs.resolve.outputs.tag }}-bin-${{ matrix.label }}.zip if-no-files-found: error + retention-days: 7 # Linux CUDA. The rest of this pipeline is CPU and Apple on the premise that GPU hosts use # diffusers/torch, and that premise fails for MiniMax-H3: its Diffusers path needs about @@ -485,6 +486,7 @@ jobs: name: sd-${{ needs.resolve.outputs.tag }}-bin-Linux-Ubuntu-22.04-x86_64-cuda12 path: dist/sd-${{ needs.resolve.outputs.tag }}-bin-Linux-Ubuntu-22.04-x86_64-cuda12.zip if-no-files-found: error + retention-days: 7 - name: Evict stale ccache files # !cancelled(), unlike the save below: a cancelled job gets one short @@ -572,10 +574,15 @@ jobs: name: sd-${{ needs.resolve.outputs.tag }}-bin-win-cpu-x64 path: dist/sd-${{ needs.resolve.outputs.tag }}-bin-win-cpu-x64.zip if-no-files-found: error + retention-days: 7 assemble: name: Assemble + publish needs: [resolve, build-unix, build-windows, build-linux-cuda] + # Consumed by `reclaim` to tell "these bundles are now release assets" from + # "nothing will ever read these". Set only after draft=false lands. + outputs: + published: ${{ steps.publish.outputs.published }} if: ${{ needs.resolve.outputs.exists != 'true' || github.event_name == 'workflow_dispatch' }} runs-on: ubuntu-22.04 permissions: @@ -649,6 +656,7 @@ jobs: retention-days: 7 - name: Publish GitHub release + id: publish if: ${{ (github.event_name == 'schedule' || inputs.publish) && needs.resolve.outputs.exists != 'true' }} run: | set -eux @@ -673,3 +681,144 @@ jobs: --notes "$NOTES" \ dist/* gh release edit "$TAG" --repo "$REPO" --draft=false + echo "published=true" >> "$GITHUB_OUTPUT" + + + # Ported from unslothai/llama.cpp's unsloth-prebuilt.yml `reclaim` job, which + # this pipeline was copied from without it. Nothing here ever deleted its own + # artifacts, so every run's bundles sat until GitHub's retention expired them: + # measured 2026-08-11, 168 live artifacts / 13.49 GiB, spread over 27 runs + # whose binaries were already published as release assets. + reclaim: + name: Reclaim artifact storage + needs: [resolve, assemble] + # always(), so a run that publishes NOTHING still cleans up after itself. + # Gating on `published` is what leaks: a workflow_dispatch defaults to + # publish:false, and a cancelled run never reaches publish either. + if: ${{ always() }} + runs-on: ubuntu-24.04 + timeout-minutes: 20 + permissions: + actions: write # delete this run's artifacts + contents: read # read the release asset list + steps: + - name: Delete artifacts already published as release assets + if: ${{ needs.assemble.outputs.published == 'true' }} + # Never fail a published release over cleanup. + continue-on-error: true + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ needs.resolve.outputs.tag }} + run: | + set -euo pipefail + repo="$GITHUB_REPOSITORY" + + if [ -z "${TAG:-}" ]; then + echo "no tag resolved; leaving artifacts untouched" + exit 0 + fi + + # Gate 1: the release must exist and be published, not a draft. + draft="$(gh release view "$TAG" --repo "$repo" --json isDraft -q .isDraft 2>/dev/null || echo missing)" + if [ "$draft" != "false" ]; then + echo "release $TAG is '$draft', not a published release; leaving artifacts untouched" + exit 0 + fi + assets="$RUNNER_TEMP/reclaim-assets.txt" + arts="$RUNNER_TEMP/reclaim-arts.tsv" + gh release view "$TAG" --repo "$repo" --json assets -q '.assets[].name' | sort > "$assets" + echo "release $TAG has $(wc -l < "$assets") assets" + + # Gate 2: only THIS run's artifacts are even considered, so the step + # cannot reach another run's -- including a concurrent build's. + gh api "repos/$repo/actions/runs/$GITHUB_RUN_ID/artifacts" --paginate \ + -q '.artifacts[] | select(.expired==false) | "\(.id)\t\(.size_in_bytes)\t\(.name)"' > "$arts" || true + echo "this run has $(grep -c . "$arts" || true) live artifacts" + + freed=0; deleted=0; kept=0; failed=0 + while IFS="$(printf '\t')" read -r id size name; do + [ -z "${id:-}" ] && continue + # Gate 3: delete only what is provably already on the release. + # Build children upload `sd--bin-