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

reviewstack: fix verify addons/ folder github ci #7

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/sapling-addons.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ jobs:
container:
image: ${{ format('ghcr.io/{0}/build_ubuntu_20_04:latest', github.repository) }}
steps:
- name: Install fb-watchman
run: curl -fsSL https://github.com/facebook/watchman/releases/download/v2023.09.18.00/watchman_ubuntu20.04_v2023.09.18.00.deb -o watchman.deb && apt -y -f install ./watchman.deb
- name: Checkout Code
uses: actions/checkout@v3
- name: Grant Access
Expand Down
12 changes: 5 additions & 7 deletions addons/isl-server/src/Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import type {CodeReviewProvider} from './CodeReviewProvider';
import type {ServerSideTracker} from './analytics/serverSideTracker';
import type {Logger} from './logger';
import type {ExecaError} from 'execa';
import type {
CommitInfo,
CommitPhaseType,
Expand Down Expand Up @@ -44,7 +43,7 @@ import {WatchForChanges} from './WatchForChanges';
import {DEFAULT_DAYS_OF_COMMITS_TO_LOAD, ErrorShortMessages} from './constants';
import {GitHubCodeReviewProvider} from './github/githubCodeReviewProvider';
import {isGithubEnterprise} from './github/queryGraphQL';
import {handleAbortSignalOnProcess, serializeAsyncCall} from './utils';
import {handleAbortSignalOnProcess, isExecaError, serializeAsyncCall} from './utils';
import execa from 'execa';
import {CommandRunner} from 'isl/src/types';
import os from 'os';
Expand Down Expand Up @@ -838,8 +837,9 @@ async function runCommand(
const result = execa(command, args, options);

let timedOut = false;
let timeoutId: NodeJS.Timeout | undefined;
if (timeout > 0) {
const timeoutId = setTimeout(() => {
timeoutId = setTimeout(() => {
result.kill('SIGTERM', {forceKillAfterTimeout: 5_000});
logger.error(`Timed out waiting for ${command} ${args[0]} to finish`);
timedOut = true;
Expand All @@ -862,13 +862,11 @@ async function runCommand(
}
}
throw err;
} finally {
clearTimeout(timeoutId);
}
}

function isExecaError(err: unknown): err is ExecaError {
return typeof err === 'object' && err != null && 'exitCode' in err;
}

export const __TEST__ = {
runCommand,
};
Expand Down
57 changes: 57 additions & 0 deletions addons/isl-server/src/__tests__/analytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,54 @@ import type {ServerPlatform} from '../serverPlatform';

import {Repository} from '../Repository';
import {makeServerSideTracker} from '../analytics/serverSideTracker';
import * as execa from 'execa';
import {mockLogger} from 'shared/testUtils';
import {defer} from 'shared/utils';

/** Matches any non-empty string */
const anyActualString = expect.stringMatching(/.+/);

jest.mock('../WatchForChanges', () => {
class MockWatchForChanges {
dispose = jest.fn();
}
return {WatchForChanges: MockWatchForChanges};
});

jest.mock('execa', () => {
return jest.fn();
});

function mockExeca(
cmds: Array<[RegExp, (() => {stdout: string} | Error) | {stdout: string} | Error]>,
) {
return jest.spyOn(execa, 'default').mockImplementation(((cmd: string, args: Array<string>) => {
const argStr = cmd + ' ' + args?.join(' ');
const execaOther = {
kill: jest.fn(),
on: jest.fn((event, cb) => {
// immediately call exit cb to teardown timeout
if (event === 'exit') {
cb();
}
}),
};
for (const [regex, output] of cmds) {
if (regex.test(argStr)) {
let value = output;
if (typeof output === 'function') {
value = output();
}
if (value instanceof Error) {
throw value;
}
return {...execaOther, ...value};
}
}
return {...execaOther, stdout: ''};
}) as unknown as typeof execa.default);
}

describe('track', () => {
const mockSendData = jest.fn();
let tracker: ServerSideTracker;
Expand Down Expand Up @@ -61,6 +103,20 @@ describe('track', () => {
});

it('allows setting repository', () => {
// No need to call the actual command lines to test tracking
const execaSpy = mockExeca([
[/^sl config paths.default/, {stdout: 'https://github.com/facebook/sapling.git'}],
[/^sl config github.pull_request_domain/, {stdout: 'github.com'}],
[/^sl root --dotdir/, {stdout: '/path/to/myRepo/.sl'}],
[/^sl root/, {stdout: '/path/to/myRepo'}],
[
/^gh auth status --hostname gitlab.myCompany.com/,
new Error('not authenticated on this hostname'),
],
[/^gh auth status --hostname ghe.myCompany.com/, {stdout: ''}],
[/^gh api graphql/, {stdout: '{}'}],
]);

const repo = new Repository(
{
type: 'success',
Expand All @@ -86,6 +142,7 @@ describe('track', () => {
mockLogger,
);
repo.dispose();
execaSpy.mockClear();
});

it('uses consistent session id, but different track ids', () => {
Expand Down
1 change: 1 addition & 0 deletions addons/isl-server/src/github/githubCodeReviewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export class GitHubCodeReviewProvider implements CodeReviewProvider {

public dispose() {
this.diffSummaries.removeAllListeners();
this.triggerDiffSummariesFetch.dispose();
}

public getSummaryName(): string {
Expand Down
28 changes: 14 additions & 14 deletions addons/isl-server/src/github/queryGraphQL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import type {ExecaError} from 'execa';

import {isExecaError} from '../utils';
import execa from 'execa';

export default async function queryGraphQL<TData, TVariables>(
Expand Down Expand Up @@ -38,25 +36,27 @@ export default async function queryGraphQL<TData, TVariables>(
args.push('--hostname', hostname);
args.push('-f', `query=${query}`);

const {stdout} = await execa('gh', args, {stdout: 'pipe', stderr: 'pipe'}).catch(
(error: ExecaError & {code?: string}) => {
try {
const {stdout} = await execa('gh', args, {stdout: 'pipe', stderr: 'pipe'});
const json = JSON.parse(stdout);

if (Array.isArray(json.errors)) {
return Promise.reject(`Error: ${json.errors[0].message}`);
}

return json.data;
} catch (error: unknown) {
if (isExecaError(error)) {
if (error.code === 'ENOENT' || error.code === 'EACCES') {
// `gh` not installed on path
throw new Error(`GhNotInstalledError: ${(error as Error).stack}`);
} else if (error.exitCode === 4) {
// `gh` CLI exit code 4 => authentication issue
throw new Error(`NotAuthenticatedError: ${(error as Error).stack}`);
}
throw error;
},
);
const json = JSON.parse(stdout);

if (Array.isArray(json.errors)) {
return Promise.reject(`Error: ${json.errors[0].message}`);
}
throw error;
}

return json.data;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions addons/isl-server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,6 @@ export function parseExecJson<T>(
});
}

export function isExecaError(s: unknown): s is ExecaError {
return s != null && typeof s === 'object' && 'stderr' in s;
export function isExecaError(s: unknown): s is ExecaError & {code?: string} {
return s != null && typeof s === 'object' && 'exitCode' in s;
}
9 changes: 9 additions & 0 deletions addons/shared/debounce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type DebouncedFunction<Args extends Array<unknown>> = {
(...args: Args): void;
reset: () => void;
isPending: () => boolean;
dispose: () => void;
};

/**
Expand Down Expand Up @@ -52,6 +53,7 @@ export function debounce<Args extends Array<unknown>>(
if (leading) {
callback = function () {
shouldCallLeading = true;
clearTimeout(timeout);
timeout = undefined;
};

Expand All @@ -66,11 +68,13 @@ export function debounce<Args extends Array<unknown>>(
} else {
debouncer.reset();
callback = function () {
clearTimeout(timeout);
timeout = undefined;
func.apply(context, args);
};
}

clearTimeout(timeout);
timeout = setTimeout(callback, wait);
}

Expand All @@ -80,6 +84,11 @@ export function debounce<Args extends Array<unknown>>(
shouldCallLeading = true;
};

debouncer.dispose = function () {
clearTimeout(timeout);
timeout = undefined;
};

debouncer.isPending = function () {
return timeout != null;
};
Expand Down
3 changes: 2 additions & 1 deletion addons/verify-addons-folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ async def verify(*, use_vendored_grammars=False):
verify_shared(),
verify_textmate(),
verify_isl(),
verify_reviewstack(use_vendored_grammars=use_vendored_grammars),
)
# shared depends on reviewstack generated textmate grammars, so can't run concurrently without flakiness
await verify_reviewstack(use_vendored_grammars=use_vendored_grammars)


async def verify_prettier():
Expand Down
Loading