Skip to content

Latest commit

 

History

History
466 lines (360 loc) · 18.3 KB

package-plugin.md

File metadata and controls

466 lines (360 loc) · 18.3 KB

Cognifide logo

Gradle AEM Plugin

Package plugin

About

Main responsibility of this plugin is to build CRX/AEM package.

Provides CRX package related tasks: packageCompose, packageDeploy, packageActivate, packagePurge etc.

Should be applied to all projects that are composing CRX packages from JCR content only.

To apply plugin use snippet:

plugins {
    id("com.cognifide.aem.package")
}

This plugin implicitly applies also Common Plugin.

Task packageCompose

Compose CRX package from JCR content and bundles.

Inherits from task ZIP.

CRX package default configuration

aem {
    tasks {
        packageCompose {
            archiveBaseName.set(this@aem.baseName)
            duplicatesStrategy = DuplicatesStrategy.WARN
            contentDir.set(packageOptions.contentDir)
            metaDir.set(contentDir.dir("META-INF"))
            bundlePath.set(packageOptions.installPath)
            nestedPath.set(packageOptions.storagePath)
            vaultDefinition {
                properties.set(mapOf(
                    "acHandling" to "merge_preserve",
                    "requiresRoot" to false
                ))
                name.set(archiveBaseName)
                group.set(project.grouop)
            }
            fileFilter {
                expanding = true
                expandFiles = listOf(
                    "**/META-INF/*.xml",
                    "**/META-INF/*.MF",
                    "**/META-INF/*.cnd"
                )
                excluding = true
                excludeFiles = listOf(
                       "**/.gradle",
                       "**/.git",
                       "**/.git/**",
                       "**/.gitattributes",
                       "**/.gitignore",
                       "**/.gitmodules",
                       "**/.vlt",
                       "**/.vlt*.tmp",
                       "**/node_modules/**",
                       "jcr_root/.vlt-sync-config.properties"
               )
               bundleChecking = true
            }
            merging {
                vaultFilters = true
            }
            fromConvention = true
        }
    }    
}

CRX package naming

ZIP file name and metadata (Vault properties) of composed CRX package are set by convention - they are derived from project names and Gradle project.group and project.version properties. However, only if it is required (adapting the project to fit convention is more recommended approach), following snippet might be useful, to know how to customize particular values:

tasks {
    packageCompose {
        // Controlling ZIP file name by properties coming from inherited Gradle ZIP task: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html
        archiveBaseName.set("my-package")

        // Controlling values visible in CRX Package Manager
        vaultDefinition { 
            properties.set(mapOf(
                "acHandling" to "merge_preserve",
                "requiresRoot" to false
            ))
            name.set(archiveBaseName)
            group.set(project.group)
        }
    }
}

CRX package validation

