AECU simplifies content migrations by executing migration scripts during package installation. It is built on top of Groovy Console.
Features:
The tool was presented at adaptTo() conference in Berlin. You can get the slides there and also see the video here:
Table of contents
AECU requires Java 8 and AEM 6.5 or AEM Cloud. For older AEM versions see below.
AEM Version | Groovy Console | AECU |
---|---|---|
6.5 (>=6.5.13) Cloud |
included | 6.x, 5.x |
6.5 (>=6.5.3 && < 6.5.13) | included | 6.0.1, 5.x |
For AEM 6.3/6.4 please see here what versions are compatible. Groovy Console can be installed manually if bundle install is not used.
AEM Version | Groovy Console | AECU |
---|---|---|
6.5 (>=6.5.3) | 16.x 14.x, 13.x |
4.x 3.x, 2.x |
6.4 | 14.x, 13.x | 3.x, 2.x |
6.3 | 12.x | 1.x |
For AEM 6.5 and Java 11 make sure to edit the sling.properties file and add org.osgi.framework.bootdelegation=sun.*,com.sun.*,jdk.internal.reflect,jdk.internal.reflect.*
to avoid a NoClassDefFoundError, see FELIX-6184 & Adobe Documantation for details.
AECU includes the Groovy Console package. Please do not install Groovy Console manually. The API is not stable and using the included version makes sure AECU and Groovy Console are compatible.
You can download the package from Maven Central or our releases section. The aecu.complete package will install the AECU software and Groovy Console.
<dependency>
<groupId>de.valtech.aecu</groupId>
<artifactId>aecu.complete</artifactId>
<version>LATEST</version>
<type>zip</type>
</dependency>
You can download the package from Maven Central or our releases section. The aecu.complete package will install the AECU software and Groovy Console.
<dependency>
<groupId>de.valtech.aecu</groupId>
<artifactId>aecu.complete.cloud</artifactId>
<version>LATEST</version>
<type>zip</type>
</dependency>
You can download the package from Maven Central or our releases section. The aecu.ui.apps package will install the AECU software. It requires that you installed Groovy Console before.
<dependency>
<groupId>de.valtech.aecu</groupId>
<artifactId>aecu.ui.apps</artifactId>
<version>LATEST</version>
<type>zip</type>
</dependency>
To simplify installation we provide a bundle package that already includes the Groovy Console. This makes sure there are no compatibility issues. The package is also available on Maven Central or our releases section.
<dependency>
<groupId>de.valtech.aecu</groupId>
<artifactId>aecu.bundle</artifactId>
<version>LATEST</version>
<type>zip</type>
</dependency>
The application can be removed by deleting the following paths:
/apps/valtech/aecu
/var/groovyconsole/scripts/aecu
/conf/groovyconsole/scripts/aecu
/var/aecu
/var/aecu-installhook
Afterwards, you can delete the "aecu.*" packages in package manager.
For Groovy Console delete:
/apps/groovyconsole
/etc/clientlibs/groovyconsole
/var/groovyconsole
Then delete "aem-groovy-console" packages in package manager.
The index was moved from /var/aecu to /oak:index for cloud compatibility reasons, please remove the /var/aecu/oak:index/aecuHistory to avoid duplicate index definitions
All migration scripts need to be located in:
/apps/aecu-scripts
(AEM Cloud automatic execution with startup hook, since 6.0.0)/conf/groovyconsole/scripts/aecu
(AEM onprem manual and install hook execution, AEM Cloud manual execution)/var/groovyconsole/scripts/aecu
(deprecated)AEM as a Cloud Service requires the scripts to be executed automatically in /apps to avoid issues with the startup hook. Manual scripts can still be located in /conf.
In this folder you can create an unlimited number of folders and files. E.g. organize your files by project or deployment. The content of the scripts is plain Groovy code that can be run via Groovy Console.
If your package containing the scripts is bundled in another package please make sure that this is done using "subPackages" in pom.xml.
There are just a few naming conventions:
This is the preferred method to execute your scripts on AEM Cloud installations. It allows to run them without any user interaction. Just package them with a content package and do a regular deployment.
The startup hook automatically runs on AEM Cloud (detecting the composite node store). It executes all scripts in /apps/aecu-scripts when the server is started during deploy. Please note that changes to /apps or /libs are not possible as the startup hook does not run during build phase of the images.
Please note that scripts will not be executed on a local development instance of AEM Cloud (no composite node store). Here you can activate the execution via install hook. See pom.xml of examples-cloud package how to activate the install hook via profile. Install hooks must not be activated for AEM Cloud environments hosted by Adobe.
As multiple AEM instances share the same repository scripts might be executed multiple times during the same deployment. AECU checks if there is an ongoing migration that started less than 30 min ago to avoid duplicate executions. But there can be edge cases where scripts are run by multiple instances. Please make sure your scripts can handle this. Scripts with "always" selector might be executed at random times as cloud instances can be added/removed dynamically based on load.
This is the preferred method to execute your scripts on non-AEM-Cloud installations. It allows to run them without any user interaction. Just package them with a content package and do a regular deployment.
You can add the install hook by adding de.valtech.aecu.core.installhook.AecuInstallHook as a hook to your package properties. The AECU package needs to be installed beforehand.
<plugin>
<groupId>com.day.jcr.vault</groupId>
<artifactId>content-package-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<filterSource>src/main/content/META-INF/vault/filter.xml</filterSource>
<verbose>true</verbose>
<failOnError>true</failOnError>
<group>Valtech</group>
<properties>
<installhook.aecu.class>de.valtech.aecu.core.installhook.AecuInstallHook</installhook.aecu.class>
</properties>
</configuration>
</plugin>
The Groovy scripts must be covered by the filter, i.e. must be imported into the repository during installation.
By default each Groovy script is only executed once (if it does not end with suffix .always.groovy
). It will be re-executed though in case any of the Groovy scripts have been modified (through the package installation) or in case the previous execution was not successful.
Manual script execution is useful in case you want to manually rerun a script (e.g. because it failed before). You can find the execute feature in AECU's tools menu.
Execution is done in two simple steps:
Once execution is done you will see if the script(s) succeeded. Click on the history link to see the details.
When executing, de.valtech.aecu.core.service.AecuServiceImpl
will emit a log statement to inform script is currently being executed, and when execution is done, a second log statement including the result (success, failure, skipped if prechecks failed) is emitted.
To capture these logs, configure a logger on de.valtech.aecu
with level INFO
to append/emit logs as you desire.
These logs are helpful if you are executing scripts via hook.
Additionally, as stated above, manual execution provides the UI to indicate which script is currently running. Please note however that if you leave the page while execution is in progress, you will not be able to go back to it to see the execution details again.
In both cases, you can see more details on the execution in the history. See below for more information.
You can find the history in AECU's tools menu.
The history shows all runs that were executed via package install hook, manual run and JMX. It will not display scripts that were executed directly via Groovy Console.
You can click on any run to see the full details. This will show the status for each script. You can also see the output of all scripts.
AECU maintains a full-text search index for the history entries. You can search for script names and their output.
Simply click on the magnifying glass in header to open the search bar:
Now you can enter a search term and will see the runs that contain this text. Click on the link to see the full history entry.
AECU adds its own binding to Groovy Console. You can reach it using "aecu" in your script.
This part provides methods to perform common tasks like property modification or node deletion.
It follows a collect, filter, execute process.
In the collect phase you define which nodes should be checked for a migration.
You can call these methods multiple times and combine them. They will be merged together.
Example:
aecu.contentUpgradeBuilder()
.forResources((String[])["/content/we-retail/ca/en"])
.forChildResourcesOf("/content/we-retail/us/en")
.forDescendantResourcesOf("/content/we-retail/us/en/experience")
.forResourcesInSubtree("/content/we-retail/us/en/experience")
.forResourcesBySql2Query("SELECT * FROM [cq:Page] AS s WHERE ISDESCENDANTNODE(s,'/content/we-retail/us/en/experience')")
.forResourcesByPropertyQuery("/content/we-retail/us", Collections.singletonMap("sling:resourceType", "weretail/components/content/heroimage"))
.forResourcesByPropertyQuery("/content/we-retail/us", Collections.singletonMap("sling:resourceType", "%/heroimage"), "nt:base")
.doSetProperty("name", "value")
.run()
These methods can be used to filter the nodes that were collected above. Multiple filters can be applied for one run (they are combined with AND
logic, i.e. all filters must match)
Filters the resources by property values.
filterByHasProperty(String name)
filterByNotHasProperty(String name)
filterByProperty(String name, Object value)
filterByNotProperty(String name, Object value)
filterByProperties(Map<String, String> properties)
filterByNotProperties(Map<String, Object> conditionProperties)
filterByMultiValuePropContains(String name, Object[] conditionValues)
filterByNotMultiValuePropContains(String name, Object[] conditionValues)
filterByPropertyRegex(String name, String regex)
filterByNotPropertyRegex(String name, String regex)
filterByAnyPropertyRegex(String regex)
filterByNoPropertyRegex(String regex)
Example:
def conditionMap = [:]
conditionMap["sling:resourceType"] = "weretail/components/structure/page"
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByHasProperty("myProperty")
.filterByProperty("sling:resourceType", "wcm/foundation/components/responsivegrid")
.filterByProperties(conditionMap)
.filterByMultiValuePropContains("myAttribute", ["value"] as String[])
.filterByPropertyRegex("myproperty", ".*test.*")
.filterByPropertyRegex("my_multiline_property", "(?s).*test.*")
.filterByAnyPropertyRegex(".*test.*")
.doSetProperty("name", "value")
.run()
You can also filter nodes by their name.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeName("jcr:content")
.filterByNotNodeName("jcr:content")
.filterByNodeNameRegex("jcr.*")
.filterByNotNodeNameRegex("jcr.*")
.doSetProperty("name", "value")
.run()
Nodes can also be filtered by their path using a regular expression.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByPathRegex(".*/jcr:content/.*")
.filterByNotPathRegex(".*/jcr:content/.*")
.filterByNodeRootPaths(Arrays.asList("/content/we-retail/ca/en", "/content/we-retail/be/nl"))
.doSetProperty("name", "value")
.run()
Filters resources by the (non-)existence of relative or absolute node path.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeExists("jcr:content/meta")
.filterByNodeExists("/content")
.filterByNodeNotExists("jcr:content/meta")
.filterByNodeNotExists("/content")
.doSetProperty("name", "value")
.run()
You can combine filters with AND and OR to build more complex filters.
def conditionMap_type = [:]
conditionMap_type['sling:resourceType'] = "weretail/components/content/heroimage"
def conditionMap_file = [:]
conditionMap_file['fileReference'] = "/content/dam/we-retail/en/activities/running/fitness-woman.jpg"
def conditionMap_page = [:]
conditionMap_page['jcr:primaryType'] = "cq:PageContent"
def complexFilter = new ORFilter(
[ new FilterByProperties(conditionMap_page),
new ANDFilter( [
new FilterByProperties(conditionMap_type),
new FilterByProperties(conditionMap_file)
] )
])
aecu.contentUpgradeBuilder()
.forDescendantResourcesOf("/content/we-retail/ca/en", false)
.filterWith(complexFilter)
.filterNotWith(complexFilter)
.filterWith(new NOTFilter(new FilterByPathRegex(".*jcr:content.*")))
.doSetProperty("name", "value")
.run()
To filter by complex conditions that are not covered by existing filterBy...()
presets you can use filterWith()
that takes a custom FilterBy
implementation as shown in the example below:
aecu.contentUpgradeBuilder()
.forDescendantResourcesOf("/content/we-retail/ca/en", false)
.filterWith(new FilterBy(){
public boolean filter(Resource resource, StringBuilder output) {
ValueMap properties = resource.valueMap
Calendar lastModified = properties.get("cq:lastModified", Calendar.class)
Calendar cal = Calendar.instance
cal.add(Calendar.YEAR, -5)
return lastModified.time.before(cal.time)
}
})
.doSetProperty("old", true) // mark pages that weren't modified in the past 5 years
.run()
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeName("jcr:content")
.doSetProperty("name", "value")
.doSetProperty("name", "value", "root/breadCrumb")
.doDeleteProperty("nameToDelete")
.doDeleteProperty("nameToDelete", "root/breadCrumb")
.doRenameProperty("oldName", "newName")
.doRenameProperty("oldName", "newName", "root/breadCrumb")
.run()
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeName("jcr:content")
.doAddValuesToMultiValueProperty("name", (String[])["value1", "value2"])
.doRemoveValuesOfMultiValueProperty("name", (String[])["value1", "value2"])
.doReplaceValuesOfMultiValueProperty("name", (String[])["old1", "old2"], (String[])["new1", "new2"])
.doJoinProperty("name")
.doJoinProperty("name", "fallbackValue")
.doJoinProperty("name", "fallbackValue", ",")
.run()
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeName("jcr:content")
.doAddMixin("mix:versionable")
.doAddMixin("mix:mymixin")
.doRemoveMixin("mix:lockable")
.run()
This will copy or move a property to a subnode. You can also change the property name.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeName("jcr:content")
.doCopyPropertyToRelativePath("name", "newName", "subnode")
.doMovePropertyToRelativePath("name", "newName", "subnode")
.run()
You can replace the content of String properties. This also supports multi-value properties.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeName("jcr:content")
.doReplaceValueInAllProperties("old", "new")
.doReplaceValueInProperties("old", "new", (String[]) ["propertyName1", "propertyName2"])
.doReplaceValueInAllPropertiesRegex("/content/([^/]+)/(.*)", "/content/newSub/\$2")
.doReplaceValueInPropertiesRegex("/content/([^/]+)/(.*)", "/content/newSub/\$2", (String[]) ["propertyName1", "propertyName2"])
.doChangePrimaryType("nt:unstructured")
.run()
The matching nodes can be copied/moved to a new location. You can use ".." if you want to step back in path.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeName("jcr:content")
.doRename("newNodeName")
.doCopyResourceToRelativePath("subNode")
.doCopyResourceToRelativePath("../subNode", "newName")
.doMoveResourceToRelativePath("../subNode")
.doMoveResourceToPathRegex("/content/we-retail/(\\w+)/(\\w+)/(\\w+)", "/content/somewhere/\$1/and/\$2")
.doReorderNode("toMove", "successor")
.run()
Sometimes a new node needs to be created e.g. to add or configure a component.
def map = [
"testval": "test"
]
aecu.contentUpgradeBuilder()
.forResources((String[]) ["/content/we-retail/jcr:content"])
.doCreateResource("mynode1", "nt:unstructured")
.doCreateResource("mynode2", "nt:unstructured", map)
.doCreateResource("mysubnode1", "nt:unstructured", "mynode1")
.doCreateResource("mysubnode2", "nt:unstructured", map, "mynode2")
.run()
You can delete nodes that match your collection and filter.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeName("jcr:content")
.doDeleteResource()
.doDeleteResource("child1", "child2", "child3")
.run()
Please note that this is for non-page resources such as commerce products. For page level (de)activation there are separate methods.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.doDeactivateResource()
.doActivateResource()
.run()
AECU can run actions on the page that contains a filtered resource. This is e.g. helpful if you filter by page resource type.
Please note that there is no check for duplicate actions. If you run a page action for two resources in the same page then the action will be executed twice.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByProperty("sling:resourceType", "weretail/components/structure/page")
.doActivateContainingPage()
.doDeactivateContainingPage()
.doTreeActivateContainingPage()
.doTreeActivateContainingPage(true)
.run()
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByProperty("sling:resourceType", "weretail/components/structure/page")
.doDeleteContainingPage()
.run()
Tags can be specified by Id (e.g. "properties:style/color") or path (e.g. "/etc/tags/properties/orientation/landscape").
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByProperty("sling:resourceType", "weretail/components/structure/page")
.doAddTagsToContainingPage("properties:style/color", "/etc/tags/properties/orientation/landscape")
.doSetTagsForContainingPage("properties:style/color", "/etc/tags/properties/orientation/landscape")
.doRemoveTagsFromContainingPage("properties:style/color", "/etc/tags/properties/orientation/landscape")
.run()
AECU can do some basic tests if pages render correctly. You can use this to verify a migration run.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByProperty("sling:resourceType", "weretail/components/structure/page")
.doCheckPageRendering()
.doCheckPageRendering(200)
.doCheckPageRendering("some test string")
.doCheckPageRendering("some test string", "exception")
.run()
Sometimes, you only want to print some information about the matched nodes.
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.filterByNodeName("jcr:content")
.printPath()
.printProperty("sling:resourceType")
.printJson()
.run()
You can also hook in custom code to perform actions on resources. For this "doCustomResourceBasedAction()" can take a Lambda expression.
def myAction = {
resource ->
hasChildren = resource.hasChildren()
String output = resource.path + " has children: "
output += hasChildren ? "yes" : "no"
return output
}
aecu.contentUpgradeBuilder()
.forChildResourcesOf("/content/we-retail/ca/en")
.doCustomResourceBasedAction(myAction)
.run()
At the end you can run all actions or perform a dry-run first. The dry-run will just provide output about modifications but not save any changes. The normal run saves the session, no additional "session.save()" is required.
AECU allows you to automate permission tests. This greatly speeds up your testing in this area since
Note: Testing is only be supported on group level. User level permissions are not supported as it is bad practice to assign permissions directly to users.
Each test block starts with "aecu.validateAccessRights()". Then you define the paths and groups to check with "forPaths/forGroups". Next, the actions to check are listed (e.g. "canRead()"). For each action there is also a "cannot" test. Finally, you start the test with "validate/simulate".
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men", "/content/we-retail/de", "/content/we-retail/fr")
.forGroups("content-authors")
.canRead()
.canModify()
.canDeletePage()
.validate()
You can call forPaths()/forGroups() multiple times. The can(not)* tests will always use the last one. E.g. this will test
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men", "/content/we-retail/de", "/content/we-retail/fr")
.forGroups("content-authors")
.canRead()
.canModify()
.canDeletePage()
.forGroups("content-readers")
.canRead()
.cannotModify()
.cannotDeletePage()
.validate()
The list of paths to check is set via "forPaths()". Here you can simply set all paths needed. Please note that the tests take some time. Therefore, take some example paths but not each and every page.
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men", "/content/we-retail/de", "/content/we-retail/fr")
.forGroups("content-authors")
.canRead()
.validate()
The list of groups to check is set via "forGroups()". Here you can simply set all groups needed. If groups need different checks then use multiple "forGroups()" or multiple calls of "aecu.validateAccessRights()".
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men")
.forGroups("content-authors", "content-readers")
.canRead()
.validate()
These are the basic tests to check e.g. for read/write access to a page/resource.
cannotWriteAcl(): cannot change the ACLs of the specified path
Example:
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men", "/content/we-retail/de", "/content/we-retail/fr")
.forGroups("content-readers")
.canRead()
.cannotModify()
.cannotReplicate()
.validate()
The following tests include additional checks for page nodes. Use them if you know the tested path is a page. As they inherit from the simple ACL tests (e.g. "canReadPage()" includes "canRead()") there is no need to call both.
cannotDeletePage(): page cannot be removed
Example:
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men", "/content/we-retail/de", "/content/we-retail/fr")
.forGroups("content-authors")
.canReadPage()
.canModifyPage()
.canCreatePage("/conf/we-retail/settings/wcm/templates/content-page")
.validate()
The following replication tests require "simulate()":
cannotReplicatePage(ReplicationActionType type): page cannot be replicated with given action (e.g. activate)
Example:
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men", "/content/we-retail/de", "/content/we-retail/fr")
.forGroups("content-authors")
.canReadPage()
.canModifyPage()
.canCreatePage("/conf/we-retail/settings/wcm/templates/content-page")
.canReplicatePage()
.canReplicatePage(ReplicationActionType.ACTIVATE)
.simulate()
The tests are executed with "validate()" or "simulate()". The call of "validate()" does not persist any changes. In contrast, "simulate()" will also perform actions that cannot be rolled-back. E.g. "simulate()" will do an actual page activation for "canReplicatePage()".
If any test requires "simulate()" then it will be noted in its description.
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men", "/content/we-retail/de", "/content/we-retail/fr")
.forGroups("content-authors")
.canRead()
.validate()
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men", "/content/we-retail/de", "/content/we-retail/fr")
.forGroups("content-authors")
.canReplicatePage()
.simulate()
Sample output:
┌───────────────┬────────────────────────────┬────────┐
│Group │Path │Rights │
├───────────────┼────────────────────────────┼────────┤
│content-authors│/content/we-retail/de │OK: Read│
│ │/content/we-retail/fr │OK: Read│
│ │/content/we-retail/us/en/men│OK: Read│
└───────────────┴────────────────────────────┴────────┘
You can stop the whole script execution when a test fails. This will mark the script run as failed.
By default, script execution will not stop on test failures.
aecu
.validateAccessRights()
.forPaths("/content/we-retail/us/en/men", "/content/we-retail/de", "/content/we-retail/fr")
.forGroups("content-authors")
.cannotReadPage()
.failOnError()
.validate()
For production systems it is recommended to limit the access to specific user groups. This can be done via OSGI configuration. Here you can specify groups for read and execute access.
Please not that user "admin" and group "administrators" always has full access. If no groups are specified then nobody except admin/administrators has access.
PID for OSGI config: de.valtech.aecu.core.security.AccessValidationService
The following service users are installed by AECU:
User | Rights | Description |
---|---|---|
aecu-admin | /: jcr:all | Validation of access rights, JMX executeWithHistory() to store script history |
aecu-content-migrator | /: jcr:read /apps: jcr:all /conf: jcr:all /content: jcr:all /etc: jcr:all /home: jcr:all /var: jcr:all |
Content changes using aecu binding |
aecu-service | /: jcr:read /var/aecu: jcr:all |
AECU execution history |
AECU provides JMX methods for executing scripts and reading the history. You can also check the version here.
This will execute the given script or folder. If a folder is specified then all files (incl. any subfolders) are executed. AECU will respect run modes during execution.
Parameters:
Use this e.g. when you copied over content from production to development and need to rerun install hook scripts.
This will execute the given script or folder. If a folder is specified then all files (incl. any subfolders) are executed. AECU will respect run modes and install hook history during execution. This means that scripts that were already executed by install hook are ignored. After execution the install hook history will be updated.
Parameters:
Sample curl call:
curl -u admin:admin 'http://localhost:5902/system/console/jmx/de.valtech%3Atype%3DAECU/op/executeWithHistory/java.lang.String' --data-raw 'Path=/conf/groovyconsole/scripts/aecu'
Prints the history of the specified last runs. The entries are sorted by date and start with the last run.
Parameters:
Executes a single file or all files of a folder structure. Additionally, you can pass json data for the script context.
Parameters:
Example json:
{
"rootPaths": [
"/content/we-retail"
]
}
This will print all files that are executable for a given path. You can use this to check which scripts of a given folder would be executed.
Parameters:
Health checks show you the status of AECU itself and the last migration run. You can access them on the status page. For the status of older runs use AECU's history page.
You can access our AECU service (AecuService class) in case you have special requirements. See the API documentation.
The AECU tool is licensed under the MIT LICENSE.
Please see our history file.
See our developer zone.