Skip to content

Commit

Permalink
Add git config user.email and user.name
Browse files Browse the repository at this point in the history
WIP
  • Loading branch information
sorenlouv committed Feb 21, 2022
1 parent baa0ff7 commit 86a608d
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 58 deletions.
2 changes: 2 additions & 0 deletions src/options/ConfigOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type Options = Partial<{
gitHostname: string;
githubApiBaseUrlV3: string;
githubApiBaseUrlV4: string;
gitUserEmail: string;
gitUserName: string;
logFilePath: string;
maxNumber: number;
multiple: boolean;
Expand Down
4 changes: 2 additions & 2 deletions src/runSequentially.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { logger, consoleLog } from './services/logger';
import { sequentially } from './services/sequentially';
import { Commit } from './services/sourceCommit/parseSourceCommit';
import { cherrypickAndCreateTargetPullRequest } from './ui/cherrypickAndCreateTargetPullRequest';
import { maybeSetupRepo } from './ui/maybeSetupRepo';
import { setupRepo } from './ui/setupRepo';

export type Result =
| {
Expand Down Expand Up @@ -32,7 +32,7 @@ export async function runSequentially({
targetBranches: string[];
}): Promise<Result[]> {
logger.verbose('Backport options', options);
await maybeSetupRepo(options);
await setupRepo(options);
const results = [] as Result[];
await sequentially(targetBranches, async (targetBranch) => {
logger.info(`Backporting ${JSON.stringify(commits)} to ${targetBranch}`);
Expand Down
18 changes: 17 additions & 1 deletion src/services/HandledError.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import dedent from 'dedent';
import { getGlobalConfigPath } from './env';
import { Commit } from './sourceCommit/parseSourceCommit';

type ErrorContext =
Expand All @@ -9,7 +11,7 @@ type ErrorContext =
}[];
}
| {
code: 'no-branches-exception' | 'abort-exception';
code: 'no-branches-exception' | 'abort-exception' | 'missing-git-config';
};

function getMessage(errorContext: ErrorContext | string): string {
Expand All @@ -24,6 +26,20 @@ function getMessage(errorContext: ErrorContext | string): string {
return 'There are no branches to backport to. Aborting.';
case 'abort-exception':
return 'Aborted';
case 'missing-git-config':
return dedent(`*** Please tell me who you are.
Run
git config user.name "Your Name"
git config user.email "you@example.com"
Or add it to ${getGlobalConfigPath()}
{
"accessToken": "***",
"gitUserName": "Your Name",
"gitUserEmail": "you@example.com"
}`);
}
}

Expand Down
54 changes: 40 additions & 14 deletions src/services/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { HandledError } from './HandledError';
import { execAsCallback, exec } from './child-process-promisified';
import { getRepoPath } from './env';
import { getShortSha } from './github/commitFormatters';
import { getRepoOwnerAndNameFromGitRemotes } from './github/v4/getRepoOwnerAndNameFromGitRemotes';
import { logger } from './logger';
import { ExpectedTargetPullRequest } from './sourceCommit/getExpectedTargetPullRequests';
import { Commit } from './sourceCommit/parseSourceCommit';
Expand Down Expand Up @@ -130,7 +129,7 @@ export async function getRepoInfoFromGitRemotes({ cwd }: { cwd: string }) {
}
}

export async function getGitProjectRoot(dir: string) {
export async function getGitProjectRootPath(dir: string) {
try {
const { stdout } = await exec('git rev-parse --show-toplevel', {
cwd: dir,
Expand Down Expand Up @@ -422,6 +421,38 @@ export async function setCommitAuthor(
}
}

export async function getGitConfig({
dir,
key,
}: {
dir: string;
key: 'user.name' | 'user.email';
}) {
try {
const res = await exec(`git config ${key}`, { cwd: dir });
return res.stdout;
} catch (e) {
return;
}
}

export async function setGitConfig({
dir,
key,
value,
}: {
dir: string;
key: 'user.name' | 'user.email';
value: string;
}) {
try {
await exec(`git config ${key} ${value}`, { cwd: dir });
} catch (e) {
logger.error(`Could not set git config: ${key}=${value}`, e);
return;
}
}

// How the commit flows:
// ${sourceBranch} -> ${backportBranch} -> ${targetBranch}
// master -> backport/7.x/pr-1234 -> 7.x
Expand Down Expand Up @@ -517,16 +548,11 @@ export async function pushBackportBranch({
}
}

export async function getSourceRepoPath(options: ValidConfigOptions) {
const gitRemote = await getRepoOwnerAndNameFromGitRemotes(options);

// where to fetch the repo from (either remotely from Github or from a local path)
const remoteUrl = getRemoteUrl(options, options.repoOwner);
const sourcePath =
options.repoName === gitRemote.repoName &&
options.repoOwner === gitRemote.repoOwner
? (await getGitProjectRoot(options.cwd)) ?? remoteUrl
: remoteUrl;

return sourcePath;
// retrieve path to local repo (cwd) if it matches `repoName` / `repoOwner`
export async function getLocalRepoPath(options: ValidConfigOptions) {
const remotes = await getRepoInfoFromGitRemotes({ cwd: options.cwd });
const hasMatchingGitRemote = remotes.some(
(remote) => remote.repoName === options.repoOwner && remote.repoOwner
);
return hasMatchingGitRemote ? getGitProjectRootPath(options.cwd) : undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getRepoInfoFromGitRemotes } from '../../git';
import { logger } from '../../logger';
import { apiRequestV4, GithubV4Exception } from './apiRequestV4';

// This method should be used to get the origin owner (instead of a fork owner)
export async function getRepoOwnerAndNameFromGitRemotes({
accessToken,
githubApiBaseUrlV4,
Expand Down
87 changes: 52 additions & 35 deletions src/ui/maybeSetupRepo.test.ts → src/ui/setupRepo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import os from 'os';
import del from 'del';
import { ValidConfigOptions } from '../options/options';
import * as childProcess from '../services/child-process-promisified';
import * as git from '../services/git';
import * as gitModule from '../services/git';
import { getOraMock } from '../test/mocks';
import { maybeSetupRepo } from './maybeSetupRepo';
import { setupRepo } from './setupRepo';

describe('maybeSetupRepo', () => {
describe('setupRepo', () => {
let execSpy: jest.SpyInstance;

beforeEach(() => {
Expand Down Expand Up @@ -36,7 +36,7 @@ describe('maybeSetupRepo', () => {
});

await expect(
maybeSetupRepo({
setupRepo({
repoName: 'kibana',
repoOwner: 'elastic',
cwd: '/path/to/source/repo',
Expand All @@ -59,9 +59,7 @@ describe('maybeSetupRepo', () => {
const spinnerTextSpy = jest.spyOn(oraMock, 'text', 'set');
const spinnerSuccessSpy = jest.spyOn(oraMock, 'succeed');

jest
.spyOn(git, 'getSourceRepoPath')
.mockResolvedValue('/path/to/source/repo');
jest.spyOn(gitModule, 'getLocalRepoPath').mockResolvedValue(undefined);

jest
.spyOn(childProcess, 'execAsCallback')
Expand Down Expand Up @@ -94,29 +92,32 @@ describe('maybeSetupRepo', () => {
onCloneComplete();
}, 50);

await maybeSetupRepo({
await setupRepo({
repoName: 'kibana',
repoOwner: 'elastic',
gitUserEmail: 'my-email',
gitUserName: 'my-username',
gitHostname: 'github.com',
cwd: '/path/to/source/repo',
} as ValidConfigOptions);

expect(spinnerTextSpy.mock.calls.map((call) => call[0]))
.toMatchInlineSnapshot(`
Array [
"0% Cloning repository from /path/to/source/repo (one-time operation)",
"1% Cloning repository from /path/to/source/repo (one-time operation)",
"9% Cloning repository from /path/to/source/repo (one-time operation)",
"18% Cloning repository from /path/to/source/repo (one-time operation)",
"90% Cloning repository from /path/to/source/repo (one-time operation)",
"90% Cloning repository from /path/to/source/repo (one-time operation)",
"91% Cloning repository from /path/to/source/repo (one-time operation)",
"92% Cloning repository from /path/to/source/repo (one-time operation)",
"100% Cloning repository from /path/to/source/repo (one-time operation)",
"0% Cloning repository from github.com (one-time operation)",
"1% Cloning repository from github.com (one-time operation)",
"9% Cloning repository from github.com (one-time operation)",
"18% Cloning repository from github.com (one-time operation)",
"90% Cloning repository from github.com (one-time operation)",
"90% Cloning repository from github.com (one-time operation)",
"91% Cloning repository from github.com (one-time operation)",
"92% Cloning repository from github.com (one-time operation)",
"100% Cloning repository from github.com (one-time operation)",
]
`);

expect(spinnerSuccessSpy).toHaveBeenCalledWith(
'100% Cloning repository from /path/to/source/repo (one-time operation)'
'100% Cloning repository from github.com (one-time operation)'
);
});
});
Expand All @@ -137,11 +138,14 @@ describe('maybeSetupRepo', () => {
});

it('should re-create remotes for both source repo and fork', async () => {
await maybeSetupRepo({
await setupRepo({
accessToken: 'myAccessToken',
authenticatedUsername: 'sqren_authenticated',
repoName: 'kibana',
repoOwner: 'elastic',
gitUserEmail: 'my-email',
gitUserName: 'my-username',
gitHostname: 'github.com',
cwd: '/path/to/source/repo',
} as ValidConfigOptions);

Expand All @@ -152,9 +156,23 @@ describe('maybeSetupRepo', () => {
cmd: 'git rev-parse --show-toplevel',
cwd: '/myHomeDir/.backport/repositories/elastic/kibana',
},
{ cmd: 'git remote --verbose', cwd: '/path/to/source/repo' },
{
cmd: 'git remote --verbose',
cwd: '/path/to/source/repo',
cmd: 'git config user.name',
cwd: '/myHomeDir/.backport/repositories/elastic/kibana',
},
{
cmd: 'git config user.email',
cwd: '/myHomeDir/.backport/repositories/elastic/kibana',
},
{ cmd: 'git remote --verbose', cwd: '/path/to/source/repo' },
{
cmd: 'git config user.name my-username',
cwd: '/myHomeDir/.backport/repositories/elastic/kibana',
},
{
cmd: 'git config user.email my-email',
cwd: '/myHomeDir/.backport/repositories/elastic/kibana',
},
{
cmd: 'git remote rm origin',
Expand Down Expand Up @@ -196,8 +214,10 @@ describe('maybeSetupRepo', () => {
});

it('should clone it from github.com', async () => {
await maybeSetupRepo({
await setupRepo({
accessToken: 'myAccessToken',
gitUserEmail: 'my-email',
gitUserName: 'my-username',
gitHostname: 'github.com',
repoName: 'kibana',
repoOwner: 'elastic',
Expand All @@ -215,9 +235,13 @@ describe('maybeSetupRepo', () => {
describe('if repo does exist locally', () => {
beforeEach(() => {
jest
.spyOn(git, 'getSourceRepoPath')
.spyOn(gitModule, 'getLocalRepoPath')
.mockResolvedValue('/path/to/source/repo');

jest
.spyOn(gitModule, 'getGitConfig')
.mockResolvedValue('email-or-username');

jest
.spyOn(childProcess, 'execAsCallback')
//@ts-expect-error
Expand All @@ -232,7 +256,7 @@ describe('maybeSetupRepo', () => {
});

it('should clone it from local folder', async () => {
await maybeSetupRepo({
await setupRepo({
repoName: 'kibana',
repoOwner: 'elastic',
cwd: '/path/to/source/repo',
Expand All @@ -247,23 +271,16 @@ describe('maybeSetupRepo', () => {
});

describe('if `repoPath` is a parent of current working directory (cwd)', () => {
beforeEach(() => {
jest
.spyOn(git, 'getSourceRepoPath')
.mockResolvedValue('/path/to/source/repo');

jest.spyOn(childProcess, 'execAsCallback');
});

it('should clone it from local folder', async () => {
await expect(() =>
maybeSetupRepo({
setupRepo({
repoName: 'kibana',
repoOwner: 'elastic',
cwd: '/myHomeDir/.backport/repositories/elastic/kibana/foo',
cwd: '/myHomeDir/.backport/repositories/owner/repo/foo',
dir: '/myHomeDir/.backport/repositories/owner/repo',
} as ValidConfigOptions)
).rejects.toThrowError(
'Refusing to clone repo into "/myHomeDir/.backport/repositories/elastic/kibana" when current working directory is "/myHomeDir/.backport/repositories/elastic/kibana/foo". Please change backport directory via `--dir` option or run backport from another location'
'Refusing to clone repo into "/myHomeDir/.backport/repositories/owner/repo" when current working directory is "/myHomeDir/.backport/repositories/owner/repo/foo". Please change backport directory via `--dir` option or run backport from another location'
);
});
});
Expand Down
Loading

0 comments on commit 86a608d

Please sign in to comment.