Skip to content

Refresh Nightly Torch Stacks #25

Refresh Nightly Torch Stacks

Refresh Nightly Torch Stacks #25

name: Refresh Nightly Torch Stacks
# Re-resolves the pytorch-nightly-index entries in torch-index-stacks.json
# against the live nightly indexes, then commits and publishes the result.
# Nightly wheels are purged from pytorch.org's index after roughly 60 days,
# so these pins decay unless refreshed continuously. If this workflow stalls,
# the desktop app stops OFFERING nightly entries ~45 days after their wheel
# date (installed nightlies are unaffected) - failures here are not urgent,
# but they should be looked at.
#
# Publishing happens here (not via the publish workflow) because commits
# made with GITHUB_TOKEN do not trigger other workflows. The shared
# concurrency group still serializes R2 writes against manual publishes.
#
# R2 is published directly from this run's refreshed working tree; main is
# synced afterwards through a rolling PR because branch protection rejects
# direct pushes to main. Until that PR merges, main lags what is live on R2 -
# that is fine, the desktop reads R2, not the repo.
on:
schedule:
# :17 offset avoids the top-of-the-hour scheduler congestion, when
# GitHub-hosted cron runs are most likely to start late or be skipped.
- cron: '17 6 * * *'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
concurrency:
group: publish-torch-index-stacks
cancel-in-progress: false
jobs:
refresh:
# Never run the schedule on forks.
if: ${{ github.repository_owner == 'Comfy-Org' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
name: Refresh, validate, publish
env:
R2_BUCKET_NAME: ${{ secrets.R2_BUCKET_NAME }}
steps:
- name: Checkout
# Credentials stay persisted: the PR step below pushes a branch.
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
ref: main
- name: Run script tests
run: python3 -m unittest scripts.test_torch_index_scripts -v
- name: Refresh nightly entries
run: python3 scripts/refresh_nightly_stacks.py torch-index-stacks.json
- name: Validate manifest
run: python3 scripts/validate_torch_index_stacks.py torch-index-stacks.json
- name: Detect changes
id: diff
run: |
if git diff --quiet torch-index-stacks.json; then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
- name: Commit updated manifest and open a PR
if: ${{ steps.diff.outputs.changed == 'true' }}
env:
GH_TOKEN: ${{ github.token }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# One rolling branch: a force-push replaces any unmerged previous
# refresh, since a newer resolve always supersedes an older one.
branch="bot/refresh-nightly-stacks"
git checkout -B "$branch"
git add torch-index-stacks.json
git commit -m "chore: refresh nightly torch index stacks"
git push --force origin "$branch"
# Reuse the open PR if one exists; otherwise create it. PRs opened
# with GITHUB_TOKEN do not trigger pull_request CI - close/reopen
# the PR to run checks if branch protection requires them. R2 was
# already validated and published by this run either way.
existing="$(gh pr list --head "$branch" --state open --json number --jq '.[0].number // empty')"
if [ -z "$existing" ]; then
gh pr create --base main --head "$branch" \
--title "chore: refresh nightly torch index stacks" \
--body "Automated nightly refresh of pytorch-nightly-index entries. The refreshed manifest passed validation and has already been published to R2 by the scheduled workflow; merging this PR syncs main with what is live."
else
echo "Updated existing PR #$existing"
fi
# Upload runs unconditionally, not only when the manifest changed: if
# a previous run pushed the commit but the upload failed, the next run
# sees no diff and would otherwise never retry R2.
- name: Upload manifest to R2
env:
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
run: |
# Publication is this workflow's whole point - a missing secret
# must be loud, not a silently-skipped step under a green check.
if [ -z "${R2_BUCKET_NAME}" ]; then
echo "::error::R2_BUCKET_NAME secret is not configured - refreshed manifest was NOT published"
exit 1
fi
# Runner-provided AWS CLI - installing one at runtime would let a
# compromised PyPI release run with publication credentials.
aws --version
# Same object and cache policy as the publish workflow: replaced
# in-place, short TTL so withdrawals and refreshes propagate.
aws s3 cp torch-index-stacks.json \
"s3://${R2_BUCKET_NAME}/standalone-environments/torch-index-stacks.json" \
--endpoint-url "${R2_ENDPOINT}" \
--content-type "application/json" \
--cache-control "public, max-age=300"
echo "Published refreshed torch-index-stacks.json to R2"