From fb3159ed35de0e4e2807354bea08699dafe82dde Mon Sep 17 00:00:00 2001 From: dmaurya929 Date: Mon, 9 Mar 2026 18:04:09 +0530 Subject: [PATCH 1/3] fix: clone functionality to include submodules --- .../src/index.js | 2 +- .../test/cloud-manager-client.test.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/spacecat-shared-cloud-manager-client/src/index.js b/packages/spacecat-shared-cloud-manager-client/src/index.js index 2b00c7849..b99b72e23 100644 --- a/packages/spacecat-shared-cloud-manager-client/src/index.js +++ b/packages/spacecat-shared-cloud-manager-client/src/index.js @@ -288,7 +288,7 @@ export default class CloudManagerClient { this.log.info(`Cloning CM repository: program=${programId}, repo=${repositoryId}, type=${repoType}`); const args = await this.#buildAuthGitArgs('clone', programId, repositoryId, { imsOrgId, repoType, repoUrl }); - this.#execGit([...args, clonePath]); + this.#execGit([...args, '--recurse-submodules', clonePath]); this.log.info(`Repository cloned to ${clonePath}`); this.#logTmpDiskUsage('clone'); diff --git a/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js b/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js index 84705f278..bfe62096f 100644 --- a/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js +++ b/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js @@ -286,6 +286,19 @@ describe('CloudManagerClient', () => { expect(cloneArgsStr).to.not.include('Bearer'); }); + it('includes --recurse-submodules in the clone arguments', async () => { + const client = CloudManagerClient.createFrom(createContext()); + + await client.clone( + TEST_PROGRAM_ID, + TEST_REPO_ID, + { imsOrgId: TEST_IMS_ORG_ID }, + ); + + const gitArgs = getGitArgs(execFileSyncStub.firstCall); + expect(gitArgs).to.include('--recurse-submodules'); + }); + it('throws when standard credentials not found for programId', async () => { const client = CloudManagerClient.createFrom( createContext({ CM_STANDARD_REPO_CREDENTIALS: TEST_STANDARD_CREDENTIALS }), From 2235092c67975f9aebc7f443403e7ac70960483b Mon Sep 17 00:00:00 2001 From: dmaurya929 Date: Mon, 13 Apr 2026 23:09:07 +0530 Subject: [PATCH 2/3] fix(cloud-manager-client): update git authentication header to use repo origin - Changed the construction of the git command's authentication header for standard repositories to use the repo origin instead of the full URL. This ensures the header is applied to all paths on the host, including submodule URLs. - Updated related test cases to reflect the new header format. --- packages/spacecat-shared-cloud-manager-client/src/index.js | 6 ++++-- .../test/cloud-manager-client.test.js | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/spacecat-shared-cloud-manager-client/src/index.js b/packages/spacecat-shared-cloud-manager-client/src/index.js index ba82f55c4..bad103da1 100644 --- a/packages/spacecat-shared-cloud-manager-client/src/index.js +++ b/packages/spacecat-shared-cloud-manager-client/src/index.js @@ -261,7 +261,8 @@ export default class CloudManagerClient { * Builds authenticated git arguments for a remote command (clone, push, or pull). * * Both repo types use http.extraheader for authentication: - * - Standard repos: Basic auth header via extraheader on the repo URL + * - Standard repos: Basic auth header via extraheader on the repo origin (scheme + host + '/'), + * so the header is applied to all paths on that host, including submodule URLs * - BYOG repos: Bearer token + API key + IMS org ID via extraheader on the CM Repo URL * * @param {string} command - The git command ('clone', 'push', or 'pull') @@ -277,8 +278,9 @@ export default class CloudManagerClient { if (repoType === CM_REPO_TYPE.STANDARD) { const credentials = this.#getStandardRepoCredentials(programId); const basicAuth = Buffer.from(credentials).toString('base64'); + const repoOrigin = `${new URL(repoUrl).origin}/`; return [ - '-c', `http.${repoUrl}.extraheader=Authorization: Basic ${basicAuth}`, + '-c', `http.${repoOrigin}.extraheader=Authorization: Basic ${basicAuth}`, command, repoUrl, ]; } diff --git a/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js b/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js index 8be397fe3..ef80dc1df 100644 --- a/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js +++ b/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js @@ -278,7 +278,7 @@ describe('CloudManagerClient', () => { const cloneArgs = getGitArgs(execFileSyncStub.firstCall); const cloneArgsStr = getGitArgsStr(execFileSyncStub.firstCall); expect(cloneArgs).to.include('clone'); - expect(cloneArgsStr).to.include(`http.${TEST_STANDARD_REPO_URL}.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw==`); + expect(cloneArgsStr).to.include('http.https://git.cloudmanager.adobe.com/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw=='); expect(cloneArgsStr).to.include(TEST_STANDARD_REPO_URL); expect(cloneArgs).to.include(EXPECTED_CLONE_PATH); // No credentials in the URL itself @@ -797,7 +797,7 @@ describe('CloudManagerClient', () => { const pushArgs = getGitArgs(execFileSyncStub.firstCall); const pushArgStr = getGitArgsStr(execFileSyncStub.firstCall); expect(pushArgStr).to.include('push'); - expect(pushArgStr).to.include(`http.${TEST_STANDARD_REPO_URL}.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw==`); + expect(pushArgStr).to.include('http.https://git.cloudmanager.adobe.com/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw=='); expect(pushArgStr).to.include(TEST_STANDARD_REPO_URL); expect(pushArgStr).to.not.include('stduser:stdtoken123@'); expect(pushArgStr).to.not.include('Bearer'); @@ -859,7 +859,7 @@ describe('CloudManagerClient', () => { const pullArgStr = getGitArgsStr(execFileSyncStub.firstCall); expect(pullArgStr).to.include('pull'); - expect(pullArgStr).to.include(`http.${TEST_STANDARD_REPO_URL}.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw==`); + expect(pullArgStr).to.include('http.https://git.cloudmanager.adobe.com/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw=='); expect(pullArgStr).to.include(TEST_STANDARD_REPO_URL); expect(pullArgStr).to.not.include('stduser:stdtoken123@'); expect(pullArgStr).to.not.include('Bearer'); From b999e36e904f263525a2b44d5c003c6d78d1ad08 Mon Sep 17 00:00:00 2001 From: dmaurya929 Date: Tue, 14 Apr 2026 16:49:06 +0530 Subject: [PATCH 3/3] fix(cloud-manager-client): refine git authentication header for standard repos --- .../spacecat-shared-cloud-manager-client/src/index.js | 11 +++++++---- .../test/cloud-manager-client.test.js | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/spacecat-shared-cloud-manager-client/src/index.js b/packages/spacecat-shared-cloud-manager-client/src/index.js index bad103da1..63d449be5 100644 --- a/packages/spacecat-shared-cloud-manager-client/src/index.js +++ b/packages/spacecat-shared-cloud-manager-client/src/index.js @@ -261,8 +261,9 @@ export default class CloudManagerClient { * Builds authenticated git arguments for a remote command (clone, push, or pull). * * Both repo types use http.extraheader for authentication: - * - Standard repos: Basic auth header via extraheader on the repo origin (scheme + host + '/'), - * so the header is applied to all paths on that host, including submodule URLs + * - Standard repos: Basic auth header via extraheader scoped to the org prefix + * (scheme + host + '/' + orgName + '/'), so the header covers all repos and submodules + * belonging to that customer org without granting access to other orgs on the same host * - BYOG repos: Bearer token + API key + IMS org ID via extraheader on the CM Repo URL * * @param {string} command - The git command ('clone', 'push', or 'pull') @@ -278,9 +279,11 @@ export default class CloudManagerClient { if (repoType === CM_REPO_TYPE.STANDARD) { const credentials = this.#getStandardRepoCredentials(programId); const basicAuth = Buffer.from(credentials).toString('base64'); - const repoOrigin = `${new URL(repoUrl).origin}/`; + const parsedUrl = new URL(repoUrl); + const orgName = parsedUrl.pathname.split('/')[1]; + const repoOrgPrefix = `${parsedUrl.origin}/${orgName}/`; return [ - '-c', `http.${repoOrigin}.extraheader=Authorization: Basic ${basicAuth}`, + '-c', `http.${repoOrgPrefix}.extraheader=Authorization: Basic ${basicAuth}`, command, repoUrl, ]; } diff --git a/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js b/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js index ef80dc1df..aea348a8a 100644 --- a/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js +++ b/packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js @@ -278,7 +278,7 @@ describe('CloudManagerClient', () => { const cloneArgs = getGitArgs(execFileSyncStub.firstCall); const cloneArgsStr = getGitArgsStr(execFileSyncStub.firstCall); expect(cloneArgs).to.include('clone'); - expect(cloneArgsStr).to.include('http.https://git.cloudmanager.adobe.com/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw=='); + expect(cloneArgsStr).to.include('http.https://git.cloudmanager.adobe.com/myorg/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw=='); expect(cloneArgsStr).to.include(TEST_STANDARD_REPO_URL); expect(cloneArgs).to.include(EXPECTED_CLONE_PATH); // No credentials in the URL itself @@ -797,7 +797,7 @@ describe('CloudManagerClient', () => { const pushArgs = getGitArgs(execFileSyncStub.firstCall); const pushArgStr = getGitArgsStr(execFileSyncStub.firstCall); expect(pushArgStr).to.include('push'); - expect(pushArgStr).to.include('http.https://git.cloudmanager.adobe.com/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw=='); + expect(pushArgStr).to.include('http.https://git.cloudmanager.adobe.com/myorg/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw=='); expect(pushArgStr).to.include(TEST_STANDARD_REPO_URL); expect(pushArgStr).to.not.include('stduser:stdtoken123@'); expect(pushArgStr).to.not.include('Bearer'); @@ -859,7 +859,7 @@ describe('CloudManagerClient', () => { const pullArgStr = getGitArgsStr(execFileSyncStub.firstCall); expect(pullArgStr).to.include('pull'); - expect(pullArgStr).to.include('http.https://git.cloudmanager.adobe.com/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw=='); + expect(pullArgStr).to.include('http.https://git.cloudmanager.adobe.com/myorg/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw=='); expect(pullArgStr).to.include(TEST_STANDARD_REPO_URL); expect(pullArgStr).to.not.include('stduser:stdtoken123@'); expect(pullArgStr).to.not.include('Bearer');