Built package is validated using OakPAL tool. It helps preventing situation when CRX Package Manager reports message like Package installed with errors. Also gives immediate feedback for mistakes taken when using copy-pasting technique, forgetting about using entities ('&' instead of '&), missing XML namespaces and much more.

By default, nothing need to be configured so that default plan will be in use. However, more detailed checks could be provided by configuring base artifact called Opear File containing OakPAL checks.

It could be done via following snippet (ACS AEM Commons OakPAL Checks as example):

aem {
    `package` {
        validator {
            base("com.adobe.acs:acs-aem-commons-oakpal-checks:4.3.4")
        }
    }
}

To use custom checks, only left thing is to choose them in custom plan. Simply create files plan-compose.json plan-satisfy.json or only plan.json under path src/main/resources/com/cognifide/gradle/aem/package/OAKPAL_OPEAR

Sample plan content:

{
  "checklists": [
    "net.adamcin.oakpal.core/basic",
    "acs-commons-integrators",
    "content-class-aem65"
  ],
  "installHookPolicy": "SKIP",
  "checks": [
    {
      "name": "basic/subpackages",
      "config": {
        "denyAll": false
      }
    },
    {
      "name": "basic/acHandling",
      "config": {
        "levelSet": "no_unsafe"
      }
    }
  ]
}

See plans documentation and examples provided by OakPAL tool.

There could be many plan files created. To validate CRX package using different plan than default one, specify property -package.validator.planName=<file>.

Notice that running OakPAL requires to have included in CRX package up-to-date node type definitions coming from AEM instance. Such definitions could be manually downloaded using CRXDE Lite interface (Tools / Export Node Type) and put inside CRX package. After installing some dependent CRX packages, the list of exported node types may change.

To keep it up-to-date, plugin is synchronizing node types from one of available AEM instances automatically. Synchronized file containing node types, later used when building CRX packages is placed at path [aem/]gradle/package/nodetypes.sync.cnd. Remember to save this file in VCS, so that CRX package validation will not fail on e.g CI server where AEM instance could be not available.

To configure node types synchronization behavior, use property package.nodeTypesSync=<option>. Available options: preserve_auto (default, sync only if file nodetypes.sync.cnd does not exist), always, auto, fallback, preserve_fallback, never.

Package validation report is saved at path relative to project building CRX package: build/aem/packageCompose/OAKPAL_OPEAR/report.json.

Including additional OSGi bundle into CRX package

Use dedicated task method named installBundle, for example:

tasks {
    packageCompose {
        installBundle("com.github.mickleroy:aem-sass-compiler:1.0.1")
    }
}

If bundle is build by other project:

tasks {
    packageCompose {
        installBundleProject(":core") // must apply plugin 'com.cognifide.aem.bundle'
    }
}

For reference, see usage above in AEM Multi-Project Example.

Nesting CRX packages

Use dedicated task method named nestPackage, For example:

tasks {
    packageCompose {
        nestPackage("com.adobe.cq:core.wcm.components.all:2.4.0")
        nestPackage("com.adobe.cq:core.wcm.components.examples:2.4.0")
    }
}

If package is build by other project:

tasks {
    packageCompose {
        nestPackageProject(":ui.content") // must apply plugin 'com.cognifide.aem.package'
    }
}

Assembling packages (merging all-in-one)

Let's assume following project structure:

  • build.gradle.kts (project :, no source files at all)
  • core/build.gradle.kts (project :core, OSGi bundle only)
  • ui.apps/build.gradle.kts (project :ui.apps, JCR content only)
  • ui.content/build.gradle.kts (project :ui.content, JCR content only)

File content of build.gradle.kts:

plugins {
    id("com.cognifide.aem.package")
}

aem {
    tasks {
        packageCompose {
            installBundleProject(":core")
            mergePackageProject(":ui.apps")
            mergePackageProject(":ui.content")
        }
    }    
}

When building via command gradlew :build, then the effect will be a CRX package with merged JCR content from projects :ui.apps, :ui.content and OSGi bundle built by project :core. Vault workspace filters (defined in file filter.xml) from :ui.apps and :ui.content sub-projects will be combined into single one automatically. However, one rule must be kept while developing a multi-module project: all Vault filter roots of all projects must be exclusive. In general, they are most often exclusive, to avoid strange JCR installer behaviors, but sometimes exceptional workspace filter rules are being applied like mode="merge" etc.

Gradle AEM Plugin is configured in a way that single Gradle project could have:

  • JCR content,
  • source code to compile OSGi bundle,
  • both.

It is worth to know a difference when comparing to package built by Maven and Content Package Maven Plugin. When using it, there is a need for much more modules. Separate one for building OSGi bundle and other one for building CRX package and nesting built OSGi bundle. When using Gradle AEM Plugin, there is just much more flexibility.

By mixing usages of methods nestPackage*, installBundle* or mergePackage* there is ability to create any assembly CRX package with content of any type without restructuring the project. When using installBundle there is an ability to pass lambda to customize options like bundle run mode.

By default, Gradle AEM Plugin will ensure:

  • Running unit tests for all sub-projects providing OSGi bundles to be put under install path inside built assembly package when using method installBundleProject. To disable, set property package.bundleTest=false.
  • Running package validations for all sub-projects providing CRX package to be nested when using method nestPackageProject. To disable it, set property package.nestedValidation=false.

Expandable properties

In exactly the same way as it works for instance files, properties can be expanded inside metadata files of package being composed.

Related configuration:

aem {
    tasks {
        packageCompose {
            fileFilter {
                expandProperties.set(mapOf(
                    "organization" to "Company"
                ))
                expandFiles.set(listOf(
                    "**/META-INF/*.xml",
                    "**/META-INF/*.MF",
                    "**/META-INF/*.cnd"
                ))
            }
        }
    }
}

Predefined expandable properties:

  • aem - AemExtension object,
  • definition - VaultDefinition object,
  • rootProject - project with directory in which settings.gradle is located,
  • project - current project.

This feature is especially useful to generate valid META-INF/properties.xml file, below template is used by plugin by default:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    {% if definition.description is not empty %}
    <comment>{{definition.description}}</comment>
    <entry key="description">{{definition.description}}</entry>
    {% endif %}
    <entry key="group">{{definition.group}}</entry>
    <entry key="name">{{definition.name}}</entry>
    <entry key="version">{{definition.version}}</entry>
    {% if definition.createdBy is not empty %}
    <entry key="createdBy">{{definition.createdBy}}</entry>
    {% endif %}
    {% for e in definition.properties %}<entry key="{{e.key}}">{{e.value | raw}}</entry>
    {% endfor %}
</properties>

Also file nodetypes.cnd is dynamically expanded from template to generate file containing all node types from all sub packages being merged into assembly package.

Each JAR file in separate hooks directory will be combined into single directory when creating assembly package.

Publishing packages

Simply add following snippets to file build.gradle.kts for each project applying package plugin.

plugins {
    id("com.cognifide.aem.package")
    id("maven-publish")
}

publishing {
    repositories {
        maven {
            // specify here e.g Nexus URL and credentials
        }   
    }

    publications {
        create<MavenPublication>("maven") {
            from(components["aem"])
        }
    }
}

To publish package to repository (upload it to e.g Nexus repository) simply run one of publish tasks (typically publish).

It might be worth to configure publishing repositories globally. Consider moving publishing { repositories { /* ... */ } } section to root project's build.gradle.kts into allprojects { } section. Then defining publishing repositories in each subproject will be no longer necessary.

Task packagePrepare

Processes CRX package metadata - combines default files provided by plugin itself with overridden ones. Also responsible for synchronizing Vault node types consumed later by CRX package validation in the end of compose task. Covers extracted logic being initially a part of compose task. Reason of separation is effectively better Gradle caching.

Task packageValidate

Validates composed CRX package.

For setting common options see section CRX package validation.

For setting project/package specific options use snippet below:

tasks {
    packageValidate {
        validator {
            enabled.set(true)
            verbose.set(true)
            severity("MAJOR")
            planName.set("plan.json")
            jcrPrivileges.set(listOf("crx:replicate"))
            cndFiles.from(packageOptions.configDir.file("nodetypes.cnd"))
        }
    }
}

Task packageDeploy

Upload & install CRX package into AEM instance(s).

Recommended form of deployment. Optimized version of packageUpload packageInstall.

Deploying only to desired instances

Simply follow general rules about instance filtering.

Deploying options

Add any of below command line parameters to customize CRX package deployment behavior:

  • -Ppackage.deploy.awaited=false - disable stability & health checks after deploying CRX package.
  • -Ppackage.deploy.distributed=true - use alternative form of deployment. At first, deploys CRX package to author instances, then triggers replication of CRX package so that it will be installed also on publish instances.
  • -Ppackage.manager.uploadForce=false - disable force installation (by default even unchanged CRX package is forced to be reinstalled)
  • -Ppackage.manager.installRecursive=false - disable automatic installation of subpackages located inside CRX package being deployed.
  • -Ppackage.manager.uploadRetry=n - customize number of retries being performed after failed CRX package upload.
  • -Ppackage.manager.installRetry=n - customize number of retries being performed after failed CRX package install.
  • -Ppackage.manager.workflowToggle=[id1=true,id2=false,...] - temporarily enable or disable AEM workflows during deployment e.g when CRX package contains generated DAM asset renditions so that regeneration could be avoided and deploy time reduced. For example: -Ppackage.deploy.workflowToggle=[dam_asset=false]. Workflow ID dam_asset is a shorthand alias for all workflows related with DAM asset processing.

Task packageUpload

Upload composed CRX package into AEM instance(s).

Task packageDelete

Delete uploaded CRX package from AEM instance(s).

Task packageInstall

Install uploaded CRX package on AEM instance(s).

Task packageUninstall

Uninstall uploaded CRX package on AEM instance(s).

To prevent data loss, this unsafe task execution must be confirmed by parameter -Pforce.

Task packagePurge

Fail-safe combination of packageUninstall and packageDelete.

To prevent data loss, this unsafe task execution must be confirmed by parameter -Pforce.

Task packageActivate

Replicate installed CRX package to other AEM instance(s).

Known issues

Caching task packageCompose

Expandable properties with dynamically calculated value (unique per build) like created and buildCount are not used by default generated properties file intentionally, because such usages will effectively forbid caching packageCompose task and it will be never UP-TO-DATE.