GitHub Cache¶
Cache dependencies and build outputs across workflow runs for faster execution.
Overview¶
The GitHubCache class provides a typed interface for GitHub Actions cache operations. It enables performance optimizations through caching across workflow runs, supporting hierarchical key fallbacks and cross-job data sharing.
API Reference¶
GitHubCache(github_token=None, github_repo=None)¶
Initializes the cache client for GitHub Actions cache operations.
Both parameters are optional but environment variables for them need to be present: GITHUB_TOKEN and GITHUB_REPOSITORY respectively.
example:
>> from github_action_toolkit import GitHubCache
>> cache = GitHubCache()
GitHubCache.save_cache(paths, key, enable_cross_os_archive=False)¶
Save cache with the specified key. Supports composite keys for fine-grained cache management.
paths: List of file/directory paths to cache (list of strings or Path objects)key: Primary cache key (can include variables like version, hash, platform)enable_cross_os_archive(optional): Enable cross-OS compatibility (default: False)
Returns the cache ID if successful, None if cache already exists.
example:
>> import hashlib
>> from pathlib import Path
>>
>> # Calculate hash of lockfile for cache key
>> lockfile = Path("package-lock.json")
>> lockfile_hash = hashlib.sha256(lockfile.read_bytes()).hexdigest()[:8]
>>
>> # Create composite key with platform and hash
>> import platform
>> cache_key = f"npm-{platform.system()}-{lockfile_hash}"
>>
>> # Save node_modules to cache
>> paths = ["node_modules", ".npm"]
>> cache_id = cache.save_cache(paths, cache_key)
>> if cache_id:
>> print(f"Cache saved with ID: {cache_id}")
>> else:
>> print("Cache already exists")
# Output:
# Cache saved with ID: 123456
GitHubCache.restore_cache(paths, primary_key, restore_keys=None, enable_cross_os_archive=False)¶
Restore cache with fallback key hierarchy. Tries the primary key first, then falls back to restore keys in order.
paths: List of file/directory paths to restore to (list of strings or Path objects)primary_key: Primary cache key to look for (exact match)restore_keys(optional): List of fallback keys to try if primary not found (supports prefix matching)enable_cross_os_archive(optional): Enable cross-OS compatibility (default: False)
Returns the matched cache key if found and restored, None if no cache found.
example:
>> import hashlib
>> import platform
>> from pathlib import Path
>>
>> # Calculate hash for primary key
>> lockfile_hash = hashlib.sha256(Path("package-lock.json").read_bytes()).hexdigest()[:8]
>> system = platform.system()
>>
>> # Define primary and fallback keys
>> primary_key = f"npm-{system}-{lockfile_hash}"
>> restore_keys = [
>> f"npm-{system}-", # Any cache for this OS
>> "npm-", # Any npm cache
>> ]
>>
>> # Try to restore cache
>> matched_key = cache.restore_cache(["node_modules"], primary_key, restore_keys)
>> if matched_key:
>> print(f"Cache restored from key: {matched_key}")
>> else:
>> print("No cache found")
# Output:
# Cache restored from key: npm-Linux-abc12345
GitHubCache.is_feature_available()¶
Check if the cache feature is available in the current environment. Useful for gracefully handling cases where cache is not available.
Returns True if cache is available, False otherwise.
example:
>> if cache.is_feature_available():
>> print("Cache is available")
>> cache.save_cache(paths, key)
>> else:
>> print("Cache is not available, skipping")
# Output:
# Cache is available
Common Usage Patterns¶
Caching Dependencies¶
from github_action_toolkit import GitHubCache
import hashlib
import platform
from pathlib import Path
cache = GitHubCache()
# Generate cache key from lockfile
lockfile = Path("requirements.txt")
lockfile_hash = hashlib.sha256(lockfile.read_bytes()).hexdigest()[:8]
cache_key = f"python-{platform.system()}-{lockfile_hash}"
# Try to restore cache
restore_keys = [f"python-{platform.system()}-"]
matched_key = cache.restore_cache([".venv"], cache_key, restore_keys)
if matched_key:
print(f"Dependencies restored from cache: {matched_key}")
else:
print("No cache found, installing dependencies...")
# Install dependencies here
# Save to cache for next time
cache_id = cache.save_cache([".venv"], cache_key)
if cache_id:
print(f"Dependencies cached with ID: {cache_id}")
Cross-Job Data Passing¶
# Job 1: Build and cache artifacts
from github_action_toolkit import GitHubCache
cache = GitHubCache()
build_key = f"build-{os.environ['GITHUB_SHA']}"
# Build your project
# ... build process ...
# Cache build outputs
cache.save_cache(["dist", "build"], build_key)
# Job 2: Use cached artifacts
from github_action_toolkit import GitHubCache
cache = GitHubCache()
build_key = f"build-{os.environ['GITHUB_SHA']}"
# Restore build outputs from Job 1
matched_key = cache.restore_cache(["dist", "build"], build_key)
if matched_key:
print("Build artifacts restored")
# Use artifacts for deployment/testing
Exception Handling¶
The cache module provides specific exceptions for different error scenarios:
from github_action_toolkit import (
GitHubCache,
CacheNotFoundError,
CacheRestoreError,
CacheSaveError
)
cache = GitHubCache()
try:
cache.save_cache(["node_modules"], "my-cache-key")
except CacheSaveError as e:
print(f"Failed to save cache: {e}")
try:
cache.restore_cache(["node_modules"], "my-cache-key")
except CacheRestoreError as e:
print(f"Failed to restore cache: {e}")
Cache Key Best Practices¶
Include platform information for OS-specific dependencies:
key = f"deps-{platform.system()}-{hash}"
Use file hashes for exact dependency matching:
hash = hashlib.sha256(Path("package-lock.json").read_bytes()).hexdigest()[:8]
Create fallback hierarchy for better cache hit rates:
primary = f"npm-Linux-abc12345" fallbacks = ["npm-Linux-", "npm-"]
Keep keys under 512 characters to comply with GitHub’s limits.
Use semantic versioning in keys when appropriate:
key = f"build-v1.2.3-{platform.system()}"
Caching Strategies¶
Dependency Caching¶
from pathlib import Path
from github_action_toolkit import GitHubCache, info
def cache_dependencies(requirements_file: Path):
"""Cache Python dependencies."""
cache = GitHubCache()
# Generate cache key from requirements hash
key = f"python-deps-{hash_file(requirements_file)}"
restore_keys = ['python-deps-'] # Fallback to any python deps
# Try to restore
cache_hit = cache.restore_cache(
paths=['.venv'],
key=key,
restore_keys=restore_keys
)
if cache_hit:
info(f'Cache hit: {key}')
return True
info('Cache miss, installing dependencies...')
# Install dependencies
install_dependencies()
# Save for next time
cache.save_cache(paths=['.venv'], key=key)
return False
def hash_file(path: Path) -> str:
"""Generate hash of file contents."""
import hashlib
return hashlib.sha256(path.read_bytes()).hexdigest()[:8]
Build Output Caching¶
from github_action_toolkit import GitHubCache, info
def cache_build_artifacts():
"""Cache build outputs."""
cache = GitHubCache()
# Include environment in key for cross-OS safety
import platform
os_name = platform.system().lower()
key = f'build-{os_name}-{get_git_commit_hash()}'
try:
if cache.restore_cache(paths=['dist', 'build'], key=key):
info('Using cached build artifacts')
return True
except Exception as e:
info(f'Cache restore failed: {e}')
return False