Skip to content

Commit cb6fa28

Browse files
bisswangerRamon Bisswangerclaude
authored
fix(cloud-manager-client): pass ref as last pull argument (#1790)
## Summary - `pull()` checks out the requested `ref` locally before running `git pull`, but never told `git pull` which branch to fetch/merge — so `git pull <url>` fell back to fetching and merging the remote's default branch (its HEAD) into the checked-out branch. - This could silently diverge from, or conflict with, the branch the caller actually wanted to update. - Fix: append `ref` as the last argument to the pull git args (mirrors how `push()` already appends `ref`), so `git pull <url> <ref>` fetches and merges the correct branch. ## Test plan - [x] Added/updated unit tests in `packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js` covering: ref appended as last arg for BYOG and standard repos, and no ref arg appended when `ref` is omitted. - [x] `npm test -w packages/spacecat-shared-cloud-manager-client` — 87 passing, 100% coverage. - [x] `npm run lint -w packages/spacecat-shared-cloud-manager-client` — clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Ramon Bisswanger <bisswang@adobe.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent c50606e commit cb6fa28

2 files changed

Lines changed: 72 additions & 1 deletion

File tree

packages/spacecat-shared-cloud-manager-client/src/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,13 @@ export default class CloudManagerClient {
909909
}
910910

911911
const pullArgs = await this.#buildAuthGitArgs('pull', programId, repositoryId, { imsOrgId, repoType, repoUrl });
912+
// Explicitly pass the ref as the last argument so `git pull` fetches and
913+
// merges that branch. Without it, `git pull <url>` merges the remote's
914+
// default branch (its HEAD) into the checked-out branch, which can
915+
// conflict with — or silently diverge from — the branch we actually want.
916+
if (hasText(ref)) {
917+
pullArgs.push(ref);
918+
}
912919
// Always pull the parent only — never `--recurse-submodules` — so a
913920
// submodule failure can't take down the parent pull. Submodules are
914921
// populated below in a path whose errors are caught and logged.

packages/spacecat-shared-cloud-manager-client/test/cloud-manager-client.test.js

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1393,12 +1393,76 @@ describe('CloudManagerClient', () => {
13931393
expect(checkoutArgStr).to.include('checkout');
13941394
expect(checkoutArgStr).to.include('feature/my-branch');
13951395

1396-
// Second call: pull
1396+
// Second call: pull, with the ref appended as the last argument so
1397+
// git pulls (fetches + merges) that branch instead of the remote's
1398+
// default branch.
1399+
const pullArgs = getGitArgs(execFileSyncStub.secondCall);
1400+
expect(pullArgs[pullArgs.length - 1]).to.equal('feature/my-branch');
13971401
const pullArgStr = getGitArgsStr(execFileSyncStub.secondCall);
13981402
expect(pullArgStr).to.include('pull');
13991403
expect(pullArgStr).to.include(`Authorization: Bearer ${TEST_TOKEN}`);
14001404
});
14011405

1406+
it('appends the ref as the last pull argument for a BYOG repo', async () => {
1407+
const client = CloudManagerClient.createFrom(createContext());
1408+
1409+
await client.pull(
1410+
'/tmp/cm-repo-test',
1411+
TEST_PROGRAM_ID,
1412+
TEST_REPO_ID,
1413+
{ imsOrgId: TEST_IMS_ORG_ID, ref: 'release/5.11' },
1414+
);
1415+
1416+
expect(execFileSyncStub).to.have.been.calledTwice;
1417+
const pullArgs = getGitArgs(execFileSyncStub.secondCall);
1418+
expect(pullArgs).to.deep.equal([
1419+
'-c', `http.${TEST_ENV.CM_REPO_URL}.extraheader=Authorization: Bearer ${TEST_TOKEN}`,
1420+
'-c', `http.${TEST_ENV.CM_REPO_URL}.extraheader=x-api-key: test-client-id`,
1421+
'-c', `http.${TEST_ENV.CM_REPO_URL}.extraheader=x-gw-ims-org-id: ${TEST_IMS_ORG_ID}`,
1422+
'pull',
1423+
`${TEST_ENV.CM_REPO_URL}/api/program/${TEST_PROGRAM_ID}/repository/${TEST_REPO_ID}.git`,
1424+
'release/5.11',
1425+
]);
1426+
});
1427+
1428+
it('appends the ref as the last pull argument for a standard repo', async () => {
1429+
const client = CloudManagerClient.createFrom(
1430+
createContext({ CM_STANDARD_REPO_CREDENTIALS: TEST_STANDARD_CREDENTIALS }),
1431+
);
1432+
1433+
await client.pull(
1434+
'/tmp/cm-repo-test',
1435+
TEST_PROGRAM_ID,
1436+
TEST_REPO_ID,
1437+
{ repoType: 'standard', repoUrl: TEST_STANDARD_REPO_URL, ref: 'main' },
1438+
);
1439+
1440+
expect(execFileSyncStub).to.have.been.calledTwice;
1441+
const pullArgs = getGitArgs(execFileSyncStub.secondCall);
1442+
expect(pullArgs).to.deep.equal([
1443+
'-c', 'http.https://git.cloudmanager.adobe.com/myorg/.extraheader=Authorization: Basic c3RkdXNlcjpzdGR0b2tlbjEyMw==',
1444+
'pull',
1445+
TEST_STANDARD_REPO_URL,
1446+
'main',
1447+
]);
1448+
});
1449+
1450+
it('does not append a ref argument to pull when ref is not provided', async () => {
1451+
const client = CloudManagerClient.createFrom(createContext());
1452+
1453+
await client.pull(
1454+
'/tmp/cm-repo-test',
1455+
TEST_PROGRAM_ID,
1456+
TEST_REPO_ID,
1457+
{ imsOrgId: TEST_IMS_ORG_ID },
1458+
);
1459+
1460+
expect(execFileSyncStub).to.have.been.calledOnce;
1461+
const pullArgs = getGitArgs(execFileSyncStub.firstCall);
1462+
expect(pullArgs[pullArgs.length - 1])
1463+
.to.equal(`${TEST_ENV.CM_REPO_URL}/api/program/${TEST_PROGRAM_ID}/repository/${TEST_REPO_ID}.git`);
1464+
});
1465+
14021466
it('skips checkout when ref is not provided', async () => {
14031467
const client = CloudManagerClient.createFrom(createContext());
14041468

0 commit comments

Comments
 (0)