From fb2f22c665dddec109b2bc8bcc446dd1eb0148c4 Mon Sep 17 00:00:00 2001 From: Juan Ayala Date: Wed, 29 Oct 2025 03:16:01 +0000 Subject: [PATCH 1/3] Disable moby on docker dependency for newer Debian distros --- src/aem-sdk/devcontainer-feature.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/aem-sdk/devcontainer-feature.json b/src/aem-sdk/devcontainer-feature.json index ef85de9..04cac07 100644 --- a/src/aem-sdk/devcontainer-feature.json +++ b/src/aem-sdk/devcontainer-feature.json @@ -47,7 +47,9 @@ } ], "dependsOn": { - "ghcr.io/devcontainers/features/docker-in-docker:2": {} + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "moby": false + } }, "installsAfter": [ "ghcr.io/devcontainers/features/java:1" From d8c263b5624e3356665f2c94e281559ba532ccab Mon Sep 17 00:00:00 2001 From: Juan Ayala Date: Wed, 29 Oct 2025 03:17:15 +0000 Subject: [PATCH 2/3] Create new aem-lts feature --- .devcontainer/devcontainer.json | 15 +++- .gitignore | 1 + src/aem-lts/NOTES.md | 40 ++++++++++ src/aem-lts/bin/_globals.sh | 33 ++++++++ src/aem-lts/bin/aem-lts | 75 ++++++++++++++++++ src/aem-lts/devcontainer-feature.json | 52 ++++++++++++ src/aem-lts/install.sh | 16 ++++ test/aem-lts/defaults-with-quickstart.sh | 55 +++++++++++++ .../mock-cq-quickstart-6.6.0.jar | Bin 0 -> 2275 bytes .../mock-http-server/SimpleHttpServer.java | 55 +++++++++++++ test/aem-lts/mock-http-server/build.sh | 13 +++ test/aem-lts/scenarios.json | 17 ++++ test/aem-lts/test.sh | 42 ++++++++++ 13 files changed, 410 insertions(+), 4 deletions(-) create mode 100644 .gitignore create mode 100644 src/aem-lts/NOTES.md create mode 100644 src/aem-lts/bin/_globals.sh create mode 100644 src/aem-lts/bin/aem-lts create mode 100644 src/aem-lts/devcontainer-feature.json create mode 100644 src/aem-lts/install.sh create mode 100644 test/aem-lts/defaults-with-quickstart.sh create mode 100644 test/aem-lts/defaults-with-quickstart/mock-cq-quickstart-6.6.0.jar create mode 100644 test/aem-lts/mock-http-server/SimpleHttpServer.java create mode 100755 test/aem-lts/mock-http-server/build.sh create mode 100644 test/aem-lts/scenarios.json create mode 100644 test/aem-lts/test.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 51998d3..b6e0f63 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "image": "mcr.microsoft.com/devcontainers/javascript-node:1-20-bookworm", + "image": "mcr.microsoft.com/devcontainers/base:debian", "customizations": { "vscode": { "settings": { @@ -18,9 +18,16 @@ } }, "features": { - "ghcr.io/devcontainers/features/docker-in-docker:2": {} + "ghcr.io/devcontainers/features/node:1": { + "version": "20" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/devcontainers/features/java:1": { + "version": "21", + "jdkDistro": "oracle", + "installMaven": true + } }, - "remoteUser": "node", "updateContentCommand": "npm install -g @devcontainers/cli", "postStartCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}" -} +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/src/aem-lts/NOTES.md b/src/aem-lts/NOTES.md new file mode 100644 index 0000000..402bdc8 --- /dev/null +++ b/src/aem-lts/NOTES.md @@ -0,0 +1,40 @@ +# Adding the Feature to a Project + +## Pre-requisites +* Visual Studio Code +* Docker +* The AEM LTS Quickstart Jar + +## Step 1: The SDK Archive +* Navigate to Adobe's [Software Distribuition](https://experience.adobe.com/#/downloads) Site. Click on [Adobe Experience Manager](https://experience.adobe.com/#/downloads/content/software-distribution/en/aem.html). And click on [Download AEM 6.5 LTS](https://experience.adobe.com/#/downloads/content/software-distribution/en/aem.html?package=/content/software-distribution/en/details.html/content/dam/aem/public/adobe/packages/cq660/quickstart/cq-quickstart-6.6.0.jar) +* Place the JAR file in your project folder (i.e. `.devcontainer/cq-quickstart-6.6.0.jar`) + +## Step 2: The Devcontainer Settings +* Add the following feature to the `.devcontainer/devcontainer.json` file +```jsonc +"features": { + "ghcr.io/juan-ayala/devcontainer-features/aem-lts:1": { + "quickstartJar": "${containerWorkspaceFolder}/.devcontainer/cq-quickstart-6.6.0.jar", + "licenseCustomerName": "Acme Corporation", + "licenseDownloadId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + } +} +``` + +## Step 3: Visual Studio Code +* Install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). And open the project folder. +* VSCode will detect `.devcontainer/devcontainer.json`. And prompt you to reopen the project in a devcontainer. + +## Run AEM Services +In VSCode, open the terminal window. This is a terminal inside the docker container. You can run any command as needed, including Maven and Node. + +There will be a script named `aem-lts`. Use this to start the author, publish or dispatcher. +* Start author: `aem-lts start author` +* Stop author: `aem-lts stop author` +* Start publish: `aem-lts start publish` +* Stop publish: `aem-lts stop publish` + +The feature also sets up volume mounts for the author and publish services. This is where the services will persist the repository. So that if the container gets deleted and/or rebuilt, the repository will persist. + +## References +* [Install local AEM Instances](https://experienceleague.adobe.com/en/docs/experience-manager-learn/foundation/development/set-up-a-local-aem-development-environment#install-local-aem-instances) diff --git a/src/aem-lts/bin/_globals.sh b/src/aem-lts/bin/_globals.sh new file mode 100644 index 0000000..3e3f3bc --- /dev/null +++ b/src/aem-lts/bin/_globals.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +source "${AEM_LTS_FEATURE_DIR}/options.sh" + +function get_runmode_port() +{ + local runmode="${1}" + if [ "${runmode}" = "publish" ]; then + echo "${AEM_LTS_PUBLISH_PORT}" + else + echo "${AEM_LTS_AUTHOR_PORT}" + fi +} + +function aem_lts_err() +{ + echo "${1}" >&2 + exit 42 +} + +get_action() { + [ "${1}" = 'start' ] && action="${1}" + [ "${1}" = 'stop' ] && action="${1}" + [ -z "${action}" ] && action="start" + echo "${action}" +} + +get_runmode() { + [ "${1}" = 'author' ] && service="${1}" + [ "${1}" = 'publish' ] && service="${1}" + [ -z "${service}" ] && service="author" + echo "${service}" +} diff --git a/src/aem-lts/bin/aem-lts b/src/aem-lts/bin/aem-lts new file mode 100644 index 0000000..33c617b --- /dev/null +++ b/src/aem-lts/bin/aem-lts @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +source "$(dirname $0)/_globals.sh" +action=$(get_action "${1}") +service=$(get_runmode "${2}") + +echo "${action^}ing AEM ${service}..." + +# copy the jar if not already there +JAR_NAME=$(basename "${AEM_LTS_QUICKSTART_JAR}") +jarFile="${AEM_LTS_FEATURE_DIR}/${JAR_NAME}" +if [[ ! -f "${jarFile}" ]]; then + if [[ -f "${AEM_LTS_QUICKSTART_JAR}" ]]; then + sudo cp "${AEM_LTS_QUICKSTART_JAR}" "${jarFile}" + else + aem_lts_err "Quickstart jar '${AEM_LTS_QUICKSTART_JAR}' not found" + fi +fi + +# create the license file if not already there +licenceFile="${AEM_LTS_FEATURE_DIR}/license.properties" +if [[ ! -f "${licenceFile}" ]]; then + + # check license properties + [[ -z "${AEM_LTS_LICENSE_CUSTOMER_NAME}" ]] && \ + aem_lts_err "License customer name not set" + [[ -z "${AEM_LTS_LICENSE_DOWNLOAD_ID}" ]] && \ + aem_lts_err "License download ID not set" + + cat < /dev/null +license.customer.name=${AEM_LTS_LICENSE_CUSTOMER_NAME} +license.downloadID=${AEM_LTS_LICENSE_DOWNLOAD_ID} +license.product.name=Adobe Experience Manager +license.product.version=6.5.0.LTS +EOF + +fi + +runmodedir="${AEM_LTS_FEATURE_DIR}/${service}" + +# link the jar if not already linked +jarFileLink="${runmodedir}/${JAR_NAME}" +[[ ! -L "${jarFileLink}" ]] && sudo ln -s "${jarFile}" "${jarFileLink}" + +# link the license if not already linked +licenseFileLink="${runmodedir}/license.properties" +[[ ! -L "${licenseFileLink}" ]] && sudo ln -s "${licenceFile}" "${licenseFileLink}" + +# make user owner of crx-quickstart (it is a volume mount) +sudo chown ${USER} "${runmodedir}/crx-quickstart" + +case "${action}" in + start) + + [[ -f "/tmp/aem-${service}.pid" ]] && aem_lts_err "AEM ${service} already running" + + port=$(get_runmode_port ${service}) + jvm_opts="-agentlib:jdwp=transport=dt_socket,address=*:3${port},server=y,suspend=n" + cq_opts="-nofork -nobrowser -nointeractive -r ${service} -p ${port}" + + sudo start-stop-daemon --start --quiet --background --chuid root \ + --make-pidfile --pidfile "/tmp/aem-${service}.pid" \ + --name ${service} \ + --chdir "$(dirname "${jarFileLink}")" \ + --startas /bin/bash -- -c "exec ${JAVA_HOME}/bin/java ${jvm_opts} -jar "${jarFileLink}" ${cq_opts} \ + > /var/log/aem-${service}.log 2>&1" + ;; + stop) + + sudo start-stop-daemon --stop --verbose \ + --remove-pidfile --pidfile /tmp/aem-${service}.pid + ;; + *) + aem_lts_err "Unknown action '${action}'" +esac diff --git a/src/aem-lts/devcontainer-feature.json b/src/aem-lts/devcontainer-feature.json new file mode 100644 index 0000000..a2b2bcf --- /dev/null +++ b/src/aem-lts/devcontainer-feature.json @@ -0,0 +1,52 @@ +{ + "id": "aem-lts", + "version": "1.0.0", + "name": "Adobe Experience Manager LTS", + "description": "Setup author and publish services. Requires the AEM LTS quickstart jar and license details.", + "options": { + "quickstartJar": { + "type": "string", + "description": "AEM LTS Quickstart Jar.", + "default": "" + }, + "licenseCustomerName": { + "type": "string", + "description": "AEM LTS License Customer Name.", + "default": "" + }, + "licenseDownloadId": { + "type": "string", + "description": "AEM LTS License Download Id.", + "default": "" + }, + "authorPort": { + "type": "string", + "description": "Author service port", + "default": "4502" + }, + "publishPort": { + "type": "string", + "description": "Publish service port", + "default": "4503" + } + }, + "containerEnv": { + "AEM_LTS_FEATURE_DIR": "/aem-lts", + "PATH": "/aem-lts/bin:/aem-lts/dispatcher/bin:${PATH}" + }, + "mounts": [ + { + "source": "aem-lts-author-${devcontainerId}", + "target": "/aem-lts/author/crx-quickstart", + "type": "volume" + }, + { + "source": "aem-lts-publish-${devcontainerId}", + "target": "/aem-lts/publish/crx-quickstart", + "type": "volume" + } + ], + "installsAfter": [ + "ghcr.io/devcontainers/features/java:1" + ] +} \ No newline at end of file diff --git a/src/aem-lts/install.sh b/src/aem-lts/install.sh new file mode 100644 index 0000000..e663c6c --- /dev/null +++ b/src/aem-lts/install.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# create the feature directory +mkdir -p ${AEM_LTS_FEATURE_DIR} + +# save feature properties +propertiesFile="${AEM_LTS_FEATURE_DIR}/options.sh" +echo "AEM_LTS_QUICKSTART_JAR=\"${QUICKSTARTJAR}\"" >> ${propertiesFile} +echo "AEM_LTS_LICENSE_CUSTOMER_NAME=\"${LICENSECUSTOMERNAME}\"" >> ${propertiesFile} +echo "AEM_LTS_LICENSE_DOWNLOAD_ID=\"${LICENSEDOWNLOADID}\"" >> ${propertiesFile} +echo "AEM_LTS_AUTHOR_PORT=\"${AUTHORPORT:-'4502'}\"" >> ${propertiesFile} +echo "AEM_LTS_PUBLISH_PORT=\"${PUBLISHPORT:-'4503'}\"" >> ${propertiesFile} +source ${propertiesFile} + +# copy custom scripts +cp -r "$(dirname $0)/bin" ${AEM_LTS_FEATURE_DIR} diff --git a/test/aem-lts/defaults-with-quickstart.sh b/test/aem-lts/defaults-with-quickstart.sh new file mode 100644 index 0000000..84e17fd --- /dev/null +++ b/test/aem-lts/defaults-with-quickstart.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +set -e + +source dev-container-features-test-lib + +# Check options file created with defaults +source ${AEM_LTS_FEATURE_DIR}/options.sh +check "quickstartJar set" \ + echo "${AEM_LTS_QUICKSTART_JAR}" | grep -E "^/workspaces/[0-9]+/.devcontainer/mock-cq-quickstart-6\.6\.0\.jar$" +check "licenseCustomerName set" \ + [ "${AEM_LTS_LICENSE_CUSTOMER_NAME}" = "test-customer" ] +check "licenseDownloadId set" \ + [ "${AEM_LTS_LICENSE_DOWNLOAD_ID}" = "test-download-id" ] +check "author port default" \ + [ "${AEM_LTS_AUTHOR_PORT}" = "4502" ] +check "publish port default" \ + [ "${AEM_LTS_PUBLISH_PORT}" = "4503" ] +# Check aem-lts in PATH is executable +check "aem-lts is +x" \ + stat -c '%A' $(which aem-lts) | grep 'x.*x.*x' + +# Check that author installs, starts & stops correctly +check "author: install & start" \ + aem-lts start author +sleep 3 # give it time to start +check "author: check log for start message" \ + grep 'Server started on port 4502' /var/log/aem-author.log +check "author: compare pid file to java process" \ + [ $(cat /tmp/aem-author.pid) -eq $(pgrep -x java) ] +check "author: stop" \ + aem-lts stop author +sleep 3 # give it time to stop +check "author: pid file should be removed" \ + [ ! -f /tmp/aem-author.pid ] +check "author: no java process should be running" \ + [ -z $(pgrep -x java) ] + +# Check that publish installs, starts & stops correctly +check "publish: install & start" \ + aem-lts start publish +sleep 3 # give it time to start +check "publish: check log for start message" \ + grep 'Server started on port 4503' /var/log/aem-publish.log +check "publish: compare pid file to java process" \ + [ $(cat /tmp/aem-publish.pid) -eq $(pgrep -x java) ] +check "publish: stop" \ + aem-lts stop publish +sleep 3 # give it time to stop +check "publish: pid file should be removed" \ + [ ! -f /tmp/aem-publish.pid ] +check "publish: no java process should be running" \ + [ -z $(pgrep -x java) ] + +reportResults diff --git a/test/aem-lts/defaults-with-quickstart/mock-cq-quickstart-6.6.0.jar b/test/aem-lts/defaults-with-quickstart/mock-cq-quickstart-6.6.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..c8a375a92a164baf8b294507df71bf0bca145fb7 GIT binary patch literal 2275 zcmah~2{hDQ8~=|nV~njdjUoyo)Yw|cBpI5nPs~=ni7M&14yBoUIE0guoOF2>n&Ks5tE~}=+d}Z#k)q3 zmJhX+7Z>RsC(xYgLT*bon_oRSTy0U?&^d*ekm8Fh*=^YwrMs&3@lJ{d>cygV*z}P8 zLPy>CsfC^HY@zTJ8BAeXZ<=lz_N2%BP#q;;L^R@DRhX5ldRp{XNKTYW+P<=LlKQb( zhD$L{b>hk+1NmOZFA;gjpf&nPI$dV&&`439>UXmk*H-kW0|@aBFRVwEoa96};tj~G zL2FFA@C!|;;l>9|vKef-^6kvE&17Ue)k zFKnl#Gm@)H(pm8uU?~Tw{jNuo&6<^CZ)E6gTP|oLtK0~@lk9KBTSky8gmqVK*R*zA z#lT%-)JFJ17u_e_gYau`yJ>$mlva;pM_L{OZGCuRm@+`GiLTFJ`+Vks4nBhAf zHTyW9f-_WNU~k53KbU~N|5&lENCi(&4s>q2wKOkn_C1O@0m@+wECt?#ga7~o{$Epu{4Z1XbGG6fT%F}`h?oRafKx5R7(DwH0%~?--rXvC7lO4D%_C#s zG3Z;0XjH@331QCO+*S*%iLz&TgOfNO>)I-CAzfxXOX8(taU|<-RajT#@V74g{TEhO z-tPnycp~!uf{|fziFrBs)NV{gI;6c*)v4Lww;{6WrY(6tU6uGW5XcB{r#+4$R3u7OR2|bZid~Zw(2HZLwxpKa17VY25TmtlY&jQMLD= zcaP5HM+Z>i;$D~Y$A)%Inm4Hq`M#1iEV|!RoR2--yv^iH1uxVr)y3yXWNy1i-EAJb z+zYW_AA-7qVmbBANDdEZ6M09TbaU~tswwiZ(^T_|`r1Lg zA$^0P`6vlB25bF4Ed zD*2$qpHOzw3rLqtlbt@JKOliv|3bmV09m5SJr(InEGJCeg4WWSMiWx)J)^vM&i9VJ z+}o~(j(_M!wEy-|zb~vaU9~{}CgHvWM>h>jV#+(k?Q>VO3(Rz$tiEoF#`MQ4lnxc0 zs0nb>FudMXM1~kL?J43D#N3OQyv3#@R`8y5rA|kd+C63s|2ZilE` zj%kCYOc?4#CT)BgK==C&yhLOTuSneOHcDam+;feyrNVngJ12} z_U=pYk(Fp<-*GE$1;QVLZTE#tX0hP)J`piEBH9HzA_|>Oa3`KsRxo@K{zW+08i!G0 zeJ|Ip3rW5YxBF9m^GQytJRSZihk1U_tn^jZ7=OC>J?wsN>07u$S%NixRE3=y?dY1= z7j@VvJazY21%VTEIlyhxGmv+M`5haXiVLO-V2Yed!9 WSsM!obe$(GSW+MWU<"); + System.exit(2); + } + } + + static class MyHandler implements HttpHandler { + + @Override + public void handle(HttpExchange exchange) throws IOException { + + final var response = "hello,world"; + exchange.sendResponseHeaders(200, response.length()); + final var os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + + } +} diff --git a/test/aem-lts/mock-http-server/build.sh b/test/aem-lts/mock-http-server/build.sh new file mode 100755 index 0000000..62c9dbc --- /dev/null +++ b/test/aem-lts/mock-http-server/build.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +[ -f "../defaults-with-quickstart/mock-cq-quickstart-6.6.0.jar" ] \ + && rm "../defaults-with-quickstart/mock-cq-quickstart-6.6.0.jar" + +javac SimpleHttpServer.java +jar --create \ + --file mock-cq-quickstart-6.6.0.jar \ + --main-class=SimpleHttpServer *.class +rm *.class + +cp mock-cq-quickstart-6.6.0.jar ../defaults-with-quickstart/ +rm mock-cq-quickstart-6.6.0.jar \ No newline at end of file diff --git a/test/aem-lts/scenarios.json b/test/aem-lts/scenarios.json new file mode 100644 index 0000000..2eb4626 --- /dev/null +++ b/test/aem-lts/scenarios.json @@ -0,0 +1,17 @@ +{ + "defaults-with-quickstart": { + "image": "mcr.microsoft.com/devcontainers/base:debian", + "features": { + "ghcr.io/devcontainers/features/java:1": { + "version": "21", + "jdkDistro": "oracle", + "installMaven": true + }, + "aem-lts": { + "quickstartJar": "${containerWorkspaceFolder}/.devcontainer/mock-cq-quickstart-6.6.0.jar", + "licenseCustomerName": "test-customer", + "licenseDownloadId": "test-download-id" + } + } + } +} \ No newline at end of file diff --git a/test/aem-lts/test.sh b/test/aem-lts/test.sh new file mode 100644 index 0000000..6823a26 --- /dev/null +++ b/test/aem-lts/test.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# +# To run this test only, add the --skip-scenarios flag. +# +# devcontainer features test \ +# --features aem-sdk \ +# --skip-scenarios \ +# --base-image mcr.microsoft.com/devcontainers/base +# + +set -e + +# Optional: Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +# Feature-specific tests +# The 'check' command comes from the dev-container-features-test-lib. + +# Check options file created with defaults +source ${AEM_LTS_FEATURE_DIR}/options.sh +check "quickstartJar default" \ + [ -z "${AEM_LTS_QUICKSTART_JAR}" ] +check "licenseCustomerName default" \ + [ -z "${AEM_LTS_LICENSE_CUSTOMER_NAME}" ] +check "licenseDownloadId default" \ + [ -z "${AEM_LTS_LICENSE_DOWNLOAD_ID}" ] +check "authorPort default" \ + [ "${AEM_LTS_AUTHOR_PORT}" = "4502" ] +check "publishPort default" \ + [ "${AEM_LTS_PUBLISH_PORT}" = "4503" ] +# Check start-aem in PATH is executable +check "aem-lts is +x" \ + stat -c '%A' $(which aem-lts) | grep 'x.*x.*x' +# Check instance jars and dispatcher run script not installed +check "no author jar" \ + [ ! -f "${AEM_LTS_FEATURE_DIR}/author/${AEM_LTS_QUICKSTART_JAR}.jar" ] +check "no publish jar" \ + [ ! -f "${AEM_LTS_FEATURE_DIR}/publish/${AEM_LTS_QUICKSTART_JAR}.jar" ] + +# Report result +# If any of the checks above exited with a non-zero exit code, the test will fail. +reportResults From 7a5e4d96f09c5b680f35c8942e705b798d25705b Mon Sep 17 00:00:00 2001 From: Juan Ayala Date: Wed, 5 Nov 2025 12:48:44 -0600 Subject: [PATCH 3/3] Update root README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 2be5248..a9445aa 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ You will need to download the required packages from [Adobe's software distribut ![Software Distribution](software-distribution.png) +## [aem-lts](/src/aem-lts) +> Install and run AEM author and publish. You must have access to the AEM 6.5 LTS. It is only available through the Adobe software distribution site. This feature only facilitates the setup process. + ## [aem-sdk](/src/aem-sdk) > Install and run AEM author, publish and dispatcher. You must have access to the AEM SDK. It is only available through the Adobe software distribution site. This feature only facilitates the setup process. See: [Developing AEM Inside a Dev Container](https://theaemmaven.com/post/developing-aem-inside-a-dev-container)