Skip to content

Commit

Permalink
Merge branch 'flutter:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
ricardoamador authored Apr 22, 2024
2 parents 8ba406b + 0abc6c9 commit 3d3f512
Show file tree
Hide file tree
Showing 45 changed files with 1,987 additions and 158 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/no-response_publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
if: ${{ github.repository == 'flutter/cocoon' }}
steps:
- name: Checkout
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f
with:
ref: ${{ github.event.release.tag_name }}
sparse-checkout: 'gh_actions/third_party/no-response'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/no-response_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
if: ${{ github.repository == 'flutter/cocoon' }}
steps:
- name: Checkout
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f
with:
ref: ${{ github.event.release.tag_name }}
sparse-checkout: 'gh_actions/third_party/no-response'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/scorecards-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:

steps:
- name: "Checkout code"
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f
with:
persist-credentials: false

Expand All @@ -43,7 +43,7 @@ jobs:

# Upload the results as artifacts (optional).
- name: "Upload artifact"
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba
with:
name: SARIF file
path: results.sarif
Expand Down
2 changes: 1 addition & 1 deletion app_dart/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


# Dart Docker official images can be found here: https://hub.docker.com/_/dart
FROM dart:beta@sha256:7fef8712c268d41081d36a58745872e527c387bf68cf0bbb86e912201e30f3bf
FROM dart:beta@sha256:cea97886b77255bae071d4baf5254d85b956a83c055c3e163c329ff9e87778ae

WORKDIR /app

Expand Down
88 changes: 88 additions & 0 deletions app_dart/lib/src/model/appengine/task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'commit.dart';
import 'key_converter.dart';

import 'package:cocoon_service/src/model/luci/buildbucket.dart' as bb;
import 'package:buildbucket/buildbucket_pb.dart' as bbv2;
part 'task.g.dart';

/// Class that represents the intersection of a test at a particular [Commit].
Expand Down Expand Up @@ -181,6 +182,56 @@ class Task extends Model<int> {
return task;
}

/// Creates a [Task] based on a buildbucket [bb.Build].
static Future<Task> fromBuildbucketV2Build(
bbv2.Build build,
DatastoreService datastore, {
String? customName,
}) async {
log.fine('Creating task from buildbucket result: ${build.toString()}');
// Example: Getting "flutter" from "mirrors/flutter".
final String repository = build.input.gitilesCommit.project.split('/')[1];
log.fine('Repository: $repository');

// Example: Getting "stable" from "refs/heads/stable".
final String branch = build.input.gitilesCommit.ref.split('/')[2];
log.fine('Branch: $branch');

final String hash = build.input.gitilesCommit.id;
log.fine('Hash: $hash');

final RepositorySlug slug = RepositorySlug('flutter', repository);
log.fine('Slug: ${slug.toString()}');

final int startTime = build.startTime.toDateTime().millisecondsSinceEpoch;
final int endTime = build.endTime.toDateTime().millisecondsSinceEpoch;
log.fine('Start/end time (ms): $startTime, $endTime');

final String id = '${slug.fullName}/$branch/$hash';
final Key<String> commitKey = datastore.db.emptyKey.append<String>(Commit, id: id);
final Commit commit = await datastore.db.lookupValue<Commit>(commitKey);
final task = Task(
attempts: 1,
buildNumber: build.number,
buildNumberList: build.number.toString(),
builderName: build.builder.builder,
commitKey: commitKey,
createTimestamp: startTime,
endTimestamp: endTime,
luciBucket: build.builder.bucket,
name: customName ?? build.builder.builder,
stageName: build.builder.project,
startTimestamp: startTime,
status: convertBuildbucketV2StatusToString(build.status),
key: commit.key.append(Task),
timeoutInMinutes: 0,
reason: '',
requiredCapabilities: [],
reservedForAgentId: '',
);
return task;
}

