blob: 69224083436cdbb2ff00bb20e570d4e482b7ebbf [file] [log] [blame]
// Copyright 2022 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:auto_submit/service/log.dart';
import 'package:github/github.dart';
/// If a pull request was behind the tip of tree by _kBehindToT commits
/// then the bot tries to rebase it
const int _kBehindToT = 10;
/// [GithubService] handles communication with the GitHub API.
class GithubService {
GithubService(this.github);
final GitHub github;
/// Retrieves check runs with the ref.
Future<List<CheckRun>> getCheckRuns(
RepositorySlug slug,
String ref,
) async {
return await github.checks.checkRuns.listCheckRunsForRef(slug, ref: ref).toList();
}
/// Fetches the specified commit.
Future<RepositoryCommit> getCommit(RepositorySlug slug, String sha) async {
return await github.repositories.getCommit(slug, sha);
}
/// Fetches the specified pull request.
Future<PullRequest> getPullRequest(RepositorySlug slug, int pullRequestNumber) async {
return await github.pullRequests.get(slug, pullRequestNumber);
}
/// Compares two commits to fetch diff.
///
/// The response will include details on the files that were changed between the two commits.
/// Relevant APIs: https://docs.github.com/en/rest/reference/commits#compare-two-commits
Future<GitHubComparison> compareTwoCommits(RepositorySlug slug, String refBase, String refHead) async {
return await github.repositories.compareCommits(slug, refBase, refHead);
}
/// Removes a lable for a pull request.
Future<bool> removeLabel(RepositorySlug slug, int issueNumber, String label) async {
return await github.issues.removeLabelForIssue(slug, issueNumber, label);
}
/// Create a comment for a pull request.
Future<IssueComment> createComment(
RepositorySlug slug,
int issueNumber,
String body,
) async {
return await github.issues.createComment(slug, issueNumber, body);
}
/// Update a pull request branch
Future<bool> updateBranch(RepositorySlug slug, int number, String headSha) async {
final response = await github.request('PUT', '/repos/${slug.fullName}/pulls/$number/update-branch',
body: GitHubJson.encode({'expected_head_sha': headSha}));
return response.statusCode == StatusCodes.ACCEPTED;
}
/// Automerges a given pull request with HEAD to ensure the commit is not in conflicting state.
Future<void> autoMergeBranch(PullRequest pullRequest) async {
final RepositorySlug slug = pullRequest.base!.repo!.slug();
final int prNumber = pullRequest.number!;
final RepositoryCommit totCommit = await getCommit(slug, 'HEAD');
final GitHubComparison comparison = await compareTwoCommits(slug, totCommit.sha!, pullRequest.base!.sha!);
if (comparison.behindBy! >= _kBehindToT) {
log.info('The current branch behinds by ${comparison.behindBy} commits.');
final String headSha = pullRequest.head!.sha!;
await updateBranch(slug, prNumber, headSha);
}
}
}