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`); }