How to properly handle Git submodules in cloud pipelines | Community
Skip to main content
Level 2
May 5, 2025
Solved

How to properly handle Git submodules in cloud pipelines

  • May 5, 2025
  • 4 replies
  • 1024 views

We are working on an AEM as a Cloud Service project using Azure DevOps Git as our repo, and our main AEM project includes a couple of Git submodules for shared components and clientlibs. Everything builds fine locally, but when deploying through Adobe Cloud Manager, the pipeline fails to recognize or fetch the submodules. TIA!

Best answer by SantoshSai

Well, because by default, Adobe Cloud Manager doesn’t automatically initialize or update submodules during build. That means, Out of the box, Cloud Manager doesn’t run git submodule update --init, so submodules won’t be pulled unless you handle it manually.

You can add a custom build hook script in your project repo to manually initialize and update submodules as part of the build process.

Here’s how:

  • In your project, create a file at .cloudmanager/hooks/pre-build.sh

  • Make sure it has the following content

    #!/bin/sh
    echo "Initializing Git submodules..."
    git submodule init
    git submodule update
  • Make the file executable:

    chmod +x .cloudmanager/hooks/pre-build.sh

    This script runs during the Cloud Manager pipeline execution (before the Maven build starts) and ensures submodules are pulled properly.

Make Sure .gitmodules and Submodule Commits Are Up to Date

  • Double-check that your .gitmodules file is committed

  • Ensure the submodule pointers are not pointing to local-only commits

  • Use HTTPS URLs for submodules if Cloud Manager needs to access private repos (and ensure credentials or access tokens are handled accordingly)

Below is one of my articles where I’ve provided a sample script along with a reference to Adobe’s official documentation:

https://techinnovia.com/syncing-customer-managed-azure-devops-repo-with-adobe-cloud-manager-repo/ 

https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/using-cloud-manager/managing-code/git-submodules

 

Hope that helps!

4 replies

SantoshSai
Community Advisor
Community Advisor
May 5, 2025

Hi @aadhirara

Could you please share the error log here so we can better understand the issue and provide a more accurate solution?

Santosh Sai
AadhiraRaAuthor
Level 2
May 5, 2025

Hi @santoshsai,

Please refer to it as below:

Step 4/12 : Checking out repository... Cloning into '/mnt/workspace/hprtc'... Submodule 'ui.apps/src/main/content/jcr_root/apps/shared/clientlibs' (https://dev.azure.com/hprtc/clientlibs.git) registered for path 'ui.apps/src/main/content/jcr_root/apps/shared/clientlibs' Cloning into '/mnt/workspace/hprtc/ui.apps/src/main/content/jcr_root/apps/shared/clientlibs'... Submodule 'ui.apps/src/main/content/jcr_root/apps/shared/clientlibs' (https://dev.azure.com/hprtc/clientlibs.git) registered for path 'ui.apps/src/main/content/jcr_root/apps/shared/clientlibs' fatal: No submodule mapping found in .gitmodules for path 'ui.apps/src/main/content/jcr_root/apps/shared/clientlibs' fatal: error while fetching submodule 'ui.apps/src/main/content/jcr_root/apps/shared/clientlibs' fatal: clone of 'https://dev.azure.com/hprtc/clientlibs.git' into submodule path '/mnt/workspace/hprtc/ui.apps/src/main/content/jcr_root/apps/shared/clientlibs' failed Failed to recurse into submodule path 'ui.apps/src/main/content/jcr_root/apps/shared/clientlibs' fatal: No submodule mapping found in .gitmodules for path 'ui.apps/src/main/content/jcr_root/apps/shared/clientlibs'
SantoshSai
Community Advisor
SantoshSaiCommunity AdvisorAccepted solution
Community Advisor
May 5, 2025

Well, because by default, Adobe Cloud Manager doesn’t automatically initialize or update submodules during build. That means, Out of the box, Cloud Manager doesn’t run git submodule update --init, so submodules won’t be pulled unless you handle it manually.

You can add a custom build hook script in your project repo to manually initialize and update submodules as part of the build process.

Here’s how:

  • In your project, create a file at .cloudmanager/hooks/pre-build.sh

  • Make sure it has the following content

    #!/bin/sh
    echo "Initializing Git submodules..."
    git submodule init
    git submodule update
  • Make the file executable:

    chmod +x .cloudmanager/hooks/pre-build.sh

    This script runs during the Cloud Manager pipeline execution (before the Maven build starts) and ensures submodules are pulled properly.

Make Sure .gitmodules and Submodule Commits Are Up to Date

  • Double-check that your .gitmodules file is committed

  • Ensure the submodule pointers are not pointing to local-only commits

  • Use HTTPS URLs for submodules if Cloud Manager needs to access private repos (and ensure credentials or access tokens are handled accordingly)

Below is one of my articles where I’ve provided a sample script along with a reference to Adobe’s official documentation:

https://techinnovia.com/syncing-customer-managed-azure-devops-repo-with-adobe-cloud-manager-repo/ 

https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/using-cloud-manager/managing-code/git-submodules

 

Hope that helps!

Santosh Sai
muskaanchandwani
Adobe Employee
Adobe Employee
May 5, 2025
AmitVishwakarma
Community Advisor
Community Advisor
May 6, 2025

Hi @aadhirara ,
Your Cloud Manager build fails with:

fatal: No submodule mapping found in .gitmodules for path ...

This means:

  - The .gitmodules file is missing or not properly committed.

  - Submodules are not initialized automatically by Cloud Manager.

Solution:

Step 1: Ensure .gitmodules is Correct & Committed

Your .gitmodules file must exist at the root of your main repo and look like:

[submodule "ui.apps/src/main/content/jcr_root/apps/shared/clientlibs"] path = ui.apps/src/main/content/jcr_root/apps/shared/clientlibs url = https://dev.azure.com/hprtc/clientlibs.git

Make sure it's committed:

git add .gitmodules git commit -m "Add submodule config"

Also commit the submodule reference:

git add ui.apps/src/main/content/jcr_root/apps/shared/clientlibs git commit -m "Add submodule folder"

Step 2: Create Cloud Manager Hook to Initialize Submodules

In your project repo, create the following structure:

.cloudmanager/ └── hooks/ └── pre-build.sh

File: .cloudmanager/hooks/pre-build.sh

#!/bin/sh echo "🔄 Initializing Git submodules..." git submodule init git submodule update --recursive

Make it executable:

chmod +x .cloudmanager/hooks/pre-build.sh

This script will be executed automatically during the Cloud Manager pipeline, before Maven build starts.


Step 3: Handle Authentication for Private Submodules (Optional)

If your submodules are from private Azure DevOps repos, Cloud Manager will need access via a Personal Access Token (PAT) or deploy key.

To use HTTPS + PAT:

Update the .gitmodules URL to include the token:

[submodule "ui.apps/src/..."] path = ... url = https://<username>:<pat_token>@dev.azure.com/...

Alternatively, inject a secure token via environment variables and use a .netrc file in the hook (more secure).

Reference:

https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/using-cloud-manager/managing-code/git-submodules

Regards,
Amit



Level 1
February 10, 2026

@AmitVishwakarma  ​@SantoshSai I am trying the above and trying to inject access token reading from cloudmanager variables and setting it up for external repo. However pre-build.sh is not recognized by cloudmanager it seems. 

 

have you come across this scenario ?

AmitVishwakarma
Community Advisor
Community Advisor
February 10, 2026

@AbhinavSi6 AEM Cloud Manager does not execute .cloudmanager/hooks/pre-build.sh for AEM as a Cloud Service pipelines. That’s why your script is “not recognized”, and even if it did run, it would not fix external-repo authentication or submodule clone issues, because those happen before any build script runs.

So the PAT‑from‑variables + pre-build.sh approach is unsupported and will never be reliable. You need to switch to one of the supported patterns below.

1. Why pre-build.sh is ignored:
For AEMaaCS, the documented build lifecycle is:

  • Cloud Manager spins up a build container.
  • It runs exactly these Maven commands and nothing else:
mvn --batch-mode org.apache.maven.plugins:maven-dependency-plugin:3.1.2:resolve-plugins
mvn --batch-mode org.apache.maven.plugins:maven-clean-plugin:3.1.0:clean -Dmaven.clean.failOnError=false
mvn --batch-mode org.jacoco:jacoco-maven-plugin:prepare-agent package

Build environment details: https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/using-cloud-manager/create-application-project/build-environment-details

There is no hook mechanism like .cloudmanager/hooks/pre-build.sh in the AEM Cloud Manager docs. Build customization is done via Maven profiles (activated by CM_BUILD env var), not shell hooks:

The .cloudmanager/hooks convention you saw in the forum thread is effectively a legacy / different-product pattern (Commerce Cloud / ece‑tools world), not something Cloud Manager for AEMaaCS supports. So your script is simply never called.

2. Even theoretically, it wouldn’t solve PAT/auth for external repos

The flow is:

  • Cloud Manager first clones the Git repo (and submodules) into the build container.
  • Only after that does Maven run inside that container.

Your problems:

  • External Azure DevOps repo needs a PAT.
  • Submodules also need credentials.
  • You tried to:
    • Store PAT in Cloud Manager variables,
    • Read them in pre-build.sh,
    • Do git submodule update / write .netrc etc.

But:

  • Git clone happens before any build step.
  • Pipeline variables are only available inside the build container for Maven, not to the outer git‑clone step.
  • So no hook or script in the repo can change how the initial clone/submodule fetch is authenticated.

Result: even if pre-build.sh did run, it would be too late.
3. Supported,  working” ways to handle submodules + external Git
Depending on what you actually want, there are two supported patterns:

3.1. Recommended: Cloud Manager Git + submodules (no PAT hacks)

Use Adobe’s Cloud Manager Git as the aggregation point and submodules between Cloud Manager repos only:

  1. Create CM repos for:
    • main AEM project
    • shared components / clientlibs, etc.
  2. Create a primary “aggregator” repo in CM that will be the pipeline source. At root:
    • <!-- pom.xml -->
      <project ...>
      <modelVersion>4.0.0</modelVersion>
      <groupId>your.group</groupId>
      <artifactId>project-reactor</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <packaging>pom</packaging>

      <modules>
      <module>main-project</module>
      <module>shared-clientlibs</module>
      </modules>
      </project>
  3. From that primary repo, add submodules using Cloud Manager Git URLs:
    • git submodule add -b main \
      https://git.cloudmanager.adobe.com/<org>/main-project/ \
      main-project

      git submodule add -b main \
      https://git.cloudmanager.adobe.com/<org>/shared-clientlibs/ \
      shared-clientlibs
    • Which produces .gitmodules like:
      [submodule "main-project"]
      path = main-project
      url = https://git.cloudmanager.adobe.com/<org>/main-project/
      branch = main

      [submodule "shared-clientlibs"]
      path = shared-clientlibs
      url = https://git.cloudmanager.adobe.com/<org>/shared-clientlibs/
      branch = main
  4. Commit .gitmodules plus the submodule folders, push, and point your Cloud Manager pipeline at this aggregator repo + branch.

Then Cloud Manager will:

  • Clone the primary repo,
  • See .gitmodules at root,
  • Automatically run git submodule update --init,
  • Build the multi-module Maven project.

No pre-build.sh, no PAT injection, no hacks. This is the officially documented pattern:

https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/using-cloud-manager/managing-code/git-submodules

 

If your source of truth is Azure DevOps, just sync/mirror those repos into CM Git (simple CI job), then use submodules between CM repos. That’s what most serious setups do.

 

3.2. BYOG (external repos) – let Cloud Manager manage the token

If you really must use an external repo (GitHub, GitLab, Bitbucket, Azure DevOps) as the pipeline source:

  1. Onboard that repo in Cloud Manager -> Repositories -> Add Repository -> Private/External Repository.
  2. Provide the required access token in the Cloud Manager UI:
kautuk_sahni
Community Manager
Community Manager
February 17, 2026

@AadhiraRa Quick follow-up! Were you able to get this issue sorted out? If you did, please consider posting the solution you used so others can learn from it. And if any of the replies above were helpful—whether they fully solved your problem or just pointed you toward the right fix—marking one as accepted helps future community members find solutions more easily. Closing the loop here makes a real difference for everyone.

Kautuk Sahni