Skip to content

Commit

Permalink
Merge pull request #193 from chromaui/record-repo-slug
Browse files Browse the repository at this point in the history
Record the repository slug (owner/repo) to support forks
  • Loading branch information
ghengeveld committed Jan 12, 2021
2 parents 0ad731d + ab0ffb8 commit c40a0db
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

- [233](https://github.com/chromaui/chromatic-cli/pull/233) Add `--branch-name` flag to override branch name
- [237](https://github.com/chromaui/chromatic-cli/pull/237) Avoid passing `--silent` when invoking npm through Node.js script
- [193](https://github.com/chromaui/chromatic-cli/pull/193) Record the repository slug (owner/repo) to support forks

# 5.5.0 - 2020-12-20

Expand Down
44 changes: 25 additions & 19 deletions bin/git/getCommitAndBranch.js
Expand Up @@ -62,33 +62,28 @@ export async function getCommitAndBranch({ branchName, patchBaseRef, ci, log } =
}
}

const { isCi, prBranch, branch: ciBranch, commit: ciCommit, slug } = envCi();

// On certain CI systems, a branch is not checked out
// (instead a detached head is used for the commit).
if (!notHead(branch)) {
const {
prBranch: prBranchFromEnvCi,
branch: branchFromEnvCi,
commit: commitFromEnvCi,
} = envCi();

commit = commitFromEnvCi;

// $HEAD is for netlify: https://www.netlify.com/docs/continuous-deployment/
// $GERRIT_BRANCH is for Gerrit/Jenkins: https://wiki.jenkins.io/display/JENKINS/Gerrit+Trigger
// $CI_BRANCH is a general setting that lots of systems use
commit = ciCommit;
branch =
notHead(prBranchFromEnvCi) ||
notHead(branchFromEnvCi) ||
notHead(process.env.HEAD) ||
notHead(process.env.GERRIT_BRANCH) ||
notHead(prBranch) ||
notHead(ciBranch) ||
notHead(process.env.HEAD) || // https://www.netlify.com/docs/continuous-deployment/
notHead(process.env.GERRIT_BRANCH) || // https://wiki.jenkins.io/display/JENKINS/Gerrit+Trigger
notHead(process.env.GITHUB_REF) || // https://docs.github.com/en/free-pro-team@latest/actions/reference/environment-variables#default-environment-variables
notHead(process.env.CI_BRANCH) ||
notHead(process.env.GITHUB_REF) ||
'HEAD';
}

// REPOSITORY_URL is for netlify: https://www.netlify.com/docs/continuous-deployment/
const fromCI =
!!ci || !!process.env.CI || !!process.env.REPOSITORY_URL || !!process.env.GITHUB_REPOSITORY;
isCi ||
!!ci ||
!!process.env.CI ||
!!process.env.REPOSITORY_URL || // https://www.netlify.com/docs/continuous-deployment/
!!process.env.GITHUB_REPOSITORY;

log.debug(
`git info: ${JSON.stringify({
Expand All @@ -97,9 +92,20 @@ export async function getCommitAndBranch({ branchName, patchBaseRef, ci, log } =
committerEmail,
committerName,
branch,
slug,
isTravisPrBuild,
fromCI,
})}`
);
return { commit, committedAt, committerEmail, committerName, branch, isTravisPrBuild, fromCI };

return {
commit,
committedAt,
committerEmail,
committerName,
branch,
slug,
isTravisPrBuild,
fromCI,
};
}
9 changes: 9 additions & 0 deletions bin/git/git.js
Expand Up @@ -68,6 +68,15 @@ export async function getVersion() {
return result.replace('git version ', '');
}

// The slug consists of the last two parts of the URL, at least for GitHub, GitLab and Bitbucket,
// and is typically followed by `.git`. The regex matches the last two parts between slashes, and
// ignores the `.git` suffix if it exists, so it matches something like `ownername/reponame`.
export async function getSlug() {
const result = await execGitCommand(`git config --get remote.origin.url`);
const [, slug] = result.match(/([^/:]+\/[^/]+?)(\.git)?$/) || [];
return slug;
}

// NOTE: At some point we should check that the commit has been pushed to the
// remote and the branch matches with origin/REF, but for now we are naive about
// adhoc builds.
Expand Down
18 changes: 17 additions & 1 deletion bin/git/git.test.js
@@ -1,18 +1,21 @@
/* eslint-disable jest/expect-expect */
import { exec } from 'child_process';
import execa from 'execa';
import process from 'process';
import tmp from 'tmp-promise';
import { promisify } from 'util';

import generateGitRepository from './generateGitRepository';
import { getBaselineCommits } from './git';
import { getBaselineCommits, getSlug } from './git';
import longLineDescription from './mocks/long-line';
import longLoopDescription from './mocks/long-loop';
import createMockIndex from './mocks/mock-index';
import simpleLoopDescription from './mocks/simple-loop';
import threeParentsDescription from './mocks/three-parents';
import twoRootsDescription from './mocks/two-roots';

const execaCommand = jest.spyOn(execa, 'command');

// Bumping up the Jest timeout for this file because it is timing out sometimes
// I think this just a bit of a slow file due to git stuff, takes ~2-3s on my computer.
jest.setTimeout(30 * 1000);
Expand Down Expand Up @@ -415,3 +418,16 @@ describe('getBaselineCommits', () => {
});
});
});

