registry/internal/database/migrations/010_migrate_canonical_package_refs.sql
Radoslav Dimitrov d9099e9117
Fix migration numbering and add migration 010 for package format (#675)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR fixes an issue we introduced on September, 30th that
caused some of the migrations to not apply. This PR fixes it. After
merging we'll have to reset staging as this would not work there (this
is expected).

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->

## Breaking Changes
<!-- Will users need to update their code or configurations? -->

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
2025-10-15 17:00:39 +03:00

204 lines
No EOL
5.7 KiB
PL/PgSQL

-- Migrate package references to canonical format
-- This migration:
-- - Converts OCI packages to use canonical single-line references (registry/namespace/image:tag)
-- - Removes redundant version and registryBaseUrl fields from MCPB packages
-- - Removes package-type-specific forbidden fields (e.g., fileSha256 from non-MCPB packages)
-- - Ensures all packages have required transport field
BEGIN;
-- Helper function to convert OCI package to canonical reference format
CREATE OR REPLACE FUNCTION convert_oci_package_to_canonical(pkg jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
DECLARE
result jsonb;
registry_url text;
registry_host text;
identifier text;
version_str text;
canonical_ref text;
BEGIN
-- Start with the original package
result := pkg;
-- Only process OCI packages
IF pkg->>'registryType' != 'oci' THEN
RETURN result;
END IF;
-- Get current values
registry_url := pkg->>'registryBaseUrl';
identifier := pkg->>'identifier';
version_str := pkg->>'version';
-- Skip if already in canonical format (no registryBaseUrl and no separate version)
-- Canonical format has registry in identifier like "ghcr.io/owner/repo:tag"
IF registry_url IS NULL AND (identifier LIKE '%:%' OR identifier LIKE '%@sha256:%') THEN
RETURN result;
END IF;
-- Extract registry host from registryBaseUrl
IF registry_url IS NOT NULL THEN
registry_host := regexp_replace(registry_url, '^https?://', '');
ELSE
-- Default to docker.io if no registry specified
registry_host := 'docker.io';
END IF;
-- Build canonical reference
-- Format: registry/namespace/image:tag
IF version_str IS NOT NULL AND version_str != '' THEN
canonical_ref := registry_host || '/' || identifier || ':' || version_str;
ELSE
-- Default to 'latest' tag if no version specified
canonical_ref := registry_host || '/' || identifier || ':latest';
END IF;
-- Update identifier to canonical reference
result := jsonb_set(result, '{identifier}', to_jsonb(canonical_ref));
-- Remove registryBaseUrl field (no longer needed for OCI)
result := result - 'registryBaseUrl';
-- Remove version field (now part of identifier for OCI)
result := result - 'version';
RETURN result;
END;
$$;
-- Helper function to convert MCPB package to canonical reference format
CREATE OR REPLACE FUNCTION convert_mcpb_package_to_canonical(pkg jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
DECLARE
result jsonb;
BEGIN
-- Start with the original package
result := pkg;
-- Only process MCPB packages
IF pkg->>'registryType' != 'mcpb' THEN
RETURN result;
END IF;
-- Remove version field if it exists (version is embedded in the download URL)
result := result - 'version';
-- Remove registryBaseUrl field if it exists (not needed for MCPB)
result := result - 'registryBaseUrl';
RETURN result;
END;
$$;
-- Helper function to remove package-type-specific forbidden fields
CREATE OR REPLACE FUNCTION remove_forbidden_fields(pkg jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
DECLARE
result jsonb;
registry_type text;
BEGIN
result := pkg;
registry_type := pkg->>'registryType';
-- Remove fileSha256 from non-MCPB packages (it's MCPB-only)
IF registry_type != 'mcpb' AND result ? 'fileSha256' THEN
result := result - 'fileSha256';
END IF;
RETURN result;
END;
$$;
-- Helper function to ensure transport field exists
CREATE OR REPLACE FUNCTION ensure_transport_field(pkg jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
DECLARE
result jsonb;
registry_type text;
runtime_hint text;
BEGIN
result := pkg;
-- If transport already exists, return as-is
IF result ? 'transport' THEN
RETURN result;
END IF;
-- Get registry type and runtime hint
registry_type := pkg->>'registryType';
runtime_hint := pkg->>'runtimeHint';
-- Add default transport based on package type
-- Most packages use stdio transport
result := jsonb_set(result, '{transport}', '{"type": "stdio"}'::jsonb);
RETURN result;
END;
$$;
-- Helper function to convert all packages in a packages array
CREATE OR REPLACE FUNCTION convert_packages_array(packages jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
DECLARE
result jsonb := '[]'::jsonb;
pkg jsonb;
BEGIN
-- Handle null or empty arrays
IF packages IS NULL OR jsonb_array_length(packages) = 0 THEN
RETURN packages;
END IF;
-- Process each package
FOR pkg IN SELECT * FROM jsonb_array_elements(packages)
LOOP
-- First convert OCI packages to canonical format
pkg := convert_oci_package_to_canonical(pkg);
-- Then convert MCPB packages to canonical format
pkg := convert_mcpb_package_to_canonical(pkg);
-- Remove package-type-specific forbidden fields
pkg := remove_forbidden_fields(pkg);
-- Finally ensure transport field exists
pkg := ensure_transport_field(pkg);
-- Add to result array
result := result || jsonb_build_array(pkg);
END LOOP;
RETURN result;
END;
$$;
-- Migrate all server packages to canonical format
-- After migration 003, all data is stored in the 'value' JSONB column
UPDATE servers
SET value = jsonb_set(
value,
'{packages}',
convert_packages_array(value->'packages')
)
WHERE value ? 'packages'
AND value->'packages' IS NOT NULL
AND jsonb_array_length(value->'packages') > 0;
-- Clean up helper functions
DROP FUNCTION IF EXISTS convert_oci_package_to_canonical(jsonb);
DROP FUNCTION IF EXISTS convert_mcpb_package_to_canonical(jsonb);
DROP FUNCTION IF EXISTS remove_forbidden_fields(jsonb);
DROP FUNCTION IF EXISTS ensure_transport_field(jsonb);
DROP FUNCTION IF EXISTS convert_packages_array(jsonb);
COMMIT;