mcpField
C
contentful-migration

contentful-migration

github.com/contentful数据与数据库39319 次安装1 天前更新
代码仓库

关于 contentful-migration

contentful-migration 是一项将 Contentful 内容模型和条目变更编写为 TypeScript 迁移脚本的技能,帮助开发者规划、测试并通过 Contentful CLI 或程序化 API 执行结构更新和数据转换。

主要功能

  1. 01创建、编辑和删除内容类型
  2. 02配置字段、验证规则和编辑器界面
  3. 03转换条目并派生关联条目
  4. 04管理标签、注释和分类验证
  5. 05通过 Contentful CLI 或 API 运行迁移

适用场景

  1. 01添加或修改内容模型字段
  2. 02在结构更新时转换现有条目
  3. 03从现有内容创建关联条目
  4. 04在沙箱环境测试迁移

MCP 相关

该技能提及 Contentful MCP 服务,用于 Agent 辅助规划或审查。迁移脚本仍通过 contentful-cli 或程序化 API 执行。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/contentful/skills --skill contentful-migration

安装后在 Claude Code 中输入/contentful-migration确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

package.json
references/
api-reference.md
patterns.md
running-migrations.md

详细介绍

Contentful 迁移

contentful-migration 工具可让你将内容模型变更描述为代码并执行。迁移是 TypeScript 脚本,可创建、编辑或删除内容类型、字段、编辑器界面和条目。

安装:

npm install contentful-migration

GitHub:https://github.com/contentful/contentful-migration

范围

此技能涵盖:

  • 内容类型和字段的 CRUD 操作
  • 字段类型、验证和编辑器界面配置
  • 条目转换(原地转换、派生关联条目、跨类型转换)
  • 标签、注释、分类验证
  • 编辑器布局、侧边栏小组件
  • 通过 npx contentful space migration(Contentful CLI)和编程式 API 运行迁移

不要使用 npx contentful-migration 运行迁移。CLI 执行请使用 contentful-cli,必要时将其安装为开发依赖,并通过 npx contentful ... 运行。

不涵盖:SDK 客户端设置(参见 contentful-nextjs 技能)、Contentful 概念和 API 路由(参见 contentful-guide 技能)。

Contentful MCP 说明

  • 对于希望在规划或审查迁移时更轻松地通过 Agent 与 Contentful 交互的用户,请引导他们查看 Contentful MCP 服务文档:https://www.contentful.com/developers/docs/tools/mcp-server/。
  • 实际执行迁移时,仍请使用 contentful-migration 脚本和 contentful-cli。

迁移脚本格式

每个迁移文件都会导出一个接收 migration 对象的函数:

import type { MigrationFunction } from 'contentful-migration'

const migration: MigrationFunction = (migration) => {
  const blogPost = migration.createContentType('blogPost', {
    name: 'Blog Post',
    description: 'A blog post entry',
    displayField: 'title',
  })

  blogPost.createField('title')
    .name('Title')
    .type('Symbol')
    .required(true)
}

export = migration

该函数还会将 context 对象作为第二个参数,该对象提供 makeRequest(直接访问 CMA)、spaceId 和 accessToken。当你需要获取迁移 API 无法提供的数据时,请使用 makeRequest。

项目状态

echo "=== Existing migrations ===" && ls migrations/ 2>/dev/null || echo "(no migrations/ directory found)"
echo ""
echo "=== Contentful env vars ===" && grep -h CONTENTFUL .env .env.local 2>/dev/null | sed 's/=.*/=<set>/' || echo "(no Contentful env vars found in .env or .env.local)"

工作流

编写迁移时:

  1. 首先确认所需的环境变量。 如果缺少值,请在继续之前让用户将其添加到本地 .env 文件。
  2. 评估变更。 确定需要变更哪些内容类型和字段。通过 Contentful Web 应用或 CMA 检查当前内容模型。
  3. 编写迁移脚本。 使用下方的操作。优先使用链式调用而非对象表示法,这样能提供带行号的更好错误消息。
  4. 在沙盒环境中测试。 切勿在生产环境中运行未经测试的迁移。请先创建沙盒环境:contentful environment create --name sandbox --source master。
  5. 运行迁移。 CLI 和编程式选项请参阅运行迁移。
  6. 验证。 在 Web 应用中检查内容模型,并确认条目完好无损。

