mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-04-28 08:39:14 -05:00
feat(tinytorch): single source of truth for version management
- install.sh now fetches version from GitHub tags API instead of hardcoding - README.md badge uses dynamic shields.io GitHub tag filter - Add release.sh script for version bumping and tagging workflow Version is now managed solely in pyproject.toml. Other files read it at runtime or via GitHub API, eliminating version drift across files.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
### Build Your Own ML Framework From Scratch
|
||||
|
||||
[](https://github.com/harvard-edge/cs249r_book/releases?q=tinytorch)
|
||||
[](https://github.com/harvard-edge/cs249r_book/releases?q=tinytorch)
|
||||
[](https://github.com/harvard-edge/cs249r_book/discussions/1076)
|
||||
[](https://mlsysbook.ai/tinytorch)
|
||||
[](https://python.org)
|
||||
|
||||
218
tinytorch/scripts/release.sh
Executable file
218
tinytorch/scripts/release.sh
Executable file
@@ -0,0 +1,218 @@
|
||||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# TinyTorch Release Script
|
||||
# ============================================================================
|
||||
#
|
||||
# USAGE
|
||||
# -----
|
||||
# ./scripts/release.sh 0.1.5
|
||||
# ./scripts/release.sh 0.1.5 --dry-run
|
||||
#
|
||||
# WHAT THIS SCRIPT DOES
|
||||
# ---------------------
|
||||
# 1. Updates version in pyproject.toml (single source of truth)
|
||||
# 2. Updates version in settings.ini (for nbdev compatibility)
|
||||
# 3. Creates a git commit with the version bump
|
||||
# 4. Creates a git tag: tinytorch-v{VERSION}
|
||||
# 5. Pushes commit and tag to origin
|
||||
#
|
||||
# SINGLE SOURCE OF TRUTH
|
||||
# ----------------------
|
||||
# pyproject.toml is THE source of truth for version.
|
||||
# Other files read from it at runtime:
|
||||
# - tinytorch/__init__.py → reads pyproject.toml
|
||||
# - tito/main.py → reads pyproject.toml
|
||||
# - install.sh → fetches from GitHub tags API
|
||||
# - README.md badge → dynamic shields.io badge
|
||||
#
|
||||
# settings.ini is updated for nbdev compatibility but is not authoritative.
|
||||
#
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_step() { echo -e "${BLUE}→${NC} $1"; }
|
||||
print_success() { echo -e "${GREEN}✓${NC} $1"; }
|
||||
print_error() { echo -e "${RED}✗${NC} $1"; }
|
||||
print_warning() { echo -e "${YELLOW}!${NC} $1"; }
|
||||
|
||||
# ============================================================================
|
||||
# Parse Arguments
|
||||
# ============================================================================
|
||||
VERSION=""
|
||||
DRY_RUN=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
print_error "Unknown option: $1"
|
||||
echo "Usage: $0 VERSION [--dry-run]"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [ -z "$VERSION" ]; then
|
||||
VERSION="$1"
|
||||
else
|
||||
print_error "Unexpected argument: $1"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
print_error "Version required"
|
||||
echo ""
|
||||
echo "Usage: $0 VERSION [--dry-run]"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 0.1.5"
|
||||
echo " $0 0.2.0 --dry-run"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate version format (semver-ish)
|
||||
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
print_error "Invalid version format: $VERSION"
|
||||
echo "Expected format: MAJOR.MINOR.PATCH (e.g., 0.1.5)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Verify we're in the right directory
|
||||
# ============================================================================
|
||||
if [ ! -f "pyproject.toml" ]; then
|
||||
print_error "pyproject.toml not found"
|
||||
echo "Run this script from the tinytorch/ directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get current version
|
||||
CURRENT_VERSION=$(grep -E "^version" pyproject.toml | head -1 | sed 's/.*= *"\([^"]*\)".*/\1/')
|
||||
if [ -z "$CURRENT_VERSION" ]; then
|
||||
print_error "Could not read current version from pyproject.toml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TAG_NAME="tinytorch-v${VERSION}"
|
||||
|
||||
# ============================================================================
|
||||
# Show plan
|
||||
# ============================================================================
|
||||
echo ""
|
||||
echo -e "${BOLD}TinyTorch Release${NC}"
|
||||
echo ""
|
||||
echo " Current version: ${CURRENT_VERSION}"
|
||||
echo " New version: ${VERSION}"
|
||||
echo " Tag: ${TAG_NAME}"
|
||||
echo ""
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
print_warning "DRY RUN - No changes will be made"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Check for uncommitted changes
|
||||
# ============================================================================
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
print_warning "You have uncommitted changes"
|
||||
git status --short
|
||||
echo ""
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
read -p "Continue anyway? [y/N] " -r
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Aborted"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Check if tag already exists
|
||||
# ============================================================================
|
||||
if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
|
||||
print_error "Tag ${TAG_NAME} already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Update versions
|
||||
# ============================================================================
|
||||
print_step "Updating pyproject.toml..."
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
sed -i.bak "s/^version = \".*\"/version = \"${VERSION}\"/" pyproject.toml
|
||||
rm -f pyproject.toml.bak
|
||||
print_success "Updated pyproject.toml"
|
||||
else
|
||||
echo " Would update: version = \"${VERSION}\""
|
||||
fi
|
||||
|
||||
print_step "Updating settings.ini..."
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
sed -i.bak "s/^version = .*/version = ${VERSION}/" settings.ini
|
||||
rm -f settings.ini.bak
|
||||
print_success "Updated settings.ini"
|
||||
else
|
||||
echo " Would update: version = ${VERSION}"
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Git commit and tag
|
||||
# ============================================================================
|
||||
print_step "Creating git commit..."
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
git add pyproject.toml settings.ini
|
||||
git commit -m "release: tinytorch v${VERSION}"
|
||||
print_success "Created commit"
|
||||
else
|
||||
echo " Would commit: release: tinytorch v${VERSION}"
|
||||
fi
|
||||
|
||||
print_step "Creating git tag ${TAG_NAME}..."
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
git tag -a "$TAG_NAME" -m "TinyTorch v${VERSION}"
|
||||
print_success "Created tag ${TAG_NAME}"
|
||||
else
|
||||
echo " Would create tag: ${TAG_NAME}"
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Push
|
||||
# ============================================================================
|
||||
print_step "Pushing to origin..."
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
git push origin HEAD
|
||||
git push origin "$TAG_NAME"
|
||||
print_success "Pushed commit and tag"
|
||||
else
|
||||
echo " Would push: HEAD and ${TAG_NAME}"
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Done
|
||||
# ============================================================================
|
||||
echo ""
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
print_success "Release v${VERSION} complete!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Create GitHub release: https://github.com/harvard-edge/cs249r_book/releases/new?tag=${TAG_NAME}"
|
||||
echo " 2. Students can update: tito system update"
|
||||
else
|
||||
print_warning "Dry run complete - no changes made"
|
||||
fi
|
||||
echo ""
|
||||
@@ -67,10 +67,14 @@ set -e # Exit on any error
|
||||
# TINYTORCH_VERSION=0.1.5 TINYTORCH_BRANCH=feature/foo ./install.sh
|
||||
REPO_URL="https://github.com/harvard-edge/cs249r_book.git"
|
||||
REPO_SHORT="harvard-edge/cs249r_book"
|
||||
TAGS_API="https://api.github.com/repos/harvard-edge/cs249r_book/tags"
|
||||
TAG_PREFIX="tinytorch-v"
|
||||
BRANCH="${TINYTORCH_BRANCH:-main}"
|
||||
INSTALL_DIR="${TINYTORCH_INSTALL_DIR:-tinytorch}"
|
||||
SPARSE_PATH="tinytorch"
|
||||
TINYTORCH_VERSION="${TINYTORCH_VERSION:-0.1.4}"
|
||||
# Version is fetched from GitHub tags (single source of truth)
|
||||
# Can be overridden for testing: TINYTORCH_VERSION=0.1.5 ./install.sh
|
||||
TINYTORCH_VERSION="${TINYTORCH_VERSION:-}"
|
||||
|
||||
# ============================================================================
|
||||
# ANSI Color Codes (for terminal output)
|
||||
@@ -132,6 +136,49 @@ command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Fetch latest version from GitHub tags API (single source of truth)
|
||||
# Sets TINYTORCH_VERSION global variable
|
||||
fetch_latest_version() {
|
||||
# Skip if already set via environment variable
|
||||
if [ -n "$TINYTORCH_VERSION" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try curl first (more reliable across platforms)
|
||||
if command_exists curl; then
|
||||
local response
|
||||
response=$(curl -fsSL --max-time 10 "$TAGS_API" 2>/dev/null) || true
|
||||
if [ -n "$response" ]; then
|
||||
# Parse JSON to find first tinytorch-v* tag
|
||||
# Uses grep/sed for portability (no jq dependency)
|
||||
local tag_name
|
||||
tag_name=$(echo "$response" | grep -o "\"name\": *\"${TAG_PREFIX}[^\"]*\"" | head -1 | sed 's/.*"name": *"\([^"]*\)".*/\1/')
|
||||
if [ -n "$tag_name" ]; then
|
||||
TINYTORCH_VERSION="${tag_name#$TAG_PREFIX}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fallback: fetch pyproject.toml directly from raw.githubusercontent.com
|
||||
if command_exists curl; then
|
||||
local pyproject_url="https://raw.githubusercontent.com/${REPO_SHORT}/${BRANCH}/tinytorch/pyproject.toml"
|
||||
local pyproject
|
||||
pyproject=$(curl -fsSL --max-time 10 "$pyproject_url" 2>/dev/null) || true
|
||||
if [ -n "$pyproject" ]; then
|
||||
local version
|
||||
version=$(echo "$pyproject" | grep -E "^version" | head -1 | sed 's/.*= *"\([^"]*\)".*/\1/')
|
||||
if [ -n "$version" ]; then
|
||||
TINYTORCH_VERSION="$version"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Final fallback: unknown version (will still work, just won't show version)
|
||||
TINYTORCH_VERSION="latest"
|
||||
}
|
||||
|
||||
# Check if Python version is 3.8+
|
||||
check_python_version() {
|
||||
local python_cmd="$1"
|
||||
@@ -467,6 +514,9 @@ print_success_message() {
|
||||
# ============================================================================
|
||||
|
||||
main() {
|
||||
# Fetch version from GitHub (single source of truth: pyproject.toml via tags)
|
||||
fetch_latest_version
|
||||
|
||||
print_banner
|
||||
|
||||
# Pre-flight checks
|
||||
|
||||
Reference in New Issue
Block a user