# migrate
A generic file migration tool that applies ordered transformations to a project directory. Think database migrations, but for files and project setup. Migrations can be written in any language (bash, TypeScript, Python, etc.) using shebangs.
## Install
### Option 1: Download binary (easiest)
Download the pre-built binary for your platform from [GitHub Releases](https://github.com/glideapps/migrate/releases), then move it to a directory in your PATH:
```bash
# Example for macOS (Apple Silicon)
curl -L https://github.com/glideapps/migrate/releases/latest/download/migrate-aarch64-apple-darwin -o migrate
chmod +x migrate
sudo mv migrate /usr/local/bin/
```
### Option 2: cargo-binstall (recommended if you have Rust)
If you have Rust installed, [cargo-binstall](https://github.com/cargo-bins/cargo-binstall) downloads pre-built binaries:
```bash
# Install cargo-binstall first (if you don't have it)
cargo install cargo-binstall
# Then install migrate
cargo binstall migrate
```
### Option 3: cargo install
Requires [Rust](https://rustup.rs):
```bash
cargo install migrate
```
### Option 4: Build from source
```bash
cargo install --git https://github.com/glideapps/migrate
```
## Usage
### Check migration status
```bash
migrate status
```
### Apply pending migrations
```bash
migrate up
```
### Preview changes without applying
```bash
migrate up --dry-run
```
### Create a new migration
```bash
# Create a bash migration (default)
migrate create add-prettier
# Create a TypeScript migration
migrate create add-config --template ts
# Create with description
migrate create add-prettier -d "Add Prettier configuration"
# List available templates
migrate create --list-templates
```
## Writing Migrations
Migration files use the format `XXXXX-name.{sh,ts,py,...}` where `XXXXX` is a 5-character base36 version automatically generated by `migrate create`. The version encodes the creation timestamp, ensuring migrations sort chronologically.
Migrations are executable files that receive context via environment variables:
```bash
MIGRATE_PROJECT_ROOT=/path/to/project # Absolute path to project root
MIGRATE_MIGRATIONS_DIR=/path/to/migrations # Where migration files live
MIGRATE_ID=1fb2g-initial-setup # Current migration ID (includes version)
### Bash example
```bash
#!/usr/bin/env bash
set -euo pipefail
# Description: Add TypeScript configuration
cd "$MIGRATE_PROJECT_ROOT"
cat > tsconfig.json << 'EOF'
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"strict": true
}
}
EOF
```
### TypeScript example
```typescript
#!/usr/bin/env -S npx tsx
// Description: Add configuration file
import * as fs from 'fs/promises';
import * as path from 'path';
const projectRoot = process.env.MIGRATE_PROJECT_ROOT!;
const config = {
version: 1,
features: ['auth', 'api']
};
await fs.writeFile(
path.join(projectRoot, 'config.json'),
JSON.stringify(config, null, 2)
);
```
Migrations run in order by their version prefix (e.g., `1fb2g-`) and are tracked in a `.history` file.
## CLI Reference
| `migrate status` | Show applied and pending migrations |
| `migrate up` | Apply all pending migrations |
| `migrate create <name>` | Create a new migration file |
### Options
| `-r, --root <path>` | Project root directory | `.` |
| `-m, --migrations <path>` | Migrations directory | `migrations` |
| `--dry-run` | Preview changes (up only) | `false` |
| `-t, --template <name>` | Template to use (create only) | `bash` |
| `-d, --description <text>` | Migration description (create only) | - |
| `--list-templates` | List available templates (create only)| - |
## Available Templates
- `bash` - Shell script (`.sh`)
- `ts` - TypeScript via tsx (`.ts`)
- `python` - Python 3 (`.py`)
- `node` - Node.js (`.js`)
- `ruby` - Ruby (`.rb`)
## Development
```bash
# Clone and setup
git clone <repo-url>
cd migrate
./scripts/setup # Enable git hooks, fetch deps, build, test
# Common commands
cargo build # Build debug binary
cargo nextest run # Run tests
cargo fmt # Format code
cargo clippy # Lint
cargo run -- status # Run CLI locally
# Build release
cargo build --release
```