describe('getSlug', () => {
it('returns the slug portion of the git url', async () => {
execaCommand.mockImplementation(() => ({ all: 'git@github.com:chromaui/chromatic-cli.git' }));
expect(await getSlug()).toBe('chromaui/chromatic-cli');

execaCommand.mockImplementation(() => ({ all: 'https://github.com/chromaui/chromatic-cli' }));
expect(await getSlug()).toBe('chromaui/chromatic-cli');

execaCommand.mockImplementation(() => ({ all: 'https://gitlab.com/foo/bar.baz.git' }));
expect(await getSlug()).toBe('foo/bar.baz');
});
});
7 changes: 4 additions & 3 deletions bin/lib/logSerializers.test.js
@@ -1,14 +1,15 @@
/* eslint-disable jest/no-conditional-expect, jest/no-try-expect */
import { execSync } from 'child_process';

import { errorSerializer } from './logSerializers';

it('strips off envPairs', () => {
let err;
try {
execSync('some hot garbage');
} catch (err) {
expect(errorSerializer(err).envPairs).toBeUndefined();
} catch (e) {
err = e;
}
expect(errorSerializer(err).envPairs).toBeUndefined();
});

it('does not add random things to the error', () => {
Expand Down
1 change: 1 addition & 0 deletions bin/main.test.js
Expand Up @@ -136,6 +136,7 @@ jest.mock('./git/git', () => ({
}),
getBranch: () => 'branch',
getBaselineCommits: () => ['baseline'],
getSlug: () => 'user/repo',
getVersion: () => '2.24.1',
}));

Expand Down
17 changes: 11 additions & 6 deletions bin/tasks/gitInfo.js
@@ -1,7 +1,7 @@
import picomatch from 'picomatch';

import { getCommitAndBranch } from '../git/getCommitAndBranch';
import { getBaselineCommits, getVersion } from '../git/git';
import { getBaselineCommits, getSlug, getVersion } from '../git/git';
import { createTask, transitionTo } from '../lib/tasks';
import {
initial,
Expand All @@ -19,16 +19,21 @@ const TesterSkipBuildMutation = `
`;

export const setGitInfo = async (ctx, task) => {
const { branchName, patchBaseRef, fromCI, ignoreLastBuildOnBranch, skip } = ctx.options;

ctx.git = await getCommitAndBranch({ branchName, patchBaseRef, ci: fromCI, log: ctx.log });
const { branchName, patchBaseRef, fromCI: ci } = ctx.options;
ctx.git = await getCommitAndBranch({ branchName, patchBaseRef, ci, log: ctx.log });
ctx.git.slug = ctx.git.slug || (await getSlug());
ctx.git.version = await getVersion();

if (ctx.options.ownerName) {
ctx.git.slug = ctx.git.slug.replace(/[^/]+/, ctx.options.ownerName);
}

const { branch, commit } = ctx.git;

const matchesBranch = (glob) => (glob && glob.length ? picomatch(glob)(branch) : !!glob);
ctx.git.matchesBranch = matchesBranch;

if (matchesBranch(skip)) {
if (matchesBranch(ctx.options.skip)) {
transitionTo(skippingBuild)(ctx, task);
if (await ctx.client.runQuery(TesterSkipBuildMutation, { commit })) {
ctx.skip = true;
Expand All @@ -39,7 +44,7 @@ export const setGitInfo = async (ctx, task) => {

const baselineCommits = await getBaselineCommits(ctx, {
branch,
ignoreLastBuildOnBranch: matchesBranch(ignoreLastBuildOnBranch),
ignoreLastBuildOnBranch: matchesBranch(ctx.options.ignoreLastBuildOnBranch),
});
ctx.git.baselineCommits = baselineCommits;
ctx.log.debug(`Found baselineCommits: ${baselineCommits}`);
Expand Down
20 changes: 19 additions & 1 deletion bin/tasks/gitInfo.test.js
@@ -1,5 +1,5 @@
import { getCommitAndBranch } from '../git/getCommitAndBranch';
import { getBaselineCommits, getVersion } from '../git/git';
import { getBaselineCommits, getSlug, getVersion } from '../git/git';
import { setGitInfo } from './gitInfo';

jest.mock('../git/getCommitAndBranch');
Expand All @@ -12,13 +12,31 @@ describe('setGitInfo', () => {
getCommitAndBranch.mockReturnValue({ commit: '123asdf', branch: 'something' });
getBaselineCommits.mockReturnValue(['asd2344']);
getVersion.mockReturnValue('Git v1.0.0');
getSlug.mockReturnValue('user/repo');
const ctx = { log, options: {} };
await setGitInfo(ctx, {});
expect(ctx.git).toMatchObject({
commit: '123asdf',
branch: 'something',
baselineCommits: ['asd2344'],
version: 'Git v1.0.0',
slug: 'user/repo',
});
});

it('supports overriding the owner name in the slug', async () => {
getCommitAndBranch.mockReturnValue({ commit: '123asdf', branch: 'something' });
getBaselineCommits.mockReturnValue(['asd2344']);
getVersion.mockReturnValue('Git v1.0.0');
getSlug.mockReturnValue('user/repo');
const ctx = { log, options: { ownerName: 'org' } };
await setGitInfo(ctx, {});
expect(ctx.git).toMatchObject({
commit: '123asdf',
branch: 'something',
baselineCommits: ['asd2344'],
version: 'Git v1.0.0',
slug: 'org/repo',
});
});
});
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "chromatic",
"version": "5.5.1-dev.1",
"version": "5.6.0-alpha.0",
"description": "Visual Testing for Storybook",
"homepage": "https://www.chromatic.com",
"bugs": {
Expand Down

0 comments on commit c40a0db

Please sign in to comment.