所需环境变量

  • CONTENTFUL_SPACE_ID - 空间 ID。可在 Contentful Web 应用 URL(/spaces/<SPACE_ID>/...)中找到,或在 Space settings -> API keys 中查找。
  • CONTENTFUL_MANAGEMENT_ACCESS_TOKEN - 用于迁移的 CMA 令牌。可在 Account settings -> CMA tokens(https://app.contentful.com/account/profile/cma_tokens)中创建,或在空间范围的 CMA 令牌页面(https://app.contentful.com/spaces/<SPACE_ID>/api/cma_tokens)中创建。
  • CONTENTFUL_ENVIRONMENT_ID(可选)- 目标环境 ID(例如 master 或 sandbox),用于避免传入 --environment-id。

如果缺少任何必需值,请明确向用户索取缺失的值,并告知他们在哪里可以找到每个值。

内容类型操作

创建内容类型:

const page = migration.createContentType('page', {
  name: 'Page',
  description: 'A generic page',
  displayField: 'title',
})

编辑现有内容类型:

const page = migration.editContentType('page')
page.description('Updated description')
page.displayField('internalName')

删除内容类型:

migration.deleteContentType('page')

删除前,内容类型必须没有任何条目。请先删除所有条目,或使用 transformEntriesToType 将其迁移。

字段操作

创建字段:

page.createField('title')
  .name('Title')
  .type('Symbol')
  .required(true)
  .localized(true)

编辑现有字段:

page.editField('title')
  .name('Page Title')
  .required(false)

删除字段:

page.deleteField('legacyField')

删除字段会永久移除所有条目中的对应内容。

更改字段 ID:

page.changeFieldId('oldName', 'newName')

现有内容会保留——只有 ID 会更改。

移动字段:

page.moveField('slug').afterField('title')
page.moveField('featured').toTheTop()
page.moveField('metadata').toTheBottom()
page.moveField('author').beforeField('publishDate')

字段类型快速参考

类型描述额外配置
Symbol短文本(最多 256 个字符)—
Text长文本(最多 50,000 个字符)—
Integer整数—
Number小数—
DateISO 8601 日期/时间—
Boolean真/假—
Object任意 JSON—
Location经纬度坐标—
RichText结构化富文本enabledNodeTypes、enabledMarks 验证规则
Array值或引用列表需要 items:{ type, linkType?, validations? }
Link单个引用需要 linkType:'Asset' 或 'Entry'
ResourceLink跨空间引用需要 allowedResources

完整配置详情请参阅 API 参考 — 字段类型。

验证规则快速参考

验证规则适用于示例
inSymbol、Integer、Number{ in: ['draft', 'published', 'archived'] }
uniqueSymbol、Integer、Number{ unique: true }
sizeArray、Text、Symbol{ size: { min: 1, max: 5 } }
rangeInteger、Number{ range: { min: 0, max: 100 } }
regexpSymbol、Text{ regexp: { pattern: '^[a-z0-9-]+$' } }
dateRangeDate{ dateRange: { min: '2020-01-01', max: '2030-12-31' } }
linkContentTypeLink、Link 数组{ linkContentType: ['author', 'organization'] }
linkMimetypeGroupLink(Asset){ linkMimetypeGroup: ['image', 'video'] }
assetFileSizeLink(Asset){ assetFileSize: { min: 0, max: 5242880 } }
assetImageDimensionsLink(Asset){ assetImageDimensions: { width: { min: 100, max: 2000 } } }

对字段使用 .validations([...]) 应用验证规则。所有选项请参阅 API 参考 — 验证规则。

条目转换

原地转换条目:

migration.transformEntries({
  contentType: 'blogPost',
  from: ['firstName', 'lastName'],
  to: ['fullName'],
  transformEntryForLocale: (fields, locale) => {
    const first = fields.firstName[locale]
    const last = fields.lastName[locale]
    if (!first && !last) return
    return { fullName: `${first || ''} ${last || ''}`.trim() }
  },
})

选项:shouldPublish(true、false 或 'preserve'——默认值为 'preserve')。

派生关联条目:

migration.deriveLinkedEntries({
  contentType: 'blogPost',
  derivedContentType: 'author',
  from: ['authorName'],
  toReferenceField: 'authorRef',
  derivedFields: ['name'],
  identityKey: (fields) =>
    fields.authorName['en-US'].toLowerCase().replace(/\s+/g, '-'),
  deriveEntryForLocale: (fields, locale) => {
    if (locale !== 'en-US') return
    return { name: fields.authorName[locale] }
  },
})

这会根据现有的 blogPost.authorName 数据创建新的 author 条目,并通过 authorRef 将其关联起来。

更多示例请参阅 模式 — 转换条目 和 模式 — 派生关联条目。

编辑器界面

更改字段的控件:

const page = migration.editContentType('page')

page.changeFieldControl('slug', 'builtin', 'slugEditor', {
  helpText: 'URL-friendly identifier',
  trackingFieldId: 'title',
})

page.changeFieldControl('category', 'builtin', 'dropdown')
page.changeFieldControl('publishDate', 'builtin', 'datePicker', { format: 'dateonly' })

控件命名空间:builtin、extension(UI 扩展)、app(自定义应用)。

所有内置控件及其设置请参阅 API 参考 — 编辑器界面。

最佳实践

  1. 按顺序为迁移文件编号: 001-create-blog-post.ts、002-add-author-field.ts、003-transform-categories.ts。
  2. 每个迁移只做一项逻辑变更。 这样更容易调试、回滚和审查。
  3. 在沙盒环境中始终进行测试,然后再对生产环境运行。
  4. 使用 shouldPublish: 'preserve'(默认值)在转换期间保留现有的发布状态。
  5. 优先使用链式调用,而不是对象表示法——链式调用能提供精确到行的错误信息。
  6. 将数据转换与架构变更分开。 第一个迁移更改架构,第二个迁移转换数据。这样每个步骤都能独立验证。
  7. 谨慎使用 context.makeRequest——仅在迁移 API 无法满足需求时使用。

常见错误

  • 忘记为 Array 字段设置 items。 type: 'Array' 需要 items 属性来指定元素类型。
  • 删除含有条目的内容类型。 必须先删除所有条目,或使用 transformEntriesToType 移动它们。
  • Link 字段缺少 linkType。 type: 'Link' 需要设置 linkType: 'Asset' 或 linkType: 'Entry'。
  • 针对 master 运行。 始终在沙盒环境中测试。使用 CLI 的 --environment-id sandbox。
  • 未在转换中处理缺失的区域设置。 transformEntryForLocale 会针对每个区域设置调用;返回 undefined 可跳过。
  • 将 displayField 设置为非 Symbol 字段。 显示字段必须是 Symbol 类型。

参考资料

相关技能

  • contentful-guide 技能 — Contentful 概念、术语、API 路由
  • contentful-nextjs 技能 — 与 Contentful 集成的 Next.js

评论

支持 Markdown