Bumps the actions group with 12 updates in the / directory: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `4.2.2` | `7.0.1` | | [actions/setup-python](https://github.com/actions/setup-python) | `6.2.0` | `7.0.0` | | [actions/setup-node](https://github.com/actions/setup-node) | `6.4.0` | `7.0.0` | | [actions/cache/restore](https://github.com/actions/cache) | `5.0.5` | `6.1.0` | | [actions/cache/save](https://github.com/actions/cache) | `5.0.5` | `6.1.0` | | [actions/upload-artifact](https://github.com/actions/upload-artifact) | `4.6.1` | `7.0.1` | | [step-security/harden-runner](https://github.com/step-security/harden-runner) | `2.19.1` | `2.20.0` | | [ossf/scorecard-action](https://github.com/ossf/scorecard-action) | `2.4.1` | `2.4.3` | | [github/codeql-action](https://github.com/github/codeql-action) | `3` | `4` | | [tauri-apps/tauri-action](https://github.com/tauri-apps/tauri-action) | `0.6.2` | `1.0.0` | | [trufflesecurity/trufflehog](https://github.com/trufflesecurity/trufflehog) | `3.95.3` | `3.95.9` | | [actions/stale](https://github.com/actions/stale) | `10.2.0` | `10.4.0` | Updates `actions/checkout` from 4.2.2 to 7.0.1 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4.2.2...3d3c42e5aac5ba805825da76410c181273ba90b1) Updates `actions/setup-python` from 6.2.0 to 7.0.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v6.2.0...5fda3b95a4ea91299a34e894583c3862153e4b97) Updates `actions/setup-node` from 6.4.0 to 7.0.0 - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](48b55a011b...8207627860) Updates `actions/cache/restore` from 5.0.5 to 6.1.0 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](27d5ce7f10...55cc834586) Updates `actions/cache/save` from 5.0.5 to 6.1.0 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](27d5ce7f10...55cc834586) Updates `actions/upload-artifact` from 4.6.1 to 7.0.1 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.6.1...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a) Updates `step-security/harden-runner` from 2.19.1 to 2.20.0 - [Release notes](https://github.com/step-security/harden-runner/releases) - [Commits](a5ad31d6a1...bf7454d06d) Updates `ossf/scorecard-action` from 2.4.1 to 2.4.3 - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](f49aabe0b5...4eaacf0543) Updates `github/codeql-action` from 3 to 4 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) Updates `tauri-apps/tauri-action` from 0.6.2 to 1.0.0 - [Release notes](https://github.com/tauri-apps/tauri-action/releases) - [Changelog](https://github.com/tauri-apps/tauri-action/blob/dev/CHANGELOG.md) - [Commits](84b9d35b5f...1deb371b0c) Updates `trufflesecurity/trufflehog` from 3.95.3 to 3.95.9 - [Release notes](https://github.com/trufflesecurity/trufflehog/releases) - [Commits](37b77001d0...27b0417c16) Updates `actions/stale` from 10.2.0 to 10.4.0 - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](b5d41d4e1d...1e223db275) --- updated-dependencies: - dependency-name: actions/cache/restore dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/cache/save dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/checkout dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/setup-node dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/setup-python dependency-version: 6.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: actions/stale dependency-version: 10.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: actions/upload-artifact dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: github/codeql-action dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: ossf/scorecard-action dependency-version: 2.4.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: step-security/harden-runner dependency-version: 2.20.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: tauri-apps/tauri-action dependency-version: 1.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: trufflesecurity/trufflehog dependency-version: 3.95.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions ... Signed-off-by: dependabot[bot] <support@github.com>
156 lines
6.4 KiB
YAML
156 lines
6.4 KiB
YAML
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved.
|
|
|
|
# Measures where Studio's startup time goes, on each platform.
|
|
#
|
|
# Nothing recorded a number before: main.py logs "lifespan startup completed in X ms"
|
|
# and studio_test_kit polls /healthz, but both throw the elapsed time away. A first
|
|
# local run (Linux, warm cache, 18-core server) put `import main` at 5.7-6.6s BEFORE
|
|
# the server can bind, dominated by eager module-level imports pulled in by routes:
|
|
# torch ~1.9s self, unsloth_zoo ~0.8s, routes ~0.6s, transformers ~0.5s.
|
|
#
|
|
# Not a gate yet: --max-healthz-seconds exists, but a budget should come from
|
|
# observed numbers rather than a guess.
|
|
|
|
name: Startup profile
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
# The measured import graph is the whole backend tree: main.py imports auth,
|
|
# core, hub, loggers, models, picker, routes and utils at module scope.
|
|
- 'studio/backend/**'
|
|
- '!studio/backend/tests/**'
|
|
# The launch phase spawns `unsloth studio --api-only`, so the CLI counts too.
|
|
- 'unsloth_cli/**'
|
|
- 'studio/src-tauri/src/preflight**'
|
|
# The profiler hardcodes the desktop argv that process.rs::backend_args builds,
|
|
# so a change there must schedule a run or the two silently diverge.
|
|
- 'studio/src-tauri/src/process.rs'
|
|
- 'scripts/profile_startup.py'
|
|
- '.github/workflows/startup-profile-ci.yml'
|
|
# The job profiles whatever `install.sh --local` built: the installers pick the
|
|
# venv's Python and the dependency specs, and pyproject's include list is what
|
|
# makes --local overlay studio.backend*.
|
|
- 'install.sh'
|
|
- 'install.ps1'
|
|
- 'pyproject.toml'
|
|
# --local also runs the checkout's setup scripts (install.sh picks
|
|
# $_REPO_ROOT/studio/setup.sh, the editable install resolves setup.ps1 to the
|
|
# repo), and both call install_python_stack.py, which picks the dependencies.
|
|
- 'studio/setup.sh'
|
|
- 'studio/setup.ps1'
|
|
- 'studio/install_python_stack.py'
|
|
workflow_dispatch:
|
|
inputs:
|
|
repeats:
|
|
description: 'launch repeats per OS (median reported)'
|
|
type: string
|
|
default: '3'
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
profile:
|
|
name: startup ${{ matrix.os }}
|
|
runs-on: ${{ matrix.os }}
|
|
timeout-minutes: 60
|
|
continue-on-error: true
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [ubuntu-latest, macos-14, windows-latest]
|
|
|
|
env:
|
|
UNSLOTH_STUDIO_HOME: ${{ github.workspace }}/.studio-home
|
|
# A wildcard bind calls ifconfig.me on the startup path; loopback times our code.
|
|
UNSLOTH_STUDIO_DISABLE_PUBLIC_CHECK: '1'
|
|
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Install Studio
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -o pipefail
|
|
mkdir -p logs
|
|
# --local is load-bearing: it overlays the checkout, so the profiled server
|
|
# is this diff. Without it install.sh resolves unsloth from PyPI.
|
|
if [ "${{ runner.os }}" = "Windows" ]; then
|
|
pwsh -NoProfile -File ./install.ps1 --local 2>&1 | tee logs/install.log
|
|
else
|
|
bash install.sh --local 2>&1 | tee logs/install.log
|
|
fi
|
|
|
|
- name: Profile startup
|
|
shell: bash
|
|
run: |
|
|
BIN="$UNSLOTH_STUDIO_HOME/unsloth_studio/bin/unsloth"
|
|
[ -x "$BIN" ] || BIN="$UNSLOTH_STUDIO_HOME/unsloth_studio/Scripts/unsloth.exe"
|
|
[ -x "$BIN" ] || BIN=""
|
|
# Profile imports with the INSTALLED interpreter: that venv is what launches.
|
|
PY="$UNSLOTH_STUDIO_HOME/unsloth_studio/bin/python"
|
|
[ -x "$PY" ] || PY="$UNSLOTH_STUDIO_HOME/unsloth_studio/Scripts/python.exe"
|
|
[ -x "$PY" ] || PY="$(command -v python3 || command -v python)"
|
|
python3 scripts/profile_startup.py \
|
|
--python "$PY" \
|
|
${BIN:+--bin "$BIN"} \
|
|
--repeats "${{ inputs.repeats || '3' }}" \
|
|
--json "startup-${{ matrix.os }}.json" 2>&1 | tee logs/profile.log
|
|
|
|
- name: Summary
|
|
if: always()
|
|
shell: bash
|
|
run: |
|
|
f="startup-${{ matrix.os }}.json"
|
|
[ -f "$f" ] || { echo "no profile produced"; exit 0; }
|
|
python3 - "$f" >> "$GITHUB_STEP_SUMMARY" <<'PY'
|
|
import json, sys
|
|
d = json.load(open(sys.argv[1]))
|
|
print(f"### {d['platform']} / {d['machine']} (py {d['python']}, {d['cpu_count']} cpu)\n")
|
|
imp = d.get("imports", {})
|
|
# Gate on ok: a failed `import main` still leaves rows, so a total can lie.
|
|
if imp.get("ok"):
|
|
print(f"**`import main`: {imp['total_seconds']}s**\n")
|
|
print("| package | self ms |")
|
|
print("|---|---:|")
|
|
for k, v in list(imp.get("self_by_package_ms", {}).items())[:8]:
|
|
print(f"| {k} | {v} |")
|
|
print()
|
|
else:
|
|
print("**`import main` failed - no valid import profile**\n")
|
|
print("```\n" + (imp.get("error") or "")[-1500:] + "\n```\n")
|
|
lau = d.get("launch") or {}
|
|
runs = len(lau.get("runs") or [])
|
|
failed = lau.get("failed_runs") or 0
|
|
if lau.get("healthz_median_seconds") is not None:
|
|
# The aggregates cover only the runs that reached healthz, so flag the
|
|
# failures: bare numbers would read as a normal fast startup.
|
|
note = f" _({runs - failed} of {runs} launches; {failed} never became healthy)_" if failed else ""
|
|
print(f"**time to a healthy port: {lau['healthz_median_seconds']}s median, "
|
|
f"{lau['healthz_max_seconds']}s max**{note}\n")
|
|
elif lau.get("skipped"):
|
|
print(f"_launch phase skipped: {lau['skipped']}_\n")
|
|
elif runs:
|
|
print(f"**no launch measurement: all {runs} launches failed to become healthy**\n")
|
|
PY
|
|
|
|
- name: Upload profile
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: startup-profile-${{ matrix.os }}
|
|
path: |
|
|
startup-*.json
|
|
logs/
|
|
retention-days: 14
|
|
if-no-files-found: warn
|