Skip to content

Commit 298f130

Browse files
authored
Merge pull request #205 from cipherstash/eql-install-helpers
Eql install helpers
2 parents f267b0a + 1af7209 commit 298f130

File tree

9 files changed

+4080
-16
lines changed

9 files changed

+4080
-16
lines changed

.changeset/fluffy-doodles-pay.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"@cipherstash/drizzle": minor
3+
---
4+
5+
Added `generate-eql-migration` CLI command to automate EQL migration generation.
6+
7+
This command consolidates the manual process of running `drizzle-kit generate --custom` and populating the SQL file into a single command. It uses the bundled EQL SQL from `@cipherstash/schema` for offline-friendly, version-locked installations.
8+
9+
Usage:
10+
```bash
11+
npx generate-eql-migration
12+
npx generate-eql-migration --name setup-eql
13+
npx generate-eql-migration --out migrations
14+
```

.changeset/silly-hounds-scream.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cipherstash/schema": major
3+
---
4+
5+
Include EQL 2.1.8 in package distribution
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# generate-eql-migration CLI Command
2+
3+
A command-line tool for easily generating Drizzle migrations that install CipherStash EQL (Encrypt Query Language) in your PostgreSQL database.
4+
5+
## Purpose
6+
7+
This CLI automates the process of:
8+
1. Creating a custom Drizzle migration file
9+
2. Populating it with the EQL SQL schema (bundled from `@cipherstash/schema`)
10+
3. Making it ready to apply to your database
11+
12+
## Installation
13+
14+
The command is automatically available when you install `@cipherstash/drizzle`:
15+
16+
```bash
17+
pnpm add @cipherstash/drizzle
18+
```
19+
20+
## Usage
21+
22+
### Basic Usage
23+
24+
From your project root (where your Drizzle config is located):
25+
26+
```bash
27+
npx generate-eql-migration
28+
```
29+
30+
This will:
31+
- Generate a migration named `install-eql` in the `drizzle/` directory
32+
- Fill it with the EQL SQL schema
33+
- Show you next steps
34+
35+
### Options
36+
37+
```bash
38+
Usage: generate-eql-migration [options]
39+
40+
Options:
41+
-n, --name <name> Migration name (default: "install-eql")
42+
-o, --out <dir> Output directory (default: "drizzle")
43+
-h, --help Display this help message
44+
```
45+
46+
### Examples
47+
48+
```bash
49+
# Default: creates drizzle/XXXX_install-eql.sql
50+
npx generate-eql-migration
51+
52+
# Custom name
53+
npx generate-eql-migration --name setup-eql
54+
55+
# Custom output directory
56+
npx generate-eql-migration --out migrations
57+
58+
# Both custom name and directory
59+
npx generate-eql-migration --name init-eql --out db/migrations
60+
```
61+
62+
## How It Works
63+
64+
1. **Calls drizzle-kit**: Executes `pnpm drizzle-kit generate --custom --name=<name>` to create an empty migration file
65+
2. **Locates EQL SQL**: Finds `cipherstash-encrypt-2-1-8.sql` from the installed `@cipherstash/schema` package
66+
3. **Populates migration**: Writes the EQL SQL content to the generated migration file
67+
4. **Reports success**: Shows the path to the migration and next steps
68+
69+
## Implementation Details
70+
71+
- **Location**: `packages/drizzle/bin/generate-eql-migration.js`
72+
- **Package.json entry**: `"bin": { "generate-eql-migration": "./bin/generate-eql-migration.js" }`
73+
- **Dependencies**:
74+
- Requires `drizzle-kit` (peer dependency, optional)
75+
- Reads SQL from `@cipherstash/schema` package
76+
- Uses Node.js built-in modules (fs, path, child_process)
77+
78+
## Error Handling
79+
80+
The CLI will exit with an error if:
81+
- `drizzle-kit` is not installed or fails to generate the migration
82+
- The EQL SQL file cannot be found in `@cipherstash/schema`
83+
- The Drizzle output directory doesn't exist
84+
- The generated migration file cannot be found
85+
86+
## After Running
87+
88+
Once the migration is created, apply it with:
89+
90+
```bash
91+
npx drizzle-kit migrate
92+
```
93+
94+
Or use your custom migration workflow.
95+
96+
## Comparison to Manual Process
97+
98+
### Before (manual):
99+
```bash
100+
npx drizzle-kit generate --custom --name=install-eql
101+
curl -sL https://github.com/cipherstash/encrypt-query-language/releases/latest/download/cipherstash-encrypt.sql > drizzle/0001_install-eql.sql
102+
npx drizzle-kit migrate
103+
```
104+
105+
### After (automated):
106+
```bash
107+
npx generate-eql-migration
108+
npx drizzle-kit migrate
109+
```
110+
111+
## Benefits
112+
113+
1. **Offline-friendly**: Uses the bundled SQL file from `@cipherstash/schema` instead of downloading from GitHub
114+
2. **Version-locked**: Always installs the EQL version that matches your installed `@cipherstash/schema` package
115+
3. **Simplified workflow**: Single command instead of multiple steps
116+
4. **Error-resistant**: Validates each step and provides clear error messages
117+
5. **Flexible**: Supports custom names and output directories
118+
119+
## Troubleshooting
120+
121+
### "Failed to generate custom migration"
122+
- Ensure `drizzle-kit` is installed: `pnpm add -D drizzle-kit`
123+
- Check that you're running from the project root with a valid Drizzle config
124+
125+
### "Could not find EQL SQL file"
126+
- Ensure `@cipherstash/schema` is installed (peer dependency)
127+
- The CLI looks for `cipherstash-encrypt-2-1-8.sql` in the schema package
128+
129+
### "Drizzle directory not found"
130+
- Run the command from your project root
131+
- Or specify the correct path with `--out`
132+
133+
### "Could not find migration file"
134+
- The CLI looks for files matching the pattern `*<name>.sql` in the output directory
135+
- Check that `drizzle-kit` successfully created the migration

