Skip to content

Commit 325cc50

Browse files
rwdaigleclaude
andcommitted
Merge origin/main into ryan/install-usage-docs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2 parents d4a7ca3 + 1b70262 commit 325cc50

File tree

20 files changed

+1301
-38
lines changed

20 files changed

+1301
-38
lines changed

.claude/skills/final-review.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Final Review Skill
2+
3+
When performing a final review before merge, include the following checks:
4+
5+
## Template Parity Check
6+
7+
Ensure all supported templates have parity across:
8+
9+
1. **Template files** (`templates/` directory):
10+
- Each template should have a file (e.g., `bash.sh`, `typescript.ts`, `python.py`, `node.js`, `ruby.rb`)
11+
- All templates should include the same example operations (copy file, update JSON, replace directory)
12+
13+
2. **Template registration** (`src/templates.rs`):
14+
- Each template file should be registered in the `TEMPLATES` array
15+
- Verify correct extension mapping
16+
17+
3. **CLI help text** (`src/main.rs`):
18+
- The `--template` flag help text should list all supported templates
19+
20+
4. **Documentation** (`CLAUDE.md`):
21+
- The templates section should list all template files
22+
23+
5. **Integration tests** (`tests/integration.rs`):
24+
- The `test_list_templates` test should assert all templates are present
25+
26+
6. **Fixture tests** (`tests/fixture_operations.rs`):
27+
- Each template should have a dedicated runtime test (e.g., `test_bash_runtime_migration`, `test_ruby_runtime_migration`)
28+
29+
## CLI Command and Option Coverage
30+
31+
Verify each CLI command and option is documented and tested:
32+
33+
### Commands
34+
35+
| Command | Documentation | Integration Test | Fixture Test |
36+
|---------|---------------|------------------|--------------|
37+
| `status` | CLAUDE.md | `test_status_no_migrations_dir`, `test_status_empty_migrations_dir`, `test_status_shows_applied_and_pending` | - |
38+
| `up` | CLAUDE.md | `test_up_applies_migrations`, `test_failed_migration_stops_execution` | All `test_migration_*` tests |
39+
| `create` | CLAUDE.md | `test_create_bash_migration`, `test_create_typescript_migration`, `test_create_increments_prefix` | - |
40+
41+
### Global Options
42+
43+
| Option | Documentation | Integration Test |
44+
|--------|---------------|------------------|
45+
| `-r, --root` | CLAUDE.md | Used in all integration tests |
46+
| `-m, --migrations` | CLAUDE.md | Default used in tests |
47+
48+
### `up` Options
49+
50+
| Option | Documentation | Integration Test | Fixture Test |
51+
|--------|---------------|------------------|--------------|
52+
| `--dry-run` | CLAUDE.md | `test_up_dry_run` | `test_dry_run_preserves_fixture` |
53+
54+
### `create` Options
55+
56+
| Option | Documentation | Integration Test |
57+
|--------|---------------|------------------|
58+
| `--template` | CLAUDE.md | `test_create_typescript_migration` |
59+
| `--description` | CLAUDE.md | (implicit in template content) |
60+
| `--list-templates` | CLAUDE.md | `test_list_templates` |
61+
62+
## How to verify template parity
63+
64+
Run this command to list all templates from the CLI:
65+
```bash
66+
cargo run -- create dummy --list-templates
67+
```
68+
69+
Then verify each template appears in:
70+
- `templates/` directory (one file per template)
71+
- `src/templates.rs` TEMPLATES array
72+
- `src/main.rs` help text for `--template`
73+
- `CLAUDE.md` templates section
74+
- `tests/integration.rs` test_list_templates assertions
75+
- `tests/fixture_operations.rs` runtime test functions
76+
77+
## How to verify CLI coverage
78+
79+
1. Run `cargo run -- -h` and `cargo run -- <command> -h` for each command
80+
2. Verify each option shown in help output has:
81+
- A corresponding entry in CLAUDE.md
82+
- At least one integration test that exercises it
83+
- A fixture test for options that affect migration execution
84+
85+
## Checklist
86+
87+
- [ ] All templates have parity (same example operations)
88+
- [ ] All CLI commands documented in CLAUDE.md
89+
- [ ] All CLI options documented in CLAUDE.md
90+
- [ ] Each command has integration tests
91+
- [ ] Each option has at least one test
92+
- [ ] Migration execution options have fixture tests
93+
- [ ] `cargo nextest run` passes
94+
- [ ] `cargo clippy` passes
95+
- [ ] `cargo fmt --check` passes

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ migrate status # Show applied/pending migrations
99
migrate up # Apply all pending migrations
1010
migrate up --dry-run # Preview without applying
1111
migrate create <name> # Create new bash migration
12-
migrate create <name> --runtime ts # Create TypeScript migration
13-
migrate create <name> --list-runtimes # List available runtimes
12+
migrate create <name> --template ts # Create TypeScript migration
13+
migrate create <name> --list-templates # List available templates
1414
```
1515

1616
## Options
@@ -66,7 +66,7 @@ await fs.writeFile(`${projectRoot}/config.json`, '{}');
6666
- `status.rs` - Status command
6767
- `up.rs` - Up command
6868
- `create.rs` - Create command
69-
- `templates/` - Template source files (bash.sh, typescript.ts, python.py, node.js)
69+
- `templates/` - Template source files (bash.sh, typescript.ts, python.py, node.js, ruby.rb)
7070

7171
## Development
7272

Cargo.lock

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ glob = "0.3"
1717

1818
[dev-dependencies]
1919
tempfile = "3"
20+
serde_json = "1"
2021

2122
[package.metadata.binstall]
2223
pkg-url = "{ repo }/releases/download/v{ version }/migrate-{ target }{ archive-suffix }"

conductor.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
{
2-
"scripts": {
3-
"setup": "./scripts/setup",
4-
"test": "./scripts/test",
5-
"build": "cargo build --release",
6-
"lint": "cargo fmt --check && cargo clippy -- -D warnings"
7-
}
8-
}
2+
"scripts": {
3+
"setup": "./scripts/setup",
4+
"run": "./scripts/test"
5+
}
6+
}

src/commands/create.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,32 @@ pub fn run(
1212
project_root: &Path,
1313
migrations_dir: &Path,
1414
name: Option<&str>,
15-
runtime: &str,
15+
template_name: &str,
1616
description: Option<&str>,
17-
list_runtimes: bool,
17+
should_list_templates: bool,
1818
) -> Result<()> {
19-
// Handle --list-runtimes flag
20-
if list_runtimes {
21-
println!("Available runtimes:");
19+
// Handle --list-templates flag
20+
if should_list_templates {
21+
println!("Available templates:");
2222
for template in list_templates() {
2323
println!(" {}", template);
2424
}
2525
return Ok(());
2626
}
2727

28-
// Name is required when not listing runtimes
28+
// Name is required when not listing templates
2929
let name = match name {
3030
Some(n) => n,
3131
None => bail!("Migration name is required. Usage: migrate create <name>"),
3232
};
3333

34-
// Validate runtime
35-
let template = match get_template(runtime) {
34+
// Validate template
35+
let template = match get_template(template_name) {
3636
Some(t) => t,
3737
None => {
3838
bail!(
39-
"Unknown runtime '{}'. Available: {}",
40-
runtime,
39+
"Unknown template '{}'. Available: {}",
40+
template_name,
4141
list_templates().collect::<Vec<_>>().join(", ")
4242
);
4343
}

src/main.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,17 @@ enum Commands {
3636
/// Migration name (e.g., "add-config")
3737
name: Option<String>,
3838

39-
/// Runtime template (bash, ts, python, node)
39+
/// Template to use (bash, ts, python, node, ruby)
4040
#[arg(short = 't', long, default_value = "bash")]
41-
runtime: String,
41+
template: String,
4242

4343
/// Migration description
4444
#[arg(short = 'd', long)]
4545
description: Option<String>,
4646

47-
/// List available runtimes
47+
/// List available templates
4848
#[arg(long)]
49-
list_runtimes: bool,
49+
list_templates: bool,
5050
},
5151
}
5252

@@ -62,17 +62,17 @@ fn main() -> Result<()> {
6262
}
6363
Commands::Create {
6464
name,
65-
runtime,
65+
template,
6666
description,
67-
list_runtimes,
67+
list_templates,
6868
} => {
6969
commands::create::run(
7070
&cli.root,
7171
&cli.migrations,
7272
name.as_deref(),
73-
&runtime,
73+
&template,
7474
description.as_deref(),
75-
list_runtimes,
75+
list_templates,
7676
)?;
7777
}
7878
}

src/templates.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ pub static TEMPLATES: &[Template] = &[
2929
extension: ".js",
3030
content: include_str!("../templates/node.js"),
3131
},
32+
Template {
33+
name: "ruby",
34+
extension: ".rb",
35+
content: include_str!("../templates/ruby.rb"),
36+
},
3237
];
3338

3439
/// Get a template by name

templates/bash.sh

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,17 @@ set -euo pipefail
44

55
cd "$MIGRATE_PROJECT_ROOT"
66

7-
# Your migration code here
87
echo "Running migration: $MIGRATE_ID"
8+
9+
# Example operations (remove or modify as needed):
10+
11+
# 1. Copy file from migration sub-dir to target location
12+
# cp "$MIGRATE_MIGRATIONS_DIR/$MIGRATE_ID/config.example.json" ./config/config.json
13+
14+
# 2. Update a JSON file: remove one element and set another value
15+
# jq 'del(.oldField) | .settings.newValue = "updated"' config.json > config.json.tmp
16+
# mv config.json.tmp config.json
17+
18+
# 3. Delete one directory and replace it with another
19+
# rm -rf ./old-directory
20+
# cp -r "$MIGRATE_MIGRATIONS_DIR/$MIGRATE_ID/new-directory" ./new-directory

templates/node.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,50 @@ const fs = require('fs').promises;
55
const path = require('path');
66

77
const projectRoot = process.env.MIGRATE_PROJECT_ROOT;
8+
const migrationsDir = process.env.MIGRATE_MIGRATIONS_DIR;
89
const migrationId = process.env.MIGRATE_ID;
910
const dryRun = process.env.MIGRATE_DRY_RUN === 'true';
1011

11-
console.log(`Running migration: ${migrationId}`);
12+
async function main() {
13+
console.log(`Running migration: ${migrationId}`);
1214

13-
// Your migration code here
15+
// Example operations (remove or modify as needed):
16+
17+
// 1. Copy file from migration sub-dir to target location
18+
// const sourceFile = path.join(migrationsDir, migrationId, 'config.example.json');
19+
// const targetFile = path.join(projectRoot, 'config', 'config.json');
20+
// await fs.mkdir(path.dirname(targetFile), { recursive: true });
21+
// await fs.copyFile(sourceFile, targetFile);
22+
23+
// 2. Update a JSON file: remove one element and set another value
24+
// const configPath = path.join(projectRoot, 'config.json');
25+
// const config = JSON.parse(await fs.readFile(configPath, 'utf-8'));
26+
// delete config.oldField;
27+
// config.settings = config.settings || {};
28+
// config.settings.newValue = 'updated';
29+
// await fs.writeFile(configPath, JSON.stringify(config, null, 2));
30+
31+
// 3. Delete one directory and replace it with another
32+
// const oldDir = path.join(projectRoot, 'old-directory');
33+
// const newDirSource = path.join(migrationsDir, migrationId, 'new-directory');
34+
// const newDirTarget = path.join(projectRoot, 'new-directory');
35+
// await fs.rm(oldDir, { recursive: true, force: true });
36+
// await copyDir(newDirSource, newDirTarget);
37+
}
38+
39+
// Helper: recursively copy a directory
40+
async function copyDir(src, dest) {
41+
await fs.mkdir(dest, { recursive: true });
42+
const entries = await fs.readdir(src, { withFileTypes: true });
43+
for (const entry of entries) {
44+
const srcPath = path.join(src, entry.name);
45+
const destPath = path.join(dest, entry.name);
46+
if (entry.isDirectory()) {
47+
await copyDir(srcPath, destPath);
48+
} else {
49+
await fs.copyFile(srcPath, destPath);
50+
}
51+
}
52+
}
53+
54+
main();

0 commit comments

Comments
 (0)