Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record the repository slug to support builds from forks #193

Merged
merged 14 commits into from Jan 12, 2021
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,7 @@
# Unreleased

- Record the repository slug (owner/repo) to support forks.

# 5.5.0 - 2020-12-20

- [212](https://github.com/chromaui/chromatic-cli/pull/212) Add support for monorepo using a new `path` argument
Expand Down
43 changes: 24 additions & 19 deletions bin/git/getCommitAndBranch.js
Expand Up @@ -67,46 +67,51 @@ export async function getCommitAndBranch({ patchBaseRef, inputFromCI, 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) ||
notHead(branch) ||
'HEAD';
}
// REPOSITORY_URL is for netlify: https://www.netlify.com/docs/continuous-deployment/

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

log.debug(
`git info: ${JSON.stringify({
commit,
committedAt,
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
3 changes: 2 additions & 1 deletion 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 @@ -22,6 +22,7 @@ export const setGitInfo = async (ctx, task) => {
const { patchBaseRef, fromCI, ignoreLastBuildOnBranch, skip } = ctx.options;

ctx.git = await getCommitAndBranch({ patchBaseRef, inputFromCI: fromCI, log: ctx.log });
ctx.git.slug = ctx.git.slug || (await getSlug());
ctx.git.version = await getVersion();
const { branch, commit } = ctx.git;

Expand Down
3 changes: 2 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 @@ -11,6 +11,7 @@ describe('setGitInfo', () => {
it('sets the git info on context', async () => {
getCommitAndBranch.mockReturnValue({ commit: '123asdf', branch: 'something' });
getBaselineCommits.mockReturnValue(['asd2344']);
getSlug.mockReturnValue('user/repo');
getVersion.mockReturnValue('Git v1.0.0');
const ctx = { log, options: {} };
await setGitInfo(ctx, {});
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "chromatic",
"version": "5.5.0",
"version": "5.6.0-alpha.0",
"description": "Visual Testing for Storybook",
"homepage": "https://www.chromatic.com",
"bugs": {
Expand Down