packages/drizzle/README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,57 @@ Seamlessly integrate Protect.js with Drizzle ORM and PostgreSQL to encrypt your
1717
npm install @cipherstash/protect @cipherstash/drizzle drizzle-orm
1818
```
1919

20+
## Database Setup
21+
22+
Before using encrypted columns, you need to install the CipherStash EQL (Encrypt Query Language) functions in your PostgreSQL database.
23+
24+
### Install EQL via migration
25+
26+
The easiest way is to use the built-in CLI command:
27+
28+
```bash
29+
npx generate-eql-migration
30+
# or: pnpm/yarn/bun generate-eql-migration
31+
```
32+
33+
This will:
34+
1. Generate a custom Drizzle migration (default name: `install-eql`)
35+
2. Populate it with the EQL SQL schema from `@cipherstash/schema`
36+
3. Place it in your `drizzle/` directory
37+
38+
Then run your migrations:
39+
40+
```bash
41+
npx drizzle-kit migrate
42+
# or: pnpm/yarn/bun drizzle-kit migrate
43+
```
44+
45+
#### CLI Options
46+
47+
```bash
48+
Usage: generate-eql-migration [options]
49+
50+
Options:
51+
-n, --name <name> Migration name (default: "install-eql")
52+
-o, --out <dir> Output directory (default: "drizzle")
53+
-h, --help Display this help message
54+
55+
Examples:
56+
npx generate-eql-migration
57+
npx generate-eql-migration --name setup-eql
58+
npx generate-eql-migration --out migrations
59+
```
60+
61+
### Manual installation (alternative)
62+
63+
If you prefer to install EQL manually:
64+
65+
```bash
66+
npx drizzle-kit generate --custom --name=install-eql
67+
curl -sL https://github.com/cipherstash/encrypt-query-language/releases/latest/download/cipherstash-encrypt.sql > drizzle/0001_install-eql.sql
68+
npx drizzle-kit migrate
69+
```
70+
2071
## Quick Start
2172

2273
### 1. Define your schema with encrypted columns
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env node
2+
3+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
4+
import { resolve, dirname, join } from 'node:path';
5+
import { execSync } from 'node:child_process';
6+
import { fileURLToPath } from 'node:url';
7+
8+
const __dirname = dirname(fileURLToPath(import.meta.url));
9+
10+
async function main() {
11+
let migrationPath = null;
12+
const args = process.argv.slice(2);
13+
14+
// Parse arguments
15+
let migrationName = 'install-eql';
16+
let drizzleDir = 'drizzle';
17+
let showHelp = false;
18+
19+
for (let i = 0; i < args.length; i++) {
20+
const arg = args[i];
21+
if (arg === '--help' || arg === '-h') {
22+
showHelp = true;
23+
} else if (arg === '--name' || arg === '-n') {
24+
migrationName = args[++i];
25+
} else if (arg === '--out' || arg === '-o') {
26+
drizzleDir = args[++i];
27+
}
28+
}
29+
30+
if (showHelp) {
31+
console.log(`
32+
Usage: generate-eql-migration [options]
33+
34+
Generate a Drizzle migration that installs CipherStash EQL
35+
36+
Options:
37+
-n, --name <name> Migration name (default: "install-eql")
38+
-o, --out <dir> Output directory (default: "drizzle")
39+
-h, --help Display this help message
40+
41+
Examples:
42+
npx generate-eql-migration
43+
npx generate-eql-migration --name setup-eql
44+
npx generate-eql-migration --out migrations
45+
46+
# Or with your package manager:
47+
pnpm generate-eql-migration
48+
yarn generate-eql-migration
49+
bun generate-eql-migration
50+
`);
51+
process.exit(0);
52+
}
53+
54+
console.log('🔐 Generating EQL migration for Drizzle...\n');
55+
56+
// Step 1: Generate custom migration with drizzle-kit
57+
try {
58+
console.log(`📝 Generating custom migration: ${migrationName}`);
59+
execSync(`npx drizzle-kit generate --custom --name=${migrationName}`, {
60+
stdio: 'inherit'
61+
});
62+
} catch (error) {
63+
console.error('❌ Failed to generate custom migration');
64+
console.error('Make sure drizzle-kit is installed in your project.');
65+
process.exit(1);
66+
}
67+
68+
try {
69+
// Step 2: Find the SQL file from @cipherstash/schema package
70+
const schemaPackagePath = resolve(__dirname, '../../schema');
71+
const sqlFileName = 'cipherstash-encrypt-2-1-8.sql';
72+
const sqlSourcePath = join(schemaPackagePath, sqlFileName);
73+
74+
if (!existsSync(sqlSourcePath)) {
75+
throw new Error(`Could not find EQL SQL file at: ${sqlSourcePath}`);
76+
}
77+
78+
// Step 3: Read the EQL SQL content
79+
const eqlSql = readFileSync(sqlSourcePath, 'utf-8');
80+
81+
// Step 4: Find the generated migration file and write EQL SQL to it
82+
// Drizzle generates migrations in format: 0001_migration_name.sql
83+
const drizzlePath = resolve(process.cwd(), drizzleDir);
84+
85+
if (!existsSync(drizzlePath)) {
86+
throw new Error(`Drizzle directory not found: ${drizzlePath}\nMake sure to run this command from your project root.`);
87+
}
88+
89+
// Find the latest migration file with the specified name
90+
const fs = await import('node:fs/promises');
91+
const files = await fs.readdir(drizzlePath);
92+
const migrationFile = files
93+
.filter(f => f.endsWith('.sql') && f.includes(migrationName))
94+
.sort()
95+
.pop();
96+
97+
if (!migrationFile) {
98+
throw new Error(`Could not find migration file for: ${migrationName}\nLooked in: ${drizzlePath}`);
99+
}
100+
101+
migrationPath = join(drizzlePath, migrationFile);
102+
console.log(`\n📄 Writing EQL SQL to: ${migrationFile}`);
103+
104+
writeFileSync(migrationPath, eqlSql, 'utf-8');
105+
106+
console.log(`\n✅ Successfully created EQL migration!`);
107+
console.log(`\nNext steps:`);
108+
console.log(` 1. Review the migration: ${migrationPath}`);
109+
console.log(` 2. Run migrations: npx drizzle-kit migrate`);
110+
console.log(` (or use your package manager: pnpm/yarn/bun drizzle-kit migrate)`);
111+
} catch (error) {
112+
// Cleanup: remove the migration file if it was created
113+
if (migrationPath && existsSync(migrationPath)) {
114+
try {
115+
unlinkSync(migrationPath);
116+
console.error(`\n🗑️ Cleaned up migration file: ${migrationPath}`);
117+
} catch (cleanupError) {
118+
console.error(`\n⚠️ Failed to cleanup migration file: ${migrationPath}`);
119+
}
120+
}
121+
throw error;
122+
}
123+
}
124+
125+
main().catch(error => {
126+
console.error('❌ Error:', error.message);
127+
process.exit(1);
128+
});

packages/drizzle/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,21 @@
2121
"license": "MIT",
2222
"author": "CipherStash <[email protected]>",
2323
"type": "module",
24+
"bin": {
25+
"generate-eql-migration": "./bin/generate-eql-migration.js"
26+
},
2427
"exports": {
2528
"./pg": {
2629
"types": "./dist/pg/index.d.ts",
2730
"import": "./dist/pg/index.js",
2831
"require": "./dist/pg/index.cjs"
2932
}
3033
},
34+
"files": [
35+
"dist",
36+
"bin",
37+
"README.md"
38+
],
3139
"scripts": {
3240
"build": "tsup",
3341
"dev": "tsup --watch",
@@ -36,7 +44,9 @@
3644
},
3745
"peerDependencies": {
3846
"@cipherstash/protect": ">=10",
47+
"@cipherstash/schema": ">=1.1",
3948
"@types/pg": "*",
49+
"drizzle-kit": ">=0.20",
4050
"drizzle-orm": ">=0.33",
4151
"pg": ">=8",
4252
"postgres": ">=3"
@@ -45,6 +55,9 @@
4555
"@types/pg": {
4656
"optional": true
4757
},
58+
"drizzle-kit": {
59+
"optional": true
60+
},
4861
"pg": {
4962
"optional": true
5063
},

0 commit comments

Comments
 (0)