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

telemetry for incorrect merge commit #253

Merged
merged 1 commit into from
May 21, 2020
Merged
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
98 changes: 95 additions & 3 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3359,7 +3359,7 @@ module.exports = {"name":"@octokit/rest","version":"16.43.1","publishConfig":{"a
/***/ }),

/***/ 227:
/***/ (function(__unusedmodule, exports) {
/***/ (function(__unusedmodule, exports, __webpack_require__) {

"use strict";

Expand All @@ -3372,7 +3372,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const url_1 = __webpack_require__(835);
const core = __importStar(__webpack_require__(470));
const github = __importStar(__webpack_require__(469));
function getCheckoutInfo(git, ref, commit) {
return __awaiter(this, void 0, void 0, function* () {
if (!git) {
Expand Down Expand Up @@ -3468,6 +3478,85 @@ function getRefSpec(ref, commit) {
}
}
exports.getRefSpec = getRefSpec;
function checkCommitInfo(token, commitInfo, repositoryOwner, repositoryName, ref, commit) {
return __awaiter(this, void 0, void 0, function* () {
try {
// GHES?
if (isGhes()) {
return;
}
// Auth token?
if (!token) {
return;
}
// Public PR synchronize, for workflow repo?
if (fromPayload('repository.private') !== false ||
github.context.eventName !== 'pull_request' ||
fromPayload('action') !== 'synchronize' ||
repositoryOwner !== github.context.repo.owner ||
repositoryName !== github.context.repo.repo ||
ref !== github.context.ref ||
!ref.startsWith('refs/pull/') ||
commit !== github.context.sha) {
return;
}
// Head SHA
const expectedHeadSha = fromPayload('after');
if (!expectedHeadSha) {
core.debug('Unable to determine head sha');
return;
}
// Base SHA
const expectedBaseSha = fromPayload('pull_request.base.sha');
if (!expectedBaseSha) {
core.debug('Unable to determine base sha');
return;
}
// Expected message?
const expectedMessage = `Merge ${expectedHeadSha} into ${expectedBaseSha}`;
if (commitInfo.indexOf(expectedMessage) >= 0) {
return;
}
// Extract details from message
const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/);
if (!match) {
core.debug('Unexpected message format');
return;
}
// Post telemetry
const actualHeadSha = match[1];
if (actualHeadSha !== expectedHeadSha) {
core.debug(`Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}`);
const octokit = new github.GitHub(token, {
userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload('number')};run_id=${process.env['GITHUB_RUN_ID']};expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})`
});
yield octokit.repos.get({ owner: repositoryOwner, repo: repositoryName });
}
}
catch (err) {
core.debug(`Error when validating commit info: ${err.stack}`);
}
});
}
exports.checkCommitInfo = checkCommitInfo;
function fromPayload(path) {
return select(github.context.payload, path);
}
function select(obj, path) {
if (!obj) {
return undefined;
}
const i = path.indexOf('.');
if (i < 0) {
return obj[path];
}
const key = path.substr(0, i);
return select(obj[key], path.substr(i + 1));
}
function isGhes() {
const ghUrl = new url_1.URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com');
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}


/***/ }),
Expand Down Expand Up @@ -5718,7 +5807,8 @@ class GitCommandManager {
}
log1() {
return __awaiter(this, void 0, void 0, function* () {
yield this.execGit(['log', '-1']);
const output = yield this.execGit(['log', '-1']);
return output.stdout;
});
}
remoteAdd(remoteName, remoteUrl) {
Expand Down Expand Up @@ -6057,7 +6147,9 @@ function getSource(settings) {
}
}
// Dump some info about the checked out commit
yield git.log1();
const commitInfo = yield git.log1();
// Check for incorrect pull request merge commit
yield refHelper.checkCommitInfo(settings.authToken, commitInfo, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit);
}
finally {
// Remove auth
Expand Down
7 changes: 4 additions & 3 deletions src/git-command-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface IGitCommandManager {
isDetached(): Promise<boolean>
lfsFetch(ref: string): Promise<void>
lfsInstall(): Promise<void>
log1(): Promise<void>
log1(): Promise<string>
remoteAdd(remoteName: string, remoteUrl: string): Promise<void>
removeEnvironmentVariable(name: string): void
setEnvironmentVariable(name: string, value: string): void
Expand Down Expand Up @@ -225,8 +225,9 @@ class GitCommandManager {
await this.execGit(['lfs', 'install', '--local'])
}

async log1(): Promise<void> {
await this.execGit(['log', '-1'])
async log1(): Promise<string> {
const output = await this.execGit(['log', '-1'])
return output.stdout
}

async remoteAdd(remoteName: string, remoteUrl: string): Promise<void> {
Expand Down
12 changes: 11 additions & 1 deletion src/git-source-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,17 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
}

// Dump some info about the checked out commit
await git.log1()
const commitInfo = await git.log1()

// Check for incorrect pull request merge commit
await refHelper.checkCommitInfo(
settings.authToken,
commitInfo,
settings.repositoryOwner,
settings.repositoryName,
settings.ref,
settings.commit
)
} finally {
// Remove auth
if (!settings.persistCredentials) {
Expand Down
108 changes: 108 additions & 0 deletions src/ref-helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {URL} from 'url'
import {IGitCommandManager} from './git-command-manager'
import * as core from '@actions/core'
import * as github from '@actions/github'

export interface ICheckoutInfo {
ref: string
Expand Down Expand Up @@ -107,3 +110,108 @@ export function getRefSpec(ref: string, commit: string): string[] {
return [`+${ref}:${ref}`]
}
}

export async function checkCommitInfo(
token: string,
commitInfo: string,
repositoryOwner: string,
repositoryName: string,
ref: string,
commit: string
): Promise<void> {
try {
// GHES?
if (isGhes()) {
return
}

// Auth token?
if (!token) {
return
}

// Public PR synchronize, for workflow repo?
if (
fromPayload('repository.private') !== false ||
github.context.eventName !== 'pull_request' ||
fromPayload('action') !== 'synchronize' ||
repositoryOwner !== github.context.repo.owner ||
repositoryName !== github.context.repo.repo ||
ref !== github.context.ref ||
!ref.startsWith('refs/pull/') ||
commit !== github.context.sha
) {
return
}

// Head SHA
const expectedHeadSha = fromPayload('after')
if (!expectedHeadSha) {
core.debug('Unable to determine head sha')
return
}

// Base SHA
const expectedBaseSha = fromPayload('pull_request.base.sha')
if (!expectedBaseSha) {
core.debug('Unable to determine base sha')
return
}

// Expected message?
const expectedMessage = `Merge ${expectedHeadSha} into ${expectedBaseSha}`
if (commitInfo.indexOf(expectedMessage) >= 0) {
return
}

// Extract details from message
const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/)
if (!match) {
core.debug('Unexpected message format')
return
}

// Post telemetry
const actualHeadSha = match[1]
if (actualHeadSha !== expectedHeadSha) {
core.debug(
`Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}`
)
const octokit = new github.GitHub(token, {
userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload(
'number'
)};run_id=${
process.env['GITHUB_RUN_ID']
};expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})`
})
await octokit.repos.get({owner: repositoryOwner, repo: repositoryName})
}
} catch (err) {
core.debug(`Error when validating commit info: ${err.stack}`)
}
}

function fromPayload(path: string): any {
return select(github.context.payload, path)
}

function select(obj: any, path: string): any {
if (!obj) {
return undefined
}

const i = path.indexOf('.')
if (i < 0) {
return obj[path]
}

const key = path.substr(0, i)
return select(obj[key], path.substr(i + 1))
}

function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
)
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
}