From 9dc20b03e5c3582e882c4eb5b91b844463cba7a4 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 5 Aug 2026 12:08:11 +0800 Subject: [PATCH] Fix collection schema import compatibility Normalize portable field settings during export and import, serialize collection category association updates as arrays, and ignore generated exports and macOS metadata. --- .gitignore | 1 + script/export-collection-schema.sh | 5 +++- script/import-collection-schema.sh | 48 +++++++----------------------- 3 files changed, 16 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index e43b0f9..96e5db2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +exports/ diff --git a/script/export-collection-schema.sh b/script/export-collection-schema.sh index 3910669..804ef53 100755 --- a/script/export-collection-schema.sh +++ b/script/export-collection-schema.sh @@ -147,7 +147,10 @@ read_collection() { portable_schema() { jq ' .data as $collection - | ($collection.fields // []) as $fields + | (($collection.fields // []) | map( + . as $field + | (($field | del(.settings)) + ($field.settings // {})) + )) as $fields | [$fields[] | select(.interface == "m2o" or .interface == "o2m" or .interface == "m2m" or .interface == "o2o") | .foreignKey diff --git a/script/import-collection-schema.sh b/script/import-collection-schema.sh index 5c48e89..fa98e6b 100755 --- a/script/import-collection-schema.sh +++ b/script/import-collection-schema.sh @@ -321,24 +321,7 @@ make_collection_payload() { jq --argjson index "$PLAN_INDEX" --argjson relations "$RELATION_INTERFACES" ' def compact_field: . as $field - | ({name:$field.name, interface:$field.interface} - + (if $field | has("title") then {title:$field.title} else {} end) - + (if $field | has("description") then {description:$field.description} else {} end) - + (if $field | has("target") then {target:$field.target} else {} end) - + (if $field | has("validation") then {validation:$field.validation} else {} end) - + (if $field | has("validators") then {validators:$field.validators} else {} end) - + (if $field | has("defaultValue") then {defaultValue:$field.defaultValue} else {} end) - + (if $field | has("enum") then {enum:$field.enum} else {} end) - + (if $field | has("reverseName") then {reverseName:$field.reverseName} else {} end) - + (if $field | has("reverseTitle") then {reverseTitle:$field.reverseTitle} else {} end) - + (if $field | has("reverseInterface") then {reverseInterface:$field.reverseInterface} else {} end) - + (if $field | has("reverseField") then {reverseField:$field.reverseField} else {} end)) - + (((($field.settings // {}) + ($field | del( - .name, .title, .interface, .description, .target, .validation, .validators, - .defaultValue, .enum, .reverseName, .reverseTitle, .reverseInterface, - .reverseField, .settings - ))) as $settings - | if $settings == {} then {} else {settings:$settings} end)); + | (($field | del(.settings, .collectionName)) + ($field.settings // {})); .[$index].schema as $schema | ({name:$schema.name, title:$schema.title, template:$schema.template, verify:true, replaceFields:false} + (if $schema | has("description") then {description:$schema.description} else {} end) @@ -363,24 +346,8 @@ make_relation_payload() { jq --argjson planIndex "$PLAN_INDEX" --argjson fieldIndex "$FIELD_INDEX" ' .[$planIndex] as $item | $item.schema.fields[$fieldIndex] as $field - | ({collectionName:$item.collection, name:$field.name, interface:$field.interface} - + (if $field | has("title") then {title:$field.title} else {} end) - + (if $field | has("description") then {description:$field.description} else {} end) - + (if $field | has("target") then {target:$field.target} else {} end) - + (if $field | has("validation") then {validation:$field.validation} else {} end) - + (if $field | has("validators") then {validators:$field.validators} else {} end) - + (if $field | has("defaultValue") then {defaultValue:$field.defaultValue} else {} end) - + (if $field | has("enum") then {enum:$field.enum} else {} end) - + (if $field | has("reverseName") then {reverseName:$field.reverseName} else {} end) - + (if $field | has("reverseTitle") then {reverseTitle:$field.reverseTitle} else {} end) - + (if $field | has("reverseInterface") then {reverseInterface:$field.reverseInterface} else {} end) - + (if $field | has("reverseField") then {reverseField:$field.reverseField} else {} end)) - + (((($field.settings // {}) + ($field | del( - .collectionName, .name, .title, .interface, .description, .target, - .validation, .validators, .defaultValue, .enum, .reverseName, - .reverseTitle, .reverseInterface, .reverseField, .settings - ))) as $settings - | if $settings == {} then {} else {settings:$settings} end)) + | (($field | del(.settings, .collectionName)) + ($field.settings // {})) + + {collectionName:$item.collection} ' "$PLAN_FILE" } @@ -456,11 +423,15 @@ while [ "$CATEGORY_INDEX" -lt "$TARGET_CATEGORY_COUNT" ]; do if [ "$CURRENT_MEMBERS" != "$DESIRED_MEMBERS" ]; then CATEGORY_VALUES=$(printf '%s' "$DESIRED_MEMBERS" | jq -c '{collections:map({name:.})}') echo " update category $CATEGORY_NAME" >&2 + # Repeat the single association flag so nb CLI serializes it as an array. + # A single query value is parsed by NocoBase as a string and causes + # UpdateGuard to fail with "list.filter is not a function". run_nb nb api resource update \ --resource collectionCategories \ --filter-by-tk "$CATEGORY_ID" \ --values "$CATEGORY_VALUES" \ --update-association-values collections \ + --update-association-values collections \ -e "$ENV_NAME" -y -j >/dev/null fi CATEGORY_INDEX=$((CATEGORY_INDEX + 1)) @@ -469,7 +440,10 @@ done portable_schema() { jq ' .data as $collection - | ($collection.fields // []) as $fields + | (($collection.fields // []) | map( + . as $field + | (($field | del(.settings)) + ($field.settings // {})) + )) as $fields | [$fields[] | select(.interface == "m2o" or .interface == "o2m" or .interface == "m2m" or .interface == "o2o") | .foreignKey