diff --git a/bin/git/git.js b/bin/git/git.js index f0eca6320..78635f2bd 100644 --- a/bin/git/git.js +++ b/bin/git/git.js @@ -281,7 +281,7 @@ export async function getBaselineCommits( } } - // Add the most recent build on a (merged) branch if as a (potential) baseline if we think + // Add the most recent build on a (merged) branch as a (potential) baseline if we think // this commit was the commit that merged the PR. // @see https://www.chromatic.com/docs/branching-and-baselines#squash-and-rebase-merging if (pullRequest && pullRequest.lastHeadBuild) { diff --git a/bin/tasks/gitInfo.js b/bin/tasks/gitInfo.js index 6d9bfa6cf..bf1ac75ef 100644 --- a/bin/tasks/gitInfo.js +++ b/bin/tasks/gitInfo.js @@ -7,8 +7,9 @@ import { initial, pending, skipFailed, - skippedForCommit, skippingBuild, + skippedForCommit, + skippedRebuild, success, } from '../ui/tasks/gitInfo'; @@ -18,6 +19,17 @@ const TesterSkipBuildMutation = ` } `; +const TesterLastBuildQuery = ` + query TesterLastBuildQuery($commit: String!, $branch: String!) { + app { + lastBuild(ref: $commit, branch: $branch) { + id + status(legacy: false) + } + } + } +`; + export const setGitInfo = async (ctx, task) => { const { branchName, patchBaseRef, fromCI: ci } = ctx.options; ctx.git = await getCommitAndBranch({ branchName, patchBaseRef, ci, log: ctx.log }); @@ -37,6 +49,7 @@ export const setGitInfo = async (ctx, task) => { if (matchesBranch(ctx.options.skip)) { transitionTo(skippingBuild)(ctx, task); + // The SkipBuildMutation ensures the commit is tagged properly. if (await ctx.client.runQuery(TesterSkipBuildMutation, { commit })) { ctx.skip = true; return transitionTo(skippedForCommit, true)(ctx, task); @@ -51,6 +64,18 @@ export const setGitInfo = async (ctx, task) => { ctx.git.baselineCommits = baselineCommits; ctx.log.debug(`Found baselineCommits: ${baselineCommits}`); + // If the sole baseline is the most recent ancestor, then this is likely a rebuild (rerun of CI job). + // If the MRA is all green, there's no need to rerun the build, we just want the CLI to exit 0 so the CI job succeeds. + // This is especially relevant for (unlinked) projects that don't use --exit-zero-on-changes. + // There's no need for a SkipBuildMutation because we don't have to tag the commit again. + if (baselineCommits.length === 1 && baselineCommits[0] === commit) { + const mostRecentAncestor = await ctx.client.runQuery(TesterLastBuildQuery, { commit, branch }); + if (mostRecentAncestor && ['PASSED', 'ACCEPTED'].includes(mostRecentAncestor.status)) { + ctx.skip = true; + return transitionTo(skippedRebuild, true)(ctx, task); + } + } + return transitionTo(success, true)(ctx, task); }; diff --git a/bin/ui/tasks/gitInfo.js b/bin/ui/tasks/gitInfo.js index a51a67df8..3645b11f8 100644 --- a/bin/ui/tasks/gitInfo.js +++ b/bin/ui/tasks/gitInfo.js @@ -30,6 +30,12 @@ export const skippedForCommit = (ctx) => ({ output: `Skipped build for commit ${ctx.git.commit.substr(0, 7)} due to --skip`, }); +export const skippedRebuild = (ctx) => ({ + status: 'success', + title: 'Skipping build', + output: `Skipped rebuild of an already fully passed/accepted build`, +}); + export const skipFailed = (ctx) => ({ status: 'error', title: 'Skipping build', diff --git a/bin/ui/tasks/gitInfo.stories.js b/bin/ui/tasks/gitInfo.stories.js index d3a9326dd..e15328b4e 100644 --- a/bin/ui/tasks/gitInfo.stories.js +++ b/bin/ui/tasks/gitInfo.stories.js @@ -1,5 +1,13 @@ import task from '../components/task'; -import { initial, pending, skipFailed, skippedForCommit, skippingBuild, success } from './gitInfo'; +import { + initial, + pending, + skipFailed, + skippedForCommit, + skippedRebuild, + skippingBuild, + success, +} from './gitInfo'; export default { title: 'CLI/Tasks/GitInfo', @@ -15,4 +23,5 @@ export const Success = () => success({ git, options }); export const NoBaselines = () => success({ git: { ...git, baselineCommits: [] }, options }); export const Skipping = () => skippingBuild({ git }); export const Skipped = () => skippedForCommit({ git }); +export const SkippedRebuild = () => skippedRebuild(); export const SkipFailed = () => skipFailed();