Local Package Management #
Velocity supports project-local package management similar to npm’s node_modules, enabling reproducible builds and CI caching.
Overview #
Local package management allows each project to have its own isolated set of packages, preventing version conflicts and ensuring reproducible builds across different environments.
Project Structure #
When you initialize a project with velo init
, Velocity creates this structure:
~/my-project/
├── .velo/ # Local package directory (like node_modules)
│ ├── Cellar/ # Locally installed packages
│ │ ├── imagemagick/7.1.1-40/
│ │ └── ffmpeg/7.1.0/
│ ├── bin/ # Local binary symlinks
│ ├── opt/ # Local opt symlinks
│ └── cache/ # Local download cache
├── velo.json # Package manifest (like package.json)
├── velo.lock # Lock file with exact versions
└── your-project-files/
Package Manifest (velo.json) #
The velo.json
file defines your project’s dependencies and configuration:
{
"name": "my-project",
"dependencies": {
"imagemagick": "^7.1.0",
"ffmpeg": "^7.0.0",
"shellcheck": "^0.10.0"
},
"taps": [
"wix/brew",
"user/custom-tools"
]
}
Fields #
- name (optional) - Project name for identification
- dependencies - Packages required by this project
- taps - Additional repositories needed for dependencies
Version Specifications #
Velocity supports semantic versioning patterns:
"^7.1.0"
- Compatible with 7.1.0, allows 7.1.x and 7.x.x"~7.1.0"
- Compatible with 7.1.0, allows 7.1.x only"7.1.0"
- Exact version 7.1.0">=7.1.0"
- Version 7.1.0 or higher"latest"
- Always use the latest available version
Lock File (velo.lock) #
Velocity automatically generates a velo.lock
file to ensure reproducible builds:
{
"lockfileVersion": 1,
"dependencies": {
"imagemagick": {
"version": "7.1.1-40",
"resolved": "https://ghcr.io/v2/homebrew/core/imagemagick/blobs/sha256:abc123...",
"sha256": "abc123def456...",
"tap": "homebrew/core",
"dependencies": {
"libpng": "1.6.40",
"jpeg-turbo": "3.0.1"
}
},
"ffmpeg": {
"version": "7.1.0",
"resolved": "https://ghcr.io/v2/homebrew/core/ffmpeg/blobs/sha256:def456...",
"sha256": "def456ghi789...",
"tap": "homebrew/core",
"dependencies": {
"x264": "r3095",
"x265": "3.5"
}
}
},
"taps": {
"homebrew/core": {
"commit": "abc123def456"
}
}
}
Lock File Features #
- Exact versions - Locks down specific package versions
- Integrity hashes - SHA256 verification for security
- Dependency resolution - Tracks exact resolved dependency versions
- Tap tracking - Records source taps and commit hashes
- Human readable - JSON format for easy inspection
Working with Local Packages #
Initialize a Project #
# Create velo.json in current directory
velo init
# Initialize with project name
velo init --name "my-project"
# Initialize with dependencies
velo init --with imagemagick,ffmpeg
Install Dependencies #
# Install packages locally (adds to velo.json)
velo install imagemagick ffmpeg
# Install all dependencies from velo.json
velo install
# Install exactly from velo.lock (CI mode)
velo install --frozen
# Verify packages before installing
velo install --check
Use Local Packages #
# Execute commands using local packages
velo exec convert image.jpg output.png # Uses local imagemagick
velo exec ffmpeg -i video.mp4 output.gif # Uses local ffmpeg
# Run interactive shell with local packages
velo exec bash
# Show which version will be used
velo which convert # Shows resolution order
Version Resolution Priority #
When running commands, Velocity resolves binaries in this order:
- Local packages -
./.velo/bin/
(project-specific) - Parent directories -
../.velo/bin/
(if enabled) - Global packages -
~/.velo/bin/
(user-wide) - System commands -
/usr/local/bin
,/usr/bin
(fallback)
This ensures project-specific packages always take precedence over global ones.
Tap Management #
Adding Taps to Projects #
# Add tap to current project (updates velo.json)
velo tap add user/homebrew-tools
# Add tap globally (doesn't update velo.json)
velo tap add user/homebrew-tools --global
Automatic Tap Resolution #
The taps
field in velo.json
ensures required repositories are automatically available:
{
"dependencies": {
"custom-tool": "^1.0.0"
},
"taps": [
"user/homebrew-tools"
]
}
When someone runs velo install
, Velocity automatically:
- Clones the required taps
- Installs dependencies from those taps
- Updates the lock file with tap commit hashes
CI/CD Integration #
GitHub Actions Example #
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Cache Velo packages
uses: actions/cache@v3
with:
path: .velo
key: ${{ runner.os }}-velo-${{ hashFiles('velo.lock') }}
restore-keys: |
${{ runner.os }}-velo-
- name: Install Velocity
run: |
git clone https://github.com/dimaosipa/velocity.git
cd velocity && ./install.sh
- name: Install dependencies
run: velo install --frozen # Uses exact versions from lock file
- name: Run tests
run: velo exec bash -c "your-test-command"
Enhanced CI Reliability #
- Required taps are automatically cloned from the
taps
field in velo.json - No manual tap setup needed in CI environments
- Consistent package resolution across all environments
- Lock file verification ensures integrity:
velo verify
Advanced Features #
Verification #
# Verify installed packages match velo.lock
velo verify
# Show detailed differences
velo verify --verbose
# Verify specific package
velo verify imagemagick
Cleaning #
# Clean local packages only
velo clean --packages
# Clean local cache
velo clean --cache
# Clean everything locally
velo clean --all
Global vs Local Commands #
# Install locally (default in project directory)
velo install wget
# Install globally (accessible everywhere)
velo install wget --global
# List local packages
velo list --local
# List global packages
velo list --global
Best Practices #
Version Management #
- Use semantic versioning patterns in velo.json (
^7.1.0
) - Commit velo.lock to version control for reproducible builds
- Don’t commit .velo/ directory (add to .gitignore)
- Use
--frozen
flag in CI to install exact versions
Project Setup #
# Add .velo to .gitignore
echo ".velo/" >> .gitignore
# Initialize project with common tools
velo init --with imagemagick,ffmpeg,shellcheck
Performance Optimization #
- Cache .velo directory in CI using lock file hash
- Use local packages for project-specific tools
- Install build tools globally for general use
- Clean cache periodically to save disk space
Benefits #
- Reproducible Builds -
velo.lock
ensures exact versions and integrity - CI Caching - Cache
.velo
directory based on lock file hash - Project Isolation - No global pollution between projects
- Familiar Workflow - Similar to npm/yarn ecosystem
- Security - SHA256 verification of all downloaded packages
- Version Conflicts - Different projects can use different tool versions
Migration from Global Packages #
If you have global packages and want to use local management:
# Initialize project
velo init
# Add currently installed global packages
velo install imagemagick ffmpeg --local
# Verify everything works
velo exec convert --version
velo exec ffmpeg -version
This creates a local installation with the same packages, isolating your project from global changes.