| // Copyright 2021 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:cocoon_service/src/model/appengine/commit.dart'; |
| import 'package:cocoon_service/src/model/appengine/stage.dart'; |
| import 'package:cocoon_service/src/model/appengine/task.dart'; |
| import 'package:github/github.dart'; |
| import 'package:meta/meta.dart'; |
| |
| import '../request_handling/body.dart'; |
| import '../request_handling/request_handler.dart'; |
| import '../service/build_status_provider.dart'; |
| import '../service/config.dart'; |
| import '../service/datastore.dart'; |
| |
| /// Returns [List<String>] of the commit shas that had all passing tests. |
| /// |
| /// A [CommitStatus] that have all passing tests is used to help the release tooling find commits Flutter infrastructure has validated. |
| /// In order to qualify as a [CommitStatus] that have all passing tests, the rules are: |
| /// 1. The [Commit] inside [CommitStatus] had all its tests run (at least those that are not in bringup) |
| /// 2. all the blocking [Task] in [CommitStatus] should pass |
| /// A [List<String>] of commit shas of the qualified [CommitStatus]s are returned, in the order of [Commit] timestamp, i.e., |
| /// A [Commit] with an earlier timestamp will apprear earlier in the result [List<String>], as compared to another [Commit] |
| /// with a later timestamp. |
| /// |
| /// Parameters: |
| /// branch: defaults to the defaults branch for the repository. |
| /// repo: default: 'flutter'. Name of the repository. |
| /// |
| /// GET: /api/public/get-green-commits?repo=$repo |
| |
| @immutable |
| class GetGreenCommits extends RequestHandler<Body> { |
| const GetGreenCommits({ |
| required super.config, |
| @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider, |
| @visibleForTesting BuildStatusServiceProvider? buildStatusProvider, |
| }) : buildStatusProvider = buildStatusProvider ?? BuildStatusService.defaultProvider; |
| |
| final DatastoreServiceProvider datastoreProvider; |
| final BuildStatusServiceProvider buildStatusProvider; |
| |
| static const String kBranchParam = 'branch'; |
| static const String kRepoParam = 'repo'; |
| |
| @override |
| Future<Body> get() async { |
| final String repoName = request!.uri.queryParameters[kRepoParam] ?? Config.flutterSlug.name; |
| final RepositorySlug slug = RepositorySlug('flutter', repoName); |
| final String branch = request!.uri.queryParameters[kBranchParam] ?? Config.defaultBranch(slug); |
| final DatastoreService datastore = datastoreProvider(config.db); |
| final BuildStatusService buildStatusService = buildStatusProvider(datastore); |
| final int commitNumber = config.commitNumber; |
| |
| final List<String?> greenCommits = await buildStatusService |
| .retrieveCommitStatus( |
| limit: commitNumber, |
| branch: branch, |
| slug: slug, |
| ) |
| .where((CommitStatus status) => everyNonFlakyTaskSucceed(status)) |
| .map<String?>((CommitStatus status) => status.commit.sha) |
| .toList(); |
| |
| return Body.forJson(greenCommits); |
| } |
| |
| bool everyNonFlakyTaskSucceed(CommitStatus status) { |
| return status.stages.every( |
| (Stage stage) => stage.tasks |
| .where((Task task) => !task.isFlaky!) |
| .every((Task nonFlakyTask) => nonFlakyTask.status == Task.statusSucceeded), |
| ); |
| } |
| } |