mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-18 13:28:12 -04:00
Add scripts to convert EF core structure to Typescript definitions (#955)
This commit is contained in:
committed by
Leendert de Borst
parent
3e82f78fe9
commit
41b2a959ed
3
apps/server/Databases/AliasClientDb/Scripts/.gitignore
vendored
Normal file
3
apps/server/Databases/AliasClientDb/Scripts/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Ignore generated files
|
||||
MigrationSql
|
||||
MigrationTs
|
||||
7
apps/server/Databases/AliasClientDb/Scripts/README.md
Normal file
7
apps/server/Databases/AliasClientDb/Scripts/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# SQL Generation Scripts
|
||||
|
||||
This directory contains scripts to generate SQL files from Entity Framework Core migrations and convert them to TypeScript constants which are used by the `./shared/vault-sql` shared TS library.
|
||||
|
||||
This shared TS library is consumed by the web app, browser extensions and mobile apps for vault creation and upgrades.
|
||||
|
||||
Refer to the docs `upgrade-ef-client-model.md` for how this scripts are used.
|
||||
233
apps/server/Databases/AliasClientDb/Scripts/convert-sql-to-ts.sh
Executable file
233
apps/server/Databases/AliasClientDb/Scripts/convert-sql-to-ts.sh
Executable file
@@ -0,0 +1,233 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to convert SQL migration files to TypeScript constants
|
||||
# Run this after generate-sql-files.sh to create SqlConstants.ts and VaultVersions.ts
|
||||
|
||||
# Configurable settings
|
||||
SQL_DIR="MigrationSql"
|
||||
OUTPUT_FILE="MigrationTs/SqlConstants.ts"
|
||||
VERSIONS_FILE="MigrationTs/VaultVersions.ts"
|
||||
TEMP_DIR="/tmp/sql_to_ts"
|
||||
|
||||
# Path to the shared vault-sql package
|
||||
SHARED_SQL_DIR="../../../../../shared/vault-sql/src/sql"
|
||||
|
||||
# Create temp directory and output directory
|
||||
mkdir -p "$TEMP_DIR"
|
||||
mkdir -p "$(dirname "$OUTPUT_FILE")"
|
||||
|
||||
# Function to escape SQL content for TypeScript
|
||||
escape_sql_for_ts() {
|
||||
local sql_content="$1"
|
||||
# Escape backticks and backslashes
|
||||
echo "$sql_content" | sed 's/\\/\\\\/g' | sed 's/`/\\`/g'
|
||||
}
|
||||
|
||||
# Function to extract migration number from filename
|
||||
extract_migration_number() {
|
||||
local filename="$1"
|
||||
# Extract number from filename like "001_InitialMigration_to_AddEmail.sql"
|
||||
echo "$filename" | sed -n 's/^0*\([0-9]*\)_.*\.sql$/\1/p'
|
||||
}
|
||||
|
||||
# Function to extract migration name from filename
|
||||
extract_migration_name() {
|
||||
local filename="$1"
|
||||
# Extract name from filename like "001_InitialMigration_to_AddEmail.sql"
|
||||
echo "$filename" | sed -n 's/^[0-9]*_\(.*\)\.sql$/\1/p'
|
||||
}
|
||||
|
||||
# Function to extract version, description, and release date from filename (using the last migration segment)
|
||||
extract_version_info() {
|
||||
local filename="$1"
|
||||
# Get the last migration segment (after the last _)
|
||||
local last_segment=$(echo "$filename" | awk -F'_to_' '{print $NF}' | sed 's/\.sql$//')
|
||||
# last_segment example: 20250310131554_1.5.0-AddTotpCodes
|
||||
local timestamp=$(echo "$last_segment" | awk -F'_' '{print $1}')
|
||||
local version_and_desc=$(echo "$last_segment" | awk -F'_' '{print $2}')
|
||||
local version=$(echo "$version_and_desc" | awk -F'-' '{print $1}')
|
||||
local description=$(echo "$version_and_desc" | awk -F'-' '{print $2}')
|
||||
# Format date
|
||||
local year=${timestamp:0:4}
|
||||
local month=${timestamp:4:2}
|
||||
local day=${timestamp:6:2}
|
||||
local release_date="$year-$month-$day"
|
||||
# Make description readable
|
||||
local readable_desc=$(echo "$description" | sed 's/\([A-Z]\)/ \1/g' | sed 's/^./\U&/' | sed 's/^U //;s/^ *//')
|
||||
echo "$version|$readable_desc|$release_date"
|
||||
}
|
||||
|
||||
# Function to copy files to shared vault-sql directory
|
||||
copy_to_shared_sql() {
|
||||
local source_file="$1"
|
||||
local target_file="$2"
|
||||
|
||||
if [ -f "$source_file" ]; then
|
||||
echo "Copying $source_file to $target_file"
|
||||
cp "$source_file" "$target_file"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✓ Successfully copied to shared vault-sql directory"
|
||||
else
|
||||
echo "✗ Failed to copy to shared vault-sql directory"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "✗ Source file $source_file not found"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Converting SQL files to TypeScript constants..."
|
||||
|
||||
# Start building the TypeScript file
|
||||
cat > "$OUTPUT_FILE" << 'EOF'
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
|
||||
/**
|
||||
* Complete database schema SQL (latest version)
|
||||
* Auto-generated from EF Core migrations
|
||||
*/
|
||||
export const COMPLETE_SCHEMA_SQL = `
|
||||
EOF
|
||||
|
||||
# Add the full schema SQL if it exists
|
||||
if [ -f "$SQL_DIR/000_FullSchema.sql" ]; then
|
||||
echo "Adding complete schema SQL..."
|
||||
FULL_SCHEMA=$(cat "$SQL_DIR/000_FullSchema.sql")
|
||||
ESCAPED_SCHEMA=$(escape_sql_for_ts "$FULL_SCHEMA")
|
||||
echo "$ESCAPED_SCHEMA" >> "$OUTPUT_FILE"
|
||||
else
|
||||
echo "Warning: 000_FullSchema.sql not found"
|
||||
fi
|
||||
|
||||
# Close the complete schema constant
|
||||
cat >> "$OUTPUT_FILE" << 'EOF'
|
||||
`;
|
||||
/**
|
||||
* Individual migration SQL scripts
|
||||
* Auto-generated from EF Core migrations
|
||||
*/
|
||||
export const MIGRATION_SCRIPTS: Record<number, string> = {
|
||||
EOF
|
||||
|
||||
# Process all migration files (excluding the full schema)
|
||||
migration_files=($(ls "$SQL_DIR"/*.sql | grep -v "000_FullSchema.sql" | sort))
|
||||
|
||||
if [ ${#migration_files[@]} -eq 0 ]; then
|
||||
echo "No migration files found in $SQL_DIR"
|
||||
# Add empty object
|
||||
echo "};" >> "$OUTPUT_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Process each migration file
|
||||
for file in "${migration_files[@]}"; do
|
||||
filename=$(basename "$file")
|
||||
migration_number=$(extract_migration_number "$filename")
|
||||
migration_name=$(extract_migration_name "$filename")
|
||||
|
||||
if [ -z "$migration_number" ]; then
|
||||
echo "Warning: Could not extract migration number from $filename, skipping..."
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Processing migration $migration_number: $migration_name"
|
||||
|
||||
# Read and escape SQL content
|
||||
sql_content=$(cat "$file")
|
||||
escaped_sql=$(escape_sql_for_ts "$sql_content")
|
||||
|
||||
# Add to TypeScript file
|
||||
cat >> "$OUTPUT_FILE" << EOF
|
||||
$migration_number: \`$escaped_sql\`,
|
||||
EOF
|
||||
done
|
||||
|
||||
# Close the TypeScript file
|
||||
cat >> "$OUTPUT_FILE" << 'EOF'
|
||||
};
|
||||
EOF
|
||||
|
||||
echo "TypeScript constants file generated: $OUTPUT_FILE"
|
||||
echo "Total migrations processed: ${#migration_files[@]}"
|
||||
|
||||
# Now generate the vault versions file
|
||||
echo ""
|
||||
echo "Generating vault versions mapping..."
|
||||
|
||||
# Start building the vault versions file
|
||||
cat > "$VERSIONS_FILE" << 'EOF'
|
||||
/**
|
||||
* Vault version information
|
||||
* Auto-generated from EF Core migration filenames
|
||||
*/
|
||||
|
||||
import { IVaultVersion } from "../types/VaultVersion";
|
||||
|
||||
/**
|
||||
* Available vault versions in chronological order
|
||||
*/
|
||||
export const VAULT_VERSIONS: IVaultVersion[] = [
|
||||
EOF
|
||||
|
||||
# Remove: declare -A versions_seen
|
||||
last_version=""
|
||||
for file in "${migration_files[@]}"; do
|
||||
filename=$(basename "$file")
|
||||
# Extract revision from filename prefix
|
||||
revision=$(echo "$filename" | sed -n 's/^0*\([0-9]*\)_.*$/\1/p')
|
||||
# Debug: print filename
|
||||
echo "DEBUG: Processing file $filename"
|
||||
last_segment=$(echo "$filename" | awk -F'_to_' '{print $NF}' | sed 's/\.sql$//')
|
||||
echo "DEBUG: Last segment: $last_segment"
|
||||
version_info=$(extract_version_info "$filename")
|
||||
version=$(echo "$version_info" | cut -d'|' -f1)
|
||||
description=$(echo "$version_info" | cut -d'|' -f2)
|
||||
release_date=$(echo "$version_info" | cut -d'|' -f3)
|
||||
|
||||
echo "DEBUG: $filename -> revision='$revision', version='$version', description='$description', release_date='$release_date'"
|
||||
|
||||
# Only output if version is not empty and not a duplicate of the last one
|
||||
if [ -n "$version" ] && [ -n "$description" ] && [ "$version" != "$last_version" ]; then
|
||||
last_version="$version"
|
||||
echo "Found version $version: $description ($release_date)"
|
||||
cat >> "$VERSIONS_FILE" << EOF
|
||||
{
|
||||
revision: $revision,
|
||||
version: '$version',
|
||||
description: '$description',
|
||||
releaseDate: '$release_date'
|
||||
},
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
|
||||
# Close the vault versions file
|
||||
cat >> "$VERSIONS_FILE" << 'EOF'
|
||||
];
|
||||
EOF
|
||||
|
||||
echo "Vault versions file generated: $VERSIONS_FILE"
|
||||
|
||||
# Copy generated files to shared vault-sql directory
|
||||
echo ""
|
||||
echo "Copying generated files to shared vault-sql directory..."
|
||||
|
||||
# Check if shared directory exists
|
||||
if [ ! -d "$SHARED_SQL_DIR" ]; then
|
||||
echo "✗ Shared vault-sql directory not found: $SHARED_SQL_DIR"
|
||||
echo "Make sure you're running this script from the correct location"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy SqlConstants.ts
|
||||
copy_to_shared_sql "$OUTPUT_FILE" "$SHARED_SQL_DIR/SqlConstants.ts"
|
||||
|
||||
# Copy VaultVersions.ts
|
||||
copy_to_shared_sql "$VERSIONS_FILE" "$SHARED_SQL_DIR/VaultVersions.ts"
|
||||
|
||||
echo ""
|
||||
echo "Done!"
|
||||
|
||||
# Clean up temp directory
|
||||
rm -rf "$TEMP_DIR"
|
||||
62
apps/server/Databases/AliasClientDb/Scripts/generate-sql-files.sh
Executable file
62
apps/server/Databases/AliasClientDb/Scripts/generate-sql-files.sh
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Make sure to install the dotnet ef tool first before running this script:
|
||||
# dotnet tool install --global dotnet-ef
|
||||
|
||||
# Configurable settings
|
||||
PROJECT="../AliasClientDb.csproj"
|
||||
STARTUP_PROJECT="../AliasClientDb.csproj" # Adjust if different from main project
|
||||
CONTEXT="AliasClientDbContext"
|
||||
OUTPUT_DIR="MigrationSql"
|
||||
FULL_FILE="$OUTPUT_DIR/000_FullSchema.sql"
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# Use 'set --' to build a list of pending migrations into $1 $2 ...
|
||||
set -- $(dotnet ef migrations list \
|
||||
--project "$PROJECT" \
|
||||
--startup-project "$STARTUP_PROJECT" \
|
||||
--context "$CONTEXT" 2>/dev/null \
|
||||
| grep '(Pending)' \
|
||||
| sed 's/ (Pending)//')
|
||||
|
||||
TOTAL=$#
|
||||
|
||||
# Generate full script from scratch if any pending migrations exist
|
||||
if [ "$TOTAL" -gt 0 ]; then
|
||||
echo "Generating full schema script..."
|
||||
dotnet ef migrations script \
|
||||
--project "$PROJECT" \
|
||||
--startup-project "$STARTUP_PROJECT" \
|
||||
--context "$CONTEXT" \
|
||||
--output "$FULL_FILE"
|
||||
else
|
||||
echo "No pending migrations found. Skipping full script."
|
||||
fi
|
||||
|
||||
# Also generate per-migration scripts if enough exist
|
||||
if [ "$TOTAL" -lt 2 ]; then
|
||||
echo "Not enough pending migrations to generate step-by-step scripts."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Loop over migration pairs
|
||||
i=1
|
||||
while [ "$i" -le "$TOTAL" ]; do
|
||||
j=$((i + 1))
|
||||
FROM=$(eval echo \${$i})
|
||||
TO=$(eval echo \${$j})
|
||||
FILE="$OUTPUT_DIR/$(printf "%03d" "$i")_${FROM}_to_${TO}.sql"
|
||||
|
||||
echo "Generating script: $FROM -> $TO"
|
||||
dotnet ef migrations script "$FROM" "$TO" \
|
||||
--project "$PROJECT" \
|
||||
--startup-project "$STARTUP_PROJECT" \
|
||||
--context "$CONTEXT" \
|
||||
--output "$FILE"
|
||||
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
echo "Done. Scripts written to $OUTPUT_DIR"
|
||||
47
apps/server/Databases/AliasClientDb/Scripts/run-all.sh
Executable file
47
apps/server/Databases/AliasClientDb/Scripts/run-all.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Combined script to generate SQL files and TypeScript constants
|
||||
# This script runs both generate-sql-files.sh and generate-sql-constants.sh
|
||||
|
||||
echo "=== AliasVault SQL Generation Pipeline ==="
|
||||
echo ""
|
||||
|
||||
# Step 1: Generate SQL files from EF Core migrations
|
||||
echo "Step 1: Generating SQL files from EF Core migrations..."
|
||||
if [ -f "generate-sql-files.sh" ]; then
|
||||
bash generate-sql-files.sh
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to generate SQL files"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Error: generate-sql-files.sh not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 2: Convert SQL files to TypeScript constants
|
||||
echo "Step 2: Converting SQL files to TypeScript constants..."
|
||||
if [ -f "convert-sql-to-ts.sh" ]; then
|
||||
bash convert-sql-to-ts.sh
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to generate TypeScript constants"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Error: generate-sql-constants.sh not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Pipeline completed successfully! ==="
|
||||
echo ""
|
||||
echo "Generated files:"
|
||||
echo "- SQL files: MigrationSql/"
|
||||
echo "- TypeScript files: MigrationTs/"
|
||||
echo ""
|
||||
echo "The TypeScript files have been copied to the shared vault-sql directory."
|
||||
echo "Make sure to rebuild the vault-sql library and test it in the client apps."
|
||||
echo ""
|
||||
echo "shared/build-and-distribute.sh"
|
||||
@@ -8,15 +8,68 @@ nav_order: 5
|
||||
|
||||
# Upgrade the AliasClientDb EF model
|
||||
|
||||
To upgrade the AliasClientDb EF model, follow these steps:
|
||||
This guide explains how to upgrade the AliasVault client database structure. The AliasVault client database is built and managed using Entity Framework code-first approach, where all SQL structure is maintained in code and then converted to SQL scripts for use across web apps, browser extensions, and mobile apps.
|
||||
|
||||
1. Make changes to the AliasClientDb EF model in the `AliasClientDb` project.
|
||||
2. Create a new migration by running the following command in the `AliasClientDb` project:
|
||||
## Overview
|
||||
|
||||
The upgrade process involves three main steps:
|
||||
|
||||
1. **Update .NET Entity Framework model** - Modify the EF model and create migrations
|
||||
2. **Generate SQL scripts** - Convert EF migrations to SQL scripts for cross-platform use
|
||||
3. **Rebuild vault-sql shared library** - Compile and distribute the updated SQL scripts
|
||||
|
||||
---
|
||||
|
||||
## 1. Update .NET Entity Framework Model
|
||||
|
||||
### Step 1.1: Modify the EF Model
|
||||
Make changes to the AliasClientDb EF model in the `AliasClientDb` project.
|
||||
|
||||
### Step 1.2: Create a New Migration
|
||||
Run the following command in the `AliasClientDb` project:
|
||||
|
||||
```bash
|
||||
# Important: make sure the migration name is prefixed by the Semver version number of the release.
|
||||
# For example, if the release version is 1.0.0, the migration name should be `1.0.0-<migration-name>`.
|
||||
# Important: Migration name must be prefixed with the Semver version number of the release.
|
||||
# Example: If the release version is 1.0.0, use `1.0.0-<migration-name>`
|
||||
dotnet ef migrations add "1.0.0-<migration-name>"
|
||||
```
|
||||
4. On the next login of a user, they will be prompted (required) to upgrade their database schema to the latest version.
|
||||
Make sure to manually test that the migration works as expected.
|
||||
|
||||
**Note:** Always prefix migration names with the release version number to ensure proper versioning across all client platforms.
|
||||
|
||||
---
|
||||
|
||||
## 2. Generate SQL Scripts
|
||||
|
||||
### Step 2.1: Run the SQL Generation Script
|
||||
Execute the SQL generation script to convert EF migrations to SQL scripts:
|
||||
|
||||
```bash
|
||||
apps/server/Databases/AliasClientDb/Scripts/run-all.sh
|
||||
```
|
||||
|
||||
### Step 2.2: Verify Output
|
||||
The script will:
|
||||
- Create individual SQL scripts for each migration
|
||||
- Convert these to TypeScript versions
|
||||
- Save the results in `shared/vault-sql/src/sql` directory
|
||||
|
||||
---
|
||||
|
||||
## 3. Rebuild vault-sql Shared Library
|
||||
|
||||
### Step 3.1: Compile and Distribute
|
||||
The vault-sql TypeScript library is consumed by web apps, browser extensions, and mobile apps for vault creation and updates. After generating the SQL scripts, rebuild the library:
|
||||
|
||||
```bash
|
||||
shared/build-and-distribute.sh
|
||||
```
|
||||
|
||||
### Step 3.2: Verify Distribution
|
||||
Ensure the updated library is properly distributed to all consuming applications.
|
||||
|
||||
---
|
||||
|
||||
## Testing and Deployment
|
||||
|
||||
### Manual Testing
|
||||
On the next login of any client app, users will be prompted (required) to upgrade their database schema to the latest version. **Always manually test that the migration works as expected** before releasing.
|
||||
|
||||
Reference in New Issue
Block a user