From d477d2c9589121e529b14c4a6115b686e1f8fee3 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 11 Aug 2023 09:14:56 +0200 Subject: [PATCH 1/6] Calculate and record uncommittedHash when creating a build --- node-src/git/git.ts | 23 +++++++++++++++++++++++ node-src/tasks/gitInfo.ts | 8 ++++++-- node-src/types.ts | 1 + 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/node-src/git/git.ts b/node-src/git/git.ts index c1db28f69..6e2e25618 100644 --- a/node-src/git/git.ts +++ b/node-src/git/git.ts @@ -90,6 +90,29 @@ export async function getBranch() { } } +// Retrieve the hash of all uncommitted files, which includes staged, unstaged, and untracked files, +// excluding deleted files (which can't be hashed) and ignored files. There is no one single Git +// command to reliably get this information, so we use a combination of commands grouped together. +export async function getUncommittedHash() { + const listStagedFiles = 'git diff --name-only --diff-filter=d --cached'; + const listUnstagedFiles = 'git diff --name-only --diff-filter=d'; + const listUntrackedFiles = 'git ls-files --others --exclude-standard'; + const listUncommittedFiles = [listStagedFiles, listUnstagedFiles, listUntrackedFiles].join(';'); + + const uncommittedHash = ( + await execGitCommand( + // Pass the combined list of filenames to hash-object to retrieve a list of hashes. Then pass + // the list of hashes to hash-object again to retrieve a single hash of all hashes. We use + // stdin to avoid the limit on command line arguments. + `(${listUncommittedFiles}) | git hash-object --stdin-paths | git hash-object --stdin` + ) + ).trim(); + + // In case there are no uncommited changes (empty list), we always get this same hash. + const noChangesHash = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'; + return uncommittedHash === noChangesHash ? '' : uncommittedHash; +} + export async function hasPreviousCommit() { const result = await execGitCommand(`git --no-pager log -n 1 --skip=1 --format="%H"`); return !!result.trim(); diff --git a/node-src/tasks/gitInfo.ts b/node-src/tasks/gitInfo.ts index 9bdcc08a0..317d2e62c 100644 --- a/node-src/tasks/gitInfo.ts +++ b/node-src/tasks/gitInfo.ts @@ -1,7 +1,7 @@ import picomatch from 'picomatch'; import getCommitAndBranch from '../git/getCommitAndBranch'; -import { getSlug, getVersion } from '../git/git'; +import { getSlug, getUncommittedHash, getVersion } from '../git/git'; import { getParentCommits } from '../git/getParentCommits'; import { getBaselineBuilds } from '../git/getBaselineBuilds'; import { exitCodes, setExitCode } from '../lib/setExitCode'; @@ -62,8 +62,12 @@ export const setGitInfo = async (ctx: Context, task: Task) => { } = ctx.options; ctx.git = { - version: await getVersion(), ...(await getCommitAndBranch(ctx, { branchName, patchBaseRef, ci })), + uncommittedHash: await getUncommittedHash().catch((e) => { + ctx.log.warn('Failed to retrieve uncommitted files hash', e); + return null; + }), + version: await getVersion(), }; if (!ctx.git.slug) { diff --git a/node-src/types.ts b/node-src/types.ts index 96219cb75..23b799c0d 100644 --- a/node-src/types.ts +++ b/node-src/types.ts @@ -144,6 +144,7 @@ export interface Context { committedAt: number; slug?: string; mergeCommit?: string; + uncommittedHash?: string; parentCommits?: string[]; baselineCommits?: string[]; changedFiles?: string[]; From 5889948dca153eae31585c58a72dcdd9fef7ab08 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 11 Aug 2023 17:09:15 +0200 Subject: [PATCH 2/6] Export getUncommittedHash as part of Node module --- node-src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/node-src/index.ts b/node-src/index.ts index 38d98f37a..3202a4b21 100644 --- a/node-src/index.ts +++ b/node-src/index.ts @@ -128,3 +128,5 @@ export async function getGitInfo(): Promise { return { branch, commit, slug: isValidSlug ? slug : '' }; } + +export { getUncommittedHash } from './git/git'; From d602ba6ef1048714d02af7ee617a5981de42e59d Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 11 Aug 2023 17:19:43 +0200 Subject: [PATCH 3/6] Include uncommittedHash in GitInfo --- node-src/index.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/node-src/index.ts b/node-src/index.ts index 3202a4b21..0e9212e87 100644 --- a/node-src/index.ts +++ b/node-src/index.ts @@ -14,7 +14,7 @@ import checkPackageJson from './lib/checkPackageJson'; import { writeChromaticDiagnostics } from './lib/writeChromaticDiagnostics'; import invalidPackageJson from './ui/messages/errors/invalidPackageJson'; import noPackageJson from './ui/messages/errors/noPackageJson'; -import { getBranch, getCommit, getSlug } from './git/git'; +import { getBranch, getCommit, getSlug, getUncommittedHash } from './git/git'; /** Make keys of `T` outside of `R` optional. @@ -112,11 +112,12 @@ export async function runAll(ctx, options?: Options) { } } -export type GitInfo = { +export interface GitInfo { branch: string; commit: string; slug: string; -}; + uncommittedHash: string; +} export async function getGitInfo(): Promise { const branch = await getBranch(); @@ -126,7 +127,6 @@ export async function getGitInfo(): Promise { const [ownerName, repoName, ...rest] = slug ? slug.split('/') : []; const isValidSlug = !!ownerName && !!repoName && !rest.length; - return { branch, commit, slug: isValidSlug ? slug : '' }; + const uncommittedHash = await getUncommittedHash(); + return { branch, commit, slug: isValidSlug ? slug : '', uncommittedHash }; } - -export { getUncommittedHash } from './git/git'; From 14264739d7b000493cd137e130e8c020bcf8707b Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Tue, 15 Aug 2023 12:29:49 +0200 Subject: [PATCH 4/6] Add mocks for uncommittedHash --- node-src/main.test.ts | 1 + node-src/tasks/gitInfo.test.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/node-src/main.test.ts b/node-src/main.test.ts index 425d4fbc2..e3de1d5a0 100644 --- a/node-src/main.test.ts +++ b/node-src/main.test.ts @@ -264,6 +264,7 @@ jest.mock('./git/git', () => ({ getVersion: () => Promise.resolve('2.24.1'), getChangedFiles: () => Promise.resolve(['src/foo.stories.js']), getRepositoryRoot: () => Promise.resolve(process.cwd()), + getUncommittedHash: () => Promise.resolve('abc123'), })); jest.mock('./git/getParentCommits', () => ({ diff --git a/node-src/tasks/gitInfo.test.ts b/node-src/tasks/gitInfo.test.ts index 8b658991a..a25a4fdaf 100644 --- a/node-src/tasks/gitInfo.test.ts +++ b/node-src/tasks/gitInfo.test.ts @@ -17,6 +17,9 @@ const getChangedFilesWithReplacement = < >getChangedFilesWithReplacementUnmocked; const getSlug = >git.getSlug; const getVersion = >git.getVersion; +const getUncommittedHash = >( + git.getUncommittedHash +); const getBaselineBuilds = >( getBaselineBuildsUnmocked @@ -42,6 +45,7 @@ const client = { runQuery: jest.fn(), setAuthorization: jest.fn() }; beforeEach(() => { getCommitAndBranch.mockResolvedValue(commitInfo); + getUncommittedHash.mockResolvedValue('abc123'); getParentCommits.mockResolvedValue(['asd2344']); getBaselineBuilds.mockResolvedValue([]); getChangedFilesWithReplacement.mockResolvedValue({ changedFiles: [] }); From 77c2815f8b05f2753867e2475df5f73a5e01bc2e Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Tue, 15 Aug 2023 14:57:58 +0200 Subject: [PATCH 5/6] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dc742ad19..eca9ed445 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chromatic", - "version": "6.21.0", + "version": "6.22.0-canary.4", "description": "Automate visual testing across browsers. Gather UI feedback. Versioned documentation.", "keywords": [ "storybook-addon", From 133fc738532434e3ea3b8d286a36c7f435b95999 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Tue, 15 Aug 2023 14:58:04 +0200 Subject: [PATCH 6/6] 6.22.0-canary.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eca9ed445..cf5fe93e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chromatic", - "version": "6.22.0-canary.4", + "version": "6.22.0-canary.5", "description": "Automate visual testing across browsers. Gather UI feedback. Versioned documentation.", "keywords": [ "storybook-addon",