/// Converts a buildbucket status to a task status.
static String convertBuildbucketStatusToString(bb.Status status) {
switch (status) {
Expand All @@ -199,6 +250,23 @@ class Task extends Model<int> {
}
}

static String convertBuildbucketV2StatusToString(bbv2.Status status) {
switch (status) {
case bbv2.Status.SUCCESS:
return statusSucceeded;
case bbv2.Status.CANCELED:
return statusCancelled;
case bbv2.Status.INFRA_FAILURE:
return statusInfraFailure;
case bbv2.Status.STARTED:
return statusInProgress;
case bbv2.Status.SCHEDULED:
return statusNew;
default:
return statusFailed;
}
}

/// The task was cancelled.
static const String statusCancelled = 'Cancelled';

Expand Down Expand Up @@ -440,6 +508,26 @@ class Task extends Model<int> {
status = convertBuildbucketStatusToString(build.status!);
}

void updateFromBuildbucketV2Build(bbv2.Build build) {
buildNumber = build.number;

if (buildNumberList == null) {
buildNumberList = '$buildNumber';
} else {
final Set<String> buildNumberSet = buildNumberList!.split(',').toSet();
buildNumberSet.add(buildNumber.toString());
buildNumberList = buildNumberSet.join(',');
}

createTimestamp = build.createTime.toDateTime().millisecondsSinceEpoch;
startTimestamp = build.startTime.toDateTime().millisecondsSinceEpoch;
endTimestamp = build.endTime.toDateTime().millisecondsSinceEpoch;

attempts = buildNumberList!.split(',').length;

status = convertBuildbucketV2StatusToString(build.status);
}

/// Get a [Task] status from a LUCI [Build] status/result.
String _setStatusFromLuciStatus(Build build) {
// Updates can come out of order. Ensure completed statuses are kept.
Expand Down
3 changes: 1 addition & 2 deletions app_dart/lib/src/model/firestore/commit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ class Commit extends Document {
/// [RepositorySlug] of where this commit exists.
RepositorySlug get slug => RepositorySlug.full(repositoryPath!);

@override
Map<String, dynamic> toJson() {
Map<String, dynamic> get facade {
return <String, dynamic>{
kCommitDocumentName: name,
kCommitRepositoryPath: repositoryPath,
Expand Down
44 changes: 39 additions & 5 deletions app_dart/lib/src/model/firestore/commit_tasks_status.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,51 @@ class CommitTasksStatus {

/// Tasks running against the commit.
final List<Task> tasks;

/// Refactors task list to fullTask list.
///
/// After migrated to Firestore, we are tracking each rerun as a separate Task entry.
/// But from Frontend side, it is expecting a build list for all retries.
///
/// Instead of adding burden to the frondend loading, a proactive preparation is
/// added here to provide build list explicitly.
///
/// Note we use the lastest run as the `task`, surfacing on the dashboard.
List<FullTask> toFullTasks(List<Task> tasks) {
final Map<String, FullTask> fullTasksMap = <String, FullTask>{};
for (Task task in tasks) {
if (!fullTasksMap.containsKey(task.taskName)) {
if (task.buildNumber == null) {
fullTasksMap[task.taskName!] = FullTask(task, <int>[]);
} else {
fullTasksMap[task.taskName!] = FullTask(task, <int>[task.buildNumber!]);
}
} else if (fullTasksMap.containsKey(task.taskName)) {
fullTasksMap[task.taskName]!.buildList.add(task.buildNumber!);
}
}
return fullTasksMap.entries.map((entry) => entry.value).toList();
}

Map<String, dynamic> toJson() {
return <String, dynamic>{
'Commit': commit.facade,
'Tasks': toFullTasks(tasks).map((e) => e.toJson()).toList(),
};
}
}

class SerializableCommitTasksStatus {
const SerializableCommitTasksStatus(this.status);
/// Latest task entry and its rerun build list.
class FullTask {
const FullTask(this.task, this.buildList);

final CommitTasksStatus status;
final Task task;
final List<int> buildList;

Map<String, dynamic> toJson() {
return <String, dynamic>{
'Commit': status.commit.toJson(),
'Tasks': status.tasks,
'Task': task.facade,
'BuildList': buildList.join(','),
};
}
}
44 changes: 42 additions & 2 deletions app_dart/lib/src/model/firestore/task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:buildbucket/buildbucket_pb.dart' as bbv2;
import 'package:cocoon_service/cocoon_service.dart';
import 'package:googleapis/firestore/v1.dart' hide Status;

Expand Down Expand Up @@ -30,6 +31,7 @@ const String kTaskTestFlakyField = 'testFlaky';
const String kTaskAttempts = 'Attempts';
const String kTaskBringup = 'Bringup';
const String kTaskBuildNumber = 'BuildNumber';
const String kTaskCommitSha = 'CommitSha';
const String kTaskCreateTimestamp = 'CreateTimestamp';
const String kTaskDocumentName = 'DocumentName';
const String kTaskEndTimestamp = 'EndTimestamp';
Expand Down Expand Up @@ -211,6 +213,22 @@ class Task extends Document {
_setStatusFromLuciStatus(build);
}

void updateFromBuildV2(bbv2.Build build) {
fields![kTaskBuildNumberField] = Value(integerValue: build.number.toString());

fields![kTaskCreateTimestampField] = Value(
integerValue: (build.createTime.toDateTime().millisecondsSinceEpoch).toString(),
);
fields![kTaskStartTimestampField] = Value(
integerValue: (build.startTime.toDateTime().millisecondsSinceEpoch).toString(),
);
fields![kTaskEndTimestampField] = Value(
integerValue: (build.endTime.toDateTime().millisecondsSinceEpoch).toString(),
);

_setStatusFromLuciStatusV2(build);
}

void resetAsRetry({int attempt = 1}) {
name = '$kDatabase/documents/$kTaskCollectionId/${commitSha}_${taskName}_$attempt';
fields = <String, Value>{
Expand Down Expand Up @@ -254,6 +272,27 @@ class Task extends Document {
}
}

String _setStatusFromLuciStatusV2(bbv2.Build build) {
// Updates can come out of order. Ensure completed statuses are kept.
if (_isStatusCompleted()) {
return status;
}

if (build.status == bbv2.Status.STARTED) {
return setStatus(statusInProgress);
} else if (build.status == bbv2.Status.SUCCESS) {
return setStatus(statusSucceeded);
} else if (build.status == bbv2.Status.CANCELED) {
return setStatus(statusCancelled);
} else if (build.status == bbv2.Status.FAILURE) {
return setStatus(statusFailed);
} else if (build.status == bbv2.Status.INFRA_FAILURE) {
return setStatus(statusInfraFailure);
} else {
throw BadRequestException('${build.status} is unknown');
}
}

bool _isStatusCompleted() {
const List<String> completedStatuses = <String>[
statusCancelled,
Expand All @@ -264,10 +303,10 @@ class Task extends Document {
return completedStatuses.contains(status);
}

@override
Map<String, dynamic> toJson() {
Map<String, dynamic> get facade {
return <String, dynamic>{
kTaskDocumentName: name,
kTaskCommitSha: commitSha,
kTaskCreateTimestamp: createTimestamp,
kTaskStartTimestamp: startTimestamp,
kTaskEndTimestamp: endTimestamp,
Expand All @@ -284,6 +323,7 @@ class Task extends Document {
String toString() {
final StringBuffer buf = StringBuffer()
..write('$runtimeType(')
..write('$kTaskBuildNumberField: $buildNumber')
..write(', $kTaskCreateTimestampField: $createTimestamp')
..write(', $kTaskStartTimestampField: $startTimestamp')
..write(', $kTaskEndTimestampField: $endTimestamp')
Expand Down
65 changes: 65 additions & 0 deletions app_dart/lib/src/model/luci/pubsub_message_v2.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2024 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 'package:cocoon_service/cocoon_service.dart';
import 'package:cocoon_service/src/model/common/json_converters.dart';
import 'package:json_annotation/json_annotation.dart';

part 'pubsub_message_v2.g.dart';

// TODO (ricardoamador) look to see how this can be removed in favor of the gcloud lib pub/sub.
// the initial finding is that it may be an issue with how gcloud packages the
// message.
@JsonSerializable(includeIfNull: false)
class PubSubPushMessageV2 extends JsonBody {
const PubSubPushMessageV2({
this.message,
this.subscription,
});

static PubSubPushMessageV2 fromJson(Map<String, dynamic> json) => _$PubSubPushMessageV2FromJson(json);

/// The message contents.
final PushMessageV2? message;

/// The name of the subscription associated with the delivery.
final String? subscription;

@override
Map<String, dynamic> toJson() => _$PubSubPushMessageV2ToJson(this);
}

// Rename this to PushMessage as it is basically that class.
@JsonSerializable(includeIfNull: false)
class PushMessageV2 extends JsonBody {
const PushMessageV2({
this.attributes,
this.data,
this.messageId,
this.publishTime,
});

/// PubSub attributes on the message.
final Map<String, String>? attributes;

/// The raw string data of the message.
@Base64Converter()
final String? data;

/// A identifier for the message from PubSub.
final String? messageId;

/// The time at which the message was published, populated by the server when
/// it receives the topics.publish call.
///
/// A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and
/// up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" and
/// "2014-10-02T15:01:23.045123456Z".
final String? publishTime;

static PushMessageV2 fromJson(Map<String, dynamic> json) => _$PushMessageV2FromJson(json);

@override
Map<String, dynamic> toJson() => _$PushMessageV2ToJson(this);
}
Loading

0 comments on commit 3d3f512

Please sign in to comment.