Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Route the request to one expert skill. Two pattern families share this skill:
| User said / saw | Expert skill |
|---|---|
| "update aem sdk", "upgrade mockito", stale `<version>` or `${property}` in pom | [`outdated-dependencies/`](outdated-dependencies/SKILL.md) |
| "package install fails on AEMaaCS", `day/cq60/product`, "CRX refuses install", Vault dependency not found at deploy time | [`vault-package-dependencies/`](vault-package-dependencies/SKILL.md) |
| "fix @Inject", "modernize Sling Models", `javax.inject.Inject` on `@Model` fields | [`inject-in-sling-model/`](inject-in-sling-model/SKILL.md) |
| "add HTTP timeouts", "outbound/external call has no timeout", `HttpClient` / `HttpClients` / `OkHttpClient` built without a timeout | [`outbound-call-timeouts/`](outbound-call-timeouts/SKILL.md) |
| "bound my query", "unbounded query", "query causing OOM", `p.limit=-1`, `setLimit(-1)` | [`unbounded-query/`](unbounded-query/SKILL.md) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Adding a pattern: add a row here, then build the expert skill from
| `unbounded-recursion` | add a depth guard to self-recursive / tree-traversal methods (incl. navigation & breadcrumb) | medium | planned | - | - |
| `unbounded-graphql` | add pagination (`first` / `limit`) to GraphQL queries | medium | planned | - | - |
| `logging-in-loops` | move logging out of hot loops / add level guards | low | planned | - | - |
| [`vault-package-dependencies`](../vault-package-dependencies/SKILL.md) | remove AEM 6.x Vault install-time package dependencies (`day/cq60/product:*`) from `content-package-maven-plugin` that block installation on AEMaaCS | high | ready | analyzer | mechanical |
| [`remove-deprecated-api`](../remove-deprecated-api/SKILL.md) | migrate deprecated and removed Java APIs to comply with AEM as a Cloud Service enforcement | high | ready | analyzer | guided |

> Out-of-memory / leak incidents — a frequent symptom — are usually caused by
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import analyzer.detectors.ResourceChangeListener;
import analyzer.detectors.Scheduler;
import analyzer.detectors.UnboundedQuery;
import analyzer.detectors.VaultPackageDependencies;

