Caching Optimization
GitHub Actions provides robust mechanisms for caching dependencies and artifacts to accelerate build times and reduce resource consumption. By leveraging caching, you can avoid redundant downloads of packages, minimize I/O operations, and ensure consistent environments across workflows. This section explores advanced caching strategies tailored for CI/CD pipelines, focusing on dependency resolution and artifact reuse.
Built-in Caching in GitHub Actions¶
GitHub Actions includes a built-in caching system for common package managers like npm, pip, and Maven. These caches are automatically managed and optimized for typical workflows. For example:
- npm: Caches
node_modulesdirectory. - pip: Caches Python packages installed via
pip install. - Maven: Caches dependencies from the local repository.
By default, these caches are scoped to the repository and workflow, ensuring that subsequent runs can reuse previously downloaded packages. However, for more granular control or custom use cases, the actions/cache action is recommended.
Using the actions/cache Action¶
The actions/cache action allows you to manually manage cache keys, paths, and expiration. This is ideal for scenarios where the built-in caching is insufficient or when you need to cache non-standard artifacts. Here's how to use it:
1. Restore Cache¶
- name: Restore cached dependencies
uses: actions/cache@v3
with:
path: ./node_modules
key: ${{ hashFiles('package.json') }}
2. Install Dependencies¶
3. Save Cache¶
- name: Save cache
uses: actions/cache@v3
with:
path: ./node_modules
key: $${{ hashFiles('package.json') }}
restore-keys: |
${{ hashFiles('package.json') }}
This approach ensures that the cache is only saved if the restore step succeeds, avoiding stale data.
Best Practices for Cache Management¶
-
Use Semantic Cache Keys
Include variables likeGITHUB_REForGITHUB_SHAin your cache key to differentiate between branches or commits: -
Limit Cache Size
GitHub Actions imposes a 10 GB limit per repository. Prioritize caching large or frequently updated dependencies. -
Invalidate Caches Strategically
Update cache keys when dependencies change (e.g., after apackage.jsonupdate) to avoid using outdated data. -
Combine with Dependency Lock Files
Usepackage-lock.jsonorPipfile.lockto ensure deterministic builds and reduce cache misses.
Example: Caching Dependencies in a Node.js Project¶
name: Build Node.js Project
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Restore cached dependencies
uses: actions/cache@v3
with:
path: ./node_modules
key: ${{ hashFiles('package.json') }}-${{ GITHUB_REF }}
- name: Install dependencies
run: npm install
- name: Build project
run: npm run build
Example: Caching Dependencies in a Python Project¶
name: Build Python Project
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.9'
- name: Restore cached pip dependencies
uses: actions/cache@v3
with:
path: ./venv
key: ${{ hashFiles('requirements.txt') }}-${{ GITHUB_REF }}
- name: Install dependencies
run: |
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
- name: Run tests
run: python -m pytest
Key takeaways¶
- Caching reduces redundant downloads and speeds up builds by reusing dependencies.
- Use
actions/cachefor granular control over cache keys, paths, and expiration. - Semantic cache keys (e.g., including branch names) ensure correct cache reuse.
- Combine caching with lock files to maintain build consistency and minimize cache misses.
- Monitor cache size limits and prioritize caching high-impact dependencies.