Automatic compilation
diff --git a/.ci.yaml b/.ci.yaml
deleted file mode 100644
index ca2ee9f..0000000
--- a/.ci.yaml
+++ /dev/null
@@ -1,162 +0,0 @@
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/main/CI_YAML.md
-enabled_branches:
- - main
-
-platform_properties:
- linux:
- properties:
- os: Linux
- device_type: "none"
- mac:
- properties:
- os: "Mac-12|Mac-13"
- cpu: x86
- mac_arm64:
- properties:
- os: "Mac-12|Mac-13"
- cpu: arm64
- windows:
- properties:
- os: Windows
- device_type: "none"
-
-targets:
- - name: Linux Cocoon
- recipe: cocoon/cocoon
- properties:
- add_recipes_cq: "true"
- runIf:
- - .ci.yaml
- - analyze/**
- - app_dart/**
- - auto_submit/**
- - cipd_packages/**
- - cloud_build/**
- - dashboard/**
- - dev/**
- - licenses/**
- - packages/**
- - test_utilities/**
- - tooling/**
-
- - name: Linux device_doctor
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/device_doctor/tool/build.sh
- cipd_name: flutter/device_doctor/linux-amd64
- runIf:
- - cipd_packages/device_doctor/**
- - .ci.yaml
-
- - name: Mac device_doctor
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/device_doctor/tool/build.sh
- cipd_name: flutter/device_doctor/mac-amd64
- device_type: none
- runIf:
- - cipd_packages/device_doctor/**
- - .ci.yaml
-
- - name: Mac_arm64 device_doctor
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/device_doctor/tool/build.sh
- cipd_name: flutter/device_doctor/mac-arm64
- device_type: none
- runIf:
- - cipd_packages/device_doctor/**
- - .ci.yaml
-
- - name: Windows device_doctor
- recipe: cocoon/cipd
- properties:
- script: cipd_packages\device_doctor\tool\build.bat
- cipd_name: flutter/device_doctor/windows-amd64
- runIf:
- - cipd_packages/device_doctor/**
- - .ci.yaml
-
- - name: Linux doxygen
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/doxygen/tool/build.sh
- cipd_name: flutter/doxygen/linux-amd64
- dependencies: >-
- [
- {"dependency": "cmake", "version": "build_id:8787856497187628321"}
- ]
- runIf:
- - cipd_packages/doxygen/**
- - .ci.yaml
-
- - name: Mac codesign
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/codesign/tool/build.sh
- cipd_name: flutter/codesign/mac-amd64
- device_type: none
- runIf:
- - cipd_packages/codesign/**
- - .ci.yaml
-
- - name: Mac_arm64 codesign
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/codesign/tool/build.sh
- cipd_name: flutter/codesign/mac-arm64
- device_type: none
- runIf:
- - cipd_packages/codesign/**
- - .ci.yaml
-
- - name: Mac ruby
- recipe: cocoon/cipd
- timeout: 60
- properties:
- script: cipd_packages/ruby/tools/build.sh
- cipd_name: flutter/ruby/mac-amd64
- device_os: iOS
- contexts: >-
- [
- "osx_sdk_devicelab"
- ]
- $flutter/osx_sdk : >-
- {
- "sdk_version": "14e300c"
- }
- runIf:
- - cipd_packages/ruby/**
- - .ci.yaml
-
- - name: Mac_arm64 ruby
- recipe: cocoon/cipd
- timeout: 60
- properties:
- script: cipd_packages/ruby/tools/build.sh
- cipd_name: flutter/ruby/mac-arm64
- device_os: iOS
- contexts: >-
- [
- "osx_sdk_devicelab"
- ]
- $flutter/osx_sdk : >-
- {
- "sdk_version": "14e300c"
- }
- runIf:
- - cipd_packages/ruby/**
- - .ci.yaml
-
- - name: Linux ci_yaml roller
- recipe: infra/ci_yaml
- properties:
- backfill: "false"
- runIf:
- - .ci.yaml
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 9642af8..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,89 +0,0 @@
-# See Dependabot documentation for all configuration options:
-# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
-
-version: 2
-enable-beta-ecosystems: true
-updates:
- # Github actions ecosystem.
- - package-ecosystem: "github-actions"
- directory: "/"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- # Pub ecosystem.
- - package-ecosystem: "pub"
- directory: "/analyze"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/app_dart"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/auto_submit"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/cipd_packages/codesign"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/dashboard"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/cipd_packages/device_doctor"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/test_utilities"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/licenses"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- # Docker ecosystem.
- - package-ecosystem: "docker"
- directory: "/app_dart"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "docker"
- directory: "/auto_submit"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- # Go ecosystem.
- - package-ecosystem: "gomod"
- directory: "/tooling"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- allow:
- - dependency-name: "github.com/slsa-framework/slsa-verifier/v2"
- # Npm ecosystem.
- - package-ecosystem: 'npm'
- directory: '/gh_actions/third_party/no-response'
- schedule:
- interval: 'daily'
diff --git a/.github/workflows/no-response_publish.yaml b/.github/workflows/no-response_publish.yaml
deleted file mode 100644
index fc3d0b4..0000000
--- a/.github/workflows/no-response_publish.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-name: no-response-publish
-
-# Declare default permissions as read only.
-permissions: read-all
-
-on:
- release:
- types: [published, edited]
- branches:
- - main
- paths:
- - 'gh_actions/third_party/no-response/**'
- - '.github/workflows/no-response_test.yaml'
- - '.github/workflows/no-response_publish.yaml'
-jobs:
- build:
- runs-on: ubuntu-latest
- permissions:
- contents: write
- if: ${{ github.repository == 'flutter/cocoon' }}
- steps:
- - name: Checkout
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- with:
- ref: ${{ github.event.release.tag_name }}
- sparse-checkout: 'gh_actions/third_party/no-response'
- sparse-checkout-cone-mode: false
- - name: move_package_to_root
- run: |
- mv -f gh_actions/third_party/no-response/{.[!.],}* ./
- rm -rf gh_actions
- - name: ls
- run: ls -la
- - name: npm_ci
- run: npm ci
- - name: npm_run_build
- run: npm run build
- - uses: JasonEtco/build-and-tag-action@dd5e4991048c325f6d85b4155e586fc211c644da
- env:
- GITHUB_TOKEN: ${{ secrets.FLUTTERGITHUBBOT_TOKEN }}
diff --git a/.github/workflows/no-response_test.yaml b/.github/workflows/no-response_test.yaml
deleted file mode 100644
index f82627e..0000000
--- a/.github/workflows/no-response_test.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: no-response-test
-
-# Declare default permissions as read only.
-permissions: read-all
-
-on:
- pull_request:
- paths:
- - 'gh_actions/third_party/no-response/**'
- - '.github/workflows/no-response_test.yaml'
- - '.github/workflows/no-response_publish.yaml'
-jobs:
- unitTest:
- runs-on: ubuntu-latest
- if: ${{ github.repository == 'flutter/cocoon' }}
- steps:
- - name: Checkout
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- with:
- ref: ${{ github.event.release.tag_name }}
- sparse-checkout: 'gh_actions/third_party/no-response'
- sparse-checkout-cone-mode: false
- - name: move_package_to_root
- run: |
- mv -f gh_actions/third_party/no-response/{.[!.],}* ./
- rm -rf gh_actions
- - name: ls
- run: ls -la
- - name: npm_ci
- run: npm ci
- - name: npm_run_ci
- run: npm run ci
- - name: npm_run_build
- run: npm run build
diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml
deleted file mode 100644
index 1cc1c8f..0000000
--- a/.github/workflows/scorecards-analysis.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-name: Scorecards supply-chain security
-on:
- # Only the default branch is supported.
- branch_protection_rule:
- push:
- branches: [ main ]
-
-# Declare default permissions as read only.
-permissions: read-all
-
-jobs:
- analysis:
- name: Scorecards analysis
- runs-on: ubuntu-latest
- if: ${{ github.repository == 'flutter/cocoon' }}
- permissions:
- # Needed to upload the results to code-scanning dashboard.
- security-events: write
- actions: read
- contents: read
- # Needed to access OIDC token.
- id-token: write
-
- steps:
- - name: "Checkout code"
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- with:
- persist-credentials: false
-
- - name: "Run analysis"
- uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736
- with:
- results_file: results.sarif
- results_format: sarif
- # Read-only PAT token. To create it,
- # follow the steps in https://github.com/ossf/scorecard-action#pat-token-creation.
- repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
- # Publish the results to enable scorecard badges. For more details, see
- # https://github.com/ossf/scorecard-action#publishing-results.
- # For private repositories, `publish_results` will automatically be set to `false`,
- # regardless of the value entered here.
- publish_results: true
-
- # Upload the results as artifacts (optional).
- - name: "Upload artifact"
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
- with:
- name: SARIF file
- path: results.sarif
- retention-days: 5
-
- # Upload the results to GitHub's code scanning dashboard.
- - name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a
- with:
- sarif_file: results.sarif
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 2466d2f..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,13 +0,0 @@
-.dart_tool
-.DS_Store
-*~
-.packages
-.pub
-**/pubspec.lock
-.flutter-plugins-dependencies
-# IntelliJ
-.idea
-*.iml
-
-# VSCode
-.vscode/
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index 5a547b7..0000000
--- a/AUTHORS
+++ /dev/null
@@ -1,7 +0,0 @@
-# Below is a list of people and organizations that have contributed
-# to the Sky project. Names should be added to the list like so:
-#
-# Name/Organization <email address>
-
-Google Inc.
-Chromium authors.
diff --git a/CI_YAML.md b/CI_YAML.md
deleted file mode 100644
index a8efb1e..0000000
--- a/CI_YAML.md
+++ /dev/null
@@ -1,310 +0,0 @@
-# Cocoon Scheduler
-
-This Dart project contains logic for constructing infrastructure configs
-to validate commits in the repositories owned by Flutter.
-
-## ci.yaml
-
-This is the config file in a repository used to tell Cocoon what tasks are used
-to validate commits. It includes both the tasks used in presubmit and postsubmit.
-
-In addition, it supports tasks from different infrastructures as long as cocoon
-supports that `scheduler`. Only `luci` and `cocoon` are supported, but contributions
-are welcome.
-
-Example config:
-```yaml
-# /.ci.yaml
-
-# Enabled branches is a list of regexes, with the assumption that these are full line matches.
-# Internally, Cocoon prefixes these with $ and suffixes with ^ to enable matches.
-enabled_branches:
- - main
- - flutter-\\d+\\.\\d+-candidate\\.\\d+
-
-# Platform properties defines common properties shared among targets from the same platform.
-platform_properties:
- linux:
- properties:
- # os will be inherited by all Linux targets, but it can be overrided at the target level
- os: Linux
-
-targets:
-# A Target is an individual unit of work that is scheduled by Flutter infra
-# Target's are composed of the following properties:
-# name: A human readable string to uniquely identify this target.
-# The first word indicates the platform this test will be run on. This should match
-# to an existing platform under platform_properties.
-# recipes: LUCI recipes the target follows to run tests
-# https://flutter.googlesource.com/recipes/+/refs/heads/main/recipes/
-# bringup: Whether this target is under active development and should not block the tree.
-# If true, will not run in presubmit and will not block postsubmit.
-# presubmit: Whether to run this target on presubmit (defaults to true).
-# postsubmit: Whether to run this target on postsubmit (defaults to true).
-# run_if: List of path regexes that can trigger this target on presubmit.
-# If none are passed, it will evaluare run_if_not. If both are empty the target
-# will always run in presubmit.
-# run_if_not: List of path regexes used to filter out presubmit targets. The target will
-# be run only if the files changed do not match any paths in this list. If run_if
-# is provided and not empty run_if_not will be ignored.
-# enabled_branches: List of strings of branches this target can run on.
-# This overrides the global enabled_branches.
-# properties: A map of string, string. Values are parsed to their closest data model.
-# postsubmit_properties: Properties that are only run on postsubmit.
-# timeout: Integer defining whole build execution time limit for all steps in minutes.
-#
-# Minimal example:
-# Linux analyze will run on all presubmit and in postsubmit.
- - name: Linux analyze
-#
-# Bringup example:
-# Linux licenses will run on postsubmit, but it also passes the properties
-# `analyze=true` to the builder. Since `bringup=true`, presubmit is not run,
-# and postsubmit runs will not block the tree.
- - name: Linux licenses
- bringup: true
- properties:
- - analyze: license
-
-#
-# Tags example:
-# This test will be categorized as host only framework test.
-# Postsubmit runs will be passed "upload_metrics: true".
- - name: Linux analyze
- properties:
- tags: >-
- ["framework", "hostonly"]
- postsubmit_properties:
- - upload_metrics: "true"
-
-#
-# Devicelab example:
-# For tests that are located https://github.com/flutter/flutter/tree/master/dev/devicelab/bin/tasks:
-# 1) target name follows format of `<platform> <taskname>`
-# 2) properties
-# 2.1) update `tags` based on hosts, devices, and tests type. These tags will be used for statistic analysis.
-# 2.2) a `taskname` property is required, which should match the task name
-#
-# Here is the target config for a task named: `analyzer_benchmark.dart`.
- - name: Linux_android analyzer_benchmark
- recipe: devicelab/devicelab_drone
- presubmit: false
- properties:
- tags: >
- ["devicelab", "android", "linux"]
- task_name: analyzer_benchmark
-```
-
-### Adding new targets
-
-All new targets should be added as `bringup: true` to ensure they do not block the tree.
-
-Targets first need to be mirrored to flutter/infra before they will be run.
-This propagation takes about 30 minutes, and will only run as non-blocking in postsubmit.
-
-The target will show runs in https://ci.chromium.org/p/flutter (under the repo). See
-https://github.com/flutter/flutter/wiki/Adding-a-new-Test-Shard for up to date information
-on the steps to promote your target to blocking.
-
-For flutter/flutter, there's a GitHub bot that will
-promote a test that has been passing for the past 50 runs.
-
-### Test Ownership
-
-**This only applies to flutter/flutter**
-
-To prevent tests from rotting, all targets are required to have a clear owner. Add an
-owner in [TESTOWNERS](https://github.com/flutter/flutter/blob/master/TESTOWNERS)
-
-### Properties
-
-Targets support specifying properties that can be passed throughout infrastructure. The
-following are a list of keys that are reserved for special use.
-
-**Properties is a Map<String, String> and any special values must be JSON encoded
-(i.e. no trailing commas). Additionally, these strings must be compatible with YAML multiline strings**
-
-**$flutter/osx_sdk**: xcode configs including sdk and runtime. **Note**: support on legacy `xcode`/`runtime`
-properties and `xcode` dependency has been deprecated.
-
-Example:
-``` yaml
-$flutter/osx_sdk : >-
- {
- "sdk_version": "14e222b",
- "runtime_versions":
- [
- "ios-16-4_14e222b",
- "ios-16-2_14c18"
- ]
- }
-```
-
-**add_recipes_cq**: String boolean whether to add this target to flutter/recipes CQ. This ensures
-changes to flutter/recipes pass on this target before landing.
-
-**dependencies**: JSON list of objects with "dependency" and optionally "version".
-The list of supported deps is in [flutter_deps recipe_module](https://cs.opensource.google/flutter/recipes/+/master:recipe_modules/flutter_deps/api.py).
-Dependencies generate a corresponding swarming cache that can be used in the
-recipe code. The path of the cache will be the name of the dependency.
-
-Versions can be located in [CIPD](https://chrome-infra-packages.appspot.com/)
-
-Example
-``` yaml
-dependencies: >-
- [
- {"dependency": "android_sdk"},
- {"dependency": "chrome_and_driver", "version": "latest"},
- {"dependency": "clang"},
- {"dependency": "goldctl"}
- ]
-```
-
-**tags**: JSON list of strings. These are currently only used in flutter/flutter to help
-with TESTOWNERSHIP and test flakiness.
-
-Example
-```yaml
-tags: >
- ["devicelab","hostonly"]
-```
-
-**test_timeout_secs** String determining seconds before timeout for an individual test step.
-Note that this is the timeout for a single test step rather than the entire build execution
-timeout.
-
-Example
-``` yaml
-test_timeout_secs: "2700"
-```
-
-**presubmit_max_attempts** The max attempts the target will be auto executed in presubmit. If it is
-not specified, the default value is `1` and it means no auto rerun will happen. If explicitly defined,
-it controls the max number of attempts. For example: `3` means it will be auto rescheduled two more times.
-
-Example
-``` yaml
-presubmit_max_attempts: "3"
-```
-
-### Updating targets
-
-#### Properties
-1. Find the cipd ref to upgrade to
- - If this is a Flutter managed package, look up its docs on uploading a new version
- - For example, JDK is at https://chrome-infra-packages.appspot.com/p/flutter_internal/java/openjdk/linux-amd64
-2. In `ci.yaml`, find a target that would be impacted by this change
- - Override the `version` specified in dependencies
- ```yaml
- - name: Linux Host Engine
- recipe: engine
- properties:
- build_host: "true"
- dependencies: >-
- [
- {"dependency": "open_jdk", "version": "11"}
- ]
- timeout: 60
- ```
- - Send PR, wait for the checks to go green (**the change takes effect on both presubmit and postsubmit as cocoon scheduling**
- **fetches latest change and applies it to new builds immediately**)
-3. If the check is red, add patches to get it green
-4. Once the PR has landed, infrastructure may take 1 or 2 commits to apply the latest properties
- 1. PRs/commits that have rebased on the changing PR do not need to wait
- 2. PRs/commits that have not rebased on the changing PR need to wait
- 3. Local LUCI runs need to wait
- 4. Package cache needs to wait for roll out
-
-**Note:** updates on other entries except `properties` will not take effect immediately. Ths PR needs
-to be landed first to wait for changes propagated in infrastructure.
-
-#### Update target platform
-
-Target depends on the prefix platform in its `name` to decide which platform to run on. This should match
-to an existing platform under `platform_properties`.
-
-If one target needs to switch running platforms, e.g. from a devicelab bot to a host only bot:
-1. Keep the old target entry
-2. Add a new entry under the new platform with
- 1. `bringup: true`
- 2. necessary dependencies
- 3. corresponding tags (tags will only be used for infra metrics analysis)
-3. Land the change with the new entry
-4. If the new target under the new platform passes in postsubmit
- 1. Remove the old target entry and mark the new target as `bringup: false`
-
-Example: say one wants to switch `Linux_android web_size__compile_test` to a vm.
-
-Existing config:
-```yaml
-- name: Linux_android web_size__compile_test
- properties:
- tags: >
- ["devicelab", "android", "linux"]
-```
-
-Add a new config:
-```yaml
-- name: Linux web_size__compile_test
- bringup: true # new target
- properties:
- dependencies: >- # optional
- [
- {"dependency": "new-dependency", "version": "new-dependency-version"}
- ]
- tags: >
- ["devicelab", "hostonly", "linux"]
-```
-
-After validating the new target passes, lands the clean up change by removing the config of old target
-`Linux_android web_size__compile_test` and removing the `bringup: true` for the new target.
-
-Note: this change may affect benchmark metrics. Notify the metrics sherrif to monitor potential regression.
-
-### External Tests
-
-Cocoon supports tests that are not owned by Flutter infrastructure. By default, these should not block the tree but act as FYI to the gardeners.
-
-1. Contact flutter-infra@ with your request (go/flutter-infra-office-hours)
-2. Add your system to SchedulerSystem (https://github.com/flutter/cocoon/blob/master/app_dart/lib/src/model/proto/internal/scheduler.proto)
-3. Add your service account to https://github.com/flutter/cocoon/blob/master/app_dart/lib/src/request_handling/swarming_authentication.dart
-4. Add a custom frontend icon - https://github.com/flutter/cocoon/blob/master/dashboard/lib/widgets/task_icon.dart
-5. Add a custom log link - https://github.com/flutter/cocoon/blob/master/dashboard/lib/logic/qualified_task.dart
-6. Wait for the next prod roll (every weekday)
-7. Add a target to `.ci.yaml`
- ```yaml
- # .ci.yaml
- # Name is an arbitrary string that will show on the build dashboard
- - name: my_external_test_a
- # External tests should not block the tree
- bringup: true
- presubmit: false
- # Scheduler must match what was added to scheduler.proto (any unique name works)
- scheduler: my_external_location
- ```
-8. Send updates to `https://flutter-dashboard.appspot.com/api/update-task-status` - https://github.com/flutter/cocoon/blob/master/app_dart/lib/src/request_handlers/update_task_status.dart
-
-
-## Scheduling Targets
-
-For targets using the Cocoon scheduler, they can run on:
- * Presubmit (via GitHub checks)
- * Postsubmit (via [build dashboard](https://flutter-dashboard.appspot.com/#/build))
-
-By default, all targets should use the Cocoon scheduler.
-
-### Presubmit Features
-
-1. GitHub checks enable targets to run immediately, and are available on the pull request page.
-2. Changes to the ci.yaml will be applied during those presubmit runs.
-3. New targets are required to be brought up with `bringup: true`
-
-### Postsubmit Features
-
-1. Targets are immediately triggered on GitHub webhooks for merged pull requests
-2. Updates are made immediate via LUCI PubSub notifications
-3. Prioritizes recently failed targets (to unblock the tree quicker)
-4. Backfills targets at a low swarming priority when nothing is actively running
-5. Batches targets that have a high queue time, and backfills in off peak hours
-6. Flakiness monitoring
diff --git a/CODEOWNERS b/CODEOWNERS
deleted file mode 100644
index b38f42c..0000000
--- a/CODEOWNERS
+++ /dev/null
@@ -1,44 +0,0 @@
-# Below is a list of Flutter team members' GitHub handles who are
-# suggested reviewers for contributions to this repository.
-#
-# These names are just suggestions. It is fine to have your changes
-# reviewed by someone else.
-
-## app_dart APIs
-app_dart/lib/src/request_handlers/check_flaky_builders.dart @keyonghan
-app_dart/lib/src/request_handlers/create_branch.dart @CaseyHillers
-app_dart/lib/src/request_handlers/dart_internal_subscription.dart @drewroengoogle
-app_dart/lib/src/request_handlers/file_flaky_issue_and_pr.dart @keyonghan
-app_dart/lib/src/request_handlers/flush_cache.dart @keyonghan
-app_dart/lib/src/request_handlers/get_build_status.dart @keyonghan
-app_dart/lib/src/request_handlers/get_release_branches.dart @CaseyHillers
-app_dart/lib/src/request_handlers/get_repos.dart @keyonghan
-app_dart/lib/src/request_handlers/get_status.dart @keyonghan
-app_dart/lib/src/request_handlers/get_green_commits.dart @XilaiZhang
-app_dart/lib/src/request_handlers/github_rate_limit_status.dart @keyonghan
-app_dart/lib/src/request_handlers/github_webhook.dart @keyonghan
-app_dart/lib/src/request_handlers/github/webhook_subscription.dart @keyonghan
-app_dart/lib/src/request_handlers/presubmit_luci_subscription.dart @keyonghan
-app_dart/lib/src/request_handlers/postsubmit_luci_subscription.dart @keyonghan
-app_dart/lib/src/request_handlers/push_build_status_to_github.dart @keyonghan
-app_dart/lib/src/request_handlers/push_gold_status_to_github.dart @Piinks
-app_dart/lib/src/request_handlers/reset_prod_task.dart @keyonghan
-app_dart/lib/src/request_handlers/reset_try_task.dart @keyonghan
-app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart @keyonghan
-app_dart/lib/src/request_handlers/scheduler/request_subscription.dart @keyonghan
-app_dart/lib/src/request_handlers/scheduler/vacuum_stale_tasks.dart @keyonghan
-app_dart/lib/src/request_handlers/update_existing_flaky_issues.dart @keyonghan
-app_dart/lib/src/request_handlers/update_task_status.dart @keyonghan
-app_dart/lib/src/request_handlers/vacuum_github_commits.dart @keyonghan
-
-## auto_submit app
-auto_submit @ricardoamador
-
-## cipd packages
-cipd_packages/codesign/** @XilaiZhang
-cipd_packages/device_doctor/** @yusuf-goog
-cipd_packages/doxygen/** @gspencergoog
-cipd_packages/ruby/** @godofredoc
-
-## gh_actions
-gh_actions/third_party/no-response/** @godofredoc
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 3bf36c8..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,37 +0,0 @@
-Want to contribute? Great! First, read this page (including the small print at
-the end).
-
-### Before you contribute
-
-Before we can use your code, you must sign the
-[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual)
-(CLA), which you can do online. The CLA is necessary mainly because you own the
-copyright to your changes, even after your contribution becomes part of our
-codebase, so we need your permission to use and distribute your code. We also
-need to be sure of various other things—for instance that you'll tell us if you
-know that your code infringes on other people's patents. You don't have to sign
-the CLA until after you've submitted your code for review and a member has
-approved it, but you must do it before we can put your code into our codebase.
-
-Before you start working on a larger contribution, you should get in touch with
-us first through the issue tracker with your idea so that we can help out and
-possibly guide you. Coordinating up front makes it much easier to avoid
-frustration later on.
-
-### Code reviews
-
-All submissions, including submissions by project members, require review.
-
-### File headers
-
-All files in the project must start with the following header.
-
- // Copyright (c) 2016, the Flutter project authors. Please see the AUTHORS file
- // for details. All rights reserved. Use of this source code is governed by a
- // BSD-style license that can be found in the LICENSE file.
-
-### The small print
-
-Contributions made by corporations are covered by a different agreement than the
-one above, the
-[Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate).
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index d5384ca..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright 2016 The Flutter Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
deleted file mode 100644
index 07eb0f8..0000000
--- a/README.md
+++ /dev/null
@@ -1,126 +0,0 @@
-<a href="https://github.com/flutter/cocoon">
- <h1 align="center">
- <picture>
- <source media="(prefers-color-scheme: dark)" srcset="https://storage.googleapis.com/cms-storage-bucket/6e19fee6b47b36ca613f.png">
- <img alt="Flutter" src="https://storage.googleapis.com/cms-storage-bucket/c823e53b3a1a7b0d36a9.png">
- </picture>
- </h1>
-</a>
-
-
-[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/flutter/cocoon/badge)](https://api.securityscorecards.dev/projects/github.com/flutter/cocoon)
-[![SLSA 3](https://slsa.dev/images/gh-badge-level3.svg)](https://slsa.dev)
-
-**Cocoon** is a Dart App Engine custom runtime (backend) with a frontend
-of Flutter apps (build and repository dashboard). Cocoon coordinates
-and aggregates the results of [flutter/flutter](https://github.com/flutter/flutter)
-builds.
-
-It is not designed to help developers build Flutter apps.
-
-Cocoon is not a Google product.
-
-
-# Using Cocoon
-
-## Forcing a refresh from GitHub
-
-The server is driven by commits made to
-https://github.com/flutter/flutter repo. It periodically syncs new
-commits. If you need to manually force a refresh, query
-`https://flutter-dashboard.appspot.com/api/refresh-github-commits`.
-
-You will need to be authenticated with Cocoon to do this.
-
-
-# Developing Cocoon
-
-Cocoon has several components:
-
-* A server, which coordinates everything. This is a Dart App Engine
- application. If you have never used that before, you may want to
- [peruse the samples for Dart App
- Engine](https://github.com/dart-lang/appengine_samples). The server
- is found in [app_dart](app_dart/).
-
-* A Flutter app (generally used as a Web app) for the build
- dashboards. The dashboard is found in [dashboard](dashboard/).
-
-Cocoon creates a _checklist_ for each Flutter commit. A checklist is
-made of multiple _tasks_. Tasks are _performed_ by _LUCI bots_.
-
-
-## Getting started
-
-First, [set up a Flutter development
-environment](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md#developing-for-flutter).
-This will, as a side-effect, provide you with a Dart SDK. Your life
-will be easier if you add that (`.../flutter/bin/cache/dart-sdk/bin/`)
-to your path.
-
-To update the production server, you will need the [Google Cloud
-SDK](https://cloud.google.com/sdk/docs/quickstarts). Since there is no
-Dart SDK, we just use the command line tools.
-
-
-## Developing the server
-
-All the commands in this section assume that you are in the
-`app_dart/` directory.
-
-### Running a local dev server
-
-**This is for legacy users who were granted old security keys. Due to overground, this is no longer supported.**
-
-```sh
-$ export GOOGLE_CLOUD_PROJECT=flutter-dashboard-dev # or flutter-dashboard for prod data
-$ export GCLOUD_KEY=#your_secret # Required for reading/writing from Google Cloud
-$ export COCOON_USE_IN_MEMORY_CACHE=true # Use an in memory cache locally instead of redis to prevent corruption
-$ dart bin/server.dart
-```
-This will output `Serving requests at 0.0.0.0:8080` indicating the server is working.
-
-New requests will be logged to the console.
-
-To develop and test some features, you need to have a local service
-account(key.json) with access to the project you will be connecting to.
-
-If you work for Google you can use the key with flutter-dashboard project
-via [internal doc](https://g3doc.corp.google.com/company/teams/flutter/infrastructure/cocoon/local_development.md?cl=head#test-with-flutter-dashboard-dev-project).
-
-### Deploying a test version on Google Cloud
-
-To run live tests, build the app, and provide instructions for deploying to
-Google App Engine, run this command:
-
-```sh
-dart dev/deploy.dart --project {PROJECT} --version {VERSION}
-```
-
-You can test the new version by accessing
-`{VERSION}-dot-flutter-dashboard.appspot.com` in your browser. If the
-result is satisfactory, the new version can be activated by using the
-Cloud Console UI:
-<https://console.cloud.google.com/appengine/versions?project=flutter-dashboard&serviceId=default>
-
-#### Optional flags
-
-`--profile`: Deploy a profile mode of `dashboard` application for debugging purposes.
-
-`--ignore-version-check`: Ignore the version of Flutter on path (expects to be relatively recent)
-
-
-## Developing the dashboard
-
-The dashboard application will use dummy data when it is not connected
-to the server, so it can be developed locally without a dev server.
-
-To run the dashboard locally, go into the `dashboard` directory and
-run `flutter run -d chrome`. The dashboard will be served from localhost
-(the exact address will be given on the console); copy the URL into
-your browser to view the application. (The dashboard should also be
-able to run on non-Web platforms, but since the Web is our main target
-that is the one that should generally be used for development.)
-
-You can run `flutter packages upgrade` to update the dependencies.
-This may be necessary if you see a failure in the dependencies.
diff --git a/gh_actions/third_party/no-response/action.yml b/action.yml
similarity index 100%
rename from gh_actions/third_party/no-response/action.yml
rename to action.yml
diff --git a/analysis_options.yaml b/analysis_options.yaml
deleted file mode 100644
index d7f6f0f..0000000
--- a/analysis_options.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
-# Specify analysis options for all of flutter/cocoon
-#
-# Until there are meta linter rules, each desired lint must be explicitly enabled.
-# See: https://github.com/dart-lang/linter/issues/288
-#
-# For a list of lints, see: http://dart-lang.github.io/linter/lints/
-# See the configuration guide for more
-# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
-#
-# There are other similar analysis options files in the flutter repos,
-# which should be kept in sync with this file:
-#
-# - analysis_options.yaml (this file)
-# - packages/flutter/lib/analysis_options_user.yaml
-# - https://github.com/flutter/packages/blob/main/analysis_options.yaml
-# - https://github.com/flutter/engine/blob/main/analysis_options.yaml
-#
-# This file contains the analysis options used by Flutter tools, such as IntelliJ,
-# Android Studio, and the `flutter analyze` command.
-include: package:flutter_lints/flutter.yaml
-
-analyzer:
- language:
- strict-casts: false
- strict-raw-types: true
- errors:
- # treat missing required parameters as a warning (not a hint)
- missing_required_param: warning
- # treat missing returns as a warning (not a hint)
- missing_return: warning
- # allow having TODOs in the code
- todo: ignore
- exclude:
- - ".dart_tool/**"
- - "**/*.g.dart"
- - "**/*.pb.dart"
- - "**/*.pbjson.dart"
- - "**/*.pbgrpc.dart"
- - "**/*.pbserver.dart"
- - "**/*.pbenum.dart"
- - "lib/generated_plugin_registrant.dart"
- - "test/**/mocks.mocks.dart"
-
-linter:
- rules:
- use_super_parameters: true
- prefer_final_fields: true
- prefer_final_locals: true
- prefer_single_quotes: true
- require_trailing_commas: true
- unawaited_futures: true
- unnecessary_await_in_return: true
diff --git a/analyze/analyze.dart b/analyze/analyze.dart
deleted file mode 100644
index 5cf5f25..0000000
--- a/analyze/analyze.dart
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2020 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:convert';
-import 'dart:core';
-import 'dart:io';
-
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:path/path.dart' as path;
-
-const FileSystem fs = LocalFileSystem();
-
-// Cocoon's root is the parent of the current working directory,
-final Directory cocoonRoot = fs.currentDirectory.parent;
-
-Future<void> main(List<String> arguments) async {
- print('STARTING ANALYSIS');
- print('cocoonRoot: ${cocoonRoot.path}');
- await run(arguments);
- print('Analysis successful.');
-}
-
-Future<void> run(List<String> arguments) async {
- bool assertsEnabled = false;
- assert(() {
- assertsEnabled = true;
- return true;
- }());
- if (!assertsEnabled) {
- exitWithError(<String>['The analyze.dart script must be run with --enable-asserts.']);
- }
-
- print('Trailing spaces...');
- await verifyNoTrailingSpaces(cocoonRoot.path);
-
- print('Executable allowlist...');
- await _checkForNewExecutables();
-
- print('Proto analysis...');
- await verifyProtos(cocoonRoot);
-}
-
-// TESTS
-
-Future<void> verifyNoTrailingSpaces(
- String workingDirectory, {
- int minimumMatches = 100,
-}) async {
- final List<File> files = await _allFiles(workingDirectory, null, minimumMatches: minimumMatches)
- .where((File file) => path.basename(file.path) != 'serviceaccount.enc')
- .where((File file) => path.basename(file.path) != 'Ahem.ttf')
- .where((File file) => path.extension(file.path) != '.snapshot')
- .where((File file) => path.extension(file.path) != '.png')
- .where((File file) => path.extension(file.path) != '.jpg')
- .where((File file) => path.extension(file.path) != '.ico')
- .where((File file) => path.extension(file.path) != '.jar')
- .where((File file) => path.extension(file.path) != '.swp')
- .where((File file) => !path.basename(file.path).endsWith('pbserver.dart'))
- .where((File file) => !path.basename(file.path).endsWith('pb.dart'))
- .where((File file) => !path.basename(file.path).endsWith('pbenum.dart'))
- .toList();
- final List<String> problems = <String>[];
- for (final File file in files) {
- final List<String> lines = file.readAsLinesSync();
- for (int index = 0; index < lines.length; index += 1) {
- if (lines[index].endsWith(' ')) {
- problems.add('${file.path}:${index + 1}: trailing U+0020 space character');
- } else if (lines[index].endsWith('\t')) {
- problems.add('${file.path}:${index + 1}: trailing U+0009 tab character');
- }
- }
- if (lines.isNotEmpty && lines.last == '') problems.add('${file.path}:${lines.length}: trailing blank line');
- }
- if (problems.isNotEmpty) exitWithError(problems);
-}
-
-Future<void> verifyProtos(Directory workingDirectory) async {
- final List<String> errors = <String>[];
- final List<File> protos = await _allFiles(workingDirectory.path, 'proto', minimumMatches: 1).toList();
- for (final File proto in protos) {
- final String content = proto.readAsStringSync();
- if (!content.contains(RegExp(r'package\ \w+;'))) {
- errors.add('${proto.path} requires a package (https://protobuf.dev/programming-guides/proto2/#packages)');
- }
- }
-
- if (errors.isNotEmpty) {
- exitWithError(<String>[
- 'The following files are missing package declarations:',
- ...errors,
- ]);
- }
-}
-
-// UTILITY FUNCTIONS
-
-Future<List<File>> _gitFiles(String workingDirectory, {bool runSilently = true}) async {
- final EvalResult evalResult = await _evalCommand(
- 'git',
- <String>['ls-files', '-z'],
- workingDirectory: workingDirectory,
- runSilently: runSilently,
- );
- if (evalResult.exitCode != 0) {
- exitWithError(<String>[
- 'git ls-files failed with exit code ${evalResult.exitCode}',
- 'stdout:',
- evalResult.stdout,
- 'stderr:',
- evalResult.stderr,
- ]);
- }
- final List<String> filenames = evalResult.stdout.split('\x00');
- assert(filenames.last.isEmpty); // git ls-files gives a trailing blank 0x00
- filenames.removeLast();
- return filenames.map<File>((String filename) => fs.file(path.join(workingDirectory, filename))).toList();
-}
-
-Stream<File> _allFiles(String workingDirectory, String? extension, {required int minimumMatches}) async* {
- final Set<String> gitFileNamesSet = <String>{};
- gitFileNamesSet.addAll((await _gitFiles(workingDirectory)).map((File f) => path.canonicalize(f.absolute.path)));
-
- assert(extension == null || !extension.startsWith('.'), 'Extension argument should not start with a period.');
- final Set<FileSystemEntity> pending = <FileSystemEntity>{fs.directory(workingDirectory)};
- int matches = 0;
- while (pending.isNotEmpty) {
- final FileSystemEntity entity = pending.first;
- pending.remove(entity);
- if (path.extension(entity.path) == '.tmpl') continue;
- if (entity is File) {
- if (!gitFileNamesSet.contains(path.canonicalize(entity.absolute.path))) continue;
- if (path.basename(entity.path) == 'flutter_export_environment.sh') continue;
- if (path.basename(entity.path) == 'gradlew.bat') continue;
- if (path.basename(entity.path) == '.DS_Store') continue;
- if (extension == null || path.extension(entity.path) == '.$extension') {
- matches += 1;
- yield entity;
- }
- } else if (entity is Directory) {
- if (fs.file(path.join(entity.path, '.dartignore')).existsSync()) continue;
- if (path.basename(entity.path) == '.git') continue;
- if (path.basename(entity.path) == '.idea') continue;
- if (path.basename(entity.path) == '.gradle') continue;
- if (path.basename(entity.path) == '.dart_tool') continue;
- if (path.basename(entity.path) == '.idea') continue;
- if (path.basename(entity.path) == 'build') continue;
- pending.addAll(entity.listSync());
- }
- }
- assert(
- matches >= minimumMatches,
- 'Expected to find at least $minimumMatches files with extension ".$extension" in "$workingDirectory", but only found $matches.',
- );
-}
-
-class EvalResult {
- EvalResult({
- required this.stdout,
- required this.stderr,
- this.exitCode = 0,
- });
-
- final String stdout;
- final String stderr;
- final int exitCode;
-}
-
-Future<EvalResult> _evalCommand(
- String executable,
- List<String> arguments, {
- required String workingDirectory,
- Map<String, String>? environment,
- bool allowNonZeroExit = false,
- bool runSilently = false,
-}) async {
- final String commandDescription = '${path.relative(executable, from: workingDirectory)} ${arguments.join(' ')}';
- final String relativeWorkingDir = path.relative(workingDirectory);
-
- if (!runSilently) {
- print('RUNNING $relativeWorkingDir $commandDescription');
- }
-
- final Stopwatch time = Stopwatch()..start();
- final Process process = await Process.start(
- executable,
- arguments,
- workingDirectory: workingDirectory,
- environment: environment,
- );
-
- final Future<List<List<int>>> savedStdout = process.stdout.toList();
- final Future<List<List<int>>> savedStderr = process.stderr.toList();
- final int exitCode = await process.exitCode;
- final EvalResult result = EvalResult(
- stdout: utf8.decode((await savedStdout).expand<int>((List<int> ints) => ints).toList()),
- stderr: utf8.decode((await savedStderr).expand<int>((List<int> ints) => ints).toList()),
- exitCode: exitCode,
- );
-
- if (!runSilently) {
- print('ELAPSED TIME: ${time.elapsed} for $commandDescription in $relativeWorkingDir');
- }
-
- if (exitCode != 0 && !allowNonZeroExit) {
- stderr.write(result.stderr);
- exitWithError(<String>[
- 'ERROR: Last command exited with $exitCode.',
- 'Command: $commandDescription',
- 'Relative working directory: $relativeWorkingDir',
- ]);
- }
-
- return result;
-}
-
-// These files legitimately require executable permissions
-const Set<String> kExecutableAllowlist = <String>{
- 'app_dart/tool/build.sh',
- 'cipd_packages/codesign/tool/build.sh',
- 'cipd_packages/device_doctor/tool/build.sh',
- 'cipd_packages/doxygen/tool/build.sh',
- 'cloud_build/dashboard_build.sh',
- 'cloud_build/deploy_app_dart.sh',
- 'cloud_build/deploy_auto_submit.sh',
- 'cloud_build/deploy_cron_jobs.sh',
- 'cloud_build/get_docker_image_provenance.sh',
- 'cloud_build/verify_provenance.sh',
- 'dashboard/regen_mocks.sh',
- 'dev/provision_salt.sh',
- 'dev/prs_to_main.sh',
- 'format.sh',
- 'packages/buildbucket-dart/tool/regenerate.sh',
- 'test_utilities/bin/analyze.sh',
- 'test_utilities/bin/config_test_runner.sh',
- 'test_utilities/bin/dart_test_runner.sh',
- 'test_utilities/bin/flutter_test_runner.sh',
- 'test_utilities/bin/global_test_runner.dart',
- 'test_utilities/bin/licenses.sh',
- 'test_utilities/bin/prepare_environment.sh',
-};
-
-const String kShebangRegex = r'#!/usr/bin/env (bash|sh)';
-
-Future<void> _checkForNewExecutables() async {
- // 0b001001001
- const int executableBitMask = 0x49;
- final List<File> files = await _gitFiles(cocoonRoot.path);
- final List<String> relativePaths = files.map<String>((File file) {
- return path.relative(
- file.path,
- from: cocoonRoot.path,
- );
- }).toList();
- for (String allowed in kExecutableAllowlist) {
- if (!relativePaths.contains(allowed)) {
- throw Exception(
- 'File $allowed in kExecutableAllowlist in analyze/analyze.dart '
- 'does not exist. Please fix path or remove from kExecutableAllowlist.',
- );
- }
- }
- int unexpectedExecutableCount = 0;
- int unexpectedShebangShellCount = 0;
- for (final File file in files) {
- final String relativePath = path.relative(
- file.path,
- from: cocoonRoot.path,
- );
- final FileStat stat = file.statSync();
- final bool isExecutable = stat.mode & executableBitMask != 0x0;
- final bool inAllowList = kExecutableAllowlist.contains(relativePath);
- if (isExecutable && !inAllowList) {
- unexpectedExecutableCount += 1;
- print('$relativePath is executable: ${(stat.mode & 0x1FF).toRadixString(2)}');
- }
- if (inAllowList && file.path.endsWith('.sh')) {
- final String shebang = file.readAsLinesSync().first;
- if (!shebang.startsWith(RegExp(kShebangRegex))) {
- unexpectedShebangShellCount += 1;
- print("$relativePath has the initial line of $shebang, which doesn't match '$kShebangRegex'");
- }
- }
- }
- if (unexpectedExecutableCount > 0) {
- throw Exception(
- 'found $unexpectedExecutableCount unexpected executable file'
- '${unexpectedExecutableCount == 1 ? '' : 's'}! If this was intended, you '
- 'must add this file to kExecutableAllowlist in analyze/analyze.dart',
- );
- }
- if (unexpectedShebangShellCount > 0) {
- throw Exception(
- 'found $unexpectedShebangShellCount unexpected shell #! line'
- '${unexpectedShebangShellCount == 1 ? '' : 's'}! If this was intended, you '
- 'must modify kShebangRegex in analyze/analyze.dart',
- );
- }
-}
-
-void exitWithError(List<String> messages) {
- final String line = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
- print(line);
- messages.forEach(print);
- print(line);
- exit(1);
-}
diff --git a/analyze/pubspec.yaml b/analyze/pubspec.yaml
deleted file mode 100644
index 46bce17..0000000
--- a/analyze/pubspec.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: cocoon_analyze
-description: Cocoon static analysis scripts
-publish_to: none
-
-environment:
- sdk: ">=2.18.0 <4.0.0"
-
-dependencies:
- file: 7.0.0
- path: 1.8.3
- platform: 3.1.3
-
-dev_dependencies:
- mockito: 5.4.2
- test_api: 0.6.1
diff --git a/app_dart/.gitignore b/app_dart/.gitignore
deleted file mode 100644
index 3c8a157..0000000
--- a/app_dart/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-# Files and directories created by pub.
-.dart_tool/
-.packages
-
-# Conventional directory for build output.
-build/
diff --git a/app_dart/Dockerfile b/app_dart/Dockerfile
deleted file mode 100644
index 27652a3..0000000
--- a/app_dart/Dockerfile
+++ /dev/null
@@ -1,17 +0,0 @@
-# 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.
-
-
-# Dart Docker official images can be found here: https://hub.docker.com/_/dart
-FROM dart:beta@sha256:88ced76ff4a63e565872df26fe2442f060e3ecf828a272090ad10c79e9d044af
-
-WORKDIR /app
-
-# Copy app source code (except anything in .dockerignore).
-COPY . .
-RUN dart pub get
-
-# Start server.
-EXPOSE 8080
-CMD ["/usr/lib/dart/bin/dart", "/app/bin/server.dart"]
diff --git a/app_dart/LICENSE b/app_dart/LICENSE
deleted file mode 100644
index d5384ca..0000000
--- a/app_dart/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright 2016 The Flutter Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/app_dart/README.md b/app_dart/README.md
deleted file mode 100644
index d135d79..0000000
--- a/app_dart/README.md
+++ /dev/null
@@ -1,166 +0,0 @@
-# Dart backend for cocoon
-
-This folder contains a Dart based backend for Cocoon.
-
-## Building and running
-
-### Prerequisites
-
-* Install the Google Cloud Developer Tools Command Line Interface
-([`gcloud`](https://cloud.google.com/sdk/docs/quickstarts)). Then initialize it
-and authenticate yourself by running:
-
-```sh
-gcloud auth login
-gcloud init
-```
-* [Install Flutter](https://flutter.dev/docs/get-started/install )
-```sh
-export PATH="$PATH":"path/to/flutter/bin/"
-flutter upgrade
-flutter pub get
-export PATH="$PATH":"path/to/flutter/bin/cache/dart-sdk/bin/"
-```
-
-### Running the tests
-
-```sh
-$ dart test
-```
-
-### Running codegen
-
-#### JSON
-
-To update the JSON serialization generated code, run:
-
-```sh
-$ dart run build_runner build
-```
-
-Any updates should be checked into source control.
-
-#### Protobuf
-
-To update the Protocol Buffer generated code:
-
-1. [Download](https://github.com/protocolbuffers/protobuf/releases) and install
- the protocol buffer compiler (`protoc`). Once installed, update your `PATH`
- to include the path to the `protoc` binary.
-
- On Linux, use `sudo apt-get install protocol-compiler` to install.
- On macOS, use `brew install protobuf`
-
-2. Install the [`protoc_plugin`](https://pub.dev/packages/protoc_plugin) Dart
- package. Once installed, update your `PATH` to include the path to the
- `protoc_plugin/bin` directory (or `$HOME/.pub-cache/bin` if you used
- `pub global activate protoc_plugin`).
-
-3. Run the following command:
-
- ```sh
- $ protoc --dart_out=. lib/src/model/proto/**/*.proto
- ```
-
-4. Remove the unused generated files:
-
- ```sh
- $ find . -regex '.*\.\(pbjson\|pbserver\)\.dart' -delete
- ```
- (you can remove the `*.pbenum.dart` files too, except for protobuffers that actually define enums,
- like `build_status_response.proto`)
-
-### Generating cloud datastore indexes
-
-To update the indexes in the App Engine project, run:
-
-```sh
-$ gcloud datastore indexes create index.yaml
-```
-
-### Local development
-
-#### Using physical machine
-
-* Setting up the environment
-
-```sh
-export COCOON_USE_IN_MEMORY_CACHE=true
-```
-
-This environment is needed as you don't have access to the remote redis
-instance during local development.
-
-* Starting server
-
-```sh
-export COCOON_USE_IN_MEMORY_CACHE=true
-dart bin/server.dart
-```
-
-If you see Serving requests at 0.0.0.0:8080 the dev server is working.
-
-#### Using Docker
-
-* Running a local development instance
-
-Once you've installed Docker and have the `docker` command-line tool in
-your path, then you can use the following commands to build, run, stop,
-and kill a local development instance.
-
-```sh
-# Build the docker image
-$ docker build -t local .
-
-# Start the local container, clearing the console buffer and tailing the logs
-$ container_id="$(docker run -d -p 8080:8080 local)" && \
- clear && \
- printf '\e[3J' && \
- docker logs $container_id -f
-
-# Stop the local Docker container
-$ docker container ls|grep local|tr -s ' '|cut -d' ' -f1|xargs docker container stop
-
-# Remove the local Docker image
-$ docker images|grep local|tr -s ' '|cut -d' ' -f3|xargs docker rmi -f
-```
-
-* ssh into instance
-
-```sh
-$ docker exec -it <container name> /bin/bash
-```
-
-### Deploying a release to App Engine
-
-#### [Auto-deploy](go/cocoon-cloud-build#auto-deploy)
-Cocoon auto deployment has been set up via
-[Google Cloud Build](https://console.cloud.google.com/cloud-build/triggers?project=flutter-dashboard)
-daily on Workdays.
-
-#### [Manual-deploy(go/cocoon-cloud-build#manual-deploy)
-
-* Using the cloud build
-
-This is easy to deploy if you simply want a new version based on
-the latest commit. Open
-[Cloud Build dashboard](https://console.cloud.google.com/cloud-build/triggers?project=flutter-dashboard)
-and click run in the push-master trigger ([example](https://screenshot.googleplex.com/4DDy4XdVQxMKqCd))
-
-* Using a cocoon checkout
-Let `PROJECT_ID` be the Google Cloud Project ID and `VERSION` be the version you're deploying to App Engine. Visit
-https://console.cloud.google.com/appengine/versions?project=flutter-dashboard
-for the list of current versions.
-
-```sh
-$ dart dev/deploy.dart --version version-$(git rev-parse --short HEAD) --project flutter-dashboard
-```
-
-The deploy script will build the Flutter project and copy it over for deployment.
-Then it will use the Google Cloud CLI to deploy the project to AppEngine.
-
-For more options run:
-
-```sh
-$ dart dev/deploy.dart --help
-```
diff --git a/app_dart/analysis_options.yaml b/app_dart/analysis_options.yaml
deleted file mode 100644
index f4cf71f..0000000
--- a/app_dart/analysis_options.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-include: ../analysis_options.yaml
-
-linter:
- rules:
- # a few rules listed below are the ones we would like to exclude from flutter_lint package, for app_dart
- # reasons for exclusions are provided in the comments to the right
- avoid_print: false # we have necessary print calls in the code
- constant_identifier_names: false # we have all capitalized enums in check_for_waiting_pull_requests_test.dart
diff --git a/app_dart/app.yaml b/app_dart/app.yaml
deleted file mode 100644
index 2a3ec5b..0000000
--- a/app_dart/app.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright 2019 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.
-
-runtime: custom
-env: flex
-service: default
-
-readiness_check:
- path: "/readiness_check"
- check_interval_sec: 20
- timeout_sec: 20
- failure_threshold: 10
- success_threshold: 2
- app_start_timeout_sec: 600
-
-resources:
- memory_gb: 4.0
-
-handlers:
-# The Dart server handles all requests. However, this is used to ensure the
-# assets the dart server needs are uploaded to AppEngine.
-- url: /v2/(.*\.(html|css|js|ico|svg|png|jpg|map))$
- application_readable: true # So the dart server can read the files
- # If the Dart custom runtime changes and starts using this handler,
- # we want to know. This will have the app only serve HTML and we
- # will know we can remove the Dart handling code. Just swap
- # index.html to \1
- static_files: build/web/index.html
- # app_flutter's build files needed to be copied over to this project. This
- # is because the Google Cloud utility cannot go outside the scope of app_dart.
- # Navigating to ../app_flutter/build/web will silently error.
- upload: build/web/.*\.(html|css|js|ico|svg|png|jpg|map)$
diff --git a/app_dart/bin/generate_jspb.dart b/app_dart/bin/generate_jspb.dart
deleted file mode 100644
index f48082b..0000000
--- a/app_dart/bin/generate_jspb.dart
+++ /dev/null
@@ -1,70 +0,0 @@
-// 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 'dart:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/protos.dart' as pb;
-import 'package:github/github.dart';
-import 'package:http/http.dart' as http;
-import 'package:yaml/yaml.dart';
-
-Future<String> githubFileContent(
- RepositorySlug slug,
- String filePath, {
- String ref = 'master',
- Duration timeout = const Duration(seconds: 5),
-}) async {
- final Uri githubUrl = Uri.https('raw.githubusercontent.com', '${slug.fullName}/$ref/$filePath');
- return getUrl(githubUrl);
-}
-
-FutureOr<String> getUrl(Uri url) async {
- final http.Client client = http.Client();
- try {
- final http.Response response = await client.get(url);
-
- if (response.statusCode == HttpStatus.ok) {
- return response.body;
- } else {
- throw HttpException('HTTP ${response.statusCode}: $url');
- }
- } finally {
- client.close();
- }
-}
-
-Future<String> getRemoteConfigContent(String repo, String ref) async {
- final String configContent = await githubFileContent(
- RepositorySlug('flutter', repo),
- '.ci.yaml',
- ref: ref,
- );
- return configContent;
-}
-
-String getLocalConfigContent(String path) {
- final File configFile = File(path);
- return configFile.readAsStringSync();
-}
-
-Future<void> main(List<String> args) async {
- if (args.length != 1 && args.length != 2) {
- print('generate_jspb.dart \$local_ci_yaml');
- print('generate_jspb.dart \$repo \$sha');
- exit(1);
- }
- String configContent;
- if (args.length == 2) {
- configContent = await getRemoteConfigContent(args[0], args[1]);
- } else {
- configContent = getLocalConfigContent(args[0]);
- }
-
- final YamlMap configYaml = loadYaml(configContent) as YamlMap;
- final pb.SchedulerConfig schedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
-
- print(jsonEncode(schedulerConfig.toProto3Json()));
-}
diff --git a/app_dart/bin/server.dart b/app_dart/bin/server.dart
deleted file mode 100644
index e278599..0000000
--- a/app_dart/bin/server.dart
+++ /dev/null
@@ -1,320 +0,0 @@
-// Copyright 2019 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:io';
-import 'dart:math';
-
-import 'package:appengine/appengine.dart';
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/service/commit_service.dart';
-import 'package:gcloud/db.dart';
-
-/// For local development, you might want to set this to true.
-const String _kCocoonUseInMemoryCache = 'COCOON_USE_IN_MEMORY_CACHE';
-
-Future<void> main() async {
- await withAppEngineServices(() async {
- useLoggingPackageAdaptor();
-
- final bool inMemoryCache = Platform.environment[_kCocoonUseInMemoryCache] == 'true';
- final CacheService cache = CacheService(inMemory: inMemoryCache);
-
- final Config config = Config(dbService, cache);
- final AuthenticationProvider authProvider = AuthenticationProvider(config: config);
- final AuthenticationProvider swarmingAuthProvider = SwarmingAuthenticationProvider(config: config);
- final BuildBucketClient buildBucketClient = BuildBucketClient(
- accessTokenService: AccessTokenService.defaultProvider(config),
- );
-
- /// LUCI service class to communicate with buildBucket service.
- final LuciBuildService luciBuildService = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: buildBucketClient,
- pubsub: const PubSub(),
- );
-
- /// Github checks api service used to provide luci test execution status on the Github UI.
- final GithubChecksService githubChecksService = GithubChecksService(
- config,
- );
-
- // Gerrit service class to communicate with GoB.
- final GerritService gerritService = GerritService(config: config);
-
- /// Cocoon scheduler service to manage validating commits in presubmit and postsubmit.
- final Scheduler scheduler = Scheduler(
- cache: cache,
- config: config,
- githubChecksService: githubChecksService,
- luciBuildService: luciBuildService,
- );
-
- final BranchService branchService = BranchService(
- config: config,
- gerritService: gerritService,
- );
-
- final CommitService commitService = CommitService(config: config);
-
- final Map<String, RequestHandler<dynamic>> handlers = <String, RequestHandler<dynamic>>{
- '/api/check_flaky_builders': CheckFlakyBuilders(
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/create-branch': CreateBranch(
- branchService: branchService,
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/dart-internal-subscription': DartInternalSubscription(
- cache: cache,
- config: config,
- buildBucketClient: buildBucketClient,
- ),
- '/api/file_flaky_issue_and_pr': FileFlakyIssueAndPR(
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/flush-cache': FlushCache(
- config: config,
- authenticationProvider: authProvider,
- cache: cache,
- ),
- '/api/github-webhook-pullrequest': GithubWebhook(
- config: config,
- pubsub: const PubSub(),
- secret: config.webhookKey,
- topic: 'github-webhooks',
- ),
- // TODO(chillers): Move to release service. https://github.com/flutter/flutter/issues/132082
- '/api/github/frob-webhook': GithubWebhook(
- config: config,
- pubsub: const PubSub(),
- secret: config.frobWebhookKey,
- topic: 'frob-webhooks',
- ),
- '/api/github/webhook-subscription': GithubWebhookSubscription(
- config: config,
- cache: cache,
- gerritService: gerritService,
- githubChecksService: githubChecksService,
- scheduler: scheduler,
- commitService: commitService,
- ),
- '/api/presubmit-luci-subscription': PresubmitLuciSubscription(
- cache: cache,
- config: config,
- buildBucketClient: buildBucketClient,
- luciBuildService: luciBuildService,
- githubChecksService: githubChecksService,
- scheduler: scheduler,
- ),
- '/api/postsubmit-luci-subscription': PostsubmitLuciSubscription(
- cache: cache,
- config: config,
- scheduler: scheduler,
- githubChecksService: githubChecksService,
- ),
- '/api/push-build-status-to-github': PushBuildStatusToGithub(
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/push-gold-status-to-github': PushGoldStatusToGithub(
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/reset-prod-task': ResetProdTask(
- config: config,
- authenticationProvider: authProvider,
- luciBuildService: luciBuildService,
- scheduler: scheduler,
- ),
- '/api/reset-try-task': ResetTryTask(
- config: config,
- authenticationProvider: authProvider,
- scheduler: scheduler,
- ),
- '/api/scheduler/batch-backfiller': BatchBackfiller(
- config: config,
- scheduler: scheduler,
- ),
- '/api/scheduler/batch-request-subscription': SchedulerRequestSubscription(
- cache: cache,
- config: config,
- buildBucketClient: buildBucketClient,
- ),
- '/api/scheduler/vacuum-stale-tasks': VacuumStaleTasks(
- config: config,
- ),
- '/api/update_existing_flaky_issues': UpdateExistingFlakyIssue(
- config: config,
- authenticationProvider: authProvider,
- ),
-
- /// Updates task related details.
- ///
- /// This API updates task status in datastore and
- /// pushes performance metrics to skia-perf.
- ///
- /// POST: /api-update-status
- ///
- /// Parameters:
- /// CommitBranch: (string in body). Branch of commit.
- /// CommitSha: (string in body). Sha of commit.
- /// BuilderName: (string in body). Name of the luci builder.
- /// NewStatus: (string in body) required. Status of the task.
- /// ResultData: (string in body) optional. Benchmark data.
- /// BenchmarkScoreKeys: (string in body) optional. Benchmark data.
- ///
- /// Response: Status 200 OK
- '/api/update-task-status': UpdateTaskStatus(
- config: config,
- authenticationProvider: swarmingAuthProvider,
- ),
- '/api/vacuum-github-commits': VacuumGithubCommits(
- config: config,
- authenticationProvider: authProvider,
- scheduler: scheduler,
- ),
-
- /// Returns status of the framework tree.
- ///
- /// Returns serialized proto with enum representing the
- /// status of the tree and list of offending tasks.
- ///
- /// GET: /api/public/build-status
- ///
- /// Parameters:
- /// branch: (string in query) default: 'master'. Name of the repo branch.
- ///
- /// Response: Status 200 OK
- /// Returns [BuildStatusResponse]:
- /// {
- /// 1: 2,
- /// 2: [ "win_tool_tests_commands", "win_build_test", "win_module_test"]
- /// }
- '/api/public/build-status': CacheRequestHandler<Body>(
- cache: cache,
- config: config,
- delegate: GetBuildStatus(config: config),
- ttl: const Duration(seconds: 15),
- ),
- '/api/public/get-release-branches': CacheRequestHandler<Body>(
- cache: cache,
- config: config,
- delegate: GetReleaseBranches(config: config, branchService: branchService),
- ttl: const Duration(hours: 1),
- ),
-
- /// Returns task results for commits.
- ///
- /// Returns result details about each task in each checklist for every commit.
- ///
- /// GET: /api/public/get-status
- ///
- /// Parameters:
- /// branch: (string in query) default: 'master'. Name of the repo branch.
- /// lastCommitKey: (string in query) optional. Encoded commit key for the last commit to return resutls.
- ///
- /// Response: Status: 200 OK
- /// {"Statuses":[
- /// {"Checklist":{
- /// "Key":"ah..jgM",
- /// "Checklist":{"FlutterRepositoryPath":"flutter/flutter",
- /// "CreateTimestamp":1620134239000,
- /// "Commit":{"Sha":"7f1d1414cc5f0b0317272ced49a9c0b44e5c3af8",
- /// "Message":"Revert \"Migrate to ChannelBuffers.push\"",
- /// "Author":{"Login":"renyou","avatar_url":"https://avatars.githubusercontent.com/u/666474?v=4"}},"Branch":"master"}},
- /// "Stages":[{"Name":"chromebot",
- /// "Tasks":[
- /// {"Task":{
- /// "ChecklistKey":"ahF..jgM",
- /// "CreateTimestamp":1620134239000,
- /// "StartTimestamp":0,
- /// "EndTimestamp":1620136203757,
- /// "Name":"linux_cubic_bezier_perf__e2e_summary",
- /// "Attempts":1,
- /// "Flaky":false,
- /// "TimeoutInMinutes":0,
- /// "Reason":"",
- /// "BuildNumber":null,
- /// "BuildNumberList":"1279",
- /// "BuilderName":"Linux cubic_bezier_perf__e2e_summary",
- /// "luciBucket":"luci.flutter.prod",
- /// "RequiredCapabilities":["can-update-github"],
- /// "ReservedForAgentID":"",
- /// "StageName":"chromebot",
- /// "Status":"Succeeded"
- /// },
- /// ],
- /// "Status": "InProgress",
- /// ]},
- /// },
- /// }
- '/api/public/get-status': CacheRequestHandler<Body>(
- cache: cache,
- config: config,
- delegate: GetStatus(config: config),
- ),
-
- '/api/public/get-green-commits': GetGreenCommits(config: config),
-
- /// Record GitHub API quota usage in BigQuery.
- ///
- /// Pushes data to BigQuery for metric collection to
- /// analyze usage over time.
- ///
- /// This api is called via cron job.
- ///
- /// GET: /api/public/github-rate-limit-status
- ///
- /// Response: Status 200 OK
- '/api/public/github-rate-limit-status': CacheRequestHandler<Body>(
- config: config,
- cache: cache,
- ttl: const Duration(minutes: 1),
- delegate: GithubRateLimitStatus(config: config),
- ),
- '/api/public/repos': GetRepos(config: config),
-
- /// Handler for AppEngine to identify when dart server is ready to serve requests.
- '/readiness_check': ReadinessCheck(config: config),
- };
-
- return runAppEngine(
- (HttpRequest request) async {
- if (handlers.containsKey(request.uri.path)) {
- final RequestHandler<dynamic> handler = handlers[request.uri.path]!;
- await handler.service(request);
- } else {
- /// Requests with query parameters and anchors need to be trimmed to get the file path.
- // TODO(chillers): Use toFilePath(), https://github.com/dart-lang/sdk/issues/39373
- final int queryIndex =
- request.uri.path.contains('?') ? request.uri.path.indexOf('?') : request.uri.path.length;
- final int anchorIndex =
- request.uri.path.contains('#') ? request.uri.path.indexOf('#') : request.uri.path.length;
-
- /// Trim to the first instance of an anchor or query.
- final int trimIndex = min(queryIndex, anchorIndex);
- final String filePath = request.uri.path.substring(0, trimIndex);
-
- const Map<String, String> redirects = <String, String>{
- '/build.html': '/#/build',
- };
- if (redirects.containsKey(filePath)) {
- request.response.statusCode = HttpStatus.permanentRedirect;
- return request.response.redirect(Uri.parse(redirects[filePath]!));
- }
-
- await StaticFileHandler(filePath, config: config).service(request);
- }
- },
- onAcceptingConnections: (InternetAddress address, int port) {
- final String host = address.isLoopback ? 'localhost' : address.host;
- print('Serving requests at http://$host:$port/');
- },
- );
- });
-}
diff --git a/app_dart/bin/validate_scheduler_config.dart b/app_dart/bin/validate_scheduler_config.dart
deleted file mode 100644
index 3b3fb11..0000000
--- a/app_dart/bin/validate_scheduler_config.dart
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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:io';
-
-import 'package:cocoon_service/protos.dart' as pb;
-import 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:yaml/yaml.dart';
-
-void main(List<String> args) async {
- if (args.length != 1) {
- print('validate_scheduler_config.dart configPath');
- exit(1);
- }
- final String configPath = args.first;
- final File configFile = File(configPath);
- if (!configFile.existsSync()) {
- print('validate_scheduler_config.dart configPath');
- exit(1);
- }
-
- final YamlMap configYaml = loadYaml(configFile.readAsStringSync()) as YamlMap;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
- print(
- CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- ).config,
- );
-}
diff --git a/app_dart/bin/validate_task_ownership.dart b/app_dart/bin/validate_task_ownership.dart
deleted file mode 100644
index f3e18c4..0000000
--- a/app_dart/bin/validate_task_ownership.dart
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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:io' as io;
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/foundation/utils.dart';
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:github/github.dart';
-import 'package:http/http.dart' as http;
-
-/// Remote check based on flutter `repo` and the commit `ref`.
-///
-/// This currently supports `flutter/flutter` only.
-Future<List<String>> remoteCheck(String repo, String ref) async {
- final String ciYamlContent = await githubFileContent(
- RepositorySlug('flutter', repo),
- kCiYamlPath,
- httpClientProvider: () => http.Client(),
- ref: ref,
- );
- final String testOwnersContent = await githubFileContent(
- RepositorySlug('flutter', repo),
- kTestOwnerPath,
- httpClientProvider: () => http.Client(),
- ref: ref,
- );
-
- final List<String> noOwnerBuilders = validateOwnership(ciYamlContent, testOwnersContent, unfilteredTargets: true);
- return noOwnerBuilders;
-}
-
-/// Local check is based on paths to the local `.ci.yaml` and `TESTOWNERS` files.
-List<String> localCheck(String ciYamlPath, String testOwnersPath) {
- const FileSystem fs = LocalFileSystem();
- final File ciYamlFile = fs.file(ciYamlPath);
- final File testOwnersFile = fs.file(testOwnersPath);
- if (!ciYamlFile.existsSync() || !testOwnersFile.existsSync()) {
- print('Make sure ciYamlPath and testOwnersPath exist.');
- io.exit(1);
- }
- final List<String> noOwnerBuilders =
- validateOwnership(ciYamlFile.readAsStringSync(), testOwnersFile.readAsStringSync(), unfilteredTargets: true);
- return noOwnerBuilders;
-}
-
-/// Validates task ownership.
-///
-/// It expects two parameters for remote validation: the flutter `repo` and the `commit`.
-///
-/// It expects three parameters for local validation: `local` arg, the full path to the config
-/// file (`.ci.yaml`), and the full pathto the `TESTOWNERS` file.
-Future<void> main(List<String> args) async {
- if (args.length != 2 && args.length != 3) {
- print('validate_task_ownership.dart \$repo \$sha');
- print('validate_task_ownership.dart local \$local_ci_yaml \$local_TESTOWNERS');
- io.exit(1);
- }
- List<String> noOwnerBuilders;
- if (args.length == 2) {
- noOwnerBuilders = await remoteCheck(args[0], args[1]);
- } else {
- noOwnerBuilders = localCheck(args[1], args[2]);
- }
- if (noOwnerBuilders.isNotEmpty) {
- print('# Test ownership check failed.');
- print('Builders missing owner: $noOwnerBuilders');
- print('Please define ownership in https://github.com/flutter/flutter/blob/master/TESTOWNERS');
- io.exit(1);
- } else {
- print('# Test ownership check succeeded.');
- }
-}
diff --git a/app_dart/build.yaml b/app_dart/build.yaml
deleted file mode 100644
index aaf332d..0000000
--- a/app_dart/build.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-targets:
- $default:
- builders:
- source_gen|combining_builder:
- options:
- ignore_for_file:
- - always_specify_types
- - implicit_dynamic_parameter
diff --git a/app_dart/cloudbuild_app_dart.yaml b/app_dart/cloudbuild_app_dart.yaml
deleted file mode 100644
index 6f10220..0000000
--- a/app_dart/cloudbuild_app_dart.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-# Provide instructions for google Cloud Build to auto-build flutter
-# dashboard to flutter-dashboard project. Auto-build will be triggered
-# by daily schedule on `main` branch. This cloudbuild calls an additional
-# cloudbuild configuration responsible for deployment.
-#
-# This job is for generating the docker image with build provenance,
-# and the deployment job uses the generated docker image and deploys it to
-# App Engine.
-
-steps:
- # Build dashboard.
- # This step generates the dashboard files using flutter, then moves the
- # generated files into the app_dart folder, where a docker image is then
- # created in the next step.
- - name: us-docker.pkg.dev/$PROJECT_ID/flutter/flutter
- entrypoint: '/bin/bash'
- args: ['cloud_build/dashboard_build.sh']
-
- # Build docker image
- - name: 'us-docker.pkg.dev/cloud-builders/ga/v1/docker'
- args: ['build', '-t', 'us-docker.pkg.dev/$PROJECT_ID/appengine/default.version-$SHORT_SHA', 'app_dart']
-
- # Trigger the cloud build that deploys the docker image
- - name: gcr.io/cloud-builders/gcloud
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- gcloud builds submit \
- --config app_dart/cloudbuild_app_dart_deploy.yaml \
- --substitutions="SHORT_SHA=$SHORT_SHA" \
- --async
-
-timeout: 1200s
-
-images: ['us-docker.pkg.dev/$PROJECT_ID/appengine/default.version-$SHORT_SHA']
-
-# If build provenance is not generated, the docker deployment will fail.
-options:
- requestedVerifyOption: VERIFIED
diff --git a/app_dart/cloudbuild_app_dart_deploy.yaml b/app_dart/cloudbuild_app_dart_deploy.yaml
deleted file mode 100644
index ab31d39..0000000
--- a/app_dart/cloudbuild_app_dart_deploy.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-# Provide instructions for google Cloud Build to auto-build flutter
-# dashboard to flutter-dashboard project. Auto-build will be triggered
-# by daily schedule on `main` branch.
-#
-# The auto-build will be skipped if no new commits since last deployment.
-
-steps:
- # Get recently pushed docker image and associated provenance, along with the
- # correct docker digest url, including the hash.
- - name: gcr.io/cloud-builders/gcloud
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- cloud_build/get_docker_image_provenance.sh \
- us-docker.pkg.dev/$PROJECT_ID/appengine/default.version-$SHORT_SHA:latest \
- unverified_provenance.json
-
- # Verify provenance is valid before proceeding with deployment.
- - name: 'golang:1.20'
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- cloud_build/verify_provenance.sh unverified_provenance.json
-
- # Deploy a new version to google cloud.
- - name: gcr.io/cloud-builders/gcloud
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- gcloud config set project $PROJECT_ID
- latest_version=$(gcloud app versions list --hide-no-traffic --format 'value(version.id)')
- if [ "$latest_version" = "version-$SHORT_SHA" ]; then
- echo "No updates since last deployment."
- else
- bash cloud_build/deploy_app_dart.sh $PROJECT_ID $SHORT_SHA
- fi
-
-timeout: 1200s
diff --git a/app_dart/dev/deploy.dart b/app_dart/dev/deploy.dart
deleted file mode 100644
index 6e74186..0000000
--- a/app_dart/dev/deploy.dart
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2019 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 'dart:io';
-
-import 'package:args/args.dart';
-
-const String cloudbuildDirectory = 'cloud_build';
-const String workspaceDirectory = '../';
-
-const String gcloudProjectIdFlag = 'project';
-const String gcloudProjectIdAbbrFlag = 'p';
-
-const String gcloudProjectVersionFlag = 'version';
-const String gcloudProjectVersionAbbrFlag = 'v';
-
-const String ignoreVersionFlag = 'ignore-version-check';
-const String helpFlag = 'help';
-
-String? _gcloudProjectId;
-String? _gcloudProjectVersion;
-
-late bool _ignoreVersion;
-
-/// Check if [gcloudProjectIdFlag] and [gcloudProjectVersionFlag]
-/// were passed as arguments. If they were, also set [_gcloudProjectId]
-/// and [_gcloudProjectVersion] accordingly.
-bool _getArgs(ArgParser argParser, List<String> arguments) {
- final ArgResults args = argParser.parse(arguments);
-
- final bool printHelpMessage = args[helpFlag] as bool;
- if (printHelpMessage) {
- return false;
- }
-
- _gcloudProjectId = args[gcloudProjectIdFlag] as String?;
- _gcloudProjectVersion = args[gcloudProjectVersionFlag] as String?;
- _ignoreVersion = args[ignoreVersionFlag] as bool;
-
- if (_gcloudProjectId == null) {
- stderr.write('--$gcloudProjectIdFlag must be defined\n');
- return false;
- }
-
- if (_gcloudProjectVersion == null) {
- stderr.write('--$gcloudProjectVersionFlag must be defined\n');
- return false;
- }
-
- return true;
-}
-
-/// Check the Flutter version installed and make sure it is a recent version
-/// from the past 21 days.
-///
-/// Flutter tools handles the rest of the checks (e.g. Dart version) when
-/// building the project.
-Future<bool> _checkDependencies() async {
- if (_ignoreVersion) {
- return true;
- }
-
- stdout.writeln('Checking Flutter version via flutter --version');
- final ProcessResult result = await Process.run('flutter', <String>['--version']);
- final String flutterVersionOutput = result.stdout as String;
-
- // This makes an assumption that only the framework will have its version
- // printed out with the date in YYYY-MM-DD format.
- final RegExp dateRegExp = RegExp(r'([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))');
- final String flutterVersionDateRaw = dateRegExp.allMatches(flutterVersionOutput).first.group(0)!;
-
- final DateTime flutterVersionDate = DateTime.parse(flutterVersionDateRaw);
- final DateTime now = DateTime.now();
- final Duration lastUpdateToFlutter = now.difference(flutterVersionDate);
-
- return lastUpdateToFlutter.inDays < 21;
-}
-
-/// Run the Google Cloud CLI tool to deploy to [_gcloudProjectId] under
-/// version [_gcloudProjectVersion].
-Future<bool> _deployToAppEngine() async {
- stdout.writeln('Deploying to AppEngine');
-
- /// The Google Cloud deployment command is an interactive process. It will
- /// print out what it is about to do, and ask for confirmation (Y/n).
- final Process process = await Process.start(
- 'gcloud',
- <String>[
- 'app',
- 'deploy',
- '--project',
- _gcloudProjectId!,
- '--version',
- _gcloudProjectVersion!,
- '--no-promote',
- '--no-stop-previous-version',
- '--quiet',
- ],
- );
-
- /// Let this user confirm the details before Google Cloud sends for deployment.
- unawaited(stdin.pipe(process.stdin));
-
- await process.stderr.pipe(stderr);
- await process.stdout.pipe(stdout);
-
- return await process.exitCode == 0;
-}
-
-/// Run [args] in bash shell and validate it finshes with exit code 0.
-Future<void> shellCommand(List<String> args) async {
- final ProcessResult result = await Process.run(
- 'bash',
- args,
- workingDirectory: workspaceDirectory,
- );
-
- if (result.exitCode != 0) {
- print('$args failed with exit code ${result.exitCode}');
- print('stdout: ${result.stdout}');
- print('stderr: ${result.stderr}');
- exit(1);
- }
-}
-
-Future<void> main(List<String> arguments) async {
- final ArgParser argParser = ArgParser()
- ..addOption(gcloudProjectIdFlag, abbr: gcloudProjectIdAbbrFlag)
- ..addOption(gcloudProjectVersionFlag, abbr: gcloudProjectVersionAbbrFlag)
- ..addFlag(ignoreVersionFlag)
- ..addFlag(helpFlag);
-
- if (!_getArgs(argParser, arguments)) {
- stdout.write('Required flags:\n'
- '--$gcloudProjectIdFlag gcp-id\n'
- '--$gcloudProjectVersionFlag version\n\n'
- 'Optional flags:\n'
- '--$ignoreVersionFlag\tForce deploy with current Flutter version\n');
- exit(1);
- }
-
- if (!await _checkDependencies()) {
- stderr.writeln('Update Flutter to a version on master from the past 3 weeks to deploy Cocoon');
- exit(1);
- }
-
- await shellCommand(<String>['$cloudbuildDirectory/dashboard_build.sh']);
-
- if (!await _deployToAppEngine()) {
- stderr.writeln('Failed to deploy to AppEngine');
- exit(1);
- }
-}
diff --git a/app_dart/index.yaml b/app_dart/index.yaml
deleted file mode 100644
index b036efd..0000000
--- a/app_dart/index.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (c) 2016 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.
-
-indexes:
-- kind: Checklist
- ancestor: yes
- properties:
- - name: CreateTimestamp
- direction: desc
-- kind: Checklist
- properties:
- - name: Branch
- - name: CreateTimestamp
- direction: desc
-- kind: Checklist
- properties:
- - name: FlutterRepositoryPath
- - name: Branch
- - name: CreateTimestamp
- direction: desc
-- kind: Task
- ancestor: yes
- properties:
- - name: StageName
- direction: desc
-- kind: Task
- properties:
- - name: Status
- - name: CreateTimestamp
- direction: desc
-- kind: Task
- ancestor: yes
- properties:
- - name: Name
- - name: CreateTimestamp
- direction: desc
-- kind: Task
- ancestor: yes
- properties:
- - name: CreateTimestamp
- direction: desc
-- kind: LogChunk
- ancestor: no
- properties:
- - name: OwnerKey
- - name: CreateTimestamp
- direction: asc
diff --git a/app_dart/integration_test/common.dart b/app_dart/integration_test/common.dart
deleted file mode 100644
index a266851..0000000
--- a/app_dart/integration_test/common.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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:io';
-
-import 'package:github/github.dart';
-import 'package:test/test.dart';
-
-/// Validates the local tree has no outstanding changes.
-void expectNoDiff(String path) {
- final ProcessResult gitResult = Process.runSync('git', <String>['diff', '--exit-code', path]);
- if (gitResult.exitCode != 0) {
- final ProcessResult gitDiffOutput = Process.runSync('git', <String>['diff', path]);
- fail('The working tree has a diff. Ensure changes are checked in:\n${gitDiffOutput.stdout}');
- }
-}
-
-/// Wrapper class to make it easy to add new repos + branches to the validation suite.
-class SupportedConfig {
- SupportedConfig(this.slug, [this.branch = 'master']);
-
- final RepositorySlug slug;
- final String branch;
-
- @override
- String toString() => '${slug.fullName}/$branch';
-}
diff --git a/app_dart/integration_test/data/cocoon_config.json b/app_dart/integration_test/data/cocoon_config.json
deleted file mode 100644
index 8186011..0000000
--- a/app_dart/integration_test/data/cocoon_config.json
+++ /dev/null
@@ -1 +0,0 @@
-{"targets":[{"name":"Linux Cocoon","properties":{"add_recipes_cq":"true"},"runIf":[".ci.yaml","analyze/**","app_dart/**","auto_submit/**","cipd_packages/**","cloud_build/**","dashboard/**","dev/**","licenses/**","packages/**","test_utilities/**","tooling/**"],"recipe":"cocoon/cocoon"},{"name":"Linux device_doctor","properties":{"script":"cipd_packages/device_doctor/tool/build.sh","cipd_name":"flutter/device_doctor/linux-amd64"},"runIf":["cipd_packages/device_doctor/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac device_doctor","properties":{"script":"cipd_packages/device_doctor/tool/build.sh","cipd_name":"flutter/device_doctor/mac-amd64","device_type":"none"},"runIf":["cipd_packages/device_doctor/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac_arm64 device_doctor","properties":{"script":"cipd_packages/device_doctor/tool/build.sh","cipd_name":"flutter/device_doctor/mac-arm64","device_type":"none"},"runIf":["cipd_packages/device_doctor/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Windows device_doctor","properties":{"script":"cipd_packages\\device_doctor\\tool\\build.bat","cipd_name":"flutter/device_doctor/windows-amd64"},"runIf":["cipd_packages/device_doctor/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Linux doxygen","properties":{"script":"cipd_packages/doxygen/tool/build.sh","cipd_name":"flutter/doxygen/linux-amd64","dependencies":"[\n {\"dependency\": \"cmake\", \"version\": \"build_id:8787856497187628321\"}\n]"},"runIf":["cipd_packages/doxygen/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac codesign","properties":{"script":"cipd_packages/codesign/tool/build.sh","cipd_name":"flutter/codesign/mac-amd64","device_type":"none"},"runIf":["cipd_packages/codesign/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac_arm64 codesign","properties":{"script":"cipd_packages/codesign/tool/build.sh","cipd_name":"flutter/codesign/mac-arm64","device_type":"none"},"runIf":["cipd_packages/codesign/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac ruby","timeout":60,"properties":{"script":"cipd_packages/ruby/tools/build.sh","cipd_name":"flutter/ruby/mac-amd64","device_os":"iOS","contexts":"[\n \"osx_sdk_devicelab\"\n]","$flutter/osx_sdk":"{\n \"sdk_version\": \"14e300c\"\n}"},"runIf":["cipd_packages/ruby/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac_arm64 ruby","timeout":60,"properties":{"script":"cipd_packages/ruby/tools/build.sh","cipd_name":"flutter/ruby/mac-arm64","device_os":"iOS","contexts":"[\n \"osx_sdk_devicelab\"\n]","$flutter/osx_sdk":"{\n \"sdk_version\": \"14e300c\"\n}"},"runIf":["cipd_packages/ruby/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Linux ci_yaml roller","properties":{"backfill":"false"},"runIf":[".ci.yaml"],"recipe":"infra/ci_yaml"}],"enabledBranches":["main"],"platformProperties":{"linux":{"properties":{"os":"Linux","device_type":"none"}},"mac":{"properties":{"os":"Mac-12|Mac-13","cpu":"x86"}},"mac_arm64":{"properties":{"os":"Mac-12|Mac-13","cpu":"arm64"}},"windows":{"properties":{"os":"Windows","device_type":"none"}}}}
diff --git a/app_dart/integration_test/generate_jspb_test.dart b/app_dart/integration_test/generate_jspb_test.dart
deleted file mode 100644
index 2938181..0000000
--- a/app_dart/integration_test/generate_jspb_test.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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:io';
-
-import 'package:test/test.dart';
-
-import 'common.dart';
-
-/// Validates `bin/generate_jspb.dart` which is used in the ci.yaml roller script.
-Future<void> main() async {
- test('validate cocoon ci.yaml generates jspb', () async {
- final ProcessResult generateResult =
- Process.runSync('dart', <String>['run', 'bin/generate_jspb.dart', '../.ci.yaml']);
- if (generateResult.exitCode != 0) {
- fail('generate_jspb.dart failed with exit code ${generateResult.exitCode}\n'
- 'stderr: ${generateResult.stderr}\n'
- 'stdout: ${generateResult.stdout}');
- }
-
- // Update expectations file
- final File jspbExpectationsFile = File('integration_test/data/cocoon_config.json');
- jspbExpectationsFile.writeAsStringSync(generateResult.stdout as String);
-
- expectNoDiff(jspbExpectationsFile.path);
- });
-}
diff --git a/app_dart/integration_test/validate_all_ci_configs_test.dart b/app_dart/integration_test/validate_all_ci_configs_test.dart
deleted file mode 100644
index 2032f56..0000000
--- a/app_dart/integration_test/validate_all_ci_configs_test.dart
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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:core';
-import 'dart:io';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart';
-import 'package:cocoon_service/src/model/proto/internal/scheduler.pbserver.dart' as pb;
-import 'package:github/github.dart';
-import 'package:http/http.dart' as http;
-import 'package:process/process.dart';
-import 'package:test/test.dart';
-import 'package:yaml/yaml.dart';
-
-import 'common.dart';
-
-/// List of repositories that have supported .ci.yaml config files.
-final List<SupportedConfig> configs = <SupportedConfig>[
- SupportedConfig(RepositorySlug('flutter', 'cocoon'), 'main'),
- SupportedConfig(RepositorySlug('flutter', 'engine'), 'main'),
- SupportedConfig(RepositorySlug('flutter', 'flutter')),
- SupportedConfig(RepositorySlug('flutter', 'packages'), 'main'),
-];
-
-Future<void> main() async {
- for (final SupportedConfig config in configs) {
- test('validate config file of $config', () async {
- final String configContent = await githubFileContent(
- config.slug,
- kCiYamlPath,
- httpClientProvider: () => http.Client(),
- ref: config.branch,
- );
- final YamlMap configYaml = loadYaml(configContent) as YamlMap;
- final pb.SchedulerConfig currentSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
- try {
- CiYaml(
- slug: config.slug,
- branch: Config.defaultBranch(config.slug),
- config: currentSchedulerConfig,
- );
- } on FormatException catch (e) {
- fail(e.message);
- }
- });
-
- test(
- 'validate enabled branches of $config',
- () async {
- final String configContent = await githubFileContent(
- config.slug,
- kCiYamlPath,
- httpClientProvider: () => http.Client(),
- ref: config.branch,
- );
- final YamlMap configYaml = loadYaml(configContent) as YamlMap;
- final pb.SchedulerConfig schedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
-
- final List<String> githubBranches = getBranchesForRepository(config.slug);
-
- final Map<String, bool> validEnabledBranches = <String, bool>{};
- // Add config wide enabled branches
- for (String enabledBranch in schedulerConfig.enabledBranches) {
- validEnabledBranches[enabledBranch] = false;
- }
- // Add all target specific enabled branches
- for (pb.Target target in schedulerConfig.targets) {
- for (String enabledBranch in target.enabledBranches) {
- validEnabledBranches[enabledBranch] = false;
- }
- }
-
- // N^2 scan to verify all enabled branch patterns match an exist branch on the repo.
- for (String enabledBranch in validEnabledBranches.keys) {
- for (String githubBranch in githubBranches) {
- if (CiYaml.enabledBranchesMatchesCurrentBranch(<String>[enabledBranch], githubBranch)) {
- validEnabledBranches[enabledBranch] = true;
- }
- }
- }
-
- if (config.slug.name == 'engine') {
- print(githubBranches);
- print(validEnabledBranches);
- }
-
- // Verify the enabled branches
- for (String enabledBranch in validEnabledBranches.keys) {
- expect(
- validEnabledBranches[enabledBranch],
- isTrue,
- reason: '$enabledBranch does not match to a branch in ${config.slug.fullName}',
- );
- }
- },
- skip: config.slug.name == 'flutter',
- );
- }
-}
-
-/// Gets all branches for [slug].
-///
-/// Internally, uses the git on path to get the branches from the remote for [slug].
-List<String> getBranchesForRepository(RepositorySlug slug) {
- const ProcessManager processManager = LocalProcessManager();
- final ProcessResult result =
- processManager.runSync(<String>['git', 'ls-remote', '--head', 'https://github.com/${slug.fullName}']);
- final List<String> lines = (result.stdout as String).split('\n');
-
- final List<String> githubBranches = <String>[];
- for (String line in lines) {
- if (line.isEmpty) {
- continue;
- }
- // Lines follow the format of `$sha\t$ref`
- final String ref = line.split('\t')[1];
- final String branch = ref.replaceAll('refs/heads/', '');
- githubBranches.add(branch);
- }
-
- return githubBranches;
-}
diff --git a/app_dart/integration_test/validate_test_ownership_test.dart b/app_dart/integration_test/validate_test_ownership_test.dart
deleted file mode 100644
index 6710c62..0000000
--- a/app_dart/integration_test/validate_test_ownership_test.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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 'dart:convert';
-import 'dart:io';
-
-import 'package:github/github.dart';
-import 'package:process/process.dart';
-import 'package:test/test.dart';
-
-import 'common.dart';
-
-/// List of supported repositories with TESTOWNERS.
-final List<SupportedConfig> configs = <SupportedConfig>[
- SupportedConfig(RepositorySlug('flutter', 'flutter')),
-];
-
-Future<void> main() async {
- for (final SupportedConfig config in configs) {
- test('validate test ownership for $config', () async {
- const String dart = 'dart';
- const String taskExecutable = 'bin/validate_task_ownership.dart';
- final List<String> taskArgs = <String>[config.slug.name, config.branch];
-
- const ProcessManager processManager = LocalProcessManager();
- final Process process = await processManager.start(
- <String>[dart, taskExecutable, ...taskArgs],
- workingDirectory: Directory.current.path,
- );
-
- final List<String> output = <String>[];
- final List<String> error = <String>[];
-
- process.stdout
- .transform<String>(const Utf8Decoder())
- .transform<String>(const LineSplitter())
- .listen((String line) {
- stdout.writeln('[STDOUT] $line');
- output.add(line);
- });
-
- process.stderr
- .transform<String>(const Utf8Decoder())
- .transform<String>(const LineSplitter())
- .listen((String line) {
- stderr.writeln('[STDERR] $line');
- error.add(line);
- });
-
- final int exitCode = await process.exitCode;
- if (exitCode != 0) {
- for (String line in error) {
- print(line);
- }
- fail('An error has occurred.');
- }
- });
- }
-}
diff --git a/app_dart/lib/ci_yaml.dart b/app_dart/lib/ci_yaml.dart
deleted file mode 100644
index 756415a..0000000
--- a/app_dart/lib/ci_yaml.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2019 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.
-
-export 'src/model/ci_yaml/ci_yaml.dart';
-export 'src/model/ci_yaml/target.dart';
diff --git a/app_dart/lib/cocoon_service.dart b/app_dart/lib/cocoon_service.dart
deleted file mode 100644
index d0137b3..0000000
--- a/app_dart/lib/cocoon_service.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2019 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.
-
-export 'src/foundation/utils.dart';
-export 'src/model/appengine/service_account_info.dart';
-export 'src/request_handlers/check_flaky_builders.dart';
-export 'src/request_handlers/create_branch.dart';
-export 'src/request_handlers/dart_internal_subscription.dart';
-export 'src/request_handlers/file_flaky_issue_and_pr.dart';
-export 'src/request_handlers/flush_cache.dart';
-export 'src/request_handlers/get_build_status.dart';
-export 'src/request_handlers/get_release_branches.dart';
-export 'src/request_handlers/get_repos.dart';
-export 'src/request_handlers/get_status.dart';
-export 'src/request_handlers/get_green_commits.dart';
-export 'src/request_handlers/github_rate_limit_status.dart';
-export 'src/request_handlers/github_webhook.dart';
-export 'src/request_handlers/github/webhook_subscription.dart';
-export 'src/request_handlers/postsubmit_luci_subscription.dart';
-export 'src/request_handlers/presubmit_luci_subscription.dart';
-export 'src/request_handlers/push_build_status_to_github.dart';
-export 'src/request_handlers/push_gold_status_to_github.dart';
-export 'src/request_handlers/readiness_check.dart';
-export 'src/request_handlers/reset_prod_task.dart';
-export 'src/request_handlers/reset_try_task.dart';
-export 'src/request_handlers/scheduler/batch_backfiller.dart';
-export 'src/request_handlers/scheduler/request_subscription.dart';
-export 'src/request_handlers/scheduler/vacuum_stale_tasks.dart';
-export 'src/request_handlers/update_existing_flaky_issues.dart';
-export 'src/request_handlers/update_task_status.dart';
-export 'src/request_handlers/vacuum_github_commits.dart';
-export 'src/request_handling/authentication.dart';
-export 'src/request_handling/body.dart';
-export 'src/request_handling/cache_request_handler.dart';
-export 'src/request_handling/pubsub.dart';
-export 'src/request_handling/pubsub_authentication.dart';
-export 'src/request_handling/request_handler.dart';
-export 'src/request_handling/static_file_handler.dart';
-export 'src/request_handling/swarming_authentication.dart';
-export 'src/service/access_token_provider.dart';
-export 'src/service/buildbucket.dart';
-export 'src/service/branch_service.dart';
-export 'src/service/cache_service.dart';
-export 'src/service/config.dart';
-export 'src/service/gerrit_service.dart';
-export 'src/service/github_checks_service.dart';
-export 'src/service/luci_build_service.dart';
-export 'src/service/scheduler.dart';
diff --git a/app_dart/lib/protos.dart b/app_dart/lib/protos.dart
deleted file mode 100644
index 5288e7f..0000000
--- a/app_dart/lib/protos.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2019 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.
-
-export 'src/model/proto/protos.dart';
diff --git a/app_dart/lib/src/foundation/github_checks_util.dart b/app_dart/lib/src/foundation/github_checks_util.dart
deleted file mode 100644
index 7880c35..0000000
--- a/app_dart/lib/src/foundation/github_checks_util.dart
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2020 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:core';
-import 'dart:io';
-
-import 'package:github/github.dart' as github;
-import 'package:github/hooks.dart';
-import 'package:retry/retry.dart';
-
-import '../service/config.dart';
-import '../service/logging.dart';
-
-/// Wrapper class for github checkrun service. This is used to simplify
-/// mocking during testing because some of the subclasses are private.
-class GithubChecksUtil {
- const GithubChecksUtil();
- Future<Map<String, github.CheckRun>> allCheckRuns(
- github.GitHub gitHubClient,
- CheckSuiteEvent checkSuiteEvent,
- ) async {
- final List<github.CheckRun> allCheckRuns = await gitHubClient.checks.checkRuns
- .listCheckRunsInSuite(
- checkSuiteEvent.repository!.slug(),
- checkSuiteId: checkSuiteEvent.checkSuite!.id!,
- )
- .toList();
- return {for (github.CheckRun check in allCheckRuns) check.name as String: check};
- }
-
- Future<github.CheckSuite> getCheckSuite(
- github.GitHub gitHubClient,
- github.RepositorySlug slug,
- int checkSuiteId,
- ) async {
- return gitHubClient.checks.checkSuites.getCheckSuite(
- slug,
- checkSuiteId: checkSuiteId,
- );
- }
-
- /// Finds all check suites that are associated with a given git [ref].
- Future<List<github.CheckSuite>> listCheckSuitesForRef(
- github.GitHub gitHubClient,
- github.RepositorySlug slug, {
- required String ref,
- int? appId,
- String? checkName,
- }) async {
- const RetryOptions r = RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 2),
- );
- return r.retry(
- () async {
- return gitHubClient.checks.checkSuites
- .listCheckSuitesForRef(
- slug,
- ref: ref,
- appId: appId,
- checkName: checkName,
- )
- .toList();
- },
- retryIf: (Exception e) => e is github.GitHubError || e is SocketException,
- );
- }
-
- /// Sends a request to github checks api to update a [CheckRun] with a given
- /// [status] and [conclusion].
- Future<void> updateCheckRun(
- Config config,
- github.RepositorySlug slug,
- github.CheckRun checkRun, {
- github.CheckRunStatus status = github.CheckRunStatus.queued,
- github.CheckRunConclusion? conclusion,
- String? detailsUrl,
- github.CheckRunOutput? output,
- }) async {
- const RetryOptions r = RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 2),
- );
- return r.retry(
- () async {
- final github.GitHub gitHubClient = await config.createGitHubClient(slug: slug);
- await gitHubClient.checks.checkRuns.updateCheckRun(
- slug,
- checkRun,
- status: status,
- conclusion: conclusion,
- detailsUrl: detailsUrl,
- output: output,
- );
- },
- retryIf: (Exception e) => e is github.GitHubError || e is SocketException,
- );
- }
-
- Future<github.CheckRun> getCheckRun(
- Config config,
- github.RepositorySlug slug,
- int? id,
- ) async {
- const RetryOptions r = RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 2),
- );
- return r.retry(
- () async {
- final github.GitHub gitHubClient = await config.createGitHubClient(slug: slug);
- return gitHubClient.checks.checkRuns.getCheckRun(
- slug,
- checkRunId: id!,
- );
- },
- retryIf: (Exception e) => e is github.GitHubError || e is SocketException,
- );
- }
-
- /// Sends a request to GitHub's Checks API to create a new [github.CheckRun].
- ///
- /// The newly created checkrun will be associated in [slug] to [sha] as [name].
- ///
- /// Optionally, will have [output] to give information to users.
- Future<github.CheckRun> createCheckRun(
- Config config,
- github.RepositorySlug slug,
- String sha,
- String name, {
- github.CheckRunOutput? output,
- }) async {
- const RetryOptions r = RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 2),
- );
- return r.retry(
- () async {
- return _createCheckRun(
- config,
- slug,
- sha,
- name,
- output: output,
- );
- },
- retryIf: (Exception e) => true,
- onRetry: (Exception e) => log.warning(
- 'createCheckRun fails for slug: ${slug.fullName}, sha: $sha, name: $name. Exception: ${e.toString()}',
- ),
- );
- }
-
- Future<github.CheckRun> _createCheckRun(
- Config config,
- github.RepositorySlug slug,
- String sha,
- String name, {
- github.CheckRunOutput? output,
- }) async {
- final github.GitHub gitHubClient = await config.createGitHubClient(slug: slug);
- return gitHubClient.checks.checkRuns.createCheckRun(
- slug,
- name: name,
- headSha: sha,
- output: output,
- );
- }
-}
diff --git a/app_dart/lib/src/foundation/providers.dart b/app_dart/lib/src/foundation/providers.dart
deleted file mode 100644
index 3c28318..0000000
--- a/app_dart/lib/src/foundation/providers.dart
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2019 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:appengine/appengine.dart' as gae;
-import 'package:http/http.dart' as http;
-
-import 'typedefs.dart';
-
-/// Class that holds static default providers.
-class Providers {
- const Providers._();
-
- /// Default [http.Client] provider.
- ///
- /// See also:
- ///
- /// * [HttpClientProvider], which defines this interface.
- static http.Client freshHttpClient() => http.Client();
-
- /// Default [gae.Logging] provider.
- ///
- /// See also:
- ///
- /// * [LoggingProvider], which defines this interface.
- static gae.Logging serviceScopeLogger() => gae.loggingService;
-
- /// Default [gae.ClientContext] provider.
- ///
- /// See also:
- ///
- /// * [ClientContextProvider], which defines this interface.
- static gae.ClientContext serviceScopeContext() => gae.context;
-}
diff --git a/app_dart/lib/src/foundation/typedefs.dart b/app_dart/lib/src/foundation/typedefs.dart
deleted file mode 100644
index fe0ddc5..0000000
--- a/app_dart/lib/src/foundation/typedefs.dart
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 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:appengine/appengine.dart';
-import 'package:http/http.dart' as http;
-
-/// Signature for a function that returns an App Engine [ClientContext].
-///
-/// This is used in [AuthenticationProvider] to provide the client context
-/// as part of the [AuthenticatedContext].
-typedef ClientContextProvider = ClientContext Function();
-
-/// Signature for a function that returns an [HttpClient].
-///
-/// This is used by [AuthenticationProvider] to provide the HTTP client that
-/// will be used (if necessary) to verify OAuth ID tokens (JWT tokens).
-typedef HttpClientProvider = http.Client Function();
diff --git a/app_dart/lib/src/foundation/utils.dart b/app_dart/lib/src/foundation/utils.dart
deleted file mode 100644
index 346ccd7..0000000
--- a/app_dart/lib/src/foundation/utils.dart
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright 2020 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 'dart:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:http/http.dart' as http;
-import 'package:retry/retry.dart';
-import 'package:yaml/yaml.dart';
-
-import '../../protos.dart' as pb;
-import '../foundation/typedefs.dart';
-import '../model/ci_yaml/ci_yaml.dart';
-import '../model/ci_yaml/target.dart';
-import '../request_handlers/flaky_handler_utils.dart';
-import '../request_handling/exceptions.dart';
-import '../service/logging.dart';
-
-const String kCiYamlPath = '.ci.yaml';
-const String kTestOwnerPath = 'TESTOWNERS';
-
-/// Signature for a function that calculates the backoff duration to wait in
-/// between requests when GitHub responds with an error.
-///
-/// The [attempt] argument is zero-based, so if the first attempt to request
-/// from GitHub fails, and we're backing off before making the second attempt,
-/// the [attempt] argument will be zero.
-typedef GitHubBackoffCalculator = Duration Function(int attempt);
-
-/// Default backoff calculator.
-Duration twoSecondLinearBackoff(int attempt) {
- return const Duration(seconds: 2) * (attempt + 1);
-}
-
-/// Get content of [filePath] from GitHub CDN.
-Future<String> githubFileContent(
- RepositorySlug slug,
- String filePath, {
- required HttpClientProvider httpClientProvider,
- String ref = 'master',
- Duration timeout = const Duration(seconds: 5),
- RetryOptions retryOptions = const RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 3),
- ),
-}) async {
- final Uri githubUrl = Uri.https('raw.githubusercontent.com', '${slug.fullName}/$ref/$filePath');
- // git-on-borg has a different path for shas and refs to github
- final String gobRef = (ref.length < 40) ? 'refs/heads/$ref' : ref;
- final Uri gobUrl = Uri.https(
- 'flutter.googlesource.com',
- 'mirrors/${slug.name}/+/$gobRef/$filePath',
- <String, String>{
- 'format': 'text',
- },
- );
- late String content;
- try {
- await retryOptions.retry(
- () async => content = await getUrl(githubUrl, httpClientProvider, timeout: timeout),
- retryIf: (Exception e) => e is HttpException || e is NotFoundException,
- );
- } catch (e) {
- await retryOptions.retry(
- () async =>
- content = String.fromCharCodes(base64Decode(await getUrl(gobUrl, httpClientProvider, timeout: timeout))),
- retryIf: (Exception e) => e is HttpException,
- );
- }
- return content;
-}
-
-/// Return [String] of response from [url] if status is [HttpStatus.ok].
-///
-/// If [url] returns [HttpStatus.notFound] throw [NotFoundException].
-/// Otherwise, throws [HttpException].
-FutureOr<String> getUrl(
- Uri url,
- HttpClientProvider httpClientProvider, {
- Duration timeout = const Duration(seconds: 5),
-}) async {
- log.info('Making HTTP GET request for $url');
- final http.Client client = httpClientProvider();
- try {
- final http.Response response = await client.get(url).timeout(timeout);
-
- if (response.statusCode == HttpStatus.ok) {
- return response.body;
- } else if (response.statusCode == HttpStatus.notFound) {
- throw NotFoundException('HTTP ${response.statusCode}: $url');
- } else {
- log.warning('HTTP ${response.statusCode}: $url');
- throw HttpException('HTTP ${response.statusCode}: $url');
- }
- } finally {
- client.close();
- }
-}
-
-/// Expands globs string to a regex for evaluation.
-Future<RegExp> parseGlob(String glob) async {
- glob = glob.replaceAll('**', '[A-Za-z0-9_/.]+');
- glob = glob.replaceAll('*', '[A-Za-z0-9_.]+');
- return RegExp('^$glob\$');
-}
-
-/// Returns a LUCI [builder] list that covers changed [files].
-///
-/// [builders]: enabled luci builders.
-/// [files]: changed files in corresponding PRs.
-///
-/// [builder] format with run_if:
-/// {
-/// "name":"yyy",
-/// "repo":"flutter",
-/// "taskName":"zzz",
-/// "enabled":true,
-/// "run_if":["a/b/", "c/d_e/**", "f", "g*h/"]
-/// }
-/// [builder] format with run_if_not:
-/// {
-/// "name":"yyy",
-/// "repo":"flutter",
-/// "taskName":"zzz",
-/// "enabled":true,
-/// "run_if_not":["a/b/", "c/d_e/**", "f", "g*h/"]
-/// }
-/// Note: if both [run_if] and [run_if_not] are provided and not empty only
-/// [run_if] is evaluated.
-///
-/// [file] is based on repo root: `a/b/c.dart`.
-Future<List<Target>> getTargetsToRun(Iterable<Target> targets, List<String?> files) async {
- log.info('Getting targets to run from diff.');
- final List<Target> targetsToRun = <Target>[];
- for (Target target in targets) {
- final List<String> globs = target.value.runIf;
- // Handle case where [Target] initializes empty runif
- if (globs.isEmpty) {
- // Evaluate run_if_not.
- final List<String> negativeGlobs = target.value.runIfNot;
- if (negativeGlobs.isEmpty) {
- targetsToRun.add(target);
- continue;
- }
- bool shouldAdd = true;
- for (String glob in negativeGlobs) {
- final RegExp regExp = await parseGlob(glob);
- // if the file is not in any of the paths then add the target.
- if (files.any((String? file) => regExp.hasMatch(file!))) {
- shouldAdd = false;
- break;
- }
- }
- if (shouldAdd) {
- targetsToRun.add(target);
- }
- } else {
- for (String glob in globs) {
- // If a file is found within a pre-set dir, the builder needs to run. No need to check further.
- final RegExp regExp = await parseGlob(glob);
- if (glob.isEmpty || files.any((String? file) => regExp.hasMatch(file!))) {
- targetsToRun.add(target);
- break;
- }
- }
- }
- }
-
- log.info('Collected the following targets to run:');
- for (var target in targetsToRun) {
- log.info(target.value.name);
- }
-
- return targetsToRun;
-}
-
-Future<void> insertBigquery(String tableName, Map<String, dynamic> data, TabledataResource tabledataResourceApi) async {
- // Define const variables for [BigQuery] operations.
- const String projectId = 'flutter-dashboard';
- const String dataset = 'cocoon';
- final String table = tableName;
- final List<Map<String, Object>> requestRows = <Map<String, Object>>[];
-
- requestRows.add(<String, Object>{
- 'json': data,
- });
-
- // Obtain [rows] to be inserted to [BigQuery].
- final TableDataInsertAllRequest request = TableDataInsertAllRequest.fromJson(<String, dynamic>{'rows': requestRows});
-
- try {
- await tabledataResourceApi.insertAll(request, projectId, dataset, table);
- } on ApiRequestError catch (error) {
- log.warning('Failed to add to BigQuery: $error');
- }
-}
-
-/// Validate test ownership defined in [testOwnersContent] for tests configured in `ciYamlContent`.
-List<String> validateOwnership(String ciYamlContent, String testOwnersContent, {bool unfilteredTargets = false}) {
- final List<String> noOwnerBuilders = <String>[];
- final YamlMap? ciYaml = loadYaml(ciYamlContent) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ciYaml);
-
- final CiYaml ciYamlFromProto = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- );
-
- final pb.SchedulerConfig schedulerConfig = ciYamlFromProto.config;
-
- for (pb.Target target in schedulerConfig.targets) {
- final String builder = target.name;
- final BuilderType builderType = getTypeForBuilder(
- builder,
- ciYamlFromProto,
- unfilteredTargets: unfilteredTargets,
- );
-
- final String? owner = getTestOwnership(
- target,
- builderType,
- testOwnersContent,
- ).owner;
- print('$builder: $owner');
- if (owner == null) {
- noOwnerBuilders.add(builder);
- }
- }
- return noOwnerBuilders;
-}
-
-/// Utility to class to wrap related objects in.
-class Tuple<S, T, U> {
- const Tuple(this.first, this.second, this.third);
-
- final S first;
- final T second;
- final U third;
-}
diff --git a/app_dart/lib/src/model/appengine/allowed_account.dart b/app_dart/lib/src/model/appengine/allowed_account.dart
deleted file mode 100644
index 120f129..0000000
--- a/app_dart/lib/src/model/appengine/allowed_account.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2019 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:gcloud/db.dart';
-
-/// Class that represents a non-Google account that has been allowlisted to
-/// make API requests to the Flutter dashboard.
-///
-/// By default, only App Engine cronjobs, and users
-/// authenticated as "@google.com" accounts are allowed to make API requests
-/// to the Cocooon backend. This class represents instances where non-Google
-/// users have been explicitly allowlisted to make such requests.
-@Kind(name: 'AllowedAccount')
-class AllowedAccount extends Model<int> {
- /// Creates a new [AllowedAccount].
- AllowedAccount({
- Key<int>? key,
- required this.email,
- }) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- /// The email address of the account that has been allowlisted.
- @StringProperty(propertyName: 'Email', required: true)
- String email;
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', email: $email')
- ..write(')');
- return buf.toString();
- }
-}
diff --git a/app_dart/lib/src/model/appengine/branch.dart b/app_dart/lib/src/model/appengine/branch.dart
deleted file mode 100644
index 04d2786..0000000
--- a/app_dart/lib/src/model/appengine/branch.dart
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-
-@Kind(name: 'Branch', idType: IdType.String)
-class Branch extends Model<String> {
- Branch({Key<String>? key, this.lastActivity, this.channel}) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- /// The timestamp (in milliseconds since the Epoch) of the last time
- /// when current branch had activity.
- @IntProperty(propertyName: 'lastActivity', required: false)
- int? lastActivity;
-
- /// The channel of current branch
- @StringProperty(propertyName: 'channel', required: false)
- String? channel;
-
- /// [RepositorySlug] of where this commit exists.
- RepositorySlug get slug => RepositorySlug.full(repository);
-
- String get repository => key.id!.substring(0, key.id!.lastIndexOf('/'));
-
- String get name => key.id!.substring(key.id!.lastIndexOf('/') + 1);
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', branch: $name')
- ..write(', channel: $channel')
- ..write(', repository: $repository')
- ..write(', lastActivity: $lastActivity')
- ..write(')');
- return buf.toString();
- }
-
- Map<String, dynamic> toJson() {
- return <String, dynamic>{
- 'id': id,
- 'branch': <String, dynamic>{
- 'branch': name,
- 'repository': repository,
- },
- };
- }
-}
diff --git a/app_dart/lib/src/model/appengine/cocoon_config.dart b/app_dart/lib/src/model/appengine/cocoon_config.dart
deleted file mode 100644
index 25705c1..0000000
--- a/app_dart/lib/src/model/appengine/cocoon_config.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2019 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:gcloud/db.dart';
-
-@Kind(name: 'CocoonConfig', idType: IdType.String)
-class CocoonConfig extends Model<String> {
- @StringProperty(propertyName: 'ParameterValue')
- late String value;
-}
diff --git a/app_dart/lib/src/model/appengine/commit.dart b/app_dart/lib/src/model/appengine/commit.dart
deleted file mode 100644
index 592934e..0000000
--- a/app_dart/lib/src/model/appengine/commit.dart
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2019 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:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-import '../../service/datastore.dart';
-import '../../service/logging.dart';
-import 'key_converter.dart';
-
-part 'commit.g.dart';
-
-/// Class that represents a commit that has landed on the master branch of a
-/// Flutter repository.
-@Kind(name: 'Checklist', idType: IdType.String)
-class Commit extends Model<String> {
- Commit({
- Key<String>? key,
- this.sha,
- this.timestamp,
- this.author,
- this.authorAvatarUrl,
- this.message,
- this.repository,
- this.branch = 'master',
- }) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- /// Create a [Key] that can be used to lookup a [Commit] from Datastore.
- static Key<String> createKey({
- required DatastoreDB db,
- required RepositorySlug slug,
- required String gitBranch,
- required String sha,
- }) {
- return db.emptyKey.append(
- Commit,
- id: '${slug.fullName}/$gitBranch/$sha',
- );
- }
-
- /// Lookup [Commit] from Datastore.
- static Future<Commit> fromDatastore({
- required DatastoreService datastore,
- required Key<String> key,
- }) async {
- log.fine('Looking up commit by key with id: ${key.id}');
- return datastore.lookupByValue<Commit>(key);
- }
-
- /// The timestamp (in milliseconds since the Epoch) of when the commit
- /// landed.
- @IntProperty(propertyName: 'CreateTimestamp', required: true)
- int? timestamp;
-
- /// The SHA1 hash of the commit.
- @StringProperty(propertyName: 'Commit.Sha', required: true)
- String? sha;
-
- /// The GitHub username of the commit author.
- @StringProperty(propertyName: 'Commit.Author.Login')
- String? author;
-
- /// URL of the [author]'s profile image / avatar.
- ///
- /// The bytes loaded from the URL are expected to be encoded image bytes.
- @StringProperty(propertyName: 'Commit.Author.AvatarURL')
- String? authorAvatarUrl;
-
- /// The commit message.
- ///
- /// This may be null, since we didn't always load/store this property in
- /// the datastore, so historical entries won't have this information.
- @StringProperty(propertyName: 'Commit.Message', required: false)
- String? message;
-
- /// A serializable form of [slug].
- ///
- /// This will be of the form `<org>/<repo>`. e.g. `flutter/flutter`.
- @StringProperty(propertyName: 'FlutterRepositoryPath', required: true)
- String? repository;
-
- /// The branch of the commit.
- @StringProperty(propertyName: 'Branch')
- String? branch;
-
- /// [RepositorySlug] of where this commit exists.
- RepositorySlug get slug => RepositorySlug.full(repository!);
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', timestamp: $timestamp')
- ..write(', sha: $sha')
- ..write(', author: $author')
- ..write(', authorAvatarUrl: $authorAvatarUrl')
- ..write(', message: ${message?.split("\n").first}')
- ..write(', repository: $repository')
- ..write(', branch: $branch')
- ..write(')');
- return buf.toString();
- }
-}
-
-/// The serialized representation of a [Commit].
-// TODO(tvolkert): Directly serialize [Commit] once frontends migrate to new serialization format.
-@JsonSerializable(createFactory: false, ignoreUnannotated: true)
-class SerializableCommit {
- const SerializableCommit(this.commit);
-
- final Commit commit;
-
- @JsonKey(name: 'Key')
- @StringKeyConverter()
- Key<String>? get key => commit.key;
-
- @JsonKey(name: 'Checklist')
- Map<String, dynamic> get facade {
- return <String, dynamic>{
- 'FlutterRepositoryPath': commit.repository,
- 'CreateTimestamp': commit.timestamp,
- 'Commit': <String, dynamic>{
- 'Sha': commit.sha,
- 'Message': commit.message,
- 'Author': <String, dynamic>{
- 'Login': commit.author,
- 'avatar_url': commit.authorAvatarUrl,
- },
- },
- 'Branch': commit.branch,
- };
- }
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$SerializableCommitToJson(this);
-}
diff --git a/app_dart/lib/src/model/appengine/commit.g.dart b/app_dart/lib/src/model/appengine/commit.g.dart
deleted file mode 100644
index 84969e7..0000000
--- a/app_dart/lib/src/model/appengine/commit.g.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'commit.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-Map<String, dynamic> _$SerializableCommitToJson(SerializableCommit instance) => <String, dynamic>{
- 'Key': const StringKeyConverter().toJson(instance.key),
- 'Checklist': instance.facade,
- };
diff --git a/app_dart/lib/src/model/appengine/github_build_status_update.dart b/app_dart/lib/src/model/appengine/github_build_status_update.dart
deleted file mode 100644
index 63ff03c..0000000
--- a/app_dart/lib/src/model/appengine/github_build_status_update.dart
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2019 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:gcloud/db.dart';
-
-/// Class that represents an update having been posted to a GitHub PR on the
-/// status of the Flutter build.
-@Kind(name: 'GithubBuildStatusUpdate')
-class GithubBuildStatusUpdate extends Model<int> {
- GithubBuildStatusUpdate({
- Key<int>? key,
- this.repository,
- this.pr,
- this.head,
- this.status,
- this.updates,
- this.updateTimeMillis,
- }) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- static const String statusSuccess = 'success';
-
- static const String statusFailure = 'failure';
-
- @StringProperty(propertyName: 'Repository', required: true)
- String? repository;
-
- @IntProperty(propertyName: 'PR', required: true)
- int? pr;
-
- @StringProperty(propertyName: 'Head')
- String? head;
-
- @StringProperty(propertyName: 'Status', required: true)
- String? status;
-
- @IntProperty(propertyName: 'Updates', required: true)
- int? updates;
-
- /// The last time when the status is updated for the PR.
- @IntProperty(propertyName: 'UpdateTimeMillis')
- int? updateTimeMillis;
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', repository: $repository')
- ..write(', pr: $pr')
- ..write(', head: $head')
- ..write(', lastStatus: $status')
- ..write(', updates: $updates')
- ..write(', updateTimeMillis: $updateTimeMillis')
- ..write(')');
- return buf.toString();
- }
-}
diff --git a/app_dart/lib/src/model/appengine/github_gold_status_update.dart b/app_dart/lib/src/model/appengine/github_gold_status_update.dart
deleted file mode 100644
index 603407a..0000000
--- a/app_dart/lib/src/model/appengine/github_gold_status_update.dart
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2020 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:gcloud/db.dart';
-
-/// Class that represents an update having been posted to a GitHub
-/// flutter/flutter PR on the status of tryjobs generated on Flutter Gold.
-@Kind(name: 'GithubGoldStatusUpdate')
-class GithubGoldStatusUpdate extends Model<int> {
- GithubGoldStatusUpdate({
- Key<int>? key,
- this.pr,
- this.head,
- this.status,
- this.description,
- this.updates,
- this.repository,
- }) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- // The flutter-gold status cannot report a `failure` status
- // due to auto-rollers. This is why we hold a `pending` status
- // when there are image changes. This provides the opportunity
- // for images to be triaged, and the auto-roller to proceed.
- // For more context, see: https://github.com/flutter/flutter/issues/48744
-
- static const String statusCompleted = 'success';
-
- static const String statusRunning = 'pending';
-
- @IntProperty(propertyName: 'PR', required: true)
- int? pr;
-
- @StringProperty(propertyName: 'Head')
- String? head;
-
- @StringProperty(propertyName: 'Status', required: true)
- String? status;
-
- @StringProperty(propertyName: 'Description', required: true)
- String? description;
-
- @IntProperty(propertyName: 'Updates', required: true)
- int? updates;
-
- @StringProperty(propertyName: 'Repository', required: true)
- String? repository;
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', pr: $pr')
- ..write(', head: $head')
- ..write(', lastStatus: $status')
- ..write(', description $description')
- ..write(', updates: $updates')
- ..write(', repository: $repository')
- ..write(')');
- return buf.toString();
- }
-}
diff --git a/app_dart/lib/src/model/appengine/key_converter.dart b/app_dart/lib/src/model/appengine/key_converter.dart
deleted file mode 100644
index 60aba60..0000000
--- a/app_dart/lib/src/model/appengine/key_converter.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2019 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:gcloud/db.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-import 'key_helper.dart';
-
-/// A converter for [Key]s encoded as strings.
-class StringKeyConverter implements JsonConverter<Key<String>?, String> {
- const StringKeyConverter();
-
- @override
- Key<String>? fromJson(String? json) =>
- (json == null || json.isEmpty) ? null : KeyHelper().decode(json) as Key<String>;
-
- @override
- String toJson(Key<String>? key) => key == null ? '' : KeyHelper().encode(key);
-}
-
-/// A converter for [Key]s encoded as strings.
-class IntKeyConverter implements JsonConverter<Key<int>, String> {
- const IntKeyConverter();
-
- @override
- Key<int> fromJson(String json) => KeyHelper().decode(json) as Key<int>;
-
- @override
- String toJson(Key<int?> key) => KeyHelper().encode(key);
-}
diff --git a/app_dart/lib/src/model/appengine/key_helper.dart b/app_dart/lib/src/model/appengine/key_helper.dart
deleted file mode 100644
index f5d6be4..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.dart
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2019 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:convert';
-import 'dart:mirrors';
-import 'dart:typed_data';
-
-import 'package:appengine/appengine.dart';
-import 'package:appengine/appengine.dart' as gae show context;
-import 'package:fixnum/fixnum.dart';
-import 'package:gcloud/db.dart';
-import 'package:meta/meta.dart';
-
-import 'allowed_account.dart';
-import 'commit.dart';
-import 'github_build_status_update.dart';
-import 'key_helper.pb.dart';
-import 'task.dart';
-
-const Set<Type> _defaultTypes = <Type>{
- Commit,
- GithubBuildStatusUpdate,
- Task,
- AllowedAccount,
-};
-
-/// Class used to encode and decode [Key] objects.
-///
-/// The encoding uses binary-encoded protocol buffers that are then base-64 URL
-/// encoded (and the decoding reverses that process).
-///
-/// This encoding scheme is necessary to match the behavior of the Go AppEngine
-/// datastore library. This parity is required while Cocoon operates with
-/// two backends, because the serialized values vended by one backend must
-/// be deserializable by the other backend.
-@immutable
-class KeyHelper {
- KeyHelper({
- AppEngineContext? applicationContext,
- Set<Type> types = _defaultTypes,
- }) : applicationContext = applicationContext ?? gae.context.applicationContext,
- types = _populateTypes(types);
-
- /// Metadata about the App Engine application.
- final AppEngineContext applicationContext;
-
- /// Maps Dart [Model] classes to their corresponding App Engine datastore
- /// type names.
- ///
- /// This is initialized when the [KeyHelper] is created by iterating over
- /// the `types` argument to the [KeyHelper.new] constructor and looking for
- /// `@`[Kind] annotations on those classes.
- final Map<Type, Kind> types;
-
- /// Encodes the specified [key] as a base-64 encoded protocol buffer
- /// representation of the key.
- ///
- /// See also:
- ///
- /// * <https://github.com/golang/appengine/blob/b2f4a3cf3c67576a2ee09e1fe62656a5086ce880/datastore/key.go#L231>
- String encode(Key<dynamic> key) {
- final Reference reference = Reference()
- ..app = applicationContext.applicationID
- ..path = _asPath(key);
- if (applicationContext.partition.isNotEmpty) {
- reference.nameSpace = applicationContext.partition;
- }
- final Uint8List buffer = reference.writeToBuffer();
- final String base64Encoded = base64Url.encode(buffer);
- return base64Encoded.split('=').first;
- }
-
- /// Decodes the specified [encoded] string into its [Key] representation.
- ///
- /// See also:
- ///
- /// * [encode], which is the complement to this method.
- /// * <https://github.com/golang/appengine/blob/b2f4a3cf3c67576a2ee09e1fe62656a5086ce880/datastore/key.go#L244>
- Key<dynamic> decode(String encoded) {
- // Re-add padding.
- final int remainder = encoded.length % 4;
- if (remainder != 0) {
- final String padding = '=' * (4 - remainder);
- encoded += padding;
- }
-
- final Uint8List decoded = base64Url.decode(encoded);
- final Reference reference = Reference.fromBuffer(decoded);
- return reference.path.element.fold<Key<dynamic>>(
- Key<int>.emptyKey(Partition(reference.nameSpace.isEmpty ? null : reference.nameSpace)),
- (Key<dynamic> previous, Path_Element element) {
- final Iterable<MapEntry<Type, Kind>> entries =
- types.entries.where((MapEntry<Type, Kind> entry) => entry.value.name == element.type);
- if (entries.isEmpty) {
- throw StateError('Unknown type: ${element.type}');
- }
- final MapEntry<Type, Kind> entry = entries.single;
- if (entry.value.idType == IdType.String) {
- return previous.append<String>(entry.key, id: element.name);
- } else {
- return previous.append<int>(entry.key, id: element.id.toInt());
- }
- },
- );
- }
-
- static Map<Type, Kind> _populateTypes(Set<Type> types) {
- final Map<Type, Kind> result = <Type, Kind>{};
-
- for (Type type in types) {
- final ClassMirror classMirror = reflectClass(type);
- final List<InstanceMirror> kindAnnotations = classMirror.metadata
- .where((InstanceMirror annotation) => annotation.hasReflectee)
- .where((InstanceMirror annotation) => annotation.reflectee.runtimeType == Kind)
- .toList();
- if (kindAnnotations.isEmpty) {
- throw StateError('Class $type has no @Kind annotation');
- }
- final Kind annotation = kindAnnotations.single.reflectee as Kind;
- result[type] = Kind(
- name: annotation.name ?? type.toString(),
- idType: annotation.idType,
- );
- }
-
- return Map<Type, Kind>.unmodifiable(result);
- }
-
- Path _asPath(Key<dynamic> key) {
- final List<Key<dynamic>> path = <Key<dynamic>>[];
- for (Key<dynamic>? current = key; current != null && !current.isEmpty; current = current.parent) {
- path.insert(0, current);
- }
- return Path()
- ..element.addAll(
- path.map<Path_Element>((Key<dynamic> key) {
- final Path_Element element = Path_Element();
- if (key.type != null) {
- element.type = types.containsKey(key.type) ? types[key.type!]!.name! : key.type.toString();
- }
- final Object? id = key.id;
- if (id is String) {
- element.name = id;
- } else if (id is int) {
- element.id = Int64(id);
- }
- return element;
- }),
- );
- }
-}
diff --git a/app_dart/lib/src/model/appengine/key_helper.pb.dart b/app_dart/lib/src/model/appengine/key_helper.pb.dart
deleted file mode 100644
index 57bc0fb..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.pb.dart
+++ /dev/null
@@ -1,229 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/appengine/key_helper.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
-
-import 'dart:core' as $core;
-
-import 'package:fixnum/fixnum.dart' as $fixnum;
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class Path_Element extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Path.Element',
- createEmptyInstance: create)
- ..aQS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'type')
- ..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
- ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name');
-
- Path_Element._() : super();
- factory Path_Element({
- $core.String? type,
- $fixnum.Int64? id,
- $core.String? name,
- }) {
- final _result = create();
- if (type != null) {
- _result.type = type;
- }
- if (id != null) {
- _result.id = id;
- }
- if (name != null) {
- _result.name = name;
- }
- return _result;
- }
- factory Path_Element.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Path_Element.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Path_Element clone() => Path_Element()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Path_Element copyWith(void Function(Path_Element) updates) =>
- super.copyWith((message) => updates(message as Path_Element)) as Path_Element; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Path_Element create() => Path_Element._();
- Path_Element createEmptyInstance() => create();
- static $pb.PbList<Path_Element> createRepeated() => $pb.PbList<Path_Element>();
- @$core.pragma('dart2js:noInline')
- static Path_Element getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Path_Element>(create);
- static Path_Element? _defaultInstance;
-
- @$pb.TagNumber(2)
- $core.String get type => $_getSZ(0);
- @$pb.TagNumber(2)
- set type($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(2)
- $core.bool hasType() => $_has(0);
- @$pb.TagNumber(2)
- void clearType() => clearField(2);
-
- @$pb.TagNumber(3)
- $fixnum.Int64 get id => $_getI64(1);
- @$pb.TagNumber(3)
- set id($fixnum.Int64 v) {
- $_setInt64(1, v);
- }
-
- @$pb.TagNumber(3)
- $core.bool hasId() => $_has(1);
- @$pb.TagNumber(3)
- void clearId() => clearField(3);
-
- @$pb.TagNumber(4)
- $core.String get name => $_getSZ(2);
- @$pb.TagNumber(4)
- set name($core.String v) {
- $_setString(2, v);
- }
-
- @$pb.TagNumber(4)
- $core.bool hasName() => $_has(2);
- @$pb.TagNumber(4)
- void clearName() => clearField(4);
-}
-
-class Path extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Path',
- createEmptyInstance: create)
- ..pc<Path_Element>(
- 1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'element', $pb.PbFieldType.PG,
- subBuilder: Path_Element.create)
- ..hasRequiredFields = false;
-
- Path._() : super();
- factory Path({
- $core.Iterable<Path_Element>? element,
- }) {
- final _result = create();
- if (element != null) {
- _result.element.addAll(element);
- }
- return _result;
- }
- factory Path.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Path.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Path clone() => Path()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Path copyWith(void Function(Path) updates) =>
- super.copyWith((message) => updates(message as Path)) as Path; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Path create() => Path._();
- Path createEmptyInstance() => create();
- static $pb.PbList<Path> createRepeated() => $pb.PbList<Path>();
- @$core.pragma('dart2js:noInline')
- static Path getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Path>(create);
- static Path? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.List<Path_Element> get element => $_getList(0);
-}
-
-class Reference extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Reference',
- createEmptyInstance: create)
- ..aQS(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'app')
- ..aQM<Path>(14, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'path',
- subBuilder: Path.create)
- ..aOS(20, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'nameSpace');
-
- Reference._() : super();
- factory Reference({
- $core.String? app,
- Path? path,
- $core.String? nameSpace,
- }) {
- final _result = create();
- if (app != null) {
- _result.app = app;
- }
- if (path != null) {
- _result.path = path;
- }
- if (nameSpace != null) {
- _result.nameSpace = nameSpace;
- }
- return _result;
- }
- factory Reference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Reference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Reference clone() => Reference()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Reference copyWith(void Function(Reference) updates) =>
- super.copyWith((message) => updates(message as Reference)) as Reference; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Reference create() => Reference._();
- Reference createEmptyInstance() => create();
- static $pb.PbList<Reference> createRepeated() => $pb.PbList<Reference>();
- @$core.pragma('dart2js:noInline')
- static Reference getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Reference>(create);
- static Reference? _defaultInstance;
-
- @$pb.TagNumber(13)
- $core.String get app => $_getSZ(0);
- @$pb.TagNumber(13)
- set app($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(13)
- $core.bool hasApp() => $_has(0);
- @$pb.TagNumber(13)
- void clearApp() => clearField(13);
-
- @$pb.TagNumber(14)
- Path get path => $_getN(1);
- @$pb.TagNumber(14)
- set path(Path v) {
- setField(14, v);
- }
-
- @$pb.TagNumber(14)
- $core.bool hasPath() => $_has(1);
- @$pb.TagNumber(14)
- void clearPath() => clearField(14);
- @$pb.TagNumber(14)
- Path ensurePath() => $_ensure(1);
-
- @$pb.TagNumber(20)
- $core.String get nameSpace => $_getSZ(2);
- @$pb.TagNumber(20)
- set nameSpace($core.String v) {
- $_setString(2, v);
- }
-
- @$pb.TagNumber(20)
- $core.bool hasNameSpace() => $_has(2);
- @$pb.TagNumber(20)
- void clearNameSpace() => clearField(20);
-}
diff --git a/app_dart/lib/src/model/appengine/key_helper.pbenum.dart b/app_dart/lib/src/model/appengine/key_helper.pbenum.dart
deleted file mode 100644
index eef0c46..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.pbenum.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/appengine/key_helper.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
diff --git a/app_dart/lib/src/model/appengine/key_helper.pbjson.dart b/app_dart/lib/src/model/appengine/key_helper.pbjson.dart
deleted file mode 100644
index 793f8d9..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.pbjson.dart
+++ /dev/null
@@ -1,46 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/appengine/key_helper.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use pathDescriptor instead')
-const Path$json = {
- '1': 'Path',
- '2': [
- {'1': 'element', '3': 1, '4': 3, '5': 10, '6': '.Path.Element', '10': 'element'},
- ],
- '3': [Path_Element$json],
-};
-
-@$core.Deprecated('Use pathDescriptor instead')
-const Path_Element$json = {
- '1': 'Element',
- '2': [
- {'1': 'type', '3': 2, '4': 2, '5': 9, '10': 'type'},
- {'1': 'id', '3': 3, '4': 1, '5': 3, '10': 'id'},
- {'1': 'name', '3': 4, '4': 1, '5': 9, '10': 'name'},
- ],
-};
-
-/// Descriptor for `Path`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List pathDescriptor = $convert.base64Decode(
- 'CgRQYXRoEicKB2VsZW1lbnQYASADKAoyDS5QYXRoLkVsZW1lbnRSB2VsZW1lbnQaQQoHRWxlbWVudBISCgR0eXBlGAIgAigJUgR0eXBlEg4KAmlkGAMgASgDUgJpZBISCgRuYW1lGAQgASgJUgRuYW1l');
-@$core.Deprecated('Use referenceDescriptor instead')
-const Reference$json = {
- '1': 'Reference',
- '2': [
- {'1': 'app', '3': 13, '4': 2, '5': 9, '10': 'app'},
- {'1': 'name_space', '3': 20, '4': 1, '5': 9, '10': 'nameSpace'},
- {'1': 'path', '3': 14, '4': 2, '5': 11, '6': '.Path', '10': 'path'},
- ],
-};
-
-/// Descriptor for `Reference`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List referenceDescriptor = $convert.base64Decode(
- 'CglSZWZlcmVuY2USEAoDYXBwGA0gAigJUgNhcHASHQoKbmFtZV9zcGFjZRgUIAEoCVIJbmFtZVNwYWNlEhkKBHBhdGgYDiACKAsyBS5QYXRoUgRwYXRo');
diff --git a/app_dart/lib/src/model/appengine/key_helper.pbserver.dart b/app_dart/lib/src/model/appengine/key_helper.pbserver.dart
deleted file mode 100644
index fd4acbd..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/appengine/key_helper.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
-
-export 'key_helper.pb.dart';
diff --git a/app_dart/lib/src/model/appengine/key_helper.proto b/app_dart/lib/src/model/appengine/key_helper.proto
deleted file mode 100644
index 9a0313d..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.proto
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 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.
-
-syntax = "proto2";
-
-package cocoon;
-
-// Message used in the serialization of [Key] objects.
-//
-// These are serialized to protocol buffers to match the behavior of the Go
-// AppEngine datastore library. This parity is required while Cocoon operates
-// with two backends, because the serialized values vended by one backend must
-// be deserializable by the other backend.
-//
-// See also:
-//
-// * <https://github.com/golang/appengine/blob/b2f4a3cf3c67576a2ee09e1fe62656a5086ce880/internal/datastore/datastore_v3.proto#L89>
-message Path {
- repeated group Element = 1 {
- required string type = 2;
- optional int64 id = 3;
- optional string name = 4;
- }
-}
-
-// Message used in the serialization of [Key] objects.
-//
-// These are serialized to protocol buffers to match the behavior of the Go
-// AppEngine datastore library. This parity is required while Cocoon operates
-// with two backends, because the serialized values vended by one backend must
-// be deserializable by the other backend.
-//
-// See also:
-//
-// * <https://github.com/golang/appengine/blob/b2f4a3cf3c67576a2ee09e1fe62656a5086ce880/internal/datastore/datastore_v3.proto#L97>
-message Reference {
- required string app = 13;
- optional string name_space = 20;
- required Path path = 14;
-}
diff --git a/app_dart/lib/src/model/appengine/key_wrapper.dart b/app_dart/lib/src/model/appengine/key_wrapper.dart
deleted file mode 100644
index c594176..0000000
--- a/app_dart/lib/src/model/appengine/key_wrapper.dart
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2019 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:fixnum/fixnum.dart';
-import 'package:gcloud/db.dart';
-
-import '../proto/protos.dart' as pb;
-
-import 'key_helper.dart';
-
-class KeyWrapper {
- const KeyWrapper(this.key);
-
- factory KeyWrapper.fromProto(pb.RootKey root) {
- Key<dynamic> result = Key<dynamic>.emptyKey(Partition(root.namespace));
- for (pb.Key key = root.child; key.hasChild(); key = key.child) {
- final Type type = _typeFromString(key.type);
- switch (key.whichId()) {
- case pb.Key_Id.uid:
- result = result.append<int>(type, id: key.uid.toInt());
- break;
- case pb.Key_Id.name:
- result = result.append<String>(type, id: key.name);
- break;
- case pb.Key_Id.notSet:
- result = result.append<dynamic>(type);
- break;
- }
- }
-
- return KeyWrapper(result);
- }
-
- final Key<dynamic> key;
-
- pb.RootKey toProto() {
- pb.Key? previous;
- for (Key<dynamic>? slice = key; slice != null; slice = key.parent) {
- final pb.Key current = pb.Key();
- if (slice.type != null) {
- current.type = slice.type.toString();
- }
- final Object? id = slice.id;
- if (id is String) {
- current.name = id;
- } else if (id is int) {
- current.uid = Int64(id);
- }
- if (previous != null) {
- current.child = previous;
- }
- previous = current;
-
- if (slice.isEmpty) {
- return pb.RootKey()
- ..namespace = slice.partition.namespace!
- ..child = previous;
- }
- }
-
- return pb.RootKey()..child = previous!;
- }
-
- static Type _typeFromString(String value) {
- final KeyHelper keyHelper = KeyHelper();
- return keyHelper.types.keys.singleWhere((Type type) => type.toString() == value);
- }
-}
diff --git a/app_dart/lib/src/model/appengine/service_account_info.dart b/app_dart/lib/src/model/appengine/service_account_info.dart
deleted file mode 100644
index 6c2d46a..0000000
--- a/app_dart/lib/src/model/appengine/service_account_info.dart
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2019 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:googleapis_auth/googleapis_auth.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-part 'service_account_info.g.dart';
-
-/// Class that represents authentication information that enables callers to
-/// execute environment-specific tasks on behalf of their apps.
-///
-/// See also:
-///
-/// * <https://cloud.google.com/appengine/docs/flexible/python/service-account>
-///
-/// * `package:googleapis_auth`, which can make use of the service account
-/// information to obtain an OAuth 2.0 access token on behalf of the
-/// application.
-@JsonSerializable()
-class ServiceAccountInfo {
- /// Creates a new [ServiceAccountInfo] object.
- const ServiceAccountInfo({
- this.type,
- this.projectId,
- this.privateKeyId,
- this.privateKey,
- this.email,
- this.clientId,
- this.authUrl,
- this.tokenUrl,
- this.authCertUrl,
- this.clientCertUrl,
- });
-
- /// Create a new [ServiceAccountInfo] object from its JSON representation.
- factory ServiceAccountInfo.fromJson(Map<String, dynamic> json) => _$ServiceAccountInfoFromJson(json);
-
- @JsonKey(name: 'type')
- final String? type;
-
- @JsonKey(name: 'project_id')
- final String? projectId;
-
- @JsonKey(name: 'private_key_id')
- final String? privateKeyId;
-
- @JsonKey(name: 'private_key')
- final String? privateKey;
-
- @JsonKey(name: 'client_email')
- final String? email;
-
- @JsonKey(name: 'client_id')
- final String? clientId;
-
- @JsonKey(name: 'auth_uri')
- final String? authUrl;
-
- @JsonKey(name: 'token_uri')
- final String? tokenUrl;
-
- @JsonKey(name: 'auth_provider_x509_cert_url')
- final String? authCertUrl;
-
- @JsonKey(name: 'client_x509_cert_url')
- final String? clientCertUrl;
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$ServiceAccountInfoToJson(this);
-
- /// Returns this object in its [ServiceAccountCredentials] form.
- ServiceAccountCredentials asServiceAccountCredentials() {
- return ServiceAccountCredentials(email!, ClientId(clientId!, null), privateKey!);
- }
-}
diff --git a/app_dart/lib/src/model/appengine/service_account_info.g.dart b/app_dart/lib/src/model/appengine/service_account_info.g.dart
deleted file mode 100644
index be7b228..0000000
--- a/app_dart/lib/src/model/appengine/service_account_info.g.dart
+++ /dev/null
@@ -1,35 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'service_account_info.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-ServiceAccountInfo _$ServiceAccountInfoFromJson(Map<String, dynamic> json) => ServiceAccountInfo(
- type: json['type'] as String?,
- projectId: json['project_id'] as String?,
- privateKeyId: json['private_key_id'] as String?,
- privateKey: json['private_key'] as String?,
- email: json['client_email'] as String?,
- clientId: json['client_id'] as String?,
- authUrl: json['auth_uri'] as String?,
- tokenUrl: json['token_uri'] as String?,
- authCertUrl: json['auth_provider_x509_cert_url'] as String?,
- clientCertUrl: json['client_x509_cert_url'] as String?,
- );
-
-Map<String, dynamic> _$ServiceAccountInfoToJson(ServiceAccountInfo instance) => <String, dynamic>{
- 'type': instance.type,
- 'project_id': instance.projectId,
- 'private_key_id': instance.privateKeyId,
- 'private_key': instance.privateKey,
- 'client_email': instance.email,
- 'client_id': instance.clientId,
- 'auth_uri': instance.authUrl,
- 'token_uri': instance.tokenUrl,
- 'auth_provider_x509_cert_url': instance.authCertUrl,
- 'client_x509_cert_url': instance.clientCertUrl,
- };
diff --git a/app_dart/lib/src/model/appengine/stage.dart b/app_dart/lib/src/model/appengine/stage.dart
deleted file mode 100644
index c09b2ef..0000000
--- a/app_dart/lib/src/model/appengine/stage.dart
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2019 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:json_annotation/json_annotation.dart';
-import 'package:meta/meta.dart';
-
-import 'commit.dart';
-import 'task.dart';
-
-part 'stage.g.dart';
-
-/// A group of related [Task]s run against a particular [Commit].
-///
-/// Stages are grouped by the infrastructure family that runs them, such as
-/// LUCI, DeviceLab on Linux, DeviceLab on Windows, etc.
-@immutable
-@JsonSerializable(createFactory: false, ignoreUnannotated: true)
-class Stage implements Comparable<Stage> {
- const Stage(this.name, this.commit, this.tasks, this.taskStatus);
-
- const Stage._(this.name, this.commit, this.tasks, this.taskStatus);
-
- /// The fixed ordering of the stages (by name).
- ///
- /// Unknown stages will be placed at the end of any ordering.
- static const List<String?> _order = <String?>[
- 'chromebot',
- 'devicelab',
- 'devicelab_win',
- 'devicelab_ios',
- ];
-
- /// Arbitrarily large index to represent the "end of the ordering".
- static const int _endOfList = 1000000;
-
- /// The name of the stage (e.g. 'devicelab', 'devicelab_win').
- ///
- /// This is guaranteed to be non-null.
- @JsonKey(name: 'Name')
- final String? name;
-
- /// The commit that owns this stage.
- ///
- /// All [tasks] will be run against this commit.
- final Commit? commit;
-
- /// The list of tasks in this stage.
- ///
- /// These tasks will be run against [commit]. This list is guaranteed to be
- /// non-empty.
- final List<Task> tasks;
-
- /// Representation of [tasks] used for JSON serialization.
- @JsonKey(name: 'Tasks')
- List<SerializableTask> get serializableTasks {
- return tasks.map<SerializableTask>((Task task) => SerializableTask(task)).toList();
- }
-
- /// The aggregate status, accounting for all [tasks] in this stage.
- ///
- /// The status is defined as follows:
- ///
- /// * If all tasks in this stage succeeded, then [Task.statusSucceeded]
- /// * If at least one task in this stage failed, then [Task.statusFailed]
- /// * If all tasks have the same status, then that status
- /// * Else [Task.statusInProgress]
- @JsonKey(name: 'Status')
- final String taskStatus;
-
- /// Whether this stage is managed by the Flutter device lab.
- ///
- /// Stages such as 'chromebot' are not managed by the Flutter
- /// device lab.
- bool get isManagedByDeviceLab => name!.startsWith('devicelab');
-
- @override
- int compareTo(Stage other) => _orderIndex(this).compareTo(_orderIndex(other));
-
- static int _orderIndex(Stage stage) {
- int index = _order.indexOf(stage.name);
- if (index == -1) {
- // Put unknown stages last.
- index = _endOfList;
- }
- return index;
- }
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$StageToJson(this);
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('name: $name')
- ..write(', commit: ${commit?.sha}')
- ..write(', tasks: ${tasks.length}')
- ..write(', taskStatus: $taskStatus')
- ..write(')');
- return buf.toString();
- }
-}
-
-/// A mutable class used to build instances of [Stage].
-class StageBuilder {
- /// The name of the stage.
- ///
- /// See also:
- /// * [Stage.name]
- String? name;
-
- /// The commit that owns the stage.
- ///
- /// See also:
- /// * [Stage.commit]
- Commit? commit;
-
- /// The tasks within the stage, run against [commit].
- ///
- /// See also:
- /// * [Stage.commit]
- List<Task> tasks = <Task>[];
-
- /// Builds a [Stage] from the information in this builder.
- ///
- /// Throws a [StateError] if [name] is null, [commit] is null, or [tasks] is
- /// empty.
- Stage build() {
- if (name == null) {
- throw StateError('Cannot build a stage with no name');
- }
- if (commit == null) {
- throw StateError('Cannot build a stage with no commit ($name)');
- }
- if (tasks.isEmpty) {
- throw StateError('Cannot build a stage with no tasks ($name)');
- }
- return Stage._(name, commit, List<Task>.unmodifiable(tasks), _taskStatus);
- }
-
- String get _taskStatus {
- assert(tasks.isNotEmpty);
- bool isSucceeded(Task task) => task.status == Task.statusSucceeded;
- bool isFailed(Task task) => task.status == Task.statusFailed;
-
- if (tasks.every(isSucceeded)) {
- return Task.statusSucceeded;
- }
-
- if (tasks.any(isFailed)) {
- return Task.statusFailed;
- }
-
- final String? commonStatus =
- tasks.map<String?>((Task task) => task.status).reduce((String? a, String? b) => a == b ? a : null);
- return commonStatus ?? Task.statusInProgress;
- }
-}
diff --git a/app_dart/lib/src/model/appengine/stage.g.dart b/app_dart/lib/src/model/appengine/stage.g.dart
deleted file mode 100644
index 09d4849..0000000
--- a/app_dart/lib/src/model/appengine/stage.g.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'stage.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-Map<String, dynamic> _$StageToJson(Stage instance) => <String, dynamic>{
- 'Name': instance.name,
- 'Tasks': instance.serializableTasks,
- 'Status': instance.taskStatus,
- };
diff --git a/app_dart/lib/src/model/appengine/task.dart b/app_dart/lib/src/model/appengine/task.dart
deleted file mode 100644
index 3bcce62..0000000
--- a/app_dart/lib/src/model/appengine/task.dart
+++ /dev/null
@@ -1,548 +0,0 @@
-// Copyright 2019 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:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-import '../../request_handling/exceptions.dart';
-import '../../service/datastore.dart';
-import '../../service/logging.dart';
-import '../ci_yaml/target.dart';
-import '../luci/push_message.dart';
-import 'commit.dart';
-import 'key_converter.dart';
-
-import 'package:cocoon_service/src/model/luci/buildbucket.dart' as bb;
-part 'task.g.dart';
-
-/// Class that represents the intersection of a test at a particular [Commit].
-///
-/// Tasks are tests that have been run N (possibly zero) times against a
-/// particular commit.
-@JsonSerializable(createFactory: false, ignoreUnannotated: true)
-@Kind(name: 'Task')
-class Task extends Model<int> {
- /// Creates a new [Task].
- Task({
- Key<int>? key,
- this.commitKey,
- this.createTimestamp = 0,
- this.startTimestamp = 0,
- this.endTimestamp = 0,
- this.name,
- this.attempts = 0,
- this.isFlaky = false,
- this.isTestFlaky = false,
- this.timeoutInMinutes,
- this.reason = '',
- this.requiredCapabilities,
- this.reservedForAgentId = '',
- this.stageName,
- this.buildNumber,
- this.buildNumberList,
- this.builderName,
- this.luciBucket,
- String? status,
- }) : _status = status {
- if (status != null && !legalStatusValues.contains(status)) {
- throw ArgumentError('Invalid state: "$status"');
- }
- parentKey = key?.parent;
- id = key?.id;
- }
-
- /// Construct [Task] from a [Target].
- factory Task.fromTarget({
- required Commit commit,
- required Target target,
- }) {
- return Task(
- attempts: 1,
- builderName: target.value.name,
- commitKey: commit.key,
- createTimestamp: commit.timestamp!,
- isFlaky: target.value.bringup,
- key: commit.key.append(Task),
- name: target.value.name,
- requiredCapabilities: <String>[target.value.testbed],
- stageName: target.value.scheduler.toString(),
- status: Task.statusNew,
- timeoutInMinutes: target.value.timeout,
- );
- }
-
- /// Lookup [Task] from Datastore from its parent key and name.
- static Future<Task> fromCommitKey({
- required DatastoreService datastore,
- required Key<String> commitKey,
- required String name,
- }) async {
- if (name.isEmpty) {
- throw const BadRequestException('task name is null');
- }
- final Query<Task> query = datastore.db.query<Task>(ancestorKey: commitKey)..filter('name =', name);
- final List<Task> tasks = await query.run().toList();
- if (tasks.length != 1) {
- log.severe('Found ${tasks.length} entries for builder $name');
- throw InternalServerError('Expected to find 1 task for $name, but found ${tasks.length}');
- }
- return tasks.single;
- }
-
- /// Lookup [Task] from its [key].
- ///
- /// This is the fastest way to lookup [Task], but requires [id] to be passed
- /// as it is generated from Datastore.
- static Future<Task> fromKey({
- required DatastoreService datastore,
- required Key<String> commitKey,
- required int id,
- }) {
- log.fine('Looking up key...');
- final Key<int> key = Key<int>(commitKey, Task, id);
- return datastore.lookupByValue<Task>(key);
- }
-
- /// Lookup [Task] from Datastore.
- ///
- /// Either name or id must be given to lookup [Task].
- ///
- /// Prefer passing [id] when possible as it is a faster lookup.
- static Future<Task> fromDatastore({
- required DatastoreService datastore,
- required Key<String> commitKey,
- String? name,
- String? id,
- }) {
- if (id == null) {
- return Task.fromCommitKey(
- datastore: datastore,
- commitKey: commitKey,
- name: name!,
- );
- }
-
- return Task.fromKey(
- datastore: datastore,
- commitKey: commitKey,
- id: int.parse(id),
- );
- }
-
- /// Creates a [Task] based on a buildbucket [bb.Build].
- static Future<Task> fromBuildbucketBuild(
- bb.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!.hash!;
- log.fine('Hash: $hash');
-
- final RepositorySlug slug = RepositorySlug('flutter', repository);
- log.fine('Slug: ${slug.toString()}');
-
- final int startTime = build.startTime?.millisecondsSinceEpoch ?? 0;
- final int endTime = build.endTime?.millisecondsSinceEpoch ?? 0;
- 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.builderId.builder,
- commitKey: commitKey,
- createTimestamp: startTime,
- endTimestamp: endTime,
- luciBucket: build.builderId.bucket,
- name: customName ?? build.builderId.builder,
- stageName: build.builderId.project,
- startTimestamp: startTime,
- status: convertBuildbucketStatusToString(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) {
- case bb.Status.success:
- return statusSucceeded;
- case bb.Status.canceled:
- return statusCancelled;
- case bb.Status.infraFailure:
- return statusInfraFailure;
- case bb.Status.started:
- return statusInProgress;
- case bb.Status.scheduled:
- return statusNew;
- default:
- return statusFailed;
- }
- }
-
- /// The task was cancelled.
- static const String statusCancelled = 'Cancelled';
-
- /// The task is yet to be run.
- static const String statusNew = 'New';
-
- /// The task failed to run due to an unexpected issue.
- static const String statusInfraFailure = 'Infra Failure';
-
- /// The task is currently running.
- static const String statusInProgress = 'In Progress';
-
- /// The task was run successfully.
- static const String statusSucceeded = 'Succeeded';
-
- /// The task failed to run successfully.
- static const String statusFailed = 'Failed';
-
- /// The task was skipped or canceled while running.
- ///
- /// This status is only used by LUCI tasks.
- static const String statusSkipped = 'Skipped';
-
- /// The list of legal values for the [status] property.
- static const List<String> legalStatusValues = <String>[
- statusCancelled,
- statusFailed,
- statusInfraFailure,
- statusInProgress,
- statusNew,
- statusSkipped,
- statusSucceeded,
- ];
-
- static const List<String> finishedStatusValues = <String>[
- statusCancelled,
- statusFailed,
- statusInfraFailure,
- statusSkipped,
- statusSucceeded,
- ];
-
- /// The key of the commit that owns this task.
- @ModelKeyProperty(propertyName: 'ChecklistKey')
- @JsonKey(name: 'ChecklistKey')
- @StringKeyConverter()
- Key<String>? commitKey;
-
- /// The timestamp (in milliseconds since the Epoch) that this task was
- /// created.
- ///
- /// This is _not_ when the task first started running, as tasks start out in
- /// the 'New' state until they've been picked up by an [Agent].
- @IntProperty(propertyName: 'CreateTimestamp', required: true)
- @JsonKey(name: 'CreateTimestamp')
- int? createTimestamp;
-
- /// The timestamp (in milliseconds since the Epoch) that this task started
- /// running.
- ///
- /// Tasks may be run more than once. If this task has been run more than
- /// once, this timestamp represents when the task was most recently started.
- @IntProperty(propertyName: 'StartTimestamp', required: true)
- @JsonKey(name: 'StartTimestamp')
- int? startTimestamp;
-
- /// The timestamp (in milliseconds since the Epoch) that this task last
- /// finished running.
- @IntProperty(propertyName: 'EndTimestamp', required: true)
- @JsonKey(name: 'EndTimestamp')
- int? endTimestamp;
-
- /// The name of the task.
- ///
- /// This is a human-readable name, typically a test name (e.g.
- /// "hello_world__memory").
- @StringProperty(propertyName: 'Name', required: true)
- @JsonKey(name: 'Name')
- String? name;
-
- /// The number of attempts that have been made to run this task successfully.
- ///
- /// New tasks that have not yet been picked up by an [Agent] will have zero
- /// attempts.
- @IntProperty(propertyName: 'Attempts', required: true)
- @JsonKey(name: 'Attempts')
- int? attempts;
-
- /// Whether this task has been marked flaky by .ci.yaml.
- ///
- /// See also:
- ///
- /// * <https://github.com/flutter/flutter/blob/master/.ci.yaml>
- ///
- /// A flaky (`bringup: true`) task will not block the tree.
- @BoolProperty(propertyName: 'Flaky')
- @JsonKey(name: 'Flaky')
- bool? isFlaky;
-
- /// Whether the test execution of this task shows flake.
- ///
- /// Test runner supports rerun, and this flag tracks if a flake happens.
- ///
- /// See also:
- /// * <https://github.com/flutter/flutter/blob/master/dev/devicelab/lib/framework/runner.dart>
- @BoolProperty(propertyName: 'TestFlaky')
- @JsonKey(name: 'TestFlaky')
- bool? isTestFlaky;
-
- /// The timeout of the task, or zero if the task has no timeout.
- @IntProperty(propertyName: 'TimeoutInMinutes', required: true)
- @JsonKey(name: 'TimeoutInMinutes')
- int? timeoutInMinutes;
-
- /// Currently unset and unused.
- @StringProperty(propertyName: 'Reason')
- @JsonKey(name: 'Reason')
- String? reason;
-
- /// The build number of luci build: https://chromium.googlesource.com/infra/luci/luci-go/+/master/buildbucket/proto/build.proto#146
- @IntProperty(propertyName: 'BuildNumber')
- @JsonKey(name: 'BuildNumber')
- int? buildNumber;
-
- /// The build number list of luci builds: comma joined string of
- /// different build numbers.
- ///
- /// For the case with single run 123, [buildNumberList] = '123';
- /// For the case with multiple reruns 123, 456, 789,
- /// [buildNumberList] = '123,456,789'.
- @StringProperty(propertyName: 'BuildNumberList')
- @JsonKey(name: 'BuildNumberList')
- String? buildNumberList;
-
- /// The builder name of luci build.
- @StringProperty(propertyName: 'BuilderName')
- @JsonKey(name: 'BuilderName')
- String? builderName;
-
- /// The luci pool where the luci task runs.
- @StringProperty(propertyName: 'LuciBucket')
- @JsonKey(name: 'luciBucket')
- String? luciBucket;
-
- /// The list of capabilities that agents are required to have to run this
- /// task.
- ///
- /// See also:
- ///
- /// * [Agent.capabilities], which list the capabilities of an agent.
- @StringListProperty(propertyName: 'RequiredCapabilities')
- @JsonKey(name: 'RequiredCapabilities')
- List<String>? requiredCapabilities;
-
- /// Set to the ID of the agent that's responsible for running this task.
- ///
- /// This will be null until an agent has reserved this task.
- @StringProperty(propertyName: 'ReservedForAgentID')
- @JsonKey(name: 'ReservedForAgentID')
- String? reservedForAgentId;
-
- /// The name of the [Stage] that groups this task with other tasks that are
- /// related to it.
- @StringProperty(propertyName: 'StageName', required: true)
- @JsonKey(name: 'StageName')
- String? stageName;
-
- /// The status of the task.
- ///
- /// Legal values and their meanings are defined in [legalStatusValues].
- @StringProperty(propertyName: 'Status', required: true)
- @JsonKey(name: 'Status')
- String get status => _status ?? statusNew;
- String? _status;
- set status(String value) {
- if (!legalStatusValues.contains(value)) {
- throw ArgumentError('Invalid state: "$value"');
- }
- _status = value;
- }
-
- /// Update [Task] fields based on a LUCI [Build].
- void updateFromBuild(Build build) {
- final List<String>? tags = build.tags;
- // Example tag: build_address:luci.flutter.prod/Linux Cocoon/271
- final String? buildAddress = tags?.firstWhere((String tag) => tag.contains('build_address'));
- if (buildAddress == null) {
- log.warning('Tags: $tags');
- throw const BadRequestException('build_address does not contain build number');
- }
-
- final int currentBuildNumber = int.parse(buildAddress.split('/').last);
- if (buildNumber == null || buildNumber! < currentBuildNumber) {
- buildNumber = currentBuildNumber;
- } else if (currentBuildNumber < buildNumber!) {
- log.fine('Skipping message as build number is before the current task');
- return;
- }
-
- if (buildNumberList == null) {
- buildNumberList = '$buildNumber';
- } else {
- final Set<String> buildNumberSet = buildNumberList!.split(',').toSet();
- buildNumberSet.add(buildNumber.toString());
- buildNumberList = buildNumberSet.join(',');
- }
-
- createTimestamp = build.createdTimestamp?.millisecondsSinceEpoch ?? 0;
- startTimestamp = build.startedTimestamp?.millisecondsSinceEpoch ?? 0;
- endTimestamp = build.completedTimestamp?.millisecondsSinceEpoch ?? 0;
-
- _setStatusFromLuciStatus(build);
- }
-
- /// Updates [Task] based on a Buildbucket [Build].
- void updateFromBuildbucketBuild(bb.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.startTime?.millisecondsSinceEpoch ?? 0;
- startTimestamp = build.startTime?.millisecondsSinceEpoch ?? 0;
- endTimestamp = build.endTime?.millisecondsSinceEpoch ?? 0;
-
- attempts = buildNumberList!.split(',').length;
-
- status = convertBuildbucketStatusToString(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.
- if (_isStatusCompleted()) {
- return status;
- }
-
- if (build.status == Status.started) {
- return status = statusInProgress;
- }
- switch (build.result) {
- case Result.success:
- return status = statusSucceeded;
- case Result.canceled:
- return status = statusCancelled;
- case Result.failure:
- // Note that `Result` does not support `infraFailure`:
- // https://github.com/luci/luci-go/blob/main/common/api/buildbucket/buildbucket/v1/buildbucket-gen.go#L247-L251
- // To determine an infra failure status, we need to combine `Result.failure` and `FailureReason.infraFailure`.
- if (build.failureReason == FailureReason.infraFailure) {
- return status = statusInfraFailure;
- } else {
- return status = statusFailed;
- }
- default:
- throw BadRequestException('${build.result} is unknown');
- }
- }
-
- bool _isStatusCompleted() {
- const List<String> completedStatuses = <String>[
- statusCancelled,
- statusFailed,
- statusInfraFailure,
- statusSucceeded,
- ];
- return completedStatuses.contains(status);
- }
-
- /// Comparator that sorts tasks by fewest attempts first.
- static int byAttempts(Task a, Task b) => a.attempts!.compareTo(b.attempts!);
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$TaskToJson(this);
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', commitKey: ${commitKey?.id}')
- ..write(', createTimestamp: $createTimestamp')
- ..write(', startTimestamp: $startTimestamp')
- ..write(', endTimestamp: $endTimestamp')
- ..write(', name: $name')
- ..write(', attempts: $attempts')
- ..write(', isFlaky: $isFlaky')
- ..write(', isTestRunFlaky: $isTestFlaky')
- ..write(', timeoutInMinutes: $timeoutInMinutes')
- ..write(', reason: $reason')
- ..write(', requiredCapabilities: $requiredCapabilities')
- ..write(', reservedForAgentId: $reservedForAgentId')
- ..write(', stageName: $stageName')
- ..write(', status: $status')
- ..write(', buildNumber: $buildNumber')
- ..write(', buildNumberList: $buildNumberList')
- ..write(', builderName: $builderName')
- ..write(', luciBucket: $luciBucket')
- ..write(')');
- return buf.toString();
- }
-}
-
-Iterable<Task> targetsToTask(Commit commit, List<Target> targets) =>
- targets.map((Target target) => Task.fromTarget(commit: commit, target: target));
-
-/// The serialized representation of a [Task].
-// TODO(tvolkert): Directly serialize [Task] once frontends migrate to new serialization format.
-@JsonSerializable(createFactory: false)
-class SerializableTask {
- const SerializableTask(this.task);
-
- @JsonKey(name: 'Task')
- final Task task;
-
- @JsonKey(name: 'Key')
- @IntKeyConverter()
- Key<int> get key => task.key;
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$SerializableTaskToJson(this);
-}
-
-/// A [Task], paired with its associated parent [Commit].
-///
-/// The [Task] model object references its parent [Commit] through the
-/// [Task.commitKey] field, but it does not hold a reference to the associated
-/// [Commit] object (just the relational mapping). This class exists for those
-/// times when the caller has loaded the associated commit from the datastore
-/// and would like to pass both the task its commit around.
-class FullTask {
- /// Creates a new [FullTask].
- const FullTask(this.task, this.commit);
-
- /// The [Task] object.
- final Task task;
-
- /// The [Commit] object references by this [task]'s [Task.commitKey].
- final Commit commit;
-}
diff --git a/app_dart/lib/src/model/appengine/task.g.dart b/app_dart/lib/src/model/appengine/task.g.dart
deleted file mode 100644
index 9ef04d6..0000000
--- a/app_dart/lib/src/model/appengine/task.g.dart
+++ /dev/null
@@ -1,35 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'task.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-Map<String, dynamic> _$TaskToJson(Task instance) => <String, dynamic>{
- 'ChecklistKey': const StringKeyConverter().toJson(instance.commitKey),
- 'CreateTimestamp': instance.createTimestamp,
- 'StartTimestamp': instance.startTimestamp,
- 'EndTimestamp': instance.endTimestamp,
- 'Name': instance.name,
- 'Attempts': instance.attempts,
- 'Flaky': instance.isFlaky,
- 'TestFlaky': instance.isTestFlaky,
- 'TimeoutInMinutes': instance.timeoutInMinutes,
- 'Reason': instance.reason,
- 'BuildNumber': instance.buildNumber,
- 'BuildNumberList': instance.buildNumberList,
- 'BuilderName': instance.builderName,
- 'luciBucket': instance.luciBucket,
- 'RequiredCapabilities': instance.requiredCapabilities,
- 'ReservedForAgentID': instance.reservedForAgentId,
- 'StageName': instance.stageName,
- 'Status': instance.status,
- };
-
-Map<String, dynamic> _$SerializableTaskToJson(SerializableTask instance) => <String, dynamic>{
- 'Task': instance.task,
- 'Key': const IntKeyConverter().toJson(instance.key),
- };
diff --git a/app_dart/lib/src/model/ci_yaml/ci_yaml.dart b/app_dart/lib/src/model/ci_yaml/ci_yaml.dart
deleted file mode 100644
index 46f1832..0000000
--- a/app_dart/lib/src/model/ci_yaml/ci_yaml.dart
+++ /dev/null
@@ -1,308 +0,0 @@
-// 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:convert';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:github/github.dart';
-
-import '../proto/internal/scheduler.pb.dart' as pb;
-import 'target.dart';
-
-/// This is a wrapper class around S[pb.SchedulerConfig].
-///
-/// See //CI_YAML.md for high level documentation.
-class CiYaml {
- /// Creates [CiYaml] from a [RepositorySlug], [branch], [pb.SchedulerConfig] and an optional [CiYaml] of tip of tree CiYaml.
- ///
- /// If [totConfig] is passed, the validation will verify no new targets have been added that may temporarily break the LUCI infrastructure (such as new prod or presubmit targets).
- CiYaml({
- required this.slug,
- required this.branch,
- required this.config,
- CiYaml? totConfig,
- bool validate = false,
- }) {
- if (validate) {
- _validate(config, branch, totSchedulerConfig: totConfig?.config);
- }
- // Do not filter bringup targets. They are required for backward compatibility
- // with release candidate branches.
- final Iterable<Target> totTargets = totConfig?._targets ?? <Target>[];
- final List<Target> totEnabledTargets = _filterEnabledTargets(totTargets);
- totTargetNames = totEnabledTargets.map((Target target) => target.value.name).toList();
- totPostsubmitTargetNames =
- totConfig?.postsubmitTargets.map((Target target) => target.value.name).toList() ?? <String>[];
- }
-
- /// List of target names used to filter target from release candidate branches
- /// that were already removed from main.
- List<String>? totTargetNames;
-
- /// List of postsubmit target names used to filter target from release candidate branches
- /// that were already removed from main.
- List<String>? totPostsubmitTargetNames;
-
- /// The underlying protobuf that contains the raw data from .ci.yaml.
- pb.SchedulerConfig config;
-
- /// The [RepositorySlug] that [config] is from.
- final RepositorySlug slug;
-
- /// The git branch currently being scheduled against.
- final String branch;
-
- /// Gets all [Target] that run on presubmit for this config.
- List<Target> get presubmitTargets {
- final Iterable<Target> presubmitTargets =
- _targets.where((Target target) => target.value.presubmit && !target.value.bringup);
- List<Target> enabledTargets = _filterEnabledTargets(presubmitTargets);
-
- if (enabledTargets.isEmpty) {
- throw Exception('$branch is not enabled for this .ci.yaml.\nAdd it to run tests against this PR.');
- }
- // Filter targets removed from main.
- if (totTargetNames!.isNotEmpty) {
- enabledTargets = filterOutdatedTargets(slug, enabledTargets, totTargetNames);
- }
- return enabledTargets;
- }
-
- /// Gets all [Target] that run on postsubmit for this config.
- List<Target> get postsubmitTargets {
- final Iterable<Target> postsubmitTargets = _targets.where((Target target) => target.value.postsubmit);
-
- List<Target> enabledTargets = _filterEnabledTargets(postsubmitTargets);
- // Filter targets removed from main.
- if (totPostsubmitTargetNames!.isNotEmpty) {
- enabledTargets = filterOutdatedTargets(slug, enabledTargets, totPostsubmitTargetNames);
- }
- // filter if release_build true if current branch is a release candidate branch.
- enabledTargets = _filterReleaseBuildTargets(enabledTargets);
- return enabledTargets;
- }
-
- /// Filters post submit targets to remove targets we do not want backfilled.
- List<Target> get backfillTargets {
- final List<Target> filteredTargets = <Target>[];
- for (Target target in postsubmitTargets) {
- final Map<String, Object> properties = target.getProperties();
- if (!properties.containsKey('backfill') || properties['backfill'] as bool) {
- filteredTargets.add(target);
- }
- }
- return filteredTargets;
- }
-
- /// Filters targets with release_build = true on release candidate branches.
- List<Target> _filterReleaseBuildTargets(List<Target> targets) {
- final List<Target> results = <Target>[];
- final bool releaseBranch = branch.contains(RegExp('^flutter-'));
- if (!releaseBranch) {
- return targets;
- }
- for (Target target in targets) {
- final Map<String, Object> properties = target.getProperties();
- if (!properties.containsKey('release_build') || !(properties['release_build'] as bool)) {
- if (!target.value.bringup) results.add(target);
- }
- }
- return results;
- }
-
- /// Filters targets that were removed from main. [slug] is the gihub
- /// slug for branch under test, [targets] is the list of targets from
- /// the branch under test and [totTargetNames] is the list of target
- /// names enabled on the default branch.
- List<Target> filterOutdatedTargets(slug, targets, totTargetNames) {
- final String defaultBranch = Config.defaultBranch(slug);
- return targets
- .where(
- (Target target) =>
- (target.value.enabledBranches.isNotEmpty && !target.value.enabledBranches.contains(defaultBranch)) ||
- totTargetNames!.contains(target.value.name),
- )
- .toList();
- }
-
- /// Filters [targets] to those that should be started immediately.
- ///
- /// Targets with a dependency are triggered when there dependency pushes a notification that it has finished.
- /// This shouldn't be confused for targets that have the property named dependency, which is used by the
- /// flutter_deps recipe module on LUCI.
- List<Target> getInitialTargets(List<Target> targets) {
- Iterable<Target> initialTargets = targets.where((Target target) => target.value.dependencies.isEmpty).toList();
- if (branch != Config.defaultBranch(slug)) {
- // Filter out bringup targets for release branches
- initialTargets = initialTargets.where((Target target) => !target.value.bringup);
- }
-
- return initialTargets.toList();
- }
-
- Iterable<Target> get _targets => config.targets.map(
- (pb.Target target) => Target(
- schedulerConfig: config,
- value: target,
- slug: slug,
- ),
- );
-
- /// Get an unfiltered list of all [targets] that are found in the ci.yaml file.
- List<Target> get targets => _targets.toList();
-
- /// Filter [targets] to only those that are expected to run for [branch].
- ///
- /// A [Target] is expected to run if:
- /// 1. [Target.enabledBranches] exists and matches [branch].
- /// 2. Otherwise, [config.enabledBranches] matches [branch].
- List<Target> _filterEnabledTargets(Iterable<Target> targets) {
- final List<Target> filteredTargets = <Target>[];
-
- // 1. Add targets with local definition
- final Iterable<Target> overrideBranchTargets =
- targets.where((Target target) => target.value.enabledBranches.isNotEmpty);
- final Iterable<Target> enabledTargets = overrideBranchTargets
- .where((Target target) => enabledBranchesMatchesCurrentBranch(target.value.enabledBranches, branch));
- filteredTargets.addAll(enabledTargets);
-
- // 2. Add targets with global definition (this is the majority of targets)
- if (enabledBranchesMatchesCurrentBranch(config.enabledBranches, branch)) {
- final Iterable<Target> defaultBranchTargets =
- targets.where((Target target) => target.value.enabledBranches.isEmpty);
- filteredTargets.addAll(defaultBranchTargets);
- }
-
- return filteredTargets;
- }
-
- /// Whether any of the possible [RegExp] in [enabledBranches] match [branch].
- static bool enabledBranchesMatchesCurrentBranch(List<String> enabledBranches, String branch) {
- final List<String> regexes = <String>[];
- for (String enabledBranch in enabledBranches) {
- // Prefix with start of line and suffix with end of line
- regexes.add('^$enabledBranch\$');
- }
- final String rawRegexp = regexes.join('|');
- final RegExp regexp = RegExp(rawRegexp);
-
- return regexp.hasMatch(branch);
- }
-
- /// Validates [pb.SchedulerConfig] extracted from [CiYaml] files.
- ///
- /// A [pb.SchedulerConfig] file is considered good if:
- /// 1. It has at least one [pb.Target] in [pb.SchedulerConfig.targets]
- /// 2. It has at least one [branch] in [pb.SchedulerConfig.enabledBranches]
- /// 3. If a second [pb.SchedulerConfig] is passed in,
- /// we compare the current list of [pb.Target] inside the current [pb.SchedulerConfig], i.e., [schedulerConfig],
- /// with the list of [pb.Target] from tip of the tree [pb.SchedulerConfig], i.e., [totSchedulerConfig].
- /// If a [pb.Target] is indentified as a new target compared to target list from tip of the tree, The new target
- /// should have its field [pb.Target.bringup] set to true.
- /// 4. no cycle should exist in the dependency graph, as tracked by map [targetGraph]
- /// 5. [pb.Target] should not depend on self
- /// 6. [pb.Target] cannot have more than 1 dependency
- /// 7. [pb.Target] should depend on target that already exist in depedency graph, and already recorded in map [targetGraph]
- void _validate(pb.SchedulerConfig schedulerConfig, String branch, {pb.SchedulerConfig? totSchedulerConfig}) {
- if (schedulerConfig.targets.isEmpty) {
- throw const FormatException('Scheduler config must have at least 1 target');
- }
-
- if (schedulerConfig.enabledBranches.isEmpty) {
- throw const FormatException('Scheduler config must have at least 1 enabled branch');
- }
-
- final Map<String, List<pb.Target>> targetGraph = <String, List<pb.Target>>{};
- final List<String> exceptions = <String>[];
- final Set<String> totTargets = <String>{};
- if (totSchedulerConfig != null) {
- for (pb.Target target in totSchedulerConfig.targets) {
- totTargets.add(target.name);
- }
- }
- // Construct [targetGraph]. With a one scan approach, cycles in the graph
- // cannot exist as it only works forward.
- for (final pb.Target target in schedulerConfig.targets) {
- if (targetGraph.containsKey(target.name)) {
- exceptions.add('ERROR: ${target.name} already exists in graph');
- } else {
- // a new build without "bringup: true"
- // link to wiki - https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness#adding-a-new-devicelab-test
- if (totTargets.isNotEmpty && !totTargets.contains(target.name) && target.bringup != true) {
- exceptions.add(
- 'ERROR: ${target.name} is a new builder added. it needs to be marked bringup: true\nIf ci.yaml wasn\'t changed, try `git fetch upstream && git merge upstream/master`',
- );
- continue;
- }
- targetGraph[target.name] = <pb.Target>[];
- // Add edges
- if (target.dependencies.isNotEmpty) {
- if (target.dependencies.length != 1) {
- exceptions
- .add('ERROR: ${target.name} has multiple dependencies which is not supported. Use only one dependency');
- } else {
- if (target.dependencies.first == target.name) {
- exceptions.add('ERROR: ${target.name} cannot depend on itself');
- } else if (targetGraph.containsKey(target.dependencies.first)) {
- targetGraph[target.dependencies.first]!.add(target);
- } else {
- exceptions.add('ERROR: ${target.name} depends on ${target.dependencies.first} which does not exist');
- }
- }
- }
- }
-
- /// Check the dependencies for the current target if it is viable and to
- /// be added to graph. Temporarily this is only being done on non-release
- /// branches.
- if (branch == Config.defaultBranch(slug)) {
- final String? dependencyJson = target.properties['dependencies'];
- if (dependencyJson != null) {
- DependencyValidator.hasVersion(dependencyJsonString: dependencyJson);
- }
- }
- }
- _checkExceptions(exceptions);
- }
-
- void _checkExceptions(List<String> exceptions) {
- if (exceptions.isNotEmpty) {
- final String fullException = exceptions.reduce((String exception, _) => '$exception\n');
- throw FormatException(fullException);
- }
- }
-}
-
-/// Class to verify the version of the dependencies in the ci.yaml config file
-/// for each target we are going to execute.
-class DependencyValidator {
- /// dependencyJsonString is guaranteed to be non empty as it must be found
- /// before this method is called.
- ///
- /// Checks a dependency string for a pinned version.
- /// If a version is found then it must not be empty or 'latest.'
- static void hasVersion({required String dependencyJsonString}) {
- final List<String> exceptions = <String>[];
-
- /// Decoded will contain a list of maps for the dependencies found.
- final List<dynamic> decoded = json.decode(dependencyJsonString) as List<dynamic>;
-
- for (Map<String, dynamic> depMap in decoded) {
- if (!depMap.containsKey('version')) {
- exceptions.add('ERROR: dependency ${depMap['dependency']} must have a version.');
- } else {
- final String version = depMap['version'] as String;
- if (version.isEmpty || version == 'latest') {
- exceptions
- .add('ERROR: dependency ${depMap['dependency']} must have a non empty, non "latest" version supplied.');
- }
- }
- }
-
- if (exceptions.isNotEmpty) {
- final String fullException = exceptions.reduce((String exception, _) => '$exception\n');
- throw FormatException(fullException);
- }
- }
-}
diff --git a/app_dart/lib/src/model/ci_yaml/target.dart b/app_dart/lib/src/model/ci_yaml/target.dart
deleted file mode 100644
index 4fa65e1..0000000
--- a/app_dart/lib/src/model/ci_yaml/target.dart
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright 2019 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:convert';
-
-import 'package:cocoon_service/src/service/scheduler/policy.dart';
-import 'package:github/github.dart';
-
-import '../../service/config.dart';
-import '../luci/buildbucket.dart';
-import '../proto/internal/scheduler.pb.dart' as pb;
-
-/// Wrapper class around [pb.Target] to support aggregate properties.
-///
-/// Changes here may also need to be upstreamed in:
-/// * https://flutter.googlesource.com/infra/+/refs/heads/main/config/lib/ci_yaml/ci_yaml.star
-class Target {
- Target({
- required this.value,
- required this.schedulerConfig,
- required this.slug,
- });
-
- /// Underlying [Target] this is based on.
- final pb.Target value;
-
- /// The [SchedulerConfig] [value] is from.
- ///
- /// This is passed for necessary lookups to platform level details.
- final pb.SchedulerConfig schedulerConfig;
-
- /// The [RepositorySlug] this [Target] is run for.
- final RepositorySlug slug;
-
- /// Target prefixes that indicate it will run on an ios device.
- static const List<String> iosPlatforms = <String>['mac_ios', 'mac_arm64_ios'];
-
- /// Dimension list defined in .ci.yaml.
- static List<String> dimensionList = <String>['os', 'device_os', 'device_type', 'mac_model', 'cores', 'cpu'];
-
- static String kIgnoreFlakiness = 'ignore_flakiness';
-
- /// Gets assembled dimensions for this [pb.Target].
- ///
- /// Swarming dimension doc: https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/lucicfg/doc/README.md#swarming.dimension
- ///
- /// Target dimensions are prioritized in:
- /// 1. [pb.Target.dimensions]
- /// 1. [pb.Target.properties]
- /// 2. [schedulerConfig.platformDimensions]
- List<RequestedDimension> getDimensions() {
- final Map<String, RequestedDimension> dimensionsMap = <String, RequestedDimension>{};
-
- final Map<String, Object> platformDimensions = _getPlatformDimensions();
- for (String key in platformDimensions.keys) {
- final String value = platformDimensions[key].toString();
- dimensionsMap[key] = RequestedDimension(key: key, value: value);
- }
-
- final Map<String, Object> properties = getProperties();
- // TODO(xilaizhang): https://github.com/flutter/flutter/issues/103557
- // remove this logic after dimensions are supported in ci.yaml files
- for (String dimension in dimensionList) {
- if (properties.containsKey(dimension)) {
- final String value = properties[dimension].toString();
- dimensionsMap[dimension] = RequestedDimension(key: dimension, value: value);
- }
- }
-
- final Map<String, Object> targetDimensions = _getTargetDimensions();
- for (String key in targetDimensions.keys) {
- final String value = targetDimensions[key].toString();
- dimensionsMap[key] = RequestedDimension(key: key, value: value);
- }
-
- return dimensionsMap.values.toList();
- }
-
- /// [SchedulerPolicy] this target follows.
- ///
- /// Targets not triggered by Cocoon will not be triggered.
- ///
- /// All targets except from [Config.guaranteedSchedulingRepos] run with [BatchPolicy] to reduce queue time.
- SchedulerPolicy get schedulerPolicy {
- if (value.scheduler != pb.SchedulerSystem.cocoon) {
- return OmitPolicy();
- }
- if (Config.guaranteedSchedulingRepos.contains(slug)) {
- return GuaranteedPolicy();
- }
- return BatchPolicy();
- }
-
- /// Get the tags from the defined properties in the ci.
- ///
- /// Return an empty list if no tags are found.
- List<String> get tags {
- final Map<String, Object> properties = getProperties();
- return (properties.containsKey('tags')) ? (properties['tags'] as List).map((e) => e as String).toList() : [];
- }
-
- String get getTestName {
- final List<String> words = value.name.split(' ');
- return words.length < 2 ? words[0] : words[1];
- }
-
- /// Gets the assembled properties for this [pb.Target].
- ///
- /// Target properties are prioritized in:
- /// 1. [schedulerConfig.platformProperties]
- /// 2. [pb.Target.properties]
- Map<String, Object> getProperties() {
- final Map<String, Object> platformProperties = _getPlatformProperties();
- final Map<String, Object> properties = _getTargetProperties();
- final Map<String, Object> mergedProperties = <String, Object>{}
- ..addAll(platformProperties)
- ..addAll(properties);
-
- final List<Dependency> targetDependencies = <Dependency>[];
- if (properties.containsKey('dependencies')) {
- final List<dynamic> rawDeps = properties['dependencies'] as List<dynamic>;
- final Iterable<Dependency> deps = rawDeps.map((dynamic rawDep) => Dependency.fromJson(rawDep as Object));
- targetDependencies.addAll(deps);
- }
- final List<Dependency> platformDependencies = <Dependency>[];
- if (platformProperties.containsKey('dependencies')) {
- final List<dynamic> rawDeps = platformProperties['dependencies'] as List<dynamic>;
- final Iterable<Dependency> deps = rawDeps.map((dynamic rawDep) => Dependency.fromJson(rawDep as Object));
- platformDependencies.addAll(deps);
- }
- // Lookup map to make merging [targetDependencies] and [platformDependencies] simpler.
- final Map<String, Dependency> mergedDependencies = <String, Dependency>{};
- for (Dependency dep in platformDependencies) {
- mergedDependencies[dep.name] = dep;
- }
- for (Dependency dep in targetDependencies) {
- mergedDependencies[dep.name] = dep;
- }
- mergedProperties['dependencies'] = mergedDependencies.values.map((Dependency dep) => dep.toJson()).toList();
- mergedProperties['bringup'] = value.bringup;
-
- return mergedProperties;
- }
-
- Map<String, Object> _getTargetDimensions() {
- final Map<String, Object> dimensions = <String, Object>{};
- for (String key in value.dimensions.keys) {
- dimensions[key] = _parseProperty(key, value.dimensions[key]!);
- }
-
- return dimensions;
- }
-
- Map<String, Object> _getTargetProperties() {
- final Map<String, Object> properties = <String, Object>{};
- for (String key in value.properties.keys) {
- properties[key] = _parseProperty(key, value.properties[key]!);
- }
-
- return properties;
- }
-
- Map<String, Object> _getPlatformProperties() {
- if (!schedulerConfig.platformProperties.containsKey(getPlatform())) {
- return <String, Object>{};
- }
-
- final Map<String, String> platformProperties = schedulerConfig.platformProperties[getPlatform()]!.properties;
- final Map<String, Object> properties = <String, Object>{};
- for (String key in platformProperties.keys) {
- properties[key] = _parseProperty(key, platformProperties[key]!);
- }
-
- return properties;
- }
-
- Map<String, Object> _getPlatformDimensions() {
- if (!schedulerConfig.platformProperties.containsKey(getPlatform())) {
- return <String, Object>{};
- }
-
- final Map<String, String> platformDimensions = schedulerConfig.platformProperties[getPlatform()]!.dimensions;
- final Map<String, Object> dimensions = <String, Object>{};
- for (String key in platformDimensions.keys) {
- dimensions[key] = _parseProperty(key, platformDimensions[key]!);
- }
-
- return dimensions;
- }
-
- /// Converts property strings to their correct type.
- ///
- /// Changes made here should also be made to [_platform_properties] and [_properties] in:
- /// * https://cs.opensource.google/flutter/infra/+/main:config/lib/ci_yaml/ci_yaml.star
- Object _parseProperty(String key, String value) {
- // Yaml will escape new lines unnecessarily for strings.
- final List<String> newLineIssues = <String>['android_sdk_license', 'android_sdk_preview_license'];
- if (value == 'true') {
- return true;
- } else if (value == 'false') {
- return false;
- } else if (value.startsWith('[') || value.startsWith('{')) {
- return jsonDecode(value) as Object;
- } else if (newLineIssues.contains(key)) {
- return value.replaceAll('\\n', '\n');
- } else if (int.tryParse(value) != null) {
- return int.parse(value);
- }
-
- return value;
- }
-
- /// Get the platform of this [Target].
- ///
- /// Platform is extracted as the first word in a target's name.
- String getPlatform() {
- return value.name.split(' ').first.toLowerCase();
- }
-
- /// Indicates whether this target should be scheduled via batches.
- ///
- /// DeviceLab targets are special as they run on a host + physical device, and there is limited
- /// capacity in the labs to run them. Their platforms contain one of `android`, `ios`, and `samsung`.
- ///
- /// Mac host only targets are scheduled via patches due to high queue time. This can be relieved
- /// when we have capacity support in Q4/2022.
- bool get shouldBatchSchedule {
- final String platform = getPlatform();
- return platform.contains('android') ||
- platform.contains('ios') ||
- platform.contains('samsung') ||
- platform == 'mac';
- }
-
- /// Get the associated LUCI bucket to run this [Target] in.
- String getBucket() {
- return value.bringup ? 'staging' : 'prod';
- }
-
- /// Returns value of ignore_flakiness property.
- ///
- /// Returns the value of ignore_flakiness property if
- /// it has been specified, else default returns false.
- bool getIgnoreFlakiness() {
- final Map<String, Object> properties = getProperties();
- if (properties.containsKey(kIgnoreFlakiness)) {
- return properties[kIgnoreFlakiness] as bool;
- }
- return false;
- }
-}
-
-/// Representation of a Flutter dependency.
-///
-/// See more:
-/// * https://flutter.googlesource.com/recipes/+/refs/heads/main/recipe_modules/flutter_deps/api.py
-class Dependency {
- Dependency(this.name, this.version);
-
- /// Constructor for converting from the flutter_deps format.
- factory Dependency.fromJson(Object json) {
- final Map<String, dynamic> map = json as Map<String, dynamic>;
- return Dependency(map['dependency']! as String, map['version'] as String?);
- }
-
- /// Human readable name of the dependency.
- final String name;
-
- /// CIPD tag to use.
- ///
- /// If null, will use the version set in the flutter_deps recipe_module.
- final String? version;
-
- Map<String, Object> toJson() {
- return <String, Object>{
- 'dependency': name,
- if (version != null) 'version': version!,
- };
- }
-}
diff --git a/app_dart/lib/src/model/common/json_converters.dart b/app_dart/lib/src/model/common/json_converters.dart
deleted file mode 100644
index f10d6ac..0000000
--- a/app_dart/lib/src/model/common/json_converters.dart
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2019 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:convert' hide json;
-import 'dart:convert' as convert show json;
-
-import 'package:json_annotation/json_annotation.dart';
-
-/// A converter for tags.
-///
-/// The JSON format is:
-///
-/// ```json
-/// [
-/// {
-/// "key": "tag_key",
-/// "value": "tag_value"
-/// }
-/// ]
-/// ```
-///
-/// Which is flattened out as a `Map<String, List<String>>`.
-class TagsConverter implements JsonConverter<Map<String?, List<String?>>?, List<dynamic>?> {
- const TagsConverter();
-
- @override
- Map<String?, List<String?>>? fromJson(List<dynamic>? json) {
- if (json == null) {
- return null;
- }
- final Map<String?, List<String?>> result = <String?, List<String?>>{};
- for (Map<String, dynamic> tag in json.cast<Map<String, dynamic>>()) {
- final String? key = tag['key'] as String?;
- result[key] ??= <String?>[];
- result[key]!.add(tag['value'] as String?);
- }
- return result;
- }
-
- @override
- List<Map<String, dynamic>>? toJson(Map<String?, List<String?>>? object) {
- if (object == null) {
- return null;
- }
- if (object.isEmpty) {
- return const <Map<String, List<String>>>[];
- }
- final List<Map<String, String>> result = <Map<String, String>>[];
- for (String? key in object.keys) {
- if (key == null) {
- continue;
- }
- final List<String?>? values = object[key];
- if (values == null) {
- continue;
- }
- for (String? value in values) {
- if (value == null) {
- continue;
- }
- result.add(<String, String>{
- 'key': key,
- 'value': value,
- });
- }
- }
- return result;
- }
-}
-
-/// A converter for a "binary" JSON field.
-///
-/// Encodes and decodes a String to and from base64.
-class Base64Converter implements JsonConverter<String, String> {
- const Base64Converter();
-
- @override
- String fromJson(String json) {
- return utf8.decode(base64.decode(json));
- }
-
- @override
- String toJson(String object) {
- return base64.encode(utf8.encode(object));
- }
-}
-
-/// A converter for "timestamp" fields encoded as microseconds since epoch.
-class MicrosecondsSinceEpochConverter implements JsonConverter<DateTime?, String?> {
- const MicrosecondsSinceEpochConverter();
-
- @override
- DateTime? fromJson(String? json) {
- if (json == null) {
- return null;
- }
- return DateTime.fromMicrosecondsSinceEpoch(int.parse(json));
- }
-
- @override
- String? toJson(DateTime? object) {
- if (object == null) {
- return null;
- }
- return object.microsecondsSinceEpoch.toString();
- }
-}
-
-/// A converter for "timestamp" fields encoded as seconds since epoch.
-class SecondsSinceEpochConverter implements JsonConverter<DateTime?, String?> {
- const SecondsSinceEpochConverter();
-
- @override
- DateTime? fromJson(String? json) {
- if (json == null) {
- return null;
- }
- return DateTime.fromMillisecondsSinceEpoch(int.parse(json) * 1000);
- }
-
- @override
- String? toJson(DateTime? dateTime) {
- if (dateTime == null) {
- return null;
- }
- final int secondsSinceEpoch = dateTime.millisecondsSinceEpoch ~/ 1000;
- return secondsSinceEpoch.toString();
- }
-}
-
-/// A converter for boolean fields encoded as strings.
-class BoolConverter implements JsonConverter<bool?, String?> {
- const BoolConverter();
-
- @override
- bool? fromJson(String? json) {
- if (json == null) {
- return null;
- }
- return json.toLowerCase() == 'true';
- }
-
- @override
- String? toJson(bool? value) {
- if (value == null) {
- return null;
- }
- return '$value';
- }
-}
-
-/// A converter for fields with nested JSON objects in String format.
-class NestedJsonConverter implements JsonConverter<Map<String, dynamic>?, String?> {
- const NestedJsonConverter();
-
- @override
- Map<String, dynamic>? fromJson(String? json) {
- if (json == null) {
- return null;
- }
- return convert.json.decode(json) as Map<String, dynamic>?;
- }
-
- @override
- String? toJson(Map<String, dynamic>? object) {
- if (object == null) {
- return null;
- }
- return convert.json.encode(object);
- }
-}
-
-const Map<String, int> _months = <String, int>{
- 'Jan': 1,
- 'Feb': 2,
- 'Mar': 3,
- 'Apr': 4,
- 'May': 5,
- 'Jun': 6,
- 'Jul': 7,
- 'Aug': 8,
- 'Sep': 9,
- 'Oct': 10,
- 'Nov': 11,
- 'Dec': 12,
-};
-
-/// Convert a DateTime format from Gerrit to [DateTime].
-///
-/// Example format is "Wed Jun 07 22:54:06 2023 +0000"
-class GerritDateTimeConverter implements JsonConverter<DateTime?, String?> {
- const GerritDateTimeConverter();
-
- @override
- DateTime? fromJson(String? json) {
- if (json == null) {
- return null;
- }
-
- final DateTime? date = DateTime.tryParse(json);
- if (date != null) {
- return date;
- }
-
- json = json.substring(4); // Trim day of the week
- final List<String> parts = json.split(' ');
- final int month = _months[parts[0]]!;
- final int year = int.parse(parts[3]);
- final int day = int.parse(parts[1]);
- final List<String> time = parts[2].split(':');
- final int hours = int.parse(time[0]);
- final int minutes = int.parse(time[1]);
- final int seconds = int.parse(time[2]);
-
- return DateTime(year, month, day, hours, minutes, seconds);
- }
-
- @override
- String? toJson(DateTime? object) {
- return object?.toIso8601String();
- }
-}
diff --git a/app_dart/lib/src/model/gerrit/commit.dart b/app_dart/lib/src/model/gerrit/commit.dart
deleted file mode 100644
index 9b3a16c..0000000
--- a/app_dart/lib/src/model/gerrit/commit.dart
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2019 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:convert';
-
-import 'package:cocoon_service/src/model/common/json_converters.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-import '../../request_handling/body.dart';
-
-part 'commit.g.dart';
-
-/// Representation of a commit on Gerrit.
-///
-/// See more:
-/// * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#commit-info
-@JsonSerializable()
-class GerritCommit extends JsonBody {
- const GerritCommit({
- this.commit,
- this.tree,
- this.author,
- this.committer,
- this.message,
- });
-
- static GerritCommit fromJson(Map<String, dynamic> json) => _$GerritCommitFromJson(json);
-
- final String? commit;
- final String? tree;
- final GerritUser? author;
- final GerritUser? committer;
- final String? message;
-
- @override
- Map<String, dynamic> toJson() => _$GerritCommitToJson(this);
-
- @override
- String toString() => jsonEncode(toJson());
-}
-
-/// Gerrit info containing the author/comitter of a commit.
-///
-/// See more:
-/// * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#git-person-info
-@JsonSerializable()
-class GerritUser extends JsonBody {
- const GerritUser({
- this.name,
- this.email,
- this.time,
- });
-
- static GerritUser fromJson(Map<String, dynamic> json) => _$GerritUserFromJson(json);
-
- final String? name;
- final String? email;
-
- @GerritDateTimeConverter()
- final DateTime? time;
-
- @override
- Map<String, dynamic> toJson() => _$GerritUserToJson(this);
-
- @override
- String toString() => jsonEncode(toJson());
-}
diff --git a/app_dart/lib/src/model/gerrit/commit.g.dart b/app_dart/lib/src/model/gerrit/commit.g.dart
deleted file mode 100644
index a12615d..0000000
--- a/app_dart/lib/src/model/gerrit/commit.g.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'commit.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-GerritCommit _$GerritCommitFromJson(Map<String, dynamic> json) => GerritCommit(
- commit: json['commit'] as String?,
- tree: json['tree'] as String?,
- author: json['author'] == null ? null : GerritUser.fromJson(json['author'] as Map<String, dynamic>),
- committer: json['committer'] == null ? null : GerritUser.fromJson(json['committer'] as Map<String, dynamic>),
- message: json['message'] as String?,
- );
-
-Map<String, dynamic> _$GerritCommitToJson(GerritCommit instance) => <String, dynamic>{
- 'commit': instance.commit,
- 'tree': instance.tree,
- 'author': instance.author,
- 'committer': instance.committer,
- 'message': instance.message,
- };
-
-GerritUser _$GerritUserFromJson(Map<String, dynamic> json) => GerritUser(
- name: json['name'] as String?,
- email: json['email'] as String?,
- time: const GerritDateTimeConverter().fromJson(json['time'] as String?),
- );
-
-Map<String, dynamic> _$GerritUserToJson(GerritUser instance) => <String, dynamic>{
- 'name': instance.name,
- 'email': instance.email,
- 'time': const GerritDateTimeConverter().toJson(instance.time),
- };
diff --git a/app_dart/lib/src/model/github/checks.dart b/app_dart/lib/src/model/github/checks.dart
deleted file mode 100644
index 8bd4f3e..0000000
--- a/app_dart/lib/src/model/github/checks.dart
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2020 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:github/github.dart' show CheckSuite, PullRequest, User, Repository;
-import 'package:github/hooks.dart' show HookEvent;
-import 'package:json_annotation/json_annotation.dart';
-
-part 'checks.g.dart';
-
-/// Data models for json messages coming from GitHub Checks API.
-///
-/// See more:
-/// * https://developer.com/v3/checks/.
-@JsonSerializable(fieldRename: FieldRename.snake)
-class CheckRunEvent extends HookEvent {
- CheckRunEvent({
- this.action,
- this.checkRun,
- this.sender,
- this.repository,
- });
-
- factory CheckRunEvent.fromJson(Map<String, dynamic> input) => _$CheckRunEventFromJson(input);
- CheckRun? checkRun;
- String? action;
- User? sender;
- Repository? repository;
-
- Map<String, dynamic> toJson() => _$CheckRunEventToJson(this);
-}
-
-@JsonSerializable(fieldRename: FieldRename.snake)
-class CheckRun {
- const CheckRun({
- this.conclusion,
- this.headSha,
- this.id,
- this.pullRequests,
- this.name,
- this.checkSuite,
- });
-
- factory CheckRun.fromJson(Map<String, dynamic> input) => _$CheckRunFromJson(input);
- final int? id;
- final String? headSha;
- final String? conclusion;
- final String? name;
- final CheckSuite? checkSuite;
- @JsonKey(name: 'pull_requests', defaultValue: <PullRequest>[])
- final List<PullRequest>? pullRequests;
-
- Map<String, dynamic> toJson() => _$CheckRunToJson(this);
-}
diff --git a/app_dart/lib/src/model/github/checks.g.dart b/app_dart/lib/src/model/github/checks.g.dart
deleted file mode 100644
index 1c80026..0000000
--- a/app_dart/lib/src/model/github/checks.g.dart
+++ /dev/null
@@ -1,44 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'checks.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-CheckRunEvent _$CheckRunEventFromJson(Map<String, dynamic> json) => CheckRunEvent(
- action: json['action'] as String?,
- checkRun: json['check_run'] == null ? null : CheckRun.fromJson(json['check_run'] as Map<String, dynamic>),
- sender: json['sender'] == null ? null : User.fromJson(json['sender'] as Map<String, dynamic>),
- repository: json['repository'] == null ? null : Repository.fromJson(json['repository'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$CheckRunEventToJson(CheckRunEvent instance) => <String, dynamic>{
- 'check_run': instance.checkRun,
- 'action': instance.action,
- 'sender': instance.sender,
- 'repository': instance.repository,
- };
-
-CheckRun _$CheckRunFromJson(Map<String, dynamic> json) => CheckRun(
- conclusion: json['conclusion'] as String?,
- headSha: json['head_sha'] as String?,
- id: json['id'] as int?,
- pullRequests: (json['pull_requests'] as List<dynamic>?)
- ?.map((e) => PullRequest.fromJson(e as Map<String, dynamic>))
- .toList() ??
- [],
- name: json['name'] as String?,
- checkSuite: json['check_suite'] == null ? null : CheckSuite.fromJson(json['check_suite'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$CheckRunToJson(CheckRun instance) => <String, dynamic>{
- 'id': instance.id,
- 'head_sha': instance.headSha,
- 'conclusion': instance.conclusion,
- 'name': instance.name,
- 'check_suite': instance.checkSuite,
- 'pull_requests': instance.pullRequests,
- };
diff --git a/app_dart/lib/src/model/google/grpc.dart b/app_dart/lib/src/model/google/grpc.dart
deleted file mode 100644
index c3593eb..0000000
--- a/app_dart/lib/src/model/google/grpc.dart
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019 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:json_annotation/json_annotation.dart';
-
-import '../../request_handling/body.dart';
-
-part 'grpc.g.dart';
-
-/// [Status] defines a logical error model that is suitable for
-/// different programming environments, including REST APIs and RPC APIs. It is
-/// used by [gRPC](https://github.com/grpc). Each [Status] message contains
-/// three pieces of data: error code, error message, and error details.
-///
-/// Resources:
-/// * https://cloud.google.com/apis/design/errors
-@JsonSerializable(includeIfNull: false)
-class GrpcStatus extends JsonBody {
- const GrpcStatus({required this.code, this.message, this.details});
-
- /// Creates a [Status] from JSON.
- static GrpcStatus fromJson(Map<String, dynamic> json) => _$GrpcStatusFromJson(json);
-
- /// The status code, which should be an enum value of [google.rpc.Code][].
- final int code;
-
- /// A developer-facing error message, which should be in English. Any
- /// user-facing error message should be localized and sent in the
- /// [google.rpc.Status.details][] field, or localized by the client.
- final String? message;
-
- /// A list of messages that carry the error details. There is a common set of
- /// message types for APIs to use.
- final dynamic details;
-
- @override
- String toString() => 'Response #$code: $message, $details';
-
- @override
- Map<String, dynamic> toJson() => _$GrpcStatusToJson(this);
-}
diff --git a/app_dart/lib/src/model/google/grpc.g.dart b/app_dart/lib/src/model/google/grpc.g.dart
deleted file mode 100644
index a690c98..0000000
--- a/app_dart/lib/src/model/google/grpc.g.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'grpc.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-GrpcStatus _$GrpcStatusFromJson(Map<String, dynamic> json) => GrpcStatus(
- code: json['code'] as int,
- message: json['message'] as String?,
- details: json['details'],
- );
-
-Map<String, dynamic> _$GrpcStatusToJson(GrpcStatus instance) {
- final val = <String, dynamic>{
- 'code': instance.code,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('message', instance.message);
- writeNotNull('details', instance.details);
- return val;
-}
diff --git a/app_dart/lib/src/model/google/token_info.dart b/app_dart/lib/src/model/google/token_info.dart
deleted file mode 100644
index 6406699..0000000
--- a/app_dart/lib/src/model/google/token_info.dart
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2019 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:json_annotation/json_annotation.dart';
-
-import '../common/json_converters.dart';
-
-part 'token_info.g.dart';
-
-@JsonSerializable()
-class TokenInfo {
- /// Creates a new [TokenInfo].
- const TokenInfo({
- this.issuer,
- this.authorizedParty,
- this.audience,
- this.subject,
- this.hostedDomain,
- this.email,
- this.emailIsVerified,
- this.accessTokenHash,
- this.fullName,
- this.profilePictureUrl,
- this.givenName,
- this.familyName,
- this.locale,
- this.issued,
- this.expiration,
- this.jwtId,
- this.algorithm,
- this.keyId,
- this.encoding,
- });
-
- /// Create a new [TokenInfo] object from its JSON representation.
- factory TokenInfo.fromJson(Map<String, dynamic> json) => _$TokenInfoFromJson(json);
-
- /// The issuer of the token (e.g. "accounts.google.com").
- @JsonKey(name: 'iss')
- final String? issuer;
-
- /// The party to which the ID Token was issued.
- @JsonKey(name: 'azp')
- final String? authorizedParty;
-
- /// The token's intended audience.
- ///
- /// This should be compared against the expected OAuth client ID.
- @JsonKey(name: 'aud')
- final String? audience;
-
- /// The principal (subject) of the token.
- @JsonKey(name: 'sub')
- final String? subject;
-
- /// The user's domain.
- ///
- /// https://developers.google.com/identity/protocols/OpenIDConnect#hd-param
- @JsonKey(name: 'hd')
- final String? hostedDomain;
-
- /// The user's email address.
- @JsonKey(name: 'email')
- final String? email;
-
- /// Boolean indicating whether the user has verified their email address.
- @JsonKey(name: 'email_verified')
- @BoolConverter()
- final bool? emailIsVerified;
-
- /// Access token hash value.
- @JsonKey(name: 'at_hash')
- final String? accessTokenHash;
-
- /// The user's full name.
- @JsonKey(name: 'name')
- final String? fullName;
-
- /// URL of the user's profile picture.
- @JsonKey(name: 'picture')
- final String? profilePictureUrl;
-
- /// The user's given name.
- @JsonKey(name: 'given_name')
- final String? givenName;
-
- /// The user's family name / surname.
- @JsonKey(name: 'family_name')
- final String? familyName;
-
- /// The user's local code (e.g. "en")
- @JsonKey(name: 'locale')
- final String? locale;
-
- /// Token issuance date.
- @JsonKey(name: 'iat')
- @SecondsSinceEpochConverter()
- final DateTime? issued;
-
- /// Token expiration.
- @JsonKey(name: 'exp')
- @SecondsSinceEpochConverter()
- final DateTime? expiration;
-
- /// Unique identifier for the token itself.
- @JsonKey(name: 'jti')
- final String? jwtId;
-
- /// Encryption algorithm used to encrypt the token (e.g. "RS256").
- @JsonKey(name: 'alg')
- final String? algorithm;
-
- /// Key identifier.
- @JsonKey(name: 'kid')
- final String? keyId;
-
- /// The encoding that was used to encode the unverified token (e.g. "JWT")
- @JsonKey(name: 'typ')
- final String? encoding;
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$TokenInfoToJson(this);
-}
diff --git a/app_dart/lib/src/model/google/token_info.g.dart b/app_dart/lib/src/model/google/token_info.g.dart
deleted file mode 100644
index 9d8de29..0000000
--- a/app_dart/lib/src/model/google/token_info.g.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'token_info.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-TokenInfo _$TokenInfoFromJson(Map<String, dynamic> json) => TokenInfo(
- issuer: json['iss'] as String?,
- authorizedParty: json['azp'] as String?,
- audience: json['aud'] as String?,
- subject: json['sub'] as String?,
- hostedDomain: json['hd'] as String?,
- email: json['email'] as String?,
- emailIsVerified: const BoolConverter().fromJson(json['email_verified'] as String?),
- accessTokenHash: json['at_hash'] as String?,
- fullName: json['name'] as String?,
- profilePictureUrl: json['picture'] as String?,
- givenName: json['given_name'] as String?,
- familyName: json['family_name'] as String?,
- locale: json['locale'] as String?,
- issued: const SecondsSinceEpochConverter().fromJson(json['iat'] as String?),
- expiration: const SecondsSinceEpochConverter().fromJson(json['exp'] as String?),
- jwtId: json['jti'] as String?,
- algorithm: json['alg'] as String?,
- keyId: json['kid'] as String?,
- encoding: json['typ'] as String?,
- );
-
-Map<String, dynamic> _$TokenInfoToJson(TokenInfo instance) => <String, dynamic>{
- 'iss': instance.issuer,
- 'azp': instance.authorizedParty,
- 'aud': instance.audience,
- 'sub': instance.subject,
- 'hd': instance.hostedDomain,
- 'email': instance.email,
- 'email_verified': const BoolConverter().toJson(instance.emailIsVerified),
- 'at_hash': instance.accessTokenHash,
- 'name': instance.fullName,
- 'picture': instance.profilePictureUrl,
- 'given_name': instance.givenName,
- 'family_name': instance.familyName,
- 'locale': instance.locale,
- 'iat': const SecondsSinceEpochConverter().toJson(instance.issued),
- 'exp': const SecondsSinceEpochConverter().toJson(instance.expiration),
- 'jti': instance.jwtId,
- 'alg': instance.algorithm,
- 'kid': instance.keyId,
- 'typ': instance.encoding,
- };
diff --git a/app_dart/lib/src/model/luci/buildbucket.dart b/app_dart/lib/src/model/luci/buildbucket.dart
deleted file mode 100644
index 903639e..0000000
--- a/app_dart/lib/src/model/luci/buildbucket.dart
+++ /dev/null
@@ -1,928 +0,0 @@
-// Copyright 2019 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:json_annotation/json_annotation.dart';
-
-import '../../request_handling/body.dart';
-import '../common/json_converters.dart';
-import '../google/grpc.dart';
-
-part 'buildbucket.g.dart';
-
-// The classes in this file are based on protos found in:
-// https://chromium.googlesource.com/infra/luci/luci-go/+/master/buildbucket/proto/build.proto
-// https://chromium.googlesource.com/infra/luci/luci-go/+/master/buildbucket/proto/common.proto
-// https://chromium.googlesource.com/infra/luci/luci-go/+/master/buildbucket/proto/rpc.proto
-//
-// The `fromJson` methods in this class are static rather than factories so that
-// they can be passed as arguments to other functions looking for a parser.
-
-/// A request for the Batch RPC.
-///
-/// This message can be used to find, get, schedule, or cancel multiple builds.
-@JsonSerializable(includeIfNull: false)
-class BatchRequest extends JsonBody {
- /// Creates a request for the Batch RPC.
- const BatchRequest({
- this.requests,
- });
-
- /// Creates a [BatchRequest] from JSON.
- static BatchRequest fromJson(Map<String, dynamic> json) => _$BatchRequestFromJson(json);
-
- /// The batch of [Request]s to make.
- final List<Request>? requests;
-
- @override
- Map<String, dynamic> toJson() => _$BatchRequestToJson(this);
-
- @override
- String toString() {
- return requests.toString();
- }
-}
-
-/// A container for one request in a batch.
-///
-/// A single request must contain only one object.
-@JsonSerializable(includeIfNull: false)
-class Request extends JsonBody {
- /// Creates a request for the Batch RPC.
- ///
- /// One and only one argument should be set.
- const Request({
- this.getBuild,
- this.searchBuilds,
- this.scheduleBuild,
- this.cancelBuild,
- }) : assert(
- (getBuild != null && searchBuilds == null && scheduleBuild == null && cancelBuild == null) ||
- (getBuild == null && searchBuilds != null && scheduleBuild == null && cancelBuild == null) ||
- (getBuild == null && searchBuilds == null && scheduleBuild != null && cancelBuild == null) ||
- (getBuild == null && searchBuilds == null && scheduleBuild == null && cancelBuild != null),
- );
-
- /// Creates a [Request] object from JSON.
- static Request fromJson(Map<String, dynamic> json) => _$RequestFromJson(json);
-
- /// A request to get build information.
- final GetBuildRequest? getBuild;
-
- /// A request to find builds.
- final SearchBuildsRequest? searchBuilds;
-
- /// A request to schedule a build.
- ///
- /// All schedule build requests are executed before other requests by LUCI.
- final ScheduleBuildRequest? scheduleBuild;
-
- /// A request to cancel a build.
- final CancelBuildRequest? cancelBuild;
-
- @override
- Map<String, dynamic> toJson() => _$RequestToJson(this);
-
- @override
- String toString() {
- return getBuild?.toString() ??
- searchBuilds?.toString() ??
- scheduleBuild?.toString() ??
- cancelBuild?.toString() ??
- 'Unknown build';
- }
-}
-
-/// A response from the Batch RPC.
-@JsonSerializable(includeIfNull: false)
-class BatchResponse extends JsonBody {
- /// Creates a response for the Batch RPC.
- const BatchResponse({
- this.responses,
- });
-
- /// Creates a [BatchResponse] from JSON.
- static BatchResponse fromJson(Map<String, dynamic>? json) => _$BatchResponseFromJson(json!);
-
- /// The collected responses from the Batch request.
- final List<Response>? responses;
-
- @override
- Map<String, dynamic> toJson() => _$BatchResponseToJson(this);
-}
-
-/// An individual response from a batch request.
-@JsonSerializable(includeIfNull: false)
-class Response extends JsonBody {
- /// Creates a response for the response from the Batch RPC.
- ///
- /// One and only one of these should be set.
- const Response({
- this.getBuild,
- this.searchBuilds,
- this.scheduleBuild,
- this.cancelBuild,
- this.error,
- }) : assert(
- getBuild != null || searchBuilds != null || scheduleBuild != null || cancelBuild != null || error != null,
- );
-
- /// Creates a [Response] from JSON.
- static Response fromJson(Map<String, dynamic> json) => _$ResponseFromJson(json);
-
- /// The [Build] response corresponding to a getBuild request.
- final Build? getBuild;
-
- /// The [SearchBuildsResponse] corresponding to a searchBuilds request.
- final SearchBuildsResponse? searchBuilds;
-
- /// The [Build] response corresponding to a scheduleBuild request.
- final Build? scheduleBuild;
-
- /// The [Build] response corresponding to a cancelBuild request.
- final Build? cancelBuild;
-
- /// Error code of the unsuccessful request.
- final GrpcStatus? error;
-
- @override
- String toString() {
- if (getBuild != null) {
- return 'getBuild: $getBuild; status: $error';
- } else if (searchBuilds != null) {
- return 'searchBuilds: $searchBuilds; status: $error';
- } else if (scheduleBuild != null) {
- return 'scheduleBuild: $scheduleBuild; status: $error';
- } else if (cancelBuild != null) {
- return 'cancelBuild: $cancelBuild; status: $error';
- }
-
- return 'No response';
- }
-
- @override
- Map<String, dynamic> toJson() => _$ResponseToJson(this);
-}
-
-/// A request for the GetBuild RPC.
-@JsonSerializable(includeIfNull: false)
-class GetBuildRequest extends JsonBody {
- /// Creates a request for the GetBuild RPC.
- const GetBuildRequest({
- this.id,
- this.builderId,
- this.buildNumber,
- this.fields,
- }) : assert(
- (id == null && builderId != null && buildNumber != null) ||
- (id != null && builderId == null && buildNumber == null),
- );
-
- /// Creates a [GetBuildRequest] from JSON.
- static GetBuildRequest fromJson(Map<String, dynamic> json) => _$GetBuildRequestFromJson(json);
-
- /// The BuildBucket build ID.
- ///
- /// If specified, [builderId] and [buildNumber] must be null.
- final String? id;
-
- /// The BuildBucket [BuilderId].
- ///
- /// If specified, [buildNumber] must be specified, and [id] must be null.
- @JsonKey(name: 'builder')
- final BuilderId? builderId;
-
- /// The BuildBucket build number.
- ///
- /// If specified, [builderId] must be specified, and [id] must be null.
- final int? buildNumber;
-
- /// The list fields to be included in the response.
- ///
- /// This is a comma separated list of Build proto fields to get included
- /// in the response.
- final String? fields;
-
- @override
- Map<String, dynamic> toJson() => _$GetBuildRequestToJson(this);
-
- @override
- String toString() {
- return 'getBuild(id: $id, buildNumber: $buildNumber, field: $fields, builderId: $builderId)';
- }
-}
-
-/// A request for the GetBuilder RPC.
-@JsonSerializable(includeIfNull: false)
-class GetBuilderRequest extends JsonBody {
- /// Creates a request for the GetBuild RPC.
- const GetBuilderRequest({
- this.builderId,
- }) : assert(builderId != null);
-
- /// Creates a [GetBuilderRequest] from JSON.
- static GetBuilderRequest fromJson(Map<String, dynamic> json) => _$GetBuilderRequestFromJson(json);
-
- /// The BuildBucket builder ID.
- final BuilderId? builderId;
-
- @override
- Map<String, dynamic> toJson() => _$GetBuilderRequestToJson(this);
-
- @override
- String toString() {
- return 'getBuild(builderId: $builderId)';
- }
-}
-
-/// Configs of a builder.
-@JsonSerializable(includeIfNull: false)
-class BuilderConfig extends JsonBody {
- /// Creates a request for the GetBuild RPC.
- const BuilderConfig({
- this.name,
- }) : assert(name != null);
-
- /// Creates a [GetBuilderRequest] from JSON.
- static BuilderConfig fromJson(Map<String, dynamic> json) => _$BuilderConfigFromJson(json);
-
- /// The BuildBucket builder ID.
- final String? name;
-
- @override
- Map<String, dynamic> toJson() => _$BuilderConfigToJson(this);
-
- @override
- String toString() {
- return 'BuilderConfig(name: $name)';
- }
-}
-
-/// A configured builder.
-///
-/// https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/builder_common.proto
-@JsonSerializable(includeIfNull: false)
-class BuilderItem extends JsonBody {
- /// Creates a request for the GetBuild RPC.
- const BuilderItem({
- this.id,
- this.config,
- });
-
- /// Creates a [GetBuilderRequest] from JSON.
- static BuilderItem fromJson(Map<String, dynamic>? json) => _$BuilderItemFromJson(json!);
-
- /// The BuildBucket builder ID.
- final BuilderId? id;
-
- /// The BuildBucket builder config.
- final BuilderConfig? config;
-
- @override
- Map<String, dynamic> toJson() => _$BuilderItemToJson(this);
-
- @override
- String toString() {
- return 'BuilderItem(builderID: $id, builderConfig: $config)';
- }
-}
-
-/// A requrst for the ListBuilders RPC.
-@JsonSerializable(includeIfNull: false)
-class ListBuildersRequest extends JsonBody {
- /// Creates a request object for the ListBuilders RPC.
- const ListBuildersRequest({
- required this.project,
- this.bucket,
- this.pageSize = 1000,
- this.pageToken,
- });
-
- /// Creates a [ListBuildersRequest] from JSON.
- static ListBuildersRequest fromJson(Map<String, dynamic> json) => _$ListBuildersRequestFromJson(json);
-
- /// LUCI project, e.g. "flutter".
- @JsonKey(required: true)
- final String project;
-
- /// A bucket in the project, e.g. "prod".
- ///
- /// Omit to list all builders or all builders in a project.
- @JsonKey(required: false)
- final String? bucket;
-
- /// The maximum number of builders to return.
- ///
- /// The service may return fewer than this value.
- /// If unspecified, at most 100 builders will be returned.
- /// The maximum value is 1000; values above 1000 will be coerced to 1000.
- @JsonKey(required: false)
- final int? pageSize;
-
- // A page token, received from a previous `ListBuilders` call.
- // Provide this to retrieve the subsequent page.
- //
- // When paginating, all other parameters provided to `ListBuilders` MUST
- // match the call that provided the page token.
- @JsonKey(required: false)
- final String? pageToken;
-
- @override
- Map<String, dynamic> toJson() => _$ListBuildersRequestToJson(this);
-
- @override
- String toString() {
- return 'listBuilders(project: $project, bucket: $bucket, pageSize: $pageSize, pageToken: $pageToken)';
- }
-}
-
-/// The response object from a ListBuilders RPC.
-@JsonSerializable(includeIfNull: false)
-class ListBuildersResponse extends JsonBody {
- /// Creates a new response object from the ListBuilders RPC.
- ///
- /// The [nextPageToken] can be used to coninue searching if there are more
- /// builds available than the [pageSize] of the request (which is always
- /// capped at 1000). It will be null if no further builders are available.
- const ListBuildersResponse({
- this.builders,
- this.nextPageToken,
- });
-
- /// Creates a [ListBuildersResponse] from JSON.
- static ListBuildersResponse fromJson(Map<String, dynamic>? json) => _$ListBuildersResponseFromJson(json!);
-
- /// The [Builders]s returned by the search.
- final List<BuilderItem>? builders;
-
- /// A token that can be used as the [ListBuildersRequest.pageToken].
- ///
- /// This value will only be specified if further results are available;
- /// otherwise, it will be null.
- final String? nextPageToken;
-
- @override
- Map<String, dynamic> toJson() => _$ListBuildersResponseToJson(this);
-
- @override
- String toString() => builders.toString();
-}
-
-/// A request for the CancelBuild RPC.
-@JsonSerializable(includeIfNull: false)
-class CancelBuildRequest extends JsonBody {
- /// Creates a request object for the CancelBuild RPC.
- ///
- /// Both [id] and [summaryMarkdown] are required.
- const CancelBuildRequest({
- required this.id,
- required this.summaryMarkdown,
- });
-
- /// Creates a [CancelBuildRequest] from JSON.
- static CancelBuildRequest fromJson(Map<String, dynamic> json) => _$CancelBuildRequestFromJson(json);
-
- /// The BuildBucket ID for the build to cancel.
- @JsonKey(required: true)
- final String id;
-
- /// A summary of the reason for canceling.
- @JsonKey(required: true)
- final String summaryMarkdown;
-
- @override
- Map<String, dynamic> toJson() => _$CancelBuildRequestToJson(this);
-
- @override
- String toString() {
- return 'cancelBuild(id: $id, summaryMarkdown: $summaryMarkdown)';
- }
-}
-
-/// A request object for the SearchBuilds RPC.
-@JsonSerializable(includeIfNull: false)
-class SearchBuildsRequest extends JsonBody {
- /// Creates a request object for the SearchBuilds RPC.
- ///
- /// The [predicate] is required.
- ///
- /// The [pageSize] defaults to 100 if not specified.
- ///
- /// The [pageToken] from a previous request can be used to page through
- /// results.
- const SearchBuildsRequest({
- required this.predicate,
- this.pageSize,
- this.pageToken,
- this.fields,
- });
-
- /// Creates a [SearchBuildsReqeuest] object from JSON.
- static SearchBuildsRequest fromJson(Map<String, dynamic> json) => _$SearchBuildsRequestFromJson(json);
-
- /// The predicate for searching.
- final BuildPredicate predicate;
-
- /// The number of builds to return per request. Defaults to 100.
- ///
- /// Any value over 1000 is treated as 1000.
- final int? pageSize;
-
- /// The value of the [SearchBuildsResponse.nextPageToken] from a previous ]
- /// request.
- ///
- /// This can be used to continue paging through results when there are more
- /// than [pageSize] builds available.
- final String? pageToken;
-
- /// The list fields to be included in the response.
- ///
- /// This is a comma separated list of Build proto fields to get included
- /// in the response.
- final String? fields;
-
- @override
- Map<String, dynamic> toJson() => _$SearchBuildsRequestToJson(this);
-
- @override
- String toString() {
- return 'searchBuild(predicate: $predicate, pageSize: $pageSize, pageToken: $pageToken, fields: $fields)';
- }
-}
-
-/// A predicate to apply when searching for builds in the SearchBuilds RPC.
-@JsonSerializable(includeIfNull: false)
-class BuildPredicate extends JsonBody {
- /// Creates a predicate to apply when searching for builds in the SearchBuilds
- /// RPC.
- ///
- /// All items specified must match for the predicate to return.
- const BuildPredicate({
- this.builderId,
- this.status,
- this.createdBy,
- this.tags,
- this.includeExperimental,
- });
-
- /// Creates a [BuildPredicate] from JSON.
- static BuildPredicate fromJson(Map<String, dynamic> json) => _$BuildPredicateFromJson(json);
-
- /// The [BuilderId] to search for.
- @JsonKey(name: 'builder')
- final BuilderId? builderId;
-
- /// The [Status] to search for.
- final Status? status;
-
- /// Used to find builds created by the specified user.
- final String? createdBy;
-
- /// Used to return builds containing all of the specified tags.
- @TagsConverter()
- final Map<String?, List<String?>>? tags;
-
- /// Determines whether to include experimental builds in the result.
- ///
- /// Defaults to false.
- final bool? includeExperimental;
-
- @override
- Map<String, dynamic> toJson() => _$BuildPredicateToJson(this);
-
- @override
- String toString() {
- return 'buildPredicate(builderId: $builderId, status: $status, createdBy: $createdBy, tags: $tags, includeExperimental: $includeExperimental)';
- }
-}
-
-/// The response object from a SearchBuilds RPC.
-@JsonSerializable(includeIfNull: false)
-class SearchBuildsResponse extends JsonBody {
- /// Creates a new response object from the SearchBuilds RPC.
- ///
- /// The [nextPageToken] can be used to coninue searching if there are more
- /// builds available than the [pageSize] of the request (which is always
- /// capped at 1000). It will be null if no further builds are available.
- const SearchBuildsResponse({
- this.builds,
- this.nextPageToken,
- });
-
- /// Creates a [SearchBuildsResponse] from JSON.
- static SearchBuildsResponse fromJson(Map<String, dynamic>? json) => _$SearchBuildsResponseFromJson(json!);
-
- /// The [Build]s returned by the search.
- final List<Build>? builds;
-
- /// A token that can be used as the [SearchBuildsRequest.pageToken].
- ///
- /// This value will only be specified if further results are available;
- /// otherwise, it will be null.
- final String? nextPageToken;
-
- @override
- Map<String, dynamic> toJson() => _$SearchBuildsResponseToJson(this);
-
- @override
- String toString() => builds.toString();
-}
-
-/// A request object for the ScheduleBuild RPC.
-@JsonSerializable(includeIfNull: false)
-class ScheduleBuildRequest extends JsonBody {
- /// Creates a new request object for the ScheduleBuild RPC.
- ///
- /// The [requestId] is "strongly recommended", and is used by the back end to
- /// deduplicate recent requests.
- ///
- /// The [builderId] is required.
- const ScheduleBuildRequest({
- this.requestId,
- required this.builderId,
- this.canary,
- this.experimental,
- this.gitilesCommit,
- this.properties,
- this.dimensions,
- this.priority,
- this.tags,
- this.notify,
- this.fields,
- this.exe,
- });
-
- /// Creates a [ScheduleBuildRequest] from JSON.
- static ScheduleBuildRequest fromJson(Map<String, dynamic> json) => _$ScheduleBuildRequestFromJson(json);
-
- /// A unique identifier per request that is used by the backend to deduplicate
- /// requests.
- ///
- /// This is "strongly recommended", but not required.
- final String? requestId;
-
- /// The [BuilderId] to schedule on. Required.
- @JsonKey(name: 'builder')
- final BuilderId builderId;
-
- /// If specified, overrides the server-defined value of
- /// Build.infra.buildbucket.canary.
- final bool? canary;
-
- /// If specified, overrides the server-defined value of
- /// Build.input.experimental.
- ///
- /// This value comes into the recipe as `api.runtime.is_experimental`.
- final Trinary? experimental;
-
- /// Properties to include in Build.input.properties.
- /// Input properties of the created build are result of merging server-defined
- /// properties and properties in this field.
- /// Each property in this field defines a new or replaces an existing property
- /// on the server.
- /// If the server config does not allow overriding/adding the property, the
- /// request will fail with InvalidArgument error code.
- /// A server-defined property cannot be removed, but its value can be
- /// replaced with null.
- ///
- /// Reserved property paths:
- /// * ["buildbucket"]
- /// * ["buildername"]
- /// * ["blamelist""]
- /// * ["$recipe_engine/runtime", "is_luci"]
- /// * ["$recipe_engine/runtime", "is_experimental"]
- final Map<String, Object>? properties;
-
- /// The value for Build.input.gitiles_commit.
- ///
- /// Setting this field will cause the created build to have a "buildset"
- /// tag with value "commit/gitiles/{hostname}/{project}/+/{id}".
- ///
- /// GitilesCommit objects MUST have host, project, ref fields set.
- final GitilesCommit? gitilesCommit;
-
- /// Tags to include in Build.tags of the created build.
- ///
- /// Note: tags of the created build may include other tags defined on the
- /// server.
- @TagsConverter()
- final Map<String?, List<String?>>? tags;
-
- /// Overrides default dimensions defined by builder config or template build.
- ///
- /// A set of entries with the same key defines a new or replaces an existing
- /// dimension with the same key.
- final List<RequestedDimension>? dimensions;
-
- // If not zero, overrides swarming task priority.
- // See also Build.infra.swarming.priority.
- final int? priority;
-
- /// The topic and user data to send build status updates to.
- final NotificationConfig? notify;
-
- /// The list fields to be included in the response.
- ///
- /// This is a comma separated list of Build proto fields to get included
- /// in the response.
- final String? fields;
-
- /// The CIPD package with the recipes.
- final Map<String, dynamic>? exe;
-
- @override
- Map<String, dynamic> toJson() => _$ScheduleBuildRequestToJson(this);
-
- @override
- String toString() {
- return 'scheduleBuildRequest(requestId: $requestId, builderId: $builderId, gitilesCommit: $gitilesCommit, fields: $fields, notify: $notify, exe: $exe)';
- }
-}
-
-/// A single build, identified by an int64 [id], belonging to a builder.
-///
-/// See also:
-/// * [BuilderId]
-/// * [GetBuildRequest]
-@JsonSerializable(includeIfNull: false)
-class Build extends JsonBody {
- /// Creates a build object.
- ///
- /// The [id] and [builderId] parameter is required.
- const Build({
- required this.id,
- required this.builderId,
- this.number,
- this.createdBy,
- this.canceledBy,
- this.startTime,
- this.endTime,
- this.status,
- this.tags,
- this.input,
- this.summaryMarkdown,
- this.critical,
- });
-
- /// Creates a [Build] object from JSON.
- static Build fromJson(Map<String, dynamic>? json) => _$BuildFromJson(json!);
-
- /// The BuildBucket ID for the build. Required.
- final String id;
-
- /// The [BuilderId] for the build. Required.
- @JsonKey(name: 'builder')
- final BuilderId builderId;
-
- /// The LUCI build number for the build.
- ///
- /// This number corresponds to the order of builds, but build numbers may have
- /// gaps.
- final int? number;
-
- /// The verified LUCI identity that created the build.
- final String? createdBy;
-
- /// The verified LUCI identity that canceled the build.
- final String? canceledBy;
-
- /// The start time of the build.
- ///
- /// Required if and only if the [status] is [Status.started], [Status.success],
- /// or [Status.failure].
- final DateTime? startTime;
-
- /// The end time of the build.
- ///
- /// Required if and only if the [status] is terminal. Must not be before
- /// [startTime].
- final DateTime? endTime;
-
- /// The build status.
- ///
- /// Must be specified, and must not be [Status.unspecified].
- final Status? status;
-
- /// Human readable summary of the build in Markdown format.
- ///
- /// Up to 4kb.
- final String? summaryMarkdown;
-
- /// Arbitrary annotations for the build.
- ///
- /// The same key for a tag may be used multiple times.
- @TagsConverter()
- final Map<String?, List<String?>>? tags;
-
- /// If [Trinary.no], then the build status should not be used to assess the
- /// correctness of the input gitilesCommit or gerritChanges.
- final Trinary? critical;
-
- /// The build input values.
- final Input? input;
-
- @override
- Map<String, dynamic> toJson() => _$BuildToJson(this);
-
- @override
- String toString() => 'build(id: $id, builderId: $builderId, number: $number, status: $status, tags: $tags)';
-}
-
-/// A unique handle to a builder on BuildBucket.
-@JsonSerializable(includeIfNull: false)
-class BuilderId extends JsonBody {
- /// Creates a unique handle to a builder on BuildBucket.
- ///
- /// The bucket and builder control what ACLs for the infra, as specified in
- /// cr-buildbucket.cfg.
- const BuilderId({
- this.project,
- this.bucket,
- this.builder,
- });
-
- /// Creates a [BuilderId] object from JSON.
- static BuilderId fromJson(Map<String, dynamic> json) => _$BuilderIdFromJson(json);
-
- /// The project, e.g. "flutter", for the builder.
- final String? project;
-
- /// The bucket, e.g. "try" or "prod", for the builder.
- ///
- /// By convention, "prod" is for assets that will be released, "ci" is for
- /// reviewed code, and "try" is for untrusted code.
- final String? bucket;
-
- /// The builder from cr-buildbucket.cfg, e.g. "Linux" or "Linux Host Engine".
- final String? builder;
-
- @override
- Map<String, dynamic> toJson() => _$BuilderIdToJson(this);
-
- @override
- String toString() => '$project/$bucket/$builder';
-
- @override
- bool operator ==(Object other) =>
- other is BuilderId && other.bucket == bucket && other.builder == builder && other.project == project;
-
- @override
- int get hashCode => toString().hashCode;
-}
-
-/// Specifies a Cloud PubSub topic to send notification updates to from a
-/// [ScheduleBuildRequest].
-@JsonSerializable(includeIfNull: false)
-class NotificationConfig extends JsonBody {
- const NotificationConfig({this.pubsubTopic, this.userData});
-
- static NotificationConfig fromJson(Map<String, dynamic> json) => _$NotificationConfigFromJson(json);
-
- /// The Cloud PubSub topic to use, e.g.
- /// `projects/flutter-dashboard/topics/luci-builds`.
- final String? pubsubTopic;
-
- /// An optional user data field that will be delivered with the message.
- ///
- /// May be omitted.
- @Base64Converter()
- final String? userData;
-
- @override
- Map<String, dynamic> toJson() => _$NotificationConfigToJson(this);
-
- @override
- String toString() => 'NotificationConfig(pubsubTopic: $pubsubTopic, userData: $userData)';
-}
-
-/// The build inputs for a build.
-@JsonSerializable(includeIfNull: false)
-class Input extends JsonBody {
- /// Creates a set of build inputs for a build.
- const Input({
- this.properties,
- this.gitilesCommit,
- this.experimental,
- });
-
- /// Creates an [Input] object from JSON.
- static Input fromJson(Map<String, dynamic> json) => _$InputFromJson(json);
-
- /// The build properties of a build.
- final Map<String, Object>? properties;
-
- /// The [GitilesCommit] information for a build.
- final GitilesCommit? gitilesCommit;
-
- /// Whether the build is experimental or not. Passed into the recipe as
- /// `api.runtime.is_experimental`.
- final bool? experimental;
-
- @override
- Map<String, dynamic> toJson() => _$InputToJson(this);
-}
-
-/// A landed Git commit hosted on Gitiles.
-@JsonSerializable(includeIfNull: false)
-class GitilesCommit extends JsonBody {
- /// Creates a object corresponding to a landed Git commit hosted on Gitiles.
- const GitilesCommit({
- this.host,
- this.project,
- this.ref,
- this.hash,
- });
-
- /// Creates a [GitilesCommit] object from JSON.
- static GitilesCommit fromJson(Map<String, dynamic> json) => _$GitilesCommitFromJson(json);
-
- /// The Gitiles host name, e.g. "chromium.googlesource.com"
- final String? host;
-
- /// The repository name on the host, e.g. "external/github.com/flutter/flutter".
- final String? project;
-
- /// The Git hash of the commit.
- @JsonKey(name: 'id')
- final String? hash;
-
- /// The Git ref of the commit, e.g. "refs/heads/master".
- final String? ref;
-
- @override
- Map<String, dynamic> toJson() => _$GitilesCommitToJson(this);
-}
-
-/// Build status values.
-enum Status {
- /// Should not be used.
- @JsonValue('STATUS_UNSPECIFIED')
- unspecified,
-
- /// The status of a scheduled or pending build.
- @JsonValue('SCHEDULED')
- scheduled,
-
- /// The status of a started (running) build.
- @JsonValue('STARTED')
- started,
-
- /// A mask of `succes | failure | infraFailure | canceled`.
- @JsonValue('ENDED_MASK')
- ended,
-
- /// The build has successfully completed.
- @JsonValue('SUCCESS')
- success,
-
- /// The build has failed to complete some step due to a faulty test or commit.
- @JsonValue('FAILURE')
- failure,
-
- /// The build has failed due to an infrastructure related failure.
- @JsonValue('INFRA_FAILURE')
- infraFailure,
-
- /// The build was canceled.
- @JsonValue('CANCELED')
- canceled,
-}
-
-/// This type doesn't quite map to a bool, because there are actually four states
-/// when you include whether it's present or not.
-enum Trinary {
- /// A true value.
- @JsonValue('YES')
- yes,
-
- /// A false value.
- @JsonValue('NO')
- no,
-
- /// An explicit null value, which may or may not be treated differently from
- /// setting the JSON field to null.
- @JsonValue('UNSET')
- unset,
-}
-
-/// A requested dimension. Looks like StringPair, but also has an expiration.
-@JsonSerializable(includeIfNull: false)
-class RequestedDimension extends JsonBody {
- const RequestedDimension({
- required this.key,
- this.value,
- this.expiration,
- });
-
- static RequestedDimension fromJson(Map<String, dynamic> json) => _$RequestedDimensionFromJson(json);
-
- final String key;
- final String? value;
-
- /// If set, ignore this dimension after this duration. Must be a multiple of 1 minute. The format is '<seconds>s',
- /// e.g. '120s' represents 120 seconds.
- final String? expiration;
-
- @override
- Map<String, dynamic> toJson() => _$RequestedDimensionToJson(this);
-}
diff --git a/app_dart/lib/src/model/luci/buildbucket.g.dart b/app_dart/lib/src/model/luci/buildbucket.g.dart
deleted file mode 100644
index 6cb274c..0000000
--- a/app_dart/lib/src/model/luci/buildbucket.g.dart
+++ /dev/null
@@ -1,532 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'buildbucket.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-BatchRequest _$BatchRequestFromJson(Map<String, dynamic> json) => BatchRequest(
- requests: (json['requests'] as List<dynamic>?)?.map((e) => Request.fromJson(e as Map<String, dynamic>)).toList(),
- );
-
-Map<String, dynamic> _$BatchRequestToJson(BatchRequest instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('requests', instance.requests);
- return val;
-}
-
-Request _$RequestFromJson(Map<String, dynamic> json) => Request(
- getBuild: json['getBuild'] == null ? null : GetBuildRequest.fromJson(json['getBuild'] as Map<String, dynamic>),
- searchBuilds: json['searchBuilds'] == null
- ? null
- : SearchBuildsRequest.fromJson(json['searchBuilds'] as Map<String, dynamic>),
- scheduleBuild: json['scheduleBuild'] == null
- ? null
- : ScheduleBuildRequest.fromJson(json['scheduleBuild'] as Map<String, dynamic>),
- cancelBuild:
- json['cancelBuild'] == null ? null : CancelBuildRequest.fromJson(json['cancelBuild'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$RequestToJson(Request instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('getBuild', instance.getBuild);
- writeNotNull('searchBuilds', instance.searchBuilds);
- writeNotNull('scheduleBuild', instance.scheduleBuild);
- writeNotNull('cancelBuild', instance.cancelBuild);
- return val;
-}
-
-BatchResponse _$BatchResponseFromJson(Map<String, dynamic> json) => BatchResponse(
- responses:
- (json['responses'] as List<dynamic>?)?.map((e) => Response.fromJson(e as Map<String, dynamic>)).toList(),
- );
-
-Map<String, dynamic> _$BatchResponseToJson(BatchResponse instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('responses', instance.responses);
- return val;
-}
-
-Response _$ResponseFromJson(Map<String, dynamic> json) => Response(
- getBuild: json['getBuild'] == null ? null : Build.fromJson(json['getBuild'] as Map<String, dynamic>),
- searchBuilds: json['searchBuilds'] == null
- ? null
- : SearchBuildsResponse.fromJson(json['searchBuilds'] as Map<String, dynamic>),
- scheduleBuild:
- json['scheduleBuild'] == null ? null : Build.fromJson(json['scheduleBuild'] as Map<String, dynamic>),
- cancelBuild: json['cancelBuild'] == null ? null : Build.fromJson(json['cancelBuild'] as Map<String, dynamic>),
- error: json['error'] == null ? null : GrpcStatus.fromJson(json['error'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$ResponseToJson(Response instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('getBuild', instance.getBuild);
- writeNotNull('searchBuilds', instance.searchBuilds);
- writeNotNull('scheduleBuild', instance.scheduleBuild);
- writeNotNull('cancelBuild', instance.cancelBuild);
- writeNotNull('error', instance.error);
- return val;
-}
-
-GetBuildRequest _$GetBuildRequestFromJson(Map<String, dynamic> json) => GetBuildRequest(
- id: json['id'] as String?,
- builderId: json['builder'] == null ? null : BuilderId.fromJson(json['builder'] as Map<String, dynamic>),
- buildNumber: json['buildNumber'] as int?,
- fields: json['fields'] as String?,
- );
-
-Map<String, dynamic> _$GetBuildRequestToJson(GetBuildRequest instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('id', instance.id);
- writeNotNull('builder', instance.builderId);
- writeNotNull('buildNumber', instance.buildNumber);
- writeNotNull('fields', instance.fields);
- return val;
-}
-
-GetBuilderRequest _$GetBuilderRequestFromJson(Map<String, dynamic> json) => GetBuilderRequest(
- builderId: json['builderId'] == null ? null : BuilderId.fromJson(json['builderId'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$GetBuilderRequestToJson(GetBuilderRequest instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('builderId', instance.builderId);
- return val;
-}
-
-BuilderConfig _$BuilderConfigFromJson(Map<String, dynamic> json) => BuilderConfig(
- name: json['name'] as String?,
- );
-
-Map<String, dynamic> _$BuilderConfigToJson(BuilderConfig instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('name', instance.name);
- return val;
-}
-
-BuilderItem _$BuilderItemFromJson(Map<String, dynamic> json) => BuilderItem(
- id: json['id'] == null ? null : BuilderId.fromJson(json['id'] as Map<String, dynamic>),
- config: json['config'] == null ? null : BuilderConfig.fromJson(json['config'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$BuilderItemToJson(BuilderItem instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('id', instance.id);
- writeNotNull('config', instance.config);
- return val;
-}
-
-ListBuildersRequest _$ListBuildersRequestFromJson(Map<String, dynamic> json) {
- $checkKeys(
- json,
- requiredKeys: const ['project'],
- );
- return ListBuildersRequest(
- project: json['project'] as String,
- bucket: json['bucket'] as String?,
- pageSize: json['pageSize'] as int? ?? 1000,
- pageToken: json['pageToken'] as String?,
- );
-}
-
-Map<String, dynamic> _$ListBuildersRequestToJson(ListBuildersRequest instance) {
- final val = <String, dynamic>{
- 'project': instance.project,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('bucket', instance.bucket);
- writeNotNull('pageSize', instance.pageSize);
- writeNotNull('pageToken', instance.pageToken);
- return val;
-}
-
-ListBuildersResponse _$ListBuildersResponseFromJson(Map<String, dynamic> json) => ListBuildersResponse(
- builders:
- (json['builders'] as List<dynamic>?)?.map((e) => BuilderItem.fromJson(e as Map<String, dynamic>)).toList(),
- nextPageToken: json['nextPageToken'] as String?,
- );
-
-Map<String, dynamic> _$ListBuildersResponseToJson(ListBuildersResponse instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('builders', instance.builders);
- writeNotNull('nextPageToken', instance.nextPageToken);
- return val;
-}
-
-CancelBuildRequest _$CancelBuildRequestFromJson(Map<String, dynamic> json) {
- $checkKeys(
- json,
- requiredKeys: const ['id', 'summaryMarkdown'],
- );
- return CancelBuildRequest(
- id: json['id'] as String,
- summaryMarkdown: json['summaryMarkdown'] as String,
- );
-}
-
-Map<String, dynamic> _$CancelBuildRequestToJson(CancelBuildRequest instance) => <String, dynamic>{
- 'id': instance.id,
- 'summaryMarkdown': instance.summaryMarkdown,
- };
-
-SearchBuildsRequest _$SearchBuildsRequestFromJson(Map<String, dynamic> json) => SearchBuildsRequest(
- predicate: BuildPredicate.fromJson(json['predicate'] as Map<String, dynamic>),
- pageSize: json['pageSize'] as int?,
- pageToken: json['pageToken'] as String?,
- fields: json['fields'] as String?,
- );
-
-Map<String, dynamic> _$SearchBuildsRequestToJson(SearchBuildsRequest instance) {
- final val = <String, dynamic>{
- 'predicate': instance.predicate,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('pageSize', instance.pageSize);
- writeNotNull('pageToken', instance.pageToken);
- writeNotNull('fields', instance.fields);
- return val;
-}
-
-BuildPredicate _$BuildPredicateFromJson(Map<String, dynamic> json) => BuildPredicate(
- builderId: json['builder'] == null ? null : BuilderId.fromJson(json['builder'] as Map<String, dynamic>),
- status: $enumDecodeNullable(_$StatusEnumMap, json['status']),
- createdBy: json['createdBy'] as String?,
- tags: const TagsConverter().fromJson(json['tags'] as List?),
- includeExperimental: json['includeExperimental'] as bool?,
- );
-
-Map<String, dynamic> _$BuildPredicateToJson(BuildPredicate instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('builder', instance.builderId);
- writeNotNull('status', _$StatusEnumMap[instance.status]);
- writeNotNull('createdBy', instance.createdBy);
- writeNotNull('tags', const TagsConverter().toJson(instance.tags));
- writeNotNull('includeExperimental', instance.includeExperimental);
- return val;
-}
-
-const _$StatusEnumMap = {
- Status.unspecified: 'STATUS_UNSPECIFIED',
- Status.scheduled: 'SCHEDULED',
- Status.started: 'STARTED',
- Status.ended: 'ENDED_MASK',
- Status.success: 'SUCCESS',
- Status.failure: 'FAILURE',
- Status.infraFailure: 'INFRA_FAILURE',
- Status.canceled: 'CANCELED',
-};
-
-SearchBuildsResponse _$SearchBuildsResponseFromJson(Map<String, dynamic> json) => SearchBuildsResponse(
- builds: (json['builds'] as List<dynamic>?)?.map((e) => Build.fromJson(e as Map<String, dynamic>)).toList(),
- nextPageToken: json['nextPageToken'] as String?,
- );
-
-Map<String, dynamic> _$SearchBuildsResponseToJson(SearchBuildsResponse instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('builds', instance.builds);
- writeNotNull('nextPageToken', instance.nextPageToken);
- return val;
-}
-
-ScheduleBuildRequest _$ScheduleBuildRequestFromJson(Map<String, dynamic> json) => ScheduleBuildRequest(
- requestId: json['requestId'] as String?,
- builderId: BuilderId.fromJson(json['builder'] as Map<String, dynamic>),
- canary: json['canary'] as bool?,
- experimental: $enumDecodeNullable(_$TrinaryEnumMap, json['experimental']),
- gitilesCommit:
- json['gitilesCommit'] == null ? null : GitilesCommit.fromJson(json['gitilesCommit'] as Map<String, dynamic>),
- properties: (json['properties'] as Map<String, dynamic>?)?.map(
- (k, e) => MapEntry(k, e as Object),
- ),
- dimensions: (json['dimensions'] as List<dynamic>?)
- ?.map((e) => RequestedDimension.fromJson(e as Map<String, dynamic>))
- .toList(),
- priority: json['priority'] as int?,
- tags: const TagsConverter().fromJson(json['tags'] as List?),
- notify: json['notify'] == null ? null : NotificationConfig.fromJson(json['notify'] as Map<String, dynamic>),
- fields: json['fields'] as String?,
- exe: json['exe'] as Map<String, dynamic>?,
- );
-
-Map<String, dynamic> _$ScheduleBuildRequestToJson(ScheduleBuildRequest instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('requestId', instance.requestId);
- val['builder'] = instance.builderId;
- writeNotNull('canary', instance.canary);
- writeNotNull('experimental', _$TrinaryEnumMap[instance.experimental]);
- writeNotNull('properties', instance.properties);
- writeNotNull('gitilesCommit', instance.gitilesCommit);
- writeNotNull('tags', const TagsConverter().toJson(instance.tags));
- writeNotNull('dimensions', instance.dimensions);
- writeNotNull('priority', instance.priority);
- writeNotNull('notify', instance.notify);
- writeNotNull('fields', instance.fields);
- writeNotNull('exe', instance.exe);
- return val;
-}
-
-const _$TrinaryEnumMap = {
- Trinary.yes: 'YES',
- Trinary.no: 'NO',
- Trinary.unset: 'UNSET',
-};
-
-Build _$BuildFromJson(Map<String, dynamic> json) => Build(
- id: json['id'] as String,
- builderId: BuilderId.fromJson(json['builder'] as Map<String, dynamic>),
- number: json['number'] as int?,
- createdBy: json['createdBy'] as String?,
- canceledBy: json['canceledBy'] as String?,
- startTime: json['startTime'] == null ? null : DateTime.parse(json['startTime'] as String),
- endTime: json['endTime'] == null ? null : DateTime.parse(json['endTime'] as String),
- status: $enumDecodeNullable(_$StatusEnumMap, json['status']),
- tags: const TagsConverter().fromJson(json['tags'] as List?),
- input: json['input'] == null ? null : Input.fromJson(json['input'] as Map<String, dynamic>),
- summaryMarkdown: json['summaryMarkdown'] as String?,
- critical: $enumDecodeNullable(_$TrinaryEnumMap, json['critical']),
- );
-
-Map<String, dynamic> _$BuildToJson(Build instance) {
- final val = <String, dynamic>{
- 'id': instance.id,
- 'builder': instance.builderId,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('number', instance.number);
- writeNotNull('createdBy', instance.createdBy);
- writeNotNull('canceledBy', instance.canceledBy);
- writeNotNull('startTime', instance.startTime?.toIso8601String());
- writeNotNull('endTime', instance.endTime?.toIso8601String());
- writeNotNull('status', _$StatusEnumMap[instance.status]);
- writeNotNull('summaryMarkdown', instance.summaryMarkdown);
- writeNotNull('tags', const TagsConverter().toJson(instance.tags));
- writeNotNull('critical', _$TrinaryEnumMap[instance.critical]);
- writeNotNull('input', instance.input);
- return val;
-}
-
-BuilderId _$BuilderIdFromJson(Map<String, dynamic> json) => BuilderId(
- project: json['project'] as String?,
- bucket: json['bucket'] as String?,
- builder: json['builder'] as String?,
- );
-
-Map<String, dynamic> _$BuilderIdToJson(BuilderId instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('project', instance.project);
- writeNotNull('bucket', instance.bucket);
- writeNotNull('builder', instance.builder);
- return val;
-}
-
-NotificationConfig _$NotificationConfigFromJson(Map<String, dynamic> json) => NotificationConfig(
- pubsubTopic: json['pubsubTopic'] as String?,
- userData: _$JsonConverterFromJson<String, String>(json['userData'], const Base64Converter().fromJson),
- );
-
-Map<String, dynamic> _$NotificationConfigToJson(NotificationConfig instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('pubsubTopic', instance.pubsubTopic);
- writeNotNull('userData', _$JsonConverterToJson<String, String>(instance.userData, const Base64Converter().toJson));
- return val;
-}
-
-Value? _$JsonConverterFromJson<Json, Value>(
- Object? json,
- Value? Function(Json json) fromJson,
-) =>
- json == null ? null : fromJson(json as Json);
-
-Json? _$JsonConverterToJson<Json, Value>(
- Value? value,
- Json? Function(Value value) toJson,
-) =>
- value == null ? null : toJson(value);
-
-Input _$InputFromJson(Map<String, dynamic> json) => Input(
- properties: (json['properties'] as Map<String, dynamic>?)?.map(
- (k, e) => MapEntry(k, e as Object),
- ),
- gitilesCommit:
- json['gitilesCommit'] == null ? null : GitilesCommit.fromJson(json['gitilesCommit'] as Map<String, dynamic>),
- experimental: json['experimental'] as bool?,
- );
-
-Map<String, dynamic> _$InputToJson(Input instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('properties', instance.properties);
- writeNotNull('gitilesCommit', instance.gitilesCommit);
- writeNotNull('experimental', instance.experimental);
- return val;
-}
-
-GitilesCommit _$GitilesCommitFromJson(Map<String, dynamic> json) => GitilesCommit(
- host: json['host'] as String?,
- project: json['project'] as String?,
- ref: json['ref'] as String?,
- hash: json['id'] as String?,
- );
-
-Map<String, dynamic> _$GitilesCommitToJson(GitilesCommit instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('host', instance.host);
- writeNotNull('project', instance.project);
- writeNotNull('id', instance.hash);
- writeNotNull('ref', instance.ref);
- return val;
-}
-
-RequestedDimension _$RequestedDimensionFromJson(Map<String, dynamic> json) => RequestedDimension(
- key: json['key'] as String,
- value: json['value'] as String?,
- expiration: json['expiration'] as String?,
- );
-
-Map<String, dynamic> _$RequestedDimensionToJson(RequestedDimension instance) {
- final val = <String, dynamic>{
- 'key': instance.key,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('value', instance.value);
- writeNotNull('expiration', instance.expiration);
- return val;
-}
diff --git a/app_dart/lib/src/model/luci/push_message.dart b/app_dart/lib/src/model/luci/push_message.dart
deleted file mode 100644
index d7c1502..0000000
--- a/app_dart/lib/src/model/luci/push_message.dart
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright 2019 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:convert';
-import 'dart:typed_data';
-
-import 'package:json_annotation/json_annotation.dart';
-
-import '../../request_handling/body.dart';
-import '../../service/logging.dart';
-import '../common/json_converters.dart';
-
-part 'push_message.g.dart';
-
-/// A Cloud PubSub push message.
-///
-/// For example:
-/// ```json
-/// {
-/// "message": {
-/// "attributes": {
-/// "key": "value"
-/// },
-/// "data": "SGVsbG8gQ2xvdWQgUHViL1N1YiEgSGVyZSBpcyBteSBtZXNzYWdlIQ==",
-/// "messageId": "136969346945"
-/// },
-/// "subscription": "projects/myproject/subscriptions/mysubscription"
-/// }
-/// ```
-///
-/// Where `data` is base64 encoded.
-///
-/// See https://cloud.google.com/pubsub/docs/push#receiving_push_messages
-@JsonSerializable(includeIfNull: false)
-class PushMessageEnvelope extends JsonBody {
- const PushMessageEnvelope({
- this.message,
- this.subscription,
- });
-
- static PushMessageEnvelope fromJson(Map<String, dynamic> json) => _$PushMessageEnvelopeFromJson(json);
-
- /// The message contents.
- final PushMessage? message;
-
- /// The name of the subscription associated with the delivery.
- final String? subscription;
-
- @override
- Map<String, dynamic> toJson() => _$PushMessageEnvelopeToJson(this);
-}
-
-/// A PubSub push message payload.
-@JsonSerializable(includeIfNull: false)
-class PushMessage extends JsonBody {
- const PushMessage({
- this.attributes,
- this.data,
- this.messageId,
- this.publishTime,
- });
-
- static PushMessage fromJson(Map<String, dynamic> json) => _$PushMessageFromJson(json);
-
- /// 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;
-
- @override
- Map<String, dynamic> toJson() => _$PushMessageToJson(this);
-}
-
-/// The LUCI build data from a PubSub push message payload.
-@JsonSerializable(includeIfNull: false, fieldRename: FieldRename.snake)
-class BuildPushMessage extends JsonBody {
- const BuildPushMessage({
- this.build,
- this.hostname,
- String? userData,
- }) : rawUserData = userData;
-
- /// Create [BuildPushMessage] from [PushMessage].
- factory BuildPushMessage.fromPushMessage(PushMessage message) {
- final data = message.data;
- if (data == null) {
- throw const FormatException('Cannot create BuildPushMessage from null data');
- }
-
- try {
- final String decodedData = String.fromCharCodes(base64.decode(data));
- log.info('Result message from base64: $decodedData');
- return BuildPushMessage.fromJson(json.decode(decodedData) as Map<String, dynamic>);
- } on FormatException {
- log.info('Result message: $data');
- return BuildPushMessage.fromJson(json.decode(data) as Map<String, dynamic>);
- }
- }
-
- static BuildPushMessage fromJson(Map<String, dynamic> json) => _$BuildPushMessageFromJson(json);
-
- /// The Build this message is for.
- final Build? build;
-
- /// The hostname for the build, e.g. `cr-buildbucket.appspot.com`.
- final String? hostname;
-
- /// Do not use this value for anything.
- ///
- /// This value cannot be marked private due to json_serializable not
- /// generating on private fields.
- ///
- /// This value is used to generate [userData].
- @JsonKey(name: 'user_data')
- final String? rawUserData;
-
- /// User data that was included in the LUCI build request.
- Map<String, dynamic> get userData {
- if (rawUserData == null) {
- return <String, dynamic>{};
- }
-
- try {
- return json.decode(rawUserData!) as Map<String, dynamic>;
- } on FormatException {
- final Uint8List bytes = base64.decode(rawUserData!);
- final String rawJson = String.fromCharCodes(bytes);
- if (rawJson.isEmpty) {
- return <String, dynamic>{};
- }
- return json.decode(rawJson) as Map<String, dynamic>;
- }
- }
-
- @override
- Map<String, dynamic> toJson() => _$BuildPushMessageToJson(this);
-}
-
-/// See https://github.com/luci/luci-go/blob/master/common/api/buildbucket/buildbucket/v1/buildbucket-gen.go#L332ƒ
-@JsonSerializable(includeIfNull: false)
-class Build extends JsonBody {
- const Build({
- this.bucket,
- this.canary,
- this.canaryPreference,
- this.cancelationReason,
- this.completedTimestamp,
- this.createdBy,
- this.createdTimestamp,
- this.failureReason,
- this.experimental,
- this.id,
- this.buildParameters,
- this.project,
- this.result,
- this.resultDetails,
- this.serviceAccount,
- this.startedTimestamp,
- this.status,
- this.tags,
- this.updatedTimestamp,
- this.utcNowTimestamp,
- this.url,
- });
-
- static Build fromJson(Map<String, dynamic> json) => _$BuildFromJson(json);
-
- /// The BuildBucket name.
- final String? bucket;
-
- /// Whether carnary hardware was used.
- final bool? canary;
-
- /// The canary preference for `canary`.
- @JsonKey(name: 'canary_preference')
- final CanaryPreference? canaryPreference;
-
- /// The reason for canceling the build.
- @JsonKey(name: 'cancelation_reason')
- final CancelationReason? cancelationReason;
-
- /// The completion time of the build.
- @JsonKey(name: 'completed_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? completedTimestamp;
-
- /// The user who created the build.
- @JsonKey(name: 'created_by')
- final String? createdBy;
-
- /// The creation time of the build.
- @JsonKey(name: 'created_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? createdTimestamp;
-
- /// Whether the build was experimental or not.
- final bool? experimental;
-
- /// The reason the build failed, if it failed.
- @JsonKey(name: 'failure_reason')
- final FailureReason? failureReason;
-
- /// The unique BuildBucket ID for the build.
- final String? id;
-
- /// Parameters passed to the build.
- @JsonKey(name: 'parameters_json')
- @NestedJsonConverter()
- final Map<String, dynamic>? buildParameters;
-
- /// The BuildBucket project for the build, e.g. `flutter`.
- final String? project;
-
- /// The result of the build.
- ///
- /// If [Result.canceled], [cancelationReason] will be populated.
- ///
- /// If [Result.failure], [failureReason] will be populated.
- final Result? result;
-
- /// A JSON object that contains additional build information based on the
- /// result.
- @JsonKey(name: 'result_details_json')
- @NestedJsonConverter()
- final Map<String, dynamic>? resultDetails;
-
- /// The service account used for the build.
- @JsonKey(name: 'service_account')
- final String? serviceAccount;
-
- /// The time of the build start.
- @JsonKey(name: 'started_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? startedTimestamp;
-
- /// The [Status] of the build.
- ///
- /// If [Status.completed], [result] will be populated.
- final Status? status;
-
- /// The swarming tags for the build.
- final List<String>? tags;
-
- /// Returns all tags matching the prefix.
- ///
- /// For example, to get the `buildset` tag(s), call `tagsByName('buildset')`;
- /// to get the `swarming_tag:os`, call `tagsByName('swarming_tag:os')`.
- List<String> tagsByName(String prefix) {
- return tags!
- .where((String tag) => tag.startsWith('$prefix:'))
- .map<String>((String tag) => tag.substring(prefix.length + 1))
- .toList();
- }
-
- /// The time of the last update to this information.
- @JsonKey(name: 'updated_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? updatedTimestamp;
-
- /// The URL of the build.
- final String? url;
-
- /// The time used as UTC now for reference to other times in this message.
- @JsonKey(name: 'utcnow_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? utcNowTimestamp;
-
- @override
- Map<String, dynamic> toJson() => _$BuildToJson(this);
-}
-
-/// The method to select whether canary hardware was chosen for a build.
-enum CanaryPreference {
- @JsonValue('AUTO')
- auto,
- @JsonValue('CANARY')
- canary,
- @JsonValue('PROD')
- prod,
-}
-
-/// The reason for canceling a build.
-enum CancelationReason {
- @JsonValue('CANCELED_EXPLICITLY')
- canceledExplicitly,
- @JsonValue('TIMEOUT')
- timeout,
-}
-
-/// The reason a build failed.
-enum FailureReason {
- @JsonValue('BUILDBUCKET_FAILURE')
- buildbucketFailure,
- @JsonValue('BUILD_FAILURE')
- buildFailure,
- @JsonValue('INFRA_FAILURE')
- infraFailure,
- @JsonValue('INVALID_BUILD_DEFINITION')
- invalidBuildDefinition,
-}
-
-/// The final result of a build, if its [Status] is [Status.completed].
-enum Result {
- @JsonValue('CANCELED')
- canceled,
- @JsonValue('FAILURE')
- failure,
- @JsonValue('SUCCESS')
- success,
-}
-
-/// The current status of a build.
-///
-/// If [Status.completed], then a [Result] will be present as well.
-enum Status {
- @JsonValue('COMPLETED')
- completed,
- @JsonValue('SCHEDULED')
- scheduled,
- @JsonValue('STARTED')
- started,
-}
diff --git a/app_dart/lib/src/model/luci/push_message.g.dart b/app_dart/lib/src/model/luci/push_message.g.dart
deleted file mode 100644
index 963d7b6..0000000
--- a/app_dart/lib/src/model/luci/push_message.g.dart
+++ /dev/null
@@ -1,173 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'push_message.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-PushMessageEnvelope _$PushMessageEnvelopeFromJson(Map<String, dynamic> json) => PushMessageEnvelope(
- message: json['message'] == null ? null : PushMessage.fromJson(json['message'] as Map<String, dynamic>),
- subscription: json['subscription'] as String?,
- );
-
-Map<String, dynamic> _$PushMessageEnvelopeToJson(PushMessageEnvelope instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('message', instance.message);
- writeNotNull('subscription', instance.subscription);
- return val;
-}
-
-PushMessage _$PushMessageFromJson(Map<String, dynamic> json) => PushMessage(
- attributes: (json['attributes'] as Map<String, dynamic>?)?.map(
- (k, e) => MapEntry(k, e as String),
- ),
- data: _$JsonConverterFromJson<String, String>(json['data'], const Base64Converter().fromJson),
- messageId: json['messageId'] as String?,
- publishTime: json['publishTime'] as String?,
- );
-
-Map<String, dynamic> _$PushMessageToJson(PushMessage instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('attributes', instance.attributes);
- writeNotNull('data', _$JsonConverterToJson<String, String>(instance.data, const Base64Converter().toJson));
- writeNotNull('messageId', instance.messageId);
- writeNotNull('publishTime', instance.publishTime);
- return val;
-}
-
-Value? _$JsonConverterFromJson<Json, Value>(
- Object? json,
- Value? Function(Json json) fromJson,
-) =>
- json == null ? null : fromJson(json as Json);
-
-Json? _$JsonConverterToJson<Json, Value>(
- Value? value,
- Json? Function(Value value) toJson,
-) =>
- value == null ? null : toJson(value);
-
-BuildPushMessage _$BuildPushMessageFromJson(Map<String, dynamic> json) => BuildPushMessage(
- build: json['build'] == null ? null : Build.fromJson(json['build'] as Map<String, dynamic>),
- hostname: json['hostname'] as String?,
- userData: json['user_data'] as String?,
- );
-
-Map<String, dynamic> _$BuildPushMessageToJson(BuildPushMessage instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('build', instance.build);
- writeNotNull('hostname', instance.hostname);
- val['user_data'] = instance.userData;
- return val;
-}
-
-Build _$BuildFromJson(Map<String, dynamic> json) => Build(
- bucket: json['bucket'] as String?,
- canary: json['canary'] as bool?,
- canaryPreference: $enumDecodeNullable(_$CanaryPreferenceEnumMap, json['canary_preference']),
- cancelationReason: $enumDecodeNullable(_$CancelationReasonEnumMap, json['cancelation_reason']),
- completedTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['completed_ts'] as String?),
- createdBy: json['created_by'] as String?,
- createdTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['created_ts'] as String?),
- failureReason: $enumDecodeNullable(_$FailureReasonEnumMap, json['failure_reason']),
- experimental: json['experimental'] as bool?,
- id: json['id'] as String?,
- buildParameters: const NestedJsonConverter().fromJson(json['parameters_json'] as String?),
- project: json['project'] as String?,
- result: $enumDecodeNullable(_$ResultEnumMap, json['result']),
- resultDetails: const NestedJsonConverter().fromJson(json['result_details_json'] as String?),
- serviceAccount: json['service_account'] as String?,
- startedTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['started_ts'] as String?),
- status: $enumDecodeNullable(_$StatusEnumMap, json['status']),
- tags: (json['tags'] as List<dynamic>?)?.map((e) => e as String).toList(),
- updatedTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['updated_ts'] as String?),
- utcNowTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['utcnow_ts'] as String?),
- url: json['url'] as String?,
- );
-
-Map<String, dynamic> _$BuildToJson(Build instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('bucket', instance.bucket);
- writeNotNull('canary', instance.canary);
- writeNotNull('canary_preference', _$CanaryPreferenceEnumMap[instance.canaryPreference]);
- writeNotNull('cancelation_reason', _$CancelationReasonEnumMap[instance.cancelationReason]);
- writeNotNull('completed_ts', const MicrosecondsSinceEpochConverter().toJson(instance.completedTimestamp));
- writeNotNull('created_by', instance.createdBy);
- writeNotNull('created_ts', const MicrosecondsSinceEpochConverter().toJson(instance.createdTimestamp));
- writeNotNull('experimental', instance.experimental);
- writeNotNull('failure_reason', _$FailureReasonEnumMap[instance.failureReason]);
- writeNotNull('id', instance.id);
- writeNotNull('parameters_json', const NestedJsonConverter().toJson(instance.buildParameters));
- writeNotNull('project', instance.project);
- writeNotNull('result', _$ResultEnumMap[instance.result]);
- writeNotNull('result_details_json', const NestedJsonConverter().toJson(instance.resultDetails));
- writeNotNull('service_account', instance.serviceAccount);
- writeNotNull('started_ts', const MicrosecondsSinceEpochConverter().toJson(instance.startedTimestamp));
- writeNotNull('status', _$StatusEnumMap[instance.status]);
- writeNotNull('tags', instance.tags);
- writeNotNull('updated_ts', const MicrosecondsSinceEpochConverter().toJson(instance.updatedTimestamp));
- writeNotNull('url', instance.url);
- writeNotNull('utcnow_ts', const MicrosecondsSinceEpochConverter().toJson(instance.utcNowTimestamp));
- return val;
-}
-
-const _$CanaryPreferenceEnumMap = {
- CanaryPreference.auto: 'AUTO',
- CanaryPreference.canary: 'CANARY',
- CanaryPreference.prod: 'PROD',
-};
-
-const _$CancelationReasonEnumMap = {
- CancelationReason.canceledExplicitly: 'CANCELED_EXPLICITLY',
- CancelationReason.timeout: 'TIMEOUT',
-};
-
-const _$FailureReasonEnumMap = {
- FailureReason.buildbucketFailure: 'BUILDBUCKET_FAILURE',
- FailureReason.buildFailure: 'BUILD_FAILURE',
- FailureReason.infraFailure: 'INFRA_FAILURE',
- FailureReason.invalidBuildDefinition: 'INVALID_BUILD_DEFINITION',
-};
-
-const _$ResultEnumMap = {
- Result.canceled: 'CANCELED',
- Result.failure: 'FAILURE',
- Result.success: 'SUCCESS',
-};
-
-const _$StatusEnumMap = {
- Status.completed: 'COMPLETED',
- Status.scheduled: 'SCHEDULED',
- Status.started: 'STARTED',
-};
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.pb.dart b/app_dart/lib/src/model/proto/internal/build_status_response.pb.dart
deleted file mode 100644
index dc5a7eb..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.pb.dart
+++ /dev/null
@@ -1,80 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/build_status_response.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-
-import 'package:protobuf/protobuf.dart' as $pb;
-
-import 'build_status_response.pbenum.dart';
-
-export 'build_status_response.pbenum.dart';
-
-class BuildStatusResponse extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BuildStatusResponse',
- package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'cocoon'),
- createEmptyInstance: create)
- ..e<EnumBuildStatus>(
- 1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'buildStatus', $pb.PbFieldType.OE,
- defaultOrMaker: EnumBuildStatus.success, valueOf: EnumBuildStatus.valueOf, enumValues: EnumBuildStatus.values)
- ..pPS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'failingTasks')
- ..hasRequiredFields = false;
-
- BuildStatusResponse._() : super();
- factory BuildStatusResponse({
- EnumBuildStatus? buildStatus,
- $core.Iterable<$core.String>? failingTasks,
- }) {
- final _result = create();
- if (buildStatus != null) {
- _result.buildStatus = buildStatus;
- }
- if (failingTasks != null) {
- _result.failingTasks.addAll(failingTasks);
- }
- return _result;
- }
- factory BuildStatusResponse.fromBuffer($core.List<$core.int> i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory BuildStatusResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- BuildStatusResponse clone() => BuildStatusResponse()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- BuildStatusResponse copyWith(void Function(BuildStatusResponse) updates) =>
- super.copyWith((message) => updates(message as BuildStatusResponse))
- as BuildStatusResponse; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static BuildStatusResponse create() => BuildStatusResponse._();
- BuildStatusResponse createEmptyInstance() => create();
- static $pb.PbList<BuildStatusResponse> createRepeated() => $pb.PbList<BuildStatusResponse>();
- @$core.pragma('dart2js:noInline')
- static BuildStatusResponse getDefault() =>
- _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BuildStatusResponse>(create);
- static BuildStatusResponse? _defaultInstance;
-
- @$pb.TagNumber(1)
- EnumBuildStatus get buildStatus => $_getN(0);
- @$pb.TagNumber(1)
- set buildStatus(EnumBuildStatus v) {
- setField(1, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasBuildStatus() => $_has(0);
- @$pb.TagNumber(1)
- void clearBuildStatus() => clearField(1);
-
- @$pb.TagNumber(2)
- $core.List<$core.String> get failingTasks => $_getList(1);
-}
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.pbenum.dart b/app_dart/lib/src/model/proto/internal/build_status_response.pbenum.dart
deleted file mode 100644
index 7663ad3..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.pbenum.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/build_status_response.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-// ignore_for_file: UNDEFINED_SHOWN_NAME
-import 'dart:core' as $core;
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class EnumBuildStatus extends $pb.ProtobufEnum {
- static const EnumBuildStatus success =
- EnumBuildStatus._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'success');
- static const EnumBuildStatus failure =
- EnumBuildStatus._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'failure');
-
- static const $core.List<EnumBuildStatus> values = <EnumBuildStatus>[
- success,
- failure,
- ];
-
- static final $core.Map<$core.int, EnumBuildStatus> _byValue = $pb.ProtobufEnum.initByValue(values);
- static EnumBuildStatus? valueOf($core.int value) => _byValue[value];
-
- const EnumBuildStatus._($core.int v, $core.String n) : super(v, n);
-}
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.pbjson.dart b/app_dart/lib/src/model/proto/internal/build_status_response.pbjson.dart
deleted file mode 100644
index 768fc3a..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.pbjson.dart
+++ /dev/null
@@ -1,35 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/build_status_response.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use enumBuildStatusDescriptor instead')
-const EnumBuildStatus$json = const {
- '1': 'EnumBuildStatus',
- '2': const [
- const {'1': 'success', '2': 1},
- const {'1': 'failure', '2': 2},
- ],
-};
-
-/// Descriptor for `EnumBuildStatus`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List enumBuildStatusDescriptor =
- $convert.base64Decode('Cg9FbnVtQnVpbGRTdGF0dXMSCwoHc3VjY2VzcxABEgsKB2ZhaWx1cmUQAg==');
-@$core.Deprecated('Use buildStatusResponseDescriptor instead')
-const BuildStatusResponse$json = const {
- '1': 'BuildStatusResponse',
- '2': const [
- const {'1': 'build_status', '3': 1, '4': 1, '5': 14, '6': '.cocoon.EnumBuildStatus', '10': 'buildStatus'},
- const {'1': 'failing_tasks', '3': 2, '4': 3, '5': 9, '10': 'failingTasks'},
- ],
-};
-
-/// Descriptor for `BuildStatusResponse`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List buildStatusResponseDescriptor = $convert.base64Decode(
- 'ChNCdWlsZFN0YXR1c1Jlc3BvbnNlEjoKDGJ1aWxkX3N0YXR1cxgBIAEoDjIXLmNvY29vbi5FbnVtQnVpbGRTdGF0dXNSC2J1aWxkU3RhdHVzEiMKDWZhaWxpbmdfdGFza3MYAiADKAlSDGZhaWxpbmdUYXNrcw==');
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.pbserver.dart b/app_dart/lib/src/model/proto/internal/build_status_response.pbserver.dart
deleted file mode 100644
index 9115ba5..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/build_status_response.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-export 'build_status_response.pb.dart';
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.proto b/app_dart/lib/src/model/proto/internal/build_status_response.proto
deleted file mode 100644
index a562744..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.proto
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 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.
-
-syntax = "proto2";
-
-package cocoon;
-
-enum EnumBuildStatus {
- success = 1;
- failure = 2;
-}
-
-message BuildStatusResponse {
- optional EnumBuildStatus build_status = 1;
- repeated string failing_tasks = 2;
-}
\ No newline at end of file
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.pb.dart b/app_dart/lib/src/model/proto/internal/github_webhook.pb.dart
deleted file mode 100644
index 2f5fafe..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.pb.dart
+++ /dev/null
@@ -1,83 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/github_webhook.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class GithubWebhookMessage extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GithubWebhookMessage',
- package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'cocoon'),
- createEmptyInstance: create)
- ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'event')
- ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'payload')
- ..hasRequiredFields = false;
-
- GithubWebhookMessage._() : super();
- factory GithubWebhookMessage({
- $core.String? event,
- $core.String? payload,
- }) {
- final _result = create();
- if (event != null) {
- _result.event = event;
- }
- if (payload != null) {
- _result.payload = payload;
- }
- return _result;
- }
- factory GithubWebhookMessage.fromBuffer($core.List<$core.int> i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory GithubWebhookMessage.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- GithubWebhookMessage clone() => GithubWebhookMessage()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- GithubWebhookMessage copyWith(void Function(GithubWebhookMessage) updates) =>
- super.copyWith((message) => updates(message as GithubWebhookMessage))
- as GithubWebhookMessage; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static GithubWebhookMessage create() => GithubWebhookMessage._();
- GithubWebhookMessage createEmptyInstance() => create();
- static $pb.PbList<GithubWebhookMessage> createRepeated() => $pb.PbList<GithubWebhookMessage>();
- @$core.pragma('dart2js:noInline')
- static GithubWebhookMessage getDefault() =>
- _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GithubWebhookMessage>(create);
- static GithubWebhookMessage? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.String get event => $_getSZ(0);
- @$pb.TagNumber(1)
- set event($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasEvent() => $_has(0);
- @$pb.TagNumber(1)
- void clearEvent() => clearField(1);
-
- @$pb.TagNumber(2)
- $core.String get payload => $_getSZ(1);
- @$pb.TagNumber(2)
- set payload($core.String v) {
- $_setString(1, v);
- }
-
- @$pb.TagNumber(2)
- $core.bool hasPayload() => $_has(1);
- @$pb.TagNumber(2)
- void clearPayload() => clearField(2);
-}
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.pbenum.dart b/app_dart/lib/src/model/proto/internal/github_webhook.pbenum.dart
deleted file mode 100644
index ef33917..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.pbenum.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/github_webhook.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.pbjson.dart b/app_dart/lib/src/model/proto/internal/github_webhook.pbjson.dart
deleted file mode 100644
index 718d1b9..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.pbjson.dart
+++ /dev/null
@@ -1,23 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/github_webhook.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use githubWebhookMessageDescriptor instead')
-const GithubWebhookMessage$json = const {
- '1': 'GithubWebhookMessage',
- '2': const [
- const {'1': 'event', '3': 1, '4': 1, '5': 9, '10': 'event'},
- const {'1': 'payload', '3': 2, '4': 1, '5': 9, '10': 'payload'},
- ],
-};
-
-/// Descriptor for `GithubWebhookMessage`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List githubWebhookMessageDescriptor = $convert
- .base64Decode('ChRHaXRodWJXZWJob29rTWVzc2FnZRIUCgVldmVudBgBIAEoCVIFZXZlbnQSGAoHcGF5bG9hZBgCIAEoCVIHcGF5bG9hZA==');
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.pbserver.dart b/app_dart/lib/src/model/proto/internal/github_webhook.pbserver.dart
deleted file mode 100644
index 8e8dfb9..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/github_webhook.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-export 'github_webhook.pb.dart';
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.proto b/app_dart/lib/src/model/proto/internal/github_webhook.proto
deleted file mode 100644
index 5c28bef..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.proto
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2019 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.
-
-syntax = "proto2";
-
-package cocoon;
-
-// For full spec, see:
-// * https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads
-message GithubWebhookMessage {
- // X-GitHub-Event HTTP Header indicating the webhook action.
- optional string event = 1;
- // JSON encoded webhook payload from GitHub.
- optional string payload = 2;
-}
\ No newline at end of file
diff --git a/app_dart/lib/src/model/proto/internal/key.pb.dart b/app_dart/lib/src/model/proto/internal/key.pb.dart
deleted file mode 100644
index 76d26db..0000000
--- a/app_dart/lib/src/model/proto/internal/key.pb.dart
+++ /dev/null
@@ -1,196 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/key.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-
-import 'package:fixnum/fixnum.dart' as $fixnum;
-import 'package:protobuf/protobuf.dart' as $pb;
-
-enum Key_Id { uid, name, notSet }
-
-class Key extends $pb.GeneratedMessage {
- static const $core.Map<$core.int, Key_Id> _Key_IdByTag = {2: Key_Id.uid, 3: Key_Id.name, 0: Key_Id.notSet};
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Key',
- package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'cocoon'),
- createEmptyInstance: create)
- ..oo(0, [2, 3])
- ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'type')
- ..aInt64(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'uid')
- ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
- ..aOM<Key>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'child', subBuilder: Key.create)
- ..hasRequiredFields = false;
-
- Key._() : super();
- factory Key({
- $core.String? type,
- $fixnum.Int64? uid,
- $core.String? name,
- Key? child,
- }) {
- final _result = create();
- if (type != null) {
- _result.type = type;
- }
- if (uid != null) {
- _result.uid = uid;
- }
- if (name != null) {
- _result.name = name;
- }
- if (child != null) {
- _result.child = child;
- }
- return _result;
- }
- factory Key.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Key.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Key clone() => Key()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Key copyWith(void Function(Key) updates) =>
- super.copyWith((message) => updates(message as Key)) as Key; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Key create() => Key._();
- Key createEmptyInstance() => create();
- static $pb.PbList<Key> createRepeated() => $pb.PbList<Key>();
- @$core.pragma('dart2js:noInline')
- static Key getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Key>(create);
- static Key? _defaultInstance;
-
- Key_Id whichId() => _Key_IdByTag[$_whichOneof(0)]!;
- void clearId() => clearField($_whichOneof(0));
-
- @$pb.TagNumber(1)
- $core.String get type => $_getSZ(0);
- @$pb.TagNumber(1)
- set type($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasType() => $_has(0);
- @$pb.TagNumber(1)
- void clearType() => clearField(1);
-
- @$pb.TagNumber(2)
- $fixnum.Int64 get uid => $_getI64(1);
- @$pb.TagNumber(2)
- set uid($fixnum.Int64 v) {
- $_setInt64(1, v);
- }
-
- @$pb.TagNumber(2)
- $core.bool hasUid() => $_has(1);
- @$pb.TagNumber(2)
- void clearUid() => clearField(2);
-
- @$pb.TagNumber(3)
- $core.String get name => $_getSZ(2);
- @$pb.TagNumber(3)
- set name($core.String v) {
- $_setString(2, v);
- }
-
- @$pb.TagNumber(3)
- $core.bool hasName() => $_has(2);
- @$pb.TagNumber(3)
- void clearName() => clearField(3);
-
- @$pb.TagNumber(4)
- Key get child => $_getN(3);
- @$pb.TagNumber(4)
- set child(Key v) {
- setField(4, v);
- }
-
- @$pb.TagNumber(4)
- $core.bool hasChild() => $_has(3);
- @$pb.TagNumber(4)
- void clearChild() => clearField(4);
- @$pb.TagNumber(4)
- Key ensureChild() => $_ensure(3);
-}
-
-class RootKey extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RootKey',
- package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'cocoon'),
- createEmptyInstance: create)
- ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'namespace')
- ..aOM<Key>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'child', subBuilder: Key.create)
- ..hasRequiredFields = false;
-
- RootKey._() : super();
- factory RootKey({
- $core.String? namespace,
- Key? child,
- }) {
- final _result = create();
- if (namespace != null) {
- _result.namespace = namespace;
- }
- if (child != null) {
- _result.child = child;
- }
- return _result;
- }
- factory RootKey.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory RootKey.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- RootKey clone() => RootKey()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- RootKey copyWith(void Function(RootKey) updates) =>
- super.copyWith((message) => updates(message as RootKey)) as RootKey; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static RootKey create() => RootKey._();
- RootKey createEmptyInstance() => create();
- static $pb.PbList<RootKey> createRepeated() => $pb.PbList<RootKey>();
- @$core.pragma('dart2js:noInline')
- static RootKey getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RootKey>(create);
- static RootKey? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.String get namespace => $_getSZ(0);
- @$pb.TagNumber(1)
- set namespace($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasNamespace() => $_has(0);
- @$pb.TagNumber(1)
- void clearNamespace() => clearField(1);
-
- @$pb.TagNumber(2)
- Key get child => $_getN(1);
- @$pb.TagNumber(2)
- set child(Key v) {
- setField(2, v);
- }
-
- @$pb.TagNumber(2)
- $core.bool hasChild() => $_has(1);
- @$pb.TagNumber(2)
- void clearChild() => clearField(2);
- @$pb.TagNumber(2)
- Key ensureChild() => $_ensure(1);
-}
diff --git a/app_dart/lib/src/model/proto/internal/key.pbenum.dart b/app_dart/lib/src/model/proto/internal/key.pbenum.dart
deleted file mode 100644
index 1c5e28e..0000000
--- a/app_dart/lib/src/model/proto/internal/key.pbenum.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/key.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
diff --git a/app_dart/lib/src/model/proto/internal/key.pbjson.dart b/app_dart/lib/src/model/proto/internal/key.pbjson.dart
deleted file mode 100644
index 315a58e..0000000
--- a/app_dart/lib/src/model/proto/internal/key.pbjson.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/key.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use keyDescriptor instead')
-const Key$json = const {
- '1': 'Key',
- '2': const [
- const {'1': 'type', '3': 1, '4': 1, '5': 9, '10': 'type'},
- const {'1': 'uid', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'uid'},
- const {'1': 'name', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'name'},
- const {'1': 'child', '3': 4, '4': 1, '5': 11, '6': '.cocoon.Key', '10': 'child'},
- ],
- '8': const [
- const {'1': 'id'},
- ],
-};
-
-/// Descriptor for `Key`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List keyDescriptor = $convert.base64Decode(
- 'CgNLZXkSEgoEdHlwZRgBIAEoCVIEdHlwZRISCgN1aWQYAiABKANIAFIDdWlkEhQKBG5hbWUYAyABKAlIAFIEbmFtZRIhCgVjaGlsZBgEIAEoCzILLmNvY29vbi5LZXlSBWNoaWxkQgQKAmlk');
-@$core.Deprecated('Use rootKeyDescriptor instead')
-const RootKey$json = const {
- '1': 'RootKey',
- '2': const [
- const {'1': 'namespace', '3': 1, '4': 1, '5': 9, '10': 'namespace'},
- const {'1': 'child', '3': 2, '4': 1, '5': 11, '6': '.cocoon.Key', '10': 'child'},
- ],
-};
-
-/// Descriptor for `RootKey`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List rootKeyDescriptor = $convert.base64Decode(
- 'CgdSb290S2V5EhwKCW5hbWVzcGFjZRgBIAEoCVIJbmFtZXNwYWNlEiEKBWNoaWxkGAIgASgLMgsuY29jb29uLktleVIFY2hpbGQ=');
diff --git a/app_dart/lib/src/model/proto/internal/key.pbserver.dart b/app_dart/lib/src/model/proto/internal/key.pbserver.dart
deleted file mode 100644
index 92721a9..0000000
--- a/app_dart/lib/src/model/proto/internal/key.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/key.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-export 'key.pb.dart';
diff --git a/app_dart/lib/src/model/proto/internal/key.proto b/app_dart/lib/src/model/proto/internal/key.proto
deleted file mode 100644
index 262418f..0000000
--- a/app_dart/lib/src/model/proto/internal/key.proto
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2019 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.
-
-syntax = "proto2";
-
-package cocoon;
-
-message Key {
- optional string type = 1;
-
- oneof id {
- int64 uid = 2;
- string name = 3;
- }
-
- optional Key child = 4;
-}
-
-message RootKey {
- optional string namespace = 1;
- optional Key child = 2;
-}
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pb.dart b/app_dart/lib/src/model/proto/internal/scheduler.pb.dart
deleted file mode 100644
index f79eede..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.pb.dart
+++ /dev/null
@@ -1,437 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/scheduler.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-
-import 'package:protobuf/protobuf.dart' as $pb;
-
-import 'scheduler.pbenum.dart';
-
-export 'scheduler.pbenum.dart';
-
-class SchedulerConfig_PlatformProperties extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SchedulerConfig.PlatformProperties',
- package:
- const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'scheduler'),
- createEmptyInstance: create)
- ..m<$core.String, $core.String>(
- 1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'properties',
- entryClassName: 'SchedulerConfig.PlatformProperties.PropertiesEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..m<$core.String, $core.String>(
- 2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dimensions',
- entryClassName: 'SchedulerConfig.PlatformProperties.DimensionsEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..hasRequiredFields = false;
-
- SchedulerConfig_PlatformProperties._() : super();
- factory SchedulerConfig_PlatformProperties({
- $core.Map<$core.String, $core.String>? properties,
- $core.Map<$core.String, $core.String>? dimensions,
- }) {
- final _result = create();
- if (properties != null) {
- _result.properties.addAll(properties);
- }
- if (dimensions != null) {
- _result.dimensions.addAll(dimensions);
- }
- return _result;
- }
- factory SchedulerConfig_PlatformProperties.fromBuffer($core.List<$core.int> i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory SchedulerConfig_PlatformProperties.fromJson($core.String i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- SchedulerConfig_PlatformProperties clone() => SchedulerConfig_PlatformProperties()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- SchedulerConfig_PlatformProperties copyWith(void Function(SchedulerConfig_PlatformProperties) updates) =>
- super.copyWith((message) => updates(message as SchedulerConfig_PlatformProperties))
- as SchedulerConfig_PlatformProperties; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static SchedulerConfig_PlatformProperties create() => SchedulerConfig_PlatformProperties._();
- SchedulerConfig_PlatformProperties createEmptyInstance() => create();
- static $pb.PbList<SchedulerConfig_PlatformProperties> createRepeated() =>
- $pb.PbList<SchedulerConfig_PlatformProperties>();
- @$core.pragma('dart2js:noInline')
- static SchedulerConfig_PlatformProperties getDefault() =>
- _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SchedulerConfig_PlatformProperties>(create);
- static SchedulerConfig_PlatformProperties? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.Map<$core.String, $core.String> get properties => $_getMap(0);
-
- @$pb.TagNumber(2)
- $core.Map<$core.String, $core.String> get dimensions => $_getMap(1);
-}
-
-class SchedulerConfig extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SchedulerConfig',
- package:
- const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'scheduler'),
- createEmptyInstance: create)
- ..pc<Target>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'targets', $pb.PbFieldType.PM,
- subBuilder: Target.create)
- ..pPS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enabledBranches')
- ..m<$core.String, SchedulerConfig_PlatformProperties>(
- 3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'platformProperties',
- entryClassName: 'SchedulerConfig.PlatformPropertiesEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OM,
- valueCreator: SchedulerConfig_PlatformProperties.create,
- packageName: const $pb.PackageName('scheduler'))
- ..hasRequiredFields = false;
-
- SchedulerConfig._() : super();
- factory SchedulerConfig({
- $core.Iterable<Target>? targets,
- $core.Iterable<$core.String>? enabledBranches,
- $core.Map<$core.String, SchedulerConfig_PlatformProperties>? platformProperties,
- }) {
- final _result = create();
- if (targets != null) {
- _result.targets.addAll(targets);
- }
- if (enabledBranches != null) {
- _result.enabledBranches.addAll(enabledBranches);
- }
- if (platformProperties != null) {
- _result.platformProperties.addAll(platformProperties);
- }
- return _result;
- }
- factory SchedulerConfig.fromBuffer($core.List<$core.int> i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory SchedulerConfig.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- SchedulerConfig clone() => SchedulerConfig()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- SchedulerConfig copyWith(void Function(SchedulerConfig) updates) =>
- super.copyWith((message) => updates(message as SchedulerConfig))
- as SchedulerConfig; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static SchedulerConfig create() => SchedulerConfig._();
- SchedulerConfig createEmptyInstance() => create();
- static $pb.PbList<SchedulerConfig> createRepeated() => $pb.PbList<SchedulerConfig>();
- @$core.pragma('dart2js:noInline')
- static SchedulerConfig getDefault() =>
- _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SchedulerConfig>(create);
- static SchedulerConfig? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.List<Target> get targets => $_getList(0);
-
- @$pb.TagNumber(2)
- $core.List<$core.String> get enabledBranches => $_getList(1);
-
- @$pb.TagNumber(3)
- $core.Map<$core.String, SchedulerConfig_PlatformProperties> get platformProperties => $_getMap(2);
-}
-
-class Target extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Target',
- package:
- const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'scheduler'),
- createEmptyInstance: create)
- ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
- ..pPS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dependencies')
- ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'bringup')
- ..a<$core.int>(
- 4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'timeout', $pb.PbFieldType.O3,
- defaultOrMaker: 30)
- ..a<$core.String>(
- 5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'testbed', $pb.PbFieldType.OS,
- defaultOrMaker: 'linux-vm')
- ..m<$core.String, $core.String>(
- 6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'properties',
- entryClassName: 'Target.PropertiesEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'builder')
- ..e<SchedulerSystem>(
- 8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'scheduler', $pb.PbFieldType.OE,
- defaultOrMaker: SchedulerSystem.cocoon, valueOf: SchedulerSystem.valueOf, enumValues: SchedulerSystem.values)
- ..a<$core.bool>(
- 9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'presubmit', $pb.PbFieldType.OB,
- defaultOrMaker: true)
- ..a<$core.bool>(
- 10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'postsubmit', $pb.PbFieldType.OB,
- defaultOrMaker: true)
- ..pPS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'runIf')
- ..pPS(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enabledBranches')
- ..aOS(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'recipe')
- ..m<$core.String, $core.String>(
- 15, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'postsubmitProperties',
- entryClassName: 'Target.PostsubmitPropertiesEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..m<$core.String, $core.String>(
- 16, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dimensions',
- entryClassName: 'Target.DimensionsEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..pPS(17, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'droneDimensions')
- ..pPS(18, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'runIfNot')
- ..hasRequiredFields = false;
-
- Target._() : super();
- factory Target({
- $core.String? name,
- $core.Iterable<$core.String>? dependencies,
- $core.bool? bringup,
- $core.int? timeout,
- $core.String? testbed,
- $core.Map<$core.String, $core.String>? properties,
- @$core.Deprecated('This field is deprecated.') $core.String? builder,
- SchedulerSystem? scheduler,
- $core.bool? presubmit,
- $core.bool? postsubmit,
- $core.Iterable<$core.String>? runIf,
- $core.Iterable<$core.String>? enabledBranches,
- $core.String? recipe,
- $core.Map<$core.String, $core.String>? postsubmitProperties,
- $core.Map<$core.String, $core.String>? dimensions,
- $core.Iterable<$core.String>? droneDimensions,
- $core.Iterable<$core.String>? runIfNot,
- }) {
- final _result = create();
- if (name != null) {
- _result.name = name;
- }
- if (dependencies != null) {
- _result.dependencies.addAll(dependencies);
- }
- if (bringup != null) {
- _result.bringup = bringup;
- }
- if (timeout != null) {
- _result.timeout = timeout;
- }
- if (testbed != null) {
- _result.testbed = testbed;
- }
- if (properties != null) {
- _result.properties.addAll(properties);
- }
- if (builder != null) {
- // ignore: deprecated_member_use_from_same_package
- _result.builder = builder;
- }
- if (scheduler != null) {
- _result.scheduler = scheduler;
- }
- if (presubmit != null) {
- _result.presubmit = presubmit;
- }
- if (postsubmit != null) {
- _result.postsubmit = postsubmit;
- }
- if (runIf != null) {
- _result.runIf.addAll(runIf);
- }
- if (enabledBranches != null) {
- _result.enabledBranches.addAll(enabledBranches);
- }
- if (recipe != null) {
- _result.recipe = recipe;
- }
- if (postsubmitProperties != null) {
- _result.postsubmitProperties.addAll(postsubmitProperties);
- }
- if (dimensions != null) {
- _result.dimensions.addAll(dimensions);
- }
- if (droneDimensions != null) {
- _result.droneDimensions.addAll(droneDimensions);
- }
- if (runIfNot != null) {
- _result.runIfNot.addAll(runIfNot);
- }
- return _result;
- }
- factory Target.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Target.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Target clone() => Target()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Target copyWith(void Function(Target) updates) =>
- super.copyWith((message) => updates(message as Target)) as Target; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Target create() => Target._();
- Target createEmptyInstance() => create();
- static $pb.PbList<Target> createRepeated() => $pb.PbList<Target>();
- @$core.pragma('dart2js:noInline')
- static Target getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Target>(create);
- static Target? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.String get name => $_getSZ(0);
- @$pb.TagNumber(1)
- set name($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasName() => $_has(0);
- @$pb.TagNumber(1)
- void clearName() => clearField(1);
-
- @$pb.TagNumber(2)
- $core.List<$core.String> get dependencies => $_getList(1);
-
- @$pb.TagNumber(3)
- $core.bool get bringup => $_getBF(2);
- @$pb.TagNumber(3)
- set bringup($core.bool v) {
- $_setBool(2, v);
- }
-
- @$pb.TagNumber(3)
- $core.bool hasBringup() => $_has(2);
- @$pb.TagNumber(3)
- void clearBringup() => clearField(3);
-
- @$pb.TagNumber(4)
- $core.int get timeout => $_getI(3, 30);
- @$pb.TagNumber(4)
- set timeout($core.int v) {
- $_setSignedInt32(3, v);
- }
-
- @$pb.TagNumber(4)
- $core.bool hasTimeout() => $_has(3);
- @$pb.TagNumber(4)
- void clearTimeout() => clearField(4);
-
- @$pb.TagNumber(5)
- $core.String get testbed => $_getS(4, 'linux-vm');
- @$pb.TagNumber(5)
- set testbed($core.String v) {
- $_setString(4, v);
- }
-
- @$pb.TagNumber(5)
- $core.bool hasTestbed() => $_has(4);
- @$pb.TagNumber(5)
- void clearTestbed() => clearField(5);
-
- @$pb.TagNumber(6)
- $core.Map<$core.String, $core.String> get properties => $_getMap(5);
-
- @$core.Deprecated('This field is deprecated.')
- @$pb.TagNumber(7)
- $core.String get builder => $_getSZ(6);
- @$core.Deprecated('This field is deprecated.')
- @$pb.TagNumber(7)
- set builder($core.String v) {
- $_setString(6, v);
- }
-
- @$core.Deprecated('This field is deprecated.')
- @$pb.TagNumber(7)
- $core.bool hasBuilder() => $_has(6);
- @$core.Deprecated('This field is deprecated.')
- @$pb.TagNumber(7)
- void clearBuilder() => clearField(7);
-
- @$pb.TagNumber(8)
- SchedulerSystem get scheduler => $_getN(7);
- @$pb.TagNumber(8)
- set scheduler(SchedulerSystem v) {
- setField(8, v);
- }
-
- @$pb.TagNumber(8)
- $core.bool hasScheduler() => $_has(7);
- @$pb.TagNumber(8)
- void clearScheduler() => clearField(8);
-
- @$pb.TagNumber(9)
- $core.bool get presubmit => $_getB(8, true);
- @$pb.TagNumber(9)
- set presubmit($core.bool v) {
- $_setBool(8, v);
- }
-
- @$pb.TagNumber(9)
- $core.bool hasPresubmit() => $_has(8);
- @$pb.TagNumber(9)
- void clearPresubmit() => clearField(9);
-
- @$pb.TagNumber(10)
- $core.bool get postsubmit => $_getB(9, true);
- @$pb.TagNumber(10)
- set postsubmit($core.bool v) {
- $_setBool(9, v);
- }
-
- @$pb.TagNumber(10)
- $core.bool hasPostsubmit() => $_has(9);
- @$pb.TagNumber(10)
- void clearPostsubmit() => clearField(10);
-
- @$pb.TagNumber(11)
- $core.List<$core.String> get runIf => $_getList(10);
-
- @$pb.TagNumber(12)
- $core.List<$core.String> get enabledBranches => $_getList(11);
-
- @$pb.TagNumber(13)
- $core.String get recipe => $_getSZ(12);
- @$pb.TagNumber(13)
- set recipe($core.String v) {
- $_setString(12, v);
- }
-
- @$pb.TagNumber(13)
- $core.bool hasRecipe() => $_has(12);
- @$pb.TagNumber(13)
- void clearRecipe() => clearField(13);
-
- @$pb.TagNumber(15)
- $core.Map<$core.String, $core.String> get postsubmitProperties => $_getMap(13);
-
- @$pb.TagNumber(16)
- $core.Map<$core.String, $core.String> get dimensions => $_getMap(14);
-
- @$pb.TagNumber(17)
- $core.List<$core.String> get droneDimensions => $_getList(15);
-
- @$pb.TagNumber(18)
- $core.List<$core.String> get runIfNot => $_getList(16);
-}
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart b/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart
deleted file mode 100644
index 8e09bd7..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart
+++ /dev/null
@@ -1,33 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/scheduler.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-// ignore_for_file: UNDEFINED_SHOWN_NAME
-import 'dart:core' as $core;
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class SchedulerSystem extends $pb.ProtobufEnum {
- static const SchedulerSystem cocoon =
- SchedulerSystem._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'cocoon');
- static const SchedulerSystem luci =
- SchedulerSystem._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'luci');
- static const SchedulerSystem google_internal =
- SchedulerSystem._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'google_internal');
- static const SchedulerSystem release =
- SchedulerSystem._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'release');
-
- static const $core.List<SchedulerSystem> values = <SchedulerSystem>[
- cocoon,
- luci,
- google_internal,
- release,
- ];
-
- static final $core.Map<$core.int, SchedulerSystem> _byValue = $pb.ProtobufEnum.initByValue(values);
- static SchedulerSystem? valueOf($core.int value) => _byValue[value];
-
- const SchedulerSystem._($core.int v, $core.String n) : super(v, n);
-}
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart b/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart
deleted file mode 100644
index 5e04860..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart
+++ /dev/null
@@ -1,186 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/scheduler.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use schedulerSystemDescriptor instead')
-const SchedulerSystem$json = const {
- '1': 'SchedulerSystem',
- '2': const [
- const {'1': 'cocoon', '2': 1},
- const {'1': 'luci', '2': 2},
- const {'1': 'google_internal', '2': 3},
- const {'1': 'release', '2': 4},
- ],
-};
-
-/// Descriptor for `SchedulerSystem`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List schedulerSystemDescriptor = $convert.base64Decode(
- 'Cg9TY2hlZHVsZXJTeXN0ZW0SCgoGY29jb29uEAESCAoEbHVjaRACEhMKD2dvb2dsZV9pbnRlcm5hbBADEgsKB3JlbGVhc2UQBA==');
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig$json = const {
- '1': 'SchedulerConfig',
- '2': const [
- const {'1': 'targets', '3': 1, '4': 3, '5': 11, '6': '.scheduler.Target', '10': 'targets'},
- const {'1': 'enabled_branches', '3': 2, '4': 3, '5': 9, '10': 'enabledBranches'},
- const {
- '1': 'platform_properties',
- '3': 3,
- '4': 3,
- '5': 11,
- '6': '.scheduler.SchedulerConfig.PlatformPropertiesEntry',
- '10': 'platformProperties'
- },
- ],
- '3': const [SchedulerConfig_PlatformPropertiesEntry$json, SchedulerConfig_PlatformProperties$json],
-};
-
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig_PlatformPropertiesEntry$json = const {
- '1': 'PlatformPropertiesEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 11, '6': '.scheduler.SchedulerConfig.PlatformProperties', '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig_PlatformProperties$json = const {
- '1': 'PlatformProperties',
- '2': const [
- const {
- '1': 'properties',
- '3': 1,
- '4': 3,
- '5': 11,
- '6': '.scheduler.SchedulerConfig.PlatformProperties.PropertiesEntry',
- '10': 'properties'
- },
- const {
- '1': 'dimensions',
- '3': 2,
- '4': 3,
- '5': 11,
- '6': '.scheduler.SchedulerConfig.PlatformProperties.DimensionsEntry',
- '10': 'dimensions'
- },
- ],
- '3': const [
- SchedulerConfig_PlatformProperties_PropertiesEntry$json,
- SchedulerConfig_PlatformProperties_DimensionsEntry$json
- ],
-};
-
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig_PlatformProperties_PropertiesEntry$json = const {
- '1': 'PropertiesEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig_PlatformProperties_DimensionsEntry$json = const {
- '1': 'DimensionsEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-/// Descriptor for `SchedulerConfig`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List schedulerConfigDescriptor = $convert.base64Decode(
- 'Cg9TY2hlZHVsZXJDb25maWcSKwoHdGFyZ2V0cxgBIAMoCzIRLnNjaGVkdWxlci5UYXJnZXRSB3RhcmdldHMSKQoQZW5hYmxlZF9icmFuY2hlcxgCIAMoCVIPZW5hYmxlZEJyYW5jaGVzEmMKE3BsYXRmb3JtX3Byb3BlcnRpZXMYAyADKAsyMi5zY2hlZHVsZXIuU2NoZWR1bGVyQ29uZmlnLlBsYXRmb3JtUHJvcGVydGllc0VudHJ5UhJwbGF0Zm9ybVByb3BlcnRpZXMadAoXUGxhdGZvcm1Qcm9wZXJ0aWVzRW50cnkSEAoDa2V5GAEgASgJUgNrZXkSQwoFdmFsdWUYAiABKAsyLS5zY2hlZHVsZXIuU2NoZWR1bGVyQ29uZmlnLlBsYXRmb3JtUHJvcGVydGllc1IFdmFsdWU6AjgBGtACChJQbGF0Zm9ybVByb3BlcnRpZXMSXQoKcHJvcGVydGllcxgBIAMoCzI9LnNjaGVkdWxlci5TY2hlZHVsZXJDb25maWcuUGxhdGZvcm1Qcm9wZXJ0aWVzLlByb3BlcnRpZXNFbnRyeVIKcHJvcGVydGllcxJdCgpkaW1lbnNpb25zGAIgAygLMj0uc2NoZWR1bGVyLlNjaGVkdWxlckNvbmZpZy5QbGF0Zm9ybVByb3BlcnRpZXMuRGltZW5zaW9uc0VudHJ5UgpkaW1lbnNpb25zGj0KD1Byb3BlcnRpZXNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBGj0KD0RpbWVuc2lvbnNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgB');
-@$core.Deprecated('Use targetDescriptor instead')
-const Target$json = const {
- '1': 'Target',
- '2': const [
- const {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'},
- const {'1': 'dependencies', '3': 2, '4': 3, '5': 9, '10': 'dependencies'},
- const {'1': 'bringup', '3': 3, '4': 1, '5': 8, '7': 'false', '10': 'bringup'},
- const {'1': 'timeout', '3': 4, '4': 1, '5': 5, '7': '30', '10': 'timeout'},
- const {'1': 'testbed', '3': 5, '4': 1, '5': 9, '7': 'linux-vm', '10': 'testbed'},
- const {'1': 'properties', '3': 6, '4': 3, '5': 11, '6': '.scheduler.Target.PropertiesEntry', '10': 'properties'},
- const {
- '1': 'builder',
- '3': 7,
- '4': 1,
- '5': 9,
- '8': const {'3': true},
- '10': 'builder',
- },
- const {
- '1': 'scheduler',
- '3': 8,
- '4': 1,
- '5': 14,
- '6': '.scheduler.SchedulerSystem',
- '7': 'cocoon',
- '10': 'scheduler'
- },
- const {'1': 'presubmit', '3': 9, '4': 1, '5': 8, '7': 'true', '10': 'presubmit'},
- const {'1': 'postsubmit', '3': 10, '4': 1, '5': 8, '7': 'true', '10': 'postsubmit'},
- const {'1': 'run_if', '3': 11, '4': 3, '5': 9, '10': 'runIf'},
- const {'1': 'enabled_branches', '3': 12, '4': 3, '5': 9, '10': 'enabledBranches'},
- const {'1': 'recipe', '3': 13, '4': 1, '5': 9, '10': 'recipe'},
- const {
- '1': 'postsubmit_properties',
- '3': 15,
- '4': 3,
- '5': 11,
- '6': '.scheduler.Target.PostsubmitPropertiesEntry',
- '10': 'postsubmitProperties'
- },
- const {'1': 'dimensions', '3': 16, '4': 3, '5': 11, '6': '.scheduler.Target.DimensionsEntry', '10': 'dimensions'},
- const {'1': 'drone_dimensions', '3': 17, '4': 3, '5': 9, '10': 'droneDimensions'},
- const {'1': 'run_if_not', '3': 18, '4': 3, '5': 9, '10': 'runIfNot'},
- ],
- '3': const [Target_PropertiesEntry$json, Target_PostsubmitPropertiesEntry$json, Target_DimensionsEntry$json],
- '9': const [
- const {'1': 14, '2': 15},
- ],
-};
-
-@$core.Deprecated('Use targetDescriptor instead')
-const Target_PropertiesEntry$json = const {
- '1': 'PropertiesEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-@$core.Deprecated('Use targetDescriptor instead')
-const Target_PostsubmitPropertiesEntry$json = const {
- '1': 'PostsubmitPropertiesEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-@$core.Deprecated('Use targetDescriptor instead')
-const Target_DimensionsEntry$json = const {
- '1': 'DimensionsEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-/// Descriptor for `Target`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List targetDescriptor = $convert.base64Decode(
- 'CgZUYXJnZXQSEgoEbmFtZRgBIAEoCVIEbmFtZRIiCgxkZXBlbmRlbmNpZXMYAiADKAlSDGRlcGVuZGVuY2llcxIfCgdicmluZ3VwGAMgASgIOgVmYWxzZVIHYnJpbmd1cBIcCgd0aW1lb3V0GAQgASgFOgIzMFIHdGltZW91dBIiCgd0ZXN0YmVkGAUgASgJOghsaW51eC12bVIHdGVzdGJlZBJBCgpwcm9wZXJ0aWVzGAYgAygLMiEuc2NoZWR1bGVyLlRhcmdldC5Qcm9wZXJ0aWVzRW50cnlSCnByb3BlcnRpZXMSHAoHYnVpbGRlchgHIAEoCUICGAFSB2J1aWxkZXISQAoJc2NoZWR1bGVyGAggASgOMhouc2NoZWR1bGVyLlNjaGVkdWxlclN5c3RlbToGY29jb29uUglzY2hlZHVsZXISIgoJcHJlc3VibWl0GAkgASgIOgR0cnVlUglwcmVzdWJtaXQSJAoKcG9zdHN1Ym1pdBgKIAEoCDoEdHJ1ZVIKcG9zdHN1Ym1pdBIVCgZydW5faWYYCyADKAlSBXJ1bklmEikKEGVuYWJsZWRfYnJhbmNoZXMYDCADKAlSD2VuYWJsZWRCcmFuY2hlcxIWCgZyZWNpcGUYDSABKAlSBnJlY2lwZRJgChVwb3N0c3VibWl0X3Byb3BlcnRpZXMYDyADKAsyKy5zY2hlZHVsZXIuVGFyZ2V0LlBvc3RzdWJtaXRQcm9wZXJ0aWVzRW50cnlSFHBvc3RzdWJtaXRQcm9wZXJ0aWVzEkEKCmRpbWVuc2lvbnMYECADKAsyIS5zY2hlZHVsZXIuVGFyZ2V0LkRpbWVuc2lvbnNFbnRyeVIKZGltZW5zaW9ucxIpChBkcm9uZV9kaW1lbnNpb25zGBEgAygJUg9kcm9uZURpbWVuc2lvbnMSHAoKcnVuX2lmX25vdBgSIAMoCVIIcnVuSWZOb3QaPQoPUHJvcGVydGllc0VudHJ5EhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZToCOAEaRwoZUG9zdHN1Ym1pdFByb3BlcnRpZXNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBGj0KD0RpbWVuc2lvbnNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBSgQIDhAP');
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart b/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart
deleted file mode 100644
index a16fa83..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/scheduler.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-export 'scheduler.pb.dart';
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.proto b/app_dart/lib/src/model/proto/internal/scheduler.proto
deleted file mode 100644
index a0df483..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.proto
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 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.
-
-syntax = "proto2";
-
-package scheduler;
-
-// Model of .ci.yaml.
-// Next ID: 4
-message SchedulerConfig {
- // Targets to run from this config.
- repeated Target targets = 1;
- // Git branches to run these targets against.
- repeated string enabled_branches = 2;
- // Universal platform args passed to LUCI builders.
- // Keys are the platforms and values are the PlatformProperties (properties, dimensions etc.).
- map<string, PlatformProperties> platform_properties = 3;
- // Next ID: 3
- message PlatformProperties {
- // Generic key, value pairs to set platform-wide properties
- map<string, string> properties = 1;
- // Generic key, value pairs to set platform-wide dimensions
- // Doc for dimension and properties: https://chromium.googlesource.com/infra/luci/luci-py/+/HEAD/appengine/swarming/doc/User-Guide.md
- map<string, string> dimensions = 2;
- }
-}
-
-// A unit of work for infrastructure to run.
-// Next ID: 17
-message Target {
- // Unique, human readable identifier.
- optional string name = 1;
- // Names of other targets required to succeed before triggering this target.
- repeated string dependencies = 2;
- // Whether this target is stable and can be used to gate commits.
- // Defaults to false which blocks builds and does not run in presubmit.
- optional bool bringup = 3 [default = false];
- // Number of minutes this target is allowed to run before being marked as failed.
- optional int32 timeout = 4 [default = 30];
- // Name of the testbed this target will run on.
- // Defaults to a linux vm.
- optional string testbed = 5 [default = 'linux-vm'];
- // Properties to configure infrastructure tooling.
- map<string, string> properties = 6;
- // Name of the LUCI builder to trigger.
- optional string builder = 7 [deprecated = true];
- // Name of the scheduler to trigger this target.
- // Defaults to being triggered by cocoon.
- optional SchedulerSystem scheduler = 8 [default = cocoon];
- // Whether target should run pre-submit. Defaults to true, will run in presubmit.
- optional bool presubmit = 9 [default = true];
- // Whether target should run post-submit. Defaults to true, will run in postsubmit.
- optional bool postsubmit = 10 [default = true];
- // List of paths that trigger this target in presubmit when there is a diff.
- // If no paths are given, it will always run.
- repeated string run_if = 11;
- // Override of enabled_branches for this target (for release targets).
- repeated string enabled_branches = 12;
- // Name of the LUCI recipe to use for the builder.
- optional string recipe = 13;
- reserved 14; // tags
- // Properties to configure infrastructure tooling for only postsubmit runs.
- map<string, string> postsubmit_properties = 15;
- // Dimensions to configure swarming dimensions of LUCI builds.
- map<string, string> dimensions = 16;
- // Dimensions used when this build runs within a drone.
- repeated string drone_dimensions = 17;
- // Runs the target if files are not in these paths.
- repeated string run_if_not = 18;
-}
-
-// Schedulers supported in SchedulerConfig.
-// Next ID: 5
-enum SchedulerSystem {
- // Cocoon will handle all actions for the target (initial trigger, retries).
- cocoon = 1;
- // LUCI triggers the build when mirrored to GoB. Cocoon triggers retries.
- luci = 2;
- // Google internally uses Flutter, and validates if tip-of-tree causes breakages.
- google_internal = 3;
- // Special Cocoon scheduler case to trigger targets intended for beta and stable releases.
- release = 4;
-}
diff --git a/app_dart/lib/src/model/proto/protos.dart b/app_dart/lib/src/model/proto/protos.dart
deleted file mode 100644
index ae7cdee..0000000
--- a/app_dart/lib/src/model/proto/protos.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2019 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.
-
-export 'internal/build_status_response.pb.dart';
-export 'internal/github_webhook.pb.dart';
-export 'internal/key.pb.dart';
-export 'internal/scheduler.pb.dart';
diff --git a/app_dart/lib/src/request_handlers/check_flaky_builders.dart b/app_dart/lib/src/request_handlers/check_flaky_builders.dart
deleted file mode 100644
index 41d64ba..0000000
--- a/app_dart/lib/src/request_handlers/check_flaky_builders.dart
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2019 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/ci_yaml.dart';
-import 'package:collection/collection.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-import 'package:yaml/yaml.dart';
-
-import '../../protos.dart' as pb;
-import '../foundation/utils.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/bigquery.dart';
-import '../service/config.dart';
-import '../service/github_service.dart';
-import 'flaky_handler_utils.dart';
-
-/// A handler to deflake builders if the builders are no longer flaky.
-///
-/// This handler gets flaky builders from ci.yaml in flutter/flutter and check
-/// the following conditions:
-/// 1. The builder is not in [ignoredBuilders].
-/// 2. The flaky issue of the builder is closed if there is one.
-/// 3. Does not have any existing pr against the target.
-/// 4. The builder has been passing for most recent [kRecordNumber] consecutive
-/// runs.
-/// 5. The builder is not marked with ignore_flakiness.
-///
-/// If all the conditions are true, this handler will file a pull request to
-/// make the builder unflaky.
-@immutable
-class CheckFlakyBuilders extends ApiRequestHandler<Body> {
- const CheckFlakyBuilders({
- required super.config,
- required super.authenticationProvider,
- });
-
- static int kRecordNumber = 50;
-
- static final RegExp _issueLinkRegex = RegExp(r'https://github.com/flutter/flutter/issues/(?<id>[0-9]+)');
-
- /// Builders that are purposefully marked flaky and should be ignored by this
- /// handler.
- static const Set<String> ignoredBuilders = <String>{
- 'Mac_ios32 flutter_gallery__transition_perf_e2e_ios32',
- 'Mac_ios32 native_ui_tests_ios',
- };
-
- @override
- Future<Body> get() async {
- final RepositorySlug slug = Config.flutterSlug;
- final GithubService gitHub = config.createGithubServiceWithToken(await config.githubOAuthToken);
- final BigqueryService bigquery = await config.createBigQueryService();
- final String ciContent = await gitHub.getFileContent(
- slug,
- kCiYamlPath,
- );
- final YamlMap? ci = loadYaml(ciContent) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci);
- final CiYaml ciYaml = CiYaml(
- slug: slug,
- branch: Config.defaultBranch(slug),
- config: unCheckedSchedulerConfig,
- );
-
- final pb.SchedulerConfig schedulerConfig = ciYaml.config;
- final List<pb.Target> targets = schedulerConfig.targets;
-
- final List<_BuilderInfo> eligibleBuilders =
- await _getEligibleFlakyBuilders(gitHub, slug, content: ciContent, ciYaml: ciYaml);
- final String testOwnerContent = await gitHub.getFileContent(
- slug,
- kTestOwnerPath,
- );
-
- for (final _BuilderInfo info in eligibleBuilders) {
- final BuilderType type = getTypeForBuilder(info.name, ciYaml);
- final TestOwnership testOwnership = getTestOwnership(
- targets.singleWhere((element) => element.name == info.name!),
- type,
- testOwnerContent,
- );
- final List<BuilderRecord> builderRecords =
- await bigquery.listRecentBuildRecordsForBuilder(kBigQueryProjectId, builder: info.name, limit: kRecordNumber);
- if (_shouldDeflake(builderRecords)) {
- await _deflakyPullRequest(gitHub, slug, info: info, ciContent: ciContent, testOwnership: testOwnership);
- // Manually add a 1s delay between consecutive GitHub requests to deal with secondary rate limit error.
- // https://docs.github.com/en/rest/guides/best-practices-for-integrators#dealing-with-secondary-rate-limits
- await Future.delayed(config.githubRequestDelay);
- }
- }
- return Body.forJson(const <String, dynamic>{
- 'Status': 'success',
- });
- }
-
- /// A builder should be deflaked if satisfying three conditions.
- /// 1) There are enough data records.
- /// 2) There is no flake
- /// 3) There is no failure
- bool _shouldDeflake(List<BuilderRecord> builderRecords) {
- return builderRecords.length >= kRecordNumber &&
- builderRecords.every((BuilderRecord record) => !record.isFlaky && !record.isFailed);
- }
-
- /// Gets the builders that match conditions:
- /// 1. The builder's ignoreFlakiness is false.
- /// 2. The builder is flaky
- /// 3. The builder is not in [ignoredBuilders].
- /// 4. The flaky issue of the builder is closed if there is one.
- /// 5. Does not have any existing pr against the builder.
- Future<List<_BuilderInfo>> _getEligibleFlakyBuilders(
- GithubService gitHub,
- RepositorySlug slug, {
- required String content,
- required CiYaml ciYaml,
- }) async {
- final YamlMap ci = loadYaml(content) as YamlMap;
- final YamlList targets = ci[kCiYamlTargetsKey] as YamlList;
- final List<YamlMap?> flakyTargets = targets
- .where((dynamic target) => target[kCiYamlTargetIsFlakyKey] == true)
- .map<YamlMap?>((dynamic target) => target as YamlMap?)
- .toList();
- final List<_BuilderInfo> result = <_BuilderInfo>[];
- final List<String> lines = content.split('\n');
- final Map<String?, PullRequest> nameToExistingPRs = await getExistingPRs(gitHub, slug);
- for (final YamlMap? flakyTarget in flakyTargets) {
- final String? builder = flakyTarget![kCiYamlTargetNameKey] as String?;
- // If target specified ignore_flakiness, then skip.
- if (getIgnoreFlakiness(builder, ciYaml)) {
- continue;
- }
- if (ignoredBuilders.contains(builder)) {
- continue;
- }
- // Skip the flaky target if the issue or pr for the flaky target is still
- // open.
- if (nameToExistingPRs.containsKey(builder)) {
- continue;
- }
-
- //TODO (ricardoamador): Refactor this so we don't need to parse the entire yaml looking for commented issues, https://github.com/flutter/flutter/issues/113232
- int builderLineNumber = lines.indexWhere((String line) => line.contains('name: $builder')) + 1;
- while (builderLineNumber < lines.length && !lines[builderLineNumber].contains('name:')) {
- if (lines[builderLineNumber].contains('$kCiYamlTargetIsFlakyKey:')) {
- final RegExpMatch? match = _issueLinkRegex.firstMatch(lines[builderLineNumber]);
- if (match == null) {
- result.add(_BuilderInfo(name: builder));
- break;
- }
- final Issue issue = await gitHub.getIssue(slug, issueNumber: int.parse(match.namedGroup('id')!))!;
- if (issue.isClosed) {
- result.add(_BuilderInfo(name: builder, existingIssue: issue));
- }
- break;
- }
- builderLineNumber += 1;
- }
- }
- return result;
- }
-
- @visibleForTesting
- static bool getIgnoreFlakiness(String? builderName, CiYaml ciYaml) {
- final Target? target =
- ciYaml.postsubmitTargets.singleWhereOrNull((Target target) => target.value.name == builderName);
- return target == null ? false : target.getIgnoreFlakiness();
- }
-
- Future<void> _deflakyPullRequest(
- GithubService gitHub,
- RepositorySlug slug, {
- required _BuilderInfo info,
- required String ciContent,
- required TestOwnership testOwnership,
- }) async {
- final String modifiedContent = _deflakeBuilderInContent(ciContent, info.name);
- final GitReference masterRef = await gitHub.getReference(slug, kMasterRefs);
- final DeflakePullRequestBuilder prBuilder = DeflakePullRequestBuilder(
- name: info.name,
- recordNumber: kRecordNumber,
- ownership: testOwnership,
- issue: info.existingIssue,
- );
- final PullRequest pullRequest = await gitHub.createPullRequest(
- slug,
- title: prBuilder.pullRequestTitle,
- body: prBuilder.pullRequestBody,
- commitMessage: prBuilder.pullRequestTitle,
- baseRef: masterRef,
- entries: <CreateGitTreeEntry>[
- CreateGitTreeEntry(
- kCiYamlPath,
- kModifyMode,
- kModifyType,
- content: modifiedContent,
- ),
- ],
- );
- await gitHub.assignReviewer(slug, reviewer: prBuilder.pullRequestReviewer, pullRequestNumber: pullRequest.number);
- }
-
- /// Removes the `bringup: true` for the builder in the ci.yaml.
- String _deflakeBuilderInContent(String content, String? builder) {
- final List<String> lines = content.split('\n');
- final int builderLineNumber = lines.indexWhere((String line) => line.contains('name: $builder'));
- int nextLine = builderLineNumber + 1;
- while (nextLine < lines.length && !lines[nextLine].contains('name:')) {
- if (lines[nextLine].contains('$kCiYamlTargetIsFlakyKey:')) {
- lines.removeAt(nextLine);
- return lines.join('\n');
- }
- nextLine += 1;
- }
- throw 'Cannot find the flaky flag, is the test really marked flaky?';
- }
-}
-
-/// The info of the builder's name and if there is any existing issue opened
-/// for the builder.
-class _BuilderInfo {
- _BuilderInfo({this.name, this.existingIssue});
- final String? name;
- final Issue? existingIssue;
-}
diff --git a/app_dart/lib/src/request_handlers/create_branch.dart b/app_dart/lib/src/request_handlers/create_branch.dart
deleted file mode 100644
index 7d0d688..0000000
--- a/app_dart/lib/src/request_handlers/create_branch.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2020 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 '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/branch_service.dart';
-
-/// Creates the flutter/recipes branch to match a flutter/flutter branch.
-///
-/// This is used by Google Testing to create release infra whenever a good
-/// commit has been found, and is being considered as the branch point to
-/// be rolled into Google.
-class CreateBranch extends ApiRequestHandler<Body> {
- const CreateBranch({
- required this.branchService,
- required super.config,
- required super.authenticationProvider,
- });
-
- final BranchService branchService;
-
- static const String branchParam = 'branch';
- static const String engineShaParam = 'engine';
-
- @override
- Future<Body> get() async {
- checkRequiredQueryParameters(<String>[branchParam, engineShaParam]);
- final String branch = request!.uri.queryParameters[branchParam]!;
- final String engineSha = request!.uri.queryParameters[engineShaParam]!;
-
- await branchService.branchFlutterRecipes(branch, engineSha);
-
- return Body.empty;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/dart_internal_subscription.dart b/app_dart/lib/src/request_handlers/dart_internal_subscription.dart
deleted file mode 100644
index b778c5d..0000000
--- a/app_dart/lib/src/request_handlers/dart_internal_subscription.dart
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2023 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:convert';
-
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:meta/meta.dart';
-
-import '../../cocoon_service.dart';
-import '../model/appengine/task.dart';
-import '../request_handling/subscription_handler.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-
-/// TODO(drewroengoogle): Make this subscription generic so we can accept more
-/// than just dart-internal builds.
-///
-/// An endpoint for listening to build updates for dart-internal builds and
-/// saving the results to the datastore.
-///
-/// The PubSub subscription is set up here:
-/// https://console.cloud.google.com/cloudpubsub/subscription/detail/dart-internal-build-results-sub?project=flutter-dashboard
-@immutable
-class DartInternalSubscription extends SubscriptionHandler {
- /// Creates an endpoint for listening for dart-internal build results.
- /// The message should contain a single buildbucket id
- const DartInternalSubscription({
- required super.cache,
- required super.config,
- super.authProvider,
- required this.buildBucketClient,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- }) : super(subscriptionName: 'dart-internal-build-results-sub');
-
- final BuildBucketClient buildBucketClient;
- final DatastoreServiceProvider datastoreProvider;
-
- @override
- Future<Body> post() async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- if (message.data == null) {
- log.info('no data in message');
- return Body.empty;
- }
-
- final dynamic buildData = json.decode(message.data!);
- log.info('Build data json: $buildData');
-
- if (buildData['build'] == null) {
- log.info('no build information in message');
- return Body.empty;
- }
-
- final String project = buildData['build']['builder']['project'];
- final String bucket = buildData['build']['builder']['bucket'];
- final String builder = buildData['build']['builder']['builder'];
-
- // This should already be covered by the pubsub filter, but adding an additional check
- // to ensure we don't process builds that aren't from dart-internal/flutter.
- if (project != 'dart-internal' || bucket != 'flutter') {
- log.info('Ignoring build not from dart-internal/flutter bucket');
- return Body.empty;
- }
-
- // Only publish the parent release_builder builds to the datastore.
- // TODO(drewroengoogle): Remove this regex in favor of supporting *all* dart-internal build results.
- // Issue: https://github.com/flutter/flutter/issues/134674
- final regex =
- RegExp(r'(Linux|Mac|Windows)\s+(engine_release_builder|packaging_release_builder|flutter_release_builder)');
- if (!regex.hasMatch(builder)) {
- log.info('Ignoring builder that is not a release builder');
- return Body.empty;
- }
-
- final String buildbucketId = buildData['build']['id'];
- log.info('Creating build request object with build id $buildbucketId');
- final GetBuildRequest request = GetBuildRequest(
- id: buildbucketId,
- );
-
- log.info(
- 'Calling buildbucket api to get build data for build $buildbucketId',
- );
- final Build build = await buildBucketClient.getBuild(request);
-
- log.info('Checking for existing task in datastore');
- final Task? existingTask = await datastore.getTaskFromBuildbucketBuild(build);
-
- late Task taskToInsert;
- if (existingTask != null) {
- log.info('Updating Task from existing Task');
- existingTask.updateFromBuildbucketBuild(build);
- taskToInsert = existingTask;
- } else {
- log.info('Creating Task from Buildbucket result');
- taskToInsert = await Task.fromBuildbucketBuild(build, datastore);
- }
-
- log.info('Inserting Task into the datastore: ${taskToInsert.toString()}');
- await datastore.insert(<Task>[taskToInsert]);
-
- return Body.forJson(taskToInsert.toString());
- }
-}
diff --git a/app_dart/lib/src/request_handlers/file_flaky_issue_and_pr.dart b/app_dart/lib/src/request_handlers/file_flaky_issue_and_pr.dart
deleted file mode 100644
index 10ab0bc..0000000
--- a/app_dart/lib/src/request_handlers/file_flaky_issue_and_pr.dart
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2019 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/ci_yaml.dart';
-import 'package:collection/collection.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-import 'package:yaml/yaml.dart';
-
-import '../../protos.dart' as pb;
-import '../foundation/utils.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/bigquery.dart';
-import '../service/config.dart';
-import '../service/github_service.dart';
-import 'flaky_handler_utils.dart';
-
-/// A handler that queries build statistics from luci and file issues and pull
-/// requests for tests that have high flaky ratios.
-///
-/// The query parameter kThresholdKey is required for this handler to use it as
-/// the standard when compares the flaky ratios.
-@immutable
-class FileFlakyIssueAndPR extends ApiRequestHandler<Body> {
- const FileFlakyIssueAndPR({
- required super.config,
- required super.authenticationProvider,
- });
-
- static const String kThresholdKey = 'threshold';
-
- @override
- Future<Body> get() async {
- final RepositorySlug slug = Config.flutterSlug;
- final GithubService gitHub = config.createGithubServiceWithToken(await config.githubOAuthToken);
- final BigqueryService bigquery = await config.createBigQueryService();
- final List<BuilderStatistic> builderStatisticList = await bigquery.listBuilderStatistic(kBigQueryProjectId);
- final YamlMap? ci = loadYaml(await gitHub.getFileContent(slug, kCiYamlPath)) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci);
- final CiYaml ciYaml = CiYaml(
- slug: slug,
- branch: Config.defaultBranch(slug),
- config: unCheckedSchedulerConfig,
- );
-
- final pb.SchedulerConfig schedulerConfig = ciYaml.config;
- final List<pb.Target> targets = schedulerConfig.targets;
-
- final String testOwnerContent = await gitHub.getFileContent(slug, kTestOwnerPath);
- final Map<String?, Issue> nameToExistingIssue = await getExistingIssues(gitHub, slug);
- final Map<String?, PullRequest> nameToExistingPR = await getExistingPRs(gitHub, slug);
- int filedIssueAndPRCount = 0;
- for (final BuilderStatistic statistic in builderStatisticList) {
- // Skip if ignore_flakiness is specified.
- if (getIgnoreFlakiness(statistic.name, ciYaml)) {
- continue;
- }
- if (statistic.flakyRate < _threshold) {
- continue;
- }
-
- final BuilderType type = getTypeForBuilder(statistic.name, ciYaml);
- final bool issueAndPRFiled = await _fileIssueAndPR(
- gitHub,
- slug,
- builderDetail: BuilderDetail(
- statistic: statistic,
- existingIssue: nameToExistingIssue[statistic.name],
- existingPullRequest: nameToExistingPR[statistic.name],
- isMarkedFlaky: _getIsMarkedFlaky(statistic.name, ci!),
- type: type,
- ownership: getTestOwnership(
- targets.singleWhere((element) => element.name == statistic.name),
- type,
- testOwnerContent,
- ),
- ),
- );
- if (issueAndPRFiled) {
- filedIssueAndPRCount++;
- }
- if (filedIssueAndPRCount == config.issueAndPRLimit) {
- break;
- }
- }
- return Body.forJson(<String, dynamic>{
- 'Status': 'success',
- 'NumberOfCreatedIssuesAndPRs': filedIssueAndPRCount,
- });
- }
-
- double get _threshold => double.parse(request!.uri.queryParameters[kThresholdKey]!);
-
- Future<bool> _fileIssueAndPR(
- GithubService gitHub,
- RepositorySlug slug, {
- required BuilderDetail builderDetail,
- }) async {
- Issue? issue = builderDetail.existingIssue;
- if (_shouldNotFileIssueAndPR(builderDetail, issue)) {
- return false;
- }
- // Manually add a 1s delay between consecutive GitHub requests to deal with secondary rate limit error.
- // https://docs.github.com/en/rest/guides/best-practices-for-integrators#dealing-with-secondary-rate-limits
- await Future.delayed(config.githubRequestDelay);
- issue = await fileFlakyIssue(builderDetail: builderDetail, gitHub: gitHub, slug: slug, threshold: _threshold);
-
- if (builderDetail.type == BuilderType.shard ||
- builderDetail.type == BuilderType.unknown ||
- builderDetail.existingPullRequest != null) {
- return true;
- }
- final String modifiedContent = _marksBuildFlakyInContent(
- await gitHub.getFileContent(
- slug,
- kCiYamlPath,
- ),
- builderDetail.statistic.name,
- issue.htmlUrl,
- );
- final GitReference masterRef = await gitHub.getReference(slug, kMasterRefs);
- final PullRequestBuilder prBuilder =
- PullRequestBuilder(statistic: builderDetail.statistic, ownership: builderDetail.ownership, issue: issue);
- final PullRequest pullRequest = await gitHub.createPullRequest(
- slug,
- title: prBuilder.pullRequestTitle,
- body: prBuilder.pullRequestBody,
- commitMessage: prBuilder.pullRequestTitle,
- baseRef: masterRef,
- entries: <CreateGitTreeEntry>[
- CreateGitTreeEntry(
- kCiYamlPath,
- kModifyMode,
- kModifyType,
- content: modifiedContent,
- ),
- ],
- );
- final String? label = getTeamLabelFromTeam(builderDetail.ownership.team);
- await gitHub.assignReviewer(slug, reviewer: prBuilder.pullRequestReviewer, pullRequestNumber: pullRequest.number);
- if (label != null) {
- await gitHub.addIssueLabels(slug, pullRequest.number!, <String>[label]);
- }
- return true;
- }
-
- bool _shouldNotFileIssueAndPR(BuilderDetail builderDetail, Issue? issue) {
- // Don't create a new issue or deflake PR using prod builds statuses if the builder has been marked as flaky.
- // If the builder is `bringup: true`, but still hit flakes, a new bug will be filed in `/api/check_flaky_builders`
- // based on staging builds statuses.
- if (builderDetail.isMarkedFlaky) {
- return true;
- }
-
- // Don't create a new issue or deflake PR if there is an open issue or a recent closed
- // issue within kGracePeriodForClosedFlake days. It takes time for the flaky ratio to go
- // down after the fix is merged.
- if (issue != null &&
- (issue.state != 'closed' ||
- DateTime.now().difference(issue.closedAt!) <= const Duration(days: kGracePeriodForClosedFlake))) {
- return true;
- }
-
- return false;
- }
-
- bool _getIsMarkedFlaky(String builderName, YamlMap ci) {
- final YamlList targets = ci[kCiYamlTargetsKey] as YamlList;
- final YamlMap? target = targets.firstWhere(
- (dynamic element) => element[kCiYamlTargetNameKey] == builderName,
- orElse: () => null,
- ) as YamlMap?;
- return target != null && target[kCiYamlTargetIsFlakyKey] == true;
- }
-
- @visibleForTesting
- static bool getIgnoreFlakiness(String builderName, CiYaml ciYaml) {
- final Target? target =
- ciYaml.postsubmitTargets.singleWhereOrNull((Target target) => target.value.name == builderName);
- return target == null ? false : target.getIgnoreFlakiness();
- }
-
- String _marksBuildFlakyInContent(String content, String builder, String issueUrl) {
- final List<String> lines = content.split('\n');
- final int builderLineNumber = lines.indexWhere((String line) => line.contains('name: $builder'));
- // Takes care the case if is kCiYamlTargetIsFlakyKey is already defined to false
- int nextLine = builderLineNumber + 1;
- while (nextLine < lines.length && !lines[nextLine].contains('name:')) {
- if (lines[nextLine].contains('$kCiYamlTargetIsFlakyKey:')) {
- lines[nextLine] = lines[nextLine].replaceFirst('false', 'true # Flaky $issueUrl');
- return lines.join('\n');
- }
- nextLine += 1;
- }
- lines.insert(builderLineNumber + 1, ' $kCiYamlTargetIsFlakyKey: true # Flaky $issueUrl');
- return lines.join('\n');
- }
-
- Future<RepositorySlug> getSlugFor(GitHub client, String repository) async {
- return RepositorySlug((await client.users.getCurrentUser()).login!, repository);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/flaky_handler_utils.dart b/app_dart/lib/src/request_handlers/flaky_handler_utils.dart
deleted file mode 100644
index a5f31df..0000000
--- a/app_dart/lib/src/request_handlers/flaky_handler_utils.dart
+++ /dev/null
@@ -1,480 +0,0 @@
-// Copyright 2019 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:convert';
-import 'dart:core';
-
-import 'package:cocoon_service/ci_yaml.dart';
-import 'package:cocoon_service/src/request_handlers/test_ownership.dart';
-import 'package:collection/collection.dart';
-import 'package:github/github.dart';
-
-import '../service/bigquery.dart';
-import '../service/github_service.dart';
-import '../../protos.dart' as pb;
-
-// String constants.
-const String kFlakeLabel = 'c: flake';
-const String kFrameworkLabel = 'team-framework';
-const String kToolLabel = 'team-tool';
-const String kEngineLabel = 'team-engine';
-const String kWebLabel = 'team-web';
-const String kInfraLabel = 'team-infra';
-const String kAndroidLabel = 'team-android';
-const String kIosLabel = 'team-ios';
-const String kReleaseLabel = 'team-release';
-const String kEcosystemLabel = 'team-ecosystem';
-const String kP0Label = 'P0';
-const String kP1Label = 'P1';
-const String kP2Label = 'P2';
-const String kP3Label = 'P3';
-
-const String kBigQueryProjectId = 'flutter-dashboard';
-const String kCiYamlTargetsKey = 'targets';
-const String kCiYamlTargetNameKey = 'name';
-const String kCiYamlTargetIgnoreFlakiness = 'ignore_flakiness';
-const String kCiYamlTargetIsFlakyKey = 'bringup';
-const String kCiYamlPropertiesKey = 'properties';
-const String kCiYamlTargetTagsKey = 'tags';
-const String kCiYamlTargetTagsShard = 'shard';
-const String kCiYamlTargetTagsFirebaselab = 'firebaselab';
-const String kCiYamlTargetTagsDevicelab = 'devicelab';
-const String kCiYamlTargetTagsFramework = 'framework';
-const String kCiYamlTargetTagsHostonly = 'hostonly';
-
-const String kMasterRefs = 'heads/master';
-const String kModifyMode = '100644'; // This is equivalent to mode: `-rw-r--r--`.
-const String kModifyType = 'blob';
-
-const int kSuccessBuildNumberLimit = 3;
-const int kFlayRatioBuildNumberList = 10;
-const double kDefaultFlakyRatioThreshold = 0.02;
-const int kGracePeriodForClosedFlake = 15; // days
-
-const String _commitPrefix = 'https://github.com/flutter/flutter/commit/';
-const String _buildDashboardPrefix = 'https://flutter-dashboard.appspot.com/#/build';
-const String _prodBuildPrefix = 'https://ci.chromium.org/ui/p/flutter/builders/prod/';
-const String _stagingBuildPrefix = 'https://ci.chromium.org/ui/p/flutter/builders/staging/';
-const String _flakeRecordPrefix =
- 'https://data.corp.google.com/sites/flutter_infra_metrics_datasite/flutter_check_test_flakiness_status_dashboard/?p=BUILDER_NAME:';
-
-/// A builder to build a new issue for a flake.
-class IssueBuilder {
- IssueBuilder({
- required this.statistic,
- required this.ownership,
- required this.threshold,
- this.bringup = false,
- });
-
- final BuilderStatistic statistic;
- final TestOwnership ownership;
- final double threshold;
- final bool bringup;
-
- Bucket get buildBucket {
- return bringup ? Bucket.staging : Bucket.prod;
- }
-
- String get issueTitle {
- return '${statistic.name} is ${_formatRate(statistic.flakyRate)}% flaky';
- }
-
- String? get issueAssignee {
- return ownership.owner;
- }
-
- /// Return `kSuccessBuildNumberLimit` successful builds if there are more. Otherwise return what's available.
- int numberOfSuccessBuilds(int numberOfAvailableSuccessBuilds) {
- return numberOfAvailableSuccessBuilds >= kSuccessBuildNumberLimit
- ? kSuccessBuildNumberLimit
- : numberOfAvailableSuccessBuilds;
- }
-
- String get issueBody {
- return '''
-${_buildHiddenMetaTags(name: statistic.name)}
-${_issueSummary(statistic, threshold, bringup)}
-
-One recent flaky example for a same commit: ${_issueBuildLink(builder: statistic.name, build: statistic.flakyBuildOfRecentCommit, bucket: buildBucket)}
-Commit: $_commitPrefix${statistic.recentCommit}
-
-Flaky builds:
-${_issueBuildLinks(builder: statistic.name, builds: statistic.flakyBuilds!, bucket: buildBucket)}
-
-Recent test runs:
-${_issueBuilderLink(statistic.name)}
-
-Please follow https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness#fixing-flaky-tests to fix the flakiness and enable the test back after validating the fix (internal dashboard to validate: go/flutter_test_flakiness).
-''';
- }
-
- List<String> get issueLabels {
- final List<String> labels = <String>[
- kFlakeLabel,
- kP0Label,
- ];
- final String? teamLabel = getTeamLabelFromTeam(ownership.team);
- if (teamLabel != null && teamLabel.isNotEmpty == true) {
- labels.add(teamLabel);
- }
- return labels;
- }
-}
-
-/// A builder to build the update comment and labels for an existing open flaky
-/// issue.
-class IssueUpdateBuilder {
- IssueUpdateBuilder({
- required this.statistic,
- required this.threshold,
- required this.existingIssue,
- required this.bucket,
- });
-
- final BuilderStatistic statistic;
- final double threshold;
- final Issue existingIssue;
- final Bucket bucket;
-
- bool get isBelow => statistic.flakyRate < threshold;
-
- String get bucketString => bucket.toString().split('.').last;
-
- List<String> get issueLabels {
- final List<String> existingLabels = existingIssue.labels.map<String>((IssueLabel label) => label.name).toList();
- // Update the priority.
- if (!existingLabels.contains(kP0Label) && !isBelow) {
- existingLabels.add(kP0Label);
- existingLabels.remove(kP1Label);
- existingLabels.remove(kP2Label);
- existingLabels.remove(kP3Label);
- }
- return existingLabels;
- }
-
- String get issueUpdateComment {
- String result =
- '[$bucketString pool] flaky ratio for the past (up to) 100 commits between ${statistic.fromDate} and ${statistic.toDate} is ${_formatRate(statistic.flakyRate)}%. Flaky number: ${statistic.flakyNumber}; total number: ${statistic.totalNumber}.\n';
- if (statistic.flakyRate > 0.0) {
- result += '''
-One recent flaky example for a same commit: ${_issueBuildLink(builder: statistic.name, build: statistic.flakyBuildOfRecentCommit, bucket: bucket)}
-Commit: $_commitPrefix${statistic.recentCommit}
-Flaky builds:
-${_issueBuildLinks(builder: statistic.name, builds: statistic.flakyBuilds!, bucket: bucket)}
-
-Recent test runs:
-${_issueBuilderLink(statistic.name)}
-''';
- }
- return result;
- }
-}
-
-/// A builder to build the pull request title and body for marking test flaky
-class PullRequestBuilder {
- PullRequestBuilder({
- required this.statistic,
- required this.ownership,
- required this.issue,
- });
-
- final BuilderStatistic statistic;
- final TestOwnership ownership;
- final Issue issue;
-
- String get pullRequestTitle => 'Marks ${statistic.name} to be flaky';
- String get pullRequestBody => '${_buildHiddenMetaTags(name: statistic.name)}Issue link: ${issue.htmlUrl}\n';
- String? get pullRequestReviewer => ownership.owner;
-}
-
-/// A builder to build the pull request title and body for marking test unflaky
-class DeflakePullRequestBuilder {
- DeflakePullRequestBuilder({
- required this.name,
- required this.recordNumber,
- required this.ownership,
- this.issue,
- });
-
- final String? name;
- final Issue? issue;
- final TestOwnership ownership;
- final int recordNumber;
-
- String get pullRequestTitle => 'Marks $name to be unflaky';
- String get pullRequestBody {
- String body = _buildHiddenMetaTags(name: name);
- if (issue != null) {
- body +=
- 'The issue ${issue!.htmlUrl} has been closed, and the test has been passing for [$recordNumber consecutive runs](${Uri.encodeFull('$_flakeRecordPrefix"$name"')}).\n';
- } else {
- body +=
- 'The test has been passing for [$recordNumber consecutive runs](${Uri.encodeFull('$_flakeRecordPrefix"$name"')}).\n';
- }
- body += 'This test can be marked as unflaky.\n';
- return body;
- }
-
- String? get pullRequestReviewer => ownership.owner;
-}
-
-// TESTOWNER Regex
-
-const String kOwnerGroupName = 'owners';
-final RegExp devicelabTestOwners =
- RegExp('## Linux Android DeviceLab tests\n(?<$kOwnerGroupName>.+)## Host only framework tests', dotAll: true);
-final RegExp frameworkHostOnlyTestOwners =
- RegExp('## Host only framework tests\n(?<$kOwnerGroupName>.+)## Firebase tests', dotAll: true);
-final RegExp firebaselabTestOwners = RegExp('## Firebase tests\n(?<$kOwnerGroupName>.+)## Shards tests', dotAll: true);
-final RegExp shardTestOwners = RegExp('## Shards tests\n(?<$kOwnerGroupName>.+)', dotAll: true);
-
-// Utils methods
-
-/// Gets the existing flaky issues.
-///
-/// The state can be 'open', 'closed', or 'all'.
-Future<Map<String?, Issue>> getExistingIssues(GithubService gitHub, RepositorySlug slug, {String state = 'all'}) async {
- final Map<String?, Issue> nameToExistingIssue = <String?, Issue>{};
- for (final Issue issue in await gitHub.listIssues(slug, state: state, labels: <String>[kFlakeLabel])) {
- if (issue.htmlUrl.contains('pull') == true) {
- // For some reason, this github api may also return pull requests.
- continue;
- }
- final Map<String, dynamic>? metaTags = retrieveMetaTagsFromContent(issue.body);
- if (metaTags != null) {
- final String? name = metaTags['name'] as String?;
- if (!nameToExistingIssue.containsKey(name) || _isOtherIssueMoreImportant(nameToExistingIssue[name]!, issue)) {
- nameToExistingIssue[name] = issue;
- }
- }
- }
- return nameToExistingIssue;
-}
-
-/// Gets the existing open pull requests that make tests flaky.
-Future<Map<String?, PullRequest>> getExistingPRs(GithubService gitHub, RepositorySlug slug) async {
- final Map<String?, PullRequest> nameToExistingPRs = <String?, PullRequest>{};
- for (final PullRequest pr in await gitHub.listPullRequests(slug, null)) {
- try {
- if (pr.body == null) {
- continue;
- }
- final Map<String, dynamic>? metaTags = retrieveMetaTagsFromContent(pr.body!);
- if (metaTags != null) {
- nameToExistingPRs[metaTags['name'] as String] = pr;
- }
- } catch (e) {
- throw 'Unable to parse body of ${pr.htmlUrl}\n$e';
- }
- }
- return nameToExistingPRs;
-}
-
-/// File a GitHub flaky issue based on builder details in recent prod/staging runs.
-Future<Issue> fileFlakyIssue({
- required BuilderDetail builderDetail,
- required GithubService gitHub,
- required RepositorySlug slug,
- double threshold = kDefaultFlakyRatioThreshold,
- bool bringup = false,
-}) async {
- final IssueBuilder issueBuilder = IssueBuilder(
- statistic: builderDetail.statistic,
- ownership: builderDetail.ownership,
- threshold: kDefaultFlakyRatioThreshold,
- bringup: bringup,
- );
- return gitHub.createIssue(
- slug,
- title: issueBuilder.issueTitle,
- body: issueBuilder.issueBody,
- labels: issueBuilder.issueLabels,
- assignee: issueBuilder.issueAssignee,
- );
-}
-
-/// Looks up the owner of a builder in TESTOWNERS file.
-TestOwnership getTestOwnership(pb.Target target, BuilderType type, String testOwnersContent) {
- final TestOwner testOwner = TestOwner(type);
- return testOwner.getTestOwnership(target, testOwnersContent);
-}
-
-/// Gets the [BuilderType] of the builder by looking up the information in the
-/// ci.yaml.
-BuilderType getTypeForBuilder(String? targetName, CiYaml ciYaml, {bool unfilteredTargets = false}) {
- final List<String>? tags = _getTags(targetName, ciYaml, unfilteredTargets: unfilteredTargets);
- if (tags == null || tags.isEmpty) {
- return BuilderType.unknown;
- }
-
- bool hasFrameworkTag = false;
- bool hasHostOnlyTag = false;
- // If tags contain 'shard', it must be a shard test.
- // If tags contain 'devicelab', it must be a devicelab test.
- // If tags contain 'firebaselab`, it must be a firebase tests.
- // Otherwise, it is framework host only test if its tags contain both
- // 'framework' and 'hostonly'.
- for (String tag in tags) {
- if (tag == kCiYamlTargetTagsFirebaselab) {
- return BuilderType.firebaselab;
- } else if (tag == kCiYamlTargetTagsShard) {
- return BuilderType.shard;
- } else if (tag == kCiYamlTargetTagsDevicelab) {
- return BuilderType.devicelab;
- } else if (tag == kCiYamlTargetTagsFramework) {
- hasFrameworkTag = true;
- } else if (tag == kCiYamlTargetTagsHostonly) {
- hasHostOnlyTag = true;
- }
- }
- return hasFrameworkTag && hasHostOnlyTag ? BuilderType.frameworkHostOnly : BuilderType.unknown;
-}
-
-List<String>? _getTags(String? targetName, CiYaml ciYaml, {bool unfilteredTargets = false}) {
- final Set<Target> allUniqueTargets = {};
- if (!unfilteredTargets) {
- allUniqueTargets.addAll(ciYaml.presubmitTargets);
- allUniqueTargets.addAll(ciYaml.postsubmitTargets);
- } else {
- allUniqueTargets.addAll(ciYaml.targets);
- }
-
- final Target? target = allUniqueTargets.firstWhereOrNull((element) => element.value.name == targetName);
- return target?.tags;
-}
-
-bool _isOtherIssueMoreImportant(Issue original, Issue other) {
- // Open issues are always more important than closed issues. If both issue
- // are closed, the one that is most recently created is more important.
- if (original.isOpen && other.isOpen) {
- throw 'There should not be two open issues for the same test';
- } else if (original.isOpen && other.isClosed) {
- return false;
- } else if (original.isClosed && other.isOpen) {
- return true;
- } else {
- return other.createdAt!.isAfter(original.createdAt!);
- }
-}
-
-String _buildHiddenMetaTags({String? name}) {
- return '''<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "$name"
-}
--->
-''';
-}
-
-final RegExp _issueHiddenMetaTagsRegex =
- RegExp(r'<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY\.(?<meta>.+)-->', dotAll: true);
-
-/// Checks whether the github content contains meta tags and returns the meta
-/// tags if it does.
-///
-/// The script generated contents for issue bodies or pull request bodies
-/// contain the meta tags. Using this method is a reliable way to check whether
-/// a issue or pull request is generated by this script.
-Map<String, dynamic>? retrieveMetaTagsFromContent(String content) {
- final RegExpMatch? match = _issueHiddenMetaTagsRegex.firstMatch(content);
- if (match == null) {
- return null;
- }
- return jsonDecode(match.namedGroup('meta')!) as Map<String, dynamic>?;
-}
-
-String _formatRate(double rate) => (rate * 100).toStringAsFixed(2);
-
-String _issueBuildLinks({String? builder, required List<String> builds, Bucket bucket = Bucket.prod}) {
- return builds.map((String build) => _issueBuildLink(builder: builder, build: build, bucket: bucket)).join('\n');
-}
-
-String _issueSummary(BuilderStatistic statistic, double threshold, bool bringup) {
- final String summary;
- if (bringup) {
- summary =
- 'The post-submit test builder `${statistic.name}`, which has been marked `bringup: true`, had ${statistic.flakyNumber} flakes over past ${statistic.totalNumber} commits.';
- } else {
- summary =
- 'The post-submit test builder `${statistic.name}` had a flaky ratio ${_formatRate(statistic.flakyRate)}% for the past (up to) 100 commits, which is above our ${_formatRate(threshold)}% threshold.';
- }
- return summary;
-}
-
-String _issueBuildLink({String? builder, String? build, Bucket bucket = Bucket.prod}) {
- final String buildPrefix = bucket == Bucket.staging ? _stagingBuildPrefix : _prodBuildPrefix;
- return Uri.encodeFull('$buildPrefix$builder/$build');
-}
-
-String _issueBuilderLink(String? builder) {
- return Uri.encodeFull('$_buildDashboardPrefix?taskFilter=$builder');
-}
-
-String? getTeamLabelFromTeam(Team? team) {
- return switch (team) {
- Team.framework => kFrameworkLabel,
- Team.engine => kEngineLabel,
- Team.tool => kToolLabel,
- Team.web => kWebLabel,
- Team.infra => kInfraLabel,
- Team.android => kAndroidLabel,
- Team.ios => kIosLabel,
- Team.release => kReleaseLabel,
- Team.plugins => kEcosystemLabel,
- Team.unknown || null => null,
- };
-}
-
-enum BuilderType {
- devicelab,
- frameworkHostOnly,
- shard,
- firebaselab,
- unknown,
-}
-
-enum Bucket {
- prod,
- staging,
-}
-
-enum Team {
- framework,
- engine,
- tool,
- web,
- infra,
- android,
- ios,
- release,
- plugins,
- unknown,
-}
-
-class TestOwnership {
- TestOwnership(
- this.owner,
- this.team,
- );
- String? owner;
- Team? team;
-}
-
-class BuilderDetail {
- const BuilderDetail({
- required this.statistic,
- required this.existingIssue,
- required this.existingPullRequest,
- required this.isMarkedFlaky,
- required this.ownership,
- required this.type,
- });
- final BuilderStatistic statistic;
- final Issue? existingIssue;
- final PullRequest? existingPullRequest;
- final TestOwnership ownership;
- final bool isMarkedFlaky;
- final BuilderType type;
-}
diff --git a/app_dart/lib/src/request_handlers/flush_cache.dart b/app_dart/lib/src/request_handlers/flush_cache.dart
deleted file mode 100644
index 824a0c7..0000000
--- a/app_dart/lib/src/request_handlers/flush_cache.dart
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 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:meta/meta.dart';
-
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../service/cache_service.dart';
-import '../service/config.dart';
-
-/// Trigger a cache flush on a config key and return empty response if successful.
-///
-/// If [cacheKeyParam] is not passed, throws [BadRequestException].
-/// If the cache does not have the given key, throws [NotFoundException].
-@immutable
-class FlushCache extends ApiRequestHandler<Body> {
- const FlushCache({
- required super.config,
- required super.authenticationProvider,
- required this.cache,
- });
-
- final CacheService cache;
-
- /// Name of the query parameter passed to the endpoint.
- ///
- /// The value is expected to be an existing value from [CocoonConfig].
- static const String cacheKeyParam = 'key';
-
- @override
- Future<Body> get() async {
- checkRequiredQueryParameters(<String>[cacheKeyParam]);
- final String cacheKey = request!.uri.queryParameters[cacheKeyParam]!;
-
- // To validate cache flushes, validate that the key exists.
- await cache.getOrCreate(
- Config.configCacheName,
- cacheKey,
- createFn: () => throw NotFoundException('Failed to find cache key: $cacheKey'),
- );
-
- await cache.purge(Config.configCacheName, cacheKey);
-
- return Body.empty;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_build_status.dart b/app_dart/lib/src/request_handlers/get_build_status.dart
deleted file mode 100644
index e434842..0000000
--- a/app_dart/lib/src/request_handlers/get_build_status.dart
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019 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:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../../protos.dart' show BuildStatusResponse, EnumBuildStatus;
-import '../request_handling/body.dart';
-import '../request_handling/request_handler.dart';
-import '../service/build_status_provider.dart';
-import '../service/datastore.dart';
-
-@immutable
-class GetBuildStatus extends RequestHandler<Body> {
- const GetBuildStatus({
- required super.config,
- @visibleForTesting DatastoreServiceProvider? datastoreProvider,
- @visibleForTesting BuildStatusServiceProvider? buildStatusProvider,
- }) : datastoreProvider = datastoreProvider ?? DatastoreService.defaultProvider,
- buildStatusProvider = buildStatusProvider ?? BuildStatusService.defaultProvider;
- final DatastoreServiceProvider datastoreProvider;
- final BuildStatusServiceProvider buildStatusProvider;
-
- static const String kRepoParam = 'repo';
-
- @override
- Future<Body> get() async {
- final DatastoreService datastore = datastoreProvider(config.db);
- final BuildStatusService buildStatusService = buildStatusProvider(datastore);
- final String repoName = request!.uri.queryParameters[kRepoParam] ?? 'flutter';
- final RepositorySlug slug = RepositorySlug('flutter', repoName);
- final BuildStatus status = (await buildStatusService.calculateCumulativeStatus(slug))!;
- final BuildStatusResponse response = BuildStatusResponse()
- ..buildStatus = status.succeeded ? EnumBuildStatus.success : EnumBuildStatus.failure
- ..failingTasks.addAll(status.failedTasks);
-
- return Body.forJson(response.writeToJsonMap());
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_green_commits.dart b/app_dart/lib/src/request_handlers/get_green_commits.dart
deleted file mode 100644
index 8a3adba..0000000
--- a/app_dart/lib/src/request_handlers/get_green_commits.dart
+++ /dev/null
@@ -1,78 +0,0 @@
-// 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),
- );
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_release_branches.dart b/app_dart/lib/src/request_handlers/get_release_branches.dart
deleted file mode 100644
index f698e2f..0000000
--- a/app_dart/lib/src/request_handlers/get_release_branches.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2020 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/request_handling/request_handler.dart';
-import 'package:github/github.dart';
-
-import '../request_handling/body.dart';
-import '../service/branch_service.dart';
-import '../service/config.dart';
-import '../service/github_service.dart';
-
-/// Return a list of release branch information.
-///
-/// GET: /api/public/get-release-branches
-///
-/// Response: Status 200 OK
-///[
-/// {
-/// "branch":"flutter-2.13-candidate.0",
-/// "name":"stable"
-/// },
-/// {
-/// "branch":"flutter-3.2-candidate.5",
-/// "name":"beta"
-/// },
-/// {
-/// "branch":"flutter-3.4-candidate.5",
-/// "name":"dev"
-/// }
-///]
-
-class GetReleaseBranches extends RequestHandler<Body> {
- GetReleaseBranches({required super.config, required this.branchService});
-
- final BranchService branchService;
-
- @override
- Future<Body> get() async {
- final GitHub github = await config.createGitHubClient(slug: Config.flutterSlug);
- final GithubService githubService = GithubService(github);
- final List<Map<String, String>> branchNames =
- await branchService.getReleaseBranches(githubService: githubService, slug: Config.flutterSlug);
- return Body.forJson(branchNames);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_repos.dart b/app_dart/lib/src/request_handlers/get_repos.dart
deleted file mode 100644
index 0366ab1..0000000
--- a/app_dart/lib/src/request_handlers/get_repos.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2020 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:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../request_handling/body.dart';
-import '../request_handling/request_handler.dart';
-import '../service/config.dart';
-
-/// Returns [Config.supportedRepos] as a list of repo names.
-@immutable
-class GetRepos extends RequestHandler<Body> {
- const GetRepos({
- required super.config,
- });
-
- @override
- Future<Body> get() async {
- final List<String> repos = config.supportedRepos.map((RepositorySlug slug) => slug.name).toList();
- return Body.forJson(repos);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_status.dart b/app_dart/lib/src/request_handlers/get_status.dart
deleted file mode 100644
index 55f812f..0000000
--- a/app_dart/lib/src/request_handlers/get_status.dart
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2019 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:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/appengine/key_helper.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../request_handling/request_handler.dart';
-import '../service/build_status_provider.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-
-@immutable
-class GetStatus extends RequestHandler<Body> {
- const GetStatus({
- required super.config,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- @visibleForTesting this.buildStatusProvider = BuildStatusService.defaultProvider,
- });
-
- final DatastoreServiceProvider datastoreProvider;
- final BuildStatusServiceProvider buildStatusProvider;
-
- static const String kLastCommitKeyParam = 'lastCommitKey';
- static const String kBranchParam = 'branch';
- static const String kRepoParam = 'repo';
-
- @override
- Future<Body> get() async {
- final String? encodedLastCommitKey = request!.uri.queryParameters[kLastCommitKeyParam];
- 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 KeyHelper keyHelper = config.keyHelper;
- final int commitNumber = config.commitNumber;
- final int lastCommitTimestamp = await _obtainTimestamp(encodedLastCommitKey, keyHelper, datastore);
-
- final List<SerializableCommitStatus> statuses = await buildStatusService
- .retrieveCommitStatus(
- limit: commitNumber,
- timestamp: lastCommitTimestamp,
- branch: branch,
- slug: slug,
- )
- .map<SerializableCommitStatus>(
- (CommitStatus status) => SerializableCommitStatus(status, keyHelper.encode(status.commit.key)),
- )
- .toList();
-
- return Body.forJson(<String, dynamic>{
- 'Statuses': statuses,
- });
- }
-
- Future<int> _obtainTimestamp(String? encodedLastCommitKey, KeyHelper keyHelper, DatastoreService datastore) async {
- /// [lastCommitTimestamp] is initially set as the current time, which allows query return
- /// latest commit list. If [owerKeyParam] is not empty, [lastCommitTimestamp] will be set
- /// as the timestamp of that [commit], and the query will return lastest commit
- /// list whose timestamp is smaller than [lastCommitTimestamp].
- int lastCommitTimestamp = DateTime.now().millisecondsSinceEpoch;
-
- if (encodedLastCommitKey != null) {
- final Key<String> ownerKey = keyHelper.decode(encodedLastCommitKey) as Key<String>;
- final Commit commit = await datastore.db.lookupValue<Commit>(
- ownerKey,
- orElse: () => throw NotFoundException('Failed to find commit with key $ownerKey'),
- );
-
- lastCommitTimestamp = commit.timestamp!;
- }
- return lastCommitTimestamp;
- }
-}
-
-/// The serialized representation of a [CommitStatus].
-// TODO(tvolkert): Directly serialize [CommitStatus] once frontends migrate to new format.
-class SerializableCommitStatus {
- const SerializableCommitStatus(this.status, this.key);
-
- final CommitStatus status;
- final String key;
-
- Map<String, dynamic> toJson() {
- return <String, dynamic>{
- 'Checklist': <String, dynamic>{
- 'Key': key,
- 'Checklist': SerializableCommit(status.commit).facade,
- },
- 'Stages': status.stages,
- };
- }
-}
diff --git a/app_dart/lib/src/request_handlers/github/webhook_subscription.dart b/app_dart/lib/src/request_handlers/github/webhook_subscription.dart
deleted file mode 100644
index 05f7762..0000000
--- a/app_dart/lib/src/request_handlers/github/webhook_subscription.dart
+++ /dev/null
@@ -1,638 +0,0 @@
-// Copyright 2019 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:convert';
-
-import 'package:cocoon_service/src/service/commit_service.dart';
-import 'package:github/github.dart';
-import 'package:github/hooks.dart';
-import 'package:meta/meta.dart';
-
-import '../../../protos.dart' as pb;
-import '../../model/gerrit/commit.dart';
-import '../../model/github/checks.dart' as cocoon_checks;
-import '../../request_handling/body.dart';
-import '../../request_handling/exceptions.dart';
-import '../../request_handling/subscription_handler.dart';
-import '../../service/config.dart';
-import '../../service/datastore.dart';
-import '../../service/gerrit_service.dart';
-import '../../service/github_checks_service.dart';
-import '../../service/logging.dart';
-import '../../service/scheduler.dart';
-
-// Filenames which are not actually tests.
-const List<String> kNotActuallyATest = <String>[
- 'packages/flutter/lib/src/gestures/hit_test.dart',
-];
-
-/// List of repos that require check for tests.
-Set<RepositorySlug> kNeedsTests = <RepositorySlug>{
- Config.engineSlug,
- Config.flutterSlug,
- Config.packagesSlug,
-};
-
-final RegExp kEngineTestRegExp = RegExp(r'(tests?|benchmarks?)\.(dart|java|mm|m|cc|sh|py)$');
-
-// Extentions for files that use // for single line comments.
-// See [_allChangesAreCodeComments] for more.
-@visibleForTesting
-const Set<String> knownCommentCodeExtensions = <String>{
- 'cc',
- 'cpp',
- 'dart',
- 'gradle',
- 'groovy',
- 'java',
- 'kt',
- 'm',
- 'mm',
- 'swift',
-};
-
-/// Subscription for processing GitHub webhooks.
-///
-/// The PubSub subscription is set up here:
-/// https://console.cloud.google.com/cloudpubsub/subscription/detail/github-webhooks-sub?project=flutter-dashboard&tab=overview
-///
-/// This endpoint enables Cocoon to recover from outages.
-///
-/// This endpoint takes in a POST request with the GitHub event JSON.
-// TODO(chillers): There's potential now to split this into seprate subscriptions
-// for various activities (such as infra vs releases). This would mitigate
-// breakages across Cocoon.
-@immutable
-class GithubWebhookSubscription extends SubscriptionHandler {
- /// Creates a subscription for processing GitHub webhooks.
- const GithubWebhookSubscription({
- required super.cache,
- required super.config,
- required this.scheduler,
- required this.gerritService,
- required this.commitService,
- this.githubChecksService,
- this.datastoreProvider = DatastoreService.defaultProvider,
- super.authProvider,
- }) : super(subscriptionName: 'github-webhooks-sub');
-
- /// Cocoon scheduler to trigger tasks against changes from GitHub.
- final Scheduler scheduler;
-
- /// To verify whether a commit was mirrored to GoB.
- final GerritService gerritService;
-
- /// Used to handle push events and create commits based on those events.
- final CommitService commitService;
-
- /// To provide build status updates to GitHub pull requests.
- final GithubChecksService? githubChecksService;
-
- final DatastoreServiceProvider datastoreProvider;
-
- @override
- Future<Body> post() async {
- if (message.data == null || message.data!.isEmpty) {
- log.warning('GitHub webhook message was empty. No-oping');
- return Body.empty;
- }
-
- final pb.GithubWebhookMessage webhook = pb.GithubWebhookMessage.fromJson(message.data!);
-
- log.fine('Processing ${webhook.event}');
- log.finest(webhook.payload);
- switch (webhook.event) {
- case 'pull_request':
- await _handlePullRequest(webhook.payload);
- break;
- case 'check_run':
- final Map<String, dynamic> event = jsonDecode(webhook.payload) as Map<String, dynamic>;
- final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(event);
- if (await scheduler.processCheckRun(checkRunEvent) == false) {
- throw InternalServerError('Failed to process check_run event. checkRunEvent: $checkRunEvent');
- }
- break;
- case 'push':
- final Map<String, dynamic> event = jsonDecode(webhook.payload) as Map<String, dynamic>;
- final String branch = event['ref'].split('/')[2]; // Eg: refs/heads/beta would return beta.
- final String repository = event['repository']['name'];
- // If the branch is beta/stable, then a commit wasn't created through a PR,
- // meaning the commit needs to be added to the datastore here instead.
- if (repository == 'flutter' && (branch == 'stable' || branch == 'beta')) {
- await commitService.handlePushGithubRequest(event);
- }
- break;
- case 'create':
- final CreateEvent createEvent = CreateEvent.fromJson(json.decode(webhook.payload) as Map<String, dynamic>);
- final RegExp candidateBranchRegex = RegExp(r'flutter-\d+\.\d+-candidate\.\d+');
- // Create a commit object for candidate branches in the datastore so
- // dart-internal builds that are triggered by the initial branch
- // creation have an associated commit.
- if (candidateBranchRegex.hasMatch(createEvent.ref!)) {
- log.fine('Branch ${createEvent.ref} is a candidate branch, creating new commit in the datastore');
- await commitService.handleCreateGithubRequest(createEvent);
- }
- }
-
- return Body.empty;
- }
-
- /// Handles a GitHub webhook with the event type "pull_request".
- ///
- /// Regarding merged pull request events: the commit must be mirrored to GoB
- /// before we can trigger postsubmit tasks. If the commit is not found, the
- /// event will be failed so it can be retried. As of Jan 26, 2023, the
- /// retention policy for Pub/Sub messages is 7 days. This event will be
- /// retried with exponential backoff within that time period. The GoB mirror
- /// should be caught up within that time frame via either the internal
- /// mirroring service or [VacuumGithubCommits].
- Future<void> _handlePullRequest(
- String rawRequest,
- ) async {
- final PullRequestEvent? pullRequestEvent = await _getPullRequestEvent(rawRequest);
- if (pullRequestEvent == null) {
- throw const BadRequestException('Expected pull request event.');
- }
- final String? eventAction = pullRequestEvent.action;
- final PullRequest pr = pullRequestEvent.pullRequest!;
-
- // See the API reference:
- // https://developer.github.com/v3/activity/events/types/#pullrequestevent
- // which unfortunately is a bit light on explanations.
- log.fine('Processing $eventAction for ${pr.htmlUrl}');
- switch (eventAction) {
- case 'closed':
- // If it was closed without merging, cancel any outstanding tryjobs.
- // We'll leave unfinished jobs if it was merged since we care about those
- // results.
- await scheduler.cancelPreSubmitTargets(
- pullRequest: pr,
- reason: (!pr.merged!) ? 'Pull request closed' : 'Pull request merged',
- );
-
- if (pr.merged!) {
- log.fine('Pull request ${pr.number} was closed and merged.');
- if (await _commitExistsInGob(pr)) {
- log.fine('Merged commit was found on GoB mirror. Scheduling postsubmit tasks...');
- return scheduler.addPullRequest(pr);
- }
- throw InternalServerError(
- '${pr.mergeCommitSha!} was not found on GoB. Failing so this event can be retried...',
- );
- }
- break;
- case 'edited':
- // Editing a PR should not trigger new jobs, but may update whether
- // it has tests.
- await _checkForTests(pullRequestEvent);
- break;
- case 'opened':
- case 'reopened':
- // These cases should trigger LUCI jobs. The closed event should happen
- // before these which should cancel all in progress checks.
- await _checkForTests(pullRequestEvent);
- await _scheduleIfMergeable(pullRequestEvent);
- await _tryReleaseApproval(pullRequestEvent);
- break;
- case 'labeled':
- break;
- case 'synchronize':
- // This indicates the PR has new commits. We need to cancel old jobs
- // and schedule new ones.
- await _scheduleIfMergeable(pullRequestEvent);
- break;
- // Ignore the rest of the events.
- case 'ready_for_review':
- case 'unlabeled':
- case 'assigned':
- case 'locked':
- case 'review_request_removed':
- case 'review_requested':
- case 'unassigned':
- case 'unlocked':
- break;
- }
- }
-
- Future<bool> _commitExistsInGob(PullRequest pr) async {
- final RepositorySlug slug = pr.base!.repo!.slug();
- final String sha = pr.mergeCommitSha!;
- final GerritCommit? gobCommit = await gerritService.findMirroredCommit(slug, sha);
- return gobCommit != null;
- }
-
- /// This method assumes that jobs should be cancelled if they are already
- /// runnning.
- Future<void> _scheduleIfMergeable(
- PullRequestEvent pullRequestEvent,
- ) async {
- final PullRequest pr = pullRequestEvent.pullRequest!;
- final RepositorySlug slug = pullRequestEvent.repository!.slug();
-
- log.info(
- 'Scheduling tasks if mergeable(${pr.mergeable}): owner=${slug.owner} repo=${slug.name} and pr=${pr.number}',
- );
-
- // The mergeable flag may be null. False indicates there's a merge conflict,
- // null indicates unknown. Err on the side of allowing the job to run.
- if (pr.mergeable == false) {
- final RepositorySlug slug = pullRequestEvent.repository!.slug();
- final GitHub gitHubClient = await config.createGitHubClient(pullRequest: pr);
- final String body = config.mergeConflictPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- return;
- }
-
- await scheduler.triggerPresubmitTargets(pullRequest: pr);
- }
-
- /// Release tooling generates cherrypick pull requests that should be granted an approval.
- Future<void> _tryReleaseApproval(
- PullRequestEvent pullRequestEvent,
- ) async {
- final PullRequest pr = pullRequestEvent.pullRequest!;
- final RepositorySlug slug = pullRequestEvent.repository!.slug();
-
- final String defaultBranch = Config.defaultBranch(slug);
- final String? branch = pr.base?.ref;
- if (branch == null || branch.contains(defaultBranch)) {
- // This isn't a release branch PR
- return;
- }
-
- final List<String> releaseAccounts = await config.releaseAccounts;
- if (releaseAccounts.contains(pr.user?.login) == false) {
- // The author isn't in the list of release accounts, do nothing
- return;
- }
-
- final GitHub gitHubClient = config.createGitHubClientWithToken(await config.githubOAuthToken);
- final CreatePullRequestReview review = CreatePullRequestReview(slug.owner, slug.name, pr.number!, 'APPROVE');
- await gitHubClient.pullRequests.createReview(slug, review);
- }
-
- Future<void> _checkForTests(PullRequestEvent pullRequestEvent) async {
- final PullRequest pr = pullRequestEvent.pullRequest!;
- final String? eventAction = pullRequestEvent.action;
- final RepositorySlug slug = pr.base!.repo!.slug();
- final bool isTipOfTree = pr.base!.ref == Config.defaultBranch(slug);
- final GitHub gitHubClient = await config.createGitHubClient(pullRequest: pr);
- await _validateRefs(gitHubClient, pr);
- if (kNeedsTests.contains(slug) && isTipOfTree) {
- switch (slug.name) {
- case 'flutter':
- return _applyFrameworkRepoLabels(gitHubClient, eventAction, pr);
- case 'engine':
- return _applyEngineRepoLabels(gitHubClient, eventAction, pr);
- case 'packages':
- return _applyPackageTestChecks(gitHubClient, eventAction, pr);
- }
- }
- }
-
- Future<void> _applyFrameworkRepoLabels(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
- if (pr.user!.login == 'engine-flutter-autoroll') {
- return;
- }
-
- final RepositorySlug slug = pr.base!.repo!.slug();
- log.info('Applying framework repo labels for: owner=${slug.owner} repo=${slug.name} and pr=${pr.number}');
- final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
-
- final Set<String> labels = <String>{};
- bool hasTests = false;
- bool needsTests = false;
-
- await for (PullRequestFile file in files) {
- final String filename = file.filename!;
-
- if (_fileContainsAddedCode(file) &&
- !_isTestExempt(filename) &&
- !filename.startsWith('dev/bots/') &&
- !filename.endsWith('.gitignore')) {
- needsTests = !_allChangesAreCodeComments(file);
- }
-
- // Check to see if tests were submitted with this PR.
- if (_isATest(filename)) {
- hasTests = true;
- }
- }
-
- if (pr.user!.login == 'fluttergithubbot') {
- needsTests = false;
- labels.addAll(<String>['c: tech-debt', 'c: flake']);
- }
-
- if (labels.isNotEmpty) {
- await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, labels.toList());
- }
-
- // We do not need to add test labels if this is an auto roller author.
- if (config.rollerAccounts.contains(pr.user!.login)) {
- return;
- }
-
- if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
- final String body = config.missingTestsPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- }
- }
-
- bool _isATest(String filename) {
- if (kNotActuallyATest.any(filename.endsWith)) {
- return false;
- }
- // Check for Objective-C tests which end in either "Tests.m" or "Test.m"
- // in the "dev" directory.
- final RegExp objectiveCTestRegex = RegExp(r'.*dev\/.*Test[s]?\.m$');
- return filename.endsWith('_test.dart') ||
- filename.endsWith('.expect') ||
- filename.contains('test_fixes') ||
- // Include updates to test utilities or test data
- filename.contains('packages/flutter_tools/test/') ||
- filename.startsWith('dev/bots/analyze.dart') ||
- filename.startsWith('dev/bots/test.dart') ||
- filename.startsWith('dev/devicelab/bin/tasks') ||
- filename.startsWith('dev/devicelab/lib/tasks') ||
- filename.startsWith('dev/benchmarks') ||
- objectiveCTestRegex.hasMatch(filename);
- }
-
- /// Returns true if changes to [filename] are exempt from the testing
- /// requirement, across repositories.
- bool _isTestExempt(String filename) {
- return filename.contains('.ci.yaml') ||
- filename.contains('analysis_options.yaml') ||
- filename.contains('AUTHORS') ||
- filename.contains('CODEOWNERS') ||
- filename.contains('TESTOWNERS') ||
- filename.contains('pubspec.yaml') ||
- // Exempt categories.
- filename.contains('.github/') ||
- filename.endsWith('.md') ||
- // Exempt paths.
- filename.startsWith('dev/devicelab/lib/versions/gallery.dart') ||
- filename.startsWith('dev/integration_tests') ||
- filename.startsWith('impeller/fixtures') ||
- filename.startsWith('impeller/golden_tests') ||
- filename.startsWith('impeller/playground') ||
- filename.startsWith('shell/platform/embedder/tests') ||
- filename.startsWith('shell/platform/embedder/fixtures');
- }
-
- Future<void> _applyEngineRepoLabels(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
- // Do not apply the test labels for the autoroller accounts.
- if (pr.user!.login == 'skia-flutter-autoroll') {
- return;
- }
-
- final RepositorySlug slug = pr.base!.repo!.slug();
- final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
- bool hasTests = false;
- bool needsTests = false;
-
- await for (PullRequestFile file in files) {
- final String filename = file.filename!.toLowerCase();
- if (_fileContainsAddedCode(file) && filename.endsWith('.dart') ||
- filename.endsWith('.mm') ||
- filename.endsWith('.m') ||
- filename.endsWith('.java') ||
- filename.endsWith('.cc')) {
- needsTests = !_allChangesAreCodeComments(file);
- }
-
- if (kEngineTestRegExp.hasMatch(filename)) {
- hasTests = true;
- }
- }
-
- // We do not need to add test labels if this is an auto roller author.
- if (config.rollerAccounts.contains(pr.user!.login)) {
- return;
- }
-
- if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
- final String body = config.missingTestsPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- }
- }
-
- bool _fileContainsAddedCode(PullRequestFile file) {
- // When null, do not assume 0 lines have been added.
- final int linesAdded = file.additionsCount ?? 1;
- final int linesDeleted = file.deletionsCount ?? 0;
- final int linesTotal = file.changesCount ?? linesDeleted + linesAdded;
- return linesAdded > 0 || linesDeleted != linesTotal;
- }
-
- // Runs automated test checks for both flutter/packages.
- Future<void> _applyPackageTestChecks(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
- final RepositorySlug slug = pr.base!.repo!.slug();
- final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
- bool hasTests = false;
- bool needsTests = false;
-
- await for (PullRequestFile file in files) {
- final String filename = file.filename!;
-
- if (_fileContainsAddedCode(file) &&
- !_isTestExempt(filename) &&
- !filename.contains('.ci/') &&
- // Custom package-specific test runners. These do not count as tests
- // for the purposes of testing a change that otherwise needs tests,
- // but since they are the driver for tests they don't need test
- // coverage.
- !filename.endsWith('tool/run_tests.dart') &&
- !filename.endsWith('run_tests.sh')) {
- needsTests = !_allChangesAreCodeComments(file);
- }
- // See https://github.com/flutter/flutter/wiki/Plugin-Tests for discussion
- // of various plugin test types and locations.
- if (filename.endsWith('_test.dart') ||
- // Native iOS/macOS tests.
- filename.contains('RunnerTests/') ||
- filename.contains('RunnerUITests/') ||
- // Native Android tests.
- filename.contains('android/src/test/') ||
- filename.contains('androidTest/') ||
- // Native Linux tests.
- filename.endsWith('_test.cc') ||
- // Native Windows tests.
- filename.endsWith('_test.cpp') ||
- // Pigeon native tests.
- filename.contains('/platform_tests/') ||
- // Test files in package-specific test folders.
- filename.contains('go_router/test_fixes/') ||
- filename.contains('go_router_builder/test_inputs/')) {
- hasTests = true;
- }
- }
-
- // We do not need to add test labels if this is an auto roller author.
- if (config.rollerAccounts.contains(pr.user!.login)) {
- return;
- }
-
- if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
- final String body = config.missingTestsPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- }
- }
-
- /// Validate the base and head refs of the PR.
- Future<void> _validateRefs(
- GitHub gitHubClient,
- PullRequest pr,
- ) async {
- final RepositorySlug slug = pr.base!.repo!.slug();
- String body;
- const List<String> releaseChannels = <String>[
- 'stable',
- 'beta',
- 'dev',
- ];
- // Close PRs that use a release branch as a source.
- if (releaseChannels.contains(pr.head!.ref)) {
- body = config.wrongHeadBranchPullRequestMessage(pr.head!.ref!);
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.pullRequests.edit(
- slug,
- pr.number!,
- state: 'closed',
- );
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- return;
- }
- final String defaultBranchName = Config.defaultBranch(pr.base!.repo!.slug());
- final String baseName = pr.base!.ref!;
- if (baseName == defaultBranchName) {
- return;
- }
- if (_isReleaseBranch(pr)) {
- body = config.releaseBranchPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- return;
- }
-
- // For repos migrated to main, close PRs opened against master.
- final bool isMaster = pr.base?.ref == 'master';
- final bool isMigrated = defaultBranchName == 'main';
- // PRs should never be open to "beta" or "stable."
- final bool isReleaseChannelBranch = releaseChannels.contains(pr.base?.ref);
- if ((isMaster && isMigrated) || isReleaseChannelBranch) {
- body = _getWrongBaseComment(base: baseName, defaultBranch: defaultBranchName);
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.pullRequests.edit(
- slug,
- pr.number!,
- base: Config.defaultBranch(slug),
- );
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- }
- }
-
- bool _isReleaseBranch(PullRequest pr) {
- final String defaultBranchName = Config.defaultBranch(pr.base!.repo!.slug());
- final String baseName = pr.base!.ref!;
-
- if (baseName == defaultBranchName) {
- return false;
- }
- // Check if branch name confroms to the format flutter-x.x-candidate.x,
- // A pr with conforming branch name is likely to be intended
- // for a release branch, whereas a pr with non conforming name is likely
- // caused by user misoperations, in which case bot
- // will suggest open pull request against default branch instead.
- final RegExp candidateTest = RegExp(r'flutter-\d+\.\d+-candidate\.\d+');
- if (candidateTest.hasMatch(baseName) && candidateTest.hasMatch(pr.head!.ref!)) {
- return true;
- }
- return false;
- }
-
- Future<bool> _alreadyCommented(
- GitHub gitHubClient,
- PullRequest pr,
- String message,
- ) async {
- final Stream<IssueComment> comments = gitHubClient.issues.listCommentsByIssue(pr.base!.repo!.slug(), pr.number!);
- await for (IssueComment comment in comments) {
- if (comment.body != null && comment.body!.contains(message)) {
- return true;
- }
- }
- return false;
- }
-
- String _getWrongBaseComment({
- required String base,
- required String defaultBranch,
- }) {
- final String messageTemplate = config.wrongBaseBranchPullRequestMessage;
- return messageTemplate.replaceAll('{{target_branch}}', base).replaceAll('{{default_branch}}', defaultBranch);
- }
-
- Future<PullRequestEvent?> _getPullRequestEvent(String request) async {
- try {
- return PullRequestEvent.fromJson(json.decode(request) as Map<String, dynamic>);
- } on FormatException {
- return null;
- }
- }
-
- /// Returns true if the changes to [file] are all code comments.
- ///
- /// If that cannot be determined with confidence, returns false. False
- /// negatives (e.g., for /* */-style multi-line comments) should be expected.
- bool _allChangesAreCodeComments(PullRequestFile file) {
- final int? linesAdded = file.additionsCount;
- final int? linesDeleted = file.deletionsCount;
- final String? patch = file.patch;
- // If information is missing, err or the side of assuming it's a non-comment
- // change.
- if (linesAdded == null || linesDeleted == null || patch == null) {
- return false;
- }
-
- final String filename = file.filename!;
- final String? extension = filename.contains('.') ? filename.split('.').last.toLowerCase() : null;
- if (extension == null || !knownCommentCodeExtensions.contains(extension)) {
- return false;
- }
-
- // Only handles single-line comments; identifying multi-line comments
- // would require the full file and non-trivial parsing. Also doesn't handle
- // end-of-line comments (e.g., "int x = 0; // Info about x").
- final RegExp commentRegex = RegExp(r'^[+-]\s*//');
- final RegExp onlyWhitespaceRegex = RegExp(r'^[+-]\s*$');
- for (String line in patch.split('\n')) {
- if (!line.startsWith('+') && !line.startsWith('-')) {
- continue;
- }
-
- if (onlyWhitespaceRegex.hasMatch(line)) {
- // whitespace only changes don't require tests
- continue;
- }
-
- if (!commentRegex.hasMatch(line)) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/github_rate_limit_status.dart b/app_dart/lib/src/request_handlers/github_rate_limit_status.dart
deleted file mode 100644
index d8cdc00..0000000
--- a/app_dart/lib/src/request_handlers/github_rate_limit_status.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 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:meta/meta.dart';
-
-import '../foundation/utils.dart';
-import '../request_handling/body.dart';
-import '../request_handling/request_handler.dart';
-import '../service/github_service.dart';
-import '../service/logging.dart';
-
-@immutable
-
-/// Endpoint to collect the current GitHub API quota usage of the flutter-dashboard app.
-///
-/// This endpoint pushes data to BigQuery for metric collection to analyze usage over time. There
-/// is a cron job set to run every minute, behind a [CacheRequestHandler] to ensure there exists
-/// at most one entry per repo per minute.
-///
-/// BigQuery entries contain the following fields:
-/// `timestamp`: [DateTime] of this entry.
-/// `limit`: Total API calls allowed on flutter-dashboard.
-/// `remaining`: Total number of API calls remaining before flutter-dashboard is blocked from sending further requests.
-/// `resets`: [DateTime] when [remaining] will reset back to [limit].
-class GithubRateLimitStatus extends RequestHandler<Body> {
- const GithubRateLimitStatus({
- required super.config,
- });
-
- @override
- Future<Body> get() async {
- final GithubService githubService = await config.createDefaultGitHubService();
- final Map<String, dynamic> quotaUsage = (await githubService.getRateLimit()).toJson();
- quotaUsage['timestamp'] = DateTime.now().toIso8601String();
-
- final int remainingQuota = quotaUsage['remaining'] as int;
- final int quotaLimit = quotaUsage['limit'] as int;
- const double githubQuotaUsageSLO = 0.5;
- if (remainingQuota < githubQuotaUsageSLO * quotaLimit) {
- log.warning(
- 'Remaining GitHub quota is $remainingQuota, which is less than quota usage SLO ${githubQuotaUsageSLO * quotaLimit} (${githubQuotaUsageSLO * 100}% of the limit $quotaLimit)).',
- );
- }
-
- /// Insert quota usage to BigQuery
- const String githubQuotaTable = 'GithubQuotaUsage';
- await insertBigquery(githubQuotaTable, quotaUsage, await config.createTabledataResourceApi());
- return Body.forJson(quotaUsage);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/github_webhook.dart b/app_dart/lib/src/request_handlers/github_webhook.dart
deleted file mode 100644
index 0d3e1d6..0000000
--- a/app_dart/lib/src/request_handlers/github_webhook.dart
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2019 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 'dart:convert';
-import 'package:crypto/crypto.dart';
-import '../model/proto/protos.dart' as pb;
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../request_handling/pubsub.dart';
-import '../request_handling/request_handler.dart';
-import '../service/logging.dart';
-
-/// The [RequestHandler] for processing GitHub webhooks and publishing valid events to PubSub.
-///
-/// Requests are only published as a [GithubWebhookMessage] iff they contain:
-/// 1. Event type from the header `X-GitHub-Event`
-/// 2. Event payload that was HMAC authenticated
-class GithubWebhook extends RequestHandler<Body> {
- GithubWebhook({
- required super.config,
- required this.pubsub,
- required this.secret,
- required this.topic,
- });
-
- final PubSub pubsub;
-
- /// PubSub topic to publish authenticated requests to.
- final String topic;
-
- /// Future that resolves to the GitHub apps webhook secret.
- final Future<String> secret;
-
- @override
- Future<Body> post() async {
- final String? event = request!.headers.value('X-GitHub-Event');
-
- if (event == null || request!.headers.value('X-Hub-Signature') == null) {
- throw const BadRequestException('Missing required headers.');
- }
- final List<int> requestBytes = await request!.expand((_) => _).toList();
- final String? hmacSignature = request!.headers.value('X-Hub-Signature');
- await _validateRequest(hmacSignature, requestBytes);
-
- final String requestString = utf8.decode(requestBytes);
-
- final pb.GithubWebhookMessage message = pb.GithubWebhookMessage.create()
- ..event = event
- ..payload = requestString;
- log.fine(message);
- await pubsub.publish(topic, message.writeToJsonMap());
-
- return Body.empty;
- }
-
- /// Ensures the signature provided for the given payload matches what is expected.
- ///
- /// The expected key is the sha1 hash of the payload using the private key of the GitHub app.
- Future<void> _validateRequest(
- String? signature,
- List<int> requestBody,
- ) async {
- final String rawKey = await secret;
- final List<int> key = utf8.encode(rawKey);
- final Hmac hmac = Hmac(sha1, key);
- final Digest digest = hmac.convert(requestBody);
- final String bodySignature = 'sha1=$digest';
- if (bodySignature != signature) {
- throw const Forbidden('X-Hub-Signature does not match expected value');
- }
- }
-}
diff --git a/app_dart/lib/src/request_handlers/postsubmit_luci_subscription.dart b/app_dart/lib/src/request_handlers/postsubmit_luci_subscription.dart
deleted file mode 100644
index 1a70e80..0000000
--- a/app_dart/lib/src/request_handlers/postsubmit_luci_subscription.dart
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2019 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/ci_yaml.dart';
-import 'package:gcloud/db.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/appengine/task.dart';
-import '../model/luci/push_message.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../request_handling/subscription_handler.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-import '../service/github_checks_service.dart';
-import '../service/scheduler.dart';
-
-/// An endpoint for listening to build updates for postsubmit builds.
-///
-/// The PubSub subscription is set up here:
-/// https://cloud.google.com/cloudpubsub/subscription/detail/luci-postsubmit?project=flutter-dashboard&tab=overview
-///
-/// This endpoint is responsible for updating Datastore with the result of builds from LUCI.
-@immutable
-class PostsubmitLuciSubscription extends SubscriptionHandler {
- /// Creates an endpoint for listening to LUCI status updates.
- const PostsubmitLuciSubscription({
- required super.cache,
- required super.config,
- super.authProvider,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- required this.scheduler,
- required this.githubChecksService,
- }) : super(subscriptionName: 'luci-postsubmit');
-
- final DatastoreServiceProvider datastoreProvider;
- final Scheduler scheduler;
- final GithubChecksService githubChecksService;
-
- @override
- Future<Body> post() async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- final BuildPushMessage buildPushMessage = BuildPushMessage.fromPushMessage(message);
- log.fine('userData=${buildPushMessage.userData}');
- log.fine('Updating buildId=${buildPushMessage.build?.id} for result=${buildPushMessage.build?.result}');
- if (buildPushMessage.userData.isEmpty) {
- log.fine('User data is empty');
- return Body.empty;
- }
-
- final String? rawTaskKey = buildPushMessage.userData['task_key'] as String?;
- final String? rawCommitKey = buildPushMessage.userData['commit_key'] as String?;
- if (rawCommitKey == null) {
- throw const BadRequestException('userData does not contain commit_key');
- }
- final Build? build = buildPushMessage.build;
- if (build == null) {
- log.warning('Build is null');
- return Body.empty;
- }
- final Key<String> commitKey = Key<String>(Key<dynamic>.emptyKey(Partition(null)), Commit, rawCommitKey);
- Task? task;
- if (rawTaskKey == null || rawTaskKey.isEmpty || rawTaskKey == 'null') {
- log.fine('Pulling builder name from parameters_json...');
- log.fine(build.buildParameters);
- final String? taskName = build.buildParameters?['builder_name'] as String?;
- if (taskName == null || taskName.isEmpty) {
- throw const BadRequestException('task_key is null and parameters_json does not contain the builder name');
- }
- final List<Task> tasks = await datastore.queryRecentTasksByName(name: taskName).toList();
- task = tasks.singleWhere((Task task) => task.parentKey?.id == commitKey.id);
- } else {
- log.fine('Looking up key...');
- final int taskId = int.parse(rawTaskKey);
- final Key<int> taskKey = Key<int>(commitKey, Task, taskId);
- task = await datastore.lookupByValue<Task>(taskKey);
- }
- log.fine('Found $task');
-
- if (_shouldUpdateTask(build, task)) {
- final String oldTaskStatus = task.status;
- task.updateFromBuild(build);
- await datastore.insert(<Task>[task]);
- log.fine('Updated datastore from $oldTaskStatus to ${task.status}');
- } else {
- log.fine('skip processing for build with status scheduled or task with status finished.');
- }
-
- final Commit commit = await datastore.lookupByValue<Commit>(commitKey);
- final CiYaml ciYaml = await scheduler.getCiYaml(commit);
- final List<Target> postsubmitTargets = ciYaml.postsubmitTargets;
- if (!postsubmitTargets.any((element) => element.value.name == task!.name)) {
- log.warning('Target ${task.name} has been deleted from TOT. Skip updating.');
- return Body.empty;
- }
- final Target target = postsubmitTargets.singleWhere((Target target) => target.value.name == task!.name);
- if (task.status == Task.statusFailed ||
- task.status == Task.statusInfraFailure ||
- task.status == Task.statusCancelled) {
- log.fine('Trying to auto-retry...');
- final bool retried = await scheduler.luciBuildService.checkRerunBuilder(
- commit: commit,
- target: target,
- task: task,
- datastore: datastore,
- );
- log.info('Retried: $retried');
- }
-
- // Only update GitHub checks if target is not bringup
- if (target.value.bringup == false && config.postsubmitSupportedRepos.contains(target.slug)) {
- log.info('Updating check status for ${target.getTestName}');
- await githubChecksService.updateCheckStatus(
- buildPushMessage,
- scheduler.luciBuildService,
- commit.slug,
- );
- }
-
- return Body.empty;
- }
-
- // No need to update task in datastore if
- // 1) the build is `scheduled`. Task is marked as `In Progress`
- // whenever scheduled, either from scheduler/backfiller/rerun. We need to update
- // task in datastore only for
- // a) `started`: update info like builder number.
- // b) `completed`: update info like status.
- // 2) the task is already completed.
- // The task may have been marked as completed from test framework via update-task-status API.
- bool _shouldUpdateTask(Build build, Task task) {
- return build.status != Status.scheduled && !Task.finishedStatusValues.contains(task.status);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/presubmit_luci_subscription.dart b/app_dart/lib/src/request_handlers/presubmit_luci_subscription.dart
deleted file mode 100644
index 63d4841..0000000
--- a/app_dart/lib/src/request_handlers/presubmit_luci_subscription.dart
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2019 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:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/ci_yaml/ci_yaml.dart';
-import '../model/ci_yaml/target.dart';
-import '../model/luci/push_message.dart';
-import '../request_handling/authentication.dart';
-import '../request_handling/body.dart';
-import '../request_handling/subscription_handler.dart';
-import '../service/buildbucket.dart';
-import '../service/config.dart';
-import '../service/github_checks_service.dart';
-import '../service/logging.dart';
-import '../service/luci_build_service.dart';
-import '../service/scheduler.dart';
-
-/// An endpoint for listening to LUCI status updates for scheduled builds.
-///
-/// [ScheduleBuildRequest.notify] property is set to tell LUCI to use this
-/// PubSub topic. LUCI then publishes updates about build status to that topic,
-/// which we listen to on the github-updater subscription. When new messages
-/// arrive, they are posted to this web service.
-///
-/// The PubSub subscription is set up here:
-/// https://console.cloud.google.com/cloudpubsub/subscription/detail/github-updater?project=flutter-dashboard
-///
-/// This endpoint is responsible for updating GitHub with the status of
-/// completed builds from LUCI.
-@immutable
-class PresubmitLuciSubscription extends SubscriptionHandler {
- /// Creates an endpoint for listening to LUCI status updates.
- const PresubmitLuciSubscription({
- required super.cache,
- required super.config,
- required this.buildBucketClient,
- required this.scheduler,
- required this.luciBuildService,
- required this.githubChecksService,
- AuthenticationProvider? authProvider,
- }) : super(subscriptionName: 'github-updater');
-
- final BuildBucketClient buildBucketClient;
- final LuciBuildService luciBuildService;
- final GithubChecksService githubChecksService;
- final Scheduler scheduler;
-
- @override
- Future<Body> post() async {
- RepositorySlug slug;
- final BuildPushMessage buildPushMessage = BuildPushMessage.fromPushMessage(message);
- final Build build = buildPushMessage.build!;
- final String builderName = build.tagsByName('builder').single;
- log.fine('Available tags: ${build.tags.toString()}');
- // Skip status update if we can not get the sha tag.
- if (build.tagsByName('buildset').isEmpty) {
- log.warning('Buildset tag not included, skipping Status Updates');
- return Body.empty;
- }
- log.fine('Setting status: ${buildPushMessage.toJson()} for $builderName');
- if (buildPushMessage.userData.containsKey('repo_owner') && buildPushMessage.userData.containsKey('repo_name')) {
- // Message is coming from a github checks api enabled repo. We need to
- // create the slug from the data in the message and send the check status
- // update.
- slug = RepositorySlug(
- buildPushMessage.userData['repo_owner'] as String,
- buildPushMessage.userData['repo_name'] as String,
- );
- bool rescheduled = false;
- if (githubChecksService.taskFailed(buildPushMessage)) {
- final int currentAttempt = githubChecksService.currentAttempt(buildPushMessage);
- final int maxAttempt = await _getMaxAttempt(buildPushMessage, slug, builderName);
- if (currentAttempt < maxAttempt) {
- rescheduled = true;
- log.fine('Rerun a failed task: $builderName');
- await luciBuildService.rescheduleBuild(
- builderName: builderName,
- buildPushMessage: buildPushMessage,
- rescheduleAttempt: currentAttempt + 1,
- );
- }
- }
- await githubChecksService.updateCheckStatus(
- buildPushMessage,
- luciBuildService,
- slug,
- rescheduled: rescheduled,
- );
- } else {
- log.shout('This repo does not support checks API');
- }
- return Body.empty;
- }
-
- /// Gets target's allowed reschedule attempt.
- ///
- /// Each target can define their own allowed max number of reschedule attemp, and it
- /// is defined as a property `presubmit_max_attempts`.
- ///
- /// If not property is defined, the target doesn't allow a reschedule after failures.
- /// Typically the property will be used for targets that are likely flaky.
- Future<int> _getMaxAttempt(
- BuildPushMessage buildPushMessage,
- RepositorySlug slug,
- String builderName,
- ) async {
- final Commit commit = Commit(
- branch: buildPushMessage.userData['commit_branch'] as String,
- repository: slug.fullName,
- sha: buildPushMessage.userData['commit_sha'] as String,
- );
- late CiYaml ciYaml;
- if (commit.branch == Config.defaultBranch(commit.slug)) {
- ciYaml = await scheduler.getCiYaml(commit, validate: true);
- } else {
- ciYaml = await scheduler.getCiYaml(commit);
- }
-
- // Do not block on the target not found.
- if (!ciYaml.presubmitTargets.any((element) => element.value.name == builderName)) {
- // do not reschedule
- log.warning('Did not find builder with name: $builderName in ciYaml for ${commit.sha}');
- final List<String> availableBuilderList = ciYaml.presubmitTargets.map((Target e) => e.value.name).toList();
- log.warning('ciYaml presubmit targets found: $availableBuilderList');
- return 1;
- }
-
- final Target target = ciYaml.presubmitTargets.where((element) => element.value.name == builderName).single;
- final Map<String, Object> properties = target.getProperties();
- if (!properties.containsKey('presubmit_max_attempts')) {
- return 1;
- }
- return properties['presubmit_max_attempts'] as int;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/push_build_status_to_github.dart b/app_dart/lib/src/request_handlers/push_build_status_to_github.dart
deleted file mode 100644
index 7c0de91..0000000
--- a/app_dart/lib/src/request_handlers/push_build_status_to_github.dart
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2019 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:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../../cocoon_service.dart';
-import '../model/appengine/github_build_status_update.dart';
-import '../request_handling/api_request_handler.dart';
-import '../service/build_status_provider.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-
-@immutable
-class PushBuildStatusToGithub extends ApiRequestHandler<Body> {
- const PushBuildStatusToGithub({
- required super.config,
- required super.authenticationProvider,
- @visibleForTesting DatastoreServiceProvider? datastoreProvider,
- @visibleForTesting BuildStatusServiceProvider? buildStatusServiceProvider,
- }) : datastoreProvider = datastoreProvider ?? DatastoreService.defaultProvider,
- buildStatusServiceProvider = buildStatusServiceProvider ?? BuildStatusService.defaultProvider;
-
- final BuildStatusServiceProvider buildStatusServiceProvider;
- final DatastoreServiceProvider datastoreProvider;
- static const String fullNameRepoParam = 'repo';
-
- @override
- Future<Body> get() async {
- if (authContext!.clientContext.isDevelopmentEnvironment) {
- // Don't push GitHub status from the local dev server.
- log.fine('GitHub statuses are not pushed from local dev environments');
- return Body.empty;
- }
-
- final String repository = request!.uri.queryParameters[fullNameRepoParam] ?? 'flutter/flutter';
- final RepositorySlug slug = RepositorySlug.full(repository);
- final DatastoreService datastore = datastoreProvider(config.db);
- final BuildStatusService buildStatusService = buildStatusServiceProvider(datastore);
-
- final BuildStatus status = (await buildStatusService.calculateCumulativeStatus(slug))!;
- await _insertBigquery(slug, status.githubStatus, Config.defaultBranch(slug), config);
- await _updatePRs(slug, status.githubStatus, datastore);
- log.fine('All the PRs for $repository have been updated with $status');
-
- return Body.empty;
- }
-
- Future<void> _updatePRs(RepositorySlug slug, String status, DatastoreService datastore) async {
- final GitHub github = await config.createGitHubClient(slug: slug);
- final List<GithubBuildStatusUpdate> updates = <GithubBuildStatusUpdate>[];
- await for (PullRequest pr in github.pullRequests.list(slug)) {
- // Tree status is only put on PRs merging into ToT.
- if (pr.base!.ref != Config.defaultBranch(slug)) {
- log.fine('This PR is not staged to land on ${Config.defaultBranch(slug)}, skipping.');
- continue;
- }
- final GithubBuildStatusUpdate update = await datastore.queryLastStatusUpdate(slug, pr);
- if (update.status != status) {
- log.fine('Updating status of ${slug.fullName}#${pr.number} from ${update.status} to $status');
- final CreateStatus request = CreateStatus(status);
- request.targetUrl = 'https://flutter-dashboard.appspot.com/#/build?repo=${slug.name}';
- request.context = 'tree-status';
- if (status != GithubBuildStatusUpdate.statusSuccess) {
- request.description = config.flutterBuildDescription;
- }
- try {
- await github.repositories.createStatus(slug, pr.head!.sha!, request);
- update.status = status;
- update.updates = (update.updates ?? 0) + 1;
- update.updateTimeMillis = DateTime.now().millisecondsSinceEpoch;
- updates.add(update);
- } catch (error) {
- log.severe('Failed to post status update to ${slug.fullName}#${pr.number}: $error');
- }
- }
- }
- await datastore.insert(updates);
- }
-
- Future<void> _insertBigquery(RepositorySlug slug, String status, String branch, Config config) async {
- const String bigqueryTableName = 'BuildStatus';
- final Map<String, dynamic> bigqueryData = <String, dynamic>{
- 'Timestamp': DateTime.now().millisecondsSinceEpoch,
- 'Status': status,
- 'Branch': branch,
- 'Repo': slug.name,
- };
- await insertBigquery(bigqueryTableName, bigqueryData, await config.createTabledataResourceApi());
- }
-}
diff --git a/app_dart/lib/src/request_handlers/push_gold_status_to_github.dart b/app_dart/lib/src/request_handlers/push_gold_status_to_github.dart
deleted file mode 100644
index d3fdcd9..0000000
--- a/app_dart/lib/src/request_handlers/push_gold_status_to_github.dart
+++ /dev/null
@@ -1,376 +0,0 @@
-// Copyright 2020 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 'dart:convert';
-import 'dart:io';
-
-import 'package:github/github.dart';
-import 'package:gql/language.dart' as lang;
-import 'package:graphql/client.dart';
-import 'package:http/http.dart' as http;
-import 'package:meta/meta.dart';
-
-import '../model/appengine/github_gold_status_update.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-
-@immutable
-class PushGoldStatusToGithub extends ApiRequestHandler<Body> {
- PushGoldStatusToGithub({
- required super.config,
- required super.authenticationProvider,
- @visibleForTesting DatastoreServiceProvider? datastoreProvider,
- http.Client? goldClient,
- this.ingestionDelay = const Duration(seconds: 10),
- }) : datastoreProvider = datastoreProvider ?? DatastoreService.defaultProvider,
- goldClient = goldClient ?? http.Client();
-
- final DatastoreServiceProvider datastoreProvider;
- final http.Client goldClient;
- final Duration ingestionDelay;
-
- @override
- Future<Body> get() async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- if (authContext!.clientContext.isDevelopmentEnvironment) {
- // Don't push gold status from the local dev server.
- return Body.empty;
- }
-
- await _sendStatusUpdates(datastore, Config.flutterSlug);
- await _sendStatusUpdates(datastore, Config.engineSlug);
-
- return Body.empty;
- }
-
- Future<void> _sendStatusUpdates(
- DatastoreService datastore,
- RepositorySlug slug,
- ) async {
- final GitHub gitHubClient = await config.createGitHubClient(slug: slug);
- final List<GithubGoldStatusUpdate> statusUpdates = <GithubGoldStatusUpdate>[];
- log.fine('Beginning Gold checks...');
- await for (PullRequest pr in gitHubClient.pullRequests.list(slug)) {
- assert(pr.number != null);
- // Get last known Gold status from datastore.
- final GithubGoldStatusUpdate lastUpdate = await datastore.queryLastGoldUpdate(slug, pr);
- CreateStatus statusRequest;
-
- log.fine('Last known Gold status for $slug#${pr.number} was with sha: '
- '${lastUpdate.head}, status: ${lastUpdate.status}, description: ${lastUpdate.description}');
-
- if (lastUpdate.status == GithubGoldStatusUpdate.statusCompleted && lastUpdate.head == pr.head!.sha) {
- log.fine('Completed status already reported for this commit.');
- // We have already seen this commit and it is completed or, this is not
- // a change staged to land on master, which we should ignore.
- continue;
- }
-
- final String defaultBranch = Config.defaultBranch(slug);
- if (pr.base!.ref != defaultBranch) {
- log.fine('This change is not staged to land on $defaultBranch, skipping.');
- // This is potentially a release branch, or another change not landing
- // on master, we don't need a Gold check.
- continue;
- }
-
- if (pr.draft!) {
- log.fine('This pull request is a draft.');
- // We don't want to query Gold while a PR is in a draft state, and we
- // don't want to needlessly hold a pending state either.
- // If a PR has been marked `draft` after the fact, and there has not
- // been a new commit, we cannot rescind a previously posted status, so
- // if it is already pending, we should make the contributor aware of
- // that fact.
- if (lastUpdate.status == GithubGoldStatusUpdate.statusRunning &&
- lastUpdate.head == pr.head!.sha &&
- !await _alreadyCommented(gitHubClient, pr, slug, config.flutterGoldDraftChange)) {
- await gitHubClient.issues
- .createComment(slug, pr.number!, config.flutterGoldDraftChange + config.flutterGoldAlertConstant(slug));
- }
- continue;
- }
-
- log.fine('Querying builds for pull request #${pr.number} with sha: ${lastUpdate.head}...');
- final GraphQLClient gitHubGraphQLClient = await config.createGitHubGraphQLClient();
- final List<String> incompleteChecks = <String>[];
- bool runsGoldenFileTests = false;
- final Map<String, dynamic> data = (await _queryGraphQL(
- gitHubGraphQLClient,
- slug,
- pr.number!,
- ))!;
- final Map<String, dynamic> prData = data['repository']['pullRequest'] as Map<String, dynamic>;
- final Map<String, dynamic> commit = prData['commits']['nodes'].single['commit'] as Map<String, dynamic>;
- List<Map<String, dynamic>>? checkRuns;
- if (commit['checkSuites']['nodes'] != null && (commit['checkSuites']['nodes'] as List<dynamic>).isNotEmpty) {
- checkRuns =
- (commit['checkSuites']['nodes']?.first['checkRuns']['nodes'] as List<dynamic>).cast<Map<String, dynamic>>();
- }
- checkRuns = checkRuns ?? <Map<String, dynamic>>[];
- log.fine('This PR has ${checkRuns.length} checks.');
- for (Map<String, dynamic> checkRun in checkRuns) {
- log.fine('Check run: $checkRun');
- final String name = checkRun['name'].toLowerCase() as String;
- // Framework shards run framework goldens
- // Web shards run web version of framework goldens
- // Misc shard runs API docs goldens
- if (name.contains('framework') || name.contains('web engine') || name.contains('misc')) {
- runsGoldenFileTests = true;
- }
- if (checkRun['conclusion'] == null || checkRun['conclusion'].toUpperCase() != 'SUCCESS') {
- incompleteChecks.add(name);
- }
- }
-
- if (runsGoldenFileTests) {
- log.fine('This PR executes golden file tests.');
- // Check when this PR was last updated. Gold does not keep results after
- // >20 days. If a PR has gone stale, we should draw attention to it to be
- // updated or closed.
- final DateTime updatedAt = pr.updatedAt!.toUtc();
- final DateTime twentyDaysAgo = DateTime.now().toUtc().subtract(const Duration(days: 20));
- if (updatedAt.isBefore(twentyDaysAgo)) {
- log.fine('Stale PR, no gold status to report.');
- if (!await _alreadyCommented(gitHubClient, pr, slug, config.flutterGoldStalePR)) {
- log.fine('Notifying for stale PR.');
- await gitHubClient.issues
- .createComment(slug, pr.number!, config.flutterGoldStalePR + config.flutterGoldAlertConstant(slug));
- }
- continue;
- }
-
- if (incompleteChecks.isNotEmpty) {
- // If checks on an open PR are running or failing, the gold status
- // should just be pending. Any draft PRs are skipped
- // until marked ready for review.
- log.fine('Waiting for checks to be completed.');
- statusRequest =
- _createStatus(GithubGoldStatusUpdate.statusRunning, config.flutterGoldPending, slug, pr.number!);
- } else {
- // We do not want to query Gold on a draft PR.
- assert(!pr.draft!);
- // Get Gold status.
- final String goldStatus = await _getGoldStatus(slug, pr);
- statusRequest = _createStatus(
- goldStatus,
- goldStatus == GithubGoldStatusUpdate.statusRunning ? config.flutterGoldChanges : config.flutterGoldSuccess,
- slug,
- pr.number!,
- );
- log.fine('New status for potential update: ${statusRequest.state}, ${statusRequest.description}');
- if (goldStatus == GithubGoldStatusUpdate.statusRunning &&
- !await _alreadyCommented(gitHubClient, pr, slug, config.flutterGoldCommentID(pr))) {
- log.fine('Notifying for triage.');
- await _commentAndApplyGoldLabels(gitHubClient, pr, slug);
- }
- }
-
- // Push updates if there is a status change (detected by unique description)
- // or this is a new commit.
- if (lastUpdate.description != statusRequest.description || lastUpdate.head != pr.head!.sha) {
- try {
- log.fine('Pushing status to GitHub: ${statusRequest.state}, ${statusRequest.description}');
- await gitHubClient.repositories.createStatus(slug, pr.head!.sha!, statusRequest);
- lastUpdate.status = statusRequest.state!;
- lastUpdate.head = pr.head!.sha;
- lastUpdate.updates = (lastUpdate.updates ?? 0) + 1;
- lastUpdate.description = statusRequest.description!;
- statusUpdates.add(lastUpdate);
- } catch (error) {
- log.severe('Failed to post status update to ${slug.fullName}#${pr.number}: $error');
- }
- }
- } else {
- log.fine('This PR does not execute golden file tests.');
- }
- }
- await datastore.insert(statusUpdates);
- log.fine('Committed all updates for $slug');
- }
-
- /// Returns a GitHub Status for the given state and description.
- CreateStatus _createStatus(String state, String description, RepositorySlug slug, int prNumber) {
- final CreateStatus statusUpdate = CreateStatus(state)
- ..targetUrl = _getTriageUrl(slug, prNumber)
- ..context = 'flutter-gold'
- ..description = description;
- return statusUpdate;
- }
-
- /// Used to check for any tryjob results from Flutter Gold associated with a
- /// pull request.
- Future<String> _getGoldStatus(RepositorySlug slug, PullRequest pr) async {
- // We wait for a few seconds in case tests _just_ finished and the tryjob
- // has not finished ingesting the results.
- await Future<void>.delayed(ingestionDelay);
- final Uri requestForTryjobStatus =
- Uri.parse('${_getGoldHost(slug)}/json/v1/changelist_summary/github/${pr.number}');
- try {
- log.fine('Querying Gold for image results...');
- final http.Response response = await goldClient.get(requestForTryjobStatus);
- if (response.statusCode != HttpStatus.ok) {
- throw HttpException(response.body);
- }
-
- final dynamic jsonResponseTriage = json.decode(response.body);
- if (jsonResponseTriage is! Map<String, dynamic>) {
- throw const FormatException('Skia gold changelist summary does not match expected format.');
- }
- final List<dynamic> patchsets = jsonResponseTriage['patchsets'] as List<dynamic>;
- int untriaged = 0;
- for (int i = 0; i < patchsets.length; i++) {
- final Map<String, dynamic> patchset = patchsets[i] as Map<String, dynamic>;
- if (patchset['patchset_id'] == pr.head!.sha) {
- untriaged = patchset['new_untriaged_images'] as int;
- break;
- }
- }
-
- if (untriaged == 0) {
- log.fine('There are no unexpected image results for #${pr.number} at sha '
- '${pr.head!.sha}.');
-
- return GithubGoldStatusUpdate.statusCompleted;
- } else {
- log.fine('Tryjob for #${pr.number} at sha ${pr.head!.sha} generated new '
- 'images.');
-
- return GithubGoldStatusUpdate.statusRunning;
- }
- } on FormatException catch (e) {
- throw BadRequestException('Formatting error detected requesting '
- 'tryjob status for pr #${pr.number} from Flutter Gold.\n'
- 'response: $response\n'
- 'error: $e');
- } catch (e) {
- throw BadRequestException('Error detected requesting tryjob status for pr '
- '#${pr.number} from Flutter Gold.\n'
- 'error: $e');
- }
- }
-
- String _getTriageUrl(RepositorySlug slug, int number) {
- return '${_getGoldHost(slug)}/cl/github/$number';
- }
-
- String _getGoldHost(RepositorySlug slug) {
- if (slug == Config.flutterSlug) {
- return 'https://flutter-gold.skia.org';
- }
-
- if (slug == Config.engineSlug) {
- return 'https://flutter-engine-gold.skia.org';
- }
-
- throw Exception('Unknown slug: $slug');
- }
-
- /// Creates a comment on a given pull request identified to have golden file
- /// changes and applies the `will affect goldens` label.
- Future<void> _commentAndApplyGoldLabels(
- GitHub gitHubClient,
- PullRequest pr,
- RepositorySlug slug,
- ) async {
- String body;
- if (await _isFirstComment(gitHubClient, pr, slug)) {
- body = config.flutterGoldInitialAlert(_getTriageUrl(slug, pr.number!));
- } else {
- body = config.flutterGoldFollowUpAlert(_getTriageUrl(slug, pr.number!));
- }
- body += config.flutterGoldAlertConstant(slug) + config.flutterGoldCommentID(pr);
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, <String>[
- 'will affect goldens',
- ]);
- }
-
- Future<bool> _alreadyCommented(
- GitHub gitHubClient,
- PullRequest pr,
- RepositorySlug slug,
- String message,
- ) async {
- final Stream<IssueComment> comments = gitHubClient.issues.listCommentsByIssue(slug, pr.number!);
- await for (IssueComment comment in comments) {
- if (comment.body!.contains(message)) {
- return true;
- }
- }
- return false;
- }
-
- Future<bool> _isFirstComment(
- GitHub gitHubClient,
- PullRequest pr,
- RepositorySlug slug,
- ) async {
- final Stream<IssueComment> comments = gitHubClient.issues.listCommentsByIssue(slug, pr.number!);
- await for (IssueComment comment in comments) {
- if (comment.body!.contains(config.flutterGoldInitialAlert(_getTriageUrl(slug, pr.number!)))) {
- return false;
- }
- }
- return true;
- }
-}
-
-Future<Map<String, dynamic>?> _queryGraphQL(
- GraphQLClient client,
- RepositorySlug slug,
- int prNumber,
-) async {
- final QueryResult result = await client.query(
- QueryOptions(
- document: lang.parseString(pullRequestChecksQuery),
- fetchPolicy: FetchPolicy.noCache,
- variables: <String, dynamic>{
- 'sPullRequest': prNumber,
- 'sRepoOwner': slug.owner,
- 'sRepoName': slug.name,
- },
- ),
- );
-
- if (result.hasException) {
- log.severe(result.exception.toString());
- throw const BadRequestException('GraphQL query failed');
- }
- return result.data;
-}
-
-const String pullRequestChecksQuery = r'''
-query ChecksForPullRequest($sPullRequest: Int!, $sRepoOwner: String!, $sRepoName: String!) {
- repository(owner: $sRepoOwner, name: $sRepoName) {
- pullRequest(number: $sPullRequest) {
- commits(last: 1) {
- nodes {
- commit {
- # (appId: 64368) == flutter-dashboard. We only care about
- # flutter-dashboard checks.
-
- checkSuites(last: 1, filterBy: {appId: 64368}) {
- nodes {
- checkRuns(first: 100) {
- nodes {
- name
- status
- conclusion
- }
- }
- }
- }
- }
- }
- }
- }
- }
-}''';
diff --git a/app_dart/lib/src/request_handlers/readiness_check.dart b/app_dart/lib/src/request_handlers/readiness_check.dart
deleted file mode 100644
index 6c8ecbe..0000000
--- a/app_dart/lib/src/request_handlers/readiness_check.dart
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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:meta/meta.dart';
-import '../request_handling/body.dart';
-import '../request_handling/request_handler.dart';
-
-@immutable
-class ReadinessCheck extends RequestHandler<Body> {
- const ReadinessCheck({required super.config});
-
- @override
- Future<Body> get() async {
- return Body.empty;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/reset_prod_task.dart b/app_dart/lib/src/request_handlers/reset_prod_task.dart
deleted file mode 100644
index 19e3162..0000000
--- a/app_dart/lib/src/request_handlers/reset_prod_task.dart
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2020 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:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/appengine/key_helper.dart';
-import '../model/appengine/task.dart';
-import '../model/ci_yaml/ci_yaml.dart';
-import '../model/ci_yaml/target.dart';
-import '../model/google/token_info.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-import '../service/luci_build_service.dart';
-import '../service/scheduler.dart';
-
-/// Reruns a postsubmit LUCI build.
-///
-/// Expects either [taskKeyParam] or a set of params that give enough detail to lookup a task in datastore.
-@immutable
-class ResetProdTask extends ApiRequestHandler<Body> {
- const ResetProdTask({
- required super.config,
- required super.authenticationProvider,
- required this.luciBuildService,
- required this.scheduler,
- @visibleForTesting DatastoreServiceProvider? datastoreProvider,
- }) : datastoreProvider = datastoreProvider ?? DatastoreService.defaultProvider;
-
- final DatastoreServiceProvider datastoreProvider;
- final LuciBuildService luciBuildService;
- final Scheduler scheduler;
-
- static const String branchParam = 'Branch';
- static const String taskKeyParam = 'Key';
- static const String ownerParam = 'Owner';
- static const String repoParam = 'Repo';
- static const String commitShaParam = 'Commit';
-
- /// Name of the task to be retried.
- ///
- /// If "all" is given, all failed tasks will be retried. This enables
- /// oncalls to quickly recover a commit without the tedium of the UI.
- static const String taskParam = 'Task';
-
- @override
- Future<Body> post() async {
- final DatastoreService datastore = datastoreProvider(config.db);
- final String? encodedKey = requestData![taskKeyParam] as String?;
- String? branch = requestData![branchParam] as String?;
- final String owner = requestData![ownerParam] as String? ?? 'flutter';
- final String? repo = requestData![repoParam] as String?;
- final String? sha = requestData![commitShaParam] as String?;
- final TokenInfo token = await tokenInfo(request!);
- final String? taskName = requestData![taskParam] as String?;
-
- RepositorySlug? slug;
- if (encodedKey != null && encodedKey.isNotEmpty) {
- // Check params required for dashboard.
- checkRequiredParameters(<String>[taskKeyParam]);
- } else {
- // Checks params required when this API is called with curl.
- checkRequiredParameters(<String>[commitShaParam, taskParam, repoParam]);
- slug = RepositorySlug(owner, repo!);
- branch ??= Config.defaultBranch(slug);
- }
-
- if (taskName == 'all') {
- final Key<String> commitKey = Commit.createKey(
- db: datastore.db,
- slug: slug!,
- gitBranch: branch!,
- sha: sha!,
- );
- final tasks = await datastore.db.query<Task>(ancestorKey: commitKey).run().toList();
- final List<Future<void>> futures = <Future<void>>[];
- for (final Task task in tasks) {
- if (!taskFailStatusSet.contains(task.status)) continue;
- futures.add(
- rerun(
- datastore: datastore,
- branch: branch,
- sha: sha,
- taskName: task.name,
- slug: slug,
- email: token.email!,
- ),
- );
- }
- await Future.wait(futures);
- } else {
- await rerun(
- datastore: datastore,
- encodedKey: encodedKey,
- branch: branch,
- sha: sha,
- taskName: taskName,
- slug: slug,
- email: token.email!,
- ignoreChecks: true,
- );
- }
-
- return Body.empty;
- }
-
- Future<void> rerun({
- required DatastoreService datastore,
- String? encodedKey,
- String? branch,
- String? sha,
- String? taskName,
- RepositorySlug? slug,
- required String email,
- bool ignoreChecks = false,
- }) async {
- final Task task = await _getTaskFromNamedParams(
- datastore: datastore,
- encodedKey: encodedKey,
- branch: branch,
- name: taskName,
- sha: sha,
- slug: slug,
- );
- final Commit commit = await _getCommitFromTask(datastore, task);
-
- final CiYaml ciYaml = await scheduler.getCiYaml(commit);
- final Target target = ciYaml.postsubmitTargets.singleWhere((Target target) => target.value.name == task.name);
-
- final Map<String, List<String>> tags = <String, List<String>>{
- 'triggered_by': <String>[email],
- 'trigger_type': <String>['manual'],
- };
- final bool isRerunning = await luciBuildService.checkRerunBuilder(
- commit: commit,
- task: task,
- target: target,
- datastore: datastore,
- tags: tags,
- ignoreChecks: ignoreChecks,
- );
-
- // For human retries from the dashboard, notify if a task failed to rerun.
- if (ignoreChecks && isRerunning == false) {
- throw InternalServerError('Failed to rerun $taskName');
- }
- }
-
- /// Retrieve [Task] from [DatastoreService] from either an encoded key or commit + task name info.
- ///
- /// If [encodedKey] is passed, [KeyHelper] will decode it directly and return the associated entity.
- ///
- /// Otherwise, [name], [branch], [sha], and [slug] must be passed to find the [Task].
- Future<Task> _getTaskFromNamedParams({
- required DatastoreService datastore,
- String? encodedKey,
- String? branch,
- String? name,
- String? sha,
- RepositorySlug? slug,
- }) async {
- if (encodedKey != null && encodedKey.isNotEmpty) {
- final Key<int> key = config.keyHelper.decode(encodedKey) as Key<int>;
- return datastore.lookupByValue<Task>(key);
- }
- final Key<String> commitKey = Commit.createKey(
- db: datastore.db,
- slug: slug!,
- gitBranch: branch!,
- sha: sha!,
- );
- return Task.fromDatastore(
- datastore: datastore,
- commitKey: commitKey,
- name: name!,
- );
- }
-
- /// Returns the [Commit] associated with [Task].
- Future<Commit> _getCommitFromTask(DatastoreService datastore, Task task) async {
- return (await datastore.lookupByKey<Commit>(<Key<dynamic>>[task.parentKey!])).single!;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/reset_try_task.dart b/app_dart/lib/src/request_handlers/reset_try_task.dart
deleted file mode 100644
index 19829a5..0000000
--- a/app_dart/lib/src/request_handlers/reset_try_task.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2020 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:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../../cocoon_service.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/exceptions.dart';
-
-/// Runs all the applicable tasks for a given PR and commit hash. This will be
-/// used to unblock rollers when creating a new commit is not possible.
-@immutable
-class ResetTryTask extends ApiRequestHandler<Body> {
- const ResetTryTask({
- required super.config,
- required super.authenticationProvider,
- required this.scheduler,
- });
-
- final Scheduler scheduler;
-
- static const String kOwnerParam = 'owner';
- static const String kRepoParam = 'repo';
- static const String kPullRequestNumberParam = 'pr';
- static const String kBuilderParam = 'builders';
-
- @override
- Future<Body> get() async {
- checkRequiredQueryParameters(<String>[kRepoParam, kPullRequestNumberParam]);
- final String owner = request!.uri.queryParameters[kOwnerParam] ?? 'flutter';
- final String repo = request!.uri.queryParameters[kRepoParam]!;
- final String pr = request!.uri.queryParameters[kPullRequestNumberParam]!;
- final String builders = request!.uri.queryParameters[kBuilderParam] ?? '';
- final List<String> builderList = getBuilderList(builders);
-
- final int? prNumber = int.tryParse(pr);
- if (prNumber == null) {
- throw const BadRequestException('$kPullRequestNumberParam must be a number');
- }
- final RepositorySlug slug = RepositorySlug(owner, repo);
- final GitHub github = await config.createGitHubClient(slug: slug);
- final PullRequest pullRequest = await github.pullRequests.get(slug, prNumber);
- await scheduler.triggerPresubmitTargets(pullRequest: pullRequest, builderTriggerList: builderList);
- return Body.empty;
- }
-
- /// Parses [builders] to a String list.
- ///
- /// The [builders] parameter is expecting comma joined string, e.g. 'builder1, builder2'.
- /// Returns an empty list if no [builders] is specified.
- List<String> getBuilderList(String builders) {
- if (builders.isEmpty) {
- return <String>[];
- }
- return builders.split(',').map((String builder) => builder.trim()).toList();
- }
-}
diff --git a/app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart b/app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart
deleted file mode 100644
index 0c80599..0000000
--- a/app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright 2019 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/src/foundation/utils.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:cocoon_service/src/service/scheduler/policy.dart';
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-import 'package:retry/retry.dart';
-
-import '../../model/ci_yaml/ci_yaml.dart';
-import '../../model/ci_yaml/target.dart';
-import '../../request_handling/exceptions.dart';
-import '../../request_handling/request_handler.dart';
-import '../../service/config.dart';
-import '../../service/logging.dart';
-import '../../service/luci_build_service.dart';
-import '../../service/scheduler.dart';
-
-/// Cron request handler for scheduling targets when capacity becomes available.
-///
-/// Targets that have a [BatchPolicy] need to have backfilling enabled to ensure that ToT is always being tested.
-@immutable
-class BatchBackfiller extends RequestHandler {
- /// Creates a subscription for sending BuildBucket requests.
- const BatchBackfiller({
- required super.config,
- required this.scheduler,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- });
-
- final DatastoreServiceProvider datastoreProvider;
- final Scheduler scheduler;
-
- @override
- Future<Body> get() async {
- final List<Future<void>> futures = <Future<void>>[];
-
- for (RepositorySlug slug in config.supportedRepos) {
- futures.add(backfillRepository(slug));
- }
-
- // Process all repos asynchronously
- await Future.wait<void>(futures);
-
- return Body.empty;
- }
-
- Future<void> backfillRepository(RepositorySlug slug) async {
- final DatastoreService datastore = datastoreProvider(config.db);
- final List<FullTask> tasks =
- await (datastore.queryRecentTasks(slug: slug, commitLimit: config.backfillerCommitLimit)).toList();
-
- // Construct Task columns to scan for backfilling
- final Map<String, List<FullTask>> taskMap = <String, List<FullTask>>{};
- for (FullTask fullTask in tasks) {
- if (taskMap.containsKey(fullTask.task.name)) {
- taskMap[fullTask.task.name]!.add(fullTask);
- } else {
- taskMap[fullTask.task.name!] = <FullTask>[fullTask];
- }
- }
-
- // Check if should be scheduled (there is no yellow runs). Run the most recent gray.
- List<Tuple<Target, FullTask, int>> backfill = <Tuple<Target, FullTask, int>>[];
- for (List<FullTask> taskColumn in taskMap.values) {
- final FullTask task = taskColumn.first;
- final CiYaml ciYaml = await scheduler.getCiYaml(task.commit);
- final List<Target> ciYamlTargets = ciYaml.backfillTargets;
- // Skips scheduling if the task is not in TOT commit anymore.
- final bool taskInToT = ciYamlTargets.map((Target target) => target.value.name).toList().contains(task.task.name);
- if (!taskInToT) {
- continue;
- }
- final Target target = ciYamlTargets.singleWhere((target) => target.value.name == task.task.name);
- if (target.schedulerPolicy is! BatchPolicy) {
- continue;
- }
- final FullTask? backfillTask = _backfillTask(target, taskColumn);
- final int? priority = backfillPriority(taskColumn.map((e) => e.task).toList());
- if (priority != null && backfillTask != null) {
- backfill.add(Tuple<Target, FullTask, int>(target, backfillTask, priority));
- }
- }
-
- // Get the number of targets to be backfilled in each cycle.
- backfill = getFilteredBackfill(backfill);
-
- log.fine('Backfilling ${backfill.length} builds');
- log.fine(backfill.map<String>((Tuple<Target, FullTask, int> tuple) => tuple.first.value.name));
-
- // Update tasks status as in progress to avoid duplicate scheduling.
- final List<Task> backfillTasks = backfill.map((Tuple<Target, FullTask, int> tuple) => tuple.second.task).toList();
- try {
- await datastore.withTransaction<void>((Transaction transaction) async {
- transaction.queueMutations(inserts: backfillTasks);
- await transaction.commit();
- log.fine(
- 'Updated ${backfillTasks.length} tasks: ${backfillTasks.map((e) => e.name).toList()} when backfilling.',
- );
- });
- // Schedule all builds asynchronously.
- // Schedule after db updates to avoid duplicate scheduling when db update fails.
- await _scheduleWithRetries(backfill);
- } catch (error) {
- log.severe('Failed to update tasks when backfilling: $error');
- }
- }
-
- /// Filters [config.backfillerTargetLimit] targets to backfill.
- ///
- /// High priority targets will be guranteed to get back filled first. If more targets
- /// than [config.backfillerTargetLimit], pick the limited number of targets after a
- /// shuffle. This is to make sure all targets are picked with the same chance.
- List<Tuple<Target, FullTask, int>> getFilteredBackfill(List<Tuple<Target, FullTask, int>> backfill) {
- if (backfill.length <= config.backfillerTargetLimit) {
- return backfill;
- }
- final List<Tuple<Target, FullTask, int>> filteredBackfill = <Tuple<Target, FullTask, int>>[];
- final List<Tuple<Target, FullTask, int>> highPriorityBackfill =
- backfill.where((element) => element.third == LuciBuildService.kRerunPriority).toList();
- final List<Tuple<Target, FullTask, int>> normalPriorityBackfill =
- backfill.where((element) => element.third != LuciBuildService.kRerunPriority).toList();
- if (highPriorityBackfill.length >= config.backfillerTargetLimit) {
- highPriorityBackfill.shuffle();
- filteredBackfill.addAll(highPriorityBackfill.sublist(0, config.backfillerTargetLimit));
- } else {
- filteredBackfill.addAll(highPriorityBackfill);
- normalPriorityBackfill.shuffle();
- filteredBackfill
- .addAll(normalPriorityBackfill.sublist(0, config.backfillerTargetLimit - highPriorityBackfill.length));
- }
- return filteredBackfill;
- }
-
- /// Schedules tasks with retry when hitting pub/sub server errors.
- Future<void> _scheduleWithRetries(List<Tuple<Target, FullTask, int>> backfill) async {
- const RetryOptions retryOptions = Config.schedulerRetry;
- try {
- await retryOptions.retry(
- () async {
- final List<List<Tuple<Target, Task, int>>> tupleLists =
- await Future.wait<List<Tuple<Target, Task, int>>>(backfillRequestList(backfill));
- if (tupleLists.any((List<Tuple<Target, Task, int>> tupleList) => tupleList.isNotEmpty)) {
- final int nonEmptyListLenght = tupleLists.where((element) => element.isNotEmpty).toList().length;
- log.info('Backfill fails and retry backfilling $nonEmptyListLenght targets.');
- backfill = _updateBackfill(backfill, tupleLists);
- throw InternalServerError('Failed to backfill ${backfill.length} targets.');
- }
- },
- retryIf: (Exception e) => e is InternalServerError,
- );
- } catch (error) {
- log.severe('Failed to backfill ${backfill.length} targets due to error: $error');
- }
- }
-
- /// Updates the [backfill] list with those that fail to get scheduled.
- ///
- /// [tupleLists] maintains the same tuple order as those in [backfill].
- /// Each element from [backfill] is encapsulated as a list in [tupleLists] to prepare for
- /// [scheduler.luciBuildService.schedulePostsubmitBuilds].
- List<Tuple<Target, FullTask, int>> _updateBackfill(
- List<Tuple<Target, FullTask, int>> backfill,
- List<List<Tuple<Target, Task, int>>> tupleLists,
- ) {
- final List<Tuple<Target, FullTask, int>> updatedBackfill = <Tuple<Target, FullTask, int>>[];
- for (int i = 0; i < tupleLists.length; i++) {
- if (tupleLists[i].isNotEmpty) {
- updatedBackfill.add(backfill[i]);
- }
- }
- return updatedBackfill;
- }
-
- /// Creates a list of backfill requests.
- List<Future<List<Tuple<Target, Task, int>>>> backfillRequestList(List<Tuple<Target, FullTask, int>> backfill) {
- final List<Future<List<Tuple<Target, Task, int>>>> futures = <Future<List<Tuple<Target, Task, int>>>>[];
- for (Tuple<Target, FullTask, int> tuple in backfill) {
- // TODO(chillers): The backfill priority is always going to be low. If this is a ToT task, we should run it at the default priority.
- final Tuple<Target, Task, int> toBeScheduled = Tuple(
- tuple.first,
- tuple.second.task,
- tuple.third,
- );
- futures.add(
- scheduler.luciBuildService.schedulePostsubmitBuilds(
- commit: tuple.second.commit,
- toBeScheduled: [toBeScheduled],
- ),
- );
- }
-
- return futures;
- }
-
- /// Returns priority for back filled targets.
- ///
- /// Skips scheduling newly created targets whose available entries are
- /// less than `BatchPolicy.kBatchSize`.
- ///
- /// Uses a higher priority if there is an earlier failed build. Otherwise,
- /// uses default `LuciBuildService.kBackfillPriority`
- int? backfillPriority(List<Task> tasks) {
- if (tasks.length < BatchPolicy.kBatchSize) {
- return null;
- }
- if (shouldRerunPriority(tasks, BatchPolicy.kBatchSize)) {
- return LuciBuildService.kRerunPriority;
- }
- return LuciBuildService.kBackfillPriority;
- }
-
- /// Returns the most recent [FullTask] to backfill.
- ///
- /// A [FullTask] is only returned iff:
- /// 1. There are no running builds (yellow)
- /// 2. There are tasks that haven't been run (gray)
- ///
- /// This is naive, and doesn't rely on knowing the actual Flutter infra capacity.
- ///
- /// Otherwise, returns null indicating nothing should be backfilled.
- FullTask? _backfillTask(Target target, List<FullTask> tasks) {
- final List<FullTask> relevantTasks = tasks.where((FullTask task) => task.task.name == target.value.name).toList();
- if (relevantTasks.any((FullTask task) => task.task.status == Task.statusInProgress)) {
- // Don't schedule more builds where there is already a running task
- return null;
- }
-
- final List<FullTask> backfillTask =
- relevantTasks.where((FullTask task) => task.task.status == Task.statusNew).toList();
- if (backfillTask.isEmpty) {
- return null;
- }
-
- // First item in the list is guranteed to be most recent.
- // Mark task as in progress to ensure it isn't scheduled over
- backfillTask.first.task.status = Task.statusInProgress;
- return backfillTask.first;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/scheduler/request_subscription.dart b/app_dart/lib/src/request_handlers/scheduler/request_subscription.dart
deleted file mode 100644
index 874d16d..0000000
--- a/app_dart/lib/src/request_handlers/scheduler/request_subscription.dart
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2019 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:convert';
-
-import 'package:meta/meta.dart';
-import 'package:retry/retry.dart';
-
-import '../../../cocoon_service.dart';
-import '../../model/luci/buildbucket.dart';
-import '../../request_handling/exceptions.dart';
-import '../../request_handling/subscription_handler.dart';
-import '../../service/logging.dart';
-
-/// Subscription for making requests to BuildBucket.
-///
-/// The PubSub subscription is set up here:
-/// https://console.cloud.google.com/cloudpubsub/subscription/detail/scheduler-requests?project=flutter-dashboard
-///
-/// This endpoint allows Cocoon to defer BuildBucket requests off the main request loop. This is critical when new
-/// commits are pushed, and they can schedule 100+ builds at once.
-///
-/// This endpoint takes in a POST request with the JSON of a [BatchRequest]. In practice, the
-/// [BatchRequest] should contain a single request.
-@immutable
-class SchedulerRequestSubscription extends SubscriptionHandler {
- /// Creates a subscription for sending BuildBucket requests.
- const SchedulerRequestSubscription({
- required super.cache,
- required super.config,
- required this.buildBucketClient,
- super.authProvider,
- this.retryOptions = Config.schedulerRetry,
- }) : super(subscriptionName: 'scheduler-requests');
-
- final BuildBucketClient buildBucketClient;
-
- final RetryOptions retryOptions;
-
- @override
- Future<Body> post() async {
- BatchRequest request;
- try {
- final String data = message.data!;
- Map<String, dynamic> jsonData;
- log.info('rawJson: $data');
- try {
- jsonData = jsonDecode(data) as Map<String, dynamic>;
- } on FormatException {
- jsonData = json.decode(String.fromCharCodes(base64.decode(data))) as Map<String, dynamic>;
- }
- request = BatchRequest.fromJson(jsonData);
- } catch (e) {
- log.severe('Failed to construct BatchRequest from message');
- log.severe(e);
- throw BadRequestException(e.toString());
- }
-
- /// Retry scheduling builds upto 3 times.
- ///
- /// Log error message when still failing after retry. Avoid endless rescheduling
- /// by acking the pub/sub message without throwing an exception.
- String? unScheduledBuilds;
- try {
- await retryOptions.retry(
- () async {
- final List<Request> requestsToRetry = await _sendBatchRequest(request);
- request = BatchRequest(requests: requestsToRetry);
- unScheduledBuilds = requestsToRetry.map((e) => e.scheduleBuild!.builderId.builder).toString();
- if (requestsToRetry.isNotEmpty) {
- throw InternalServerError('Failed to schedule builds: $unScheduledBuilds.');
- }
- },
- retryIf: (Exception e) => e is InternalServerError,
- );
- } catch (e) {
- log.warning('Failed to schedule builds: $unScheduledBuilds.');
- return Body.forString('Failed to schedule builds: $unScheduledBuilds.');
- }
-
- return Body.empty;
- }
-
- /// Wrapper around [BuildbucketClient.batch] to ensure all requests are made.
- ///
- /// Returns [List<Request>] of requests that need to be retried.
- Future<List<Request>> _sendBatchRequest(BatchRequest request) async {
- final BatchResponse response = await buildBucketClient.batch(request);
- log.fine('Made ${request.requests?.length} and received ${response.responses?.length}');
- log.fine('Responses: ${response.responses}');
-
- // By default, retry everything. Then remove requests with a verified response.
- final List<Request> retry = request.requests ?? <Request>[];
- response.responses?.forEach((Response subresponse) {
- if (subresponse.scheduleBuild != null) {
- retry
- .removeWhere((Request request) => request.scheduleBuild?.builderId == subresponse.scheduleBuild!.builderId);
- } else {
- log.warning('Response does not have schedule build: $subresponse');
- }
- if (subresponse.error?.code != 0) {
- log.fine('Non-zero grpc code: $subresponse');
- }
- });
-
- return retry;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/scheduler/vacuum_stale_tasks.dart b/app_dart/lib/src/request_handlers/scheduler/vacuum_stale_tasks.dart
deleted file mode 100644
index 852c526..0000000
--- a/app_dart/lib/src/request_handlers/scheduler/vacuum_stale_tasks.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 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/cocoon_service.dart';
-import 'package:github/github.dart' as gh;
-import 'package:meta/meta.dart';
-
-import '../../model/appengine/task.dart';
-import '../../service/datastore.dart';
-import '../../service/logging.dart';
-
-/// Vacuum stale tasks.
-///
-/// Occassionally, a build never gets processed by LUCI. To prevent tasks
-/// being stuck as "In Progress," this will return tasks to "New" if they have
-/// no updates after 3 hours.
-@immutable
-class VacuumStaleTasks extends RequestHandler<Body> {
- const VacuumStaleTasks({
- required super.config,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- @visibleForTesting this.nowValue,
- });
-
- final DatastoreServiceProvider datastoreProvider;
-
- /// For testing, can be used to inject a deterministic time.
- final DateTime? nowValue;
-
- /// Tasks that are in progress for this duration will be reset.
- static const Duration kTimeoutLimit = Duration(hours: 3);
-
- @override
- Future<Body> get() async {
- final List<Future<void>> futures = <Future<void>>[];
- for (gh.RepositorySlug slug in config.supportedRepos) {
- futures.add(_vacuumRepository(slug));
- }
-
- await Future.wait(futures);
-
- return Body.empty;
- }
-
- /// Scans [slug] for tasks that have reached the timeout limit, and sets
- /// them back to the new state.
- ///
- /// The expectation is the [BatchBackfiller] will be able to reschedule these.
- Future<void> _vacuumRepository(gh.RepositorySlug slug) async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- final List<FullTask> tasks = await datastore.queryRecentTasks(slug: slug).toList();
- final Set<Task> tasksToBeReset = <Task>{};
- for (FullTask fullTask in tasks) {
- final Task task = fullTask.task;
- if (task.status != Task.statusInProgress) {
- continue;
- }
-
- if (task.createTimestamp == null) {
- log.fine('Vacuuming $task due to createTimestamp being null');
- tasksToBeReset.add(task);
- continue;
- }
-
- final DateTime now = nowValue ?? DateTime.now();
- final DateTime create = DateTime.fromMillisecondsSinceEpoch(task.createTimestamp!);
- final Duration queueTime = now.difference(create);
-
- if (queueTime > kTimeoutLimit) {
- log.fine('Vacuuming $task due to staleness');
- tasksToBeReset.add(task);
- continue;
- }
- }
-
- final Iterable<Task> inserts =
- tasksToBeReset.map((Task task) => task..status = Task.statusNew).map((Task task) => task..createTimestamp = 0);
- await datastore.insert(inserts.toList());
- }
-}
diff --git a/app_dart/lib/src/request_handlers/test_ownership.dart b/app_dart/lib/src/request_handlers/test_ownership.dart
deleted file mode 100644
index cdf3210..0000000
--- a/app_dart/lib/src/request_handlers/test_ownership.dart
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright 2023 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/src/request_handlers/flaky_handler_utils.dart';
-import '../../protos.dart' as pb;
-
-abstract class TestOwner {
- factory TestOwner(BuilderType builderType) {
- switch (builderType) {
- case BuilderType.devicelab:
- return DeviceLabTestOwner();
- case BuilderType.firebaselab:
- return FirebaseLabTestOwner();
- case BuilderType.frameworkHostOnly:
- return FrameworkHostOnlyTestOwner();
- case BuilderType.shard:
- return ShardTestOwner();
- default:
- return UnknownTestOwner();
- }
- }
-
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- );
-}
-
-Team teamFromString(String teamString) {
- switch (teamString) {
- case 'flutter/framework':
- return Team.framework;
- case 'flutter/engine':
- return Team.engine;
- case 'flutter/tool':
- return Team.tool;
- case 'flutter/web':
- return Team.web;
- }
- return Team.unknown;
-}
-
-String getTestNameFromTargetName(String targetName) {
- // The builder names is in the format '<platform> <test name>'.
- final List<String> words = targetName.split(' ');
- return words.length < 2 ? words[0] : words[1];
-}
-
-class DeviceLabTestOwner implements TestOwner {
- DeviceLabTestOwner();
-
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- String? owner;
- Team? team;
- final String testName = target.properties['task_name']!;
- // The format looks like this:
- // /dev/devicelab/bin/tasks/dart_plugin_registry_test.dart @stuartmorgan @flutter/plugin
- final RegExpMatch? match = devicelabTestOwners.firstMatch(testOwnersContent);
- if (match != null && match.namedGroup(kOwnerGroupName) != null) {
- final List<String> lines = match
- .namedGroup(kOwnerGroupName)!
- .split('\n')
- .where((String line) => line.isNotEmpty && !line.startsWith('#'))
- .toList();
-
- for (final String line in lines) {
- final List<String> words = line.trim().split(' ');
- // e.g. words = ['/xxx/xxx/xxx_test.dart', '@stuartmorgan' '@flutter/tool']
- if (words[0].endsWith('$testName.dart')) {
- owner = words[1].substring(1); // Strip out the lead '@'
- team = words.length < 3 ? Team.unknown : teamFromString(words[2].substring(1)); // Strip out the lead '@'
- break;
- }
- }
- }
-
- return TestOwnership(owner, team);
- }
-}
-
-class ShardTestOwner implements TestOwner {
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- // The format looks like this:
- // # build_tests @zanderso @flutter/tool
- final String testName = getTestNameFromTargetName(target.name);
- String? owner;
- Team? team;
- final RegExpMatch? match = shardTestOwners.firstMatch(testOwnersContent);
- if (match != null && match.namedGroup(kOwnerGroupName) != null) {
- final List<String> lines =
- match.namedGroup(kOwnerGroupName)!.split('\n').where((String line) => line.contains('@')).toList();
-
- for (final String line in lines) {
- final List<String> words = line.trim().split(' ');
- // e.g. words = ['#', 'build_test', '@zanderso' '@flutter/tool']
- if (testName.contains(words[1])) {
- owner = words[2].substring(1); // Strip out the lead '@'
- team = words.length < 4 ? Team.unknown : teamFromString(words[3].substring(1)); // Strip out the lead '@'
- break;
- }
- }
- }
-
- return TestOwnership(owner, team);
- }
-}
-
-class FrameworkHostOnlyTestOwner implements TestOwner {
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- final String testName = getTestNameFromTargetName(target.name);
- String? owner;
- Team? team;
- // The format looks like this:
- // # Linux analyze
- // /dev/bots/analyze.dart @HansMuller @flutter/framework
- final RegExpMatch? match = frameworkHostOnlyTestOwners.firstMatch(testOwnersContent);
- if (match != null && match.namedGroup(kOwnerGroupName) != null) {
- final List<String> lines =
- match.namedGroup(kOwnerGroupName)!.split('\n').where((String line) => line.isNotEmpty).toList();
- int index = 0;
- while (index < lines.length) {
- if (lines[index].startsWith('#')) {
- // Multiple tests can share same test file and ownership.
- // e.g.
- // # Linux docs_test
- // # Linux docs_public
- // /dev/bots/docs.sh @HansMuller @flutter/framework
- bool isTestDefined = false;
- while (lines[index].startsWith('#') && index + 1 < lines.length) {
- final List<String> commentWords = lines[index].trim().split(' ');
- if (testName.contains(commentWords[2])) {
- isTestDefined = true;
- }
- index += 1;
- }
- if (isTestDefined) {
- final List<String> ownerWords = lines[index].trim().split(' ');
- // e.g. ownerWords = ['/xxx/xxx/xxx_test.dart', '@HansMuller' '@flutter/framework']
- owner = ownerWords[1].substring(1); // Strip out the lead '@'
- team = ownerWords.length < 3
- ? Team.unknown
- : teamFromString(ownerWords[2].substring(1)); // Strip out the lead '@'
- break;
- }
- }
- index += 1;
- }
- }
-
- return TestOwnership(owner, team);
- }
-}
-
-class FirebaseLabTestOwner implements TestOwner {
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- final String testName = getTestNameFromTargetName(target.name);
- String? owner;
- Team? team;
-
- // The format looks like this for builder `Linux firebase_abstrac_method_smoke_test`:
- // /dev/integration_tests/abstrac_method_smoke_test @blasten @flutter/android
- final RegExpMatch? match = firebaselabTestOwners.firstMatch(testOwnersContent);
- if (match != null && match.namedGroup(kOwnerGroupName) != null) {
- final List<String> lines = match
- .namedGroup(kOwnerGroupName)!
- .split('\n')
- .where((String line) => line.isNotEmpty && !line.startsWith('#'))
- .toList();
-
- for (final String line in lines) {
- final List<String> words = line.trim().split(' ');
- final List<String> dirs = words[0].split('/').toList();
- if (testName.contains(dirs.last)) {
- owner = words[1].substring(1); // Strip out the lead '@'
- team = words.length < 3 ? Team.unknown : teamFromString(words[2].substring(1)); // Strip out the lead '@'
- break;
- }
- }
- }
-
- return TestOwnership(owner, team);
- }
-}
-
-class UnknownTestOwner implements TestOwner {
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- return TestOwnership(null, Team.unknown);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/update_existing_flaky_issues.dart b/app_dart/lib/src/request_handlers/update_existing_flaky_issues.dart
deleted file mode 100644
index d1f2b58..0000000
--- a/app_dart/lib/src/request_handlers/update_existing_flaky_issues.dart
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2019 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/ci_yaml.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-import 'package:yaml/yaml.dart';
-
-import '../../protos.dart' as pb;
-import '../foundation/utils.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/bigquery.dart';
-import '../service/config.dart';
-import '../service/github_service.dart';
-import 'flaky_handler_utils.dart';
-
-/// This handler updates existing open flaky issues with the latest build
-/// statistics.
-///
-/// The query parameter kThresholdKey is required in order for the handler to
-/// properly adjusts the priority labels.
-@immutable
-class UpdateExistingFlakyIssue extends ApiRequestHandler<Body> {
- const UpdateExistingFlakyIssue({
- required super.config,
- required super.authenticationProvider,
- @visibleForTesting this.ciYaml,
- });
-
- static const String kThresholdKey = 'threshold';
- static const int kFreshPeriodForOpenFlake = 7; // days
-
- final CiYaml? ciYaml;
-
- @override
- Future<Body> get() async {
- final RepositorySlug slug = Config.flutterSlug;
- final GithubService gitHub = config.createGithubServiceWithToken(await config.githubOAuthToken);
- final BigqueryService bigquery = await config.createBigQueryService();
-
- CiYaml? localCiYaml = ciYaml;
- if (localCiYaml == null) {
- final YamlMap? ci = loadYaml(
- await gitHub.getFileContent(
- slug,
- kCiYamlPath,
- ),
- ) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci);
- localCiYaml = CiYaml(
- slug: slug,
- branch: Config.defaultBranch(slug),
- config: unCheckedSchedulerConfig,
- );
- }
-
- final List<BuilderStatistic> prodBuilderStatisticList =
- await bigquery.listBuilderStatistic(kBigQueryProjectId, bucket: 'prod');
- final List<BuilderStatistic> stagingBuilderStatisticList =
- await bigquery.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging');
- final Map<String?, Issue> nameToExistingIssue = await getExistingIssues(gitHub, slug, state: 'open');
- await _updateExistingFlakyIssue(
- gitHub,
- slug,
- localCiYaml,
- prodBuilderStatisticList: prodBuilderStatisticList,
- stagingBuilderStatisticList: stagingBuilderStatisticList,
- nameToExistingIssue: nameToExistingIssue,
- );
- return Body.forJson(const <String, dynamic>{
- 'Status': 'success',
- });
- }
-
- double get _threshold => double.parse(request!.uri.queryParameters[kThresholdKey]!);
-
- /// Adds an update comment and adjusts the labels of the existing issue based
- /// on the latest statistics.
- ///
- /// This method skips issues that are created within kFreshPeriodForOpenFlake
- /// days.
- Future<void> _addCommentToExistingIssue(
- GithubService gitHub,
- RepositorySlug slug, {
- required Bucket bucket,
- required BuilderStatistic statistic,
- required Issue existingIssue,
- required CiYaml ciYaml,
- }) async {
- if (DateTime.now().difference(existingIssue.createdAt!) < const Duration(days: kFreshPeriodForOpenFlake)) {
- return;
- }
- final IssueUpdateBuilder updateBuilder =
- IssueUpdateBuilder(statistic: statistic, threshold: _threshold, existingIssue: existingIssue, bucket: bucket);
- await gitHub.createComment(slug, issueNumber: existingIssue.number, body: updateBuilder.issueUpdateComment);
- await gitHub.replaceLabelsForIssue(slug, issueNumber: existingIssue.number, labels: updateBuilder.issueLabels);
- if (existingIssue.assignee == null && !updateBuilder.isBelow) {
- final String testOwnerContent = await gitHub.getFileContent(
- slug,
- kTestOwnerPath,
- );
-
- final pb.SchedulerConfig schedulerConfig = ciYaml.config;
- final List<pb.Target> targets = schedulerConfig.targets;
-
- final String? testOwner = getTestOwnership(
- targets.singleWhere((element) => element.name == statistic.name),
- getTypeForBuilder(statistic.name, ciYaml),
- testOwnerContent,
- ).owner;
- if (testOwner != null) {
- await gitHub.assignIssue(slug, issueNumber: existingIssue.number, assignee: testOwner);
- }
- }
- }
-
- /// Updates existing flaky issues based on corrresponding builder stats.
- Future<void> _updateExistingFlakyIssue(
- GithubService gitHub,
- RepositorySlug slug,
- CiYaml ciYaml, {
- required List<BuilderStatistic> prodBuilderStatisticList,
- required List<BuilderStatistic> stagingBuilderStatisticList,
- required Map<String?, Issue> nameToExistingIssue,
- }) async {
- final Map<String, bool> builderFlakyMap = <String, bool>{};
- final Map<String, bool> ignoreFlakyMap = <String, bool>{};
- for (Target target in ciYaml.postsubmitTargets) {
- builderFlakyMap[target.value.name] = target.value.bringup;
- if (target.getIgnoreFlakiness()) {
- ignoreFlakyMap[target.value.name] = true;
- }
- }
- // Update an existing flaky bug with only prod stats if the builder is with `bringup: false`, such as a shard builder.
- //
- // Update an existing flaky bug with both prod and staging stats if the builder is with `bringup: true`. When a builder
- // is newly identified as flaky, there is a gap between the builder is marked as `bringup: true` and the flaky bug is filed.
- // For this case, there will be builds still running in `prod` pool, and we need to append `prod` stats as well.
- for (final BuilderStatistic statistic in prodBuilderStatisticList) {
- // ignore: iterable_contains_unrelated_type
- if (nameToExistingIssue.containsKey(statistic.name) &&
- builderFlakyMap.containsKey(statistic.name) &&
- // ignore: iterable_contains_unrelated_type
- !ignoreFlakyMap.containsKey(statistic.name)) {
- await _addCommentToExistingIssue(
- gitHub,
- slug,
- bucket: Bucket.prod,
- statistic: statistic,
- existingIssue: nameToExistingIssue[statistic.name]!,
- ciYaml: ciYaml,
- );
- }
- }
- // For all staging builder stats, updates any existing flaky bug.
- for (final BuilderStatistic statistic in stagingBuilderStatisticList) {
- if (nameToExistingIssue.containsKey(statistic.name) &&
- builderFlakyMap[statistic.name] == true &&
- // ignore: iterable_contains_unrelated_type
- !ignoreFlakyMap.containsKey(statistic.name)) {
- await _addCommentToExistingIssue(
- gitHub,
- slug,
- bucket: Bucket.staging,
- statistic: statistic,
- existingIssue: nameToExistingIssue[statistic.name]!,
- ciYaml: ciYaml,
- );
- }
- }
- }
-}
diff --git a/app_dart/lib/src/request_handlers/update_task_status.dart b/app_dart/lib/src/request_handlers/update_task_status.dart
deleted file mode 100644
index e4e6ed4..0000000
--- a/app_dart/lib/src/request_handlers/update_task_status.dart
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2019 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:gcloud/db.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/appengine/task.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-
-/// Endpoint for task runners to update Cocoon with test run information.
-///
-/// This handler requires (1) task identifier and (2) task status information.
-///
-/// 1. Tasks are identified by:
-/// [gitBranchParam], [gitShaParam], [builderNameParam]
-///
-/// 2. Task status information
-/// A. Required: [newStatusParam], either [Task.statusSucceeded] or [Task.statusFailed].
-/// B. Optional: [resultsParam] and [scoreKeysParam] which hold performance benchmark data.
-@immutable
-class UpdateTaskStatus extends ApiRequestHandler<UpdateTaskStatusResponse> {
- const UpdateTaskStatus({
- required super.config,
- required super.authenticationProvider,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- });
-
- final DatastoreServiceProvider datastoreProvider;
-
- static const String gitBranchParam = 'CommitBranch';
- static const String gitShaParam = 'CommitSha';
- static const String newStatusParam = 'NewStatus';
- static const String builderNameParam = 'BuilderName';
- static const String testFlayParam = 'TestFlaky';
-
- @override
- Future<UpdateTaskStatusResponse> post() async {
- checkRequiredParameters(<String>[newStatusParam, gitBranchParam, gitShaParam, builderNameParam]);
-
- final DatastoreService datastore = datastoreProvider(config.db);
- final String newStatus = requestData![newStatusParam] as String;
- final bool isTestFlaky = (requestData![testFlayParam] as bool?) ?? false;
-
- if (newStatus != Task.statusSucceeded && newStatus != Task.statusFailed) {
- throw const BadRequestException('NewStatus can be one of "Succeeded", "Failed"');
- }
-
- final Task task = await _getTaskFromNamedParams(datastore);
-
- task.status = newStatus;
- task.endTimestamp = DateTime.now().millisecondsSinceEpoch;
- task.isTestFlaky = isTestFlaky;
-
- await datastore.insert(<Task>[task]);
- return UpdateTaskStatusResponse(task);
- }
-
- /// Retrieve [Task] from [DatastoreService] when given [gitShaParam], [gitBranchParam], and [builderNameParam].
- ///
- /// This is used when the DeviceLab test runner is uploading results to Cocoon for runs on LUCI.
- /// LUCI does not know the [Key] assigned to task when scheduling the build, but Cocoon can
- /// lookup the task based on these key values.
- ///
- /// To lookup the value, we construct the ancestor key, which corresponds to the [Commit].
- /// Then we query the tasks with that ancestor key and search for the one that matches the builder name.
- Future<Task> _getTaskFromNamedParams(DatastoreService datastore) async {
- final Key<String> commitKey = await _constructCommitKey(datastore);
-
- final String? builderName = requestData![builderNameParam] as String?;
- final Query<Task> query = datastore.db.query<Task>(ancestorKey: commitKey);
- final List<Task> initialTasks = await query.run().toList();
- log.fine('Found ${initialTasks.length} tasks for commit');
- final List<Task> tasks = <Task>[];
- log.fine('Searching for task with builderName=$builderName');
- for (Task task in initialTasks) {
- if (task.builderName == builderName || task.name == builderName) {
- tasks.add(task);
- }
- }
-
- if (tasks.length != 1) {
- log.severe('Found ${tasks.length} entries for builder $builderName');
- throw InternalServerError('Expected to find 1 task for $builderName, but found ${tasks.length}');
- }
-
- return tasks.first;
- }
-
- /// Construct the Datastore key for [Commit] that is the ancestor to this [Task].
- ///
- /// Throws [BadRequestException] if the given git branch does not exist in [CocoonConfig].
- Future<Key<String>> _constructCommitKey(DatastoreService datastore) async {
- final String gitBranch = (requestData![gitBranchParam] as String).trim();
- final String gitSha = (requestData![gitShaParam] as String).trim();
-
- final String id = 'flutter/flutter/$gitBranch/$gitSha';
- final Key<String> commitKey = datastore.db.emptyKey.append<String>(Commit, id: id);
- log.fine('Constructed commit key=$id');
- // Return the official key from Datastore for task lookups.
- final Commit commit = await config.db.lookupValue<Commit>(commitKey);
- return commit.key;
- }
-}
-
-@immutable
-class UpdateTaskStatusResponse extends JsonBody {
- const UpdateTaskStatusResponse(this.task);
-
- final Task task;
-
- @override
- Map<String, dynamic> toJson() {
- return <String, dynamic>{
- 'Name': task.name,
- 'Status': task.status,
- };
- }
-}
diff --git a/app_dart/lib/src/request_handlers/vacuum_github_commits.dart b/app_dart/lib/src/request_handlers/vacuum_github_commits.dart
deleted file mode 100644
index 2f70d0c..0000000
--- a/app_dart/lib/src/request_handlers/vacuum_github_commits.dart
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2019 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:gcloud/db.dart';
-import 'package:github/github.dart' as gh;
-import 'package:meta/meta.dart';
-import 'package:truncate/truncate.dart';
-
-import '../model/appengine/commit.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-import '../service/github_service.dart';
-import '../service/logging.dart';
-import '../service/scheduler.dart';
-
-/// Query GitHub for commits from the past day and ensure they exist in datastore.
-@immutable
-class VacuumGithubCommits extends ApiRequestHandler<Body> {
- const VacuumGithubCommits({
- required super.config,
- required super.authenticationProvider,
- required this.scheduler,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- });
-
- final DatastoreServiceProvider datastoreProvider;
-
- final Scheduler scheduler;
-
- static const String branchParam = 'branch';
-
- @override
- Future<Body> get() async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- for (gh.RepositorySlug slug in config.supportedRepos) {
- final String branch = request!.uri.queryParameters[branchParam] ?? Config.defaultBranch(slug);
- await _vacuumRepository(slug, datastore: datastore, branch: branch);
- }
-
- return Body.empty;
- }
-
- Future<void> _vacuumRepository(
- gh.RepositorySlug slug, {
- DatastoreService? datastore,
- required String branch,
- }) async {
- final GithubService githubService = await config.createGithubService(slug);
- final List<Commit> commits = await _vacuumBranch(
- slug,
- branch,
- datastore: datastore,
- githubService: githubService,
- );
- await scheduler.addCommits(commits);
- }
-
- Future<List<Commit>> _vacuumBranch(
- gh.RepositorySlug slug,
- String branch, {
- DatastoreService? datastore,
- required GithubService githubService,
- }) async {
- List<gh.RepositoryCommit> commits = <gh.RepositoryCommit>[];
- // Sliding window of times to add commits from.
- final DateTime queryAfter = DateTime.now().subtract(const Duration(days: 1));
- final DateTime queryBefore = DateTime.now().subtract(const Duration(minutes: 3));
- try {
- log.fine('Listing commit for slug: $slug branch: $branch and msSinceEpoch: ${queryAfter.millisecondsSinceEpoch}');
- commits = await githubService.listBranchedCommits(slug, branch, queryAfter.millisecondsSinceEpoch);
- log.fine('Retrieved ${commits.length} commits from GitHub');
- // Do not try to add recent commits as they may already be processed
- // by cocoon, which can cause race conditions.
- commits = commits
- .where(
- (gh.RepositoryCommit commit) =>
- commit.commit!.committer!.date!.millisecondsSinceEpoch < queryBefore.millisecondsSinceEpoch,
- )
- .toList();
- } on gh.GitHubError catch (error) {
- log.severe('$error');
- }
-
- return _toDatastoreCommit(slug, commits, datastore, branch);
- }
-
- /// Convert [gh.RepositoryCommit] to Cocoon's [Commit] format.
- Future<List<Commit>> _toDatastoreCommit(
- gh.RepositorySlug slug,
- List<gh.RepositoryCommit> commits,
- DatastoreService? datastore,
- String branch,
- ) async {
- final List<Commit> recentCommits = <Commit>[];
- for (gh.RepositoryCommit commit in commits) {
- final String id = '${slug.fullName}/$branch/${commit.sha}';
- final Key<String> key = datastore!.db.emptyKey.append<String>(Commit, id: id);
- recentCommits.add(
- Commit(
- key: key,
- timestamp: commit.commit!.committer!.date!.millisecondsSinceEpoch,
- repository: slug.fullName,
- sha: commit.sha!,
- author: commit.author!.login!,
- authorAvatarUrl: commit.author!.avatarUrl!,
- // The field has a size of 1500 we need to ensure the commit message
- // is at most 1500 chars long.
- message: truncate(commit.commit!.message!, 1490, omission: '...'),
- branch: branch,
- ),
- );
- }
- return recentCommits;
- }
-}
diff --git a/app_dart/lib/src/request_handlin