import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -28,7 +29,8 @@ public static List<Detector> all() {
new AssetManager(),
new OutboundCallTimeouts(),
new UnboundedQuery(),
new RemoveDeprecatedApi()
new RemoveDeprecatedApi(),
new VaultPackageDependencies()
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package analyzer.detectors;

import analyzer.Corpus;
import analyzer.Detector;
import analyzer.Finding;
import analyzer.PomUnit;
import analyzer.util.Poms;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Detects AEM 6.x product-package Vault install-time {@code <dependencies>} declared inside
* {@code content-package-maven-plugin / <configuration>} that cannot be resolved on AEM as a
* Cloud Service. The product packages (day/cq60/product, day/cq560/*, adobe/cq60) are baked
* into the AEMaaCS container image and are not present in CRX Package Manager — so CRX refuses
* to install the customer package at deploy time even though {@code mvn clean install} succeeds.
*
* <p>Emits one finding per {@code <dependencies>} block (not per {@code <dependency>} entry)
* because the fix removes the entire block.
*/
public final class VaultPackageDependencies implements Detector {

public String pattern() { return "vault-package-dependencies"; }
public boolean needsJava() { return false; } // pom-only detector

/** Group-path prefixes whose packages don't exist on AEMaaCS. */
private static final String[] LEGACY_PREFIXES = {
"day/cq60/",
"day/cq560/",
"adobe/cq60",
};

private static boolean isLegacyGroup(String group) {
if (group == null) return false;
for (String p : LEGACY_PREFIXES) {
if (group.startsWith(p) || group.equals(p.replaceAll("/$", ""))) return true;
}
return false;
}

public void detect(Corpus c, List<Finding> out, List<String> warnings) {
for (PomUnit pom : c.poms) {
Map<String, Integer> cursor = new HashMap<>();
NodeList plugins = pom.doc.getElementsByTagName("plugin");
for (int i = 0; i < plugins.getLength(); i++) {
Element plugin = (Element) plugins.item(i);
String artifactId = Poms.childText(plugin, "artifactId");
if (!"content-package-maven-plugin".equals(artifactId)) continue;

// Find direct <configuration> children of this plugin
NodeList pluginChildren = plugin.getChildNodes();
for (int j = 0; j < pluginChildren.getLength(); j++) {
Node pch = pluginChildren.item(j);
if (pch.getNodeType() != Node.ELEMENT_NODE) continue;
if (!"configuration".equals(pch.getNodeName())) continue;
Element config = (Element) pch;

// Find direct <dependencies> children of configuration
NodeList configChildren = config.getChildNodes();
for (int k = 0; k < configChildren.getLength(); k++) {
Node cch = configChildren.item(k);
if (cch.getNodeType() != Node.ELEMENT_NODE) continue;
if (!"dependencies".equals(cch.getNodeName())) continue;
Element depList = (Element) cch;

// Emit one finding if any <dependency> carries a legacy <group>
NodeList deps = depList.getElementsByTagName("dependency");
for (int m = 0; m < deps.getLength(); m++) {
String group = Poms.childText((Element) deps.item(m), "group");
if (isLegacyGroup(group)) {
long ln = Poms.findLine(pom.lines,
"<artifactId>content-package-maven-plugin</artifactId>",
cursor);
out.add(new Finding(pattern(), pom.rel, ln,
"content-package-maven-plugin: legacy Vault dependency group=" + group));
break; // one finding per <dependencies> block
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
name: vault-package-dependencies
description: |
AEM Cloud Service expert skill — remove legacy AEM 6.x Vault install-time <dependencies> from
content-package-maven-plugin <configuration> in pom.xml. These day/cq60/product:* entries do not
exist on AEMaaCS (the platform is baked into the container image), so CRX Package Manager refuses
to install the package at deploy time even though the Maven build succeeds. Use for "package install
fails on AEMaaCS", "day/cq60/product dependency", "CRX refuses install", or when scanning a project
for deployment blockers. Fix is mechanical: remove the entire <dependencies> block.
license: Apache-2.0
---

# Vault package dependencies — AEM as a Cloud Service

> This pattern is executed by the code-assessment runbook — follow [`../references/runbook.md`](../references/runbook.md) for the full flow (preflight → plan → apply → verify, run log). This skill supplies the detection + recipe the runbook applies.

## Overview

Content packages built for AEM 6.x declare Vault install-time `<dependencies>` inside
`content-package-maven-plugin / <configuration>`. These reference AEM 6.x product packages
(`day/cq60/product:cq-content`, `day/cq60/product:cq-commerce-content`, etc.) that were
pre-installed on AEM 6.x instances. On AEM as a Cloud Service the platform is delivered via a
container image — those product packages no longer exist as installable entries in CRX Package
Manager. CRX Package Manager checks all declared Vault dependencies at install time and **refuses
the install** if any are unresolvable, even though `mvn clean install` succeeds.

The fix is mechanical: remove the entire `<dependencies>` block. The functionality the packages
provided is still present on AEMaaCS; only the install-time check needs to be removed.

## Classification — confirm this pattern applies

- A `pom.xml` whose `content-package-maven-plugin / <configuration>` contains a `<dependencies>`
block with at least one `<dependency>` whose `<group>` starts with `day/cq60/`, `day/cq560/`,
or `adobe/cq60`.
- Applies regardless of whether the plugin groupId is `com.day.jcr.vault` (legacy) or
`org.apache.jackrabbit` (current filevault-package-maven-plugin).
- The same pom may have the plugin in both main `<build>` and inside `<profiles>` — check both.

## Discovery

Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by
the runbook:

```bash
bash ../scripts/analyze.sh <workspace-root> --pattern vault-package-dependencies
```

**Match criteria:** a `<plugin>` whose direct `<artifactId>` child equals
`content-package-maven-plugin`, with a direct `<configuration>` child containing a direct
`<dependencies>` child that has at least one `<dependency>/<group>` prefixed with `day/cq60/`,
`day/cq560/`, or `adobe/cq60`. One finding per `<dependencies>` block.

## Resolution contract

**self-evident** — always remove the entire `<dependencies>` block. No user input required.

## Review checklist

- [ ] Only the `<dependencies>` block under `content-package-maven-plugin / <configuration>` removed — no other plugin config touched
- [ ] Maven classpath `<dependencies>` at the project level left intact
- [ ] No whitespace churn outside the removed block
- [ ] All occurrences removed — check both main `<build>` and any `<profiles>` sections
- [ ] Build still succeeds after the change (`mvn clean install`)

## Recipe

Read [`recipe.md`](recipe.md) in full before editing: locator, edit, unlocatable reasons, before/after example, editing strategy.

## Handoff

The skill never commits. See [`../references/git-workflow.md`](../references/git-workflow.md) for git vs in-place handoff and the suggested commit message.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Recipe — Vault package dependencies

> Read this fully before editing. Control plane: [SKILL.md](SKILL.md).

## Input contract

Per invocation, a deduplicated list of repo-relative pom paths:

```json
{
"files": [
"ui.apps/pom.xml",
"ui.commons/pom.xml"
]
}
```

Sources:

1. **User-named** — the user names the pom file(s) directly.
2. **Discover** — the file list is the output of the **Discovery** scan in [`SKILL.md`](SKILL.md).

## Locator

For each file:

1. Find the `<plugin>` element whose direct `<artifactId>` child text equals `content-package-maven-plugin`.
2. Inside that plugin, find the direct `<configuration>` child element.
3. Inside `<configuration>`, find the direct `<dependencies>` child element containing at least one `<dependency>/<group>` prefixed with `day/cq60/`, `day/cq560/`, or `adobe/cq60`.
4. Repeat for every occurrence of `content-package-maven-plugin` in the file — the same pom may declare the plugin in the main `<build>` section **and** in one or more `<profiles>/<profile>/<build>` sections.

## Edit

Remove the entire `<dependencies>...</dependencies>` block, including its surrounding blank/indentation line. Do not touch any sibling elements (`<subPackages>`, `<embeddeds>`, `<filters>`, `<properties>`, etc.).

## Before / after

**Before (`ui.apps/pom.xml`):**
```xml
<configuration>
<verbose>true</verbose>
<failOnError>true</failOnError>
<group>adobe/aem6/sample</group>
<failOnMissingEmbed>true</failOnMissingEmbed>
<dependencies>
<dependency>
<group>day/cq60/product</group>
<name>cq-content</name>
<version>[6.3.0,)</version>
</dependency>
<dependency>
<group>day/cq60/product</group>
<name>cq-commerce-content</name>
<version>[1.5.0,)</version>
</dependency>
</dependencies>
<subPackages>
...
</subPackages>
</configuration>
```

**After:**
```xml
<configuration>
<verbose>true</verbose>
<failOnError>true</failOnError>
<group>adobe/aem6/sample</group>
<failOnMissingEmbed>true</failOnMissingEmbed>
<subPackages>
...
</subPackages>
</configuration>
```

## Unlocatable / skip reasons

| Situation | `skipped` reason string |
|---|---|
| `content-package-maven-plugin` not found in pom | `vault-package-dependencies-no-plugin: content-package-maven-plugin not found in <file>` |
| Plugin found but no `<configuration>/<dependencies>` block present | `vault-package-dependencies-no-deps-block: no <dependencies> block under <configuration> in <file>` |
| `<dependencies>` block contains no legacy group prefixes | `vault-package-dependencies-no-legacy-groups: no day/cq60 or day/cq560 group prefixes found in <file>` |

## Editing strategy

Surgical XML edit — anchor on ≥3 lines of context before and after the `<dependencies>` block. Use `replace_string_in_file`. Do not re-serialize or reformat any surrounding XML.
1 change: 1 addition & 0 deletions plugins/aem/cloud-service/skills/migration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This skill drives the **migration workflow**: BPA data, CAM/MCP, **one pattern p
| A **BPA CSV** | *"Fix **scheduler** findings using `./path/to/bpa.csv`"* | Fastest path: CSV → cached collection → files |
| **CAM + MCP** only | *"Get **scheduler** findings from CAM; I'll pick the project when you list them."* | Agent lists projects → you confirm → MCP fetch ([cam-mcp.md](references/cam-mcp.md)) |
| **Just a few files** | *"Migrate **scheduler** in `core/.../MyJob.java`"* | Manual flow: no BPA required |
| **Vault deploy blocker** | *"Fix **vault-package-dependencies** — my package won't install on AEMaaCS."* | Agent scans pom.xml files for `day/cq60/product` Vault deps and removes the `<dependencies>` block |
| **OSGi → Cloud Manager** | *"**Scan my config files and create Cloud Manager environment secrets or variables.**"* | Agent **auto-reads** [references/osgi-cfg-json-cloud-manager.md](references/osgi-cfg-json-cloud-manager.md) (full Adobe-aligned rules inlined there); no BPA pattern id |
| **HTL lint warnings** | *"Fix **htlLint** issues in `ui.apps`"* | Proactive discovery via `rg` → fix per the HTL lint reference |
| **Template modernization** | *"**Migrate my static templates to editable templates and generate Modernize Tools rules.**"* / *"Create editable templates from my static templates."* / *"Generate AEM Modernize Tools structure/component/policy rules."* | Agent **auto-reads** [references/template-modernization/template-modernization-context.md](references/template-modernization/template-modernization-context.md) (shared discovery + structured context), produces a **per-template plan table**, then executes the plan using [editable-template-creation.md](references/template-modernization/editable-template-creation.md) and [aem-modernization.md](references/template-modernization/aem-modernization.md), and validates via [template-modernization-validation.md](references/template-modernization/template-modernization-validation.md). No BPA pattern id. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- clean: content-package-maven-plugin present but no <dependencies> block -->
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>example-ui.apps</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>content-package</packaging>
<build>
<plugins>
<plugin>
<groupId>com.day.jcr.vault</groupId>
<artifactId>content-package-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<verbose>true</verbose>
<failOnError>true</failOnError>
<group>adobe/aem6/sample</group>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>example-ui.apps</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>content-package</packaging>
<build>
<plugins>
<plugin>
<groupId>com.day.jcr.vault</groupId>
<artifactId>content-package-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<verbose>true</verbose>
<failOnError>true</failOnError>
<group>adobe/aem6/sample</group>
<dependencies>
<dependency>
<group>day/cq60/product</group>
<name>cq-content</name>
<version>[6.3.0,)</version>
</dependency>
<dependency>
<group>day/cq60/product</group>
<name>cq-commerce-content</name>
<version>[1.5.0,)</version>
</dependency>
<dependency>
<group>day/cq560/social/commons</group>
<name>cq-social-commons-pkg</name>
<version>[1.7.0,)</version>
</dependency>
</dependencies>
</configuration>
</plugin>
</plugins>
</build>
</project>
9 changes: 9 additions & 0 deletions plugins/aem/cloud-service/test/code-assessment/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,15 @@ assert_absent "no findings when cache is missing" "$OUT" '"pattern":"remove-de
rm -f "$RULES_TSV"


echo "[vault-package-dependencies] legacy AEM 6.x Vault install-time deps detected; clean pom not flagged"
OUT="$(run "$FIX/vault-package-dependencies")"
assert_contains "vault-package-dependencies pattern present" "$OUT" '"pattern":"vault-package-dependencies"'
assert_contains "antipattern pom flagged" "$OUT" 'pom.xml'
assert_contains "day/cq60/product group in snippet" "$OUT" 'day/cq60/product'

OUT="$(run "$FIX/vault-package-dependencies-clean")"
assert_absent "clean vault pom not flagged" "$OUT" 'vault-package-dependencies'

echo "----"
echo "PASS=$PASS FAIL=$FAIL"
[ "$FAIL" -eq 0 ]
Loading