enonic-content-migration
Enonic Content Migration
Procedures
Step 1: Identify the Enonic XP project and operation scope
- Inspect the workspace for Enonic XP project markers:
build.gradlewithcom.enonic.xpdependencies,src/main/resources/directory structure, orgradle.propertieswithxpVersion. - Execute
node scripts/find-enonic-targets.mjs .to scan for Enonic XP project markers and existing content operation files when a Node runtime is available. - If no Enonic XP project is detected, stop and explain that this skill targets Enonic XP content operations only.
- Determine the target XP version from
gradle.propertiesorbuild.gradleto select the correct API surface. - Classify the requested operation:
- Bulk create: Importing content from external sources (JSON, CSV, APIs).
- Bulk update: Modifying existing content matching query criteria.
- Bulk delete: Removing content matching query criteria.
- Migration: Moving or transforming content between paths, sites, or environments.
- Query/Aggregation: Retrieving and analyzing content with NoQL and aggregations.
- Long-running task: Any operation processing more than ~100 items that needs progress reporting.
Step 2: Select the API layer and context
- Read
references/migration-reference.mdfor query DSL patterns, batch processing strategies, and branch handling rules. - Choose
lib-contentwhen the operation works with CMS content and needs publish/unpublish, content-type validation, and the content domain abstraction. - Choose
lib-nodewhen the operation needs low-level node manipulation, custom repositories, or bypasses content-type validation for raw data migration. - Determine the required context:
- Use
lib-contextto run operations indraftbranch for modifications andmasterbranch for reading published content. - Use
lib-contextwithrole:system.adminprincipal when the operation requires elevated permissions.
- Use
- If the operation processes more than ~100 items, wrap it in a task controller using
lib-task. Readreferences/migration-reference.mdsection on task controllers for the pattern.
Step 3: Build the query
- Construct the NoQL query string or DSL expression to match the target content.
- For content-type filtering, use the
contentTypesparameter oncontentLib.query()or atypeproperty comparison in node queries. - For date-range queries, use
instant()ordateTime()functions and therange()query function. - For full-text search, use
fulltext()with the appropriate field paths and operator (AND/OR). - For path-scoped operations, use
_path LIKE '/content/site-path/*'to match descendants. Note: the_pathproperty in NoQL queries includes the internal/content/prefix, buthit._pathin results returns the content-domain path without it. Always prepend/content/when building queries from result paths. - Add
filtersfor efficient post-query narrowing usingexists,notExists,hasValue, orbooleancombinations. - Add
aggregationswhen the operation needs grouped statistics (term counts, date histograms, numeric ranges, stats). - Read
references/examples.mdfor complete query and aggregation patterns.
Step 4: Implement batch processing
- Use paginated retrieval with
startandcountparameters to avoid loading all results into memory. - Set
countto a batch size between 50 and 200 depending on the complexity of per-item processing. - Loop until
result.hits.length === 0orstart >= result.total, incrementingstartby the batch size each iteration. - For
contentLib.create()in bulk, setrefresh: falseto avoid per-item index refresh; call a manual refresh after the batch completes. - For
contentLib.modify(), use the editor callback pattern to safely transform each content item. - For
contentLib.publish(), batch keys into groups of 50–100 to avoid timeout on large publish sets. - Track success and failure counts for reporting.
- Read
assets/bulk-update.template.tsfor the reusable batch update controller template. Note: templates use TypeScript/ESM syntax (import,const, arrow functions); adapt to CommonJS JavaScript (require(),var,function()) for XP runtime deployment as.jsfiles.
Step 5: Handle branch operations and publishing
- Run content modifications in the
draftbranch context. - After modifications complete, publish changed items to
masterusingcontentLib.publish(). On XP < 7.12, passsourceBranch: 'draft'andtargetBranch: 'master'. On XP 7.12+, these parameters are ignored (publish always goes draft→master). - Set
includeDependencies: falsewhen publishing bulk-updated items to avoid unintended dependency publishing. - For operations comparing draft and master state, use
repo.diff()fromlib-nodewithtarget: 'master'andincludeChildren: true.
Step 6: Wrap long-running operations in a task controller
- Use
taskLib.executeFunction()for inline task functions ortaskLib.submitTask()for named task descriptors. - Report progress using
taskLib.progress({ info, current, total })at regular intervals during batch processing. - Read
assets/task-migration.template.tsfor the reusable task controller template with progress reporting. - Check for existing running instances with
taskLib.isRunning()before starting a duplicate operation. - Use
taskLib.sleep()for throttling between batches if the operation generates excessive load. - For named tasks on XP 7.13+, the
runfunction receivestaskIdas its second argument:exports.run = function(params, taskId) { ... }.
Step 7: Validate and report results
- After the operation completes, log a summary: items processed, items created/updated/deleted, items failed, total duration.
- For migration operations, verify target content exists by querying the destination path.
- For publish operations, verify published state by querying the
masterbranch. - If errors occurred, collect error details (content path, error message) into a structured report.
Error Handling
- If
scripts/find-enonic-targets.mjsfinds no Enonic XP project, explain that the workspace does not contain an Enonic XP application. - If a content operation fails with
contentAlreadyExists, check whether the target exists and decide whether to update or skip. Readreferences/troubleshooting.mdfor duplicate handling patterns. - If a query returns zero results, verify the query syntax, branch context, and property paths. NoQL string-format mismatches (e.g. missing
instant()wrapper for date comparisons) are a common cause. - If a task fails or times out, check
taskLib.get(taskId)for state and progress details. Readreferences/troubleshooting.mdfor timeout and permission issues. - If an
AccessDeniedExceptionoccurs on publish or modify, ensure the context includesrole:system.adminprincipals vialib-context.
More from webmaxru/enonic-agent-skills
enonic-webhook-integrator
Sets up Enonic XP event listeners, webhook configurations, and external system integrations triggered by content lifecycle events. Covers lib-event listener registration, node event filtering, outbound webhook configuration via com.enonic.xp.webhooks.cfg, custom HTTP service controllers for inbound webhooks, task-based async processing with lib-task, and outbound HTTP calls with lib-httpClient. Use when listening for content publish/create/update/delete events, configuring outbound webhooks, building HTTP service endpoints for inbound webhooks, or triggering async processing on content changes. Do not use for content querying, frontend component development, non-Enonic event systems, or GitHub webhook configuration.
91skill-creator
Authors and structures professional-grade agent skills following the agentskills.io spec. Use when creating new skill directories, drafting procedural instructions, or optimizing metadata for discoverability. Don't use for general documentation, non-agentic library code, or README files.
89enonic-api-reference
Enonic XP server-side JavaScript/TypeScript API reference for all /lib/xp/* libraries. Provides function signatures, parameters, return types, and usage examples for lib-content, lib-node, lib-auth, lib-portal, lib-context, lib-event, lib-task, lib-repo, lib-io, lib-mail, and lib-schema. Use when looking up Enonic XP library functions, parameter shapes, return types, or usage examples. Do not use for Guillotine GraphQL queries, content type schema definitions, Enonic CLI commands, or non-Enonic JavaScript APIs.
89agent-skill-deploy
>
89enonic-sandbox-manager
Guides developers through Enonic CLI commands for sandbox management, project scaffolding, local development, app deployment, and CI/CD pipeline generation. Use when creating Enonic XP sandboxes, starting or stopping local instances, scaffolding projects from starters, running dev mode with hot-reload, deploying apps, or generating CI/CD workflows for Enonic apps. Don't use for writing XP application code (controllers, content types), querying via Guillotine or lib-content APIs, configuring non-Enonic environments, or Docker/Kubernetes deployment of XP.
89enonic-guillotine-query-builder
Composes, debugs, and optimizes Guillotine GraphQL queries for Enonic XP headless content delivery. Covers query construction, variable usage, filtering, aggregation, pagination, sorting, and TypeScript type generation from the auto-generated Guillotine schema. Use when writing or troubleshooting Guillotine queries, querying custom content types through GraphQL, or generating typed interfaces from Guillotine responses. Don't use for content type XML definitions, non-Enonic GraphQL APIs (Apollo, Hasura), server-side lib-content queries, or Guillotine deployment and CORS configuration.
89