mirror of
https://github.com/unslothai/unsloth.git
synced 2026-07-09 15:58:41 +00:00
* Studio: UNSLOTH_NPM_REGISTRY opt-in for corporate npm mirrors (#6491) studio/frontend/.npmrc pins registry=https://registry.npmjs.org/ as a supply-chain lock. A project-level pin takes precedence over a user's ~/.npmrc, so behind a corporate firewall that blocks npmjs.org the frontend bun/npm install hit npmjs.org directly and failed with 403. Add an opt-in UNSLOTH_NPM_REGISTRY env var (off by default). When set it is threaded as --registry into every registry-touching install in setup.sh, setup.ps1 and build.sh (bun bootstrap, bun install + retry, npm fallback, OXC validator runtime). --registry is the highest-precedence override for both bun and npm and leaves min-release-age and save-exact in force, so the default lock is unchanged for everyone else. On an install failure that looks like a blocked registry, print guidance pointing at UNSLOTH_NPM_REGISTRY and auto-suggest the mirror already set in the user's npm config. Registries are never switched automatically. Also correct the .npmrc comment: the pin does not block an ambient NPM_CONFIG_REGISTRY env var (npm and bun honor that at higher precedence); it only guards against a lower-precedence stale ~/.npmrc. * Studio: make the registry hint reachable under set -e; clean temp log (#6491) run_quiet_no_exit returns non-zero on failure, which under `set -euo pipefail` exits the script at the call site before the exit code is captured, so the new UNSLOTH_NPM_REGISTRY hint never printed on the npm fallback and OXC validator paths. Guard both with `|| _rc=$?` (the same idiom every other run_quiet_no_exit caller already uses) so the failure branch runs, and remove the _FRONTEND_INSTALL_LOG temp file on the early-exit path. * Studio: detect the user's mirror outside the pinned frontend dir (#6491) _suggest_npm_registry / Show-NpmRegistryHint run while the cwd is still studio/frontend, whose .npmrc pins registry=https://registry.npmjs.org/. So `npm config get registry` returned that pin instead of the user's ~/.npmrc mirror, and the "Detected a registry" branch was skipped for the main corporate case (mirror set in ~/.npmrc). Run the lookup from a directory with no project .npmrc (/ in bash, the temp dir in PowerShell) so the user/global mirror is surfaced. The NPM_CONFIG_REGISTRY env check is unchanged and still takes precedence.
119 lines
4.2 KiB
Bash
119 lines
4.2 KiB
Bash
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
|
|
set -euo pipefail
|
|
|
|
# PyPI/Studio release publishing must use `./build.sh publish` (or an
|
|
# equivalent stamp -> build -> verify-dist -> upload flow) so packaged Studio
|
|
# artifacts include the display-only Studio release version.
|
|
|
|
# 1. Build frontend (Vite outputs to dist/)
|
|
cd studio/frontend
|
|
|
|
# Clean stale dist to force a full rebuild
|
|
rm -rf dist
|
|
|
|
# Tailwind v4's oxide scanner respects .gitignore in parent directories.
|
|
# Python venvs create a .gitignore with "*" (ignore everything), which
|
|
# prevents Tailwind from scanning .tsx source files for class names.
|
|
# Temporarily hide any such .gitignore during the build, then restore it.
|
|
_HIDDEN_GITIGNORES=()
|
|
_dir="$(pwd)"
|
|
while [ "$_dir" != "/" ]; do
|
|
_dir="$(dirname "$_dir")"
|
|
if [ -f "$_dir/.gitignore" ] && grep -qx '\*' "$_dir/.gitignore" 2>/dev/null; then
|
|
mv "$_dir/.gitignore" "$_dir/.gitignore._twbuild"
|
|
_HIDDEN_GITIGNORES+=("$_dir/.gitignore")
|
|
fi
|
|
done
|
|
|
|
_restore_gitignores() {
|
|
for _gi in "${_HIDDEN_GITIGNORES[@]+"${_HIDDEN_GITIGNORES[@]}"}"; do
|
|
mv "${_gi}._twbuild" "$_gi" 2>/dev/null || true
|
|
done
|
|
}
|
|
trap _restore_gitignores EXIT
|
|
|
|
# Corporate-mirror / proxy escape hatch (#6491). When UNSLOTH_NPM_REGISTRY is set we
|
|
# thread it as `--registry <url>` into the installs (overrides frontend/.npmrc's pinned
|
|
# registry for both bun and npm; min-release-age / save-exact stay in force). Empty
|
|
# array (the default) expands to nothing under `set -u`.
|
|
_NPM_REGISTRY_ARGS=()
|
|
if [ -n "${UNSLOTH_NPM_REGISTRY:-}" ]; then
|
|
_NPM_REGISTRY_ARGS=(--registry "$UNSLOTH_NPM_REGISTRY")
|
|
fi
|
|
|
|
# Use bun for install if available (faster), fall back to npm.
|
|
_install_ok=false
|
|
if command -v bun &>/dev/null; then
|
|
if bun install "${_NPM_REGISTRY_ARGS[@]+"${_NPM_REGISTRY_ARGS[@]}"}"; then
|
|
_install_ok=true
|
|
else
|
|
echo "⚠ bun install failed, falling back to npm"
|
|
rm -rf node_modules
|
|
fi
|
|
fi
|
|
if [ "$_install_ok" != "true" ]; then
|
|
if ! npm install "${_NPM_REGISTRY_ARGS[@]+"${_NPM_REGISTRY_ARGS[@]}"}"; then
|
|
echo "❌ ERROR: package install failed" >&2
|
|
echo " If you are behind a corporate firewall/proxy, set UNSLOTH_NPM_REGISTRY to your mirror and retry, e.g.:" >&2
|
|
echo " UNSLOTH_NPM_REGISTRY=https://your-mirror.example/api/npm/ ./build.sh" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
npm run build # outputs to studio/frontend/dist/
|
|
|
|
_restore_gitignores
|
|
trap - EXIT
|
|
|
|
# Validate CSS output -- catch truncated Tailwind builds before packaging
|
|
MAX_CSS_SIZE=$(find dist/assets -name '*.css' -exec wc -c {} + 2>/dev/null | sort -n | tail -1 | awk '{print $1}')
|
|
if [ -z "$MAX_CSS_SIZE" ]; then
|
|
echo "❌ ERROR: No CSS files were emitted into dist/assets."
|
|
echo " The frontend build may have failed silently."
|
|
exit 1
|
|
fi
|
|
if [ "$MAX_CSS_SIZE" -lt 100000 ]; then
|
|
echo "❌ ERROR: Largest CSS file is only $((MAX_CSS_SIZE / 1024))KB (expected >100KB)."
|
|
echo " Tailwind may not have scanned all source files."
|
|
echo " Check for .gitignore files blocking the Tailwind oxide scanner."
|
|
exit 1
|
|
fi
|
|
echo "✅ Frontend CSS validated (${MAX_CSS_SIZE} bytes)"
|
|
|
|
cd ../..
|
|
|
|
# 2. Clean old artifacts
|
|
rm -rf build dist *.egg-info
|
|
|
|
# 3. Stamp display-only Studio release metadata for packaged builds.
|
|
_STUDIO_BUILD_INFO="studio/backend/utils/_studio_release_build.py"
|
|
_STUDIO_BUILD_INFO_BACKUP="$(mktemp)"
|
|
cp "$_STUDIO_BUILD_INFO" "$_STUDIO_BUILD_INFO_BACKUP"
|
|
_restore_studio_build_info() {
|
|
cp "$_STUDIO_BUILD_INFO_BACKUP" "$_STUDIO_BUILD_INFO" 2>/dev/null || true
|
|
rm -f "$_STUDIO_BUILD_INFO_BACKUP"
|
|
}
|
|
trap _restore_studio_build_info EXIT
|
|
|
|
if [ "${1:-}" = "publish" ]; then
|
|
STUDIO_STAMPED_VERSION="$(python scripts/stamp_studio_release.py --require-release)"
|
|
else
|
|
STUDIO_STAMPED_VERSION="$(python scripts/stamp_studio_release.py)"
|
|
fi
|
|
|
|
# 4. Build wheel/sdist
|
|
python -m build
|
|
|
|
if [ "${1:-}" = "publish" ]; then
|
|
python scripts/stamp_studio_release.py --verify-dist dist --expected "$STUDIO_STAMPED_VERSION"
|
|
fi
|
|
|
|
_restore_studio_build_info
|
|
trap - EXIT
|
|
|
|
# 5. Optionally publish
|
|
if [ "${1:-}" = "publish" ]; then
|
|
python -m twine upload dist/*
|
|
fi
|