Skip to content

feat: add interactive PTY terminal with launch cwd and clud access (#… #89

feat: add interactive PTY terminal with launch cwd and clud access (#…

feat: add interactive PTY terminal with launch cwd and clud access (#… #89

Workflow file for this run

name: Auto Release
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: write
pull-requests: write
actions: read
jobs:
detect-version:
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.version_check.outputs.current }}
should_release: ${{ steps.version_check.outputs.should_release }}
steps:
- uses: actions/checkout@v5
- name: Check if version needs release
id: version_check
run: |
VERSION=$(python -c 'import tomllib; from pathlib import Path; manifest = tomllib.loads(Path("Cargo.toml").read_text(encoding="utf-8")); print(manifest["workspace"]["package"]["version"])')
echo "current=$VERSION" >> $GITHUB_OUTPUT
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/fastled/$VERSION/json")
if [ "$STATUS" = "200" ]; then
echo "should_release=false" >> $GITHUB_OUTPUT
echo "Version $VERSION already on PyPI, skipping release"
else
echo "should_release=true" >> $GITHUB_OUTPUT
echo "Version $VERSION not on PyPI, will release"
fi
# The per-platform CI build workflows already produce release-identical
# wheel/binary artifacts for this same SHA (same rust targets, plat-name
# tags, and soldr cache keys — see the build wrappers). Wait for those runs
# and reuse their artifacts instead of rebuilding all 6 platforms (#170).
# Any platform whose CI run is missing, failed, or timed out falls back to
# a build-* job below. Runs only on push: a workflow_dispatch SHA may have
# no CI runs, so dispatch always builds.
collect-artifacts:
needs: [detect-version]
if: needs.detect-version.outputs.should_release == 'true' && github.event_name == 'push'
runs-on: ubuntu-24.04
timeout-minutes: 45
outputs:
reused_linux_x86: ${{ steps.collect.outputs.reused_linux_x86 }}
reused_linux_arm: ${{ steps.collect.outputs.reused_linux_arm }}
reused_windows_x86: ${{ steps.collect.outputs.reused_windows_x86 }}
reused_windows_arm: ${{ steps.collect.outputs.reused_windows_arm }}
reused_macos_x86: ${{ steps.collect.outputs.reused_macos_x86 }}
reused_macos_arm: ${{ steps.collect.outputs.reused_macos_arm }}
steps:
- name: Wait for CI build runs and download artifacts
id: collect
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
run: |
set -uo pipefail
mkdir -p artifacts
declare -A WF_SUFFIX=(
["linux-x86-build.yml"]="ubuntu-24.04-x86_64-unknown-linux-gnu"
["linux-arm-build.yml"]="ubuntu-24.04-arm-aarch64-unknown-linux-gnu"
["windows-x86-build.yml"]="windows-2025-x86_64-pc-windows-msvc"
["windows-arm-build.yml"]="windows-2025-aarch64-pc-windows-msvc"
["macos-x86-build.yml"]="macos-14-x86_64-apple-darwin"
["macos-arm-build.yml"]="macos-15-aarch64-apple-darwin"
)
deadline=$(( $(date +%s) + 2100 ))
for wf in linux-x86-build.yml linux-arm-build.yml windows-x86-build.yml windows-arm-build.yml macos-x86-build.yml macos-arm-build.yml; do
suffix="${WF_SUFFIX[$wf]}"
key="reused_$(echo "$wf" | sed 's/-build\.yml$//; s/-/_/g')"
reused=false
while :; do
run_json=$(gh api "repos/$REPO/actions/workflows/$wf/runs?head_sha=$SHA&per_page=1" --jq '.workflow_runs[0] // empty' 2>/dev/null || true)
if [ -n "$run_json" ]; then
status=$(jq -r .status <<<"$run_json")
if [ "$status" = "completed" ]; then
conclusion=$(jq -r .conclusion <<<"$run_json")
run_id=$(jq -r .id <<<"$run_json")
if [ "$conclusion" = "success" ] && \
gh run download "$run_id" -R "$REPO" --dir artifacts -n "wheel-$suffix" -n "binary-$suffix"; then
reused=true
else
echo "$wf run $run_id concluded '$conclusion' or artifact download failed; falling back to build."
fi
break
fi
fi
if [ "$(date +%s)" -ge "$deadline" ]; then
echo "Timed out waiting for $wf; falling back to build."
break
fi
sleep 30
done
echo "$wf -> $key=$reused"
echo "$key=$reused" >> "$GITHUB_OUTPUT"
done
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_linux_x86 == 'true'
with:
name: wheel-ubuntu-24.04-x86_64-unknown-linux-gnu
path: artifacts/wheel-ubuntu-24.04-x86_64-unknown-linux-gnu/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_linux_x86 == 'true'
with:
name: binary-ubuntu-24.04-x86_64-unknown-linux-gnu
path: artifacts/binary-ubuntu-24.04-x86_64-unknown-linux-gnu/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_linux_arm == 'true'
with:
name: wheel-ubuntu-24.04-arm-aarch64-unknown-linux-gnu
path: artifacts/wheel-ubuntu-24.04-arm-aarch64-unknown-linux-gnu/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_linux_arm == 'true'
with:
name: binary-ubuntu-24.04-arm-aarch64-unknown-linux-gnu
path: artifacts/binary-ubuntu-24.04-arm-aarch64-unknown-linux-gnu/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_windows_x86 == 'true'
with:
name: wheel-windows-2025-x86_64-pc-windows-msvc
path: artifacts/wheel-windows-2025-x86_64-pc-windows-msvc/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_windows_x86 == 'true'
with:
name: binary-windows-2025-x86_64-pc-windows-msvc
path: artifacts/binary-windows-2025-x86_64-pc-windows-msvc/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_windows_arm == 'true'
with:
name: wheel-windows-2025-aarch64-pc-windows-msvc
path: artifacts/wheel-windows-2025-aarch64-pc-windows-msvc/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_windows_arm == 'true'
with:
name: binary-windows-2025-aarch64-pc-windows-msvc
path: artifacts/binary-windows-2025-aarch64-pc-windows-msvc/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_macos_x86 == 'true'
with:
name: wheel-macos-14-x86_64-apple-darwin
path: artifacts/wheel-macos-14-x86_64-apple-darwin/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_macos_x86 == 'true'
with:
name: binary-macos-14-x86_64-apple-darwin
path: artifacts/binary-macos-14-x86_64-apple-darwin/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_macos_arm == 'true'
with:
name: wheel-macos-15-aarch64-apple-darwin
path: artifacts/wheel-macos-15-aarch64-apple-darwin/*
- uses: actions/upload-artifact@v4
if: steps.collect.outputs.reused_macos_arm == 'true'
with:
name: binary-macos-15-aarch64-apple-darwin
path: artifacts/binary-macos-15-aarch64-apple-darwin/*
# Fallback builds: each runs only when the matching CI artifact could not
# be reused (collect skipped on workflow_dispatch, CI run failed/missing,
# or download failed).
build-linux-x86:
needs: [detect-version, collect-artifacts]
if: >-
!cancelled() &&
needs.detect-version.outputs.should_release == 'true' &&
!(needs.collect-artifacts.result == 'success' && needs.collect-artifacts.outputs.reused_linux_x86 == 'true')
uses: ./.github/workflows/_build.yml
with:
runs-on: ubuntu-24.04
rust-target: x86_64-unknown-linux-gnu
build-linux-arm:
needs: [detect-version, collect-artifacts]
if: >-
!cancelled() &&
needs.detect-version.outputs.should_release == 'true' &&
!(needs.collect-artifacts.result == 'success' && needs.collect-artifacts.outputs.reused_linux_arm == 'true')
uses: ./.github/workflows/_build.yml
with:
runs-on: ubuntu-24.04-arm
rust-target: aarch64-unknown-linux-gnu
build-windows-x86:
needs: [detect-version, collect-artifacts]
if: >-
!cancelled() &&
needs.detect-version.outputs.should_release == 'true' &&
!(needs.collect-artifacts.result == 'success' && needs.collect-artifacts.outputs.reused_windows_x86 == 'true')
uses: ./.github/workflows/_build.yml
with:
runs-on: windows-2025
rust-target: x86_64-pc-windows-msvc
build-windows-arm:
needs: [detect-version, collect-artifacts]
if: >-
!cancelled() &&
needs.detect-version.outputs.should_release == 'true' &&
!(needs.collect-artifacts.result == 'success' && needs.collect-artifacts.outputs.reused_windows_arm == 'true')
uses: ./.github/workflows/_build.yml
with:
runs-on: windows-2025
rust-target: aarch64-pc-windows-msvc
build-macos-x86:
needs: [detect-version, collect-artifacts]
if: >-
!cancelled() &&
needs.detect-version.outputs.should_release == 'true' &&
!(needs.collect-artifacts.result == 'success' && needs.collect-artifacts.outputs.reused_macos_x86 == 'true')
uses: ./.github/workflows/_build.yml
with:
runs-on: macos-14
rust-target: x86_64-apple-darwin
build-macos-arm:
needs: [detect-version, collect-artifacts]
if: >-
!cancelled() &&
needs.detect-version.outputs.should_release == 'true' &&
!(needs.collect-artifacts.result == 'success' && needs.collect-artifacts.outputs.reused_macos_arm == 'true')
uses: ./.github/workflows/_build.yml
with:
runs-on: macos-15
rust-target: aarch64-apple-darwin
create-tag:
needs:
- detect-version
- build-linux-x86
- build-linux-arm
- build-windows-x86
- build-windows-arm
- build-macos-x86
- build-macos-arm
# A skipped build means its artifact was reused from CI by
# collect-artifacts; any failed or cancelled build blocks the release.
if: >-
!cancelled() &&
needs.detect-version.outputs.should_release == 'true' &&
contains(fromJSON('["success", "skipped"]'), needs.build-linux-x86.result) &&
contains(fromJSON('["success", "skipped"]'), needs.build-linux-arm.result) &&
contains(fromJSON('["success", "skipped"]'), needs.build-windows-x86.result) &&
contains(fromJSON('["success", "skipped"]'), needs.build-windows-arm.result) &&
contains(fromJSON('["success", "skipped"]'), needs.build-macos-x86.result) &&
contains(fromJSON('["success", "skipped"]'), needs.build-macos-arm.result)
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/checkout@v5
- name: Create tag if not present
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
TAG="v${{ needs.detect-version.outputs.version }}"
if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
echo "Tag ${TAG} already exists; continuing release workflow."
else
git tag -a "${TAG}" -m "Release ${{ needs.detect-version.outputs.version }}"
git push origin "${TAG}"
fi
publish-pypi:
needs: [detect-version, create-tag]
# Job-level success() (implied by a plain `if`) evaluates false when any
# transitive ancestor was skipped — and the build-* jobs are skipped
# whenever collect-artifacts reused CI artifacts, which silently skipped
# publishing on the v2.0.12 release. Gate explicitly on create-tag instead.
if: >-
!cancelled() &&
needs.detect-version.outputs.should_release == 'true' &&
needs.create-tag.result == 'success'
runs-on: ubuntu-24.04
environment:
name: release
url: https://pypi.org/p/fastled
permissions:
id-token: write
steps:
- name: Download all wheels
uses: actions/download-artifact@v4
with:
pattern: wheel-*
path: dist
merge-multiple: true
- name: List artifacts
run: ls -la dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
create-release:
needs: [detect-version, create-tag]
# Same skipped-ancestor gotcha as publish-pypi above.
if: >-
!cancelled() &&
needs.detect-version.outputs.should_release == 'true' &&
needs.create-tag.result == 'success'
runs-on: ubuntu-24.04
permissions:
contents: write
id-token: write
steps:
# With merge-multiple unset, each artifact lands in its own
# binaries/<artifact-name>/ directory, so the case match below on the
# target-suffixed artifact name works for both reused and rebuilt
# artifacts.
- name: Download all binaries
uses: actions/download-artifact@v4
with:
pattern: binary-*
path: binaries
- name: Prepare release files
shell: bash
run: |
mkdir release
cd binaries
for d in binary-*; do
case "$d" in
*x86_64-unknown-linux-gnu) zip -j "../release/fastled-linux-x86_64.zip" "$d"/fastled ;;
*aarch64-unknown-linux-gnu) zip -j "../release/fastled-linux-aarch64.zip" "$d"/fastled ;;
*x86_64-pc-windows-msvc) zip -j "../release/fastled-windows-x86_64.zip" "$d"/fastled.exe ;;
*aarch64-pc-windows-msvc) zip -j "../release/fastled-windows-aarch64.zip" "$d"/fastled.exe ;;
*x86_64-apple-darwin) zip -j "../release/fastled-macos-x86_64.zip" "$d"/fastled ;;
*aarch64-apple-darwin) zip -j "../release/fastled-macos-aarch64.zip" "$d"/fastled ;;
esac
done
cd ..
ls -la release/
- name: Sign release artifacts with sigstore
uses: sigstore/gh-action-sigstore-python@v3.3.0
with:
inputs: ./release/*.zip
release-signing-artifacts: true
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.detect-version.outputs.version }}
files: |
release/*.zip
release/*.sigstore
draft: false
prerelease: false