diff --git a/.github/workflows/db-migration-backwards-compatibility.yaml b/.github/workflows/db-migration-backwards-compatibility.yaml index 882755384..d5798ba31 100644 --- a/.github/workflows/db-migration-backwards-compatibility.yaml +++ b/.github/workflows/db-migration-backwards-compatibility.yaml @@ -1,4 +1,4 @@ -name: DB migrations are backwards-compatible with main branch +name: DB migrations are backwards-compatible on: push: @@ -16,6 +16,7 @@ jobs: runs-on: ubuntu-latest outputs: migrations_changed: ${{ steps.check-diff.outputs.migrations_changed }} + base_branch: ${{ steps.check-diff.outputs.base_branch }} steps: - name: Checkout current branch uses: actions/checkout@v6 @@ -25,9 +26,17 @@ jobs: - name: Check for migration changes id: check-diff run: | - # Get the merge base with main - git fetch origin main - MERGE_BASE=$(git merge-base HEAD origin/main) + # Determine base branch: dev compares to main, all others compare to dev + if [ "${{ github.ref }}" = "refs/heads/dev" ]; then + BASE_BRANCH="main" + else + BASE_BRANCH="dev" + fi + echo "base_branch=$BASE_BRANCH" >> $GITHUB_OUTPUT + + # Get the merge base with the base branch + git fetch origin $BASE_BRANCH + MERGE_BASE=$(git merge-base HEAD origin/$BASE_BRANCH) # Check if there are any changes in the migrations folder if git diff --quiet "$MERGE_BASE" HEAD -- apps/backend/prisma/migrations/; then @@ -39,7 +48,7 @@ jobs: fi backwards-compatibility: - name: Test migrations with main branch code + name: Test migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code needs: check-migrations-changed if: needs.check-migrations-changed.outputs.migrations_changed == 'true' runs-on: ubicloud-standard-8 @@ -61,19 +70,19 @@ jobs: mkdir -p saved-migrations cp -r current-branch/apps/backend/prisma/migrations/* saved-migrations/ - # Now checkout main branch - - name: Checkout main branch + # Now checkout base branch (main for dev, dev for all others) + - name: Checkout base branch uses: actions/checkout@v6 with: - ref: main - path: main-branch + ref: ${{ needs.check-migrations-changed.outputs.base_branch }} + path: base-branch - # Move main-branch to the root for the rest of the workflow (keep main's migrations for now) + # Move base-branch to the root for the rest of the workflow (keep base's migrations for now) - name: Setup working directory run: | shopt -s dotglob - mv main-branch/* . - rm -rf main-branch current-branch + mv base-branch/* . + rm -rf base-branch current-branch - name: Setup Node.js uses: actions/setup-node@v6 @@ -187,8 +196,8 @@ jobs: - name: Wait 10 seconds run: sleep 10 - # First test run: main branch with main's migrations - - name: Run tests (main branch with original migrations) + # First test run: base branch with base's migrations + - name: Run tests (base branch with original migrations) run: pnpm test # Now copy over current branch's migrations and run migrate @@ -201,8 +210,8 @@ jobs: - name: Run database migrations run: pnpm run db:migrate - # Second test run: main branch code with new migrations applied - - name: Run tests (main branch with new migrations) + # Second test run: base branch code with new migrations applied + - name: Run tests (base branch with new migrations) run: pnpm test - name: Verify data integrity diff --git a/apps/backend/prisma/migrations/20260201230002_env_to_branch_config/migration.sql b/apps/backend/prisma/migrations/20260201230004_env_to_branch_config/migration.sql similarity index 85% rename from apps/backend/prisma/migrations/20260201230002_env_to_branch_config/migration.sql rename to apps/backend/prisma/migrations/20260201230004_env_to_branch_config/migration.sql index a3ad60aae..c23f13895 100644 --- a/apps/backend/prisma/migrations/20260201230002_env_to_branch_config/migration.sql +++ b/apps/backend/prisma/migrations/20260201230004_env_to_branch_config/migration.sql @@ -178,6 +178,52 @@ END; $$ LANGUAGE plpgsql IMMUTABLE; -- SPLIT_STATEMENT_SENTINEL +-- Create function to flatten nested JSONB into dot-notation +-- e.g., {"auth": {"oauth": {"providers": {"github": {"isShared": true}}}}} +-- becomes {"auth.oauth.providers.github.isShared": true} +-- SPLIT_STATEMENT_SENTINEL +-- SINGLE_STATEMENT_SENTINEL +CREATE OR REPLACE FUNCTION temp_flatten_config(config JSONB, path_prefix TEXT DEFAULT '') +RETURNS JSONB AS $$ +DECLARE + result JSONB := '{}'::jsonb; + key TEXT; + value JSONB; + full_path TEXT; + flattened_child JSONB; + child_key TEXT; + child_value JSONB; +BEGIN + IF config IS NULL THEN + RETURN '{}'::jsonb; + END IF; + + FOR key, value IN SELECT * FROM jsonb_each(config) LOOP + -- Build the full path for this key + IF path_prefix = '' THEN + full_path := key; + ELSE + full_path := path_prefix || '.' || key; + END IF; + + -- If value is an object, recurse and flatten + IF jsonb_typeof(value) = 'object' THEN + flattened_child := temp_flatten_config(value, full_path); + -- Merge all flattened child keys into result + FOR child_key, child_value IN SELECT * FROM jsonb_each(flattened_child) LOOP + result := result || jsonb_build_object(child_key, child_value); + END LOOP; + ELSE + -- Non-object values: add with full dot-notation path + result := result || jsonb_build_object(full_path, value); + END IF; + END LOOP; + + RETURN result; +END; +$$ LANGUAGE plpgsql IMMUTABLE; +-- SPLIT_STATEMENT_SENTINEL + -- Create temporary index to speed up the migration -- SPLIT_STATEMENT_SENTINEL -- SINGLE_STATEMENT_SENTINEL @@ -216,7 +262,7 @@ inserted AS ( ), updated AS ( UPDATE "EnvironmentConfigOverride" eco - SET "config" = temp_filter_env_only_config(eco."config"), + SET "config" = temp_flatten_config(temp_filter_env_only_config(eco."config")), "updatedAt" = CURRENT_TIMESTAMP FROM inserted i WHERE eco."projectId" = i."projectId" @@ -259,7 +305,7 @@ upserted_branch AS ( RETURNING "projectId", "branchId" ) UPDATE "EnvironmentConfigOverride" eco -SET "config" = temp_filter_env_only_config(eco."config"), +SET "config" = temp_flatten_config(temp_filter_env_only_config(eco."config")), "updatedAt" = CURRENT_TIMESTAMP FROM leftover_configs lc WHERE eco."projectId" = lc."projectId" @@ -267,6 +313,7 @@ WHERE eco."projectId" = lc."projectId" -- SPLIT_STATEMENT_SENTINEL -- Clean up temporary functions +DROP FUNCTION IF EXISTS temp_flatten_config(JSONB, TEXT); DROP FUNCTION IF EXISTS temp_filter_env_only_config(JSONB); DROP FUNCTION IF EXISTS temp_filter_branch_config(JSONB); DROP FUNCTION IF EXISTS temp_filter_config(JSONB, BOOLEAN, TEXT);