From e4594ef2d8943bea9564daa5d620deedfffb9933 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:17:57 +0000 Subject: [PATCH] ci: add fallback logic for downstream branch resolution in the CLI and CDK update action (#63678) In some cases, such as when a new exceptional minor branch is created. The branch may not exist in the downstream repo. For example, during an exceptional minor release (e.g. FW 20.3.x and Components: 20.2.x). This commit introduces a fallback mechanism to handle such scenarios. If the current branch is not found in the downstream repository, the script will now fall back to the last known branch from the build information. This ensures that the files can be updated properly. PR Close #63678 --- adev/scripts/shared/copy-json-assets.mjs | 117 ++++++++++++++--------- 1 file changed, 71 insertions(+), 46 deletions(-) diff --git a/adev/scripts/shared/copy-json-assets.mjs b/adev/scripts/shared/copy-json-assets.mjs index ef62017b2ee..139e9f9736b 100644 --- a/adev/scripts/shared/copy-json-assets.mjs +++ b/adev/scripts/shared/copy-json-assets.mjs @@ -7,6 +7,7 @@ */ //tslint:disable:no-console +import assert from 'node:assert'; import {execSync} from 'node:child_process'; import {existsSync, constants as fsConstants} from 'node:fs'; import {copyFile, mkdtemp, readdir, readFile, realpath, unlink, writeFile} from 'node:fs/promises'; @@ -19,72 +20,96 @@ export async function copyJsonAssets({repo, githubApi, assetsPath, destPath}) { throw new Error(`${buildInfoPath} does not exist.`); } - const branch = process.env.GITHUB_REF; - const {sha: currentSha} = JSON.parse(await readFile(buildInfoPath, 'utf-8')); - const latestSha = await githubApi.getShaForBranch(branch); + assert(process.env.GITHUB_REF); + const currentBranch = process.env.GITHUB_REF; - console.log(`Comparing ${currentSha}...${latestSha}.`); - const affectedFiles = await githubApi.getAffectedFiles(currentSha, latestSha); - const changedFiles = affectedFiles.filter((file) => file.startsWith(`${assetsPath}/`)); - - if (changedFiles.length === 0) { - console.log(`No '${assetsPath}/**' files changed between ${currentSha} and ${latestSha}.`); - return; - } - - console.log( - `The below files changed between ${currentSha} and ${latestSha}:\n` + - changedFiles.map((f) => '* ' + f).join('\n'), + const {sha: storedSha, branchName: storedBranch} = JSON.parse( + await readFile(buildInfoPath, 'utf-8'), ); - const temporaryDir = await realpath(await mkdtemp(join(tmpdir(), 'copy-json-assets-'))); - const execOptions = {cwd: temporaryDir, stdio: 'inherit'}; - execSync('git init', execOptions); - execSync(`git remote add origin https://github.com/${repo}.git`, execOptions); - // fetch a commit - execSync(`git fetch origin ${latestSha}`, execOptions); - // reset this repository's main branch to the commit of interest - execSync('git reset --hard FETCH_HEAD', execOptions); - // get sha when files where changed - const shaWhenFilesChanged = execSync(`git rev-list -1 ${latestSha} "${assetsPath}/"`, { - encoding: 'utf8', - cwd: temporaryDir, - stdio: ['ignore', 'pipe', 'ignore'], - }).trim(); + let downstreamBranch; + let latestSha; + try { + latestSha = await githubApi.getShaForBranch(currentBranch); + downstreamBranch = currentBranch; + } catch (e) { + // In some cases, such as when a new branch is created for a feature, + // the branch may not exist in the downstream repo. For example, during an + // exceptional minor release (e.g. FW 20.3.x and Components: 20.2.x). + // In such scenarios, we fallback to the last known branch. + if (currentBranch !== 'refs/heads/main' && currentBranch !== storedBranch) { + latestSha = await githubApi.getShaForBranch(storedBranch); + downstreamBranch = storedBranch; + } else { + throw e; + } + } - // Delete existing asset files. - const apiFilesUnlink = (await readdir(destPath)) - .filter((f) => f.endsWith('.json')) - .map((f) => unlink(join(destPath, f))); + console.log(`Comparing ${storedSha}...${latestSha}.`); + const affectedFiles = await githubApi.getAffectedFiles(storedSha, latestSha); + const changedFiles = affectedFiles.filter((file) => file.startsWith(`${assetsPath}/`)); - await Promise.allSettled(apiFilesUnlink); + let shaWhenFilesChanged; + if (changedFiles.length > 0) { + console.log( + `The below files changed between ${storedSha} and ${latestSha}:\n` + + changedFiles.map((f) => '* ' + f).join('\n'), + ); - // Copy new asset files - const tempAssetsDir = join(temporaryDir, assetsPath); - const assetFilesCopy = (await readdir(tempAssetsDir)).map((f) => { - const src = join(tempAssetsDir, f); - const dest = join(destPath, f); + const temporaryDir = await realpath(await mkdtemp(join(tmpdir(), 'copy-json-assets-'))); + const execOptions = {cwd: temporaryDir, stdio: 'inherit'}; + execSync('git init', execOptions); + execSync(`git remote add origin https://github.com/${repo}.git`, execOptions); + // fetch a commit + execSync(`git fetch origin ${latestSha}`, execOptions); + // reset this repository's main branch to the commit of interest + execSync('git reset --hard FETCH_HEAD', execOptions); + // get sha when files where changed + shaWhenFilesChanged = execSync(`git rev-list -1 ${latestSha} "${assetsPath}/"`, { + encoding: 'utf8', + cwd: temporaryDir, + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); - return copyFile(src, dest, fsConstants.COPYFILE_FICLONE); - }); + // Delete existing asset files. + const apiFilesUnlink = (await readdir(destPath)) + .filter((f) => f.endsWith('.json')) + .map((f) => unlink(join(destPath, f))); - await Promise.allSettled(assetFilesCopy); + await Promise.allSettled(apiFilesUnlink); + + // Copy new asset files + const tempAssetsDir = join(temporaryDir, assetsPath); + const assetFilesCopy = (await readdir(tempAssetsDir)).map((f) => { + const src = join(tempAssetsDir, f); + const dest = join(destPath, f); + + return copyFile(src, dest, fsConstants.COPYFILE_FICLONE); + }); + + await Promise.allSettled(assetFilesCopy); + + console.log(`Successfully updated asset files in '${destPath}'.\n`); + } else { + console.log(`No '${assetsPath}/**' files changed between ${storedSha} and ${latestSha}.`); + } // Write SHA to file. await writeFile( buildInfoPath, JSON.stringify( { - branchName: branch, - sha: shaWhenFilesChanged, + branchName: downstreamBranch, + sha: shaWhenFilesChanged ?? storedSha, }, undefined, 2, ), ); + // The below command will show uncommitted changes. + // This is expected because the framework repo and component/cli might have different minors + // (e.g. during exceptional minor releases), leading to changes that need to be committed. console.log('\nChanges: '); execSync(`git status --porcelain`, {stdio: 'inherit'}); - - console.log(`Successfully updated asset files in '${destPath}'.\n`); }